diff --git a/.dockerignore b/.dockerignore index b9d63f4b..6c6a1a30 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,4 +6,6 @@ server/index server/keypair server/sqlite .git -node_modules \ No newline at end of file +node_modules +server/node_modules + diff --git a/.env b/.env index 807dccad..830a0be8 100644 --- a/.env +++ b/.env @@ -3,6 +3,7 @@ DATABUS_RESOURCE_BASE_URL=http://localhost:3000 # do not use qoutation marks for the value here and no trailing space DATABUS_NAME=My Local Databus +DATABUS_ABSTRACT=A local instance for development DATABUS_ORG_ICON= DATABUS_BANNER_COLOR= # Default is #81b8b2 DATABUS_TITLE_COLOR= # Default is white @@ -10,8 +11,6 @@ DATABUS_TITLE_COLOR= # Default is white DATABUS_OIDC_ISSUER_BASE_URL=https://kilt.eu.auth0.com DATABUS_OIDC_CLIENT_ID=K5PCEOr7OJGBGU9xN7SvBrX1RWDs4S4n DATABUS_OIDC_SECRET=LiL_X1tzwRmReU3RO7kBlBdDopmVEGf4gj5Ve8No16kifyi3weXK7u6IS1Ttpl_q - - DATABUS_PRIVATE_MODE=false ####### DATABASE SETTINGS ####### diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..c61e8dd4 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,84 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Start Databus With AUTH0", + "program": "${workspaceFolder}/server/www", + "cwd": "${workspaceFolder}/server", + "runtimeArgs": [ + "--trace-warnings" + ], + "env": { + "DATABUS_RESOURCE_BASE_URL": "http://localhost:3000", + "DATABUS_NAME": "Local Databus", + "DATABUS_ABSTRACT": "Go ham", + "DATABUS_ORG_ICON": "https://www.dbpedia.org/wp-content/uploads/2020/09/dbpedia-org-logo.png", + "DATABUS_BANNER_COLOR": "\\#5b798d", + "DATABUS_DATABASE_URL": "http://localhost:3002", + "DATABUS_OIDC_ISSUER_BASE_URL": "https://kilt.eu.auth0.com", + "DATABUS_OIDC_CLIENT_ID": "e9eOLS9IkGvuyBl7cBorWUQTNgbqejhc", + "DATABUS_OIDC_REQUIRED_ROLE": "", + "DATABUS_OIDC_SECRET": "0RVo9jMnbhPnkR6Ttz0aXQRTcRuSz5DpqyUEjbbcbgRuGA4rbwCjnHM2cOlTrv9q", + "LOOKUP_BASE_URL": "http://localhost:3004", + "DATABUS_OIDC_USER_ID_PROPERTY": "sub", + "MAX_WORKERS": "1", + "METRICS_PORT": "3001" + } + }, + { + "type": "node", + "request": "launch", + "name": "Start Databus With DBpedia Community Account", + "program": "${workspaceFolder}/server/www", + "cwd": "${workspaceFolder}/server", + "runtimeArgs": [ + "--trace-warnings" + ], + "env": { + "DATABUS_RESOURCE_BASE_URL": "http://localhost:3000", + "DATABUS_NAME": "Local Databus", + "DATABUS_ABSTRACT": "Go ham", + "DATABUS_ORG_ICON": "https://www.dbpedia.org/wp-content/uploads/2020/09/dbpedia-org-logo.png", + "DATABUS_BANNER_COLOR": "\\#5b798d", + "DATABUS_DATABASE_URL": "http://localhost:3002", + "DATABUS_OIDC_ISSUER_BASE_URL": "https://auth.dbpedia.org/realms/dbpedia", + "DATABUS_OIDC_CLIENT_ID": "databus-dev", + "DATABUS_OIDC_SECRET": "O55igP05y7iMlZdhweAEUjjhBIkiu0Mq", + "DATABUS_OIDC_USER_ID_PROPERTY": "sub", + "DATABUS_OIDC_REQUIRED_ROLE": "", + "LOOKUP_BASE_URL": "http://localhost:3004", + "MAX_WORKERS": "1", + "METRICS_PORT": "3001" + } + }, + { + "type": "node", + "request": "launch", + "name": "Run Tests", + "runtimeExecutable": "npm", + "runtimeArgs": [ + "test" + ], + "console": "integratedTerminal", + "cwd": "${workspaceFolder}/server", + "env": { + "DATABUS_RESOURCE_BASE_URL": "http://localhost:3000", + "DATABUS_NAME": "Local Databus", + "DATABUS_ABSTRACT": "Go ham", + "DATABUS_ORG_ICON": "https://www.dbpedia.org/wp-content/uploads/2020/09/dbpedia-org-logo.png", + "DATABUS_BANNER_COLOR": "#5b798d", + "DATABUS_DATABASE_URL": "http://localhost:3002", + "DATABUS_OIDC_ISSUER_BASE_URL": "https://auth.dbpedia.org/realms/dbpedia", + "DATABUS_OIDC_CLIENT_ID": "databus-dev", + "DATABUS_OIDC_SECRET": "O55igP05y7iMlZdhweAEUjjhBIkiu0Mq", + "DATABUS_OIDC_REQUIRED_ROLE": "", + "DATABUS_OIDC_USER_ID_PROPERTY": "sub", + "LOOKUP_BASE_URL": "http://localhost:3004", + "MAX_WORKERS": "1", + "METRICS_PORT": "3001" + } + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..53d1379c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "editor.detectIndentation": true +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 5742b1ca..bb860f70 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,56 +1,50 @@ +<<<<<<< HEAD +# Use a consistent Node.js version across build and release stages +FROM node:23-slim AS builder +======= FROM docker.io/ubuntu:22.04 +>>>>>>> master -# Install node.js, Caddy as proxy server, and java. -RUN apt-get update && \ - apt-get -y install curl debian-keyring debian-archive-keyring apt-transport-https && \ - curl -sL https://deb.nodesource.com/setup_16.x | bash - && \ - curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg && \ - curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list && \ - apt-get update && \ - apt-get -y install \ - nodejs \ - caddy \ - ca-certificates-java \ - openjdk-17-jdk \ - openjdk-17-jre && \ - rm -rf /var/lib/apt/lists/* && \ - node -v && \ - npm -v && \ - java -version - -# Disable the proxy server by default: -ENV DATABUS_PROXY_SERVER_ENABLE=false - -# When using the proxy, use ACME by default: -ENV DATABUS_PROXY_SERVER_USE_ACME=true - -# When not using ACME, what is the name of the own certificate file? -ENV DATABUS_PROXY_SERVER_OWN_CERT="cert.pem" - -# When not using ACME, what is the name of the own certificate's key file? -ENV DATABUS_PROXY_SERVER_OWN_CERT_KEY="key.pem" - -# What is the hostname of this machine, when using the proxy server? -# It is necessary to know this, in order to set up ACME etc. -# Note: the host name should be identical to DATABUS_RESOURCE_BASE_URL, -# but without specifying a port, protocol i.e. HTTP(S) etc. -ENV DATABUS_PROXY_SERVER_HOSTNAME="my-databus.org" - -# Define the volume for the TLS certificate: -VOLUME /tls - -COPY ./server /databus/server -COPY ./public /databus/public -COPY ./search /databus/search -COPY ./model/generated /databus/model/generated - -COPY ./setup.sh /databus/setup.sh - -# Set up the NPM projects: -RUN cd /databus/server && \ - npm install && \ - cd ../public && \ - npm install +# Install Python for node-gyp (required for sqlite3 build) +RUN apt-get update && apt-get install -y python3 build-essential && \ + ln -s /usr/bin/python3 /usr/bin/python && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /databus/server +COPY server/package*.json . +RUN --mount=type=bind,source=server/package.json,target=package.json \ + --mount=type=cache,target=/root/.npm \ + bash -c "if [ ! -f package-lock.json ]; then npm install --package-lock-only; fi && npm ci --omit=dev" + +WORKDIR /databus/public +COPY public/package*.json . +RUN --mount=type=bind,source=public/package.json,target=package.json \ + --mount=type=cache,target=/root/.npm \ + bash -c "if [ ! -f package-lock.json ]; then npm install --package-lock-only; fi && npm ci --omit=dev" + +WORKDIR / + +# Copy source code +COPY server ./databus/server +COPY public ./databus/public +COPY search ./databus/search + +# Rebuild sqlite3 in case it's a native module +# RUN cd databus/server && npm rebuild sqlite3 + +FROM node:23-slim AS runtime WORKDIR /databus -ENTRYPOINT [ "/bin/bash", "./setup.sh" ] + +# Install only necessary system packages +RUN apt-get update && \ + apt-get install -y \ + curl \ + ca-certificates && \ + rm -rf /var/lib/apt/lists/* + +# Copy app from build stage +COPY --from=builder /databus . + +# Entrypoint +ENTRYPOINT [ "/bin/bash", "./server/setup.sh" ] diff --git a/backup.sh b/backup.sh new file mode 100644 index 00000000..028fc821 --- /dev/null +++ b/backup.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# VARIABLES + +# Define a prefix for the backup filename +prefix="backup" + +# Get the current date in YYYY-MM-DD format +date=$(date +%F) + +# Compose the full filename with prefix and date +filename="${prefix}_${date}.tar.gz" + +# Specify the local folder to compress +localFolder="/devenv/data" + +# Target folder on the remote server to copy the file into +targetFolder="/path/to/remote/folder" + +# Shut down the databus, adjust for stack +docker compose stop + +# Create a tar.gz archive of the local folder with the composed filename +tar -czf "$filename" "$localFolder" + +# Securely copy the archive to the remote server's target folder +scp "$filename" "$targetFolder" + +# Remove the local tar.gz archive to clean up +rm "$filename" + +# Restart the databus, adjust for stack +docker compose up -d \ No newline at end of file diff --git a/deployment/.env b/deployment/.env new file mode 100644 index 00000000..bab8ccf9 --- /dev/null +++ b/deployment/.env @@ -0,0 +1,20 @@ +####### DATABUS SETTINGS ####### +DATA_PATH="../../databus-data" +# RESOURCE BASE URL - This should match the DNS entry pointing to your server +DATABUS_RESOURCE_BASE_URL=http://localhost:3000 +# do not use qoutation marks for the value here and no trailing space +DATABUS_NAME=My Local Databus +DATABUS_ABSTRACT=A local instance for development +DATABUS_ORG_ICON= +DATABUS_BANNER_COLOR= # Default is #81b8b2 +DATABUS_TITLE_COLOR= # Default is white +# OIDC SETTINGS - Only use these values for testing and enter your own OIDC provider values +DATABUS_OIDC_ISSUER_BASE_URL=https://kilt.eu.auth0.com +DATABUS_OIDC_CLIENT_ID=K5PCEOr7OJGBGU9xN7SvBrX1RWDs4S4n +DATABUS_OIDC_SECRET=LiL_X1tzwRmReU3RO7kBlBdDopmVEGf4gj5Ve8No16kifyi3weXK7u6IS1Ttpl_q +DATABUS_PRIVATE_MODE=false + +####### DATABASE SETTINGS ####### +VIRTUOSO_USER=dba +VIRTUOSO_PASSWORD=onlyweknow + diff --git a/deployment/README.md b/deployment/README.md new file mode 100644 index 00000000..788707e8 --- /dev/null +++ b/deployment/README.md @@ -0,0 +1,10 @@ + +# Install Instructions + +## Starting a local Databus + +To start a local Databus instance + +``` +docker compose up +``` \ No newline at end of file diff --git a/deployment/docker-compose.yml b/deployment/docker-compose.yml new file mode 100644 index 00000000..83a03576 --- /dev/null +++ b/deployment/docker-compose.yml @@ -0,0 +1,59 @@ +name: "databus-dockerized" +services: + databus: + image: "docker.io/dbpedia/databus:dev" + ports: + - 3000:3000 + - 3001:3001 + environment: + DATABUS_RESOURCE_BASE_URL: ${DATABUS_RESOURCE_BASE_URL} + DATABUS_DATABASE_URL: "http://gstore:8080" + LOOKUP_BASE_URL: "http://lookup:8082" + METRICS_PORT: "3001" + DATABUS_OIDC_ISSUER_BASE_URL: ${DATABUS_OIDC_ISSUER_BASE_URL} + DATABUS_NAME: ${DATABUS_NAME} + DATABUS_ABSTRACT: ${DATABUS_ABSTRACT} + DATABUS_ORG_ICON: ${DATABUS_ORG_ICON} + DATABUS_BANNER_COLOR: ${DATABUS_BANNER_COLOR} + DATABUS_OIDC_CLIENT_ID: ${DATABUS_OIDC_CLIENT_ID} + DATABUS_OIDC_SECRET: ${DATABUS_OIDC_SECRET} + DATABUS_PRIVATE_MODE: ${DATABUS_PRIVATE_MODE} # Private mode will only show metadata for logged-in users + volumes: + - ${DATA_PATH}/data/dav/:/databus/server/dav/ + - ${DATA_PATH}/data/sqlite/:/databus/server/sqlite + - ${DATA_PATH}/data/keypair/:/databus/server/keypair + gstore: + image: "docker.io/dbpedia/gstore" + environment: + STORAGE_USER: ${VIRTUOSO_USER} + STORAGE_PASS: ${VIRTUOSO_PASSWORD} + STORAGE_SPARQL_ENDPOINT_URI: http://virtuoso:8890/sparql + DEFAULT_JSONLD_LOCALHOST_CONTEXT: http://localhost:3000/res/context.jsonld + DEFAULT_JSONLD_LOCALHOST_CONTEXT_LOCATION: http://databus:3000/res/context.jsonld + ports: + - 3002:8080 + volumes: + - ${DATA_PATH}/data/gstore/repo:/gstore/git + - ${DATA_PATH}/data/gstore/logs:/gstore/logs + virtuoso: + image: "docker.io/openlink/virtuoso-opensource-7" + environment: + DBA_PASSWORD: ${VIRTUOSO_PASSWORD} + SPARQL_UPDATE: "true" + DEFAULT_GRAPH: ${DATABUS_RESOURCE_BASE_URL} + ports: + - 3003:8890 + volumes: + - ${DATA_PATH}/data/virtuoso:/database + entrypoint: /bin/bash -c " + echo 'grant SPARQL_LOAD_SERVICE_DATA to \"SPARQL\";' > /opt/virtuoso-opensource/initdb.d/ini.sql && + echo 'grant SPARQL_SPONGE to \"SPARQL\";' >> /opt/virtuoso-opensource/initdb.d/ini.sql && + echo 'grant SPARQL_SELECT_FED to \"SPARQL\";' > /opt/virtuoso-opensource/initdb.d/ini.sql && + /virtuoso-entrypoint.sh" + lookup: + image: "docker.io/dbpedia/lookup:dev" + ports: + - 3004:8082 + volumes: + - ${DATA_PATH}/data/index/:/index + - ../search/servlet-config.yml:/resources/config.yml diff --git a/devenv/Makefile b/devenv/Makefile index acc08329..1b54374a 100644 --- a/devenv/Makefile +++ b/devenv/Makefile @@ -1,6 +1,7 @@ DATABUS_RESOURCE_BASE_URL=http://localhost:3000 DATABUS_DATABASE_URL=http://localhost:3002 DATABUS_NAME="Just Another Databus" +DATABUS_ABSTRACT="Another Node in the Databus Network" DATABUS_ORG_ICON="https://www.dbpedia.org/wp-content/uploads/2020/09/dbpedia-org-logo.png" DATABUS_BANNER_COLOR="\#5b798d" @@ -9,7 +10,7 @@ webpack: env-clean: sudo rm -r data/; \ - sudo rm -r ../server/users; \ + sudo rm -r ../server/users docker rm -f devenv_lookup; \ docker rm -f devenv_virtuoso; \ docker rm -f devenv_gstore; @@ -42,7 +43,9 @@ srv-install: srv-start-auth0: DATABUS_RESOURCE_BASE_URL=${DATABUS_RESOURCE_BASE_URL} \ DATABUS_NAME=${DATABUS_NAME} \ + DATABUS_ABSTRACT=${DATABUS_ABSTRACT} \ DATABUS_ORG_ICON=${DATABUS_ORG_ICON} \ + DATABUS_BANNER=${DATABUS_BANNER} \ DATABUS_BANNER_COLOR=${DATABUS_BANNER_COLOR} \ DATABUS_DATABASE_URL=${DATABUS_DATABASE_URL} \ DATABUS_OIDC_ISSUER_BASE_URL=https://kilt.eu.auth0.com \ @@ -81,3 +84,6 @@ srv-test: DATABUS_OIDC_CLIENT_ID=e9eOLS9IkGvuyBl7cBorWUQTNgbqejhc \ DATABUS_OIDC_SECRET=0RVo9jMnbhPnkR6Ttz0aXQRTcRuSz5DpqyUEjbbcbgRuGA4rbwCjnHM2cOlTrv9q \ npm run test --prefix ./../server + +dockerize: + docker buildx build --push -t dbpedia/databus:dev -f ../Dockerfile .. \ No newline at end of file diff --git a/devenv/README.md b/devenv/README.md index c439a9ce..904de905 100644 --- a/devenv/README.md +++ b/devenv/README.md @@ -1,5 +1,12 @@ # Development Environment +## Orientation + +The Databus is implemented in Javascript using Nodejs+Express for the Server and Angularjs for the client-side webapp. +The nodejs server is providing both the API and the webapp resources. The webapp does not have its own server and runs only in the browser. + +The Databus is best developed using Visual Studio Code to use the startup scripts in the `.vscode` folder. + ## Requirements * `node.js`: v16.13.0 or higher diff --git a/devenv/docker-compose.yml b/devenv/docker-compose.yml index 5394a0a4..ffdfff67 100644 --- a/devenv/docker-compose.yml +++ b/devenv/docker-compose.yml @@ -1,22 +1,44 @@ # Starts everything except the databus -version: "3.0" +name: databus_devenv services: gstore: +<<<<<<< HEAD + restart: unless-stopped + image: dbpedia/gstore:dev + container_name: databus_devenv_gstore +======= image: docker.io/dbpedia/gstore:dev container_name: devenv_gstore +>>>>>>> master environment: STORAGE_USER: "dba" STORAGE_PASS: "everyoneknows" STORAGE_SPARQL_ENDPOINT_URI: http://virtuoso:8890/sparql - GSTORE_LOCALHOST_CONTEXT_FALLBACK_URL: http://172.17.0.1 + DEFAULT_JSONLD_LOCALHOST_CONTEXT: http://localhost:3000/res/context.jsonld + DEFAULT_JSONLD_LOCALHOST_CONTEXT_LOCATION: http://host.docker.internal:3000/res/context.jsonld ports: - - "3002:8080" + - "3002:8080" volumes: - ./data/gstore/git:/gstore/git - ./data/gstore/logs:/gstore/logs + lookup: + restart: unless-stopped + image: dbpedia/lookup:dev + container_name: databus_devenv_lookup + ports: + - "3004:8082" + volumes: + - ./data/index/:/index + - ../search/servlet-config.yml:/resources/config.yml virtuoso: +<<<<<<< HEAD + restart: unless-stopped + image: "openlink/virtuoso-opensource-7:latest" + container_name: databus_devenv_virtuoso +======= image: "docker.io/openlink/virtuoso-opensource-7:latest" container_name: devenv_virtuoso +>>>>>>> master environment: DBA_PASSWORD: "everyoneknows" SPARQL_UPDATE: "true" @@ -29,7 +51,30 @@ services: echo 'grant SPARQL_LOAD_SERVICE_DATA to \"SPARQL\";' > /opt/virtuoso-opensource/initdb.d/ini.sql && echo 'grant SPARQL_SPONGE to \"SPARQL\";' >> /opt/virtuoso-opensource/initdb.d/ini.sql && echo 'grant SPARQL_SELECT_FED to \"SPARQL\";' > /opt/virtuoso-opensource/initdb.d/ini.sql && - /virtuoso-entrypoint.sh - " - - + /virtuoso-entrypoint.sh" + prometheus: + image: prom/prometheus + container_name: databus_devenv_prometheus + ports: + - "3005:9090" + volumes: + - ./data/prometheus:/prometheus + - ./prometheus.yml:/etc/prometheus/prometheus.yml + command: + - "--config.file=/etc/prometheus/prometheus.yml" + - "--storage.tsdb.path=/prometheus" + restart: unless-stopped + grafana: + image: grafana/grafana + container_name: databus_devenv_grafana + ports: + - "3006:3000" + volumes: + - ./data/grafana:/var/lib/grafana + environment: + - GF_SECURITY_ADMIN_USER=admin + - GF_SECURITY_ADMIN_PASSWORD=admin + - GF_USERS_ALLOW_SIGN_UP=false + restart: unless-stopped + depends_on: + - prometheus \ No newline at end of file diff --git a/devenv/grafana-promql.md b/devenv/grafana-promql.md new file mode 100644 index 00000000..944c111b --- /dev/null +++ b/devenv/grafana-promql.md @@ -0,0 +1,7 @@ +# Grafana Dashboard Queries + +## Downloads Over Time +> ceil(sum(increase(databus_downloads[1m])) or vector(0)) + +## TOP 5 Artifact + diff --git a/devenv/prometheus.yml b/devenv/prometheus.yml new file mode 100644 index 00000000..18a1b0d3 --- /dev/null +++ b/devenv/prometheus.yml @@ -0,0 +1,7 @@ +global: + scrape_interval: 15s + +scrape_configs: + - job_name: 'databus-server' + static_configs: + - targets: ['host.docker.internal:3001'] \ No newline at end of file diff --git a/devenv/random-downloader.sh b/devenv/random-downloader.sh new file mode 100644 index 00000000..6f55aed9 --- /dev/null +++ b/devenv/random-downloader.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +URL="http://localhost:3000/janfo/dumps/wikidata/2025-02-13/wikidata.java" + +while true; do + curl -s "$URL" + sleep $(awk -v min=0.1 -v max=1 'BEGIN{srand(); print min+rand()*(max-min)}') +done diff --git a/docker-compose-4-swarm.yml b/docker-compose-4-swarm.yml new file mode 100644 index 00000000..1301fbe4 --- /dev/null +++ b/docker-compose-4-swarm.yml @@ -0,0 +1,67 @@ +version: "3.8" # Updated version for Swarm compatibility +services: + ${CLIENT_ID}_server: + image: "databus-rc12" + environment: + DATABUS_RESOURCE_BASE_URL: ${DATABUS_RESOURCE_BASE_URL} + DATABUS_DATABASE_URL: "http://databus_gstore:8080" + LOOKUP_BASE_URL: "http://lookup:8082" + DATABUS_OIDC_ISSUER_BASE_URL: ${DATABUS_OIDC_ISSUER_BASE_URL} + DATABUS_NAME: ${DATABUS_NAME} + DATABUS_ORG_ICON: ${DATABUS_ORG_ICON} + DATABUS_BANNER_COLOR: ${DATABUS_BANNER_COLOR} + DATABUS_OIDC_CLIENT_ID: ${DATABUS_OIDC_CLIENT_ID} + DATABUS_OIDC_SECRET: ${DATABUS_OIDC_SECRET} + DATABUS_PRIVATE_MODE: ${DATABUS_PRIVATE_MODE} + DATABUS_PROXY_SERVER_ENABLE: ${DATABUS_PROXY_SERVER_ENABLE} + DATABUS_PROXY_SERVER_USE_ACME: ${DATABUS_PROXY_SERVER_USE_ACME} + DATABUS_PROXY_SERVER_OWN_CERT: ${DATABUS_PROXY_SERVER_OWN_CERT} + DATABUS_PROXY_SERVER_OWN_CERT_KEY: ${DATABUS_PROXY_SERVER_OWN_CERT_KEY} + DATABUS_PROXY_SERVER_HOSTNAME: ${DATABUS_PROXY_SERVER_HOSTNAME} + volumes: + - ./data/${CLIENT_ID}/dav/:/databus/server/dav/ + - ./data/${CLIENT_ID}/sqlite/:/databus/server/sqlite + - ./data/${CLIENT_ID}/keypair/:/databus/server/keypair + - ./data/${CLIENT_ID}/tls/:/tls:ro + - ./data/${CLIENT_ID}/tls/caddy:/root/.local/share/caddy/ + networks: + - ${CLIENT_ID}_network + databus_gstore: + image: "docker.io/dbpedia/gstore" + environment: + STORAGE_USER: ${VIRTUOSO_USER} + STORAGE_PASS: ${VIRTUOSO_PASSWORD} + STORAGE_SPARQL_ENDPOINT_URI: http://databus_virtuoso:8890/sparql + volumes: + - ./data/${CLIENT_ID}/gstore/repo:/gstore/git + - ./data/${CLIENT_ID}/gstore/logs:/gstore/logs + networks: + - ${CLIENT_ID}_network + databus_virtuoso: + image: "docker.io/openlink/virtuoso-opensource-7" + environment: + DBA_PASSWORD: ${VIRTUOSO_PASSWORD} + SPARQL_UPDATE: "true" + DEFAULT_GRAPH: ${DATABUS_RESOURCE_BASE_URL} + ports: + - "127.0.0.1:${CLIENT_ID}_3003:8890" + volumes: + - ./data/${CLIENT_ID}/virtuoso:/database + entrypoint: /bin/bash -c " + echo 'grant SPARQL_LOAD_SERVICE_DATA to \"SPARQL\";' > /opt/virtuoso-opensource/initdb.d/ini.sql && + echo 'grant SPARQL_SPONGE to \"SPARQL\";' >> /opt/virtuoso-opensource/initdb.d/ini.sql && + echo 'grant SPARQL_SELECT_FED to \"SPARQL\";' > /opt/virtuoso-opensource/initdb.d/ini.sql && + /virtuoso-entrypoint.sh + "" + networks: + - ${CLIENT_ID}_network + databus_lookup: + image: lookup:dev + volumes: + - ./data/index/:/index + - ../search/servlet-config.yml:/resources/config.yml + networks: + - ${CLIENT_ID}_network +networks: + ${CLIENT_ID}_network: + driver: overlay \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 70d9d0bd..83b49a2d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,8 @@ version: "3.0" +name: "databus-nodejs-dockerized" services: databus: - image: "docker.io/dbpedia/databus" + image: "databus-rc12" ports: - 3000:3000 # HTTP: Databus web UI # - 80:80 # ** uncomment if proxy enabled only** HTTP port of included proxy (caddy) necessary for Auto-HTTPS via ACME and HTTP->HTTPS redirect @@ -9,8 +10,11 @@ services: environment: DATABUS_RESOURCE_BASE_URL: ${DATABUS_RESOURCE_BASE_URL} DATABUS_DATABASE_URL: "http://gstore:8080" + LOOKUP_BASE_URL: "http://lookup:8082" + METRICS_PORT: "3001" DATABUS_OIDC_ISSUER_BASE_URL: ${DATABUS_OIDC_ISSUER_BASE_URL} DATABUS_NAME: ${DATABUS_NAME} + DATABUS_ABSTRACT: ${DATABUS_ABSTRACT} DATABUS_ORG_ICON: ${DATABUS_ORG_ICON} DATABUS_BANNER_COLOR: ${DATABUS_BANNER_COLOR} DATABUS_OIDC_CLIENT_ID: ${DATABUS_OIDC_CLIENT_ID} @@ -33,8 +37,12 @@ services: STORAGE_USER: ${VIRTUOSO_USER} STORAGE_PASS: ${VIRTUOSO_PASSWORD} STORAGE_SPARQL_ENDPOINT_URI: http://virtuoso:8890/sparql + # gstore now is preloading the context for localhost from a configured remote location + # only needed for dev env with context at localhost + DEFAULT_JSONLD_LOCALHOST_CONTEXT: http://localhost:3000/res/context.jsonld + DEFAULT_JSONLD_LOCALHOST_CONTEXT_LOCATION: http://databus:3000/res/context.jsonld ports: - - "127.0.0.1:3002:8080" + - 3002:8080 volumes: - ./data/gstore/repo:/gstore/git - ./data/gstore/logs:/gstore/logs @@ -45,12 +53,19 @@ services: SPARQL_UPDATE: "true" DEFAULT_GRAPH: ${DATABUS_RESOURCE_BASE_URL} ports: - - "127.0.0.1:3003:8890" + - 3003:8890 volumes: - ./data/virtuoso:/database entrypoint: /bin/bash -c " echo 'grant SPARQL_LOAD_SERVICE_DATA to \"SPARQL\";' > /opt/virtuoso-opensource/initdb.d/ini.sql && echo 'grant SPARQL_SPONGE to \"SPARQL\";' >> /opt/virtuoso-opensource/initdb.d/ini.sql && echo 'grant SPARQL_SELECT_FED to \"SPARQL\";' > /opt/virtuoso-opensource/initdb.d/ini.sql && - /virtuoso-entrypoint.sh - " + /virtuoso-entrypoint.sh" + lookup: + image: lookup:dev + container_name: databus_devenv_lookup + ports: + - 3004:8082 + volumes: + - ./data/index/:/index + - ../search/servlet-config.yml:/resources/config.yml diff --git a/docs/account.md b/docs/account.md index 3ecc16f0..c8606390 100644 --- a/docs/account.md +++ b/docs/account.md @@ -98,7 +98,7 @@ Spec (OWL, SHACL, JSON-LD Context) "@id": "foaf:primaryTopic", "@type": "@id" }, - "name": {"@id": "foaf:name"}, + "displayName": {"@id": "foaf:name"}, "account": { "@id": "databus:account", "@type": "@id" diff --git a/docs/artifact.md b/docs/artifact.md index 97286b4e..ced0a4e3 100644 --- a/docs/artifact.md +++ b/docs/artifact.md @@ -31,8 +31,8 @@ databus:Artifact a owl:Class ; sh:property [ sh:path [ sh:inversePath rdf:type ] ; sh:nodekind sh:IRI ; - sh:pattern "/[a-zA-Z0-9\\-_]{4,}/[a-zA-Z0-9\\-_\\.]{3,}/[a-zA-Z0-9\\-_\\.]{3,}$" ; - sh:message "IRI for databus:Artifact must match /[a-zA-Z0-9\\-_]{4,}/[a-zA-Z0-9\\-_\\.]{3,}/[a-zA-Z0-9\\-_\\.]{3,}$"@en ; + sh:pattern "^[\\w+.-]+:\\/\\/[\\w+.:-]+\\/[\\w+.-]{4,}(?:\\/[\\w+.-]{3,}){2,2}$" ; + sh:message "IRI for databus:Artifact must be a 3-segment URI and match ^[\\w+.-]+:\\/\\/[\\w+.:-]+\\/[\\w+.-]{4,}(?:\\/[\\w+.-]{3,}){3,3}$"@en ; ] . ``` ```javascript @@ -75,10 +75,10 @@ dct:title sh:qualifiedValueShape [ sh:datatype xsd:string ] ; sh:qualifiedMaxCount 1 ; ] ; - sh:property [ + sh:property [ sh:path dct:title ; sh:severity sh:Violation ; - sh:maxLength 100 ; + sh:maxLength 300 ; sh:message "dct:title must have less than 100 characters and each language must occure only once."@en ; sh:uniqueLang true ; ] . diff --git a/docs/group.md b/docs/group.md index 38828c4b..88523dbe 100644 --- a/docs/group.md +++ b/docs/group.md @@ -31,8 +31,8 @@ databus:Group a owl:Class ; sh:property [ sh:path [ sh:inversePath rdf:type ] ; sh:nodekind sh:IRI ; - sh:pattern "/[a-zA-Z0-9\\-_]{4,}/[a-zA-Z0-9\\-_\\.]{3,}$" ; - sh:message "IRI for databus:Group must match /[a-zA-Z0-9\\-_]{4,}/[a-zA-Z0-9\\-_\\.]{3,}$"@en ; + sh:pattern "^[\\w+.-]+:\\/\\/[\\w+.:-]+\\/[\\w+.-]{4,}\\/[\\w+.-]{3,}$" ; + sh:message "IRI for databus:Group must be a 2-segment URI and match ^[\\w+.-]+:\\/\\/[\\w+.:-]+\\/[\\w+.-]{4,}\\/[\\w+.-]{3,}$"@en ; ] . ``` ```javascript @@ -75,9 +75,10 @@ dct:title sh:qualifiedValueShape [ sh:datatype xsd:string ] ; sh:qualifiedMaxCount 1 ; ] ; - sh:property [ + sh:property [ sh:path dct:title ; sh:severity sh:Violation ; + sh:maxLength 300 ; sh:message "dct:title can be used with language tag, but each language only once."@en ; sh:uniqueLang true ; ] . diff --git a/docs/version.md b/docs/version.md index 74b1339e..5540613e 100644 --- a/docs/version.md +++ b/docs/version.md @@ -42,12 +42,13 @@ databus:Version a owl:Class ; sh:property [ sh:path [ sh:inversePath rdf:type ] ; sh:nodekind sh:IRI ; - sh:pattern "/[a-zA-Z0-9\\-_]{4,}/[a-zA-Z0-9\\-_\\.]{1,}/[a-zA-Z0-9\\-_\\.]{1,}/[a-zA-Z0-9\\-_\\.]{1,}$" ; - sh:message "IRI for databus:Version must match /USER/GROUP/ARTIFACT/VERSION , |USER|>3"@en ; + sh:pattern "^[\\w+.-]+:\\/\\/[\\w+.:-]+\\/[\\w+.-]{4,}(?:\\/[\\w+.-]{3,}){3,3}$" ; + sh:message "IRI for databus:Version must be a 4-segment URI and match ^[\\w+.-]+:\\/\\/[\\w+.:-]+\\/[\\w+.-]{4,}(?:\\/[\\w+.-]{3,}){3,3}$"@en ; ] . ``` ```javascript -"Version": "databus:Version" +"Version": "databus:Version", +"Dataset": "dcat:Dataset" ``` ## 1. General Metadata @@ -296,7 +297,13 @@ prov:wasDerivedFrom a owl:ObjectProperty ; ``` ```turtle - +<#was-derived-from> + a sh:PropertyShape ; + sh:targetClass databus:Version ; + sh:severity sh:Violation ; + sh:message "Value of prov:wasDerivedFrom from must be a valid IRI."@en ; + sh:path prov:wasDerivedFrom ; + sh:nodeKind sh:IRI . ``` ```javascript "wasDerivedFrom": { @@ -398,7 +405,7 @@ databus:group rdf:type owl:ObjectProperty ; a sh:NodeShape; sh:targetClass databus:Version ; sh:sparql [ - sh:message "Dataset URI must contain the group URI of the associated group." ; + sh:message "Version URI must contain the group URI of the associated group." ; sh:prefixes databus: ; sh:select """ SELECT $this ?group diff --git a/model/account.php b/model/account.php index c481d3c0..8b91958f 100644 --- a/model/account.php +++ b/model/account.php @@ -102,7 +102,7 @@ "@id": "foaf:primaryTopic", "@type": "@id" }, - "name": {"@id": "foaf:name"}, + "displayName": {"@id": "foaf:name"}, "account": { "@id": "databus:account", "@type": "@id" diff --git a/model/artifact.php b/model/artifact.php index ce0f1df8..bce2529b 100644 --- a/model/artifact.php +++ b/model/artifact.php @@ -30,8 +30,8 @@ sh:property [ sh:path [ sh:inversePath rdf:type ] ; sh:nodekind sh:IRI ; - sh:pattern "/[a-zA-Z0-9\\\\-_]{4,}/[a-zA-Z0-9\\\\-_\\\\.]{3,}/[a-zA-Z0-9\\\\-_\\\\.]{3,}$" ; - sh:message "IRI for databus:Artifact must match /[a-zA-Z0-9\\\\-_]{4,}/[a-zA-Z0-9\\\\-_\\\\.]{3,}/[a-zA-Z0-9\\\\-_\\\\.]{3,}$"@en ; + sh:pattern "^[\\\\w+.-]+:\\\\/\\\\/[\\\\w+.:-]+\\\\/[\\\\w+.-]{4,}(?:\\\\/[\\\\w+.-]{3,}){2,2}$" ; + sh:message "IRI for databus:Artifact must be a 3-segment URI and match ^[\\\\w+.-]+:\\\\/\\\\/[\\\\w+.:-]+\\\\/[\\\\w+.-]{4,}(?:\\\\/[\\\\w+.-]{3,}){3,3}$"@en ; ] .'; $example='"@type": "Artifact",'; @@ -67,10 +67,10 @@ sh:qualifiedValueShape [ sh:datatype xsd:string ] ; sh:qualifiedMaxCount 1 ; ] ; - sh:property [ + sh:property [ sh:path dct:title ; sh:severity sh:Violation ; - sh:maxLength 100 ; + sh:maxLength 300 ; sh:message "dct:title must have less than 100 characters and each language must occure only once."@en ; sh:uniqueLang true ; ] . '; diff --git a/model/finalize.php b/model/finalize.php index 9a796739..c17c89ab 100644 --- a/model/finalize.php +++ b/model/finalize.php @@ -58,6 +58,7 @@ function headerFooterShacl($shaclDir){ @prefix dbo: . @prefix foaf: . @prefix db: . +@prefix prov: . "; diff --git a/model/generated/context.json b/model/generated/context.json index 78ef3b33..34da9805 100644 --- a/model/generated/context.json +++ b/model/generated/context.json @@ -33,7 +33,8 @@ "@type": "@id" }, -"Version": "databus:Version" , +"Version": "databus:Version", +"Dataset": "dcat:Dataset" , "publisher": { "@id": "dct:publisher", @@ -121,7 +122,7 @@ "@id": "foaf:primaryTopic", "@type": "@id" }, - "name": {"@id": "foaf:name"}, + "displayName": {"@id": "foaf:name"}, "account": { "@id": "databus:account", "@type": "@id" diff --git a/model/generated/shacl/account.shacl b/model/generated/shacl/account.shacl index dfb37fa5..5466de50 100644 --- a/model/generated/shacl/account.shacl +++ b/model/generated/shacl/account.shacl @@ -11,6 +11,7 @@ @prefix dbo: . @prefix foaf: . @prefix db: . +@prefix prov: . <#person-exists> a sh:NodeShape ; diff --git a/model/generated/shacl/artifact.shacl b/model/generated/shacl/artifact.shacl index 0ec70ca3..710acb60 100644 --- a/model/generated/shacl/artifact.shacl +++ b/model/generated/shacl/artifact.shacl @@ -11,6 +11,7 @@ @prefix dbo: . @prefix foaf: . @prefix db: . +@prefix prov: . <#artifact-exists> a sh:NodeShape ; @@ -24,8 +25,8 @@ sh:property [ sh:path [ sh:inversePath rdf:type ] ; sh:nodekind sh:IRI ; - sh:pattern "/[a-zA-Z0-9\\-_]{4,}/[a-zA-Z0-9\\-_\\.]{3,}/[a-zA-Z0-9\\-_\\.]{3,}$" ; - sh:message "IRI for databus:Artifact must match /[a-zA-Z0-9\\-_]{4,}/[a-zA-Z0-9\\-_\\.]{3,}/[a-zA-Z0-9\\-_\\.]{3,}$"@en ; + sh:pattern "^[\\w+.-]+:\\/\\/[\\w+.:-]+\\/[\\w+.-]{4,}(?:\\/[\\w+.-]{3,}){2,2}$" ; + sh:message "IRI for databus:Artifact must be a 3-segment URI and match ^[\\w+.-]+:\\/\\/[\\w+.:-]+\\/[\\w+.-]{4,}(?:\\/[\\w+.-]{3,}){3,3}$"@en ; ] . <#title-artifact> @@ -38,10 +39,10 @@ sh:qualifiedValueShape [ sh:datatype xsd:string ] ; sh:qualifiedMaxCount 1 ; ] ; - sh:property [ + sh:property [ sh:path dct:title ; sh:severity sh:Violation ; - sh:maxLength 100 ; + sh:maxLength 300 ; sh:message "dct:title must have less than 100 characters and each language must occure only once."@en ; sh:uniqueLang true ; ] . diff --git a/model/generated/shacl/collection.shacl b/model/generated/shacl/collection.shacl index 51e07754..e1655349 100644 --- a/model/generated/shacl/collection.shacl +++ b/model/generated/shacl/collection.shacl @@ -11,6 +11,7 @@ @prefix dbo: . @prefix foaf: . @prefix db: . +@prefix prov: . <#collection-exists> a sh:NodeShape ; diff --git a/model/generated/shacl/group.shacl b/model/generated/shacl/group.shacl index f8ad9496..289cf611 100644 --- a/model/generated/shacl/group.shacl +++ b/model/generated/shacl/group.shacl @@ -11,6 +11,7 @@ @prefix dbo: . @prefix foaf: . @prefix db: . +@prefix prov: . <#group-exists> a sh:NodeShape ; @@ -24,8 +25,8 @@ sh:property [ sh:path [ sh:inversePath rdf:type ] ; sh:nodekind sh:IRI ; - sh:pattern "/[a-zA-Z0-9\\-_]{4,}/[a-zA-Z0-9\\-_\\.]{3,}$" ; - sh:message "IRI for databus:Group must match /[a-zA-Z0-9\\-_]{4,}/[a-zA-Z0-9\\-_\\.]{3,}$"@en ; + sh:pattern "^[\\w+.-]+:\\/\\/[\\w+.:-]+\\/[\\w+.-]{4,}\\/[\\w+.-]{3,}$" ; + sh:message "IRI for databus:Group must be a 2-segment URI and match ^[\\w+.-]+:\\/\\/[\\w+.:-]+\\/[\\w+.-]{4,}\\/[\\w+.-]{3,}$"@en ; ] . <#title-group> @@ -38,9 +39,10 @@ sh:qualifiedValueShape [ sh:datatype xsd:string ] ; sh:qualifiedMaxCount 1 ; ] ; - sh:property [ + sh:property [ sh:path dct:title ; sh:severity sh:Violation ; + sh:maxLength 300 ; sh:message "dct:title can be used with language tag, but each language only once."@en ; sh:uniqueLang true ; ] . diff --git a/model/generated/shacl/version.shacl b/model/generated/shacl/version.shacl index 0360b6b2..f8fa9fa0 100644 --- a/model/generated/shacl/version.shacl +++ b/model/generated/shacl/version.shacl @@ -11,6 +11,7 @@ @prefix dbo: . @prefix foaf: . @prefix db: . +@prefix prov: . <#version-exists> a sh:NodeShape ; @@ -24,8 +25,8 @@ sh:property [ sh:path [ sh:inversePath rdf:type ] ; sh:nodekind sh:IRI ; - sh:pattern "/[a-zA-Z0-9\\-_]{4,}/[a-zA-Z0-9\\-_\\.]{1,}/[a-zA-Z0-9\\-_\\.]{1,}/[a-zA-Z0-9\\-_\\.]{1,}$" ; - sh:message "IRI for databus:Version must match /USER/GROUP/ARTIFACT/VERSION , |USER|>3"@en ; + sh:pattern "^[\\w+.-]+:\\/\\/[\\w+.:-]+\\/[\\w+.-]{4,}(?:\\/[\\w+.-]{3,}){3,3}$" ; + sh:message "IRI for databus:Version must be a 4-segment URI and match ^[\\w+.-]+:\\/\\/[\\w+.:-]+\\/[\\w+.-]{4,}(?:\\/[\\w+.-]{3,}){3,3}$"@en ; ] . <#title-version> @@ -103,7 +104,13 @@ sh:maxCount 1 ; sh:nodeKind sh:IRI . - +<#was-derived-from> + a sh:PropertyShape ; + sh:targetClass databus:Version ; + sh:severity sh:Violation ; + sh:message "Value of prov:wasDerivedFrom from must be a valid IRI."@en ; + sh:path prov:wasDerivedFrom ; + sh:nodeKind sh:IRI . @@ -122,7 +129,7 @@ a sh:NodeShape; sh:targetClass databus:Version ; sh:sparql [ - sh:message "Dataset URI must contain the group URI of the associated group." ; + sh:message "Version URI must contain the group URI of the associated group." ; sh:prefixes databus: ; sh:select """ SELECT $this ?group diff --git a/model/group.php b/model/group.php index aaeaf287..826f0302 100644 --- a/model/group.php +++ b/model/group.php @@ -30,8 +30,8 @@ sh:property [ sh:path [ sh:inversePath rdf:type ] ; sh:nodekind sh:IRI ; - sh:pattern "/[a-zA-Z0-9\\\\-_]{4,}/[a-zA-Z0-9\\\\-_\\\\.]{3,}$" ; - sh:message "IRI for databus:Group must match /[a-zA-Z0-9\\\\-_]{4,}/[a-zA-Z0-9\\\\-_\\\\.]{3,}$"@en ; + sh:pattern "^[\\\\w+.-]+:\\\\/\\\\/[\\\\w+.:-]+\\\\/[\\\\w+.-]{4,}\\\\/[\\\\w+.-]{3,}$" ; + sh:message "IRI for databus:Group must be a 2-segment URI and match ^[\\\\w+.-]+:\\\\/\\\\/[\\\\w+.:-]+\\\\/[\\\\w+.-]{4,}\\\\/[\\\\w+.-]{3,}$"@en ; ] .'; $example='"@type": "Group",'; @@ -67,9 +67,10 @@ sh:qualifiedValueShape [ sh:datatype xsd:string ] ; sh:qualifiedMaxCount 1 ; ] ; - sh:property [ + sh:property [ sh:path dct:title ; sh:severity sh:Violation ; + sh:maxLength 300 ; sh:message "dct:title can be used with language tag, but each language only once."@en ; sh:uniqueLang true ; ] . '; diff --git a/model/version.php b/model/version.php index e987300a..c7e73a05 100644 --- a/model/version.php +++ b/model/version.php @@ -40,14 +40,15 @@ sh:property [ sh:path [ sh:inversePath rdf:type ] ; sh:nodekind sh:IRI ; - sh:pattern "/[a-zA-Z0-9\\\\-_]{4,}/[a-zA-Z0-9\\\\-_\\\\.]{1,}/[a-zA-Z0-9\\\\-_\\\\.]{1,}/[a-zA-Z0-9\\\\-_\\\\.]{1,}$" ; - sh:message "IRI for databus:Version must match /USER/GROUP/ARTIFACT/VERSION , |USER|>3"@en ; + sh:pattern "^[\\\\w+.-]+:\\\\/\\\\/[\\\\w+.:-]+\\\\/[\\\\w+.-]{4,}(?:\\\\/[\\\\w+.-]{3,}){3,3}$" ; + sh:message "IRI for databus:Version must be a 4-segment URI and match ^[\\\\w+.-]+:\\\\/\\\\/[\\\\w+.:-]+\\\\/[\\\\w+.-]{4,}(?:\\\\/[\\\\w+.-]{3,}){3,3}$"@en ; ] . '; $example='"@type": "databus:Version",'; -$context='"Version": "databus:Version" '; +$context='"Version": "databus:Version", +"Dataset": "dcat:Dataset" '; table($section,$sectionExampleURI,$owl,$shacl,$example,$context); ?> @@ -265,7 +266,13 @@ rdfs:range prov:Entity . '; -$shacl=''; +$shacl='<#was-derived-from> + a sh:PropertyShape ; + sh:targetClass databus:Version ; + sh:severity sh:Violation ; + sh:message "Value of prov:wasDerivedFrom from must be a valid IRI."@en ; + sh:path prov:wasDerivedFrom ; + sh:nodeKind sh:IRI .'; $example='"wasDerivedFrom": "https://databus.dbpedia.org/dbpedia/generic/labels/2022.09.01",'; @@ -356,7 +363,7 @@ a sh:NodeShape; sh:targetClass databus:Version ; sh:sparql [ - sh:message "Dataset URI must contain the group URI of the associated group." ; + sh:message "Version URI must contain the group URI of the associated group." ; sh:prefixes databus: ; sh:select """ SELECT $this ?group diff --git a/public/css/_colors.scss b/public/css/_colors.scss index 8cb5d07b..e8623cba 100644 --- a/public/css/_colors.scss +++ b/public/css/_colors.scss @@ -1,4 +1,23 @@ // COLOR PALETTE + +// Navbar +$nav-background-dark: #353e47; +$nav-background-medium: #3d4853; +$nav-search-border: #fff; +$nav-links: #c4c4c4; +$nav-search-background: $nav-background-medium; + +// Action Banner +$banner-background: #e6e7ec; +$banner-box-frame: #fff; +$banner-box-background: #366281; +$banner-box-background-hover: rgb(255 255 255 / 13%); + +// Backgrounds +$background-white: #fff; +$background-light: #fff; + + $link: #3867d6; $highlight: #f5f5f5; @@ -9,7 +28,6 @@ $icon-color: #b5b5b5; $yasqe-disabled-background: $background; $yasqe-disabled-border: #e6e6e6; -$navbar-background: #24292e; $topic-header: #7a7a7a; $grey-light: #dbdbdb; @@ -29,13 +47,13 @@ $primary: #65bdb2; $entity-artifact: #66a0c9; $entity-artifact-bg: #7BA0BB; -$entity-consumer: #737381; -$entity-consumer-bg: #7e7e8a; +$entity-consumer: #29acbf; +$entity-consumer-bg: #28b0c5; $entity-group: #6e7699; $entity-group-bg: #7B89AB; -$entity-version: #76bfb6; +$entity-version: #47b49f; $entity-version-bg: #89BDB7; $entity-collection: #81b180; diff --git a/public/css/_components.scss b/public/css/_components.scss index f42657e4..1d261c65 100644 --- a/public/css/_components.scss +++ b/public/css/_components.scss @@ -1,5 +1,27 @@ // Hoverable Icon +.fill { + position: absolute; + top: 0; + bottom: 0; + right: 0; + left: 0; +} + +.center-content { + display: flex; + align-items: center; + justify-content: center; +} + +.databus-ribbon { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + background-color: $banner-background; +} + .hover-icon::before { content: ''; position: absolute; @@ -122,6 +144,10 @@ .entity-card { + padding: 1.1em; + border: 1px solid #dbdbdb; + border-radius: 8px; + .entity-card-icon { width: auto; height: 52px; @@ -133,11 +159,22 @@ align-items: center; } + .entity-card-header .title { + font-size: 1.2em; + margin-bottom: 4px; + } + + .entity-card-header uri-breadcrumbs { + font-size: 0.9em; + } + .entity-card-content { margin-top: 0.5em; } .entity-card-footer { + font-size: .8em; + color: #626568; margin-top: 0.5em; } } @@ -148,7 +185,7 @@ background-repeat: no-repeat; background-size: contain; text-align: center; - min-height: 200px; + height: 160px; .title { color: white; @@ -207,9 +244,6 @@ // NAVBAR -.navbar-db { - background-color: $navbar-background; -} // Settings and boxes diff --git a/public/css/_entities.scss b/public/css/_entities.scss index 0077c959..be56167a 100644 --- a/public/css/_entities.scss +++ b/public/css/_entities.scss @@ -69,6 +69,13 @@ .is-version { + .databus-icon svg { + fill: $entity-version; + &.is-white { + fill: $white; + } + } + path { fill: $entity-version; @@ -77,6 +84,12 @@ } } + &.button { + background-color: $entity-version; + color: $white; + border: none; + } + &.title { color: $entity-version; } @@ -101,14 +114,14 @@ } .is-consumer { - - path { + + .databus-icon svg { fill: $entity-consumer; &.is-white { fill: $white; } } - + &.button { background-color: $entity-consumer; color: $white; diff --git a/public/css/_fonts.scss b/public/css/_fonts.scss new file mode 100644 index 00000000..2b30984b --- /dev/null +++ b/public/css/_fonts.scss @@ -0,0 +1,47 @@ +@font-face { + font-family: 'PT Sans'; + src: url('./../fonts/PTSans-Regular.ttf'); + } + + @font-face { + font-family: "PT Sans"; + src: url("./../fonts/PTSans-Bold.ttf"); + font-weight: bold; + } + + @font-face { + font-family: "PT Sans"; + src: url("./../fonts/PTSans-Italic.ttf"); + font-style: italic; + } + + @font-face { + font-family: "PT Sans"; + src: url("./../fonts/PTSans-BoldItalic.ttf"); + font-weight: bold; + font-style: italic; + } + + @font-face { + font-family: 'Roboto'; + src: url('./../fonts/Roboto-Regular.ttf'); + } + + @font-face { + font-family: 'Roboto'; + src: url('./../fonts/Roboto-Bold.ttf'); + font-weight: bold; + } + + @font-face { + font-family: 'Inter'; + src: url('./../fonts/Inter-Regular.ttf'); + } + + + + @font-face { + font-family: 'Inter'; + src: url('./../fonts/Inter-SemiBold.ttf'); + font-weight: 600; + } \ No newline at end of file diff --git a/public/css/_frontpage.scss b/public/css/_frontpage.scss new file mode 100644 index 00000000..33744820 --- /dev/null +++ b/public/css/_frontpage.scss @@ -0,0 +1,90 @@ +.section.call-to-action { + background-color: $banner-background; + padding: 3em 0em; + position: relative; +} + +.frontpage-box-frame { + + background-color: $banner-box-frame; + border-radius: 20px; + padding: 0px; + box-shadow: 0px 0px 9px rgba(0, 0, 0, 0.05); + +} + +.frontpage-box { + background-color: $banner-box-background; + border-radius: 20px; + display: flex; + color: white; + width: 380px; + padding: 1.2em; + position: relative; + align-items: center; + cursor: pointer; +} + +.frontpage-box.left { + background-color: $entity-consumer-bg; +} + + +.frontpage-box.center { + background-color: $entity-version; +} + + +$frontpage-box-scale-time: 0.4s; +$frontpage-box-scale-amount: -4px; + +.frontpage-box:hover { + -moz-transform: translateY($frontpage-box-scale-amount); + -webkit-transform: translateY($frontpage-box-scale-amount); + -o-transform: translateY($frontpage-box-scale-amount); + -ms-transform: translateY($frontpage-box-scale-amount); + -webkit-transform: translateY($frontpage-box-scale-amount); + transform: translateY($frontpage-box-scale-amount); + + -webkit-transition: transform $frontpage-box-scale-time ease-out; + -moz-transition: transform $frontpage-box-scale-time ease-out; + -ms-transition: transform $frontpage-box-scale-time ease-out; +} + +.frontpage-box-arrow { + position: absolute; + right: 1em; + top: 1em; +} + +.frontpage-box-frame:not(:last-child) { + margin-right: 2em; +} + +.frontpage-box .content { + padding: 0; + margin-left: 1em; +} + +.frontpage-box .content h1 { + margin-bottom: 4px; + color: white; + font-weight: 600; +} + +.frontpage-box .headline databus-icon { + margin-right: .7em; +} + + +.frontpage-box h1 { + font-size: 1.2em; + margin-top: 0; +} + +.has-background-pattern { + background-image: url(../img/banner-background.png); + background-repeat: repeat; + background-position: center; + background-size: 150px; +} \ No newline at end of file diff --git a/public/css/_nav.scss b/public/css/_nav.scss new file mode 100644 index 00000000..998cad97 --- /dev/null +++ b/public/css/_nav.scss @@ -0,0 +1,355 @@ +// Bar and gap +.databus-navbar { + height: 78px; + + + // Fixed container + .databus-navbar-fixed { + z-index: 8000; + height: 78px; + align-items: center; + background-color: $nav-background-dark; + position: fixed; + top: 0; + left: 0; + right: 0; + box-shadow: 0px 0px 4px 4px rgba(0, 0, 0, 0.1); + } + + // Content flexbox + .databus-navbar-container { + display: flex; + height: inherit; + justify-content: stretch; + align-items: center; + width: 100%; + padding: 0em 2em; + } + + @media screen and (min-width: 1500px), + print { + .databus-navbar-container { + max-width: 1500px; + margin: auto; + } + } + + // Logo section + .databus-navbar-brand { + display: flex; + margin-left: 1.5em; + } + + // Content section + .databus-navbar-content { + display: flex !important; + height: inherit; + position: relative; + flex: 1; + } + + // Home navigation + .databus-home-icon { + display: flex; + align-items: center; + } + + // Organization icon + .databus-home-icon .org-icon { + max-width: 200px; + max-height: 36px; + min-height: 1.5em; + } + + // Vertical line + .databus-home-icon .org-separator { + width: 2px; + height: 20px; + background-color: white; + margin: 0em 1em; + } + + // "DATABUS" + .databus-home-icon .org-subtitle { + font-size: 1.2em; + color: white; + margin-right: 2em; + letter-spacing: 1px; + } + + // Navbar content item + .databus-navbar-item { + display: flex; + align-items: center; + height: inherit; + + &.fill { + flex-grow: 1; + position: relative; + } + + &.end { + margin-left: 1em; + } + + &.account-name { + cursor: pointer; + padding-right: 1em; + padding-left: 1em; + margin-right: .5em; + color: white; + } + + &.has-dropdown { + cursor: pointer; + + padding: 0em .75em; + color: white; + + &:hover { + background-color: $nav-background-medium; + } + } + + &.link { + cursor: pointer; + color: $nav-links; + padding: 0em .75em; + + &:hover { + background-color: $nav-background-medium; + color: white; + } + + .icon { + margin-left: 0; + height: initial; + width: initial; + margin-right: .5em; + } + } + + } + + .databus-navbar-dropdown { + min-width: 200px; + font-size: 1em; + + .navbar-dropdown-item { + min-width: 200px; + font-size: 1em; + } + } + + // Search + .nav-search { + flex: 1; + + // Result list + .search-results { + position: absolute; + background-color: $nav-background-medium; + box-shadow: 0px 0px 4px 4px rgba(0, 0, 0, .1); + min-width: 100%; + max-width: 2000px; + z-index: 1000; + top: 100%; + } + + + input { + background-color: $nav-search-background; + border-color: #96a3b2; + outline: none; + color: white; + padding: 1.2em; + border-radius: 8px; + } + + input::placeholder { + color: #b2becd; + opacity: 1; + /* Firefox */ + } + + .icon { + top: 2px !important; + } + + .nav-search input:focus { + border-color: #c3d2e4; + box-shadow: none; + } + + + .results { + max-height: 60vh; + overflow-y: auto; + + .result-item { + padding: .35em .7em; + display: flex; + align-items: center; + cursor: pointer; + overflow: hidden; + + &:hover { + background-color: #4e5b6a; + } + + &:first-child { + padding-top: .7em; + } + + &:last-child { + padding-bottom: .7em; + } + + uri-breadcrumbs { + color: #b9b9b9 !important; + } + + uri-breadcrumbs a { + color: #b9b9b9 !important; + } + + uri-breadcrumbs a:hover { + color: #e7e7e7 !important; + } + + .item-header { + display: box; + color: white + } + } + + &::-webkit-scrollbar { + width: 8px; + } + + /* Track */ + &::-webkit-scrollbar-track { + background: #353b45; + } + + /* Handle */ + &::-webkit-scrollbar-thumb { + background: #b6b6b6; + } + + /* Handle on hover */ + &::-webkit-scrollbar-thumb:hover { + background: #a3a3a3; + } + + + + } + + + .result-header { + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 2px solid #f5f5f5; + padding: .5em 1em; + color: white; + font-weight: 600; + } + + .filters { + display: flex; + align-items: center; + line-height: 1; + } + + .filters p { + font-weight: 600; + color: white; + margin-right: .6em; + } + + .filters .grey-out-dark .tag { + background-color: $nav-background-medium; + } + + .filters .grey-out-dark path { + fill: #a7a7a7; + } + + .result-item type-tag { + margin-right: 12px; + } + + .result-item .item-label { + font-weight: 600; + margin-right: 8px; + margin-bottom: -4px; + } + + .result-item .item-abstract { + color: white; + width: 620px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + + .item-header a { + color: white; + } + } + + .navbar-account-icon { + height: 48px; + width: 48px; + background-color: white; + border-radius: 24px; + margin-left: .5em; + } + + +} + + + + + +.databus-navbar-item.is-light:hover { + opacity: 1; +} + + + + +.databus-navbar-item.has-dropdown .databus-navbar-dropdown { + display: none; +} + +.databus-navbar-item.has-dropdown.is-active .databus-navbar-dropdown { + display: block; +} + +.databus-navbar-dropdown { + position: absolute; + top: 100%; + right: 0; + background-color: $nav-background-medium; + padding-bottom: 8px; + min-width: 250px; +} + +.databus-navbar-dropdown-arrow { + margin-left: 8px; +} + +.databus-navbar-item.has-dropdown:hover .databus-navbar-dropdown-arrow { + opacity: 0.7; +} + +@media screen and (max-width: 1024px), +print { + .databus-navbar-group.is-hidden-for-mobile { + display: none; + } +} \ No newline at end of file diff --git a/public/css/_sparql.scss b/public/css/_sparql.scss new file mode 100644 index 00000000..cd5de710 --- /dev/null +++ b/public/css/_sparql.scss @@ -0,0 +1,31 @@ + +.yasqe .yasqe_buttons { + right: 16px !important; + top: 16px !important; +} + +.yasqe .yasqe_buttons .yasqe_share { + display: none; +} + +.yasqe .yasqe_buttons .yasqe_queryButton { + margin-right: .2em; + margin-top: .2em; +} + +.error-box { + background-color: #ffdbdb; + color:#f13232; + border: 1px solid #f13232; + padding: 1em; + white-space: pre-line; + display: grid; +} + +.error-box h1 { + font-weight: 600; + padding: 0; + margin: 0; + margin-bottom: 1em; + font-size: 1em; +} \ No newline at end of file diff --git a/public/css/website.css b/public/css/website.css index 6357bbb0..ba26dfbd 100644 --- a/public/css/website.css +++ b/public/css/website.css @@ -20,6 +20,24 @@ font-weight: bold; font-style: italic; } +@font-face { + font-family: "Roboto"; + src: url("./../fonts/Roboto-Regular.ttf"); +} +@font-face { + font-family: "Roboto"; + src: url("./../fonts/Roboto-Bold.ttf"); + font-weight: bold; +} +@font-face { + font-family: "Inter"; + src: url("./../fonts/Inter-Regular.ttf"); +} +@font-face { + font-family: "Inter"; + src: url("./../fonts/Inter-SemiBold.ttf"); + font-weight: 600; +} .collection-summary.table { background-color: transparent; } @@ -311,7 +329,7 @@ .modal-close, .delete { -moz-appearance: none; -webkit-appearance: none; - background-color: rgba(10, 10, 10, 0.2); + background-color: rgba(10.2, 10.2, 10.2, 0.2); border: none; border-radius: 290486px; cursor: pointer; @@ -349,10 +367,10 @@ width: 2px; } .modal-close:hover, .delete:hover, .modal-close:focus, .delete:focus { - background-color: rgba(10, 10, 10, 0.3); + background-color: rgba(10.2, 10.2, 10.2, 0.3); } .modal-close:active, .delete:active { - background-color: rgba(10, 10, 10, 0.4); + background-color: rgba(10.2, 10.2, 10.2, 0.4); } .is-small.modal-close, .is-small.delete { height: 16px; @@ -1088,7 +1106,7 @@ table th { } a.has-text-white:hover, a.has-text-white:focus { - color: #e6e6e6 !important; + color: rgb(229.5, 229.5, 229.5) !important; } .has-background-white { @@ -1100,7 +1118,7 @@ a.has-text-white:hover, a.has-text-white:focus { } a.has-text-black:hover, a.has-text-black:focus { - color: black !important; + color: hsl(0, 0%, 0%) !important; } .has-background-black { @@ -1112,7 +1130,7 @@ a.has-text-black:hover, a.has-text-black:focus { } a.has-text-light:hover, a.has-text-light:focus { - color: #dbdbdb !important; + color: hsl(0, 0%, 86%) !important; } .has-background-light { @@ -1124,7 +1142,7 @@ a.has-text-light:hover, a.has-text-light:focus { } a.has-text-dark:hover, a.has-text-dark:focus { - color: #1c1c1c !important; + color: hsl(0, 0%, 11%) !important; } .has-background-dark { @@ -1136,7 +1154,7 @@ a.has-text-dark:hover, a.has-text-dark:focus { } a.has-text-primary:hover, a.has-text-primary:focus { - color: #48a79b !important; + color: rgb(71.7, 167.3, 155.35) !important; } .has-background-primary { @@ -1148,7 +1166,7 @@ a.has-text-primary:hover, a.has-text-primary:focus { } a.has-text-link:hover, a.has-text-link:focus { - color: #2550b6 !important; + color: rgb(37.4125, 80.3, 181.5875) !important; } .has-background-link { @@ -1160,7 +1178,7 @@ a.has-text-link:hover, a.has-text-link:focus { } a.has-text-info:hover, a.has-text-info:focus { - color: #0f81cc !important; + color: hsl(204, 86%, 43%) !important; } .has-background-info { @@ -1172,7 +1190,7 @@ a.has-text-info:hover, a.has-text-info:focus { } a.has-text-success:hover, a.has-text-success:focus { - color: #1ca64c !important; + color: hsl(141, 71%, 38%) !important; } .has-background-success { @@ -1184,7 +1202,7 @@ a.has-text-success:hover, a.has-text-success:focus { } a.has-text-warning:hover, a.has-text-warning:focus { - color: #ffd324 !important; + color: hsl(48, 100%, 57%) !important; } .has-background-warning { @@ -1196,7 +1214,7 @@ a.has-text-warning:hover, a.has-text-warning:focus { } a.has-text-danger:hover, a.has-text-danger:focus { - color: #ff0537 !important; + color: hsl(348, 100%, 51%) !important; } .has-background-danger { @@ -1692,17 +1710,17 @@ a.has-text-danger:hover, a.has-text-danger:focus { .box { background-color: #fff; border-radius: 6px; - box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); + box-shadow: 0 2px 3px rgba(10.2, 10.2, 10.2, 0.1), 0 0 0 1px rgba(10.2, 10.2, 10.2, 0.1); color: hsl(0, 0%, 29%); display: block; padding: 1.25rem; } a.box:hover, a.box:focus { - box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px #3867d6; + box-shadow: 0 2px 3px rgba(10.2, 10.2, 10.2, 0.1), 0 0 0 1px #3867d6; } a.box:active { - box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.2), 0 0 0 1px #3867d6; + box-shadow: inset 0 1px 2px rgba(10.2, 10.2, 10.2, 0.2), 0 0 0 1px #3867d6; } .button { @@ -1764,7 +1782,7 @@ a.box:active { color: hsl(0, 0%, 21%); } .button.is-text:active, .button.is-text.is-active { - background-color: #e8e8e8; + background-color: rgb(232.25, 232.25, 232.25); color: hsl(0, 0%, 21%); } .button.is-text[disabled], fieldset[disabled] .button.is-text { @@ -1778,7 +1796,7 @@ a.box:active { color: hsl(0, 0%, 4%); } .button.is-white:hover, .button.is-white.is-hovered { - background-color: #f9f9f9; + background-color: rgb(248.625, 248.625, 248.625); border-color: transparent; color: hsl(0, 0%, 4%); } @@ -1790,7 +1808,7 @@ a.box:active { box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25); } .button.is-white:active, .button.is-white.is-active { - background-color: #f2f2f2; + background-color: rgb(242.25, 242.25, 242.25); border-color: transparent; color: hsl(0, 0%, 4%); } @@ -1804,7 +1822,7 @@ a.box:active { color: #fff; } .button.is-white.is-inverted:hover, .button.is-white.is-inverted.is-hovered { - background-color: black; + background-color: hsl(0, 0%, 0%); } .button.is-white.is-inverted[disabled], fieldset[disabled] .button.is-white.is-inverted { background-color: hsl(0, 0%, 4%); @@ -1861,7 +1879,7 @@ a.box:active { color: #fff; } .button.is-black:hover, .button.is-black.is-hovered { - background-color: #040404; + background-color: hsl(0, 0%, 1.5%); border-color: transparent; color: #fff; } @@ -1870,10 +1888,10 @@ a.box:active { color: #fff; } .button.is-black:focus:not(:active), .button.is-black.is-focused:not(:active) { - box-shadow: 0 0 0 0.125em rgba(10, 10, 10, 0.25); + box-shadow: 0 0 0 0.125em rgba(10.2, 10.2, 10.2, 0.25); } .button.is-black:active, .button.is-black.is-active { - background-color: black; + background-color: hsl(0, 0%, 0%); border-color: transparent; color: #fff; } @@ -1887,7 +1905,7 @@ a.box:active { color: hsl(0, 0%, 4%); } .button.is-black.is-inverted:hover, .button.is-black.is-inverted.is-hovered { - background-color: #f2f2f2; + background-color: rgb(242.25, 242.25, 242.25); } .button.is-black.is-inverted[disabled], fieldset[disabled] .button.is-black.is-inverted { background-color: #fff; @@ -1944,7 +1962,7 @@ a.box:active { color: hsl(0, 0%, 21%); } .button.is-light:hover, .button.is-light.is-hovered { - background-color: #eeeeee; + background-color: hsl(0, 0%, 93.5%); border-color: transparent; color: hsl(0, 0%, 21%); } @@ -1953,10 +1971,10 @@ a.box:active { color: hsl(0, 0%, 21%); } .button.is-light:focus:not(:active), .button.is-light.is-focused:not(:active) { - box-shadow: 0 0 0 0.125em rgba(245, 245, 245, 0.25); + box-shadow: 0 0 0 0.125em rgba(244.8, 244.8, 244.8, 0.25); } .button.is-light:active, .button.is-light.is-active { - background-color: #e8e8e8; + background-color: hsl(0, 0%, 91%); border-color: transparent; color: hsl(0, 0%, 21%); } @@ -1970,7 +1988,7 @@ a.box:active { color: hsl(0, 0%, 96%); } .button.is-light.is-inverted:hover, .button.is-light.is-inverted.is-hovered { - background-color: #292929; + background-color: hsl(0, 0%, 16%); } .button.is-light.is-inverted[disabled], fieldset[disabled] .button.is-light.is-inverted { background-color: hsl(0, 0%, 21%); @@ -2027,7 +2045,7 @@ a.box:active { color: hsl(0, 0%, 96%); } .button.is-dark:hover, .button.is-dark.is-hovered { - background-color: #2f2f2f; + background-color: hsl(0, 0%, 18.5%); border-color: transparent; color: hsl(0, 0%, 96%); } @@ -2036,10 +2054,10 @@ a.box:active { color: hsl(0, 0%, 96%); } .button.is-dark:focus:not(:active), .button.is-dark.is-focused:not(:active) { - box-shadow: 0 0 0 0.125em rgba(54, 54, 54, 0.25); + box-shadow: 0 0 0 0.125em rgba(53.55, 53.55, 53.55, 0.25); } .button.is-dark:active, .button.is-dark.is-active { - background-color: #292929; + background-color: hsl(0, 0%, 16%); border-color: transparent; color: hsl(0, 0%, 96%); } @@ -2053,7 +2071,7 @@ a.box:active { color: hsl(0, 0%, 21%); } .button.is-dark.is-inverted:hover, .button.is-dark.is-inverted.is-hovered { - background-color: #e8e8e8; + background-color: hsl(0, 0%, 91%); } .button.is-dark.is-inverted[disabled], fieldset[disabled] .button.is-dark.is-inverted { background-color: hsl(0, 0%, 96%); @@ -2110,7 +2128,7 @@ a.box:active { color: #fff; } .button.is-primary:hover, .button.is-primary.is-hovered { - background-color: #5cb9ae; + background-color: rgb(92.075, 185.175, 173.5375); border-color: transparent; color: #fff; } @@ -2122,7 +2140,7 @@ a.box:active { box-shadow: 0 0 0 0.125em rgba(101, 189, 178, 0.25); } .button.is-primary:active, .button.is-primary.is-active { - background-color: #53b5a9; + background-color: rgb(83.15, 181.35, 169.075); border-color: transparent; color: #fff; } @@ -2136,7 +2154,7 @@ a.box:active { color: #65bdb2; } .button.is-primary.is-inverted:hover, .button.is-primary.is-inverted.is-hovered { - background-color: #f2f2f2; + background-color: rgb(242.25, 242.25, 242.25); } .button.is-primary.is-inverted[disabled], fieldset[disabled] .button.is-primary.is-inverted { background-color: #fff; @@ -2193,7 +2211,7 @@ a.box:active { color: #fff; } .button.is-link:hover, .button.is-link.is-hovered { - background-color: #2d5fd4; + background-color: rgb(45.428125, 94.925, 211.821875); border-color: transparent; color: #fff; } @@ -2205,7 +2223,7 @@ a.box:active { box-shadow: 0 0 0 0.125em rgba(56, 103, 214, 0.25); } .button.is-link:active, .button.is-link.is-active { - background-color: #2a5acb; + background-color: rgb(41.76875, 89.65, 202.73125); border-color: transparent; color: #fff; } @@ -2219,7 +2237,7 @@ a.box:active { color: #3867d6; } .button.is-link.is-inverted:hover, .button.is-link.is-inverted.is-hovered { - background-color: #f2f2f2; + background-color: rgb(242.25, 242.25, 242.25); } .button.is-link.is-inverted[disabled], fieldset[disabled] .button.is-link.is-inverted { background-color: #fff; @@ -2276,7 +2294,7 @@ a.box:active { color: #fff; } .button.is-info:hover, .button.is-info.is-hovered { - background-color: #1496ed; + background-color: hsl(204, 86%, 50.5%); border-color: transparent; color: #fff; } @@ -2285,10 +2303,10 @@ a.box:active { color: #fff; } .button.is-info:focus:not(:active), .button.is-info.is-focused:not(:active) { - box-shadow: 0 0 0 0.125em rgba(32, 156, 238, 0.25); + box-shadow: 0 0 0 0.125em rgba(32.079, 155.7642, 238.221, 0.25); } .button.is-info:active, .button.is-info.is-active { - background-color: #118fe4; + background-color: hsl(204, 86%, 48%); border-color: transparent; color: #fff; } @@ -2302,7 +2320,7 @@ a.box:active { color: hsl(204, 86%, 53%); } .button.is-info.is-inverted:hover, .button.is-info.is-inverted.is-hovered { - background-color: #f2f2f2; + background-color: rgb(242.25, 242.25, 242.25); } .button.is-info.is-inverted[disabled], fieldset[disabled] .button.is-info.is-inverted { background-color: #fff; @@ -2359,7 +2377,7 @@ a.box:active { color: #fff; } .button.is-success:hover, .button.is-success.is-hovered { - background-color: #22c65b; + background-color: hsl(141, 71%, 45.5%); border-color: transparent; color: #fff; } @@ -2368,10 +2386,10 @@ a.box:active { color: #fff; } .button.is-success:focus:not(:active), .button.is-success.is-focused:not(:active) { - box-shadow: 0 0 0 0.125em rgba(35, 209, 96, 0.25); + box-shadow: 0 0 0 0.125em rgba(35.496, 209.304, 96.3288, 0.25); } .button.is-success:active, .button.is-success.is-active { - background-color: #20bc56; + background-color: hsl(141, 71%, 43%); border-color: transparent; color: #fff; } @@ -2385,7 +2403,7 @@ a.box:active { color: hsl(141, 71%, 48%); } .button.is-success.is-inverted:hover, .button.is-success.is-inverted.is-hovered { - background-color: #f2f2f2; + background-color: rgb(242.25, 242.25, 242.25); } .button.is-success.is-inverted[disabled], fieldset[disabled] .button.is-success.is-inverted { background-color: #fff; @@ -2442,7 +2460,7 @@ a.box:active { color: rgba(0, 0, 0, 0.7); } .button.is-warning:hover, .button.is-warning.is-hovered { - background-color: #ffdb4a; + background-color: hsl(48, 100%, 64.5%); border-color: transparent; color: rgba(0, 0, 0, 0.7); } @@ -2451,10 +2469,10 @@ a.box:active { color: rgba(0, 0, 0, 0.7); } .button.is-warning:focus:not(:active), .button.is-warning.is-focused:not(:active) { - box-shadow: 0 0 0 0.125em rgba(255, 221, 87, 0.25); + box-shadow: 0 0 0 0.125em rgba(255, 221.34, 86.7, 0.25); } .button.is-warning:active, .button.is-warning.is-active { - background-color: #ffd83d; + background-color: hsl(48, 100%, 62%); border-color: transparent; color: rgba(0, 0, 0, 0.7); } @@ -2525,7 +2543,7 @@ a.box:active { color: #fff; } .button.is-danger:hover, .button.is-danger.is-hovered { - background-color: #ff2b56; + background-color: hsl(348, 100%, 58.5%); border-color: transparent; color: #fff; } @@ -2534,10 +2552,10 @@ a.box:active { color: #fff; } .button.is-danger:focus:not(:active), .button.is-danger.is-focused:not(:active) { - box-shadow: 0 0 0 0.125em rgba(255, 56, 96, 0.25); + box-shadow: 0 0 0 0.125em rgba(255, 56.1, 95.88, 0.25); } .button.is-danger:active, .button.is-danger.is-active { - background-color: #ff1f4b; + background-color: hsl(348, 100%, 56%); border-color: transparent; color: #fff; } @@ -2551,7 +2569,7 @@ a.box:active { color: hsl(348, 100%, 61%); } .button.is-danger.is-inverted:hover, .button.is-danger.is-inverted.is-hovered { - background-color: #f2f2f2; + background-color: rgb(242.25, 242.25, 242.25); } .button.is-danger.is-inverted[disabled], fieldset[disabled] .button.is-danger.is-inverted { background-color: #fff; @@ -3616,10 +3634,10 @@ a.box:active { width: 1px; } .tag:not(body).is-delete:hover, .tag:not(body).is-delete:focus { - background-color: #e8e8e8; + background-color: rgb(232.25, 232.25, 232.25); } .tag:not(body).is-delete:active { - background-color: gainsboro; + background-color: rgb(219.5, 219.5, 219.5); } .tag:not(body).is-rounded { border-radius: 290486px; @@ -3766,16 +3784,16 @@ a.tag:hover { color: hsl(0, 0%, 21%); } .select select::-moz-placeholder, .textarea::-moz-placeholder, .input::-moz-placeholder { - color: rgba(54, 54, 54, 0.3); + color: rgba(53.55, 53.55, 53.55, 0.3); } .select select::-webkit-input-placeholder, .textarea::-webkit-input-placeholder, .input::-webkit-input-placeholder { - color: rgba(54, 54, 54, 0.3); + color: rgba(53.55, 53.55, 53.55, 0.3); } .select select:-moz-placeholder, .textarea:-moz-placeholder, .input:-moz-placeholder { - color: rgba(54, 54, 54, 0.3); + color: rgba(53.55, 53.55, 53.55, 0.3); } .select select:-ms-input-placeholder, .textarea:-ms-input-placeholder, .input:-ms-input-placeholder { - color: rgba(54, 54, 54, 0.3); + color: rgba(53.55, 53.55, 53.55, 0.3); } .select select:hover, .textarea:hover, .input:hover, .select select.is-hovered, .is-hovered.textarea, .is-hovered.input { border-color: #dbdbdb; @@ -3791,20 +3809,20 @@ a.tag:hover { color: hsl(0, 0%, 48%); } .select select[disabled]::-moz-placeholder, [disabled].textarea::-moz-placeholder, [disabled].input::-moz-placeholder, fieldset[disabled] .select select::-moz-placeholder, .select fieldset[disabled] select::-moz-placeholder, fieldset[disabled] .textarea::-moz-placeholder, fieldset[disabled] .input::-moz-placeholder { - color: rgba(122, 122, 122, 0.3); + color: rgba(122.4, 122.4, 122.4, 0.3); } .select select[disabled]::-webkit-input-placeholder, [disabled].textarea::-webkit-input-placeholder, [disabled].input::-webkit-input-placeholder, fieldset[disabled] .select select::-webkit-input-placeholder, .select fieldset[disabled] select::-webkit-input-placeholder, fieldset[disabled] .textarea::-webkit-input-placeholder, fieldset[disabled] .input::-webkit-input-placeholder { - color: rgba(122, 122, 122, 0.3); + color: rgba(122.4, 122.4, 122.4, 0.3); } .select select[disabled]:-moz-placeholder, [disabled].textarea:-moz-placeholder, [disabled].input:-moz-placeholder, fieldset[disabled] .select select:-moz-placeholder, .select fieldset[disabled] select:-moz-placeholder, fieldset[disabled] .textarea:-moz-placeholder, fieldset[disabled] .input:-moz-placeholder { - color: rgba(122, 122, 122, 0.3); + color: rgba(122.4, 122.4, 122.4, 0.3); } .select select[disabled]:-ms-input-placeholder, [disabled].textarea:-ms-input-placeholder, [disabled].input:-ms-input-placeholder, fieldset[disabled] .select select:-ms-input-placeholder, .select fieldset[disabled] select:-ms-input-placeholder, fieldset[disabled] .textarea:-ms-input-placeholder, fieldset[disabled] .input:-ms-input-placeholder { - color: rgba(122, 122, 122, 0.3); + color: rgba(122.4, 122.4, 122.4, 0.3); } .textarea, .input { - box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.1); + box-shadow: inset 0 1px 2px rgba(10.2, 10.2, 10.2, 0.1); max-width: 100%; width: 100%; } @@ -3821,19 +3839,19 @@ a.tag:hover { border-color: hsl(0, 0%, 4%); } .is-black.textarea:focus, .is-black.input:focus, .is-black.is-focused.textarea, .is-black.is-focused.input, .is-black.textarea:active, .is-black.input:active, .is-black.is-active.textarea, .is-black.is-active.input { - box-shadow: 0 0 0 0.125em rgba(10, 10, 10, 0.25); + box-shadow: 0 0 0 0.125em rgba(10.2, 10.2, 10.2, 0.25); } .is-light.textarea, .is-light.input { border-color: hsl(0, 0%, 96%); } .is-light.textarea:focus, .is-light.input:focus, .is-light.is-focused.textarea, .is-light.is-focused.input, .is-light.textarea:active, .is-light.input:active, .is-light.is-active.textarea, .is-light.is-active.input { - box-shadow: 0 0 0 0.125em rgba(245, 245, 245, 0.25); + box-shadow: 0 0 0 0.125em rgba(244.8, 244.8, 244.8, 0.25); } .is-dark.textarea, .is-dark.input { border-color: hsl(0, 0%, 21%); } .is-dark.textarea:focus, .is-dark.input:focus, .is-dark.is-focused.textarea, .is-dark.is-focused.input, .is-dark.textarea:active, .is-dark.input:active, .is-dark.is-active.textarea, .is-dark.is-active.input { - box-shadow: 0 0 0 0.125em rgba(54, 54, 54, 0.25); + box-shadow: 0 0 0 0.125em rgba(53.55, 53.55, 53.55, 0.25); } .is-primary.textarea, .is-primary.input { border-color: #65bdb2; @@ -3851,25 +3869,25 @@ a.tag:hover { border-color: hsl(204, 86%, 53%); } .is-info.textarea:focus, .is-info.input:focus, .is-info.is-focused.textarea, .is-info.is-focused.input, .is-info.textarea:active, .is-info.input:active, .is-info.is-active.textarea, .is-info.is-active.input { - box-shadow: 0 0 0 0.125em rgba(32, 156, 238, 0.25); + box-shadow: 0 0 0 0.125em rgba(32.079, 155.7642, 238.221, 0.25); } .is-success.textarea, .is-success.input { border-color: hsl(141, 71%, 48%); } .is-success.textarea:focus, .is-success.input:focus, .is-success.is-focused.textarea, .is-success.is-focused.input, .is-success.textarea:active, .is-success.input:active, .is-success.is-active.textarea, .is-success.is-active.input { - box-shadow: 0 0 0 0.125em rgba(35, 209, 96, 0.25); + box-shadow: 0 0 0 0.125em rgba(35.496, 209.304, 96.3288, 0.25); } .is-warning.textarea, .is-warning.input { border-color: hsl(48, 100%, 67%); } .is-warning.textarea:focus, .is-warning.input:focus, .is-warning.is-focused.textarea, .is-warning.is-focused.input, .is-warning.textarea:active, .is-warning.input:active, .is-warning.is-active.textarea, .is-warning.is-active.input { - box-shadow: 0 0 0 0.125em rgba(255, 221, 87, 0.25); + box-shadow: 0 0 0 0.125em rgba(255, 221.34, 86.7, 0.25); } .is-danger.textarea, .is-danger.input { border-color: hsl(348, 100%, 61%); } .is-danger.textarea:focus, .is-danger.input:focus, .is-danger.is-focused.textarea, .is-danger.is-focused.input, .is-danger.textarea:active, .is-danger.input:active, .is-danger.is-active.textarea, .is-danger.is-active.input { - box-shadow: 0 0 0 0.125em rgba(255, 56, 96, 0.25); + box-shadow: 0 0 0 0.125em rgba(255, 56.1, 95.88, 0.25); } .is-small.textarea, .is-small.input { border-radius: 2px; @@ -3993,7 +4011,7 @@ a.tag:hover { border-color: #fff; } .select.is-white select:hover, .select.is-white select.is-hovered { - border-color: #f2f2f2; + border-color: rgb(242.25, 242.25, 242.25); } .select.is-white select:focus, .select.is-white select.is-focused, .select.is-white select:active, .select.is-white select.is-active { box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25); @@ -4005,10 +4023,10 @@ a.tag:hover { border-color: hsl(0, 0%, 4%); } .select.is-black select:hover, .select.is-black select.is-hovered { - border-color: black; + border-color: hsl(0, 0%, 0%); } .select.is-black select:focus, .select.is-black select.is-focused, .select.is-black select:active, .select.is-black select.is-active { - box-shadow: 0 0 0 0.125em rgba(10, 10, 10, 0.25); + box-shadow: 0 0 0 0.125em rgba(10.2, 10.2, 10.2, 0.25); } .select.is-light:not(:hover)::after { border-color: hsl(0, 0%, 96%); @@ -4017,10 +4035,10 @@ a.tag:hover { border-color: hsl(0, 0%, 96%); } .select.is-light select:hover, .select.is-light select.is-hovered { - border-color: #e8e8e8; + border-color: hsl(0, 0%, 91%); } .select.is-light select:focus, .select.is-light select.is-focused, .select.is-light select:active, .select.is-light select.is-active { - box-shadow: 0 0 0 0.125em rgba(245, 245, 245, 0.25); + box-shadow: 0 0 0 0.125em rgba(244.8, 244.8, 244.8, 0.25); } .select.is-dark:not(:hover)::after { border-color: hsl(0, 0%, 21%); @@ -4029,10 +4047,10 @@ a.tag:hover { border-color: hsl(0, 0%, 21%); } .select.is-dark select:hover, .select.is-dark select.is-hovered { - border-color: #292929; + border-color: hsl(0, 0%, 16%); } .select.is-dark select:focus, .select.is-dark select.is-focused, .select.is-dark select:active, .select.is-dark select.is-active { - box-shadow: 0 0 0 0.125em rgba(54, 54, 54, 0.25); + box-shadow: 0 0 0 0.125em rgba(53.55, 53.55, 53.55, 0.25); } .select.is-primary:not(:hover)::after { border-color: #65bdb2; @@ -4041,7 +4059,7 @@ a.tag:hover { border-color: #65bdb2; } .select.is-primary select:hover, .select.is-primary select.is-hovered { - border-color: #53b5a9; + border-color: rgb(83.15, 181.35, 169.075); } .select.is-primary select:focus, .select.is-primary select.is-focused, .select.is-primary select:active, .select.is-primary select.is-active { box-shadow: 0 0 0 0.125em rgba(101, 189, 178, 0.25); @@ -4053,7 +4071,7 @@ a.tag:hover { border-color: #3867d6; } .select.is-link select:hover, .select.is-link select.is-hovered { - border-color: #2a5acb; + border-color: rgb(41.76875, 89.65, 202.73125); } .select.is-link select:focus, .select.is-link select.is-focused, .select.is-link select:active, .select.is-link select.is-active { box-shadow: 0 0 0 0.125em rgba(56, 103, 214, 0.25); @@ -4065,10 +4083,10 @@ a.tag:hover { border-color: hsl(204, 86%, 53%); } .select.is-info select:hover, .select.is-info select.is-hovered { - border-color: #118fe4; + border-color: hsl(204, 86%, 48%); } .select.is-info select:focus, .select.is-info select.is-focused, .select.is-info select:active, .select.is-info select.is-active { - box-shadow: 0 0 0 0.125em rgba(32, 156, 238, 0.25); + box-shadow: 0 0 0 0.125em rgba(32.079, 155.7642, 238.221, 0.25); } .select.is-success:not(:hover)::after { border-color: hsl(141, 71%, 48%); @@ -4077,10 +4095,10 @@ a.tag:hover { border-color: hsl(141, 71%, 48%); } .select.is-success select:hover, .select.is-success select.is-hovered { - border-color: #20bc56; + border-color: hsl(141, 71%, 43%); } .select.is-success select:focus, .select.is-success select.is-focused, .select.is-success select:active, .select.is-success select.is-active { - box-shadow: 0 0 0 0.125em rgba(35, 209, 96, 0.25); + box-shadow: 0 0 0 0.125em rgba(35.496, 209.304, 96.3288, 0.25); } .select.is-warning:not(:hover)::after { border-color: hsl(48, 100%, 67%); @@ -4089,10 +4107,10 @@ a.tag:hover { border-color: hsl(48, 100%, 67%); } .select.is-warning select:hover, .select.is-warning select.is-hovered { - border-color: #ffd83d; + border-color: hsl(48, 100%, 62%); } .select.is-warning select:focus, .select.is-warning select.is-focused, .select.is-warning select:active, .select.is-warning select.is-active { - box-shadow: 0 0 0 0.125em rgba(255, 221, 87, 0.25); + box-shadow: 0 0 0 0.125em rgba(255, 221.34, 86.7, 0.25); } .select.is-danger:not(:hover)::after { border-color: hsl(348, 100%, 61%); @@ -4101,10 +4119,10 @@ a.tag:hover { border-color: hsl(348, 100%, 61%); } .select.is-danger select:hover, .select.is-danger select.is-hovered { - border-color: #ff1f4b; + border-color: hsl(348, 100%, 56%); } .select.is-danger select:focus, .select.is-danger select.is-focused, .select.is-danger select:active, .select.is-danger select.is-active { - box-shadow: 0 0 0 0.125em rgba(255, 56, 96, 0.25); + box-shadow: 0 0 0 0.125em rgba(255, 56.1, 95.88, 0.25); } .select.is-small { border-radius: 2px; @@ -4154,7 +4172,7 @@ a.tag:hover { color: hsl(0, 0%, 4%); } .file.is-white:hover .file-cta, .file.is-white.is-hovered .file-cta { - background-color: #f9f9f9; + background-color: rgb(248.625, 248.625, 248.625); border-color: transparent; color: hsl(0, 0%, 4%); } @@ -4164,7 +4182,7 @@ a.tag:hover { color: hsl(0, 0%, 4%); } .file.is-white:active .file-cta, .file.is-white.is-active .file-cta { - background-color: #f2f2f2; + background-color: rgb(242.25, 242.25, 242.25); border-color: transparent; color: hsl(0, 0%, 4%); } @@ -4174,17 +4192,17 @@ a.tag:hover { color: #fff; } .file.is-black:hover .file-cta, .file.is-black.is-hovered .file-cta { - background-color: #040404; + background-color: hsl(0, 0%, 1.5%); border-color: transparent; color: #fff; } .file.is-black:focus .file-cta, .file.is-black.is-focused .file-cta { border-color: transparent; - box-shadow: 0 0 0.5em rgba(10, 10, 10, 0.25); + box-shadow: 0 0 0.5em rgba(10.2, 10.2, 10.2, 0.25); color: #fff; } .file.is-black:active .file-cta, .file.is-black.is-active .file-cta { - background-color: black; + background-color: hsl(0, 0%, 0%); border-color: transparent; color: #fff; } @@ -4194,17 +4212,17 @@ a.tag:hover { color: hsl(0, 0%, 21%); } .file.is-light:hover .file-cta, .file.is-light.is-hovered .file-cta { - background-color: #eeeeee; + background-color: hsl(0, 0%, 93.5%); border-color: transparent; color: hsl(0, 0%, 21%); } .file.is-light:focus .file-cta, .file.is-light.is-focused .file-cta { border-color: transparent; - box-shadow: 0 0 0.5em rgba(245, 245, 245, 0.25); + box-shadow: 0 0 0.5em rgba(244.8, 244.8, 244.8, 0.25); color: hsl(0, 0%, 21%); } .file.is-light:active .file-cta, .file.is-light.is-active .file-cta { - background-color: #e8e8e8; + background-color: hsl(0, 0%, 91%); border-color: transparent; color: hsl(0, 0%, 21%); } @@ -4214,17 +4232,17 @@ a.tag:hover { color: hsl(0, 0%, 96%); } .file.is-dark:hover .file-cta, .file.is-dark.is-hovered .file-cta { - background-color: #2f2f2f; + background-color: hsl(0, 0%, 18.5%); border-color: transparent; color: hsl(0, 0%, 96%); } .file.is-dark:focus .file-cta, .file.is-dark.is-focused .file-cta { border-color: transparent; - box-shadow: 0 0 0.5em rgba(54, 54, 54, 0.25); + box-shadow: 0 0 0.5em rgba(53.55, 53.55, 53.55, 0.25); color: hsl(0, 0%, 96%); } .file.is-dark:active .file-cta, .file.is-dark.is-active .file-cta { - background-color: #292929; + background-color: hsl(0, 0%, 16%); border-color: transparent; color: hsl(0, 0%, 96%); } @@ -4234,7 +4252,7 @@ a.tag:hover { color: #fff; } .file.is-primary:hover .file-cta, .file.is-primary.is-hovered .file-cta { - background-color: #5cb9ae; + background-color: rgb(92.075, 185.175, 173.5375); border-color: transparent; color: #fff; } @@ -4244,7 +4262,7 @@ a.tag:hover { color: #fff; } .file.is-primary:active .file-cta, .file.is-primary.is-active .file-cta { - background-color: #53b5a9; + background-color: rgb(83.15, 181.35, 169.075); border-color: transparent; color: #fff; } @@ -4254,7 +4272,7 @@ a.tag:hover { color: #fff; } .file.is-link:hover .file-cta, .file.is-link.is-hovered .file-cta { - background-color: #2d5fd4; + background-color: rgb(45.428125, 94.925, 211.821875); border-color: transparent; color: #fff; } @@ -4264,7 +4282,7 @@ a.tag:hover { color: #fff; } .file.is-link:active .file-cta, .file.is-link.is-active .file-cta { - background-color: #2a5acb; + background-color: rgb(41.76875, 89.65, 202.73125); border-color: transparent; color: #fff; } @@ -4274,17 +4292,17 @@ a.tag:hover { color: #fff; } .file.is-info:hover .file-cta, .file.is-info.is-hovered .file-cta { - background-color: #1496ed; + background-color: hsl(204, 86%, 50.5%); border-color: transparent; color: #fff; } .file.is-info:focus .file-cta, .file.is-info.is-focused .file-cta { border-color: transparent; - box-shadow: 0 0 0.5em rgba(32, 156, 238, 0.25); + box-shadow: 0 0 0.5em rgba(32.079, 155.7642, 238.221, 0.25); color: #fff; } .file.is-info:active .file-cta, .file.is-info.is-active .file-cta { - background-color: #118fe4; + background-color: hsl(204, 86%, 48%); border-color: transparent; color: #fff; } @@ -4294,17 +4312,17 @@ a.tag:hover { color: #fff; } .file.is-success:hover .file-cta, .file.is-success.is-hovered .file-cta { - background-color: #22c65b; + background-color: hsl(141, 71%, 45.5%); border-color: transparent; color: #fff; } .file.is-success:focus .file-cta, .file.is-success.is-focused .file-cta { border-color: transparent; - box-shadow: 0 0 0.5em rgba(35, 209, 96, 0.25); + box-shadow: 0 0 0.5em rgba(35.496, 209.304, 96.3288, 0.25); color: #fff; } .file.is-success:active .file-cta, .file.is-success.is-active .file-cta { - background-color: #20bc56; + background-color: hsl(141, 71%, 43%); border-color: transparent; color: #fff; } @@ -4314,17 +4332,17 @@ a.tag:hover { color: rgba(0, 0, 0, 0.7); } .file.is-warning:hover .file-cta, .file.is-warning.is-hovered .file-cta { - background-color: #ffdb4a; + background-color: hsl(48, 100%, 64.5%); border-color: transparent; color: rgba(0, 0, 0, 0.7); } .file.is-warning:focus .file-cta, .file.is-warning.is-focused .file-cta { border-color: transparent; - box-shadow: 0 0 0.5em rgba(255, 221, 87, 0.25); + box-shadow: 0 0 0.5em rgba(255, 221.34, 86.7, 0.25); color: rgba(0, 0, 0, 0.7); } .file.is-warning:active .file-cta, .file.is-warning.is-active .file-cta { - background-color: #ffd83d; + background-color: hsl(48, 100%, 62%); border-color: transparent; color: rgba(0, 0, 0, 0.7); } @@ -4334,17 +4352,17 @@ a.tag:hover { color: #fff; } .file.is-danger:hover .file-cta, .file.is-danger.is-hovered .file-cta { - background-color: #ff2b56; + background-color: hsl(348, 100%, 58.5%); border-color: transparent; color: #fff; } .file.is-danger:focus .file-cta, .file.is-danger.is-focused .file-cta { border-color: transparent; - box-shadow: 0 0 0.5em rgba(255, 56, 96, 0.25); + box-shadow: 0 0 0.5em rgba(255, 56.1, 95.88, 0.25); color: #fff; } .file.is-danger:active .file-cta, .file.is-danger.is-active .file-cta { - background-color: #ff1f4b; + background-color: hsl(348, 100%, 56%); border-color: transparent; color: #fff; } @@ -4442,18 +4460,18 @@ a.tag:hover { position: relative; } .file-label:hover .file-cta { - background-color: #eeeeee; + background-color: hsl(0, 0%, 93.5%); color: hsl(0, 0%, 21%); } .file-label:hover .file-name { - border-color: #d5d5d5; + border-color: hsl(0, 0%, 83.5%); } .file-label:active .file-cta { - background-color: #e8e8e8; + background-color: hsl(0, 0%, 91%); color: hsl(0, 0%, 21%); } .file-label:active .file-name { - border-color: #cfcfcf; + border-color: hsl(0, 0%, 81%); } .file-input { @@ -4866,7 +4884,7 @@ a.tag:hover { .card { background-color: #fff; - box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); + box-shadow: 0 2px 3px rgba(10.2, 10.2, 10.2, 0.1), 0 0 0 1px rgba(10.2, 10.2, 10.2, 0.1); color: hsl(0, 0%, 29%); max-width: 100%; position: relative; @@ -4875,7 +4893,7 @@ a.tag:hover { .card-header { background-color: transparent; align-items: stretch; - box-shadow: 0 1px 2px rgba(10, 10, 10, 0.1); + box-shadow: 0 1px 2px rgba(10.2, 10.2, 10.2, 0.1); display: flex; } @@ -4965,7 +4983,7 @@ a.tag:hover { .dropdown-content { background-color: #fff; border-radius: 4px; - box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); + box-shadow: 0 2px 3px rgba(10.2, 10.2, 10.2, 0.1), 0 0 0 1px rgba(10.2, 10.2, 10.2, 0.1); padding-bottom: 0.5rem; padding-top: 0.5rem; } @@ -5105,7 +5123,7 @@ button.dropdown-item.is-active { .list { background-color: #fff; border-radius: 4px; - box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); + box-shadow: 0 2px 3px rgba(10.2, 10.2, 10.2, 0.1), 0 0 0 1px rgba(10.2, 10.2, 10.2, 0.1); } .list-item { @@ -5145,7 +5163,7 @@ a.list-item { margin-bottom: 0.75rem; } .media .media { - border-top: 1px solid rgba(219, 219, 219, 0.5); + border-top: 1px solid rgba(219.3, 219.3, 219.3, 0.5); display: flex; padding-top: 0.75rem; } @@ -5160,7 +5178,7 @@ a.list-item { margin-top: 0.5rem; } .media + .media { - border-top: 1px solid rgba(219, 219, 219, 0.5); + border-top: 1px solid rgba(219.3, 219.3, 219.3, 0.5); margin-top: 1rem; padding-top: 1rem; } @@ -5275,10 +5293,10 @@ a.list-item { } .message.is-white .message-body { border-color: #fff; - color: #4d4d4d; + color: rgb(76.5, 76.5, 76.5); } .message.is-black { - background-color: #fafafa; + background-color: hsl(0, 0%, 98%); } .message.is-black .message-header { background-color: hsl(0, 0%, 4%); @@ -5286,10 +5304,10 @@ a.list-item { } .message.is-black .message-body { border-color: hsl(0, 0%, 4%); - color: #0a0a0a; + color: hsl(0, 0%, 3.7875311115%); } .message.is-light { - background-color: #fafafa; + background-color: hsl(0, 0%, 98%); } .message.is-light .message-header { background-color: hsl(0, 0%, 96%); @@ -5297,10 +5315,10 @@ a.list-item { } .message.is-light .message-body { border-color: hsl(0, 0%, 96%); - color: #4f4f4f; + color: hsl(0, 0%, 31.1072581112%); } .message.is-dark { - background-color: #fafafa; + background-color: hsl(0, 0%, 98%); } .message.is-dark .message-header { background-color: hsl(0, 0%, 21%); @@ -5308,10 +5326,10 @@ a.list-item { } .message.is-dark .message-body { border-color: hsl(0, 0%, 21%); - color: #2a2a2a; + color: hsl(0, 0%, 16.5244144729%); } .message.is-primary { - background-color: #f8fcfb; + background-color: rgb(247.86, 251.94, 251.43); } .message.is-primary .message-header { background-color: #65bdb2; @@ -5319,10 +5337,10 @@ a.list-item { } .message.is-primary .message-body { border-color: #65bdb2; - color: #2c4a46; + color: rgb(43.7914289101, 73.7864083652, 70.0370359333); } .message.is-link { - background-color: #f7f9fd; + background-color: rgb(246.5425, 248.54, 253.2575); } .message.is-link .message-header { background-color: #3867d6; @@ -5330,10 +5348,10 @@ a.list-item { } .message.is-link .message-body { border-color: #3867d6; - color: #284b9e; + color: rgb(39.8180666742, 75.035522011, 158.2086612105); } .message.is-info { - background-color: #f6fbfe; + background-color: hsl(204, 86%, 98%); } .message.is-info .message-header { background-color: hsl(204, 86%, 53%); @@ -5341,10 +5359,10 @@ a.list-item { } .message.is-info .message-body { border-color: hsl(204, 86%, 53%); - color: #12537e; + color: hsl(204, 75.34225737%, 28.1319338633%); } .message.is-success { - background-color: #f6fef9; + background-color: hsl(141, 71%, 98%); } .message.is-success .message-header { background-color: hsl(141, 71%, 48%); @@ -5352,10 +5370,10 @@ a.list-item { } .message.is-success .message-body { border-color: hsl(141, 71%, 48%); - color: #0e311a; + color: hsl(141, 55.68003025%, 12.2534039166%); } .message.is-warning { - background-color: #fffdf5; + background-color: hsl(48, 100%, 98%); } .message.is-warning .message-header { background-color: hsl(48, 100%, 67%); @@ -5363,10 +5381,10 @@ a.list-item { } .message.is-warning .message-body { border-color: hsl(48, 100%, 67%); - color: #3c3108; + color: hsl(48, 76.9411683441%, 13.1960594696%); } .message.is-danger { - background-color: #fff5f7; + background-color: hsl(348, 100%, 98%); } .message.is-danger .message-header { background-color: hsl(348, 100%, 61%); @@ -5374,7 +5392,7 @@ a.list-item { } .message.is-danger .message-body { border-color: hsl(348, 100%, 61%); - color: #cd0930; + color: hsl(348, 91.806027338%, 41.8807304553%); } .message-header { @@ -5430,7 +5448,7 @@ a.list-item { } .modal-background { - background-color: rgba(10, 10, 10, 0.86); + background-color: rgba(10.2, 10.2, 10.2, 0.86); } .modal-content, @@ -5528,7 +5546,7 @@ a.list-item { .navbar.is-white .navbar-brand .navbar-link:focus, .navbar.is-white .navbar-brand .navbar-link:hover, .navbar.is-white .navbar-brand .navbar-link.is-active { - background-color: #f2f2f2; + background-color: rgb(242.25, 242.25, 242.25); color: hsl(0, 0%, 4%); } .navbar.is-white .navbar-brand .navbar-link::after { @@ -5554,7 +5572,7 @@ a.list-item { .navbar.is-white .navbar-end .navbar-link:focus, .navbar.is-white .navbar-end .navbar-link:hover, .navbar.is-white .navbar-end .navbar-link.is-active { - background-color: #f2f2f2; + background-color: rgb(242.25, 242.25, 242.25); color: hsl(0, 0%, 4%); } .navbar.is-white .navbar-start .navbar-link::after, @@ -5564,7 +5582,7 @@ a.list-item { .navbar.is-white .navbar-item.has-dropdown:focus .navbar-link, .navbar.is-white .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-white .navbar-item.has-dropdown.is-active .navbar-link { - background-color: #f2f2f2; + background-color: rgb(242.25, 242.25, 242.25); color: hsl(0, 0%, 4%); } .navbar.is-white .navbar-dropdown a.navbar-item.is-active { @@ -5584,7 +5602,7 @@ a.list-item { .navbar.is-black .navbar-brand .navbar-link:focus, .navbar.is-black .navbar-brand .navbar-link:hover, .navbar.is-black .navbar-brand .navbar-link.is-active { - background-color: black; + background-color: hsl(0, 0%, 0%); color: #fff; } .navbar.is-black .navbar-brand .navbar-link::after { @@ -5610,7 +5628,7 @@ a.list-item { .navbar.is-black .navbar-end .navbar-link:focus, .navbar.is-black .navbar-end .navbar-link:hover, .navbar.is-black .navbar-end .navbar-link.is-active { - background-color: black; + background-color: hsl(0, 0%, 0%); color: #fff; } .navbar.is-black .navbar-start .navbar-link::after, @@ -5620,7 +5638,7 @@ a.list-item { .navbar.is-black .navbar-item.has-dropdown:focus .navbar-link, .navbar.is-black .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-black .navbar-item.has-dropdown.is-active .navbar-link { - background-color: black; + background-color: hsl(0, 0%, 0%); color: #fff; } .navbar.is-black .navbar-dropdown a.navbar-item.is-active { @@ -5640,7 +5658,7 @@ a.list-item { .navbar.is-light .navbar-brand .navbar-link:focus, .navbar.is-light .navbar-brand .navbar-link:hover, .navbar.is-light .navbar-brand .navbar-link.is-active { - background-color: #e8e8e8; + background-color: hsl(0, 0%, 91%); color: hsl(0, 0%, 21%); } .navbar.is-light .navbar-brand .navbar-link::after { @@ -5666,7 +5684,7 @@ a.list-item { .navbar.is-light .navbar-end .navbar-link:focus, .navbar.is-light .navbar-end .navbar-link:hover, .navbar.is-light .navbar-end .navbar-link.is-active { - background-color: #e8e8e8; + background-color: hsl(0, 0%, 91%); color: hsl(0, 0%, 21%); } .navbar.is-light .navbar-start .navbar-link::after, @@ -5676,7 +5694,7 @@ a.list-item { .navbar.is-light .navbar-item.has-dropdown:focus .navbar-link, .navbar.is-light .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-light .navbar-item.has-dropdown.is-active .navbar-link { - background-color: #e8e8e8; + background-color: hsl(0, 0%, 91%); color: hsl(0, 0%, 21%); } .navbar.is-light .navbar-dropdown a.navbar-item.is-active { @@ -5696,7 +5714,7 @@ a.list-item { .navbar.is-dark .navbar-brand .navbar-link:focus, .navbar.is-dark .navbar-brand .navbar-link:hover, .navbar.is-dark .navbar-brand .navbar-link.is-active { - background-color: #292929; + background-color: hsl(0, 0%, 16%); color: hsl(0, 0%, 96%); } .navbar.is-dark .navbar-brand .navbar-link::after { @@ -5722,7 +5740,7 @@ a.list-item { .navbar.is-dark .navbar-end .navbar-link:focus, .navbar.is-dark .navbar-end .navbar-link:hover, .navbar.is-dark .navbar-end .navbar-link.is-active { - background-color: #292929; + background-color: hsl(0, 0%, 16%); color: hsl(0, 0%, 96%); } .navbar.is-dark .navbar-start .navbar-link::after, @@ -5732,7 +5750,7 @@ a.list-item { .navbar.is-dark .navbar-item.has-dropdown:focus .navbar-link, .navbar.is-dark .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-dark .navbar-item.has-dropdown.is-active .navbar-link { - background-color: #292929; + background-color: hsl(0, 0%, 16%); color: hsl(0, 0%, 96%); } .navbar.is-dark .navbar-dropdown a.navbar-item.is-active { @@ -5752,7 +5770,7 @@ a.list-item { .navbar.is-primary .navbar-brand .navbar-link:focus, .navbar.is-primary .navbar-brand .navbar-link:hover, .navbar.is-primary .navbar-brand .navbar-link.is-active { - background-color: #53b5a9; + background-color: rgb(83.15, 181.35, 169.075); color: #fff; } .navbar.is-primary .navbar-brand .navbar-link::after { @@ -5778,7 +5796,7 @@ a.list-item { .navbar.is-primary .navbar-end .navbar-link:focus, .navbar.is-primary .navbar-end .navbar-link:hover, .navbar.is-primary .navbar-end .navbar-link.is-active { - background-color: #53b5a9; + background-color: rgb(83.15, 181.35, 169.075); color: #fff; } .navbar.is-primary .navbar-start .navbar-link::after, @@ -5788,7 +5806,7 @@ a.list-item { .navbar.is-primary .navbar-item.has-dropdown:focus .navbar-link, .navbar.is-primary .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-primary .navbar-item.has-dropdown.is-active .navbar-link { - background-color: #53b5a9; + background-color: rgb(83.15, 181.35, 169.075); color: #fff; } .navbar.is-primary .navbar-dropdown a.navbar-item.is-active { @@ -5808,7 +5826,7 @@ a.list-item { .navbar.is-link .navbar-brand .navbar-link:focus, .navbar.is-link .navbar-brand .navbar-link:hover, .navbar.is-link .navbar-brand .navbar-link.is-active { - background-color: #2a5acb; + background-color: rgb(41.76875, 89.65, 202.73125); color: #fff; } .navbar.is-link .navbar-brand .navbar-link::after { @@ -5834,7 +5852,7 @@ a.list-item { .navbar.is-link .navbar-end .navbar-link:focus, .navbar.is-link .navbar-end .navbar-link:hover, .navbar.is-link .navbar-end .navbar-link.is-active { - background-color: #2a5acb; + background-color: rgb(41.76875, 89.65, 202.73125); color: #fff; } .navbar.is-link .navbar-start .navbar-link::after, @@ -5844,7 +5862,7 @@ a.list-item { .navbar.is-link .navbar-item.has-dropdown:focus .navbar-link, .navbar.is-link .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-link .navbar-item.has-dropdown.is-active .navbar-link { - background-color: #2a5acb; + background-color: rgb(41.76875, 89.65, 202.73125); color: #fff; } .navbar.is-link .navbar-dropdown a.navbar-item.is-active { @@ -5864,7 +5882,7 @@ a.list-item { .navbar.is-info .navbar-brand .navbar-link:focus, .navbar.is-info .navbar-brand .navbar-link:hover, .navbar.is-info .navbar-brand .navbar-link.is-active { - background-color: #118fe4; + background-color: hsl(204, 86%, 48%); color: #fff; } .navbar.is-info .navbar-brand .navbar-link::after { @@ -5890,7 +5908,7 @@ a.list-item { .navbar.is-info .navbar-end .navbar-link:focus, .navbar.is-info .navbar-end .navbar-link:hover, .navbar.is-info .navbar-end .navbar-link.is-active { - background-color: #118fe4; + background-color: hsl(204, 86%, 48%); color: #fff; } .navbar.is-info .navbar-start .navbar-link::after, @@ -5900,7 +5918,7 @@ a.list-item { .navbar.is-info .navbar-item.has-dropdown:focus .navbar-link, .navbar.is-info .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-info .navbar-item.has-dropdown.is-active .navbar-link { - background-color: #118fe4; + background-color: hsl(204, 86%, 48%); color: #fff; } .navbar.is-info .navbar-dropdown a.navbar-item.is-active { @@ -5920,7 +5938,7 @@ a.list-item { .navbar.is-success .navbar-brand .navbar-link:focus, .navbar.is-success .navbar-brand .navbar-link:hover, .navbar.is-success .navbar-brand .navbar-link.is-active { - background-color: #20bc56; + background-color: hsl(141, 71%, 43%); color: #fff; } .navbar.is-success .navbar-brand .navbar-link::after { @@ -5946,7 +5964,7 @@ a.list-item { .navbar.is-success .navbar-end .navbar-link:focus, .navbar.is-success .navbar-end .navbar-link:hover, .navbar.is-success .navbar-end .navbar-link.is-active { - background-color: #20bc56; + background-color: hsl(141, 71%, 43%); color: #fff; } .navbar.is-success .navbar-start .navbar-link::after, @@ -5956,7 +5974,7 @@ a.list-item { .navbar.is-success .navbar-item.has-dropdown:focus .navbar-link, .navbar.is-success .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-success .navbar-item.has-dropdown.is-active .navbar-link { - background-color: #20bc56; + background-color: hsl(141, 71%, 43%); color: #fff; } .navbar.is-success .navbar-dropdown a.navbar-item.is-active { @@ -5976,7 +5994,7 @@ a.list-item { .navbar.is-warning .navbar-brand .navbar-link:focus, .navbar.is-warning .navbar-brand .navbar-link:hover, .navbar.is-warning .navbar-brand .navbar-link.is-active { - background-color: #ffd83d; + background-color: hsl(48, 100%, 62%); color: rgba(0, 0, 0, 0.7); } .navbar.is-warning .navbar-brand .navbar-link::after { @@ -6002,7 +6020,7 @@ a.list-item { .navbar.is-warning .navbar-end .navbar-link:focus, .navbar.is-warning .navbar-end .navbar-link:hover, .navbar.is-warning .navbar-end .navbar-link.is-active { - background-color: #ffd83d; + background-color: hsl(48, 100%, 62%); color: rgba(0, 0, 0, 0.7); } .navbar.is-warning .navbar-start .navbar-link::after, @@ -6012,7 +6030,7 @@ a.list-item { .navbar.is-warning .navbar-item.has-dropdown:focus .navbar-link, .navbar.is-warning .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-warning .navbar-item.has-dropdown.is-active .navbar-link { - background-color: #ffd83d; + background-color: hsl(48, 100%, 62%); color: rgba(0, 0, 0, 0.7); } .navbar.is-warning .navbar-dropdown a.navbar-item.is-active { @@ -6032,7 +6050,7 @@ a.list-item { .navbar.is-danger .navbar-brand .navbar-link:focus, .navbar.is-danger .navbar-brand .navbar-link:hover, .navbar.is-danger .navbar-brand .navbar-link.is-active { - background-color: #ff1f4b; + background-color: hsl(348, 100%, 56%); color: #fff; } .navbar.is-danger .navbar-brand .navbar-link::after { @@ -6058,7 +6076,7 @@ a.list-item { .navbar.is-danger .navbar-end .navbar-link:focus, .navbar.is-danger .navbar-end .navbar-link:hover, .navbar.is-danger .navbar-end .navbar-link.is-active { - background-color: #ff1f4b; + background-color: hsl(348, 100%, 56%); color: #fff; } .navbar.is-danger .navbar-start .navbar-link::after, @@ -6068,7 +6086,7 @@ a.list-item { .navbar.is-danger .navbar-item.has-dropdown:focus .navbar-link, .navbar.is-danger .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-danger .navbar-item.has-dropdown.is-active .navbar-link { - background-color: #ff1f4b; + background-color: hsl(348, 100%, 56%); color: #fff; } .navbar.is-danger .navbar-dropdown a.navbar-item.is-active { @@ -6282,7 +6300,7 @@ a.navbar-item:focus, a.navbar-item:focus-within, a.navbar-item:hover, a.navbar-i } .navbar-menu { background-color: #fff; - box-shadow: 0 8px 16px rgba(10, 10, 10, 0.1); + box-shadow: 0 8px 16px rgba(10.2, 10.2, 10.2, 0.1); padding: 0.5rem 0; } .navbar-menu.is-active { @@ -6298,7 +6316,7 @@ a.navbar-item:focus, a.navbar-item:focus-within, a.navbar-item:hover, a.navbar-i bottom: 0; } .navbar.is-fixed-bottom-touch.has-shadow { - box-shadow: 0 -2px 3px rgba(10, 10, 10, 0.1); + box-shadow: 0 -2px 3px rgba(10.2, 10.2, 10.2, 0.1); } .navbar.is-fixed-top-touch { top: 0; @@ -6378,7 +6396,7 @@ a.navbar-item:focus, a.navbar-item:focus-within, a.navbar-item:hover, a.navbar-i border-radius: 6px 6px 0 0; border-top: none; bottom: 100%; - box-shadow: 0 -8px 8px rgba(10, 10, 10, 0.1); + box-shadow: 0 -8px 8px rgba(10.2, 10.2, 10.2, 0.1); top: auto; } .navbar-item.is-active .navbar-dropdown, .navbar-item.is-hoverable:focus .navbar-dropdown, .navbar-item.is-hoverable:focus-within .navbar-dropdown, .navbar-item.is-hoverable:hover .navbar-dropdown { @@ -6406,7 +6424,7 @@ a.navbar-item:focus, a.navbar-item:focus-within, a.navbar-item:hover, a.navbar-i border-bottom-left-radius: 6px; border-bottom-right-radius: 6px; border-top: 2px solid hsl(0, 0%, 86%); - box-shadow: 0 8px 8px rgba(10, 10, 10, 0.1); + box-shadow: 0 8px 8px rgba(10.2, 10.2, 10.2, 0.1); display: none; font-size: 0.875rem; left: 0; @@ -6433,7 +6451,7 @@ a.navbar-item:focus, a.navbar-item:focus-within, a.navbar-item:hover, a.navbar-i .navbar.is-spaced .navbar-dropdown, .navbar-dropdown.is-boxed { border-radius: 6px; border-top: none; - box-shadow: 0 8px 8px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); + box-shadow: 0 8px 8px rgba(10.2, 10.2, 10.2, 0.1), 0 0 0 1px rgba(10.2, 10.2, 10.2, 0.1); display: block; opacity: 0; pointer-events: none; @@ -6467,7 +6485,7 @@ a.navbar-item:focus, a.navbar-item:focus-within, a.navbar-item:hover, a.navbar-i bottom: 0; } .navbar.is-fixed-bottom-desktop.has-shadow { - box-shadow: 0 -2px 3px rgba(10, 10, 10, 0.1); + box-shadow: 0 -2px 3px rgba(10.2, 10.2, 10.2, 0.1); } .navbar.is-fixed-top-desktop { top: 0; @@ -6568,7 +6586,7 @@ a.navbar-item:focus, a.navbar-item:focus-within, a.navbar-item:hover, a.navbar-i .pagination-previous:active, .pagination-next:active, .pagination-link:active { - box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.2); + box-shadow: inset 0 1px 2px rgba(10.2, 10.2, 10.2, 0.2); } .pagination-previous[disabled], .pagination-next[disabled], @@ -8639,7 +8657,7 @@ label.panel-block:hover { color: hsl(0, 0%, 4%); } .hero.is-white .subtitle { - color: rgba(10, 10, 10, 0.9); + color: rgba(10.2, 10.2, 10.2, 0.9); } .hero.is-white .subtitle a:not(.button), .hero.is-white .subtitle strong { @@ -8652,12 +8670,12 @@ label.panel-block:hover { } .hero.is-white .navbar-item, .hero.is-white .navbar-link { - color: rgba(10, 10, 10, 0.7); + color: rgba(10.2, 10.2, 10.2, 0.7); } .hero.is-white a.navbar-item:hover, .hero.is-white a.navbar-item.is-active, .hero.is-white .navbar-link:hover, .hero.is-white .navbar-link.is-active { - background-color: #f2f2f2; + background-color: rgb(242.25, 242.25, 242.25); color: hsl(0, 0%, 4%); } .hero.is-white .tabs a { @@ -8674,7 +8692,7 @@ label.panel-block:hover { color: hsl(0, 0%, 4%); } .hero.is-white .tabs.is-boxed a:hover, .hero.is-white .tabs.is-toggle a:hover { - background-color: rgba(10, 10, 10, 0.1); + background-color: rgba(10.2, 10.2, 10.2, 0.1); } .hero.is-white .tabs.is-boxed li.is-active a, .hero.is-white .tabs.is-boxed li.is-active a:hover, .hero.is-white .tabs.is-toggle li.is-active a, .hero.is-white .tabs.is-toggle li.is-active a:hover { background-color: hsl(0, 0%, 4%); @@ -8682,11 +8700,11 @@ label.panel-block:hover { color: #fff; } .hero.is-white.is-bold { - background-image: linear-gradient(141deg, #e8e3e4 0%, #fff 71%, white 100%); + background-image: linear-gradient(141deg, rgb(229.5, 229.5, 229.5) 0%, #fff 71%, white 100%); } @media screen and (max-width: 768px) { .hero.is-white.is-bold .navbar-menu { - background-image: linear-gradient(141deg, #e8e3e4 0%, #fff 71%, white 100%); + background-image: linear-gradient(141deg, rgb(229.5, 229.5, 229.5) 0%, #fff 71%, white 100%); } } .hero.is-black { @@ -8719,7 +8737,7 @@ label.panel-block:hover { .hero.is-black a.navbar-item:hover, .hero.is-black a.navbar-item.is-active, .hero.is-black .navbar-link:hover, .hero.is-black .navbar-link.is-active { - background-color: black; + background-color: hsl(0, 0%, 0%); color: #fff; } .hero.is-black .tabs a { @@ -8736,7 +8754,7 @@ label.panel-block:hover { color: #fff; } .hero.is-black .tabs.is-boxed a:hover, .hero.is-black .tabs.is-toggle a:hover { - background-color: rgba(10, 10, 10, 0.1); + background-color: rgba(10.2, 10.2, 10.2, 0.1); } .hero.is-black .tabs.is-boxed li.is-active a, .hero.is-black .tabs.is-boxed li.is-active a:hover, .hero.is-black .tabs.is-toggle li.is-active a, .hero.is-black .tabs.is-toggle li.is-active a:hover { background-color: #fff; @@ -8744,11 +8762,11 @@ label.panel-block:hover { color: hsl(0, 0%, 4%); } .hero.is-black.is-bold { - background-image: linear-gradient(141deg, black 0%, hsl(0, 0%, 4%) 71%, #181616 100%); + background-image: linear-gradient(141deg, hsl(350, 10%, 0%) 0%, hsl(0, 0%, 4%) 71%, hsl(10, 5%, 9%) 100%); } @media screen and (max-width: 768px) { .hero.is-black.is-bold .navbar-menu { - background-image: linear-gradient(141deg, black 0%, hsl(0, 0%, 4%) 71%, #181616 100%); + background-image: linear-gradient(141deg, hsl(350, 10%, 0%) 0%, hsl(0, 0%, 4%) 71%, hsl(10, 5%, 9%) 100%); } } .hero.is-light { @@ -8763,7 +8781,7 @@ label.panel-block:hover { color: hsl(0, 0%, 21%); } .hero.is-light .subtitle { - color: rgba(54, 54, 54, 0.9); + color: rgba(53.55, 53.55, 53.55, 0.9); } .hero.is-light .subtitle a:not(.button), .hero.is-light .subtitle strong { @@ -8776,12 +8794,12 @@ label.panel-block:hover { } .hero.is-light .navbar-item, .hero.is-light .navbar-link { - color: rgba(54, 54, 54, 0.7); + color: rgba(53.55, 53.55, 53.55, 0.7); } .hero.is-light a.navbar-item:hover, .hero.is-light a.navbar-item.is-active, .hero.is-light .navbar-link:hover, .hero.is-light .navbar-link.is-active { - background-color: #e8e8e8; + background-color: hsl(0, 0%, 91%); color: hsl(0, 0%, 21%); } .hero.is-light .tabs a { @@ -8798,7 +8816,7 @@ label.panel-block:hover { color: hsl(0, 0%, 21%); } .hero.is-light .tabs.is-boxed a:hover, .hero.is-light .tabs.is-toggle a:hover { - background-color: rgba(10, 10, 10, 0.1); + background-color: rgba(10.2, 10.2, 10.2, 0.1); } .hero.is-light .tabs.is-boxed li.is-active a, .hero.is-light .tabs.is-boxed li.is-active a:hover, .hero.is-light .tabs.is-toggle li.is-active a, .hero.is-light .tabs.is-toggle li.is-active a:hover { background-color: hsl(0, 0%, 21%); @@ -8806,11 +8824,11 @@ label.panel-block:hover { color: hsl(0, 0%, 96%); } .hero.is-light.is-bold { - background-image: linear-gradient(141deg, #dfd8d9 0%, hsl(0, 0%, 96%) 71%, white 100%); + background-image: linear-gradient(141deg, hsl(350, 10%, 86%) 0%, hsl(0, 0%, 96%) 71%, hsl(10, 5%, 100%) 100%); } @media screen and (max-width: 768px) { .hero.is-light.is-bold .navbar-menu { - background-image: linear-gradient(141deg, #dfd8d9 0%, hsl(0, 0%, 96%) 71%, white 100%); + background-image: linear-gradient(141deg, hsl(350, 10%, 86%) 0%, hsl(0, 0%, 96%) 71%, hsl(10, 5%, 100%) 100%); } } .hero.is-dark { @@ -8825,7 +8843,7 @@ label.panel-block:hover { color: hsl(0, 0%, 96%); } .hero.is-dark .subtitle { - color: rgba(245, 245, 245, 0.9); + color: rgba(244.8, 244.8, 244.8, 0.9); } .hero.is-dark .subtitle a:not(.button), .hero.is-dark .subtitle strong { @@ -8838,12 +8856,12 @@ label.panel-block:hover { } .hero.is-dark .navbar-item, .hero.is-dark .navbar-link { - color: rgba(245, 245, 245, 0.7); + color: rgba(244.8, 244.8, 244.8, 0.7); } .hero.is-dark a.navbar-item:hover, .hero.is-dark a.navbar-item.is-active, .hero.is-dark .navbar-link:hover, .hero.is-dark .navbar-link.is-active { - background-color: #292929; + background-color: hsl(0, 0%, 16%); color: hsl(0, 0%, 96%); } .hero.is-dark .tabs a { @@ -8860,7 +8878,7 @@ label.panel-block:hover { color: hsl(0, 0%, 96%); } .hero.is-dark .tabs.is-boxed a:hover, .hero.is-dark .tabs.is-toggle a:hover { - background-color: rgba(10, 10, 10, 0.1); + background-color: rgba(10.2, 10.2, 10.2, 0.1); } .hero.is-dark .tabs.is-boxed li.is-active a, .hero.is-dark .tabs.is-boxed li.is-active a:hover, .hero.is-dark .tabs.is-toggle li.is-active a, .hero.is-dark .tabs.is-toggle li.is-active a:hover { background-color: hsl(0, 0%, 96%); @@ -8868,11 +8886,11 @@ label.panel-block:hover { color: hsl(0, 0%, 21%); } .hero.is-dark.is-bold { - background-image: linear-gradient(141deg, #1f191a 0%, hsl(0, 0%, 21%) 71%, #46403f 100%); + background-image: linear-gradient(141deg, hsl(350, 10%, 11%) 0%, hsl(0, 0%, 21%) 71%, hsl(10, 5%, 26%) 100%); } @media screen and (max-width: 768px) { .hero.is-dark.is-bold .navbar-menu { - background-image: linear-gradient(141deg, #1f191a 0%, hsl(0, 0%, 21%) 71%, #46403f 100%); + background-image: linear-gradient(141deg, hsl(350, 10%, 11%) 0%, hsl(0, 0%, 21%) 71%, hsl(10, 5%, 26%) 100%); } } .hero.is-primary { @@ -8905,7 +8923,7 @@ label.panel-block:hover { .hero.is-primary a.navbar-item:hover, .hero.is-primary a.navbar-item.is-active, .hero.is-primary .navbar-link:hover, .hero.is-primary .navbar-link.is-active { - background-color: #53b5a9; + background-color: rgb(83.15, 181.35, 169.075); color: #fff; } .hero.is-primary .tabs a { @@ -8922,7 +8940,7 @@ label.panel-block:hover { color: #fff; } .hero.is-primary .tabs.is-boxed a:hover, .hero.is-primary .tabs.is-toggle a:hover { - background-color: rgba(10, 10, 10, 0.1); + background-color: rgba(10.2, 10.2, 10.2, 0.1); } .hero.is-primary .tabs.is-boxed li.is-active a, .hero.is-primary .tabs.is-boxed li.is-active a:hover, .hero.is-primary .tabs.is-toggle li.is-active a, .hero.is-primary .tabs.is-toggle li.is-active a:hover { background-color: #fff; @@ -8930,11 +8948,11 @@ label.panel-block:hover { color: #65bdb2; } .hero.is-primary.is-bold { - background-image: linear-gradient(141deg, #3cb390 0%, #65bdb2 71%, #72c6ca 100%); + background-image: linear-gradient(141deg, rgb(59.75, 179.25, 144.3958333333) 0%, #65bdb2 71%, rgb(113.9875, 197.865625, 201.5125) 100%); } @media screen and (max-width: 768px) { .hero.is-primary.is-bold .navbar-menu { - background-image: linear-gradient(141deg, #3cb390 0%, #65bdb2 71%, #72c6ca 100%); + background-image: linear-gradient(141deg, rgb(59.75, 179.25, 144.3958333333) 0%, #65bdb2 71%, rgb(113.9875, 197.865625, 201.5125) 100%); } } .hero.is-link { @@ -8967,7 +8985,7 @@ label.panel-block:hover { .hero.is-link a.navbar-item:hover, .hero.is-link a.navbar-item.is-active, .hero.is-link .navbar-link:hover, .hero.is-link .navbar-link.is-active { - background-color: #2a5acb; + background-color: rgb(41.76875, 89.65, 202.73125); color: #fff; } .hero.is-link .tabs a { @@ -8984,7 +9002,7 @@ label.panel-block:hover { color: #fff; } .hero.is-link .tabs.is-boxed a:hover, .hero.is-link .tabs.is-toggle a:hover { - background-color: rgba(10, 10, 10, 0.1); + background-color: rgba(10.2, 10.2, 10.2, 0.1); } .hero.is-link .tabs.is-boxed li.is-active a, .hero.is-link .tabs.is-boxed li.is-active a:hover, .hero.is-link .tabs.is-toggle li.is-active a, .hero.is-link .tabs.is-toggle li.is-active a:hover { background-color: #fff; @@ -8992,11 +9010,11 @@ label.panel-block:hover { color: #3867d6; } .hero.is-link.is-bold { - background-image: linear-gradient(141deg, #1a68c1 0%, #3867d6 71%, #485ce0 100%); + background-image: linear-gradient(141deg, rgb(26.4625, 103.5437236287, 192.5375) 0%, #3867d6 71%, rgb(71.78125, 91.6549314346, 223.71875) 100%); } @media screen and (max-width: 768px) { .hero.is-link.is-bold .navbar-menu { - background-image: linear-gradient(141deg, #1a68c1 0%, #3867d6 71%, #485ce0 100%); + background-image: linear-gradient(141deg, rgb(26.4625, 103.5437236287, 192.5375) 0%, #3867d6 71%, rgb(71.78125, 91.6549314346, 223.71875) 100%); } } .hero.is-info { @@ -9029,7 +9047,7 @@ label.panel-block:hover { .hero.is-info a.navbar-item:hover, .hero.is-info a.navbar-item.is-active, .hero.is-info .navbar-link:hover, .hero.is-info .navbar-link.is-active { - background-color: #118fe4; + background-color: hsl(204, 86%, 48%); color: #fff; } .hero.is-info .tabs a { @@ -9046,7 +9064,7 @@ label.panel-block:hover { color: #fff; } .hero.is-info .tabs.is-boxed a:hover, .hero.is-info .tabs.is-toggle a:hover { - background-color: rgba(10, 10, 10, 0.1); + background-color: rgba(10.2, 10.2, 10.2, 0.1); } .hero.is-info .tabs.is-boxed li.is-active a, .hero.is-info .tabs.is-boxed li.is-active a:hover, .hero.is-info .tabs.is-toggle li.is-active a, .hero.is-info .tabs.is-toggle li.is-active a:hover { background-color: #fff; @@ -9054,11 +9072,11 @@ label.panel-block:hover { color: hsl(204, 86%, 53%); } .hero.is-info.is-bold { - background-image: linear-gradient(141deg, #04a6d7 0%, hsl(204, 86%, 53%) 71%, #3287f5 100%); + background-image: linear-gradient(141deg, hsl(194, 96%, 43%) 0%, hsl(204, 86%, 53%) 71%, hsl(214, 91%, 58%) 100%); } @media screen and (max-width: 768px) { .hero.is-info.is-bold .navbar-menu { - background-image: linear-gradient(141deg, #04a6d7 0%, hsl(204, 86%, 53%) 71%, #3287f5 100%); + background-image: linear-gradient(141deg, hsl(194, 96%, 43%) 0%, hsl(204, 86%, 53%) 71%, hsl(214, 91%, 58%) 100%); } } .hero.is-success { @@ -9091,7 +9109,7 @@ label.panel-block:hover { .hero.is-success a.navbar-item:hover, .hero.is-success a.navbar-item.is-active, .hero.is-success .navbar-link:hover, .hero.is-success .navbar-link.is-active { - background-color: #20bc56; + background-color: hsl(141, 71%, 43%); color: #fff; } .hero.is-success .tabs a { @@ -9108,7 +9126,7 @@ label.panel-block:hover { color: #fff; } .hero.is-success .tabs.is-boxed a:hover, .hero.is-success .tabs.is-toggle a:hover { - background-color: rgba(10, 10, 10, 0.1); + background-color: rgba(10.2, 10.2, 10.2, 0.1); } .hero.is-success .tabs.is-boxed li.is-active a, .hero.is-success .tabs.is-boxed li.is-active a:hover, .hero.is-success .tabs.is-toggle li.is-active a, .hero.is-success .tabs.is-toggle li.is-active a:hover { background-color: #fff; @@ -9116,11 +9134,11 @@ label.panel-block:hover { color: hsl(141, 71%, 48%); } .hero.is-success.is-bold { - background-image: linear-gradient(141deg, #12af2f 0%, hsl(141, 71%, 48%) 71%, #2ce28a 100%); + background-image: linear-gradient(141deg, hsl(131, 81%, 38%) 0%, hsl(141, 71%, 48%) 71%, hsl(151, 76%, 53%) 100%); } @media screen and (max-width: 768px) { .hero.is-success.is-bold .navbar-menu { - background-image: linear-gradient(141deg, #12af2f 0%, hsl(141, 71%, 48%) 71%, #2ce28a 100%); + background-image: linear-gradient(141deg, hsl(131, 81%, 38%) 0%, hsl(141, 71%, 48%) 71%, hsl(151, 76%, 53%) 100%); } } .hero.is-warning { @@ -9153,7 +9171,7 @@ label.panel-block:hover { .hero.is-warning a.navbar-item:hover, .hero.is-warning a.navbar-item.is-active, .hero.is-warning .navbar-link:hover, .hero.is-warning .navbar-link.is-active { - background-color: #ffd83d; + background-color: hsl(48, 100%, 62%); color: rgba(0, 0, 0, 0.7); } .hero.is-warning .tabs a { @@ -9170,7 +9188,7 @@ label.panel-block:hover { color: rgba(0, 0, 0, 0.7); } .hero.is-warning .tabs.is-boxed a:hover, .hero.is-warning .tabs.is-toggle a:hover { - background-color: rgba(10, 10, 10, 0.1); + background-color: rgba(10.2, 10.2, 10.2, 0.1); } .hero.is-warning .tabs.is-boxed li.is-active a, .hero.is-warning .tabs.is-boxed li.is-active a:hover, .hero.is-warning .tabs.is-toggle li.is-active a, .hero.is-warning .tabs.is-toggle li.is-active a:hover { background-color: rgba(0, 0, 0, 0.7); @@ -9178,11 +9196,11 @@ label.panel-block:hover { color: hsl(48, 100%, 67%); } .hero.is-warning.is-bold { - background-image: linear-gradient(141deg, #ffaf24 0%, hsl(48, 100%, 67%) 71%, #fffa70 100%); + background-image: linear-gradient(141deg, hsl(38, 100%, 57%) 0%, hsl(48, 100%, 67%) 71%, hsl(58, 100%, 72%) 100%); } @media screen and (max-width: 768px) { .hero.is-warning.is-bold .navbar-menu { - background-image: linear-gradient(141deg, #ffaf24 0%, hsl(48, 100%, 67%) 71%, #fffa70 100%); + background-image: linear-gradient(141deg, hsl(38, 100%, 57%) 0%, hsl(48, 100%, 67%) 71%, hsl(58, 100%, 72%) 100%); } } .hero.is-danger { @@ -9215,7 +9233,7 @@ label.panel-block:hover { .hero.is-danger a.navbar-item:hover, .hero.is-danger a.navbar-item.is-active, .hero.is-danger .navbar-link:hover, .hero.is-danger .navbar-link.is-active { - background-color: #ff1f4b; + background-color: hsl(348, 100%, 56%); color: #fff; } .hero.is-danger .tabs a { @@ -9232,7 +9250,7 @@ label.panel-block:hover { color: #fff; } .hero.is-danger .tabs.is-boxed a:hover, .hero.is-danger .tabs.is-toggle a:hover { - background-color: rgba(10, 10, 10, 0.1); + background-color: rgba(10.2, 10.2, 10.2, 0.1); } .hero.is-danger .tabs.is-boxed li.is-active a, .hero.is-danger .tabs.is-boxed li.is-active a:hover, .hero.is-danger .tabs.is-toggle li.is-active a, .hero.is-danger .tabs.is-toggle li.is-active a:hover { background-color: #fff; @@ -9240,11 +9258,11 @@ label.panel-block:hover { color: hsl(348, 100%, 61%); } .hero.is-danger.is-bold { - background-image: linear-gradient(141deg, #ff0561 0%, hsl(348, 100%, 61%) 71%, #ff5257 100%); + background-image: linear-gradient(141deg, hsl(338, 100%, 51%) 0%, hsl(348, 100%, 61%) 71%, hsl(358, 100%, 66%) 100%); } @media screen and (max-width: 768px) { .hero.is-danger.is-bold .navbar-menu { - background-image: linear-gradient(141deg, #ff0561 0%, hsl(348, 100%, 61%) 71%, #ff5257 100%); + background-image: linear-gradient(141deg, hsl(338, 100%, 51%) 0%, hsl(348, 100%, 61%) 71%, hsl(358, 100%, 66%) 100%); } } .hero.is-small .hero-body { @@ -9943,6 +9961,28 @@ label.panel-block:hover { border: 1px solid #e6e6e6; } +.fill { + position: absolute; + top: 0; + bottom: 0; + right: 0; + left: 0; +} + +.center-content { + display: flex; + align-items: center; + justify-content: center; +} + +.databus-ribbon { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + background-color: #e6e7ec; +} + .hover-icon::before { content: ""; position: absolute; @@ -10046,6 +10086,11 @@ label.panel-block:hover { transition-duration: 0.5s; } +.entity-card { + padding: 1.1em; + border: 1px solid #dbdbdb; + border-radius: 8px; +} .entity-card .entity-card-icon { width: auto; height: 52px; @@ -10055,10 +10100,19 @@ label.panel-block:hover { display: flex; align-items: center; } +.entity-card .entity-card-header .title { + font-size: 1.2em; + margin-bottom: 4px; +} +.entity-card .entity-card-header uri-breadcrumbs { + font-size: 0.9em; +} .entity-card .entity-card-content { margin-top: 0.5em; } .entity-card .entity-card-footer { + font-size: 0.8em; + color: #626568; margin-top: 0.5em; } @@ -10068,7 +10122,7 @@ label.panel-block:hover { background-repeat: no-repeat; background-size: contain; text-align: center; - min-height: 200px; + height: 160px; } .hero.databus-banner .title { color: white; @@ -10116,10 +10170,6 @@ label.panel-block:hover { margin-bottom: 1em; } -.navbar-db { - background-color: #24292e; -} - .databus-settings-box { border: 1px solid #dbdbdb; border-radius: 8px; @@ -10227,17 +10277,28 @@ label.panel-block:hover { background-size: 150px; } +.is-version .databus-icon svg { + fill: #47b49f; +} +.is-version .databus-icon svg.is-white { + fill: #fff; +} .is-version path { - fill: #76bfb6; + fill: #47b49f; } .is-version path.is-white { fill: #fff; } +.is-version.button { + background-color: #47b49f; + color: #fff; + border: none; +} .is-version.title { - color: #76bfb6; + color: #47b49f; } .is-version.tag:not(body) { - background-color: #76bfb6; + background-color: #47b49f; color: #fff; } .is-version.databus-banner { @@ -10252,28 +10313,28 @@ label.panel-block:hover { color: #fff; } -.is-consumer path { - fill: #737381; +.is-consumer .databus-icon svg { + fill: #29acbf; } -.is-consumer path.is-white { +.is-consumer .databus-icon svg.is-white { fill: #fff; } .is-consumer.button { - background-color: #737381; + background-color: #29acbf; color: #fff; border: none; } .is-consumer.title { - color: #737381; + color: #29acbf; } .is-consumer.tag:not(body) { - background-color: #737381; + background-color: #29acbf; color: #fff; } .is-consumer.databus-banner { background-image: url("../img/banner-background.png"); background-repeat: repeat; - background-color: #7e7e8a; + background-color: #28b0c5; background-size: 150px; } @@ -10493,10 +10554,405 @@ template { display: none; } +.databus-navbar { + height: 78px; +} +.databus-navbar .databus-navbar-fixed { + z-index: 8000; + height: 78px; + align-items: center; + background-color: #353e47; + position: fixed; + top: 0; + left: 0; + right: 0; + box-shadow: 0px 0px 4px 4px rgba(0, 0, 0, 0.1); +} +.databus-navbar .databus-navbar-container { + display: flex; + height: inherit; + justify-content: stretch; + align-items: center; + width: 100%; + padding: 0em 2em; +} +@media screen and (min-width: 1500px), print { + .databus-navbar .databus-navbar-container { + max-width: 1500px; + margin: auto; + } +} +.databus-navbar .databus-navbar-brand { + display: flex; + margin-left: 1.5em; +} +.databus-navbar .databus-navbar-content { + display: flex !important; + height: inherit; + position: relative; + flex: 1; +} +.databus-navbar .databus-home-icon { + display: flex; + align-items: center; +} +.databus-navbar .databus-home-icon .org-icon { + max-width: 200px; + max-height: 36px; + min-height: 1.5em; +} +.databus-navbar .databus-home-icon .org-separator { + width: 2px; + height: 20px; + background-color: white; + margin: 0em 1em; +} +.databus-navbar .databus-home-icon .org-subtitle { + font-size: 1.2em; + color: white; + margin-right: 2em; + letter-spacing: 1px; +} +.databus-navbar .databus-navbar-item { + display: flex; + align-items: center; + height: inherit; +} +.databus-navbar .databus-navbar-item.fill { + flex-grow: 1; + position: relative; +} +.databus-navbar .databus-navbar-item.end { + margin-left: 1em; +} +.databus-navbar .databus-navbar-item.account-name { + cursor: pointer; + padding-right: 1em; + padding-left: 1em; + margin-right: 0.5em; + color: white; +} +.databus-navbar .databus-navbar-item.has-dropdown { + cursor: pointer; + padding: 0em 0.75em; + color: white; +} +.databus-navbar .databus-navbar-item.has-dropdown:hover { + background-color: #3d4853; +} +.databus-navbar .databus-navbar-item.link { + cursor: pointer; + color: #c4c4c4; + padding: 0em 0.75em; +} +.databus-navbar .databus-navbar-item.link:hover { + background-color: #3d4853; + color: white; +} +.databus-navbar .databus-navbar-item.link .icon { + margin-left: 0; + height: initial; + width: initial; + margin-right: 0.5em; +} +.databus-navbar .databus-navbar-dropdown { + min-width: 200px; + font-size: 1em; +} +.databus-navbar .databus-navbar-dropdown .navbar-dropdown-item { + min-width: 200px; + font-size: 1em; +} +.databus-navbar .nav-search { + flex: 1; +} +.databus-navbar .nav-search .search-results { + position: absolute; + background-color: #3d4853; + box-shadow: 0px 0px 4px 4px rgba(0, 0, 0, 0.1); + min-width: 100%; + max-width: 2000px; + z-index: 1000; + top: 100%; +} +.databus-navbar .nav-search input { + background-color: #3d4853; + border-color: #96a3b2; + outline: none; + color: white; + padding: 1.2em; + border-radius: 8px; +} +.databus-navbar .nav-search input::placeholder { + color: #b2becd; + opacity: 1; + /* Firefox */ +} +.databus-navbar .nav-search .icon { + top: 2px !important; +} +.databus-navbar .nav-search .nav-search input:focus { + border-color: #c3d2e4; + box-shadow: none; +} +.databus-navbar .nav-search .results { + max-height: 60vh; + overflow-y: auto; + /* Track */ + /* Handle */ + /* Handle on hover */ +} +.databus-navbar .nav-search .results .result-item { + padding: 0.35em 0.7em; + display: flex; + align-items: center; + cursor: pointer; + overflow: hidden; +} +.databus-navbar .nav-search .results .result-item:hover { + background-color: #4e5b6a; +} +.databus-navbar .nav-search .results .result-item:first-child { + padding-top: 0.7em; +} +.databus-navbar .nav-search .results .result-item:last-child { + padding-bottom: 0.7em; +} +.databus-navbar .nav-search .results .result-item uri-breadcrumbs { + color: #b9b9b9 !important; +} +.databus-navbar .nav-search .results .result-item uri-breadcrumbs a { + color: #b9b9b9 !important; +} +.databus-navbar .nav-search .results .result-item uri-breadcrumbs a:hover { + color: #e7e7e7 !important; +} +.databus-navbar .nav-search .results .result-item .item-header { + display: box; + color: white; +} +.databus-navbar .nav-search .results::-webkit-scrollbar { + width: 8px; +} +.databus-navbar .nav-search .results::-webkit-scrollbar-track { + background: #353b45; +} +.databus-navbar .nav-search .results::-webkit-scrollbar-thumb { + background: #b6b6b6; +} +.databus-navbar .nav-search .results::-webkit-scrollbar-thumb:hover { + background: #a3a3a3; +} +.databus-navbar .nav-search .result-header { + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 2px solid #f5f5f5; + padding: 0.5em 1em; + color: white; + font-weight: 600; +} +.databus-navbar .nav-search .filters { + display: flex; + align-items: center; + line-height: 1; +} +.databus-navbar .nav-search .filters p { + font-weight: 600; + color: white; + margin-right: 0.6em; +} +.databus-navbar .nav-search .filters .grey-out-dark .tag { + background-color: #3d4853; +} +.databus-navbar .nav-search .filters .grey-out-dark path { + fill: #a7a7a7; +} +.databus-navbar .nav-search .result-item type-tag { + margin-right: 12px; +} +.databus-navbar .nav-search .result-item .item-label { + font-weight: 600; + margin-right: 8px; + margin-bottom: -4px; +} +.databus-navbar .nav-search .result-item .item-abstract { + color: white; + width: 620px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} +.databus-navbar .nav-search .item-header a { + color: white; +} +.databus-navbar .navbar-account-icon { + height: 48px; + width: 48px; + background-color: white; + border-radius: 24px; + margin-left: 0.5em; +} + +.databus-navbar-item.is-light:hover { + opacity: 1; +} + +.databus-navbar-item.has-dropdown .databus-navbar-dropdown { + display: none; +} + +.databus-navbar-item.has-dropdown.is-active .databus-navbar-dropdown { + display: block; +} + +.databus-navbar-dropdown { + position: absolute; + top: 100%; + right: 0; + background-color: #3d4853; + padding-bottom: 8px; + min-width: 250px; +} + +.databus-navbar-dropdown-arrow { + margin-left: 8px; +} + +.databus-navbar-item.has-dropdown:hover .databus-navbar-dropdown-arrow { + opacity: 0.7; +} + +@media screen and (max-width: 1024px), print { + .databus-navbar-group.is-hidden-for-mobile { + display: none; + } +} +.section.call-to-action { + background-color: #e6e7ec; + padding: 3em 0em; + position: relative; +} + +.frontpage-box-frame { + background-color: #fff; + border-radius: 20px; + padding: 0px; + box-shadow: 0px 0px 9px rgba(0, 0, 0, 0.05); +} + +.frontpage-box { + background-color: #366281; + border-radius: 20px; + display: flex; + color: white; + width: 380px; + padding: 1.2em; + position: relative; + align-items: center; + cursor: pointer; +} + +.frontpage-box.left { + background-color: #28b0c5; +} + +.frontpage-box.center { + background-color: #47b49f; +} + +.frontpage-box:hover { + -moz-transform: translateY(-4px); + -webkit-transform: translateY(-4px); + -o-transform: translateY(-4px); + -ms-transform: translateY(-4px); + -webkit-transform: translateY(-4px); + transform: translateY(-4px); + -webkit-transition: transform 0.4s ease-out; + -moz-transition: transform 0.4s ease-out; + -ms-transition: transform 0.4s ease-out; +} + +.frontpage-box-arrow { + position: absolute; + right: 1em; + top: 1em; +} + +.frontpage-box-frame:not(:last-child) { + margin-right: 2em; +} + +.frontpage-box .content { + padding: 0; + margin-left: 1em; +} + +.frontpage-box .content h1 { + margin-bottom: 4px; + color: white; + font-weight: 600; +} + +.frontpage-box .headline databus-icon { + margin-right: 0.7em; +} + +.frontpage-box h1 { + font-size: 1.2em; + margin-top: 0; +} + +.has-background-pattern { + background-image: url(../img/banner-background.png); + background-repeat: repeat; + background-position: center; + background-size: 150px; +} + +.yasqe .yasqe_buttons { + right: 16px !important; + top: 16px !important; +} + +.yasqe .yasqe_buttons .yasqe_share { + display: none; +} + +.yasqe .yasqe_buttons .yasqe_queryButton { + margin-right: 0.2em; + margin-top: 0.2em; +} + +.error-box { + background-color: #ffdbdb; + color: #f13232; + border: 1px solid #f13232; + padding: 1em; + white-space: pre-line; + display: grid; +} + +.error-box h1 { + font-weight: 600; + padding: 0; + margin: 0; + margin-bottom: 1em; + font-size: 1em; +} + +* { + margin: 0px; + padding: 0px; +} + body, html { - font-family: "PT Sans", sans-serif; + font-family: "Inter", "PT Sans", sans-serif; + text-rendering: optimizeLegibility; line-height: 1.5em; + font-size: 16px; + color: rgb(95, 99, 104); background: #fff; } @@ -10721,15 +11177,6 @@ body.has-navbar-fixed-top { fill: rgb(200, 200, 200); } -.yasqe .yasqe_buttons .yasqe_share { - display: none; -} - -.yasqe .yasqe_buttons .yasqe_queryButton { - margin-right: 0.2em; - margin-top: 0.2em; -} - .editable-collection-element { margin: 0 !important; } @@ -11368,6 +11815,7 @@ a.navbar-item.is-active, a.navbar-item { min-height: 200px; } +/* .call-to-action { background-color: transparent; color: #fff; @@ -11379,7 +11827,7 @@ a.navbar-item.is-active, a.navbar-item { border-width: 2px; font-size: 1.2em; font-weight: 500; - margin-right: 0.5em; + margin-right: .5em; } .call-to-action.colored { @@ -11392,8 +11840,7 @@ a.navbar-item.is-active, a.navbar-item { background-color: #fff; border-color: #fff; color: #69aaa9; -} - +}*/ .open-beta { top: 0; } @@ -11409,7 +11856,6 @@ a.navbar-item.is-active, a.navbar-item { .navbar-item { color: white; - background-color: #343a40; } a.navbar-item:focus-within { @@ -11431,23 +11877,6 @@ a.navbar-item:hover { color: white; } -.org-icon { - max-width: 512px; - max-height: 36px; - margin-right: 1em; -} - -.databus-navbar { - position: relative; - z-index: 2000; - height: 78px; -} - -.navbar-dropdown { - min-width: 200px; - font-size: 1em; -} - .cookie-dialogue { position: fixed; bottom: 30px; @@ -11487,133 +11916,59 @@ small pre.version { background-color: initial !important; } -.databus-navbar { - display: flex; - background-color: #343a40; - align-items: stretch; -} - -.databus-navbar-container { - display: flex; - align-items: stretch; - flex: 1; - margin-left: auto; - margin-right: auto; -} - -@media screen and (min-width: 1500px), print { - .databus-navbar-container { - max-width: 1500px; - } -} -.databus-navbar-brand { - display: flex; - margin-left: 1.5em; +.fill-parent { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; } -.databus-navbar-menu { - display: flex !important; - flex: 1; +.databus-banner { + position: relative; } -.databus-navbar-item { +.align-content-center { display: flex; align-items: center; + justify-content: center; } -.databus-navbar-item.is-light:hover { - opacity: 0.5; -} - -a.databus-navbar-item { +.databus-banner h1 { color: white; - margin-right: 1em; -} - -.databus-navbar-item.is-light { - opacity: 0.7; -} - -.databus-navbar-group { - display: flex; - align-items: stretch; - flex: 1; - flex-grow: 10; -} - -.databus-navbar-end { - display: flex; - flex: 1; - justify-content: flex-end; - justify-self: flex-end; - margin-right: 1em; - align-items: stretch; -} - -.databus-navbar-item.account-name { - padding-right: 1em; - padding-left: 1em; - margin-right: 0.5em; - justify-content: stretch; - display: flex; -} - -.databus-navbar-item.account-name:hover { - opacity: 0.8; -} - -.databus-navbar-item.has-dropdown { - position: relative; - cursor: pointer; - align-items: stretch; -} - -.databus-navbar-item.has-dropdown .databus-navbar-dropdown { - display: none; + font-weight: 600; + font-size: 3em; } -.databus-navbar-item.has-dropdown.is-active .databus-navbar-dropdown { - display: block; +.databus-banner h2 { + color: white; + font-size: 1.5em; } -.databus-navbar-dropdown { +.version-tag { + color: white; + float: right; position: absolute; - top: 100%; + padding: 0.5em; right: 0; - background-color: #343a40; - border-top: 2px solid white; - padding-bottom: 8px; - min-width: 250px; -} - -.databus-navbar-dropdown-arrow { - margin-left: 8px; -} - -.databus-navbar-item.has-dropdown:hover .databus-navbar-dropdown-arrow { - opacity: 0.7; } -@media screen and (max-width: 1024px), print { - .databus-navbar-group.is-hidden-for-mobile { - display: none; - } -} .frontpage-banner { background-image: url(../img/banner-background.png); text-align: left; - padding: 100px; + padding: 50px; position: relative; background-repeat: repeat; + height: 160px; background-position: center; background-size: 150px; color: white; } .frontpage-banner .databus-name { - font-size: 6em; + font-size: 3em; font-weight: bolder; - margin: 0; + margin: 0px 16px; line-height: 1em; } @@ -11883,3 +12238,52 @@ a.databus-navbar-item { .box-separator { height: 30px; } + +.hero-body { + padding: 2.5em 1em; +} + +databus-icon { + display: inline-block; +} + +.field.has-addons input.control:first-child { + border-radius: 4px 0px 0px 4px; +} + +.error-notification-box { + background-color: #fdf2f2; /* very light red/pink background */ + border-left: 3px solid #f14668; /* Bulma 'danger' color */ + border-radius: 4px; + margin-top: 0.5rem; + font-size: 0.95rem; + transition: background 0.3s ease; +} + +.error-notification-box:hover { + background-color: #faeaea; +} + +.expand-arrow { + transition: transform 0.3s ease; +} + +.expand-arrow.rotated { + transform: rotate(180deg); +} + +.error-details { + transition: all 0.3s ease; +} + +.error-tag { + border-color: hsl(348, 100%, 61%); + color: #cd0930; + border-radius: 4px; + border-style: solid; + border-width: 0 0 0 4px; + padding: 0.5em 1em; + background-color: #fff5f7; + margin-top: 0.75em; + font-size: 0.9em; +} diff --git a/public/css/website.scss b/public/css/website.scss index 1056eb39..1df5318a 100644 --- a/public/css/website.scss +++ b/public/css/website.scss @@ -1,30 +1,9 @@ @charset "utf-8"; -@font-face { - font-family: 'PT Sans'; - src: url('./../fonts/PTSans-Regular.ttf'); -} - -@font-face { - font-family: "PT Sans"; - src: url("./../fonts/PTSans-Bold.ttf"); - font-weight: bold; -} +@import 'fonts'; +@import 'colors'; -@font-face { - font-family: "PT Sans"; - src: url("./../fonts/PTSans-Italic.ttf"); - font-style: italic; -} -@font-face { - font-family: "PT Sans"; - src: url("./../fonts/PTSans-BoldItalic.ttf"); - font-weight: bold; - font-style: italic; -} - -@import 'colors'; @import 'collections'; @import '../node_modules/bulma/bulma.sass'; @@ -33,9 +12,7 @@ @import '../node_modules/@triply/yasqe/src/scss/yasqe.scss'; @import '../node_modules/@triply/yasqe/src/scss/buttons.scss'; @import '../node_modules/@triply/yasqe/src/scss/codemirrorMods.scss'; - @import '../node_modules/@triply/yasr/src/main.scss'; - @import 'override-checkbox'; @import 'checkmarks'; @import 'yasqe'; @@ -44,11 +21,22 @@ @import 'entities'; @import 'markdown'; @import 'normalize'; +@import 'nav'; +@import 'frontpage'; +@import 'sparql'; + +* { + margin: 0px; + padding: 0px; +} body, html { - font-family: 'PT Sans', sans-serif; + font-family: 'Inter', 'PT Sans', sans-serif; + text-rendering: optimizeLegibility; line-height: 1.5em; + font-size: 16px; + color: rgb(95, 99, 104); background: #fff; } @@ -75,11 +63,9 @@ body.has-navbar-fixed-top { @media screen and (max-width: 412px), print { - html { font-size: 14px; } - } @media screen and (max-width: 768px), print { @@ -91,8 +77,6 @@ body.has-navbar-fixed-top { font-size: 12px !important ; } - - .description-markdown h1 { font-size: 1.6em !important; } @@ -292,14 +276,6 @@ body.has-navbar-fixed-top { fill: rgba(200, 200, 200, 1); } -.yasqe .yasqe_buttons .yasqe_share { - display: none; -} - -.yasqe .yasqe_buttons .yasqe_queryButton { - margin-right: .2em; - margin-top: .2em; -} .editable-collection-element { margin: 0 !important; @@ -936,6 +912,7 @@ a.navbar-item.is-active, a.navbar-item { } +/* .call-to-action { background-color: transparent; color: #fff; @@ -960,7 +937,7 @@ a.navbar-item.is-active, a.navbar-item { background-color: #fff; border-color: #fff; color: #69aaa9; -} +}*/ .open-beta { top: 0 @@ -977,7 +954,6 @@ a.navbar-item.is-active, a.navbar-item { .navbar-item { color: white; - background-color: #343a40; } a.navbar-item:focus-within { @@ -999,22 +975,7 @@ a.navbar-item:hover { color: white; } -.org-icon { - max-width: 512px; - max-height: 36px; - margin-right: 1em; -} -.databus-navbar { - position: relative; - z-index: 2000; - height: 78px; -} - -.navbar-dropdown { - min-width: 200px; - font-size: 1em; -} .cookie-dialogue { position: fixed; @@ -1055,138 +1016,61 @@ small pre.version { background-color: initial !important; } -.databus-navbar { - display: flex; - background-color: #343a40; - align-items: stretch; - -} -.databus-navbar-container { - display: flex; - align-items: stretch; - flex: 1; - margin-left: auto; - margin-right: auto; -} - -@media screen and (min-width: 1500px), -print { - .databus-navbar-container { - max-width: 1500px; - } -} - -.databus-navbar-brand { - display: flex; - margin-left: 1.5em; +.fill-parent { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; } -.databus-navbar-menu { - display: flex !important; - flex: 1; +.databus-banner { + position: relative; } -.databus-navbar-item { +.align-content-center { display: flex; align-items: center; + justify-content: center; } -.databus-navbar-item.is-light:hover { - opacity: .5; -} - -a.databus-navbar-item { +.databus-banner h1 { color: white; - margin-right: 1em; -} - -.databus-navbar-item.is-light { - opacity: 0.7; -} - -.databus-navbar-group { - display: flex; - align-items: stretch; - flex: 1; - flex-grow: 10; -} - -.databus-navbar-end { - display: flex; - flex: 1; - justify-content: flex-end; - justify-self: flex-end; - margin-right: 1em; - align-items: stretch; -} - -.databus-navbar-item.account-name { - padding-right: 1em; - padding-left: 1em; - margin-right: .5em; - justify-content: stretch; - display: flex; -} - -.databus-navbar-item.account-name:hover { - opacity: 0.8; -} - -.databus-navbar-item.has-dropdown { - position: relative; - cursor: pointer; - align-items: stretch; -} - -.databus-navbar-item.has-dropdown .databus-navbar-dropdown { - display: none; + font-weight: 600; + font-size: 3em; } -.databus-navbar-item.has-dropdown.is-active .databus-navbar-dropdown { - display: block; +.databus-banner h2 { + color: white; + font-size: 1.5em; } -.databus-navbar-dropdown { +.version-tag { + color: white; + float: right; position: absolute; - top: 100%; + padding: 0.5em; right: 0; - background-color: #343a40; - border-top: 2px solid white; - padding-bottom: 8px; - min-width: 250px; -} - -.databus-navbar-dropdown-arrow { - margin-left: 8px; } -.databus-navbar-item.has-dropdown:hover .databus-navbar-dropdown-arrow { - opacity: 0.7; -} - -@media screen and (max-width: 1024px), -print { - .databus-navbar-group.is-hidden-for-mobile { - display: none; - } -} .frontpage-banner { background-image: url(../img/banner-background.png); text-align: left; - padding: 100px; + padding: 50px; position: relative; background-repeat: repeat; + height: 160px; background-position: center; background-size: 150px; color: white; } .frontpage-banner .databus-name { - font-size: 6em; + font-size: 3em; font-weight: bolder; - margin: 0; + margin: 0px 16px; line-height: 1em; } @@ -1458,4 +1342,53 @@ print { .box-separator { height: 30px; +} + +.hero-body { + padding: 2.5em 1em; +} + +databus-icon { + display: inline-block; +} + +.field.has-addons input.control:first-child { + border-radius: 4px 0px 0px 4px; +} + +.error-notification-box { + background-color: #fdf2f2; /* very light red/pink background */ + border-left: 3px solid #f14668; /* Bulma 'danger' color */ + border-radius: 4px; + margin-top: 0.5rem; + font-size: 0.95rem; + transition: background 0.3s ease; +} + +.error-notification-box:hover { + background-color: #faeaea; +} + +.expand-arrow { + transition: transform 0.3s ease; +} + +.expand-arrow.rotated { + transform: rotate(180deg); +} + +.error-details { + transition: all 0.3s ease; +} + +.error-tag { + border-color: hsl(348, 100%, 61%); + color: #cd0930; + border-radius: 4px; + border-style: solid; + border-width: 0 0 0 4px; + padding: 0.5em 1em; + background-color: #fff5f7; + margin-top: 0.75em; + font-size: 0.9em; } \ No newline at end of file diff --git a/public/dist/main.js b/public/dist/main.js index 45818af7..caea7cc7 100644 --- a/public/dist/main.js +++ b/public/dist/main.js @@ -15,7 +15,7 @@ \***********************************/ /***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { -eval("const AccountPageController = __webpack_require__(/*! ./page-controller/account-controller */ \"./js/page-controller/account-controller.js\");\nconst ArtifactPageController = __webpack_require__(/*! ./page-controller/artifact-controller */ \"./js/page-controller/artifact-controller.js\");\nconst FrontPageController = __webpack_require__(/*! ./page-controller/frontpage-controller */ \"./js/page-controller/frontpage-controller.js\");\nconst HeaderController = __webpack_require__(/*! ./page-controller/header-controller */ \"./js/page-controller/header-controller.js\");\nconst CollectionController = __webpack_require__(/*! ./page-controller/collection-controller */ \"./js/page-controller/collection-controller.js\");\nconst CollectionsEditorController = __webpack_require__(/*! ./page-controller/collections-editor-controller */ \"./js/page-controller/collections-editor-controller.js\");\nconst GroupPageController = __webpack_require__(/*! ./page-controller/group-controller */ \"./js/page-controller/group-controller.js\");\nconst ProfileController = __webpack_require__(/*! ./page-controller/profile-controller */ \"./js/page-controller/profile-controller.js\");\nconst PublishWizardController = __webpack_require__(/*! ./page-controller/publish-wizard-controller */ \"./js/page-controller/publish-wizard-controller.js\");\nconst VersionPageController = __webpack_require__(/*! ./page-controller/version-controller */ \"./js/page-controller/version-controller.js\");\nconst DatabusCollectionManager = __webpack_require__(/*! ./collections/databus-collection-manager */ \"./js/collections/databus-collection-manager.js\");\nconst SearchManager = __webpack_require__(/*! ./search/search-manager */ \"./js/search/search-manager.js\");\nconst SearchController = __webpack_require__(/*! ./components/search/search-controller */ \"./js/components/search/search-controller.js\");\nconst DatabusAlertController = __webpack_require__(/*! ./components/databus-alert/databus-alert-controller */ \"./js/components/databus-alert/databus-alert-controller.js\");\nconst EntityCardController = __webpack_require__(/*! ./components/entity-card/entity-card */ \"./js/components/entity-card/entity-card.js\");\nconst OverrideCheckboxController = __webpack_require__(/*! ./components/override-checkbox/override-checkbox */ \"./js/components/override-checkbox/override-checkbox.js\");\nconst AutofillDropdownController = __webpack_require__(/*! ./components/autofill-dropdown/autofill-dropdown */ \"./js/components/autofill-dropdown/autofill-dropdown.js\");\nconst DatabusIconController = __webpack_require__(/*! ./components/databus-icon/databus-icon */ \"./js/components/databus-icon/databus-icon.js\");\nconst TypeTagController = __webpack_require__(/*! ./components/type-tag/type-tag */ \"./js/components/type-tag/type-tag.js\");\nconst CollectionEditorWidgetController = __webpack_require__(/*! ./components/collection-editor-widget/collection-editor-widget */ \"./js/components/collection-editor-widget/collection-editor-widget.js\");\nconst CollectionHierarchyControllerTwo = __webpack_require__(/*! ./components/collection-hierarchy-two/collection-hierarchy */ \"./js/components/collection-hierarchy-two/collection-hierarchy.js\");\nconst UriBreadcrumbsController = __webpack_require__(/*! ./components/uri-breadcrumbs/uri-breadcrumbs */ \"./js/components/uri-breadcrumbs/uri-breadcrumbs.js\");\nconst TableEditorController = __webpack_require__(/*! ./components/table-editor/table-editor */ \"./js/components/table-editor/table-editor.js\");\nconst MultiselectDropdownController = __webpack_require__(/*! ./components/multiselect-dropdown/multiselect-dropdown */ \"./js/components/multiselect-dropdown/multiselect-dropdown.js\");\nconst FileBrowserController = __webpack_require__(/*! ./components/file-browser/file-browser */ \"./js/components/file-browser/file-browser.js\");\nconst FacetsViewController = __webpack_require__(/*! ./components/facets-view/facets-view */ \"./js/components/facets-view/facets-view.js\");\nconst ExpandableArrowController = __webpack_require__(/*! ./components/expandable-arrow/expandable-arrow */ \"./js/components/expandable-arrow/expandable-arrow.js\");\nconst YasqeTextController = __webpack_require__(/*! ./components/yasqe-text/yasqe-text */ \"./js/components/yasqe-text/yasqe-text.js\");\nconst YasrViewController = __webpack_require__(/*! ./components/yasr-view/yasr-view */ \"./js/components/yasr-view/yasr-view.js\");\nconst CollectionStatisticsController = __webpack_require__(/*! ./components/collection-statistics/collection-statistics */ \"./js/components/collection-statistics/collection-statistics.js\");\nconst CollectionNodeController = __webpack_require__(/*! ./components/collection-node/collection-node */ \"./js/components/collection-node/collection-node.js\");\nconst CollectionSearchController = __webpack_require__(/*! ./components/collection-search/collection-search */ \"./js/components/collection-search/collection-search.js\");\nconst CollectionStatusController = __webpack_require__(/*! ./components/collection-status/collection-status */ \"./js/components/collection-status/collection-status.js\");\nconst CollectionDataTableController = __webpack_require__(/*! ./components/collection-data-table/collection-data-table */ \"./js/components/collection-data-table/collection-data-table.js\");\nconst AccountHistoryController = __webpack_require__(/*! ./components/account-history/account-history */ \"./js/components/account-history/account-history.js\");\nconst SparqlEditorController = __webpack_require__(/*! ./page-controller/sparql-editor-controller */ \"./js/page-controller/sparql-editor-controller.js\");\nconst BetterDropdownController = __webpack_require__(/*! ./components/better-dropdown/better-dropdown */ \"./js/components/better-dropdown/better-dropdown.js\");\n\nvar databusApplication = angular.module(\"databusApplication\", [])\n .controller(\"HeaderController\", [\"$scope\", \"$http\", \"collectionManager\", HeaderController])\n .factory('collectionManager', [ \"$interval\", \"$http\", function ($interval, $http) { return new DatabusCollectionManager($http, $interval, 'databus_collections'); }])\n .factory('searchManager', [ \"$interval\", \"$http\", function ($interval, $http) { return new SearchManager($http, $interval); }])\n .factory('focus', [\"$timeout\", \"$window\", function ($timeout, $window) {\n return function (id) {\n $timeout(function () {\n var element = $window.document.getElementById(id);\n if (element)\n element.focus();\n });\n };\n }])\n .controller(\"HeaderController\", [\"$scope\", \"$http\", \"collectionManager\", \"searchManager\", HeaderController])\n .controller(\"AccountPageController\", [\"$scope\", \"$http\", \"$location\", \"collectionManager\", AccountPageController])\n .controller(\"FrontPageController\", [\"$scope\", \"$sce\", \"$http\", FrontPageController])\n .controller(\"ArtifactPageController\", [\"$scope\", \"$http\", \"$sce\", \"$location\", \"collectionManager\", ArtifactPageController])\n .controller(\"CollectionController\", [\"$scope\", \"$sce\", \"$http\", \"collectionManager\", CollectionController])\n .controller(\"CollectionsEditorController\", [\"$scope\", \"$timeout\", \"$http\", \"$location\", \"collectionManager\", CollectionsEditorController])\n .controller(\"GroupPageController\", [\"$scope\", \"$http\", \"$sce\", \"$interval\", \"$location\", \"collectionManager\", GroupPageController])\n .controller(\"ProfileController\", [\"$scope\", \"$http\", ProfileController])\n .controller(\"SparqlEditorController\", [\"$scope\", \"$http\", SparqlEditorController])\n .controller(\"PublishWizardController\", [\"$scope\", \"$http\", \"$interval\", \"focus\", \"$q\", PublishWizardController])\n .controller(\"VersionPageController\", [\"$scope\", \"$http\", \"$sce\", \"$location\", \"collectionManager\", VersionPageController])\n .directive('uploadRanking', function () {\n return {\n restrict: 'E',\n replace: true,\n templateUrl: '/website/templates/upload-ranking.html',\n scope: {\n data: '=data'\n }\n }\n });\n \nfunction config($locationProvider) {\n $locationProvider.html5Mode({\n enabled: true,\n requireBase: false,\n rewriteLinks: false\n });\n};\n\ndatabusApplication.filter('collectionfilter', function() {\n return function(input, search) {\n if (!input) return input;\n \n var expected = '';\n\n if (search != null) {\n expected = ('' + search).toLowerCase();\n }\n\n var result = [];\n\n angular.forEach(input, function(value, key) {\n if(value.title == undefined) {\n return;\n }\n \n if(value.title.toLowerCase().includes(expected)) {\n result.push(value); \n }\n });\n\n return result;\n }\n});\n\ndatabusApplication.config(['$locationProvider', config]);\n\n// Components\ndatabusApplication.component('overrideCheckbox', {\n templateUrl: '/js/components/override-checkbox/override-checkbox.html',\n controller: OverrideCheckboxController,\n bindings: {\n checkValue: '<',\n label: '<',\n id: '<',\n readonly: '<',\n isOverride: '<',\n onChange: '&'\n }\n});\n\ndatabusApplication.component('accountHistory', {\n templateUrl: '/js/components/account-history/account-history.html',\n controller: [ '$http', AccountHistoryController ],\n bindings: {\n accountName: '<'\n }\n});\n\n// Components\ndatabusApplication.component('databusAlert', {\n templateUrl: '/js/components/databus-alert/databus-alert.html',\n controller: [ '$scope', '$timeout', DatabusAlertController ],\n});\n\ndatabusApplication.component('entityCard', {\n templateUrl: '/js/components/entity-card/entity-card.html',\n controller: ['$sce', EntityCardController ],\n bindings: {\n label: '<',\n uri: '<',\n desc: '<',\n date: '<',\n type: '<',\n imageUrl: '<',\n absolute: '<'\n }\n});\n\ndatabusApplication.component('search', {\n templateUrl: '/js/components/search/search.html',\n controller: ['$http', '$interval', '$sce', 'searchManager', SearchController],\n bindings: {\n searchInput: '=',\n settings: '<',\n }\n});\n\n/*\ndatabusApplication.component('databusSearch', {\n templateUrl: '/js/components/databus-search/databus-search.html',\n controller: ['$http', '$interval', '$sce', DatabusSearchController],\n bindings: {\n filters: '=',\n input: '='\n }\n});*/\n\ndatabusApplication.component('autofillDropdown', {\n templateUrl: '/js/components/autofill-dropdown/autofill-dropdown.html',\n controller: ['$timeout', AutofillDropdownController ],\n bindings: {\n input: '=',\n values: '<',\n isDisabled: '<',\n placeholder: '@',\n onChange: '&'\n }\n});\n\n\ndatabusApplication.component('databusIcon', {\n templateUrl: '/js/components/databus-icon/databus-icon.html',\n controller: DatabusIconController,\n bindings: {\n size: '<',\n shape: '<',\n onClick: '&',\n isClickable: '<',\n color: '<'\n }\n});\n\ndatabusApplication.component('typeTag', {\n templateUrl: '/js/components/type-tag/type-tag.html',\n controller: TypeTagController,\n bindings: {\n type: '<',\n height: '<',\n width: '<',\n }\n});\n\n/*\n\ndatabusApplication.component('collectionEditor', {\n templateUrl: '/js/components/collection-editor/collection-editor.html',\n controller: ['$http', '$location', '$sce', CollectionEditorController],\n bindings: {\n collection: '=',\n readonly: '<',\n onPublish: '&',\n onDelete: '&',\n loggedIn: '<'\n }\n});*/\n\ndatabusApplication.component('collectionEditorWidget', {\n templateUrl: '/js/components/collection-editor-widget/collection-editor-widget.html',\n controller: ['collectionManager', '$scope', CollectionEditorWidgetController ],\n bindings: {\n selection: '<',\n collection: '=',\n }\n});\n\n/*\ndatabusApplication.component('collectionHierarchy', {\n templateUrl: '/js/components/collection-hierarchy/collection-hierarchy.html',\n controller: ['$http', '$location', '$sce', CollectionHierarchyController],\n bindings: {\n collection: '=',\n readonly: '<',\n onPublish: '&',\n onDelete: '&',\n loggedIn: '<',\n onChange: '&'\n }\n});*/\n\ndatabusApplication.component('collectionHierarchyTwo', {\n templateUrl: '/js/components/collection-hierarchy-two/collection-hierarchy.html',\n controller: ['$http', '$location', '$sce', '$scope', 'collectionManager', CollectionHierarchyControllerTwo ],\n bindings: {\n collection: '=',\n onChange: '&',\n onAddContent: '&'\n }\n});\n\ndatabusApplication.component('collectionNode', {\n templateUrl: '/js/components/collection-node/collection-node.html',\n controller: CollectionNodeController,\n bindings: {\n node: '<',\n readonly: '<',\n onRemoveNode: '&',\n onClick: '&',\n count: '<',\n isExpandable: '<'\n }\n});\n\ndatabusApplication.component('collectionSearch', {\n templateUrl: '/js/components/collection-search/collection-search.html',\n controller: ['collectionManager', '$http', '$interval', '$sce', CollectionSearchController ],\n bindings: {\n collection: '=',\n targetDatabusUrl: '<',\n onComponentAdded: '&'\n }\n});\n\ndatabusApplication.component('collectionStatistics', {\n templateUrl: '/js/components/collection-statistics/collection-statistics.html',\n controller: ['$http', '$scope', '$location', '$sce', CollectionStatisticsController ],\n bindings: {\n collection: '<'\n }\n});\n\ndatabusApplication.component('collectionStatus', {\n templateUrl: '/js/components/collection-status/collection-status.html',\n controller: ['$http', '$location', '$sce', CollectionStatusController ],\n bindings: {\n hasLocalChanges: '<',\n isPublished: '<',\n isDraft: '<',\n }\n});\n\n/*\n\ndatabusApplication.component('editLabel', {\n templateUrl: '/js/components/edit-label/edit-label.html',\n controller: ['$element', EditLabelController],\n bindings: {\n text: '=',\n singleLine: '<',\n onBlur: '&',\n onChange: '&'\n }\n});*/\n\ndatabusApplication.component('expandableArrow', {\n templateUrl: '/js/components/expandable-arrow/expandable-arrow.html',\n controller: ExpandableArrowController,\n bindings: {\n expanded: '=',\n onChange: '&',\n isReadonly: '<'\n }\n});\n\ndatabusApplication.component('facetsView', {\n templateUrl: '/js/components/facets-view/facets-view.html',\n controller: ['$http', '$scope', FacetsViewController ],\n bindings: {\n node: '=',\n readonly: '<',\n resourceType: '@',\n onChange: '&',\n onLoaded: '&'\n }\n});\n\ndatabusApplication.component('facetsViewHorizontal', {\n templateUrl: '/js/components/facets-view/facets-view-horizontal.html',\n controller: ['$http', '$scope', FacetsViewController ],\n bindings: {\n node: '=',\n readonly: '<',\n resourceType: '@',\n onChange: '&',\n onLoaded: '&'\n }\n});\n\ndatabusApplication.component('fileBrowser', {\n templateUrl: '/js/components/file-browser/file-browser.html',\n controller: ['$http', '$scope', FileBrowserController ],\n bindings: {\n resourceUri: '<',\n resourceType: '@',\n node: '<',\n facetSettings: '<',\n parentFacetSettings: '<',\n query: '<',\n fullQuery: '<',\n config: '<'\n }\n});\n\n/*\n\ndatabusApplication.component('multiselectArtifactDropdown', {\n templateUrl: '/js/components/multiselect-artifact-dropdown/multiselect-artifact-dropdown.html',\n controller: ['$timeout', '$sce', MultiselectArtifactDropdownController],\n bindings: {\n data: '<',\n node: '<',\n values: '<',\n isDisabled: '<',\n icon: '<',\n onChange: '&'\n }\n});*/\n\ndatabusApplication.component('multiselectDropdown', {\n templateUrl: '/js/components/multiselect-dropdown/multiselect-dropdown.html',\n controller: ['$timeout', '$sce', MultiselectDropdownController],\n bindings: {\n parentInput: '<',\n input: '=',\n values: '<',\n isDisabled: '<',\n placeholder: '@',\n onChange: '&'\n }\n});\n\ndatabusApplication.component('tableEditor', {\n templateUrl: '/js/components/table-editor/table-editor.html',\n controller: TableEditorController,\n bindings: {\n model: '=',\n onRemoveFile: '&',\n onAnalyzeFile: '&',\n analysisProcesses: '<'\n }\n});\n\ndatabusApplication.component('uriBreadcrumbs', {\n templateUrl: '/js/components/uri-breadcrumbs/uri-breadcrumbs.html',\n controller: UriBreadcrumbsController,\n bindings: {\n uri: '<',\n absolute: '<'\n }\n});\n\n\ndatabusApplication.component('yasqeText', {\n templateUrl: '/js/components/yasqe-text/yasqe-text.html',\n controller: ['$scope', '$element', YasqeTextController ],\n bindings: {\n query: '=',\n autoSize: '<',\n readOnly: '<',\n onChange: '&',\n onSend: '&',\n hasSend: '<'\n }\n});\n\ndatabusApplication.component('betterDropdown', {\n templateUrl: '/js/components/better-dropdown/better-dropdown.html',\n controller: ['$scope', '$interval', '$element', BetterDropdownController ],\n bindings: {\n rootNode: '=',\n onNodeClicked: '&',\n icon: '<',\n label: '<'\n }\n});\n\n\ndatabusApplication.component('yasrView', {\n templateUrl: '/js/components/yasr-view/yasr-view.html',\n controller: ['$scope', '$element', YasrViewController ],\n bindings: {\n data: '=',\n autoSize: '<',\n readOnly: '<',\n onChange: '&'\n }\n});\n\ndatabusApplication.component('collectionDataTable', {\n templateUrl: '/js/components/collection-data-table/collection-data-table.html',\n controller: ['$http', '$scope', '$location', '$sce', CollectionDataTableController],\n bindings: {\n collection: '<'\n }\n});\n\n\ndatabusApplication.directive('selectOnClick', ['$window', function ($window) {\n return {\n restrict: 'A',\n link: function (scope, element, attrs) {\n element.on('click', function () {\n if (!$window.getSelection().toString() && this.readonly == false) {\n // Required for mobile Safari\n this.setSelectionRange(0, this.value.length)\n }\n });\n }\n };\n}]);\n\ndatabusApplication.directive('focusMe', ['$timeout', '$parse', function ($timeout, $parse) {\n return {\n //scope: true, // optionally create a child scope\n link: function (scope, element, attrs) {\n var model = $parse(attrs.focusMe);\n scope.$watch(model, function (value) {\n if (value === true) {\n $timeout(function () {\n element[0].focus();\n });\n }\n });\n }\n };\n}]);\n\ndatabusApplication.directive('eventFocus', function (focus) {\n return function (scope, elem, attr) {\n elem.on(attr.eventFocus, function () {\n focus(attr.eventFocusId);\n });\n\n // Removes bound events in the element itself\n // when the scope is destroyed\n scope.$on('$destroy', function () {\n elem.off(attr.eventFocus);\n });\n };\n});\n\n\n\ndatabusApplication.directive('uploaderRanking', function () {\n return {\n restrict: 'E',\n replace: true,\n template: '
UserUploadsDerived Data
{{ row.account }}{{ row.numUploads }}{{ row.uploadSize }}
',\n scope: {\n data: '=data',\n }\n }\n});\n\n\ndatabusApplication.directive('groupsTable', function () {\n return {\n restrict: 'E',\n replace: true,\n template: '
Group Id# Artifacts
{{ row.label }}{{ row.artifactCount }}
',\n scope: {\n data: '=data',\n }\n }\n});\n\n\ndatabusApplication.directive('activityChart', function () {\n return {\n restrict: 'E',\n replace: true,\n template: '',\n scope: {\n data: '=data',\n height: '=height'\n },\n link: function (scope, element, attrs) {\n\n var svgHeight = scope.height;\n\n for (d in scope.data) {\n scope.data[d].date = new Date(scope.data[d].date);\n }\n\n var svg = d3.select(element[0])\n .attr(\"id\", \"graph\")\n .attr(\"width\", \"107%\")\n .attr(\"height\", svgHeight);\n\n var bounds = svg.node().getBoundingClientRect();\n var svgWidth = bounds.width;\n\n var margin = { top: 20, right: 50, bottom: 60, left: 50 };\n var width = svgWidth - margin.left - margin.right;\n var height = svgHeight - margin.top - margin.bottom;\n\n var g = svg.append(\"g\")\n .attr(\"transform\",\n \"translate(\" + margin.left + \",\" + margin.top + \")\"\n );\n\n var x = d3.scaleTime().rangeRound([0, width]);\n var y = d3.scaleLinear().rangeRound([height, 0]);\n\n var line = d3.line()\n .x(function (d) { return x(d.date) })\n .y(function (d) { return y(d.value) })\n\n x.domain(d3.extent(scope.data, function (d) { return d.date }));\n y.domain(d3.extent(scope.data, function (d) { return d.value }));\n\n g.append(\"g\")\n .attr(\"transform\", \"translate(0,\" + height + \")\")\n .call(d3.axisBottom(x))\n .selectAll(\"text\")\n .attr(\"y\", 0)\n .attr(\"x\", 9)\n .attr(\"dy\", \".35em\")\n .attr(\"transform\", \"rotate(90)\")\n .style(\"text-anchor\", \"start\");\n\n g.append(\"g\")\n .call(d3.axisLeft(y))\n .append(\"text\")\n .attr(\"fill\", \"#000\")\n .attr(\"transform\", \"rotate(-90)\")\n .attr(\"y\", 6)\n .attr(\"dy\", \"1em\")\n .attr(\"font-size\", \"1.1em\")\n .attr(\"text-anchor\", \"end\")\n .text(\"Uploaded Data (GByte)\");\n\n var path = g.append(\"path\")\n .datum(scope.data)\n .attr(\"fill\", \"none\")\n .attr(\"stroke\", \"steelblue\")\n .attr(\"stroke-linejoin\", \"round\")\n .attr(\"stroke-linecap\", \"round\")\n .attr(\"stroke-width\", 2)\n .attr(\"d\", line);\n }\n }\n});\n\ndatabusApplication.directive('onFinishRender', ['$timeout', '$parse', function ($timeout, $parse) {\n return {\n restrict: 'A',\n link: function (scope, element, attr) {\n if (scope.$last === true) {\n $timeout(function () {\n scope.$emit('ngRepeatFinished');\n if (!!attr.onFinishRender) {\n $parse(attr.onFinishRender)(scope);\n }\n });\n }\n }\n }\n}]);\n\ndatabusApplication.directive('clickOutside', [\n '$document', '$parse', '$timeout',\n clickOutside\n]);\n\n/**\n * @ngdoc directive\n * @name angular-click-outside.directive:clickOutside\n * @description Directive to add click outside capabilities to DOM elements\n * @requires $document\n * @requires $parse\n * @requires $timeout\n **/\n function clickOutside($document, $parse, $timeout) {\n return {\n restrict: 'A',\n link: function($scope, elem, attr) {\n\n // postpone linking to next digest to allow for unique id generation\n $timeout(function() {\n var classList = (attr.outsideIfNot !== undefined) ? attr.outsideIfNot.split(/[ ,]+/) : [],\n fn;\n\n function eventHandler(e) {\n var i,\n element,\n r,\n id,\n classNames,\n l;\n\n // check if our element already hidden and abort if so\n if (angular.element(elem).hasClass(\"ng-hide\")) {\n return;\n }\n\n // if there is no click target, no point going on\n if (!e || !e.target) {\n return;\n }\n\n // loop through the available elements, looking for classes in the class list that might match and so will eat\n for (element = e.target; element; element = element.parentNode) {\n // check if the element is the same element the directive is attached to and exit if so (props @CosticaPuntaru)\n if (element === elem[0]) {\n return;\n }\n \n // now we have done the initial checks, start gathering id's and classes\n id = element.id,\n classNames = element.className,\n l = classList.length;\n\n // Unwrap SVGAnimatedString classes\n if (classNames && classNames.baseVal !== undefined) {\n classNames = classNames.baseVal;\n }\n\n // if there are no class names on the element clicked, skip the check\n if (classNames || id) {\n\n // loop through the elements id's and classnames looking for exceptions\n for (i = 0; i < l; i++) {\n //prepare regex for class word matching\n r = new RegExp('\\\\b' + classList[i] + '\\\\b');\n\n // check for exact matches on id's or classes, but only if they exist in the first place\n if ((id !== undefined && id === classList[i]) || (classNames && r.test(classNames))) {\n // now let's exit out as it is an element that has been defined as being ignored for clicking outside\n return;\n }\n }\n }\n }\n\n // if we have got this far, then we are good to go with processing the command passed in via the click-outside attribute\n $timeout(function() {\n fn = $parse(attr['clickOutside']);\n fn($scope, { event: e });\n });\n }\n\n // if the devices has a touchscreen, listen for this event\n if (_hasTouch()) {\n $document.on('touchstart', eventHandler);\n }\n\n // still listen for the click event even if there is touch to cater for touchscreen laptops\n $document.on('click', eventHandler);\n\n // when the scope is destroyed, clean up the documents event handlers as we don't want it hanging around\n $scope.$on('$destroy', function() {\n if (_hasTouch()) {\n $document.off('touchstart', eventHandler);\n }\n\n $document.off('click', eventHandler);\n });\n\n /**\n * @description Private function to attempt to figure out if we are on a touch device\n * @private\n **/\n function _hasTouch() {\n // works on most browsers, IE10/11 and Surface\n return 'ontouchstart' in window || navigator.maxTouchPoints;\n };\n });\n }\n };\n}\n\n\n//# sourceURL=webpack://databus-webapp/./js/angular-application.js?"); +eval("const AccountPageController = __webpack_require__(/*! ./page-controller/account-controller */ \"./js/page-controller/account-controller.js\");\r\nconst ArtifactPageController = __webpack_require__(/*! ./page-controller/artifact-controller */ \"./js/page-controller/artifact-controller.js\");\r\nconst FrontPageController = __webpack_require__(/*! ./page-controller/frontpage-controller */ \"./js/page-controller/frontpage-controller.js\");\r\nconst HeaderController = __webpack_require__(/*! ./page-controller/header-controller */ \"./js/page-controller/header-controller.js\");\r\nconst CollectionController = __webpack_require__(/*! ./page-controller/collection-controller */ \"./js/page-controller/collection-controller.js\");\r\nconst CollectionsEditorController = __webpack_require__(/*! ./page-controller/collections-editor-controller */ \"./js/page-controller/collections-editor-controller.js\");\r\nconst GroupPageController = __webpack_require__(/*! ./page-controller/group-controller */ \"./js/page-controller/group-controller.js\");\r\nconst ProfileController = __webpack_require__(/*! ./page-controller/profile-controller */ \"./js/page-controller/profile-controller.js\");\r\nconst PublishWizardController = __webpack_require__(/*! ./page-controller/publish-wizard-controller */ \"./js/page-controller/publish-wizard-controller.js\");\r\nconst VersionPageController = __webpack_require__(/*! ./page-controller/version-controller */ \"./js/page-controller/version-controller.js\");\r\nconst UserSettingsController = __webpack_require__(/*! ./page-controller/user-settings-controller */ \"./js/page-controller/user-settings-controller.js\");\r\nconst DatabusCollectionManager = __webpack_require__(/*! ./collections/databus-collection-manager */ \"./js/collections/databus-collection-manager.js\");\r\nconst SearchManager = __webpack_require__(/*! ./search/search-manager */ \"./js/search/search-manager.js\");\r\nconst SearchController = __webpack_require__(/*! ./components/search/search-controller */ \"./js/components/search/search-controller.js\");\r\nconst DatabusAlertController = __webpack_require__(/*! ./components/databus-alert/databus-alert-controller */ \"./js/components/databus-alert/databus-alert-controller.js\");\r\nconst EntityCardController = __webpack_require__(/*! ./components/entity-card/entity-card */ \"./js/components/entity-card/entity-card.js\");\r\nconst OverrideCheckboxController = __webpack_require__(/*! ./components/override-checkbox/override-checkbox */ \"./js/components/override-checkbox/override-checkbox.js\");\r\nconst AutofillDropdownController = __webpack_require__(/*! ./components/autofill-dropdown/autofill-dropdown */ \"./js/components/autofill-dropdown/autofill-dropdown.js\");\r\nconst DatabusIconController = __webpack_require__(/*! ./components/databus-icon/databus-icon */ \"./js/components/databus-icon/databus-icon.js\");\r\nconst TypeTagController = __webpack_require__(/*! ./components/type-tag/type-tag */ \"./js/components/type-tag/type-tag.js\");\r\nconst CollectionEditorWidgetController = __webpack_require__(/*! ./components/collection-editor-widget/collection-editor-widget */ \"./js/components/collection-editor-widget/collection-editor-widget.js\");\r\nconst CollectionHierarchyControllerTwo = __webpack_require__(/*! ./components/collection-hierarchy-two/collection-hierarchy */ \"./js/components/collection-hierarchy-two/collection-hierarchy.js\");\r\nconst UriBreadcrumbsController = __webpack_require__(/*! ./components/uri-breadcrumbs/uri-breadcrumbs */ \"./js/components/uri-breadcrumbs/uri-breadcrumbs.js\");\r\nconst TableEditorController = __webpack_require__(/*! ./components/table-editor/table-editor */ \"./js/components/table-editor/table-editor.js\");\r\nconst MultiselectDropdownController = __webpack_require__(/*! ./components/multiselect-dropdown/multiselect-dropdown */ \"./js/components/multiselect-dropdown/multiselect-dropdown.js\");\r\nconst FileBrowserController = __webpack_require__(/*! ./components/file-browser/file-browser */ \"./js/components/file-browser/file-browser.js\");\r\nconst FacetsViewController = __webpack_require__(/*! ./components/facets-view/facets-view */ \"./js/components/facets-view/facets-view.js\");\r\nconst ExpandableArrowController = __webpack_require__(/*! ./components/expandable-arrow/expandable-arrow */ \"./js/components/expandable-arrow/expandable-arrow.js\");\r\nconst YasqeTextController = __webpack_require__(/*! ./components/yasqe-text/yasqe-text */ \"./js/components/yasqe-text/yasqe-text.js\");\r\nconst YasrViewController = __webpack_require__(/*! ./components/yasr-view/yasr-view */ \"./js/components/yasr-view/yasr-view.js\");\r\nconst CollectionStatisticsController = __webpack_require__(/*! ./components/collection-statistics/collection-statistics */ \"./js/components/collection-statistics/collection-statistics.js\");\r\nconst CollectionNodeController = __webpack_require__(/*! ./components/collection-node/collection-node */ \"./js/components/collection-node/collection-node.js\");\r\nconst CollectionSearchController = __webpack_require__(/*! ./components/collection-search/collection-search */ \"./js/components/collection-search/collection-search.js\");\r\nconst CollectionStatusController = __webpack_require__(/*! ./components/collection-status/collection-status */ \"./js/components/collection-status/collection-status.js\");\r\nconst CollectionDataTableController = __webpack_require__(/*! ./components/collection-data-table/collection-data-table */ \"./js/components/collection-data-table/collection-data-table.js\");\r\nconst AccountHistoryController = __webpack_require__(/*! ./components/account-history/account-history */ \"./js/components/account-history/account-history.js\");\r\nconst SparqlEditorController = __webpack_require__(/*! ./page-controller/sparql-editor-controller */ \"./js/page-controller/sparql-editor-controller.js\");\r\nconst BetterDropdownController = __webpack_require__(/*! ./components/better-dropdown/better-dropdown */ \"./js/components/better-dropdown/better-dropdown.js\");\r\nconst NavSearchController = __webpack_require__(/*! ./components/nav-search/nav-search-controller */ \"./js/components/nav-search/nav-search-controller.js\");\r\nconst EntityDropdownController = __webpack_require__(/*! ./components/entity-dropdown/entity-dropdown */ \"./js/components/entity-dropdown/entity-dropdown.js\");\r\nconst EntityApiViewController = __webpack_require__(/*! ./components/entity-api-view/entity-api-view */ \"./js/components/entity-api-view/entity-api-view.js\");\r\nconst ErrorNotificationController = __webpack_require__(/*! ./components/error-notification/error-notifcation */ \"./js/components/error-notification/error-notifcation.js\");\r\n\r\nvar databusApplication = angular.module(\"databusApplication\", [])\r\n .controller(\"HeaderController\", [\"$scope\", \"$http\", \"collectionManager\", HeaderController])\r\n .factory('collectionManager', [ \"$interval\", \"$http\", function ($interval, $http) { return new DatabusCollectionManager($http, $interval, 'databus_collections'); }])\r\n .factory('searchManager', [ \"$interval\", \"$http\", function ($interval, $http) { return new SearchManager($http, $interval); }])\r\n .factory('focus', [\"$timeout\", \"$window\", function ($timeout, $window) {\r\n return function (id) {\r\n $timeout(function () {\r\n var element = $window.document.getElementById(id);\r\n if (element)\r\n element.focus();\r\n });\r\n };\r\n }])\r\n .controller(\"UserSettingsController\", [ \"$scope\", \"$http\", \"$sce\", \"$location\", UserSettingsController])\r\n .controller(\"HeaderController\", [\"$scope\", \"$http\", \"collectionManager\", \"searchManager\", HeaderController])\r\n .controller(\"AccountPageController\", [\"$scope\", \"$http\", \"$location\", \"collectionManager\", AccountPageController])\r\n .controller(\"FrontPageController\", [\"$scope\", \"$sce\", \"$http\", FrontPageController])\r\n .controller(\"ArtifactPageController\", [\"$scope\", \"$http\", \"$sce\", \"$location\", \"collectionManager\", ArtifactPageController])\r\n .controller(\"CollectionController\", [\"$scope\", \"$sce\", \"$http\", \"collectionManager\", CollectionController])\r\n .controller(\"CollectionsEditorController\", [\"$scope\", \"$timeout\", \"$http\", \"$location\", \"collectionManager\", CollectionsEditorController])\r\n .controller(\"GroupPageController\", [\"$scope\", \"$http\", \"$sce\", \"$interval\", \"$location\", \"collectionManager\", GroupPageController])\r\n .controller(\"ProfileController\", [\"$scope\", \"$http\", ProfileController])\r\n .controller(\"SparqlEditorController\", [\"$scope\", \"$http\", \"$location\", SparqlEditorController])\r\n .controller(\"PublishWizardController\", [\"$scope\", \"$http\", \"$interval\", \"focus\", \"$q\", \"$location\", PublishWizardController])\r\n .controller(\"VersionPageController\", [\"$scope\", \"$http\", \"$sce\", \"$location\", \"collectionManager\", VersionPageController])\r\n .directive('uploadRanking', function () {\r\n return {\r\n restrict: 'E',\r\n replace: true,\r\n templateUrl: '/website/templates/upload-ranking.html',\r\n scope: {\r\n data: '=data'\r\n }\r\n }\r\n });\r\n \r\nfunction config($locationProvider) {\r\n $locationProvider.html5Mode({\r\n enabled: true,\r\n requireBase: false,\r\n rewriteLinks: false\r\n });\r\n};\r\n\r\ndatabusApplication.filter('collectionfilter', function() {\r\n return function(input, search) {\r\n if (!input) return input;\r\n \r\n var expected = '';\r\n\r\n if (search != null) {\r\n expected = ('' + search).toLowerCase();\r\n }\r\n\r\n var result = [];\r\n\r\n angular.forEach(input, function(value, key) {\r\n if(value.title == undefined) {\r\n return;\r\n }\r\n \r\n if(value.title.toLowerCase().includes(expected)) {\r\n result.push(value); \r\n }\r\n });\r\n\r\n return result;\r\n }\r\n});\r\n\r\ndatabusApplication.config(['$locationProvider', config]);\r\n\r\n// Components\r\ndatabusApplication.component('overrideCheckbox', {\r\n templateUrl: '/js/components/override-checkbox/override-checkbox.html',\r\n controller: OverrideCheckboxController,\r\n bindings: {\r\n checkValue: '<',\r\n label: '<',\r\n id: '<',\r\n readonly: '<',\r\n isOverride: '<',\r\n onChange: '&'\r\n }\r\n});\r\n\r\ndatabusApplication.component('errorTag', {\r\n controller: ErrorNotificationController,\r\n templateUrl: '/js/components/error-notification/error-notification.html',\r\n bindings: {\r\n entity: '<',\r\n key: '@',\r\n texts: '<'\r\n }\r\n});\r\n\r\ndatabusApplication.component('entityDropdown', {\r\n bindings: {\r\n placeholder: '@',\r\n items: '<',\r\n displayProperty: '@',\r\n loading: '<',\r\n selected: '<',\r\n onSelect: '&'\r\n },\r\n controller: EntityDropdownController,\r\n templateUrl: '/js/components/entity-dropdown/entity-dropdown.html'\r\n});\r\n\r\ndatabusApplication.component('entityApiView', {\r\n bindings: {\r\n entity: '<',\r\n apiKeys: '<',\r\n texts: '<',\r\n publishLog: '<'\r\n },\r\n controller: EntityApiViewController,\r\n templateUrl: '/js/components/entity-api-view/entity-api-view.html'\r\n });\r\n\r\n\r\n\r\ndatabusApplication.component('accountHistory', {\r\n templateUrl: '/js/components/account-history/account-history.html',\r\n controller: [ '$http', AccountHistoryController ],\r\n bindings: {\r\n accountName: '<'\r\n }\r\n});\r\n\r\n// Components\r\ndatabusApplication.component('databusAlert', {\r\n templateUrl: '/js/components/databus-alert/databus-alert.html',\r\n controller: [ '$scope', '$timeout', DatabusAlertController ],\r\n});\r\n\r\ndatabusApplication.component('entityCard', {\r\n templateUrl: '/js/components/entity-card/entity-card.html',\r\n controller: ['$sce', EntityCardController ],\r\n bindings: {\r\n label: '<',\r\n uri: '<',\r\n desc: '<',\r\n date: '<',\r\n type: '<',\r\n imageUrl: '<',\r\n absolute: '<'\r\n }\r\n});\r\n\r\ndatabusApplication.component('search', {\r\n templateUrl: '/js/components/search/search.html',\r\n controller: ['$http', '$interval', '$sce', 'searchManager', SearchController],\r\n bindings: {\r\n searchInput: '=',\r\n settings: '<',\r\n }\r\n});\r\n\r\n\r\ndatabusApplication.component('navSearch', {\r\n templateUrl: '/js/components/nav-search/nav-search.html',\r\n controller: ['$http', '$interval', '$sce', 'searchManager', NavSearchController],\r\n bindings: {\r\n searchInput: '=',\r\n settings: '<',\r\n }\r\n});\r\n\r\n/*\r\ndatabusApplication.component('databusSearch', {\r\n templateUrl: '/js/components/databus-search/databus-search.html',\r\n controller: ['$http', '$interval', '$sce', DatabusSearchController],\r\n bindings: {\r\n filters: '=',\r\n input: '='\r\n }\r\n});*/\r\n\r\ndatabusApplication.component('autofillDropdown', {\r\n templateUrl: '/js/components/autofill-dropdown/autofill-dropdown.html',\r\n controller: ['$timeout', AutofillDropdownController ],\r\n bindings: {\r\n input: '=',\r\n values: '<',\r\n isDisabled: '<',\r\n placeholder: '@',\r\n onChange: '&'\r\n }\r\n});\r\n\r\n\r\ndatabusApplication.component('databusIcon', {\r\n templateUrl: '/js/components/databus-icon/databus-icon.html',\r\n controller: DatabusIconController,\r\n bindings: {\r\n size: '<',\r\n shape: '<',\r\n onClick: '&',\r\n isClickable: '<',\r\n color: '<'\r\n }\r\n});\r\n\r\ndatabusApplication.component('typeTag', {\r\n templateUrl: '/js/components/type-tag/type-tag.html',\r\n controller: TypeTagController,\r\n bindings: {\r\n type: '<',\r\n height: '<',\r\n width: '<',\r\n }\r\n});\r\n\r\n/*\r\n\r\ndatabusApplication.component('collectionEditor', {\r\n templateUrl: '/js/components/collection-editor/collection-editor.html',\r\n controller: ['$http', '$location', '$sce', CollectionEditorController],\r\n bindings: {\r\n collection: '=',\r\n readonly: '<',\r\n onPublish: '&',\r\n onDelete: '&',\r\n loggedIn: '<'\r\n }\r\n});*/\r\n\r\ndatabusApplication.component('collectionEditorWidget', {\r\n templateUrl: '/js/components/collection-editor-widget/collection-editor-widget.html',\r\n controller: ['collectionManager', '$scope', CollectionEditorWidgetController ],\r\n bindings: {\r\n selection: '<',\r\n collection: '=',\r\n }\r\n});\r\n\r\n/*\r\ndatabusApplication.component('collectionHierarchy', {\r\n templateUrl: '/js/components/collection-hierarchy/collection-hierarchy.html',\r\n controller: ['$http', '$location', '$sce', CollectionHierarchyController],\r\n bindings: {\r\n collection: '=',\r\n readonly: '<',\r\n onPublish: '&',\r\n onDelete: '&',\r\n loggedIn: '<',\r\n onChange: '&'\r\n }\r\n});*/\r\n\r\ndatabusApplication.component('collectionHierarchyTwo', {\r\n templateUrl: '/js/components/collection-hierarchy-two/collection-hierarchy.html',\r\n controller: ['$http', '$location', '$sce', '$scope', 'collectionManager', CollectionHierarchyControllerTwo ],\r\n bindings: {\r\n collection: '=',\r\n onChange: '&',\r\n onAddContent: '&'\r\n }\r\n});\r\n\r\ndatabusApplication.component('collectionNode', {\r\n templateUrl: '/js/components/collection-node/collection-node.html',\r\n controller: CollectionNodeController,\r\n bindings: {\r\n node: '<',\r\n readonly: '<',\r\n onRemoveNode: '&',\r\n onClick: '&',\r\n count: '<',\r\n isExpandable: '<'\r\n }\r\n});\r\n\r\ndatabusApplication.component('collectionSearch', {\r\n templateUrl: '/js/components/collection-search/collection-search.html',\r\n controller: ['collectionManager', '$http', '$interval', '$sce', CollectionSearchController ],\r\n bindings: {\r\n collection: '=',\r\n targetDatabusUrl: '<',\r\n onComponentAdded: '&'\r\n }\r\n});\r\n\r\ndatabusApplication.component('collectionStatistics', {\r\n templateUrl: '/js/components/collection-statistics/collection-statistics.html',\r\n controller: ['$http', '$scope', '$location', '$sce', CollectionStatisticsController ],\r\n bindings: {\r\n collection: '<'\r\n }\r\n});\r\n\r\ndatabusApplication.component('collectionStatus', {\r\n templateUrl: '/js/components/collection-status/collection-status.html',\r\n controller: ['$http', '$location', '$sce', CollectionStatusController ],\r\n bindings: {\r\n hasLocalChanges: '<',\r\n isPublished: '<',\r\n isDraft: '<',\r\n }\r\n});\r\n\r\n/*\r\n\r\ndatabusApplication.component('editLabel', {\r\n templateUrl: '/js/components/edit-label/edit-label.html',\r\n controller: ['$element', EditLabelController],\r\n bindings: {\r\n text: '=',\r\n singleLine: '<',\r\n onBlur: '&',\r\n onChange: '&'\r\n }\r\n});*/\r\n\r\ndatabusApplication.component('expandableArrow', {\r\n templateUrl: '/js/components/expandable-arrow/expandable-arrow.html',\r\n controller: ExpandableArrowController,\r\n bindings: {\r\n expanded: '=',\r\n onChange: '&',\r\n isReadonly: '<'\r\n }\r\n});\r\n\r\ndatabusApplication.component('facetsView', {\r\n templateUrl: '/js/components/facets-view/facets-view.html',\r\n controller: ['$http', '$scope', FacetsViewController ],\r\n bindings: {\r\n node: '=',\r\n readonly: '<',\r\n resourceType: '@',\r\n onChange: '&',\r\n onLoaded: '&'\r\n }\r\n});\r\n\r\ndatabusApplication.component('facetsViewHorizontal', {\r\n templateUrl: '/js/components/facets-view/facets-view-horizontal.html',\r\n controller: ['$http', '$scope', FacetsViewController ],\r\n bindings: {\r\n node: '=',\r\n readonly: '<',\r\n resourceType: '@',\r\n onChange: '&',\r\n onLoaded: '&'\r\n }\r\n});\r\n\r\ndatabusApplication.component('fileBrowser', {\r\n templateUrl: '/js/components/file-browser/file-browser.html',\r\n controller: ['$http', '$scope', FileBrowserController ],\r\n bindings: {\r\n resourceUri: '<',\r\n resourceType: '@',\r\n node: '<',\r\n facetSettings: '<',\r\n parentFacetSettings: '<',\r\n query: '<',\r\n fullQuery: '<',\r\n config: '<'\r\n }\r\n});\r\n\r\n/*\r\n\r\ndatabusApplication.component('multiselectArtifactDropdown', {\r\n templateUrl: '/js/components/multiselect-artifact-dropdown/multiselect-artifact-dropdown.html',\r\n controller: ['$timeout', '$sce', MultiselectArtifactDropdownController],\r\n bindings: {\r\n data: '<',\r\n node: '<',\r\n values: '<',\r\n isDisabled: '<',\r\n icon: '<',\r\n onChange: '&'\r\n }\r\n});*/\r\n\r\ndatabusApplication.component('multiselectDropdown', {\r\n templateUrl: '/js/components/multiselect-dropdown/multiselect-dropdown.html',\r\n controller: ['$timeout', '$sce', MultiselectDropdownController],\r\n bindings: {\r\n parentInput: '<',\r\n input: '=',\r\n values: '<',\r\n isDisabled: '<',\r\n placeholder: '@',\r\n onChange: '&'\r\n }\r\n});\r\n\r\ndatabusApplication.component('tableEditor', {\r\n templateUrl: '/js/components/table-editor/table-editor.html',\r\n controller: TableEditorController,\r\n bindings: {\r\n model: '=',\r\n onRemoveFile: '&',\r\n onEditContentVariant: '&',\r\n onAnalyzeFile: '&',\r\n analysisProcesses: '<'\r\n }\r\n});\r\n\r\ndatabusApplication.component('uriBreadcrumbs', {\r\n templateUrl: '/js/components/uri-breadcrumbs/uri-breadcrumbs.html',\r\n controller: UriBreadcrumbsController,\r\n bindings: {\r\n uri: '<',\r\n absolute: '<'\r\n }\r\n});\r\n\r\n\r\ndatabusApplication.component('yasqeText', {\r\n templateUrl: '/js/components/yasqe-text/yasqe-text.html',\r\n controller: ['$scope', '$element', YasqeTextController ],\r\n bindings: {\r\n query: '=',\r\n autoSize: '<',\r\n readOnly: '<',\r\n onChange: '&',\r\n onSend: '&',\r\n hasSend: '<'\r\n }\r\n});\r\n\r\ndatabusApplication.component('betterDropdown', {\r\n templateUrl: '/js/components/better-dropdown/better-dropdown.html',\r\n controller: ['$scope', '$interval', '$element', BetterDropdownController ],\r\n bindings: {\r\n rootNode: '=',\r\n onNodeClicked: '&',\r\n icon: '<',\r\n label: '<'\r\n }\r\n});\r\n\r\n\r\ndatabusApplication.component('yasrView', {\r\n templateUrl: '/js/components/yasr-view/yasr-view.html',\r\n controller: ['$scope', '$element', YasrViewController ],\r\n bindings: {\r\n data: '=',\r\n autoSize: '<',\r\n readOnly: '<',\r\n onChange: '&'\r\n }\r\n});\r\n\r\ndatabusApplication.component('collectionDataTable', {\r\n templateUrl: '/js/components/collection-data-table/collection-data-table.html',\r\n controller: ['$http', '$scope', '$location', '$sce', CollectionDataTableController],\r\n bindings: {\r\n collection: '<'\r\n }\r\n});\r\n\r\n\r\ndatabusApplication.directive('selectOnClick', ['$window', function ($window) {\r\n return {\r\n restrict: 'A',\r\n link: function (scope, element, attrs) {\r\n element.on('click', function () {\r\n if (!$window.getSelection().toString() && this.readonly == false) {\r\n // Required for mobile Safari\r\n this.setSelectionRange(0, this.value.length)\r\n }\r\n });\r\n }\r\n };\r\n}]);\r\n\r\ndatabusApplication.directive('focusMe', ['$timeout', '$parse', function ($timeout, $parse) {\r\n return {\r\n //scope: true, // optionally create a child scope\r\n link: function (scope, element, attrs) {\r\n var model = $parse(attrs.focusMe);\r\n scope.$watch(model, function (value) {\r\n if (value === true) {\r\n $timeout(function () {\r\n element[0].focus();\r\n });\r\n }\r\n });\r\n }\r\n };\r\n}]);\r\n\r\ndatabusApplication.directive('eventFocus', function (focus) {\r\n return function (scope, elem, attr) {\r\n elem.on(attr.eventFocus, function () {\r\n focus(attr.eventFocusId);\r\n });\r\n\r\n // Removes bound events in the element itself\r\n // when the scope is destroyed\r\n scope.$on('$destroy', function () {\r\n elem.off(attr.eventFocus);\r\n });\r\n };\r\n});\r\n\r\n\r\n\r\ndatabusApplication.directive('uploaderRanking', function () {\r\n return {\r\n restrict: 'E',\r\n replace: true,\r\n template: '
UserUploadsDerived Data
{{ row.account }}{{ row.numUploads }}{{ row.uploadSize }}
',\r\n scope: {\r\n data: '=data',\r\n }\r\n }\r\n});\r\n\r\n\r\ndatabusApplication.directive('groupsTable', function () {\r\n return {\r\n restrict: 'E',\r\n replace: true,\r\n template: '
Group Id# Artifacts
{{ row.label }}{{ row.artifactCount }}
',\r\n scope: {\r\n data: '=data',\r\n }\r\n }\r\n});\r\n\r\n\r\ndatabusApplication.directive('activityChart', function () {\r\n return {\r\n restrict: 'E',\r\n replace: true,\r\n template: '',\r\n scope: {\r\n data: '=data',\r\n height: '=height'\r\n },\r\n link: function (scope, element, attrs) {\r\n\r\n var svgHeight = scope.height;\r\n\r\n for (d in scope.data) {\r\n scope.data[d].date = new Date(scope.data[d].date);\r\n }\r\n\r\n var svg = d3.select(element[0])\r\n .attr(\"id\", \"graph\")\r\n .attr(\"width\", \"107%\")\r\n .attr(\"height\", svgHeight);\r\n\r\n var bounds = svg.node().getBoundingClientRect();\r\n var svgWidth = bounds.width;\r\n\r\n var margin = { top: 20, right: 50, bottom: 60, left: 50 };\r\n var width = svgWidth - margin.left - margin.right;\r\n var height = svgHeight - margin.top - margin.bottom;\r\n\r\n var g = svg.append(\"g\")\r\n .attr(\"transform\",\r\n \"translate(\" + margin.left + \",\" + margin.top + \")\"\r\n );\r\n\r\n var x = d3.scaleTime().rangeRound([0, width]);\r\n var y = d3.scaleLinear().rangeRound([height, 0]);\r\n\r\n var line = d3.line()\r\n .x(function (d) { return x(d.date) })\r\n .y(function (d) { return y(d.value) })\r\n\r\n x.domain(d3.extent(scope.data, function (d) { return d.date }));\r\n y.domain(d3.extent(scope.data, function (d) { return d.value }));\r\n\r\n g.append(\"g\")\r\n .attr(\"transform\", \"translate(0,\" + height + \")\")\r\n .call(d3.axisBottom(x))\r\n .selectAll(\"text\")\r\n .attr(\"y\", 0)\r\n .attr(\"x\", 9)\r\n .attr(\"dy\", \".35em\")\r\n .attr(\"transform\", \"rotate(90)\")\r\n .style(\"text-anchor\", \"start\");\r\n\r\n g.append(\"g\")\r\n .call(d3.axisLeft(y))\r\n .append(\"text\")\r\n .attr(\"fill\", \"#000\")\r\n .attr(\"transform\", \"rotate(-90)\")\r\n .attr(\"y\", 6)\r\n .attr(\"dy\", \"1em\")\r\n .attr(\"font-size\", \"1.1em\")\r\n .attr(\"text-anchor\", \"end\")\r\n .text(\"Uploaded Data (GByte)\");\r\n\r\n var path = g.append(\"path\")\r\n .datum(scope.data)\r\n .attr(\"fill\", \"none\")\r\n .attr(\"stroke\", \"steelblue\")\r\n .attr(\"stroke-linejoin\", \"round\")\r\n .attr(\"stroke-linecap\", \"round\")\r\n .attr(\"stroke-width\", 2)\r\n .attr(\"d\", line);\r\n }\r\n }\r\n});\r\n\r\ndatabusApplication.directive('onFinishRender', ['$timeout', '$parse', function ($timeout, $parse) {\r\n return {\r\n restrict: 'A',\r\n link: function (scope, element, attr) {\r\n if (scope.$last === true) {\r\n $timeout(function () {\r\n scope.$emit('ngRepeatFinished');\r\n if (!!attr.onFinishRender) {\r\n $parse(attr.onFinishRender)(scope);\r\n }\r\n });\r\n }\r\n }\r\n }\r\n}]);\r\n\r\ndatabusApplication.directive('clickOutside', [\r\n '$document', '$parse', '$timeout',\r\n clickOutside\r\n]);\r\n\r\n/**\r\n * @ngdoc directive\r\n * @name angular-click-outside.directive:clickOutside\r\n * @description Directive to add click outside capabilities to DOM elements\r\n * @requires $document\r\n * @requires $parse\r\n * @requires $timeout\r\n **/\r\n function clickOutside($document, $parse, $timeout) {\r\n return {\r\n restrict: 'A',\r\n link: function($scope, elem, attr) {\r\n\r\n // postpone linking to next digest to allow for unique id generation\r\n $timeout(function() {\r\n var classList = (attr.outsideIfNot !== undefined) ? attr.outsideIfNot.split(/[ ,]+/) : [],\r\n fn;\r\n\r\n function eventHandler(e) {\r\n var i,\r\n element,\r\n r,\r\n id,\r\n classNames,\r\n l;\r\n\r\n // check if our element already hidden and abort if so\r\n if (angular.element(elem).hasClass(\"ng-hide\")) {\r\n return;\r\n }\r\n\r\n // if there is no click target, no point going on\r\n if (!e || !e.target) {\r\n return;\r\n }\r\n\r\n // loop through the available elements, looking for classes in the class list that might match and so will eat\r\n for (element = e.target; element; element = element.parentNode) {\r\n // check if the element is the same element the directive is attached to and exit if so (props @CosticaPuntaru)\r\n if (element === elem[0]) {\r\n return;\r\n }\r\n \r\n // now we have done the initial checks, start gathering id's and classes\r\n id = element.id,\r\n classNames = element.className,\r\n l = classList.length;\r\n\r\n // Unwrap SVGAnimatedString classes\r\n if (classNames && classNames.baseVal !== undefined) {\r\n classNames = classNames.baseVal;\r\n }\r\n\r\n // if there are no class names on the element clicked, skip the check\r\n if (classNames || id) {\r\n\r\n // loop through the elements id's and classnames looking for exceptions\r\n for (i = 0; i < l; i++) {\r\n //prepare regex for class word matching\r\n r = new RegExp('\\\\b' + classList[i] + '\\\\b');\r\n\r\n // check for exact matches on id's or classes, but only if they exist in the first place\r\n if ((id !== undefined && id === classList[i]) || (classNames && r.test(classNames))) {\r\n // now let's exit out as it is an element that has been defined as being ignored for clicking outside\r\n return;\r\n }\r\n }\r\n }\r\n }\r\n\r\n // if we have got this far, then we are good to go with processing the command passed in via the click-outside attribute\r\n $timeout(function() {\r\n fn = $parse(attr['clickOutside']);\r\n fn($scope, { event: e });\r\n });\r\n }\r\n\r\n // if the devices has a touchscreen, listen for this event\r\n if (_hasTouch()) {\r\n $document.on('touchstart', eventHandler);\r\n }\r\n\r\n // still listen for the click event even if there is touch to cater for touchscreen laptops\r\n $document.on('click', eventHandler);\r\n\r\n // when the scope is destroyed, clean up the documents event handlers as we don't want it hanging around\r\n $scope.$on('$destroy', function() {\r\n if (_hasTouch()) {\r\n $document.off('touchstart', eventHandler);\r\n }\r\n\r\n $document.off('click', eventHandler);\r\n });\r\n\r\n /**\r\n * @description Private function to attempt to figure out if we are on a touch device\r\n * @private\r\n **/\r\n function _hasTouch() {\r\n // works on most browsers, IE10/11 and Surface\r\n return 'ontouchstart' in window || navigator.maxTouchPoints;\r\n };\r\n });\r\n }\r\n };\r\n}\r\n\n\n//# sourceURL=webpack://databus-webapp/./js/angular-application.js?"); /***/ }), @@ -25,7 +25,7 @@ eval("const AccountPageController = __webpack_require__(/*! ./page-controller/ac \******************************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("const DatabusUris = __webpack_require__(/*! ../utils/databus-uris */ \"./js/utils/databus-uris.js\");\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\nconst DatabusCollectionUtils = __webpack_require__(/*! ./databus-collection-utils */ \"./js/collections/databus-collection-utils.js\");\nconst DatabusCollectionWrapper = __webpack_require__(/*! ./databus-collection-wrapper */ \"./js/collections/databus-collection-wrapper.js\");\n\nclass DatabusCollectionManager {\n\n // Daten die wir haben:\n\n // Liste von Remote Collections (ungeladen) { uri: databus.org/asdf, label: asdffasd }\n // Liste von Working Copies in der Local Storage\n // Beispiel:\n // [0] : { uri: databus.org/asdf, label: asdffasd, content: { ... }, ... }\n // [1] : { uri: databus.org/asdsdff, label: asdasdfffasd }\n\n\n\n // On Initizialze:\n // Fuer alle remove collections -> finde lokale Kopie / erzeuge lokale Kopie\n\n // On Select / On Set Active\n // 1: Ist es ein Draft -> uri ist undefined\n // Ja? -> Collection direkt als Draft Anzeigen\n // Nein? -> Ist Collection schon geladen? content ist nicht undefined\n // Ja? -> Lade async, uberschreibe remote entry\n // Nein? -> Lade async, setze remote und local entry\n\n constructor($http, $interval, storageKey) {\n\n try {\n this.storageKeyPrefix = `${storageKey}_${encodeURI(DATABUS_RESOURCE_BASE_URL)}`;\n // window.sessionStorage.removeItem(`${this.storageKeyPrefix}_session`);\n \n this.sessionInfo = JSON.parse(window.sessionStorage.getItem(`${this.storageKeyPrefix}_session`));\n \n if (this.sessionInfo == undefined) {\n this.sessionInfo = {};\n }\n } catch(err) {\n this.sessionInfo = {};\n }\n \n\n this.http = $http;\n this.interval = $interval;\n }\n\n clearSession() {\n this.sessionInfo = {};\n window.sessionStorage.setItem(`${this.storageKeyPrefix}_session`, JSON.stringify(this.sessionInfo));\n }\n\n get accountName() {\n return this.sessionInfo != undefined ? this.sessionInfo.accountName : undefined;\n }\n\n async tryInitialize(accountName, loadFromServer) {\n\n if(accountName == undefined) {\n return;\n }\n\n this.sessionInfo.accountName = accountName;\n window.sessionStorage.setItem(`${this.storageKeyPrefix}_session`, JSON.stringify(this.sessionInfo));\n\n this.storageKey = `${this.storageKeyPrefix}__${accountName}`;\n this.remote = this.loadCollectionsFromStorage(false);\n this.local = this.loadCollectionsFromStorage(true);\n this.findActive();\n\n\n if (loadFromServer) {\n try {\n var res = await this.http.get(`/app/account/collections?account=${accountName}`);\n this.initialize(res.data);\n\n } catch (e) {\n console.log(`Failed to initialze collection manager.`);\n console.log(e);\n }\n }\n\n var self = this;\n\n this.interval(function () {\n var storageHash = window.localStorage.getItem(`${self.storageKey}_hash`);\n\n if (storageHash != self.currentHash) {\n self.local = JSON.parse(window.localStorage.getItem(self.storageKey));\n self.currentHash = storageHash;\n\n for (let identifier in self.local) {\n if (identifier === undefined || identifier === \"undefined\") {\n delete (self.local[identifier]);\n } else {\n //enable Collection Utils for all collections in local storage\n self.local[identifier] = new DatabusCollectionWrapper(self.local[identifier]);\n }\n }\n\n if (self.onCollectionChangedInDifferentTab != null) {\n self.onCollectionChangedInDifferentTab();\n }\n }\n }, 300);\n }\n\n // Setze das remote array und update local array\n initialize(remoteCollections) {\n // We keep remote entries and local entries separately to detect diffs\n this.remote = {};\n\n // Load everyting from the local browser storage. All entries in the local browser\n // storage are indexed with a UUID identifier.\n // Remote collections that are pulled to the local browser storage will\n // also be given such an identifier\n // this.local = this.loadCollectionsFromLocalStorage();\n\n // This map will keep track of all local entries that already claim to have a remote counterpart\n let localPublished = {};\n\n for (let identifier in this.local) {\n if (!identifier.startsWith('___')) {\n delete this.local[identifier];\n continue;\n }\n\n if (identifier !== this.local[identifier].uuid) {\n delete this.local[identifier];\n continue;\n }\n\n let localCollection = this.local[identifier];\n\n if (localCollection.uri !== undefined && remoteCollections !== undefined) {\n let uri = localCollection.uri;\n // The local collection already has a URI\n if (remoteCollections[uri] === undefined) {\n // There is no remote collection with that URI - delete it! Keep the collection as a draft\n delete (this.local[identifier].uri);\n delete (this.local[identifier].issued);\n delete (this.local[identifier].created);\n } else {\n // Remember that the collection with uri already has a working copy\n localPublished[uri] = true;\n // Also remember the remote entry as an entry with a local working copy\n this.remote[identifier] = remoteCollections[uri];\n // Make sure the unchangeable values are set to the remote entry\n this.local[identifier].publisher = remoteCollections[uri].publisher;\n this.local[identifier].issued = remoteCollections[uri].issued;\n this.local[identifier].created = remoteCollections[uri].created;\n this.local[identifier].files = remoteCollections[uri].files;\n }\n }\n }\n\n for (let uri in remoteCollections) {\n if (localPublished[uri] === undefined) {\n // We don't have a working copy in our local storage yet, time to create an identifier!\n let identifier = DatabusCollectionUtils.uuidv4();\n remoteCollections[uri].uuid = identifier;\n remoteCollections[uri].isHidden = remoteCollections[uri].issued == undefined;\n // Create two entries, one in the local map, one in the remote map\n this.local[identifier] = DatabusCollectionUtils.createCleanCopy(remoteCollections[uri]);\n this.remote[identifier] = DatabusCollectionUtils.createCleanCopy(remoteCollections[uri]);\n }\n }\n\n for (let identifier in this.local) {\n // The local collection is now either a draft or a working copy of the remote - wrap it.\n this.local[identifier] = new DatabusCollectionWrapper(this.local[identifier]);\n // Sanitize content\n if (!(this.local[identifier].content instanceof Object)) {\n this.local[identifier].content = { groups: [], customQueries: [] };\n }\n }\n\n /*\n let activeIdentifier = this.activeCollectionIdentifier;\n // Set first collection as active\n\n if (this.local[activeIdentifier] !== undefined) {\n this.activeCollectionIdentifier = activeIdentifier;\n }\n */\n\n // Call this always in header-controller.js\n if (this.activeCollection == null) {\n // select first or create a new draft if we don't have any local drafts yet\n this.selectFirstOrCreate();\n }\n\n // QueryNode.assignParents(this.activeCollection.content.root);\n\n // Save locally in case we created any local working copies\n this.saveLocally();\n\n if (this.initialized !== undefined) {\n this.initialized();\n }\n\n\n }\n\n\n\n findActive() {\n if (!this.isInitialized) throw \"Databus-Collection-Manager is not initialized1.\";\n if (this.activeCollection == undefined) {\n this.selectFirstOrCreate();\n }\n }\n\n get isInitialized() {\n return this.accountName != null;\n }\n\n loadCollectionsFromStorage(local = true) {\n let collections;\n\n if (local) {\n collections = JSON.parse(window.localStorage.getItem(this.storageKey));\n } else {\n collections = JSON.parse(window.sessionStorage.getItem(this.storageKey));\n }\n\n if (collections == null) {\n collections = {};\n }\n\n for (let identifier in collections) {\n if (identifier === undefined || identifier === \"undefined\") {\n delete (collections[identifier]);\n } else {\n //enable Collection Utils for all collections in local storage\n collections[identifier] = new DatabusCollectionWrapper(collections[identifier]);\n }\n }\n\n return collections;\n }\n\n /**\n * Selects the first collection in the local list or creates a new draft\n */\n selectFirstOrCreate() {\n\n for (let identifier in this.local) {\n this.setActive(identifier);\n break;\n }\n\n // Create new collection if current is null\n if (this.activeCollection == null) {\n this.createNew(\"Unnamed Collection\", \"\", function (response) { });\n }\n }\n\n setActive(uuid) {\n if (!this.isInitialized) throw \"Databus-Collection-Manager is not initialized1.\";\n\n this.convertCollectionContentToTree(uuid);\n\n let collection = this.local[uuid];\n // QueryNode.assignParents(collection.content.root);\n\n this.sessionInfo.activeCollectionIdentifier = uuid;\n window.sessionStorage.setItem(`${this.storageKeyPrefix}_session`, JSON.stringify(this.sessionInfo));\n\n }\n\n get activeCollectionIdentifier() {\n return this.sessionInfo != null ? this.sessionInfo.activeCollectionIdentifier : null;\n }\n\n get activeCollection() {\n if (this.activeCollectionIdentifier == null) {\n return null;\n }\n\n if (this.local == null) {\n return null;\n }\n\n return this.local[this.activeCollectionIdentifier];\n }\n\n convertCollectionContentToTree(uuid) {\n let collection = this.local[uuid];\n\n if (collection.content.root !== undefined) {\n return;\n }\n\n collection.content.root = new QueryNode(null, null);\n\n for (var g in collection.content.groups) {\n var group = collection.content.groups[g];\n var groupNode = new QueryNode(group.uri, 'databus:group');\n\n // add group facets\n for (var s in group.settings) {\n var setting = group.settings[s];\n\n if (setting.value === 'SYSTEM_LATEST_ARTIFACT_VERSION' || setting.value === 'SYSTEM_LATEST_GROUP_VERSION') {\n setting.value = '$latest';\n }\n\n groupNode.setFacet(setting.facet, setting.value, setting.checked);\n }\n\n collection.content.root.addChild(groupNode);\n\n\n for (var a in group.artifacts) {\n var artifact = group.artifacts[a];\n\n var artifactNode = new QueryNode(artifact.uri, 'databus:artifact');\n\n // add artifact facets\n\n groupNode.addChild(artifactNode);\n }\n }\n\n }\n\n createSnapshot(source) { // convert each version=\"latest\" to actual latest version\n if (!this.isInitialized) throw \"Databus-Collection-Manager is not initialized.\";\n\n let collection = DatabusCollectionWrapper.createNew();\n collection.content = DatabusCollectionUtils.createCleanCopy(source.content);\n\n let root = collection.content.root;\n for (var g in root.childNodes) {\n var graph = root.childNodes[g];\n\n for (var s in graph.facetSettings) {\n if (graph.facetSettings[s][0].value === '$latest') {\n this.http.get('/app/utils/facets', {\n params: { uri: artifact.uri, type: 'group' }\n }).then(function (result) {\n let versions = result.data[\"http://purl.org/dc/terms/hasVersion\"].values;\n let latestVersion = versions.reduce(function (a, b) { return a > b ? a : b; });\n artifact.facetSettings[s][0].value = latestVersion;\n });\n }\n }\n\n for (var a in graph.childNodes) {\n var artifact = graph.childNodes[a];\n\n for (var s in artifact.facetSettings) {\n if (artifact.facetSettings[s][0].value === '$latest') {\n this.http.get('/app/utils/facets', {\n params: { uri: artifact.uri, type: 'artifact' }\n }).then(function (result) {\n let versions = result.data[\"http://purl.org/dc/terms/hasVersion\"].values;\n let latestVersion = versions.reduce(function (a, b) { return a > b ? a : b; });\n artifact.facetSettings[s][0].value = latestVersion;\n });\n }\n }\n\n }\n\n }\n\n\n collection.title = `Snapshot of ${source.title}`;\n collection.description = source.description;\n\n this.local[collection.uuid] = new DatabusCollectionWrapper(collection);\n this.saveLocally();\n this.setActive(collection.uuid);\n\n return collection;\n }\n\n saveLocally() {\n if (!this.isInitialized) throw \"Databus-Collection-Manager is not initialized.\";\n\n if (this.activeCollection != null) {\n this.activeCollection.hasLocalChanges = this.hasLocalChanges(this.activeCollection);\n }\n\n var hash = DatabusCollectionUtils.cyrb53Hash(DatabusCollectionUtils.serialize(this.local));\n this.currentHash = hash;\n\n window.localStorage.setItem(`${this.storageKey}_hash`, hash);\n\n try {\n //write local collections to local storage\n window.localStorage.setItem(this.storageKey, DatabusCollectionUtils.serialize(this.local));\n //write remote collections to session storage\n window.sessionStorage.setItem(this.storageKey, DatabusCollectionUtils.serialize(this.remote));\n } catch (e) {\n console.log(e);\n }\n }\n\n hasLocalChanges(localCollection) {\n if (this.remote[localCollection.uuid] === undefined) {\n return true;\n }\n\n let remoteCollection = this.remote[localCollection.uuid];\n\n if (remoteCollection.isHidden != localCollection.isHidden) {\n return true;\n }\n\n if (localCollection.title !== remoteCollection.title) {\n return true;\n }\n\n if (localCollection.description !== remoteCollection.description) {\n return true;\n }\n\n let serializedRemoteContent = DatabusCollectionUtils.serialize(remoteCollection.content);\n let serializedLocalContent = DatabusCollectionUtils.serialize(localCollection.content);\n\n return serializedLocalContent !== serializedRemoteContent;\n }\n\n discardLocalChanges() {\n\n if (!this.isInitialized) throw \"Databus-Collection-Manager is not initialized.\";\n\n let uuid = this.activeCollection.uuid;\n\n if (this.remote[uuid] === undefined) {\n return;\n }\n\n let uri = this.activeCollection.uri;\n\n if (uri == undefined) {\n return;\n }\n\n this.local[uuid].title = this.remote[uuid].title;\n this.local[uuid].abstract = this.remote[uuid].abstract;\n this.local[uuid].description = this.remote[uuid].description;\n this.local[uuid].content = DatabusCollectionUtils.createCleanCopy(this.remote[uuid].content);\n this.local[uuid].hasLocalChanges = this.hasLocalChanges(this.local[uuid]);\n\n this.saveLocally();\n }\n\n addElement(elementQuery) {\n this.current.addElement(elementQuery);\n this.saveLocally();\n\n if (this.onActiveCollectionChanged != null) {\n this.onActiveCollectionChanged(this.current);\n }\n }\n\n removeElement(elementGuid) {\n this.current.removeElement(elementGuid);\n this.saveLocally();\n\n if (this.onActiveCollectionChanged != null) {\n this.onActiveCollectionChanged(this.current);\n }\n }\n\n\n\n createNew(title, description, callback) {\n if (!this.isInitialized) throw \"Databus-Collection-Manager is not initialized.\";\n\n let reg = /^\\w+[\\w\\s]*$/;\n\n if (title === undefined || !reg.test(title)) {\n callback(false);\n return;\n }\n\n let collection = DatabusCollectionWrapper.createNew(title, description, DATABUS_RESOURCE_BASE_URL);\n\n this.local[collection.uuid] = new DatabusCollectionWrapper(collection);\n this.setActive(collection.uuid);\n this.saveLocally();\n\n callback(true);\n }\n\n createDraft(callback) {\n if (!this.isInitialized) {\n return;\n }\n\n let collection = DatabusCollectionWrapper.createNew('', '');\n this.local[collection.uuid] = new DatabusCollectionWrapper(collection);\n this.setActive(collection.uuid);\n this.saveLocally()\n\n callback(DatabusResponse.COLLECTION_DRAFT_CREATED);\n }\n\n createCopy(source) {\n if (!this.isInitialized) throw \"Databus-Collection-Manager is not initialized.\";\n\n let collection = DatabusCollectionWrapper.createNew();\n collection.content = DatabusCollectionUtils.createCleanCopy(source.content);\n collection.title = `Copy of ${source.title}`;\n collection.abstract = source.abstract;\n collection.description = source.description;\n\n this.local[collection.uuid] = new DatabusCollectionWrapper(collection);\n this.saveLocally();\n this.setActive(collection.uuid);\n\n return collection;\n }\n\n\n\n deleteLocally() {\n delete this.local[this.activeCollection.uuid];\n this.saveLocally();\n }\n\n /**\n * Returns the collection or null\n * @param {[type]} uri [description]\n * @return {[type]} [description]\n */\n getCollectionByUri(uri) {\n if (uri == null)\n return null;\n\n for (let identifier in this.local) {\n if (uri === this.local[identifier].uri) {\n return this.local[identifier];\n }\n }\n return null;\n }\n\n /**\n * Returns the first collection or null\n * @return {[type]} [description]\n */\n getFirstCollection() {\n if (this.local.length === 0) {\n return null;\n }\n return this.local[0];\n }\n\n\n async changeCollection(username, collectionUri) {\n try {\n if (!this.isInitialized) throw \"Databus-Collection-Manager is not initialized.\";\n\n this.saveLocally();\n\n // Keep the identifier of the collection we want to push\n var pushIdentifier = this.activeCollection.uuid;\n var publisherUri = `${DATABUS_RESOURCE_BASE_URL}/${username}#this`;\n\n var ignoreKeys = [\n 'parent',\n '$$hashKey',\n 'expanded',\n 'files',\n 'eventListeners',\n 'hasLocalChanges',\n 'published',\n 'uuid',\n ];\n\n var contentString = encodeURIComponent(DatabusCollectionUtils.serialize(this.activeCollection.content, ignoreKeys));\n\n // Format collection as json-ld\n let collectionJsonLd = {\n \"@context\": DATABUS_CONTEXT[DatabusUris.JSONLD_CONTEXT],\n \"@graph\": [\n {\n \"@id\": collectionUri,\n \"@type\": \"Collection\",\n \"publisher\": publisherUri,\n \"title\": this.activeCollection.title,\n \"abstract\": this.activeCollection.abstract,\n \"description\": this.activeCollection.description,\n \"databus:collectionContent\": contentString,\n }\n ]\n };\n\n if (!this.activeCollection.isHidden) {\n collectionJsonLd[\"@graph\"][0].issued = DatabusUtils.timeStringNow();\n }\n\n var response = null;\n\n try {\n\n var relativeUri = new URL(collectionUri).pathname;\n response = await this.http.put(relativeUri, collectionJsonLd);\n\n } catch (errResponse) {\n console.log(errResponse);\n throw { code: errResponse.data.code };\n }\n\n try {\n var relativeUri = new URL(collectionUri).pathname;\n\n var response = await this.http({\n method: 'GET',\n url: relativeUri,\n headers: {\n 'Accept': 'application/ld+json',\n 'X-Jsonld-Formatting': 'compact'\n }\n });\n\n\n } catch (errResponse) {\n console.log(errResponse);\n throw { code: errResponse.data.code };\n }\n\n // Get the remotely saved collection from the payload\n var remoteGraph = response.data;\n\n // If the user changed the active collection in the meantime throw an error. This\n // should be prevented by a loading dialog\n if (this.activeCollection.uuid != pushIdentifier) {\n throw { code: DatabusResponse.COLLECTION_INVALID_ARGUMENT };\n }\n\n this.local[pushIdentifier].uri = remoteGraph['@id'];\n this.local[pushIdentifier].hasLocalChanges = false;\n this.local[pushIdentifier].modified = remoteGraph.modified;\n this.local[pushIdentifier].issued = remoteGraph.issued;\n // this.local[pushIdentifier].created = remoteGraph.created;\n\n //Update remote data\n this.remote[pushIdentifier] = JSON.parse(DatabusCollectionUtils.serialize(this.activeCollection));\n\n // Update the local data\n // this.local[pushIdentifier].uri = remoteGraph['@id'];\n //this.local[pushIdentifier].hasLocalChanges = this.hasLocalChanges(this.local[pushIdentifier]);\n //this.local[pushIdentifier].modified = this.activeCollection.modified;\n //this.local[pushIdentifier].issued = this.activeCollection.issued;\n //this.local[pushIdentifier].created = this.activeCollection.created;\n\n this.saveLocally();\n\n return response.data;\n\n } catch (err) {\n\n console.log(err);\n throw {\n code: err.data !== undefined && err.data.code !== undefined ? err.data.code :\n DatabusResponse.COLLECTION_UPDATE_ERROR\n };\n }\n }\n\n async updateCollection(username, collectionTag) {\n\n if (this.activeCollection.uri != null) {\n return await this.changeCollection(username, this.activeCollection.uri);\n } else {\n var collectionUri = `${DATABUS_RESOURCE_BASE_URL}/${username}/collections/${collectionTag}`;\n\n for (var uuid in this.local) {\n if (this.local[uuid].uri == collectionUri) {\n throw \"A collection with the specifed URI already exists.\";\n }\n }\n\n return await this.changeCollection(username, collectionUri);\n }\n }\n\n\n /**\n * Fetches the remote data of the current collection and assigns the field values to the local copy\n */\n async fetchCollection(uri) {\n try {\n if (!this.isInitialized) throw \"Databus-Collection-Manager is not initialized.\";\n\n var req = {\n method: 'GET',\n url: uri,\n headers: { 'Accept': 'application/json' }\n };\n\n var getResponse = await this.http(req);\n var collection = getCollectionByUri(uri);\n\n this.local[collection.uuid].content = getResponse.data.content;\n this.local[collection.uuid].created = getResponse.data.created;\n this.local[collection.uuid].issued = getResponse.data.issued;\n this.local[collection.uuid].title = getResponse.data.title;\n this.local[collection.uuid].description = getResponse.data.description;\n this.local[collection.uuid].files = getResponse.data.files;\n } catch (errResponse) {\n console.log(errResponse);\n return errResponse.data;\n }\n }\n\n async deleteCollection(username, collectionTag) {\n try {\n if (!this.isInitialized) throw \"Databus-Collection-Manager is not initialized.\";\n\n\n\n // Keep the identifier of the collection we want to push\n let deleteIdentifier = this.activeCollection.uuid;\n\n if (this.activeCollection.isDraft) {\n delete this.local[deleteIdentifier];\n this.saveLocally();\n return { code: DatabusResponse.COLLECTION_DELETED };\n }\n\n var targetUri = `/${username}/collections/${collectionTag}`;\n\n let deleteResponse = await this.http.delete(targetUri);\n\n delete this.remote[deleteIdentifier];\n delete this.local[deleteIdentifier];\n\n return deleteResponse.data;\n } catch (errResponse) {\n console.log(errResponse);\n return errResponse.data;\n }\n }\n\n /**\n * Deletes the active collection from the server but keeps the local storage entry\n */\n async unpublishActiveCollection() {\n\n if (!this.isInitialized) throw \"Databus-Collection-Manager is not initialized.\";\n\n if (this.activeCollection.isDraft) {\n throw \"Cannot unpublish an unpublished draft\";\n }\n\n // Keep the identifier of the collection we want to push\n let uuid = this.activeCollection.uuid;\n let identifier = DatabusUtils.uriToName(this.activeCollection.uri);\n\n var targetUri = `/${this.accountName}/collections/${identifier}`;\n await this.http.delete(targetUri);\n\n delete this.remote[uuid];\n delete this.local[uuid].uri;\n delete this.local[uuid].issued;\n this.saveLocally();\n }\n}\n\n\nmodule.exports = DatabusCollectionManager;\n\n//# sourceURL=webpack://databus-webapp/./js/collections/databus-collection-manager.js?"); +eval("const AppJsonFormatter = __webpack_require__(/*! ../utils/app-json-formatter */ \"./js/utils/app-json-formatter.js\");\nconst DatabusUris = __webpack_require__(/*! ../utils/databus-uris */ \"./js/utils/databus-uris.js\");\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\nconst DatabusCollectionUtils = __webpack_require__(/*! ./databus-collection-utils */ \"./js/collections/databus-collection-utils.js\");\nconst DatabusCollectionWrapper = __webpack_require__(/*! ./databus-collection-wrapper */ \"./js/collections/databus-collection-wrapper.js\");\n\nclass DatabusCollectionManager {\n\n // Daten die wir haben:\n\n // Liste von Remote Collections (ungeladen) { uri: databus.org/asdf, label: asdffasd }\n // Liste von Working Copies in der Local Storage\n // Beispiel:\n // [0] : { uri: databus.org/asdf, label: asdffasd, content: { ... }, ... }\n // [1] : { uri: databus.org/asdsdff, label: asdasdfffasd }\n\n\n\n // On Initizialze:\n // Fuer alle remove collections -> finde lokale Kopie / erzeuge lokale Kopie\n\n // On Select / On Set Active\n // 1: Ist es ein Draft -> uri ist undefined\n // Ja? -> Collection direkt als Draft Anzeigen\n // Nein? -> Ist Collection schon geladen? content ist nicht undefined\n // Ja? -> Lade async, uberschreibe remote entry\n // Nein? -> Lade async, setze remote und local entry\n\n _isInitialized = false;\n _initSubscribers = [];\n\n constructor($http, $interval, storageKey) {\n\n try {\n this.storageKeyPrefix = `${encodeURI(DATABUS_RESOURCE_BASE_URL)}`;\n // window.sessionStorage.removeItem(`${this.storageKeyPrefix}_session`);\n\n this.sessionInfo = JSON.parse(window.sessionStorage.getItem(`${this.storageKeyPrefix}_session`));\n\n if (this.sessionInfo == undefined) {\n this.sessionInfo = {};\n }\n\n window.sessionStorage.setItem(`${this.storageKeyPrefix}_session`, JSON.stringify(this.sessionInfo));\n this.storageKey = `${this.storageKeyPrefix}__collections`;\n this.local = this.loadCollectionsFromStorage(true);\n this.remote = {};\n\n } catch (err) {\n this.sessionInfo = {};\n }\n\n\n this.http = $http;\n this.interval = $interval;\n }\n\n clearSession() {\n this.sessionInfo = {};\n window.sessionStorage.setItem(`${this.storageKeyPrefix}_session`, JSON.stringify(this.sessionInfo));\n }\n\n get accountName() {\n return this.sessionInfo != undefined ? this.sessionInfo.accountName : undefined;\n }\n\n getLocalCollectionByUri(uri) {\n for (let guid in this.local) {\n let localCollection = this.local[guid];\n\n if (localCollection.uri == uri) {\n return localCollection;\n }\n }\n\n return undefined;\n }\n\n /**\n * Set up the collection mananger for a specific account.\n * 1) Load ALL the collections of this account from the remote\n * 2) Save to remote map\n * 3) Create local working copies if local has no entry for remote collection\n * @param {*} accountName \n * @returns \n */\n async tryInitialize(accountName) {\n\n // Needs an account name to set up\n if (accountName == undefined) {\n return;\n }\n\n // this.remote = this.loadCollectionsFromStorage(false);\n this.sessionInfo.accountName = accountName;\n\n\n\n let collectionListResponse = await this.http.get(`/app/account/collections?account=${encodeURIComponent(accountName)}`);\n let remoteCollections = collectionListResponse.data;\n\n\n let wasLocalCollectionAdded = false;\n\n for (let collectionUri in remoteCollections) {\n let remoteCollection = remoteCollections[collectionUri];\n let localCollection = this.getLocalCollectionByUri(collectionUri);\n\n // Create local copy if not exist\n if (localCollection == undefined) {\n localCollection = JSON.parse(JSON.stringify(remoteCollection));\n localCollection.uuid = DatabusCollectionUtils.uuidv4();\n this.local[localCollection.uuid] = localCollection;\n wasLocalCollectionAdded = true;\n }\n\n this.remote[localCollection.uuid] = remoteCollection;\n this.remote[localCollection.uuid].isHidden = this.remote[localCollection.uuid].issued == undefined;\n\n if(this.local[localCollection.uuid].isHidden == undefined) {\n this.local[localCollection.uuid].isHidden = this.remote[localCollection.uuid].isHidden;\n }\n }\n\n if(wasLocalCollectionAdded) {\n this.saveLocally();\n }\n\n this.findActive();\n\n /*\n\n if (loadFromServer) {\n try {\n var res = await this.http.get(`/app/account/collections?account=${accountName}`);\n this.initialize(res.data);\n\n } catch (e) {\n console.log(`Failed to initialze collection manager.`);\n console.log(e);\n }\n }\n */\n\n var self = this;\n\n this.interval(function () {\n var storageHash = window.localStorage.getItem(`${self.storageKey}_hash`);\n\n if (storageHash != self.currentHash) {\n self.local = JSON.parse(window.localStorage.getItem(self.storageKey));\n self.currentHash = storageHash;\n\n for (let identifier in self.local) {\n if (identifier === undefined || identifier === \"undefined\") {\n delete (self.local[identifier]);\n } else {\n //enable Collection Utils for all collections in local storage\n self.local[identifier] = new DatabusCollectionWrapper(self.local[identifier]);\n }\n }\n\n if (self.onCollectionChangedInDifferentTab != null) {\n self.onCollectionChangedInDifferentTab();\n }\n }\n }, 300);\n\n\n this._isInitialized = true;\n this._notifyInitialized();\n }\n\n get isInitialized() {\n return this._isInitialized;\n }\n\n subscribeOnInitialized(callback) {\n if (this._isInitialized) {\n callback();\n } else {\n this._initSubscribers.push(callback);\n }\n }\n\n _notifyInitialized() {\n this._initSubscribers.forEach(cb => cb());\n this._initSubscribers = [];\n }\n\n get hasAccountName() {\n return this.accountName != null;\n }\n\n // Setze das remote array und update local array\n initialize(remoteCollections) {\n // We keep remote entries and local entries separately to detect diffs\n this.remote = {};\n\n // Load everyting from the local browser storage. All entries in the local browser\n // storage are indexed with a UUID identifier.\n // Remote collections that are pulled to the local browser storage will\n // also be given such an identifier\n // this.local = this.loadCollectionsFromLocalStorage();\n\n // This map will keep track of all local entries that already claim to have a remote counterpart\n let localPublished = {};\n\n for (let identifier in this.local) {\n if (!identifier.startsWith('___')) {\n delete this.local[identifier];\n continue;\n }\n\n if (identifier !== this.local[identifier].uuid) {\n delete this.local[identifier];\n continue;\n }\n\n let localCollection = this.local[identifier];\n\n if (localCollection.uri !== undefined && remoteCollections !== undefined) {\n let uri = localCollection.uri;\n // The local collection already has a URI\n if (remoteCollections[uri] === undefined) {\n // There is no remote collection with that URI - delete it! Keep the collection as a draft\n delete (this.local[identifier].uri);\n delete (this.local[identifier].issued);\n delete (this.local[identifier].created);\n } else {\n // Remember that the collection with uri already has a working copy\n localPublished[uri] = true;\n // Also remember the remote entry as an entry with a local working copy\n this.remote[identifier] = remoteCollections[uri];\n // Make sure the unchangeable values are set to the remote entry\n this.local[identifier].publisher = remoteCollections[uri].publisher;\n this.local[identifier].issued = remoteCollections[uri].issued;\n this.local[identifier].created = remoteCollections[uri].created;\n this.local[identifier].files = remoteCollections[uri].files;\n }\n }\n }\n\n for (let uri in remoteCollections) {\n if (localPublished[uri] === undefined) {\n // We don't have a working copy in our local storage yet, time to create an identifier!\n let identifier = DatabusCollectionUtils.uuidv4();\n remoteCollections[uri].uuid = identifier;\n remoteCollections[uri].isHidden = remoteCollections[uri].issued == undefined;\n // Create two entries, one in the local map, one in the remote map\n this.local[identifier] = DatabusCollectionUtils.createCleanCopy(remoteCollections[uri]);\n this.remote[identifier] = DatabusCollectionUtils.createCleanCopy(remoteCollections[uri]);\n }\n }\n\n for (let identifier in this.local) {\n // The local collection is now either a draft or a working copy of the remote - wrap it.\n this.local[identifier] = new DatabusCollectionWrapper(this.local[identifier]);\n // Sanitize content\n if (!(this.local[identifier].content instanceof Object)) {\n this.local[identifier].content = { groups: [], customQueries: [] };\n }\n }\n\n /*\n let activeIdentifier = this.activeCollectionIdentifier;\n // Set first collection as active\n\n if (this.local[activeIdentifier] !== undefined) {\n this.activeCollectionIdentifier = activeIdentifier;\n }\n */\n\n\n\n // QueryNode.assignParents(this.activeCollection.content.root);\n\n // Save locally in case we created any local working copies\n\n this.saveLocally();\n\n // Call this always in header-controller.js\n if (this.activeCollection == null) {\n // select first or create a new draft if we don't have any local drafts yet\n this.selectFirstOrCreate();\n }\n\n }\n\n\n\n findActive() {\n if (!this.hasAccountName) throw \"Databus-Collection-Manager is not initialized1.\";\n if (this.activeCollection == undefined) {\n this.selectFirstOrCreate();\n }\n }\n\n\n loadCollectionsFromStorage(local = true) {\n let collections;\n\n if (local) {\n collections = JSON.parse(window.localStorage.getItem(this.storageKey));\n } else {\n collections = JSON.parse(window.sessionStorage.getItem(this.storageKey));\n }\n\n if (collections == null) {\n collections = {};\n }\n\n for (let identifier in collections) {\n if (identifier === undefined || identifier === \"undefined\") {\n delete (collections[identifier]);\n } else if (collections[identifier].accountName == null) {\n delete (collections[identifier]);\n } else {\n //enable Collection Utils for all collections in local storage\n collections[identifier] = new DatabusCollectionWrapper(collections[identifier]);\n }\n }\n\n return collections;\n }\n\n /**\n * Selects the first collection in the local list or creates a new draft\n */\n selectFirstOrCreate(accountName) {\n\n for (let identifier in this.local) {\n this.setActive(identifier);\n break;\n }\n\n // Create new collection if current is null\n if (this.activeCollection == null) {\n this.createNew(accountName, \"Unnamed Collection\", \"\", function (response) { });\n }\n }\n\n setActive(uuid) {\n if (!this.hasAccountName) throw \"Databus-Collection-Manager is not initialized1.\";\n\n this.convertCollectionContentToTree(uuid);\n\n let collection = this.local[uuid];\n // QueryNode.assignParents(collection.content.root);\n\n this.sessionInfo.activeCollectionIdentifier = uuid;\n window.sessionStorage.setItem(`${this.storageKeyPrefix}_session`, JSON.stringify(this.sessionInfo));\n\n }\n\n get activeCollectionIdentifier() {\n return this.sessionInfo != null ? this.sessionInfo.activeCollectionIdentifier : null;\n }\n\n get activeCollection() {\n if (this.activeCollectionIdentifier == null) {\n return null;\n }\n\n if (this.local == null) {\n return null;\n }\n\n return this.local[this.activeCollectionIdentifier];\n }\n\n convertCollectionContentToTree(uuid) {\n let collection = this.local[uuid];\n\n if (collection.content.root !== undefined) {\n return;\n }\n\n collection.content.root = new QueryNode(null, null);\n\n for (var g in collection.content.groups) {\n var group = collection.content.groups[g];\n var groupNode = new QueryNode(group.uri, 'databus:group');\n\n // add group facets\n for (var s in group.settings) {\n var setting = group.settings[s];\n\n if (setting.value === 'SYSTEM_LATEST_ARTIFACT_VERSION' || setting.value === 'SYSTEM_LATEST_GROUP_VERSION') {\n setting.value = '$latest';\n }\n\n groupNode.setFacet(setting.facet, setting.value, setting.checked);\n }\n\n collection.content.root.addChild(groupNode);\n\n\n for (var a in group.artifacts) {\n var artifact = group.artifacts[a];\n\n var artifactNode = new QueryNode(artifact.uri, 'databus:artifact');\n\n // add artifact facets\n\n groupNode.addChild(artifactNode);\n }\n }\n\n }\n\n createSnapshot(source) { // convert each version=\"latest\" to actual latest version\n if (!this.hasAccountName) throw \"Databus-Collection-Manager is not initialized.\";\n\n let collection = DatabusCollectionWrapper.createNew();\n collection.accountName = this.accountName;\n collection.content = DatabusCollectionUtils.createCleanCopy(source.content);\n\n let root = collection.content.root;\n for (var g in root.childNodes) {\n var graph = root.childNodes[g];\n\n for (var s in graph.facetSettings) {\n if (graph.facetSettings[s][0].value === '$latest') {\n this.http.get('/app/utils/facets', {\n params: { uri: artifact.uri, type: 'group' }\n }).then(function (result) {\n let versions = result.data[\"http://purl.org/dc/terms/hasVersion\"].values;\n let latestVersion = versions.reduce(function (a, b) { return a > b ? a : b; });\n artifact.facetSettings[s][0].value = latestVersion;\n });\n }\n }\n\n for (var a in graph.childNodes) {\n var artifact = graph.childNodes[a];\n\n for (var s in artifact.facetSettings) {\n if (artifact.facetSettings[s][0].value === '$latest') {\n this.http.get('/app/utils/facets', {\n params: { uri: artifact.uri, type: 'artifact' }\n }).then(function (result) {\n let versions = result.data[\"http://purl.org/dc/terms/hasVersion\"].values;\n let latestVersion = versions.reduce(function (a, b) { return a > b ? a : b; });\n artifact.facetSettings[s][0].value = latestVersion;\n });\n }\n }\n\n }\n\n }\n\n\n collection.title = `Snapshot of ${source.title}`;\n collection.description = source.description;\n\n this.local[collection.uuid] = new DatabusCollectionWrapper(collection);\n this.saveLocally();\n this.setActive(collection.uuid);\n\n return collection;\n }\n\n saveLocally() {\n if (!this.hasAccountName) throw \"Databus-Collection-Manager is not initialized.\";\n\n if (this.activeCollection != null) {\n this.activeCollection.hasLocalChanges = this.hasLocalChanges(this.activeCollection);\n }\n\n var hash = DatabusCollectionUtils.cyrb53Hash(DatabusCollectionUtils.serialize(this.local));\n this.currentHash = hash;\n\n window.localStorage.setItem(`${this.storageKey}_hash`, hash);\n\n for (let identifier in this.local) {\n if (this.local[identifier].accountName == null) {\n delete (this.local[identifier]);\n }\n }\n\n try {\n //write local collections to local storage\n window.localStorage.setItem(this.storageKey, DatabusCollectionUtils.serialize(this.local));\n //write remote collections to session storage\n window.sessionStorage.setItem(this.storageKey, DatabusCollectionUtils.serialize(this.remote));\n } catch (e) {\n console.log(e);\n }\n }\n\n hasLocalChanges(localCollection) {\n if (this.remote[localCollection.uuid] === undefined) {\n return true;\n }\n\n let remoteCollection = this.remote[localCollection.uuid];\n\n if (remoteCollection.isHidden != localCollection.isHidden) {\n return true;\n }\n\n if (localCollection.title !== remoteCollection.title) {\n return true;\n }\n\n if (localCollection.description !== remoteCollection.description) {\n return true;\n }\n\n let serializedRemoteContent = DatabusCollectionUtils.serialize(remoteCollection.content);\n let serializedLocalContent = DatabusCollectionUtils.serialize(localCollection.content);\n\n return serializedLocalContent !== serializedRemoteContent;\n }\n\n discardLocalChanges() {\n\n if (!this.hasAccountName) throw \"Databus-Collection-Manager is not initialized.\";\n\n let uuid = this.activeCollection.uuid;\n\n if (this.remote[uuid] === undefined) {\n return;\n }\n\n let uri = this.activeCollection.uri;\n\n if (uri == undefined) {\n return;\n }\n\n this.local[uuid].title = this.remote[uuid].title;\n this.local[uuid].abstract = this.remote[uuid].abstract;\n this.local[uuid].description = this.remote[uuid].description;\n this.local[uuid].content = DatabusCollectionUtils.createCleanCopy(this.remote[uuid].content);\n this.local[uuid].hasLocalChanges = this.hasLocalChanges(this.local[uuid]);\n\n this.saveLocally();\n }\n\n addElement(elementQuery) {\n this.current.addElement(elementQuery);\n this.saveLocally();\n\n if (this.onActiveCollectionChanged != null) {\n this.onActiveCollectionChanged(this.current);\n }\n }\n\n removeElement(elementGuid) {\n this.current.removeElement(elementGuid);\n this.saveLocally();\n\n if (this.onActiveCollectionChanged != null) {\n this.onActiveCollectionChanged(this.current);\n }\n }\n\n\n\n createNew(accountName, title, description, callback) {\n if (!this.hasAccountName) throw \"Databus-Collection-Manager is not initialized.\";\n\n let reg = /^\\w+[\\w\\s]*$/;\n\n if (title === undefined || !reg.test(title)) {\n callback(false);\n return;\n }\n\n let collection = DatabusCollectionWrapper.createNew(title, description, DATABUS_RESOURCE_BASE_URL, accountName);\n\n this.local[collection.uuid] = new DatabusCollectionWrapper(collection);\n this.setActive(collection.uuid);\n this.saveLocally();\n\n callback(collection);\n }\n\n createDraft(callback) {\n if (!this.hasAccountName) {\n return;\n }\n\n let collection = DatabusCollectionWrapper.createNew('', '');\n this.local[collection.uuid] = new DatabusCollectionWrapper(collection);\n this.setActive(collection.uuid);\n this.saveLocally()\n\n callback(DatabusResponse.COLLECTION_DRAFT_CREATED);\n }\n\n createCopy(source) {\n if (!this.hasAccountName) throw \"Databus-Collection-Manager is not initialized.\";\n\n let collection = DatabusCollectionWrapper.createNew();\n collection.content = DatabusCollectionUtils.createCleanCopy(source.content);\n collection.title = `Copy of ${source.title}`;\n collection.abstract = source.abstract;\n collection.description = source.description;\n collection.accountName = this.accountName;\n\n this.local[collection.uuid] = new DatabusCollectionWrapper(collection);\n this.saveLocally();\n this.setActive(collection.uuid);\n\n return collection;\n }\n\n\n\n deleteLocally() {\n delete this.local[this.activeCollection.uuid];\n this.saveLocally();\n }\n\n /**\n * Returns the collection or null\n * @param {[type]} uri [description]\n * @return {[type]} [description]\n */\n getCollectionByUri(uri) {\n if (uri == null)\n return null;\n\n for (let identifier in this.local) {\n if (uri === this.local[identifier].uri) {\n return this.local[identifier];\n }\n }\n return null;\n }\n\n /**\n * Returns the first collection or null\n * @return {[type]} [description]\n */\n getFirstCollection() {\n if (this.local.length === 0) {\n return null;\n }\n return this.local[0];\n }\n\n\n async changeCollection(username, collectionUri) {\n try {\n if (!this.hasAccountName) throw \"Databus-Collection-Manager is not initialized.\";\n\n this.saveLocally();\n\n // Keep the identifier of the collection we want to push\n var pushIdentifier = this.activeCollection.uuid;\n var publisherUri = `${DATABUS_RESOURCE_BASE_URL}/${username}#this`;\n\n var ignoreKeys = [\n 'parent',\n '$$hashKey',\n 'expanded',\n 'files',\n 'eventListeners',\n 'hasLocalChanges',\n 'published',\n 'uuid',\n ];\n\n var contentString = encodeURIComponent(DatabusCollectionUtils.serialize(this.activeCollection.content, ignoreKeys));\n\n // Format collection as json-ld\n let collectionJsonLd = {\n \"@context\": DATABUS_CONTEXT[DatabusUris.JSONLD_CONTEXT],\n \"@graph\": [\n {\n \"@id\": collectionUri,\n \"@type\": \"Collection\",\n \"publisher\": publisherUri,\n \"title\": this.activeCollection.title,\n \"abstract\": this.activeCollection.abstract,\n \"description\": this.activeCollection.description,\n \"databus:collectionContent\": contentString,\n }\n ]\n };\n\n if (!this.activeCollection.isHidden) {\n collectionJsonLd[\"@graph\"][0].issued = DatabusUtils.timeStringNow();\n }\n\n var response = null;\n\n try {\n\n // var relativeUri = new URL(collectionUri).pathname;\n // response = await this.http.put(relativeUri, collectionJsonLd);\n\n response = await this.http.post('/api/register', collectionJsonLd);\n\n } catch (errResponse) {\n console.log(errResponse);\n throw { code: errResponse.data.code };\n }\n\n try {\n var relativeUri = new URL(collectionUri).pathname;\n\n var response = await this.http({\n method: 'GET',\n url: relativeUri,\n headers: {\n 'Accept': 'application/ld+json',\n 'X-Jsonld-Formatting': 'compact'\n }\n });\n\n\n } catch (errResponse) {\n console.log(errResponse);\n throw { code: errResponse.data.code };\n }\n\n // Get the remotely saved collection from the payload\n var remoteGraph = response.data;\n\n // If the user changed the active collection in the meantime throw an error. This\n // should be prevented by a loading dialog\n if (this.activeCollection.uuid != pushIdentifier) {\n throw { code: DatabusResponse.COLLECTION_INVALID_ARGUMENT };\n }\n\n this.local[pushIdentifier].uri = remoteGraph['@id'];\n this.local[pushIdentifier].hasLocalChanges = false;\n this.local[pushIdentifier].modified = remoteGraph.modified;\n this.local[pushIdentifier].issued = remoteGraph.issued;\n this.local[pushIdentifier].isHidden = remoteGraph.issued == null;\n // this.local[pushIdentifier].created = remoteGraph.created;\n\n //Update remote data\n this.remote[pushIdentifier] = JSON.parse(DatabusCollectionUtils.serialize(this.activeCollection));\n\n // Update the local data\n // this.local[pushIdentifier].uri = remoteGraph['@id'];\n //this.local[pushIdentifier].hasLocalChanges = this.hasLocalChanges(this.local[pushIdentifier]);\n //this.local[pushIdentifier].modified = this.activeCollection.modified;\n //this.local[pushIdentifier].issued = this.activeCollection.issued;\n //this.local[pushIdentifier].created = this.activeCollection.created;\n\n this.saveLocally();\n\n return response.data;\n\n } catch (err) {\n\n console.log(err);\n throw {\n code: err.data !== undefined && err.data.code !== undefined ? err.data.code :\n DatabusResponse.COLLECTION_UPDATE_ERROR\n };\n }\n }\n\n async updateCollection(username, collectionTag) {\n\n if (this.activeCollection.uri != null) {\n return await this.changeCollection(username, this.activeCollection.uri);\n } else {\n var collectionUri = `${DATABUS_RESOURCE_BASE_URL}/${username}/collections/${collectionTag}`;\n\n for (var uuid in this.local) {\n if (this.local[uuid].uri == collectionUri) {\n throw \"A collection with the specifed URI already exists.\";\n }\n }\n\n return await this.changeCollection(username, collectionUri);\n }\n }\n\n\n /**\n * Fetches the remote data of the current collection and assigns the field values to the local copy\n */\n async fetchCollection(uri) {\n try {\n if (!this.hasAccountName) throw \"Databus-Collection-Manager is not initialized.\";\n\n var req = {\n method: 'GET',\n url: uri,\n headers: { 'Accept': 'application/json' }\n };\n\n var getResponse = await this.http(req);\n var collection = getCollectionByUri(uri);\n\n this.local[collection.uuid].content = getResponse.data.content;\n this.local[collection.uuid].created = getResponse.data.created;\n this.local[collection.uuid].issued = getResponse.data.issued;\n this.local[collection.uuid].title = getResponse.data.title;\n this.local[collection.uuid].description = getResponse.data.description;\n this.local[collection.uuid].files = getResponse.data.files;\n } catch (errResponse) {\n console.log(errResponse);\n return errResponse.data;\n }\n }\n\n async deleteCollection(username, collectionTag) {\n try {\n if (!this.hasAccountName) throw \"Databus-Collection-Manager is not initialized.\";\n\n\n\n // Keep the identifier of the collection we want to push\n let deleteIdentifier = this.activeCollection.uuid;\n\n if (this.activeCollection.isDraft) {\n delete this.local[deleteIdentifier];\n this.saveLocally();\n return { code: DatabusResponse.COLLECTION_DELETED };\n }\n\n var targetUri = `/${username}/collections/${collectionTag}`;\n\n let deleteResponse = await this.http.delete(targetUri);\n\n delete this.remote[deleteIdentifier];\n delete this.local[deleteIdentifier];\n\n return deleteResponse.data;\n } catch (errResponse) {\n console.log(errResponse);\n return errResponse.data;\n }\n }\n\n /**\n * Deletes the active collection from the server but keeps the local storage entry\n */\n async unpublishActiveCollection() {\n\n if (!this.hasAccountName) throw \"Databus-Collection-Manager is not initialized.\";\n\n if (this.activeCollection.isDraft) {\n throw \"Cannot unpublish an unpublished draft\";\n }\n\n // Keep the identifier of the collection we want to push\n let uuid = this.activeCollection.uuid;\n let identifier = DatabusUtils.uriToName(this.activeCollection.uri);\n\n var targetUri = `/${this.accountName}/collections/${identifier}`;\n await this.http.delete(targetUri);\n\n delete this.remote[uuid];\n delete this.local[uuid].uri;\n delete this.local[uuid].issued;\n this.saveLocally();\n }\n}\n\n\nmodule.exports = DatabusCollectionManager;\n\n//# sourceURL=webpack://databus-webapp/./js/collections/databus-collection-manager.js?"); /***/ }), @@ -45,7 +45,7 @@ eval("const QueryBuilder = __webpack_require__(/*! ../query-builder/query-builde \******************************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("const QueryBuilder = __webpack_require__(/*! ../query-builder/query-builder */ \"./js/query-builder/query-builder.js\");\nconst QueryNode = __webpack_require__(/*! ../query-builder/query-node */ \"./js/query-builder/query-node.js\");\nconst QueryTemplates = __webpack_require__(/*! ../query-builder/query-templates */ \"./js/query-builder/query-templates.js\");\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\n\nclass DatabusCollectionWrapper {\n\n /**\n * Creates a new DatabusCollection from an already existing\n * @param {[type]} data [description]\n */\n constructor(data) {\n Object.assign(this, data);\n this.eventListeners = {};\n }\n\n addEventListener(name, callback) {\n if(this.eventListeners[name] == undefined) {\n this.eventListeners[name] = [];\n }\n\n this.eventListeners[name].push(callback);\n }\n\n isPublisher(username) {\n return this.uri != undefined && this.uri.startsWith('https://databus.dbpedia.org/' + username);\n }\n\n get isPublished() {\n return this.issued != undefined;\n }\n\n get displayLabelHtml() {\n var l = '';\n if(this.isDraft) {\n l += 'DRAFT:';\n }\n l += (this.label != undefined && this.label.length > 0) ? this.label : 'Untitled Collection';\n return l;\n }\n\n get isDraft() {\n return this.uri === undefined;\n }\n\n get hasContent() {\n\n if(this.content.root.childNodes.length == 0) {\n return false;\n }\n\n for(var childNode of this.content.root.childNodes) {\n if(childNode.childNodes.length > 0) {\n return true;\n }\n }\n\n return false;\n }\n\n fireEvent(name) {\n if(this.eventListeners[name] == undefined) {\n return;\n }\n\n for(var c in this.eventListeners[name]) {\n var callback = this.eventListeners[name][c];\n callback();\n }\n }\n\n\n static createNew(title, description, source) {\n var data = {};\n data.uuid = DatabusUtils.uuidv4();\n data.title = title;\n data.description = description;\n data.abstract = description;\n data.content = {};\n data.content.root = new QueryNode(null, null);\n data.content.root.addChild(new QueryNode(source, null));\n\n return data;\n }\n\n /**\n * Builds a composed query from all elements\n * @return {[type]} [description]\n */\n createQuery() {\n\n if(this.content.root == undefined) {\n return null;\n }\n\n return QueryBuilder.build({\n template : QueryTemplates.DEFAULT_FILE_TEMPLATE,\n resourceBaseUrl : DATABUS_RESOURCE_BASE_URL,\n node: this.content.root\n });\n }\n\n /**\n * Downloads the entire collection object as json\n * @return {[type]} [description]\n */\n downloadAsJson(){\n var dataStr = \"data:text/json;charset=utf-8,\" + encodeURIComponent(angular.toJson(this));\n var downloadAnchorNode = document.createElement('a');\n downloadAnchorNode.setAttribute(\"href\", dataStr);\n downloadAnchorNode.setAttribute(\"download\", this.title + \".json\");\n document.body.appendChild(downloadAnchorNode); // required for firefox\n downloadAnchorNode.click();\n downloadAnchorNode.remove();\n }\n\n removeCustomQueryNode(node) {\n this.content.customQueries = this.content.customQueries.filter(function(n){\n return node.guid != n.guid;\n });\n }\n\n removeNodeByUri(uri) {\n QueryNode.removeChildByUri(this.content.root, uri);\n }\n\n getParentNode(node) {\n return QueryNode.findParentNodeRecursive(this.content.root, node);\n }\n\n removeGroupNode(groupNode) {\n this.content.groups = this.content.groups.filter(function(a){\n return groupNode.uri != a.uri;\n });\n }\n\n addCustomQueryNode(label, query) {\n this.content.customQueries.push({\n guid : DatabusUtils.uuidv4(),\n label : label,\n query : query,\n });\n }\n\n hasGroup(groupUri) {\n var group = this.findGroup(groupUri);\n return group != undefined;\n }\n\n hasArtifact(artifactUri) {\n var groupUri = DatabusUtils.navigateUp(artifactUri);\n\n var group = this.findGroup(groupUri);\n\n if(group == undefined) {\n return false;\n }\n\n var artifact = this.findArtifact(group, artifactUri);\n return artifact != undefined;\n }\n\n /**\n * Adds a new group node with label, uri and settings\n * @param {[type]} groupUri [description]\n * @param {[type]} groupLabel [description]\n * @param {[type]} settings [description]\n */\n addGroupNode(groupUri, settings) {\n\n var group = this.findGroup(groupUri);\n\n if(group == undefined) {\n\n var publisherUri = DatabusUtils.navigateUp(groupUri);\n\n var groupLabel = DatabusUtils.uriToName(groupUri);\n var publisherLabel = DatabusUtils.uriToName(publisherUri);\n\n group = {};\n group.uri = groupUri;\n group.artifacts = [];\n group.label = publisherLabel + \" » \" + groupLabel;\n group.settings = settings;\n group.expanded = true;\n\n this.content.groups.push(group);\n\n this.fireEvent(\"onGroupAdded\");\n }\n\n return group;\n }\n\n /**\n * Adds a new artifact node with label uri and settings\n * This will fail if the appropriate group node has not been\n * added previously\n * @param {[type]} artifactUri [description]\n * @param {[type]} artifactLabel [description]\n * @param {[type]} settings [description]\n */\n addArtifactNode(artifactUri, artifactLabel, settings) {\n\n var groupUri = DatabusUtils.navigateUp(artifactUri);\n var group = this.addGroupNode(groupUri, [ \n {\n facet: \"http://purl.org/dc/terms/hasVersion\",\n value: \"SYSTEM_LATEST_ARTIFACT_VERSION\",\n checked: true\n }]);\n\n var artifact = this.findArtifact(group, artifactUri);\n\n if(artifact == undefined) {\n artifact = {};\n artifact.uri = artifactUri;\n artifact.label = artifactLabel;\n artifact.settings = settings;\n\n group.artifacts.push(artifact);\n \n // TODO: merge facets\n \n\n this.fireEvent(\"onArtifactAdded\");\n }\n }\n\n findGroup(groupUri) {\n for(var g in this.content.groups) {\n var group = this.content.groups[g];\n\n if(group.uri == groupUri) {\n return group;\n }\n }\n\n return null;\n }\n\n findArtifact(group, artifactUri) {\n for(var a in group.artifacts) {\n var artifact = group.artifacts[a];\n\n if(artifact.uri == artifactUri) {\n return artifact;\n }\n }\n\n return null;\n }\n}\n\nmodule.exports = DatabusCollectionWrapper;\n\n//# sourceURL=webpack://databus-webapp/./js/collections/databus-collection-wrapper.js?"); +eval("const QueryBuilder = __webpack_require__(/*! ../query-builder/query-builder */ \"./js/query-builder/query-builder.js\");\r\nconst QueryNode = __webpack_require__(/*! ../query-builder/query-node */ \"./js/query-builder/query-node.js\");\r\nconst QueryTemplates = __webpack_require__(/*! ../query-builder/query-templates */ \"./js/query-builder/query-templates.js\");\r\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\r\n\r\nclass DatabusCollectionWrapper {\r\n\r\n /**\r\n * Creates a new DatabusCollection from an already existing\r\n * @param {[type]} data [description]\r\n */\r\n constructor(data) {\r\n Object.assign(this, data);\r\n this.eventListeners = {};\r\n }\r\n\r\n addEventListener(name, callback) {\r\n if(this.eventListeners[name] == undefined) {\r\n this.eventListeners[name] = [];\r\n }\r\n\r\n this.eventListeners[name].push(callback);\r\n }\r\n\r\n isPublisher(username) {\r\n return this.uri != undefined && this.uri.startsWith('https://databus.dbpedia.org/' + username);\r\n }\r\n\r\n get isPublished() {\r\n return this.issued != undefined;\r\n }\r\n\r\n get displayLabelHtml() {\r\n var l = '';\r\n if(this.isDraft) {\r\n l += 'DRAFT:';\r\n }\r\n l += (this.label != undefined && this.label.length > 0) ? this.label : 'Untitled Collection';\r\n return l;\r\n }\r\n\r\n get isDraft() {\r\n return this.uri === undefined;\r\n }\r\n\r\n get hasContent() {\r\n\r\n if(this.content.root.childNodes.length == 0) {\r\n return false;\r\n }\r\n\r\n for(var childNode of this.content.root.childNodes) {\r\n if(childNode.childNodes.length > 0) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n }\r\n\r\n fireEvent(name) {\r\n if(this.eventListeners[name] == undefined) {\r\n return;\r\n }\r\n\r\n for(var c in this.eventListeners[name]) {\r\n var callback = this.eventListeners[name][c];\r\n callback();\r\n }\r\n }\r\n\r\n\r\n static createNew(title, description, source, accountName) {\r\n var data = {};\r\n data.uuid = DatabusUtils.uuidv4();\r\n data.title = title;\r\n data.description = description;\r\n data.accountName = accountName;\r\n data.abstract = description;\r\n data.content = {};\r\n data.content.root = new QueryNode(null, null);\r\n data.content.root.addChild(new QueryNode(source, null));\r\n\r\n return data;\r\n }\r\n\r\n /**\r\n * Builds a composed query from all elements\r\n * @return {[type]} [description]\r\n */\r\n createQuery() {\r\n\r\n if(this.content.root == undefined) {\r\n return null;\r\n }\r\n\r\n return QueryBuilder.build({\r\n template : QueryTemplates.DEFAULT_FILE_TEMPLATE,\r\n resourceBaseUrl : DATABUS_RESOURCE_BASE_URL,\r\n node: this.content.root\r\n });\r\n }\r\n\r\n /**\r\n * Downloads the entire collection object as json\r\n * @return {[type]} [description]\r\n */\r\n downloadAsJson(){\r\n var dataStr = \"data:text/json;charset=utf-8,\" + encodeURIComponent(angular.toJson(this));\r\n var downloadAnchorNode = document.createElement('a');\r\n downloadAnchorNode.setAttribute(\"href\", dataStr);\r\n downloadAnchorNode.setAttribute(\"download\", this.title + \".json\");\r\n document.body.appendChild(downloadAnchorNode); // required for firefox\r\n downloadAnchorNode.click();\r\n downloadAnchorNode.remove();\r\n }\r\n\r\n removeCustomQueryNode(node) {\r\n this.content.customQueries = this.content.customQueries.filter(function(n){\r\n return node.guid != n.guid;\r\n });\r\n }\r\n\r\n removeNodeByUri(uri) {\r\n QueryNode.removeChildByUri(this.content.root, uri);\r\n }\r\n\r\n getParentNode(node) {\r\n return QueryNode.findParentNodeRecursive(this.content.root, node);\r\n }\r\n\r\n removeGroupNode(groupNode) {\r\n this.content.groups = this.content.groups.filter(function(a){\r\n return groupNode.uri != a.uri;\r\n });\r\n }\r\n\r\n addCustomQueryNode(label, query) {\r\n this.content.customQueries.push({\r\n guid : DatabusUtils.uuidv4(),\r\n label : label,\r\n query : query,\r\n });\r\n }\r\n\r\n hasGroup(groupUri) {\r\n var group = this.findGroup(groupUri);\r\n return group != undefined;\r\n }\r\n\r\n hasArtifact(artifactUri) {\r\n var groupUri = DatabusUtils.navigateUp(artifactUri);\r\n\r\n var group = this.findGroup(groupUri);\r\n\r\n if(group == undefined) {\r\n return false;\r\n }\r\n\r\n var artifact = this.findArtifact(group, artifactUri);\r\n return artifact != undefined;\r\n }\r\n\r\n /**\r\n * Adds a new group node with label, uri and settings\r\n * @param {[type]} groupUri [description]\r\n * @param {[type]} groupLabel [description]\r\n * @param {[type]} settings [description]\r\n */\r\n addGroupNode(groupUri, settings) {\r\n\r\n var group = this.findGroup(groupUri);\r\n\r\n if(group == undefined) {\r\n\r\n var publisherUri = DatabusUtils.navigateUp(groupUri);\r\n\r\n var groupLabel = DatabusUtils.uriToName(groupUri);\r\n var publisherLabel = DatabusUtils.uriToName(publisherUri);\r\n\r\n group = {};\r\n group.uri = groupUri;\r\n group.artifacts = [];\r\n group.label = publisherLabel + \" » \" + groupLabel;\r\n group.settings = settings;\r\n group.expanded = true;\r\n\r\n this.content.groups.push(group);\r\n\r\n this.fireEvent(\"onGroupAdded\");\r\n }\r\n\r\n return group;\r\n }\r\n\r\n /**\r\n * Adds a new artifact node with label uri and settings\r\n * This will fail if the appropriate group node has not been\r\n * added previously\r\n * @param {[type]} artifactUri [description]\r\n * @param {[type]} artifactLabel [description]\r\n * @param {[type]} settings [description]\r\n */\r\n addArtifactNode(artifactUri, artifactLabel, settings) {\r\n\r\n var groupUri = DatabusUtils.navigateUp(artifactUri);\r\n var group = this.addGroupNode(groupUri, [ \r\n {\r\n facet: \"http://purl.org/dc/terms/hasVersion\",\r\n value: \"SYSTEM_LATEST_ARTIFACT_VERSION\",\r\n checked: true\r\n }]);\r\n\r\n var artifact = this.findArtifact(group, artifactUri);\r\n\r\n if(artifact == undefined) {\r\n artifact = {};\r\n artifact.uri = artifactUri;\r\n artifact.label = artifactLabel;\r\n artifact.settings = settings;\r\n\r\n group.artifacts.push(artifact);\r\n \r\n // TODO: merge facets\r\n \r\n\r\n this.fireEvent(\"onArtifactAdded\");\r\n }\r\n }\r\n\r\n findGroup(groupUri) {\r\n for(var g in this.content.groups) {\r\n var group = this.content.groups[g];\r\n\r\n if(group.uri == groupUri) {\r\n return group;\r\n }\r\n }\r\n\r\n return null;\r\n }\r\n\r\n findArtifact(group, artifactUri) {\r\n for(var a in group.artifacts) {\r\n var artifact = group.artifacts[a];\r\n\r\n if(artifact.uri == artifactUri) {\r\n return artifact;\r\n }\r\n }\r\n\r\n return null;\r\n }\r\n}\r\n\r\nmodule.exports = DatabusCollectionWrapper;\n\n//# sourceURL=webpack://databus-webapp/./js/collections/databus-collection-wrapper.js?"); /***/ }), @@ -105,7 +105,7 @@ eval("\n// hinzufügen eines Controllers zum Modul\nfunction CollectionEditorWid \************************************************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("const DatabusCollectionWrapper = __webpack_require__(/*! ../../collections/databus-collection-wrapper */ \"./js/collections/databus-collection-wrapper.js\");\nconst QueryBuilder = __webpack_require__(/*! ../../query-builder/query-builder */ \"./js/query-builder/query-builder.js\");\nconst QueryNode = __webpack_require__(/*! ../../query-builder/query-node */ \"./js/query-builder/query-node.js\");\nconst QueryTemplates = __webpack_require__(/*! ../../query-builder/query-templates */ \"./js/query-builder/query-templates.js\");\nconst DatabusConstants = __webpack_require__(/*! ../../utils/databus-constants */ \"./js/utils/databus-constants.js\");\nconst DatabusFacetsCache = __webpack_require__(/*! ../../utils/databus-facets-cache */ \"./js/utils/databus-facets-cache.js\");\nconst DatabusUris = __webpack_require__(/*! ../../utils/databus-uris */ \"./js/utils/databus-uris.js\");\nconst DatabusUtils = __webpack_require__(/*! ../../utils/databus-utils */ \"./js/utils/databus-utils.js\");\nconst DatabusWebappUtils = __webpack_require__(/*! ../../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\n\n// hinzufügen eines Controllers zum Modul\nfunction CollectionHierarchyControllerTwo($http, $location, $sce, $scope, collectionManager) {\n\n var ctrl = this;\n\n ctrl.viewMode = -1;\n ctrl.$http = $http;\n ctrl.$scope = $scope;\n ctrl.facets = new DatabusFacetsCache($http);\n ctrl.utils = new DatabusWebappUtils($scope, $sce);\n ctrl.$sce = $sce;\n\n collectionManager.onCollectionChangedInDifferentTab = function () {\n ctrl.previousCollectionId = null;\n }\n\n ctrl.defaultQuery = `PREFIX databus: \nPREFIX dcv: \nPREFIX dct: \nPREFIX dcat: \nPREFIX rdf: \nPREFIX rdfs: \nSELECT ?file WHERE {\n # Replace this with your custom query:\n ?file .\n} LIMIT 0`;\n const DATAID_ARTIFACT_PROPERTY = 'databus:artifact';\n const DATAID_GROUP_PROPERTY = 'databus:group';\n const KEY_LATEST_VERSION = \"$latest\";\n\n\n ctrl.$onInit = function () {\n\n ctrl.viewMode = -1;\n\n if (ctrl.collection == null) {\n return;\n }\n }\n\n ctrl.onAddContentClicked = function (sourceNode) {\n ctrl.onAddContent({ source: sourceNode.uri });\n\n ctrl.onChange();\n ctrl.updateViewModel();\n }\n\n\n ctrl.onAddCustomQueryClicked = function (sourceNode) {\n var node = QueryNode.createFrom(sourceNode);\n node.addChild(new QueryNode(DatabusUtils.uuidv4(), null));\n ctrl.onChange();\n }\n\n ctrl.toggleCollapsed = function (node, view) {\n view.collapsed = !view.collapsed;\n\n if (!view.collapsed) {\n ctrl.query(node);\n }\n }\n\n ctrl.isDatabus = async function (uri) {\n var req = {\n method: 'GET',\n url: uri,\n headers: {\n 'Accept': 'application/rdf+turtle'\n }\n }\n\n var res = await ctrl.$http(req);\n var manifest = await DatabusUtils.parseDatabusManifest(res.data);\n var expectedUri = new URL(uri);\n\n if (manifest == undefined || manifest.uri != expectedUri.origin) {\n return false;\n }\n\n return true;\n }\n\n ctrl.getDatabusUri = async function (uri) {\n\n var url = new URL(uri);\n var segments = url.pathname.split('/');\n var base = url.origin;\n var currentUrl = base;\n\n var isDatabus = await ctrl.isDatabus(currentUrl);\n\n if (isDatabus) {\n return currentUrl;\n }\n\n for (var i = 0; segments.length; i++) {\n\n currentUrl += `/${segments[i]}`;\n var isDatabus = await ctrl.isDatabus(currentUrl);\n\n if (isDatabus) {\n return currentUrl;\n }\n }\n\n }\n\n ctrl.onAddResource = async function (uri) {\n\n if (uri.endsWith('/')) {\n uri = uri.substr(0, uri.length - 1);\n }\n\n let node = QueryNode.findChildByUri(ctrl.root, uri);\n\n // Resource already in collection\n if (node != undefined) {\n return;\n }\n\n var databusUri = await ctrl.getDatabusUri(uri);\n\n\n var databusUriLength = DatabusUtils.getResourcePathLength(databusUri);\n var resourceUriLength = DatabusUtils.getResourcePathLength(uri);\n var diff = resourceUriLength - databusUriLength;\n\n if (diff < 0 && diff > 3 || diff == 1) {\n return;\n }\n\n if (diff == 0) {\n ctrl.addDatabus(uri);\n }\n\n if (diff == 2) {\n ctrl.addDatabus(databusUri);\n let databusNode = QueryNode.findChildByUri(ctrl.root, databusUri);\n ctrl.addGroup(databusNode, uri);\n }\n\n if (diff == 3) {\n ctrl.addDatabus(databusUri);\n let databusNode = QueryNode.findChildByUri(ctrl.root, databusUri);\n let groupUri = DatabusUtils.navigateUp(uri);\n ctrl.addGroup(databusNode, groupUri);\n let groupNode = QueryNode.findChildByUri(ctrl.root, groupUri);\n ctrl.addArtifact(groupNode, uri);\n }\n\n ctrl.onChange();\n ctrl.updateViewModel();\n ctrl.$scope.$apply();\n }\n\n ctrl.addDatabus = function (uri) {\n let node = QueryNode.findChildByUri(ctrl.root, uri);\n\n if (node == null) {\n ctrl.root.childNodes.push(new QueryNode(uri, null));\n }\n }\n\n ctrl.addGroup = function (databusNode, uri) {\n let node = QueryNode.findChildByUri(ctrl.root, uri);\n\n if (node == null) {\n databusNode.childNodes.push(new QueryNode(uri, DATAID_GROUP_PROPERTY));\n }\n }\n\n ctrl.addArtifact = function (groupNode, uri) {\n let node = QueryNode.findChildByUri(ctrl.root, uri);\n\n if (node == null) {\n groupNode.childNodes.push(new QueryNode(uri, DATAID_ARTIFACT_PROPERTY));\n }\n }\n\n ctrl.addToCollection = function (source, view, result) {\n\n if (ctrl.isInCollection(result)) {\n QueryNode.removeChildByUri(ctrl.root, result.resource[0].value);\n }\n else {\n if (result.typeName[0].value == 'Group') {\n let node = new QueryNode(result.resource[0].value, DATAID_GROUP_PROPERTY);\n\n source.childNodes.push(node);\n }\n\n if (result.typeName[0].value == 'Artifact') {\n\n var artifactUri = result.resource[0].value;\n let groupUri = DatabusUtils.navigateUp(artifactUri);\n let groupNode = QueryNode.findChildByUri(ctrl.root, groupUri);\n\n if (groupNode == null) {\n groupNode = new QueryNode(groupUri, DATAID_GROUP_PROPERTY);\n source.childNodes.push(groupNode);\n }\n\n let node = new QueryNode(artifactUri, DATAID_ARTIFACT_PROPERTY);\n groupNode.addChild(node);\n }\n }\n\n for (var res of view.searchResults) {\n res.inCollection = ctrl.isInCollection(res);\n }\n\n ctrl.onChange();\n ctrl.updateViewModel();\n }\n\n ctrl.isLastChild = function (group, artifact) {\n\n if (group.childNodes == undefined || group.childNodes.length == 0) {\n return false;\n }\n\n return group.childNodes[group.childNodes.length - 1].uri == artifact.uri;\n }\n\n ctrl.toggleExpand = function (node) {\n node.expanded = !node.expanded;\n ctrl.onChange();\n }\n\n ctrl.mergeFacets = function (node, facets) {\n\n if (node.facets == undefined) {\n node.facets = JSON.parse(JSON.stringify(facets));\n return;\n }\n\n for (var f in facets) {\n\n if (node.facets[f] == undefined) {\n node.facets[f] = JSON.parse(JSON.stringify(facets[f]));\n continue;\n }\n\n for (var value of facets[f].values) {\n if (!node.facets[f].values.includes(value)) {\n node.facets[f].values.push(value);\n }\n }\n }\n\n node.facetLabels = null;\n }\n\n ctrl.getAllFilters = function (groupNode, artifactNode) {\n\n if (artifactNode == null) {\n var result = Object.keys(groupNode.facetSettings)\n return DatabusUtils.uniqueList(result);\n }\n\n var result = Object.keys(groupNode.facetSettings).concat(Object.keys(artifactNode.facetSettings));\n return DatabusUtils.uniqueList(result);\n }\n\n ctrl.$doCheck = function () {\n\n if (ctrl.collection == null) {\n ctrl.previousCollectionId = null;\n return;\n }\n\n if (ctrl.previousCollectionId != ctrl.collection.uuid) {\n ctrl.previousCollectionId = ctrl.collection.uuid;\n\n ctrl.activeNode = null;\n ctrl.viewMode = -1;\n ctrl.updateViewModel();\n }\n }\n\n ctrl.handleKey = function (e, nodeView) {\n if (e.which === 9) {\n nodeView.showSearchResults = false;\n }\n }\n\n ctrl.isInCollection = function (result) {\n let uri = result.resource[0].value;\n let node = QueryNode.findChildByUri(ctrl.root, uri);\n return node != null;\n }\n\n ctrl.updateSearchResults = function (view) {\n\n if (view == null || view.searchResults == null) {\n return;\n }\n\n for (var res of view.searchResults) {\n res.inCollection = ctrl.isInCollection(res);\n }\n }\n\n ctrl.searchNode = function (node, nodeView) {\n\n var baseUrl = new URL(node.uri).origin;\n var typeFilters = `typeName=Artifact Group`;\n\n if (node.property == DATAID_GROUP_PROPERTY) {\n var groupName = DatabusUtils.uriToResourceName(node.uri);\n var accountName = DatabusUtils.uriToResourceName(DatabusUtils.navigateUp(node.uri));\n typeFilters = `typeName=Artifact&publisher=${accountName}&group=${groupName}`;\n }\n\n var url = `${baseUrl}/api/search?${typeFilters}&typeNameWeight=0&format=JSON_FULL&minRelevance=15&maxResults=10&query=${nodeView.search}`;\n\n try {\n $http({ method: 'GET', url: url }).then(function successCallback(response) {\n\n nodeView.searchResults = [];\n\n for (var doc of response.data.docs) {\n doc.inCollection = ctrl.isInCollection(doc);\n nodeView.searchResults.push(doc);\n }\n\n }, function errorCallback(response) {\n console.log(response);\n });\n } catch (err) {\n\n }\n\n };\n\n ctrl.toggleExpand = function (view) {\n view.expanded = !view.expanded;\n }\n\n\n ctrl.isValidHttpUrl = function (url) {\n return DatabusUtils.isValidHttpUrl(url);\n }\n\n\n ctrl.updateViewModel = function () {\n ctrl.collectionWrapper = new DatabusCollectionWrapper(ctrl.collection);\n ctrl.root = ctrl.collection.content.root;\n\n ctrl.view = {};\n ctrl.view.groups = {};\n ctrl.view.artifacts = {};\n ctrl.view.sources = {};\n\n for (var s in ctrl.root.childNodes) {\n\n var sourceNode = ctrl.root.childNodes[s];\n\n if (ctrl.view.sources[sourceNode.uri] == undefined) {\n ctrl.view.sources[sourceNode.uri] = {};\n ctrl.view.sources[sourceNode.uri].uri = sourceNode.uri;\n ctrl.view.sources[sourceNode.uri].expanded = true;\n ctrl.view.sources[sourceNode.uri].addMode = 'artifact';\n ctrl.view.sources[sourceNode.uri].customQueryLabel = `New Custom Query`;\n ctrl.view.sources[sourceNode.uri].customQueryInput = ctrl.defaultQuery;\n }\n\n for (var g in sourceNode.childNodes) {\n\n var groupNode = sourceNode.childNodes[g];\n groupNode.expanded = true;\n\n\n ctrl.view.groups[groupNode.uri] = {};\n\n if (DatabusUtils.isValidHttpUrl(groupNode.uri)) {\n\n ctrl.facets.get(groupNode.uri).then(function (res) {\n delete res.facets[DatabusUris.DATABUS_ARTIFACT_PROPERTY];\n ctrl.view.groups[res.uri].facets = res.facets;\n\n var hasVersionFacets = ctrl.view.groups[res.uri].facets[DatabusUris.DCT_HAS_VERSION];\n\n if (hasVersionFacets != null && !hasVersionFacets.values.includes(KEY_LATEST_VERSION)) {\n hasVersionFacets.values.unshift(KEY_LATEST_VERSION);\n }\n\n $scope.$apply();\n });\n\n ctrl.query(groupNode);\n\n for (var a in groupNode.childNodes) {\n\n var artifactNode = groupNode.childNodes[a];\n\n ctrl.view.artifacts[artifactNode.uri] = {};\n ctrl.view.artifacts[artifactNode.uri].expanded = false;\n ctrl.view.artifacts[artifactNode.uri].collapsed = true;\n\n ctrl.facets.get(artifactNode.uri).then(function (res) {\n ctrl.view.artifacts[res.uri].facets = res.facets;\n\n var hasVersionFacets = ctrl.view.artifacts[res.uri].facets[DatabusUris.DCT_HAS_VERSION];\n\n if (hasVersionFacets != null && !hasVersionFacets.values.includes(KEY_LATEST_VERSION)) {\n hasVersionFacets.values.unshift(KEY_LATEST_VERSION);\n }\n\n $scope.$apply();\n //var groupUri = DatabusUtils.navigateUp(artifactNode.uri);\n //ctrl.view.artifacts[artifactNode.uri].facets = result.data;\n //ctrl.mergeFacets(ctrl.view.groups[groupUri], result.data);\n });\n\n\n\n /*en(function(result) {\n\n = result['http://purl.org/dc/terms/hasVersion'].values.unshift(\"$latest\");\n\n\n var groupUri = DatabusUtils.navigateUp(artifactNode.uri);\n ctrl.view.artifacts[artifactNode.uri].facets = result.data;\n ctrl.mergeFacets(ctrl.view.groups[groupUri], result.data);\n\n });\n\n\n \n */\n }\n }\n }\n }\n }\n\n\n\n ctrl.onArtifactDropdownChanged = function (groupNode) {\n ctrl.onChange();\n ctrl.query(groupNode);\n }\n\n ctrl.selectAddFilterValue = function (viewNode, value) {\n viewNode.addFilterValueInput = value;\n viewNode.showValueDrop = false;\n\n ctrl.onAddFilterValueInputChanged(viewNode);\n }\n\n ctrl.selectAddFilterFacet = function (viewNode, value) {\n viewNode.addFilterFacetInput = value;\n viewNode.showFacetDrop = false;\n\n ctrl.onAddFilterFacetInputChanged(viewNode);\n }\n\n ctrl.onAddFilterValueInputChanged = function (viewNode) {\n\n for (var value of viewNode.facets[viewNode.addFilterFacet].values) {\n\n if (viewNode.addFilterValueInput == value) {\n viewNode.addFilterValue = value;\n return;\n }\n }\n\n viewNode.addFilterValue = null;\n }\n\n ctrl.onAddFilterFacetInputChanged = function (viewNode) {\n\n for (var facet in viewNode.facets) {\n\n if (viewNode.addFilterFacetInput == viewNode.facets[facet].label) {\n\n if (viewNode.addFilterFacet != facet) {\n viewNode.addFilterFacet = facet;\n viewNode.addFilterValue = [];\n }\n\n return;\n }\n }\n\n viewNode.addFilterFacet = null;\n viewNode.addFilterValue = [];\n }\n\n ctrl.includesValue = function (objs, value) {\n if (objs == undefined) {\n return false;\n }\n\n for (var obj of objs) {\n if (obj.value == value) {\n return true;\n }\n }\n\n return false;\n }\n\n ctrl.isLocalDatabusNode = function (node) {\n return node.uri == DATABUS_RESOURCE_BASE_URL;\n }\n ctrl.addFilter = function (node, facet, values, checked) {\n\n if (values == null) {\n return;\n }\n\n if (node.facetSettings[facet] == undefined) {\n node.facetSettings[facet] = [];\n }\n\n for (var value of values) {\n\n if (!ctrl.includesValue(node.facetSettings[facet], value.value)) {\n node.facetSettings[facet].push(value);\n }\n }\n\n ctrl.onChange();\n ctrl.query(node);\n }\n\n ctrl.query = function (node) {\n\n if (node.childNodes != undefined && node.childNodes.length > 0) {\n\n node.files = null;\n for (var child of node.childNodes) {\n ctrl.query(child);\n }\n\n return;\n }\n\n var queryNode = QueryNode.createSubTree(node);\n\n var fullQuery = QueryBuilder.build({\n node: queryNode,\n template: QueryTemplates.NODE_FILE_TEMPLATE,\n resourceBaseUrl: DATABUS_RESOURCE_BASE_URL,\n root: ctrl.root\n });\n\n this.querySparql(fullQuery).then(function (result) {\n node.files = result;\n ctrl.$scope.$apply();\n\n });\n }\n\n ctrl.removeFilter = function (node, facet) {\n\n if (node.facetSettings[facet] == undefined) {\n return;\n }\n\n delete node.facetSettings[facet];\n\n ctrl.onChange();\n\n ctrl.query(node);\n }\n\n ctrl.onActiveFilterChanged = function (node) {\n ctrl.onChange();\n ctrl.query(node);\n }\n\n ctrl.getFacetLabels = function (viewNode) {\n\n if (viewNode.facetLabels != undefined) {\n return viewNode.facetLabels;\n }\n var result = [];\n\n for (var f in viewNode.facets) {\n result.push(viewNode.facets[f].label);\n }\n\n viewNode.facetLabels = result;\n return result;\n }\n\n\n ctrl.sortBy = function (property) {\n\n if (ctrl.sortProperty == property) {\n ctrl.sortReverse = !ctrl.sortReverse;\n }\n ctrl.sortProperty = property;\n }\n\n ctrl.formatFileSize = function (size) {\n return DatabusUtils.formatFileSize(size);\n };\n\n ctrl.toHTML = function (html) {\n return $sce.trustAsHtml(html);\n };\n\n ctrl.onComponentAdded = function () {\n\n }\n\n ctrl.customExpanded = function () {\n return ctrl.customNode.expanded && ctrl.collection.content.customQueries.length > 0;\n }\n\n ctrl.generatedExpanded = function () {\n return ctrl.generatedNode.expanded && ctrl.collection.content.groups.length > 0;\n }\n\n ctrl.publishCollection = function () {\n ctrl.onPublish();\n }\n\n ctrl.delete = function () {\n ctrl.onDelete();\n }\n\n ctrl.goToResource = function (node) {\n window.location = node.uri;\n }\n\n ctrl.formatGroupPrefix = function (uri) {\n return DatabusUtils.uriToName(DatabusUtils.navigateUp(uri));\n }\n\n ctrl.formatArtifactPrefix = function (uri) {\n var nav = DatabusUtils.navigateUp(uri);\n var groupName = DatabusUtils.uriToName(nav);\n var userName = DatabusUtils.uriToName(DatabusUtils.navigateUp(nav));\n\n return userName + '/' + groupName;\n }\n\n ctrl.uriToName = function (uri) {\n return DatabusUtils.uriToName(uri);\n }\n\n ctrl.objSize = function (obj) {\n return DatabusUtils.objSize(obj);\n }\n\n ctrl.showCollectionSearch = function () {\n ctrl.open = false;\n ctrl.viewMode = 0;\n ctrl.activeNode = ctrl.rootNode;\n\n $location.hash('search');\n }\n\n // ctrl.printJSON = function() {\n // console.log(JSON.stringify(ctrl.collection));\n // }\n\n // SHOW NODES\n ctrl.showGroupNode = function (groupNode) {\n ctrl.open = true;\n ctrl.viewMode = 3;\n ctrl.activeNode = QueryNode.createFrom(groupNode);\n\n this.updateQuery();\n }\n\n ctrl.showArtifactNode = function (artifactNode, groupNode) {\n\n ctrl.open = true;\n ctrl.viewMode = 1;\n ctrl.activeNode = QueryNode.createFrom(artifactNode);\n\n this.updateQuery();\n }\n\n ctrl.querySparql = async function (query) {\n\n\n try {\n\n var req = {\n method: 'POST',\n url: DatabusConstants.DATABUS_SPARQL_ENDPOINT_URL,\n data: \"format=json&query=\" + encodeURIComponent(query),\n headers: {\n \"Content-type\": \"application/x-www-form-urlencoded\"\n },\n }\n\n var updateResponse = await ctrl.$http(req);\n\n var data = updateResponse.data;\n var bindings = data.results.bindings;\n\n for (var b in bindings) {\n ctrl.reduceBinding(bindings[b]);\n }\n\n return bindings;\n\n\n } catch (e) {\n console.log(e);\n }\n }\n\n ctrl.reduceBinding = function (binding) {\n for (var key in binding) {\n binding[key] = binding[key].value;\n }\n\n return binding;\n }\n\n\n\n ctrl.updateQuery = function () {\n var queryNode = QueryNode.createSubTree(ctrl.activeNode);\n\n ctrl.activeFileQuery = QueryBuilder.build({\n node: queryNode,\n template: QueryTemplates.DEFAULT_FILE_TEMPLATE,\n resourceBaseUrl: DATABUS_RESOURCE_BASE_URL\n });\n\n ctrl.activeFullQuery = QueryBuilder.build({\n node: queryNode,\n template: QueryTemplates.NODE_FILE_TEMPLATE,\n resourceBaseUrl: DATABUS_RESOURCE_BASE_URL\n });\n }\n\n ctrl.onActiveNodeChanged = function () {\n this.updateQuery();\n\n ctrl.onChange();\n }\n\n ctrl.addCustomNode = function (sourceNode, label, desc, query) {\n\n var node = new QueryNode(label, query);\n sourceNode.childNodes.push(node);\n\n\n ctrl.updateViewModel();\n ctrl.onChange();\n }\n\n ctrl.removeNode = function (node) {\n\n var parent = ctrl.collectionWrapper.getParentNode(node);\n ctrl.collectionWrapper.removeNodeByUri(node.uri);\n\n if (parent != null) {\n ctrl.query(parent);\n }\n\n ctrl.onChange();\n }\n\n ctrl.showCustomQueryNode = function (customQueryNode) {\n ctrl.open = true;\n ctrl.viewMode = 2;\n ctrl.activeNode = customQueryNode;\n }\n\n ctrl.list = function (setting) {\n\n var allEntries = Object.keys(setting).map(function (key, index) {\n\n var label = undefined;\n var entry = setting[key];\n\n if (entry.value == '') {\n label = 'None';\n } else if (entry.value == '$latest') {\n label = 'Latest Version';\n } else {\n label = entry.value;\n }\n\n if (entry.checked) {\n return label;\n } else {\n return `${label}`;\n }\n });\n\n\n var list = [];\n var maxLength = 50;\n var length = 0;\n var hasOverflow = false;\n\n for (var entry of allEntries) {\n if (entry.length + length > maxLength) {\n hasOverflow = true;\n break;\n }\n\n length += entry.length;\n list.push(entry);\n }\n\n if (hasOverflow) {\n list.push('...');\n }\n\n return ctrl.$sce.trustAsHtml(list.join(', '));\n // return setting.map(function (v) { return v.value }).join(', ');\n }\n}\n\n\nmodule.exports = CollectionHierarchyControllerTwo;\n\n//# sourceURL=webpack://databus-webapp/./js/components/collection-hierarchy-two/collection-hierarchy.js?"); +eval("const DatabusCollectionWrapper = __webpack_require__(/*! ../../collections/databus-collection-wrapper */ \"./js/collections/databus-collection-wrapper.js\");\nconst QueryBuilder = __webpack_require__(/*! ../../query-builder/query-builder */ \"./js/query-builder/query-builder.js\");\nconst QueryNode = __webpack_require__(/*! ../../query-builder/query-node */ \"./js/query-builder/query-node.js\");\nconst QueryTemplates = __webpack_require__(/*! ../../query-builder/query-templates */ \"./js/query-builder/query-templates.js\");\nconst DatabusConstants = __webpack_require__(/*! ../../utils/databus-constants */ \"./js/utils/databus-constants.js\");\nconst DatabusFacetsCache = __webpack_require__(/*! ../../utils/databus-facets-cache */ \"./js/utils/databus-facets-cache.js\");\nconst DatabusUris = __webpack_require__(/*! ../../utils/databus-uris */ \"./js/utils/databus-uris.js\");\nconst DatabusUtils = __webpack_require__(/*! ../../utils/databus-utils */ \"./js/utils/databus-utils.js\");\nconst DatabusWebappUtils = __webpack_require__(/*! ../../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\n\n// hinzufügen eines Controllers zum Modul\nfunction CollectionHierarchyControllerTwo($http, $location, $sce, $scope, collectionManager) {\n\n var ctrl = this;\n\n ctrl.viewMode = -1;\n ctrl.$http = $http;\n ctrl.$scope = $scope;\n ctrl.facets = new DatabusFacetsCache($http);\n ctrl.utils = new DatabusWebappUtils($scope, $sce);\n ctrl.$sce = $sce;\n\n collectionManager.onCollectionChangedInDifferentTab = function () {\n ctrl.previousCollectionId = null;\n }\n\n ctrl.defaultQuery = `PREFIX databus: \nPREFIX dcv: \nPREFIX dct: \nPREFIX dcat: \nPREFIX rdf: \nPREFIX rdfs: \nSELECT ?file WHERE {\n # Replace this with your custom query:\n ?file .\n} LIMIT 0`;\n const DATAID_ARTIFACT_PROPERTY = 'databus:artifact';\n const DATAID_GROUP_PROPERTY = 'databus:group';\n const KEY_LATEST_VERSION = \"$latest\";\n\n\n ctrl.$onInit = function () {\n\n ctrl.viewMode = -1;\n\n if (ctrl.collection == null) {\n return;\n }\n }\n\n ctrl.onAddContentClicked = function (sourceNode) {\n ctrl.onAddContent({ source: sourceNode.uri });\n\n ctrl.onChange();\n ctrl.updateViewModel();\n }\n\n\n ctrl.onAddCustomQueryClicked = function (sourceNode) {\n var node = QueryNode.createFrom(sourceNode);\n node.addChild(new QueryNode(DatabusUtils.uuidv4(), null));\n ctrl.onChange();\n }\n\n ctrl.toggleCollapsed = function (node, view) {\n view.collapsed = !view.collapsed;\n\n if (!view.collapsed) {\n ctrl.query(node);\n }\n }\n\n ctrl.isDatabus = async function (uri) {\n var req = {\n method: 'GET',\n url: uri,\n headers: {\n 'Accept': 'application/rdf+turtle'\n }\n }\n\n var res = await ctrl.$http(req);\n var manifest = await DatabusUtils.parseDatabusManifest(res.data);\n var expectedUri = new URL(uri);\n\n if (manifest == undefined || manifest.uri != expectedUri.origin) {\n return false;\n }\n\n return true;\n }\n\n ctrl.getDatabusUri = async function (uri) {\n\n var url = new URL(uri);\n var segments = url.pathname.split('/');\n var base = url.origin;\n var currentUrl = base;\n\n var isDatabus = await ctrl.isDatabus(currentUrl);\n\n if (isDatabus) {\n return currentUrl;\n }\n\n for (var i = 0; segments.length; i++) {\n\n currentUrl += `/${segments[i]}`;\n var isDatabus = await ctrl.isDatabus(currentUrl);\n\n if (isDatabus) {\n return currentUrl;\n }\n }\n\n }\n\n ctrl.onAddResource = async function (uri) {\n\n if (uri.endsWith('/')) {\n uri = uri.substr(0, uri.length - 1);\n }\n\n let node = QueryNode.findChildByUri(ctrl.root, uri);\n\n // Resource already in collection\n if (node != undefined) {\n return;\n }\n\n var databusUri = await ctrl.getDatabusUri(uri);\n\n\n var databusUriLength = DatabusUtils.getResourcePathLength(databusUri);\n var resourceUriLength = DatabusUtils.getResourcePathLength(uri);\n var diff = resourceUriLength - databusUriLength;\n\n if (diff < 0 && diff > 3 || diff == 1) {\n return;\n }\n\n if (diff == 0) {\n ctrl.addDatabus(uri);\n }\n\n if (diff == 2) {\n ctrl.addDatabus(databusUri);\n let databusNode = QueryNode.findChildByUri(ctrl.root, databusUri);\n ctrl.addGroup(databusNode, uri);\n }\n\n if (diff == 3) {\n ctrl.addDatabus(databusUri);\n let databusNode = QueryNode.findChildByUri(ctrl.root, databusUri);\n let groupUri = DatabusUtils.navigateUp(uri);\n ctrl.addGroup(databusNode, groupUri);\n let groupNode = QueryNode.findChildByUri(ctrl.root, groupUri);\n ctrl.addArtifact(groupNode, uri);\n }\n\n ctrl.onChange();\n ctrl.updateViewModel();\n ctrl.$scope.$apply();\n }\n\n ctrl.addDatabus = function (uri) {\n let node = QueryNode.findChildByUri(ctrl.root, uri);\n\n if (node == null) {\n ctrl.root.childNodes.push(new QueryNode(uri, null));\n }\n }\n\n ctrl.addGroup = function (databusNode, uri) {\n let node = QueryNode.findChildByUri(ctrl.root, uri);\n\n if (node == null) {\n databusNode.childNodes.push(new QueryNode(uri, DATAID_GROUP_PROPERTY));\n }\n }\n\n ctrl.addArtifact = function (groupNode, uri) {\n let node = QueryNode.findChildByUri(ctrl.root, uri);\n\n if (node == null) {\n groupNode.childNodes.push(new QueryNode(uri, DATAID_ARTIFACT_PROPERTY));\n }\n }\n\n ctrl.addToCollection = function (source, view, result) {\n\n if (ctrl.isInCollection(result)) {\n QueryNode.removeChildByUri(ctrl.root, result.id[0].value);\n }\n else {\n if (result.typeName[0].value == 'Group') {\n let node = new QueryNode(result.id[0].value, DATAID_GROUP_PROPERTY);\n\n source.childNodes.push(node);\n }\n\n if (result.typeName[0].value == 'Artifact') {\n\n var artifactUri = result.id[0].value;\n let groupUri = DatabusUtils.navigateUp(artifactUri);\n let groupNode = QueryNode.findChildByUri(ctrl.root, groupUri);\n\n if (groupNode == null) {\n groupNode = new QueryNode(groupUri, DATAID_GROUP_PROPERTY);\n source.childNodes.push(groupNode);\n }\n\n let node = new QueryNode(artifactUri, DATAID_ARTIFACT_PROPERTY);\n groupNode.addChild(node);\n }\n }\n\n for (var res of view.searchResults) {\n res.inCollection = ctrl.isInCollection(res);\n }\n\n ctrl.onChange();\n ctrl.updateViewModel();\n }\n\n ctrl.isLastChild = function (group, artifact) {\n\n if (group.childNodes == undefined || group.childNodes.length == 0) {\n return false;\n }\n\n return group.childNodes[group.childNodes.length - 1].uri == artifact.uri;\n }\n\n ctrl.toggleExpand = function (node) {\n node.expanded = !node.expanded;\n ctrl.onChange();\n }\n\n ctrl.mergeFacets = function (node, facets) {\n\n if (node.facets == undefined) {\n node.facets = JSON.parse(JSON.stringify(facets));\n return;\n }\n\n for (var f in facets) {\n\n if (node.facets[f] == undefined) {\n node.facets[f] = JSON.parse(JSON.stringify(facets[f]));\n continue;\n }\n\n for (var value of facets[f].values) {\n if (!node.facets[f].values.includes(value)) {\n node.facets[f].values.push(value);\n }\n }\n }\n\n node.facetLabels = null;\n }\n\n ctrl.getAllFilters = function (groupNode, artifactNode) {\n\n if (artifactNode == null) {\n var result = Object.keys(groupNode.facetSettings)\n return DatabusUtils.uniqueList(result);\n }\n\n var result = Object.keys(groupNode.facetSettings).concat(Object.keys(artifactNode.facetSettings));\n return DatabusUtils.uniqueList(result);\n }\n\n ctrl.$doCheck = function () {\n\n if (ctrl.collection == null) {\n ctrl.previousCollectionId = null;\n return;\n }\n\n if (ctrl.previousCollectionId != ctrl.collection.uuid) {\n ctrl.previousCollectionId = ctrl.collection.uuid;\n\n ctrl.activeNode = null;\n ctrl.viewMode = -1;\n ctrl.updateViewModel();\n }\n }\n\n ctrl.handleKey = function (e, nodeView) {\n if (e.which === 9) {\n nodeView.showSearchResults = false;\n }\n }\n\n ctrl.isInCollection = function (result) {\n let uri = result.id[0].value;\n let node = QueryNode.findChildByUri(ctrl.root, uri);\n return node != null;\n }\n\n ctrl.updateSearchResults = function (view) {\n\n if (view == null || view.searchResults == null) {\n return;\n }\n\n for (var res of view.searchResults) {\n res.inCollection = ctrl.isInCollection(res);\n }\n }\n\n ctrl.searchNode = function (node, nodeView) {\n\n var baseUrl = new URL(node.uri).origin;\n var typeFilters = `typeName=Artifact Group`;\n\n if (node.property == DATAID_GROUP_PROPERTY) {\n var groupName = DatabusUtils.uriToResourceName(node.uri);\n var accountName = DatabusUtils.uriToResourceName(DatabusUtils.navigateUp(node.uri));\n typeFilters = `typeName=Artifact&publisher=${accountName}&group=${groupName}`;\n }\n\n var url = `${baseUrl}/api/search?${typeFilters}&typeNameWeight=0&format=JSON_FULL&minRelevance=15&maxResults=10&query=${nodeView.search}`;\n\n try {\n $http({ method: 'GET', url: url }).then(function successCallback(response) {\n\n nodeView.searchResults = [];\n\n for (var doc of response.data.docs) {\n doc.inCollection = ctrl.isInCollection(doc);\n nodeView.searchResults.push(doc);\n }\n\n }, function errorCallback(response) {\n console.log(response);\n });\n } catch (err) {\n\n }\n\n };\n\n ctrl.toggleExpand = function (view) {\n view.expanded = !view.expanded;\n }\n\n\n ctrl.isValidHttpUrl = function (url) {\n return DatabusUtils.isValidHttpUrl(url);\n }\n\n\n ctrl.updateViewModel = function () {\n ctrl.collectionWrapper = new DatabusCollectionWrapper(ctrl.collection);\n ctrl.root = ctrl.collection.content.root;\n\n ctrl.view = {};\n ctrl.view.groups = {};\n ctrl.view.artifacts = {};\n ctrl.view.sources = {};\n\n for (var s in ctrl.root.childNodes) {\n\n var sourceNode = ctrl.root.childNodes[s];\n\n if (ctrl.view.sources[sourceNode.uri] == undefined) {\n ctrl.view.sources[sourceNode.uri] = {};\n ctrl.view.sources[sourceNode.uri].uri = sourceNode.uri;\n ctrl.view.sources[sourceNode.uri].expanded = true;\n ctrl.view.sources[sourceNode.uri].addMode = 'artifact';\n ctrl.view.sources[sourceNode.uri].customQueryLabel = `New Custom Query`;\n ctrl.view.sources[sourceNode.uri].customQueryInput = ctrl.defaultQuery;\n }\n\n for (var g in sourceNode.childNodes) {\n\n var groupNode = sourceNode.childNodes[g];\n groupNode.expanded = true;\n\n\n ctrl.view.groups[groupNode.uri] = {};\n\n if (DatabusUtils.isValidHttpUrl(groupNode.uri)) {\n\n ctrl.facets.get(groupNode.uri).then(function (res) {\n delete res.facets[DatabusUris.DATABUS_ARTIFACT_PROPERTY];\n ctrl.view.groups[res.uri].facets = res.facets;\n\n var hasVersionFacets = ctrl.view.groups[res.uri].facets[DatabusUris.DCT_HAS_VERSION];\n\n if (hasVersionFacets != null && !hasVersionFacets.values.includes(KEY_LATEST_VERSION)) {\n hasVersionFacets.values.unshift(KEY_LATEST_VERSION);\n }\n\n $scope.$apply();\n });\n\n ctrl.query(groupNode);\n\n for (var a in groupNode.childNodes) {\n\n var artifactNode = groupNode.childNodes[a];\n\n ctrl.view.artifacts[artifactNode.uri] = {};\n ctrl.view.artifacts[artifactNode.uri].expanded = false;\n ctrl.view.artifacts[artifactNode.uri].collapsed = true;\n\n ctrl.facets.get(artifactNode.uri).then(function (res) {\n ctrl.view.artifacts[res.uri].facets = res.facets;\n\n var hasVersionFacets = ctrl.view.artifacts[res.uri].facets[DatabusUris.DCT_HAS_VERSION];\n\n if (hasVersionFacets != null && !hasVersionFacets.values.includes(KEY_LATEST_VERSION)) {\n hasVersionFacets.values.unshift(KEY_LATEST_VERSION);\n }\n\n $scope.$apply();\n //var groupUri = DatabusUtils.navigateUp(artifactNode.uri);\n //ctrl.view.artifacts[artifactNode.uri].facets = result.data;\n //ctrl.mergeFacets(ctrl.view.groups[groupUri], result.data);\n });\n\n\n\n /*en(function(result) {\n\n = result['http://purl.org/dc/terms/hasVersion'].values.unshift(\"$latest\");\n\n\n var groupUri = DatabusUtils.navigateUp(artifactNode.uri);\n ctrl.view.artifacts[artifactNode.uri].facets = result.data;\n ctrl.mergeFacets(ctrl.view.groups[groupUri], result.data);\n\n });\n\n\n \n */\n }\n }\n }\n }\n }\n\n\n\n ctrl.onArtifactDropdownChanged = function (groupNode) {\n ctrl.onChange();\n ctrl.query(groupNode);\n }\n\n ctrl.selectAddFilterValue = function (viewNode, value) {\n viewNode.addFilterValueInput = value;\n viewNode.showValueDrop = false;\n\n ctrl.onAddFilterValueInputChanged(viewNode);\n }\n\n ctrl.selectAddFilterFacet = function (viewNode, value) {\n viewNode.addFilterFacetInput = value;\n viewNode.showFacetDrop = false;\n\n ctrl.onAddFilterFacetInputChanged(viewNode);\n }\n\n ctrl.onAddFilterValueInputChanged = function (viewNode) {\n\n for (var value of viewNode.facets[viewNode.addFilterFacet].values) {\n\n if (viewNode.addFilterValueInput == value) {\n viewNode.addFilterValue = value;\n return;\n }\n }\n\n viewNode.addFilterValue = null;\n }\n\n ctrl.onAddFilterFacetInputChanged = function (viewNode) {\n\n for (var facet in viewNode.facets) {\n\n if (viewNode.addFilterFacetInput == viewNode.facets[facet].label) {\n\n if (viewNode.addFilterFacet != facet) {\n viewNode.addFilterFacet = facet;\n viewNode.addFilterValue = [];\n }\n\n return;\n }\n }\n\n viewNode.addFilterFacet = null;\n viewNode.addFilterValue = [];\n }\n\n ctrl.includesValue = function (objs, value) {\n if (objs == undefined) {\n return false;\n }\n\n for (var obj of objs) {\n if (obj.value == value) {\n return true;\n }\n }\n\n return false;\n }\n\n ctrl.isLocalDatabusNode = function (node) {\n return node.uri == DATABUS_RESOURCE_BASE_URL;\n }\n ctrl.addFilter = function (node, facet, values, checked) {\n\n if (values == null) {\n return;\n }\n\n if (node.facetSettings[facet] == undefined) {\n node.facetSettings[facet] = [];\n }\n\n for (var value of values) {\n\n if (!ctrl.includesValue(node.facetSettings[facet], value.value)) {\n node.facetSettings[facet].push(value);\n }\n }\n\n ctrl.onChange();\n ctrl.query(node);\n }\n\n ctrl.query = function (node) {\n\n if (node.childNodes != undefined && node.childNodes.length > 0) {\n\n node.files = null;\n for (var child of node.childNodes) {\n ctrl.query(child);\n }\n\n return;\n }\n\n var queryNode = QueryNode.createSubTree(node);\n\n var fullQuery = QueryBuilder.build({\n node: queryNode,\n template: QueryTemplates.NODE_FILE_TEMPLATE,\n resourceBaseUrl: DATABUS_RESOURCE_BASE_URL,\n root: ctrl.root\n });\n\n this.querySparql(fullQuery).then(function (result) {\n node.files = result;\n ctrl.$scope.$apply();\n\n });\n }\n\n ctrl.removeFilter = function (node, facet) {\n\n if (node.facetSettings[facet] == undefined) {\n return;\n }\n\n delete node.facetSettings[facet];\n\n ctrl.onChange();\n\n ctrl.query(node);\n }\n\n ctrl.onActiveFilterChanged = function (node) {\n ctrl.onChange();\n ctrl.query(node);\n }\n\n ctrl.getFacetLabels = function (viewNode) {\n\n if (viewNode.facetLabels != undefined) {\n return viewNode.facetLabels;\n }\n var result = [];\n\n for (var f in viewNode.facets) {\n result.push(viewNode.facets[f].label);\n }\n\n viewNode.facetLabels = result;\n return result;\n }\n\n\n ctrl.sortBy = function (property) {\n\n if (ctrl.sortProperty == property) {\n ctrl.sortReverse = !ctrl.sortReverse;\n }\n ctrl.sortProperty = property;\n }\n\n ctrl.formatFileSize = function (size) {\n return DatabusUtils.formatFileSize(size);\n };\n\n ctrl.toHTML = function (html) {\n return $sce.trustAsHtml(html);\n };\n\n ctrl.onComponentAdded = function () {\n\n }\n\n ctrl.customExpanded = function () {\n return ctrl.customNode.expanded && ctrl.collection.content.customQueries.length > 0;\n }\n\n ctrl.generatedExpanded = function () {\n return ctrl.generatedNode.expanded && ctrl.collection.content.groups.length > 0;\n }\n\n ctrl.publishCollection = function () {\n ctrl.onPublish();\n }\n\n ctrl.delete = function () {\n ctrl.onDelete();\n }\n\n ctrl.goToResource = function (node) {\n window.location = node.uri;\n }\n\n ctrl.formatGroupPrefix = function (uri) {\n return DatabusUtils.uriToName(DatabusUtils.navigateUp(uri));\n }\n\n ctrl.formatArtifactPrefix = function (uri) {\n var nav = DatabusUtils.navigateUp(uri);\n var groupName = DatabusUtils.uriToName(nav);\n var userName = DatabusUtils.uriToName(DatabusUtils.navigateUp(nav));\n\n return userName + '/' + groupName;\n }\n\n ctrl.uriToName = function (uri) {\n return DatabusUtils.uriToName(uri);\n }\n\n ctrl.objSize = function (obj) {\n return DatabusUtils.objSize(obj);\n }\n\n ctrl.showCollectionSearch = function () {\n ctrl.open = false;\n ctrl.viewMode = 0;\n ctrl.activeNode = ctrl.rootNode;\n\n $location.hash('search');\n }\n\n // ctrl.printJSON = function() {\n // console.log(JSON.stringify(ctrl.collection));\n // }\n\n // SHOW NODES\n ctrl.showGroupNode = function (groupNode) {\n ctrl.open = true;\n ctrl.viewMode = 3;\n ctrl.activeNode = QueryNode.createFrom(groupNode);\n\n this.updateQuery();\n }\n\n ctrl.showArtifactNode = function (artifactNode, groupNode) {\n\n ctrl.open = true;\n ctrl.viewMode = 1;\n ctrl.activeNode = QueryNode.createFrom(artifactNode);\n\n this.updateQuery();\n }\n\n ctrl.querySparql = async function (query) {\n\n\n try {\n\n var req = {\n method: 'POST',\n url: DatabusConstants.DATABUS_SPARQL_ENDPOINT_URL,\n data: \"format=json&query=\" + encodeURIComponent(query),\n headers: {\n \"Content-type\": \"application/x-www-form-urlencoded\"\n },\n }\n\n var updateResponse = await ctrl.$http(req);\n\n var data = updateResponse.data;\n var bindings = data.results.bindings;\n\n for (var b in bindings) {\n ctrl.reduceBinding(bindings[b]);\n }\n\n return bindings;\n\n\n } catch (e) {\n console.log(e);\n }\n }\n\n ctrl.reduceBinding = function (binding) {\n for (var key in binding) {\n binding[key] = binding[key].value;\n }\n\n return binding;\n }\n\n\n\n ctrl.updateQuery = function () {\n var queryNode = QueryNode.createSubTree(ctrl.activeNode);\n\n ctrl.activeFileQuery = QueryBuilder.build({\n node: queryNode,\n template: QueryTemplates.DEFAULT_FILE_TEMPLATE,\n resourceBaseUrl: DATABUS_RESOURCE_BASE_URL\n });\n\n ctrl.activeFullQuery = QueryBuilder.build({\n node: queryNode,\n template: QueryTemplates.NODE_FILE_TEMPLATE,\n resourceBaseUrl: DATABUS_RESOURCE_BASE_URL\n });\n }\n\n ctrl.onActiveNodeChanged = function () {\n this.updateQuery();\n\n ctrl.onChange();\n }\n\n ctrl.addCustomNode = function (sourceNode, label, desc, query) {\n\n var node = new QueryNode(label, query);\n sourceNode.childNodes.push(node);\n\n\n ctrl.updateViewModel();\n ctrl.onChange();\n }\n\n ctrl.removeNode = function (node) {\n\n var parent = ctrl.collectionWrapper.getParentNode(node);\n ctrl.collectionWrapper.removeNodeByUri(node.uri);\n\n if (parent != null) {\n ctrl.query(parent);\n }\n\n ctrl.onChange();\n }\n\n ctrl.showCustomQueryNode = function (customQueryNode) {\n ctrl.open = true;\n ctrl.viewMode = 2;\n ctrl.activeNode = customQueryNode;\n }\n\n ctrl.list = function (setting) {\n\n var allEntries = Object.keys(setting).map(function (key, index) {\n\n var label = undefined;\n var entry = setting[key];\n\n if (entry.value == '') {\n label = 'None';\n } else if (entry.value == '$latest') {\n label = 'Latest Version';\n } else {\n label = entry.value;\n }\n\n if (entry.checked) {\n return label;\n } else {\n return `${label}`;\n }\n });\n\n\n var list = [];\n var maxLength = 50;\n var length = 0;\n var hasOverflow = false;\n\n for (var entry of allEntries) {\n if (entry.length + length > maxLength) {\n hasOverflow = true;\n break;\n }\n\n length += entry.length;\n list.push(entry);\n }\n\n if (hasOverflow) {\n list.push('...');\n }\n\n return ctrl.$sce.trustAsHtml(list.join(', '));\n // return setting.map(function (v) { return v.value }).join(', ');\n }\n}\n\n\nmodule.exports = CollectionHierarchyControllerTwo;\n\n//# sourceURL=webpack://databus-webapp/./js/components/collection-hierarchy-two/collection-hierarchy.js?"); /***/ }), @@ -125,7 +125,7 @@ eval("// TODO Fabian evtl bug\n\n// hinzufügen eines Controllers zum Modul\nfun \**************************************************************/ /***/ ((module) => { -eval("// hinzufügen eines Controllers zum Modul\nfunction CollectionSearchController(collectionManager, $http, $interval, $sce) {\n\n var ctrl = this;\n\n ctrl.results = [];\n ctrl.collectionManager = collectionManager;\n\n\n\n \n\n\n\n ctrl.formatResult = function (result) {\n return $sce.trustAsHtml(result);\n }\n\n ctrl.getDatabusUrls = function () {\n\n if (ctrl.databusUrls != undefined) {\n return ctrl.databusUrls;\n }\n\n ctrl.databusUrls = [];\n var root = ctrl.collection.content.root;\n\n for (var sourceNode of root.childNodes) {\n ctrl.databusUrls.push(sourceNode.uri);\n }\n\n return ctrl.databusUrls;\n }\n\n ctrl.$onInit = function () {\n\n ctrl.searchInput = '';\n ctrl.filters = {};\n ctrl.filters.filterArtifact = false;\n ctrl.filters.filterGroup = false;\n ctrl.searchCooldown = 1000;\n\n ctrl.root = QueryNode.createFrom(ctrl.collection.content.root);\n\n ctrl.collectionWrapper = new DatabusCollectionWrapper(ctrl.collection);\n ctrl.autoFocus = true;\n }\n\n // TODO Fabian\n ctrl.isInCollection = function (result) {\n let uri = result.resource[0].value;\n let node = QueryNode.findChildByUri(ctrl.root, uri);\n\n return node != null;\n }\n\n\n ctrl.addToCollection = function (result) {\n\n var currentSource = ctrl.targetDatabusUrl;\n var sourceNode = QueryNode.findChildByUri(ctrl.root, currentSource);\n\n if (result.inCollection) {\n QueryNode.removeChildByUri(ctrl.root, result.resource[0].value);\n }\n else {\n if (result.typeName[0].value == 'Group') {\n let node = new QueryNode(result.resource[0].value, 'databus:group');\n\n sourceNode.addChild(node);\n ctrl.onComponentAdded();\n }\n\n if (result.typeName[0].value == 'Artifact') {\n\n var artifactUri = result.resource[0].value;\n let groupUri = DatabusUtils.navigateUp(artifactUri);\n let groupNode = QueryNode.findChildByUri(ctrl.root, groupUri);\n\n if (groupNode == null) {\n groupNode = new QueryNode(groupUri, 'databus:group');\n sourceNode.addChild(groupNode);\n }\n\n let node = new QueryNode(artifactUri, 'databus:artifact');\n groupNode.addChild(node);\n\n ctrl.onComponentAdded();\n }\n }\n\n for (let r in ctrl.results) {\n ctrl.results[r].inCollection = ctrl.isInCollection(ctrl.results[r]);\n }\n\n ctrl.collectionManager.saveLocally();\n\n console.log(ctrl.root);\n }\n\n $interval(function () {\n\n if (ctrl.searchChanged) {\n\n if (!DatabusUtils.isValidHttpUrl(ctrl.targetDatabusUrl)) {\n return;\n }\n\n var typeFilters = '?typeName=Artifact Group';\n\n if (ctrl.filters.filterArtifact || ctrl.filters.filterGroup) {\n\n typeFilters = '?typeName='\n if (ctrl.filters.filterArtifact) {\n typeFilters += 'Artifact ';\n }\n if (ctrl.filters.filterGroup) {\n typeFilters += 'Group ';\n }\n }\n\n ctrl.lastQuery = ctrl.searchInput;\n\n try {\n\n $http({\n method: 'GET',\n url: ctrl.targetDatabusUrl + '/api/search' + typeFilters + '&format=JSON_FULL&minRelevance=10&maxResults=10&query='\n + ctrl.searchInput,\n }).then(function successCallback(response) {\n\n if (ctrl.lastQuery != response.data.query) {\n return;\n }\n\n ctrl.results = response.data.docs;\n\n for (var r in ctrl.results) {\n ctrl.results[r].inCollection = ctrl.isInCollection(ctrl.results[r]);\n }\n\n }, function errorCallback(response) {\n });\n } catch (err) {\n\n }\n\n ctrl.searchChanged = false;\n };\n }, ctrl.searchCooldown);\n\n ctrl.search = function () {\n ctrl.searchChanged = true;\n };\n}\n\n\nmodule.exports = CollectionSearchController;\n\n//# sourceURL=webpack://databus-webapp/./js/components/collection-search/collection-search.js?"); +eval("// hinzufügen eines Controllers zum Modul\nfunction CollectionSearchController(collectionManager, $http, $interval, $sce) {\n\n var ctrl = this;\n\n ctrl.results = [];\n ctrl.collectionManager = collectionManager;\n\n\n\n \n\n\n\n ctrl.formatResult = function (result) {\n return $sce.trustAsHtml(result);\n }\n\n ctrl.getDatabusUrls = function () {\n\n if (ctrl.databusUrls != undefined) {\n return ctrl.databusUrls;\n }\n\n ctrl.databusUrls = [];\n var root = ctrl.collection.content.root;\n\n for (var sourceNode of root.childNodes) {\n ctrl.databusUrls.push(sourceNode.uri);\n }\n\n return ctrl.databusUrls;\n }\n\n ctrl.$onInit = function () {\n\n ctrl.searchInput = '';\n ctrl.filters = {};\n ctrl.filters.filterArtifact = false;\n ctrl.filters.filterGroup = false;\n ctrl.searchCooldown = 1000;\n\n ctrl.root = QueryNode.createFrom(ctrl.collection.content.root);\n\n ctrl.collectionWrapper = new DatabusCollectionWrapper(ctrl.collection);\n ctrl.autoFocus = true;\n }\n\n // TODO Fabian\n ctrl.isInCollection = function (result) {\n let uri = result.id[0].value;\n let node = QueryNode.findChildByUri(ctrl.root, uri);\n\n return node != null;\n }\n\n\n ctrl.addToCollection = function (result) {\n\n var currentSource = ctrl.targetDatabusUrl;\n var sourceNode = QueryNode.findChildByUri(ctrl.root, currentSource);\n\n if (result.inCollection) {\n QueryNode.removeChildByUri(ctrl.root, result.id[0].value);\n }\n else {\n if (result.typeName[0].value == 'Group') {\n let node = new QueryNode(result.id[0].value, 'databus:group');\n\n sourceNode.addChild(node);\n ctrl.onComponentAdded();\n }\n\n if (result.typeName[0].value == 'Artifact') {\n\n var artifactUri = result.id[0].value;\n let groupUri = DatabusUtils.navigateUp(artifactUri);\n let groupNode = QueryNode.findChildByUri(ctrl.root, groupUri);\n\n if (groupNode == null) {\n groupNode = new QueryNode(groupUri, 'databus:group');\n sourceNode.addChild(groupNode);\n }\n\n let node = new QueryNode(artifactUri, 'databus:artifact');\n groupNode.addChild(node);\n\n ctrl.onComponentAdded();\n }\n }\n\n for (let r in ctrl.results) {\n ctrl.results[r].inCollection = ctrl.isInCollection(ctrl.results[r]);\n }\n\n ctrl.collectionManager.saveLocally();\n\n console.log(ctrl.root);\n }\n\n $interval(function () {\n\n if (ctrl.searchChanged) {\n\n if (!DatabusUtils.isValidHttpUrl(ctrl.targetDatabusUrl)) {\n return;\n }\n\n var typeFilters = '?typeName=Artifact Group';\n\n if (ctrl.filters.filterArtifact || ctrl.filters.filterGroup) {\n\n typeFilters = '?typeName='\n if (ctrl.filters.filterArtifact) {\n typeFilters += 'Artifact ';\n }\n if (ctrl.filters.filterGroup) {\n typeFilters += 'Group ';\n }\n }\n\n ctrl.lastQuery = ctrl.searchInput;\n\n try {\n\n $http({\n method: 'GET',\n url: ctrl.targetDatabusUrl + '/api/search' + typeFilters + '&format=JSON_FULL&minRelevance=10&maxResults=10&query='\n + ctrl.searchInput,\n }).then(function successCallback(response) {\n\n if (ctrl.lastQuery != response.data.query) {\n return;\n }\n\n ctrl.results = response.data.docs;\n\n for (var r in ctrl.results) {\n ctrl.results[r].inCollection = ctrl.isInCollection(ctrl.results[r]);\n }\n\n }, function errorCallback(response) {\n });\n } catch (err) {\n\n }\n\n ctrl.searchChanged = false;\n };\n }, ctrl.searchCooldown);\n\n ctrl.search = function () {\n ctrl.searchChanged = true;\n };\n}\n\n\nmodule.exports = CollectionSearchController;\n\n//# sourceURL=webpack://databus-webapp/./js/components/collection-search/collection-search.js?"); /***/ }), @@ -175,7 +175,17 @@ eval("\nclass DatabusAlert {\n static alert($scope, isSuccess, message, ms) {\n \****************************************************/ /***/ ((module) => { -eval("// hinzufügen eines Controllers zum Modul\nfunction DatabusIconController() {\n\n var ctrl = this;\n\n ctrl.iconMap = {};\n ctrl.iconMap['databus'] = \"m 0.76949155,0.7702454 v 5.24959 l 29.33129045,-10e-4 6.27262,8.8675006 -0.002,32.0824 4.7212,-0.002 V 0.7702354 Z m 18.43511045,8.3952603 -5.68354,0.006 7.1979,10.5484003 -0.004,27.24663 4.70393,-0.002 0.0167,-28.60108 z m -9.4730904,0.002 -8.96510005,0.002 0.004,37.7960503 16.79563045,-0.004 0.001,-26.29103 z m 13.2512904,0 5.59825,8.2188903 -0.0396,29.57614 4.70307,-0.002 0.006,-31.09587 -4.55858,-6.6940403 z\";\n ctrl.iconMap['add'] = \"M 11 11 M 2 12 L 11 12 L 11 21 L 12 21 L 12 12 L 21 12 L 21 11 L 12 11 L 12 2 L 11 2 L 11 11 L 2 11 L 2 12\";\n ctrl.iconMap['remove'] = \"M 11 11 M 2 12 L 21 12 L 21 11 L 2 11 L 2 12\";\n ctrl.iconMap['add-thick'] = \"m11 11h-7.25c-.414 0-.75.336-.75.75s.336.75.75.75h7.25v7.25c0 .414.336.75.75.75s.75-.336.75-.75v-7.25h7.25c.414 0 .75-.336.75-.75s-.336-.75-.75-.75h-7.25v-7.25c0-.414-.336-.75-.75-.75s-.75.336-.75.75z\";\n ctrl.iconMap['close'] = \"m12 10.93 5.719-5.72c.146-.146.339-.219.531-.219.404 0 .75.324.75.749 0 .193-.073.385-.219.532l-5.72 5.719 5.719 5.719c.147.147.22.339.22.531 0 .427-.349.75-.75.75-.192 0-.385-.073-.531-.219l-5.719-5.719-5.719 5.719c-.146.146-.339.219-.531.219-.401 0-.75-.323-.75-.75 0-.192.073-.384.22-.531l5.719-5.719-5.72-5.719c-.146-.147-.219-.339-.219-.532 0-.425.346-.749.75-.749.192 0 .385.073.531.219z\";\n ctrl.iconMap['delete'] = \"M9 3h6v-1.75c0-.066-.026-.13-.073-.177-.047-.047-.111-.073-.177-.073h-5.5c-.066 0-.13.026-.177.073-.047.047-.073.111-.073.177v1.75zm11 1h-16v18c0 .552.448 1 1 1h14c.552 0 1-.448 1-1v-18zm-10 3.5c0-.276-.224-.5-.5-.5s-.5.224-.5.5v12c0 .276.224.5.5.5s.5-.224.5-.5v-12zm5 0c0-.276-.224-.5-.5-.5s-.5.224-.5.5v12c0 .276.224.5.5.5s.5-.224.5-.5v-12zm8-4.5v1h-2v18c0 1.105-.895 2-2 2h-14c-1.105 0-2-.895-2-2v-18h-2v-1h7v-2c0-.552.448-1 1-1h6c.552 0 1 .448 1 1v2h7z\";\n ctrl.iconMap['goto'] = \"M21.883 12l-7.527 6.235.644.765 9-7.521-9-7.479-.645.764 7.529 6.236h-21.884v1h21.883z\";\n ctrl.iconMap['edit'] = \"M8.071 21.586l-7.071 1.414 1.414-7.071 14.929-14.929 5.657 5.657-14.929 14.929zm-.493-.921l-4.243-4.243-1.06 5.303 5.303-1.06zm9.765-18.251l-13.3 13.301 4.242 4.242 13.301-13.3-4.243-4.243z\";\n ctrl.iconMap['edit-thick'] = \"M7.127 22.564l-7.126 1.436 1.438-7.125 5.688 5.689zm-4.274-7.104l5.688 5.689 15.46-15.46-5.689-5.689-15.459 15.46z\";\n ctrl.iconMap['goback'] = \"M2.117 12l7.527 6.235-.644.765-9-7.521 9-7.479.645.764-7.529 6.236h21.884v1h-21.883z\";\n ctrl.iconMap['right'] = \"M4 .755l14.374 11.245-14.374 11.219.619.781 15.381-12-15.391-12-.609.755z\";\n ctrl.iconMap['left'] = \"M20 .755l-14.374 11.245 14.374 11.219-.619.781-15.381-12 15.391-12 .609.755z\";\n ctrl.iconMap['down'] = \"M23.245 4l-11.245 14.374-11.219-14.374-.781.619 12 15.381 12-15.391-.755-.609z\";\n ctrl.iconMap['left-thick'] = \"M16.67 0l2.83 2.829-9.339 9.175 9.339 9.167-2.83 2.829-12.17-11.996z\";\n ctrl.iconMap['help'] = \"M12 0c6.623 0 12 5.377 12 12s-5.377 12-12 12-12-5.377-12-12 5.377-12 12-12zm0 1c6.071 0 11 4.929 11 11s-4.929 11-11 11-11-4.929-11-11 4.929-11 11-11zm.053 17c.466 0 .844-.378.844-.845 0-.466-.378-.844-.844-.844-.466 0-.845.378-.845.844 0 .467.379.845.845.845zm.468-2.822h-.998c-.035-1.162.182-2.054.939-2.943.491-.57 1.607-1.479 1.945-2.058.722-1.229.077-3.177-2.271-3.177-1.439 0-2.615.877-2.928 2.507l-1.018-.102c.28-2.236 1.958-3.405 3.922-3.405 1.964 0 3.615 1.25 3.615 3.22 0 1.806-1.826 2.782-2.638 3.868-.422.563-.555 1.377-.568 2.09z\";\n ctrl.iconMap['max'] = \"M24 22h-24v-20h24v20zm-7-1v-15h-16v15h16zm1 0h5v-18h-22v2h17v16zm-6-6h-1v-3.241l-7.241 7.241-.759-.759 7.241-7.241h-3.241v-1h5v5z\";\n ctrl.iconMap['min'] = \"M24 22h-24v-20h24v20zm-23-9v8h10v-8h-10zm22 8v-18h-22v9h11v9h11zm-4-9h-5v-5h1v3.241l5.241-5.241.759.759-5.241 5.241h3.241v1z\";\n ctrl.iconMap['error'] = \"M24 23h-24l12-22 12 22zm-22.315-1h20.63l-10.315-18.912-10.315 18.912zm10.315-2c.466 0 .845-.378.845-.845 0-.466-.379-.844-.845-.844-.466 0-.845.378-.845.844 0 .467.379.845.845.845zm.5-11v8h-1v-8h1z\";\n ctrl.iconMap['info'] = \"M12 0c6.623 0 12 5.377 12 12s-5.377 12-12 12-12-5.377-12-12 5.377-12 12-12zm0 1c6.071 0 11 4.929 11 11s-4.929 11-11 11-11-4.929-11-11 4.929-11 11-11zm.5 17h-1v-9h1v9zm-.5-12c.466 0 .845.378.845.845 0 .466-.379.844-.845.844-.466 0-.845-.378-.845-.844 0-.467.379-.845.845-.845z\";\n ctrl.iconMap['eye'] = \"M12.01 20c-5.065 0-9.586-4.211-12.01-8.424 2.418-4.103 6.943-7.576 12.01-7.576 5.135 0 9.635 3.453 11.999 7.564-2.241 4.43-6.726 8.436-11.999 8.436zm-10.842-8.416c.843 1.331 5.018 7.416 10.842 7.416 6.305 0 10.112-6.103 10.851-7.405-.772-1.198-4.606-6.595-10.851-6.595-6.116 0-10.025 5.355-10.842 6.584zm10.832-4.584c2.76 0 5 2.24 5 5s-2.24 5-5 5-5-2.24-5-5 2.24-5 5-5zm0 1c2.208 0 4 1.792 4 4s-1.792 4-4 4-4-1.792-4-4 1.792-4 4-4z\";\n ctrl.iconMap['add-artifact'] = \"M 12 0 M 12.016 1.424 L 21.756 12.053 L 12.016 22.563 L 2.204 12.005 L 3.396 10.721 L 2.774 10.058 L 1 12 L 12 24 L 23 12 L 12 0 L 10.153 2.078 L 10.837 2.741 Z M 6 6 L 6 2 L 7 2 L 7 6 L 11 6 L 11 7 L 7 7 L 7 11 L 6 11 L 6 7 L 2 7 L 2 6 L 6 6\";\n ctrl.iconMap['add-button'] = \"M24 10h-10v-10h-4v10h-10v4h10v10h4v-10h10z\";\n ctrl.iconMap['collections'] = \"M11.499 12.03v11.971l-10.5-5.603v-11.835l10.5 5.467zm11.501 6.368l-10.501 5.602v-11.968l10.501-5.404v11.77zm-16.889-15.186l10.609 5.524-4.719 2.428-10.473-5.453 4.583-2.499zm16.362 2.563l-4.664 2.4-10.641-5.54 4.831-2.635 10.474 5.775z\";\n ctrl.iconMap['collections-thin'] = \"M23 6.066v12.065l-11.001 5.869-11-5.869v-12.131l11-6 11.001 6.066zm-21.001 11.465l9.5 5.069v-10.57l-9.5-4.946v10.447zm20.001-10.388l-9.501 4.889v10.568l9.501-5.069v-10.388zm-5.52 1.716l-9.534-4.964-4.349 2.373 9.404 4.896 4.479-2.305zm-8.476-5.541l9.565 4.98 3.832-1.972-9.405-5.185-3.992 2.177z\";\n ctrl.iconMap['content'] = \"M9.484 15.696l-.711-.696-2.552 2.607-1.539-1.452-.698.709 2.25 2.136 3.25-3.304zm0-5l-.711-.696-2.552 2.607-1.539-1.452-.698.709 2.25 2.136 3.25-3.304zm0-5l-.711-.696-2.552 2.607-1.539-1.452-.698.709 2.25 2.136 3.25-3.304zm10.516 11.304h-8v1h8v-1zm0-5h-8v1h8v-1zm0-5h-8v1h8v-1zm4-5h-24v20h24v-20zm-1 19h-22v-18h22v18z\"\n ctrl.iconMap['menu'] = \"M24 18v1h-24v-1h24zm0-6v1h-24v-1h24zm0-6v1h-24v-1h24z\";\n ctrl.iconMap['copy'] = \"M17 7h6v16h-16v-6h-6v-16h16v6zm5 1h-14v14h14v-14zm-6-1v-5h-14v14h5v-9h9z\";\n ctrl.iconMap['upload'] = \"M9 16h-8v6h22v-6h-8v-1h9v8h-24v-8h9v1zm11 2c.552 0 1 .448 1 1s-.448 1-1 1-1-.448-1-1 .448-1 1-1zm-7.5 0h-1v-14.883l-4.735 5.732-.765-.644 6.021-7.205 5.979 7.195-.764.645-4.736-5.724v14.884z\";\n ctrl.iconMap['hide'] = \"M8.137 15.147c-.71-.857-1.146-1.947-1.146-3.147 0-2.76 2.241-5 5-5 1.201 0 2.291.435 3.148 1.145l1.897-1.897c-1.441-.738-3.122-1.248-5.035-1.248-6.115 0-10.025 5.355-10.842 6.584.529.834 2.379 3.527 5.113 5.428l1.865-1.865zm6.294-6.294c-.673-.53-1.515-.853-2.44-.853-2.207 0-4 1.792-4 4 0 .923.324 1.765.854 2.439l5.586-5.586zm7.56-6.146l-19.292 19.293-.708-.707 3.548-3.548c-2.298-1.612-4.234-3.885-5.548-6.169 2.418-4.103 6.943-7.576 12.01-7.576 2.065 0 4.021.566 5.782 1.501l3.501-3.501.707.707zm-2.465 3.879l-.734.734c2.236 1.619 3.628 3.604 4.061 4.274-.739 1.303-4.546 7.406-10.852 7.406-1.425 0-2.749-.368-3.951-.938l-.748.748c1.475.742 3.057 1.19 4.699 1.19 5.274 0 9.758-4.006 11.999-8.436-1.087-1.891-2.63-3.637-4.474-4.978zm-3.535 5.414c0-.554-.113-1.082-.317-1.562l.734-.734c.361.69.583 1.464.583 2.296 0 2.759-2.24 5-5 5-.832 0-1.604-.223-2.295-.583l.734-.735c.48.204 1.007.318 1.561.318 2.208 0 4-1.792 4-4z\";\n ctrl.iconMap['download'] = \"M6 16h-5v6h22v-6h-5v-1h6v8h-24v-8h6v1zm14 2c.552 0 1 .448 1 1s-.448 1-1 1-1-.448-1-1 .448-1 1-1zm-7.5-17v14.884l4.736-5.724.764.645-5.979 7.195-6.021-7.205.765-.644 4.735 5.732v-14.883h1z\";\n ctrl.iconMap['import'] = \"M16.965 2.381c3.593 1.946 6.035 5.749 6.035 10.119 0 6.347-5.153 11.5-11.5 11.5s-11.5-5.153-11.5-11.5c0-4.37 2.442-8.173 6.035-10.119l.608.809c-3.353 1.755-5.643 5.267-5.643 9.31 0 5.795 4.705 10.5 10.5 10.5s10.5-4.705 10.5-10.5c0-4.043-2.29-7.555-5.643-9.31l.608-.809zm-4.965-2.381v14.826l3.747-4.604.753.666-5 6.112-5-6.101.737-.679 3.763 4.608v-14.828h1z\";\n ctrl.iconMap['filter'] = \"M23 0l-9 14.146v7.73l-3.996 2.124v-9.853l-9.004-14.147h22zm-20.249 1l8.253 12.853v8.491l1.996-1.071v-7.419l8.229-12.854h-18.478z\";\n ctrl.iconMap['check'] = \"M9 22l-10-10.598 2.798-2.859 7.149 7.473 13.144-14.016 2.909 2.806z\";\n ctrl.iconMap['settings'] = \"M12 8.666c-1.838 0-3.333 1.496-3.333 3.334s1.495 3.333 3.333 3.333 3.333-1.495 3.333-3.333-1.495-3.334-3.333-3.334m0 7.667c-2.39 0-4.333-1.943-4.333-4.333s1.943-4.334 4.333-4.334 4.333 1.944 4.333 4.334c0 2.39-1.943 4.333-4.333 4.333m-1.193 6.667h2.386c.379-1.104.668-2.451 2.107-3.05 1.496-.617 2.666.196 3.635.672l1.686-1.688c-.508-1.047-1.266-2.199-.669-3.641.567-1.369 1.739-1.663 3.048-2.099v-2.388c-1.235-.421-2.471-.708-3.047-2.098-.572-1.38.057-2.395.669-3.643l-1.687-1.686c-1.117.547-2.221 1.257-3.642.668-1.374-.571-1.656-1.734-2.1-3.047h-2.386c-.424 1.231-.704 2.468-2.099 3.046-.365.153-.718.226-1.077.226-.843 0-1.539-.392-2.566-.893l-1.687 1.686c.574 1.175 1.251 2.237.669 3.643-.571 1.375-1.734 1.654-3.047 2.098v2.388c1.226.418 2.468.705 3.047 2.098.581 1.403-.075 2.432-.669 3.643l1.687 1.687c1.45-.725 2.355-1.204 3.642-.669 1.378.572 1.655 1.738 2.1 3.047m3.094 1h-3.803c-.681-1.918-.785-2.713-1.773-3.123-1.005-.419-1.731.132-3.466.952l-2.689-2.689c.873-1.837 1.367-2.465.953-3.465-.412-.991-1.192-1.087-3.123-1.773v-3.804c1.906-.678 2.712-.782 3.123-1.773.411-.991-.071-1.613-.953-3.466l2.689-2.688c1.741.828 2.466 1.365 3.465.953.992-.412 1.082-1.185 1.775-3.124h3.802c.682 1.918.788 2.714 1.774 3.123 1.001.416 1.709-.119 3.467-.952l2.687 2.688c-.878 1.847-1.361 2.477-.952 3.465.411.992 1.192 1.087 3.123 1.774v3.805c-1.906.677-2.713.782-3.124 1.773-.403.975.044 1.561.954 3.464l-2.688 2.689c-1.728-.82-2.467-1.37-3.456-.955-.988.41-1.08 1.146-1.785 3.126\";\n ctrl.iconMap['clipboard'] = \"M 17 17 L 7 17 L 7 16 L 17 16 L 17 17 Z M 17 14 L 7 14 L 7 13 L 17 13 L 17 14 Z M 17 11 L 7 11 L 7 10 L 17 10 L 17 11 Z M 16 6 L 8 6 L 7 1 L 17 1 L 16 6 Z M 15.7 2 L 8.25 2 L 8.9 5 L 15.1 5 L 15.7 2 Z M 22 23 L 2 23 L 2 3 L 5.5 3 L 5.7 4 L 3 4 L 3 22 L 21 22 L 21 4 L 18.3 4 L 18.5 3 L 22 3 L 22 23 Z\";\n ctrl.iconMap['sort-desc'] = \"M11 21.883l-6.235-7.527-.765.644 7.521 9 7.479-9-.764-.645-6.236 7.529v-21.884h-1v21.883z\";\n ctrl.iconMap['sort-asc'] = \"M11 2.206l-6.235 7.528-.765-.645 7.521-9 7.479 9-.764.646-6.236-7.53v21.884h-1v-21.883z\";\n ctrl.$onInit = function() {\n ctrl.path = ctrl.iconMap[ctrl.shape];\n }\n}\n\n\nmodule.exports = DatabusIconController;\n\n//# sourceURL=webpack://databus-webapp/./js/components/databus-icon/databus-icon.js?"); +eval("// hinzufügen eines Controllers zum Modul\r\nfunction DatabusIconController() {\r\n\r\n var ctrl = this;\r\n\r\n ctrl.iconMap = {};\r\n ctrl.iconMap['databus'] = \"m 1.8847 0.3851 v 2.6248 l 14.6656 -0.0005 l 3.1363 4.4338 l -0.001 16.0412 l 2.3606 -0.001 V 0.3851 Z m 9.2176 4.1976 l -2.8418 0.003 l 3.5989 5.2742 l -0.002 13.6233 l 2.352 -0.001 l 0.0083 -14.3005 z m -4.7365 0.001 l -4.4826 0.001 l 0.002 18.898 l 8.3978 -0.002 l 0.0005 -13.1455 z m 6.6256 0 l 2.7991 4.1094 l -0.0198 14.7881 l 2.3515 -0.001 l 0.003 -15.5479 l -2.2793 -3.347 z\";\r\n ctrl.iconMap['add'] = \"M 11 11 M 2 12 L 11 12 L 11 21 L 12 21 L 12 12 L 21 12 L 21 11 L 12 11 L 12 2 L 11 2 L 11 11 L 2 11 L 2 12\";\r\n ctrl.iconMap['remove'] = \"M 11 11 M 2 12 L 21 12 L 21 11 L 2 11 L 2 12\";\r\n ctrl.iconMap['add-thick'] = \"m11 11h-7.25c-.414 0-.75.336-.75.75s.336.75.75.75h7.25v7.25c0 .414.336.75.75.75s.75-.336.75-.75v-7.25h7.25c.414 0 .75-.336.75-.75s-.336-.75-.75-.75h-7.25v-7.25c0-.414-.336-.75-.75-.75s-.75.336-.75.75z\";\r\n ctrl.iconMap['close'] = \"m12 10.93 5.719-5.72c.146-.146.339-.219.531-.219.404 0 .75.324.75.749 0 .193-.073.385-.219.532l-5.72 5.719 5.719 5.719c.147.147.22.339.22.531 0 .427-.349.75-.75.75-.192 0-.385-.073-.531-.219l-5.719-5.719-5.719 5.719c-.146.146-.339.219-.531.219-.401 0-.75-.323-.75-.75 0-.192.073-.384.22-.531l5.719-5.719-5.72-5.719c-.146-.147-.219-.339-.219-.532 0-.425.346-.749.75-.749.192 0 .385.073.531.219z\";\r\n ctrl.iconMap['delete'] = \"M9 3h6v-1.75c0-.066-.026-.13-.073-.177-.047-.047-.111-.073-.177-.073h-5.5c-.066 0-.13.026-.177.073-.047.047-.073.111-.073.177v1.75zm11 1h-16v18c0 .552.448 1 1 1h14c.552 0 1-.448 1-1v-18zm-10 3.5c0-.276-.224-.5-.5-.5s-.5.224-.5.5v12c0 .276.224.5.5.5s.5-.224.5-.5v-12zm5 0c0-.276-.224-.5-.5-.5s-.5.224-.5.5v12c0 .276.224.5.5.5s.5-.224.5-.5v-12zm8-4.5v1h-2v18c0 1.105-.895 2-2 2h-14c-1.105 0-2-.895-2-2v-18h-2v-1h7v-2c0-.552.448-1 1-1h6c.552 0 1 .448 1 1v2h7z\";\r\n ctrl.iconMap['goto'] = \"M21.883 12l-7.527 6.235.644.765 9-7.521-9-7.479-.645.764 7.529 6.236h-21.884v1h21.883z\";\r\n ctrl.iconMap['edit'] = \"M8.071 21.586l-7.071 1.414 1.414-7.071 14.929-14.929 5.657 5.657-14.929 14.929zm-.493-.921l-4.243-4.243-1.06 5.303 5.303-1.06zm9.765-18.251l-13.3 13.301 4.242 4.242 13.301-13.3-4.243-4.243z\";\r\n ctrl.iconMap['edit-thick'] = \"M7.127 22.564l-7.126 1.436 1.438-7.125 5.688 5.689zm-4.274-7.104l5.688 5.689 15.46-15.46-5.689-5.689-15.459 15.46z\";\r\n ctrl.iconMap['goback'] = \"M2.117 12l7.527 6.235-.644.765-9-7.521 9-7.479.645.764-7.529 6.236h21.884v1h-21.883z\";\r\n ctrl.iconMap['right'] = \"M4 .755l14.374 11.245-14.374 11.219.619.781 15.381-12-15.391-12-.609.755z\";\r\n ctrl.iconMap['left'] = \"M20 .755l-14.374 11.245 14.374 11.219-.619.781-15.381-12 15.391-12 .609.755z\";\r\n ctrl.iconMap['down'] = \"M23.245 4l-11.245 14.374-11.219-14.374-.781.619 12 15.381 12-15.391-.755-.609z\";\r\n ctrl.iconMap['left-thick'] = \"M16.67 0l2.83 2.829-9.339 9.175 9.339 9.167-2.83 2.829-12.17-11.996z\";\r\n ctrl.iconMap['help'] = \"M12 0c6.623 0 12 5.377 12 12s-5.377 12-12 12-12-5.377-12-12 5.377-12 12-12zm0 1c6.071 0 11 4.929 11 11s-4.929 11-11 11-11-4.929-11-11 4.929-11 11-11zm.053 17c.466 0 .844-.378.844-.845 0-.466-.378-.844-.844-.844-.466 0-.845.378-.845.844 0 .467.379.845.845.845zm.468-2.822h-.998c-.035-1.162.182-2.054.939-2.943.491-.57 1.607-1.479 1.945-2.058.722-1.229.077-3.177-2.271-3.177-1.439 0-2.615.877-2.928 2.507l-1.018-.102c.28-2.236 1.958-3.405 3.922-3.405 1.964 0 3.615 1.25 3.615 3.22 0 1.806-1.826 2.782-2.638 3.868-.422.563-.555 1.377-.568 2.09z\";\r\n ctrl.iconMap['max'] = \"M24 22h-24v-20h24v20zm-7-1v-15h-16v15h16zm1 0h5v-18h-22v2h17v16zm-6-6h-1v-3.241l-7.241 7.241-.759-.759 7.241-7.241h-3.241v-1h5v5z\";\r\n ctrl.iconMap['min'] = \"M24 22h-24v-20h24v20zm-23-9v8h10v-8h-10zm22 8v-18h-22v9h11v9h11zm-4-9h-5v-5h1v3.241l5.241-5.241.759.759-5.241 5.241h3.241v1z\";\r\n ctrl.iconMap['error'] = \"M24 23h-24l12-22 12 22zm-22.315-1h20.63l-10.315-18.912-10.315 18.912zm10.315-2c.466 0 .845-.378.845-.845 0-.466-.379-.844-.845-.844-.466 0-.845.378-.845.844 0 .467.379.845.845.845zm.5-11v8h-1v-8h1z\";\r\n ctrl.iconMap['info'] = \"M12 0c6.623 0 12 5.377 12 12s-5.377 12-12 12-12-5.377-12-12 5.377-12 12-12zm0 1c6.071 0 11 4.929 11 11s-4.929 11-11 11-11-4.929-11-11 4.929-11 11-11zm.5 17h-1v-9h1v9zm-.5-12c.466 0 .845.378.845.845 0 .466-.379.844-.845.844-.466 0-.845-.378-.845-.844 0-.467.379-.845.845-.845z\";\r\n ctrl.iconMap['eye'] = \"M12.01 20c-5.065 0-9.586-4.211-12.01-8.424 2.418-4.103 6.943-7.576 12.01-7.576 5.135 0 9.635 3.453 11.999 7.564-2.241 4.43-6.726 8.436-11.999 8.436zm-10.842-8.416c.843 1.331 5.018 7.416 10.842 7.416 6.305 0 10.112-6.103 10.851-7.405-.772-1.198-4.606-6.595-10.851-6.595-6.116 0-10.025 5.355-10.842 6.584zm10.832-4.584c2.76 0 5 2.24 5 5s-2.24 5-5 5-5-2.24-5-5 2.24-5 5-5zm0 1c2.208 0 4 1.792 4 4s-1.792 4-4 4-4-1.792-4-4 1.792-4 4-4z\";\r\n ctrl.iconMap['add-artifact'] = \"M 12 0 M 12.016 1.424 L 21.756 12.053 L 12.016 22.563 L 2.204 12.005 L 3.396 10.721 L 2.774 10.058 L 1 12 L 12 24 L 23 12 L 12 0 L 10.153 2.078 L 10.837 2.741 Z M 6 6 L 6 2 L 7 2 L 7 6 L 11 6 L 11 7 L 7 7 L 7 11 L 6 11 L 6 7 L 2 7 L 2 6 L 6 6\";\r\n ctrl.iconMap['add-button'] = \"M24 10h-10v-10h-4v10h-10v4h10v10h4v-10h10z\";\r\n ctrl.iconMap['collections'] = \"M11.499 12.03v11.971l-10.5-5.603v-11.835l10.5 5.467zm11.501 6.368l-10.501 5.602v-11.968l10.501-5.404v11.77zm-16.889-15.186l10.609 5.524-4.719 2.428-10.473-5.453 4.583-2.499zm16.362 2.563l-4.664 2.4-10.641-5.54 4.831-2.635 10.474 5.775z\";\r\n ctrl.iconMap['collections-thin'] = \"M23 6.066v12.065l-11.001 5.869-11-5.869v-12.131l11-6 11.001 6.066zm-21.001 11.465l9.5 5.069v-10.57l-9.5-4.946v10.447zm20.001-10.388l-9.501 4.889v10.568l9.501-5.069v-10.388zm-5.52 1.716l-9.534-4.964-4.349 2.373 9.404 4.896 4.479-2.305zm-8.476-5.541l9.565 4.98 3.832-1.972-9.405-5.185-3.992 2.177z\";\r\n ctrl.iconMap['content'] = \"M9.484 15.696l-.711-.696-2.552 2.607-1.539-1.452-.698.709 2.25 2.136 3.25-3.304zm0-5l-.711-.696-2.552 2.607-1.539-1.452-.698.709 2.25 2.136 3.25-3.304zm0-5l-.711-.696-2.552 2.607-1.539-1.452-.698.709 2.25 2.136 3.25-3.304zm10.516 11.304h-8v1h8v-1zm0-5h-8v1h8v-1zm0-5h-8v1h8v-1zm4-5h-24v20h24v-20zm-1 19h-22v-18h22v18z\"\r\n ctrl.iconMap['menu'] = \"M24 18v1h-24v-1h24zm0-6v1h-24v-1h24zm0-6v1h-24v-1h24z\";\r\n ctrl.iconMap['copy'] = \"M17 7h6v16h-16v-6h-6v-16h16v6zm5 1h-14v14h14v-14zm-6-1v-5h-14v14h5v-9h9z\";\r\n ctrl.iconMap['upload'] = \"M9 16h-8v6h22v-6h-8v-1h9v8h-24v-8h9v1zm11 2c.552 0 1 .448 1 1s-.448 1-1 1-1-.448-1-1 .448-1 1-1zm-7.5 0h-1v-14.883l-4.735 5.732-.765-.644 6.021-7.205 5.979 7.195-.764.645-4.736-5.724v14.884z\";\r\n ctrl.iconMap['hide'] = \"M8.137 15.147c-.71-.857-1.146-1.947-1.146-3.147 0-2.76 2.241-5 5-5 1.201 0 2.291.435 3.148 1.145l1.897-1.897c-1.441-.738-3.122-1.248-5.035-1.248-6.115 0-10.025 5.355-10.842 6.584.529.834 2.379 3.527 5.113 5.428l1.865-1.865zm6.294-6.294c-.673-.53-1.515-.853-2.44-.853-2.207 0-4 1.792-4 4 0 .923.324 1.765.854 2.439l5.586-5.586zm7.56-6.146l-19.292 19.293-.708-.707 3.548-3.548c-2.298-1.612-4.234-3.885-5.548-6.169 2.418-4.103 6.943-7.576 12.01-7.576 2.065 0 4.021.566 5.782 1.501l3.501-3.501.707.707zm-2.465 3.879l-.734.734c2.236 1.619 3.628 3.604 4.061 4.274-.739 1.303-4.546 7.406-10.852 7.406-1.425 0-2.749-.368-3.951-.938l-.748.748c1.475.742 3.057 1.19 4.699 1.19 5.274 0 9.758-4.006 11.999-8.436-1.087-1.891-2.63-3.637-4.474-4.978zm-3.535 5.414c0-.554-.113-1.082-.317-1.562l.734-.734c.361.69.583 1.464.583 2.296 0 2.759-2.24 5-5 5-.832 0-1.604-.223-2.295-.583l.734-.735c.48.204 1.007.318 1.561.318 2.208 0 4-1.792 4-4z\";\r\n ctrl.iconMap['download'] = \"M6 16h-5v6h22v-6h-5v-1h6v8h-24v-8h6v1zm14 2c.552 0 1 .448 1 1s-.448 1-1 1-1-.448-1-1 .448-1 1-1zm-7.5-17v14.884l4.736-5.724.764.645-5.979 7.195-6.021-7.205.765-.644 4.735 5.732v-14.883h1z\";\r\n ctrl.iconMap['import'] = \"M16.965 2.381c3.593 1.946 6.035 5.749 6.035 10.119 0 6.347-5.153 11.5-11.5 11.5s-11.5-5.153-11.5-11.5c0-4.37 2.442-8.173 6.035-10.119l.608.809c-3.353 1.755-5.643 5.267-5.643 9.31 0 5.795 4.705 10.5 10.5 10.5s10.5-4.705 10.5-10.5c0-4.043-2.29-7.555-5.643-9.31l.608-.809zm-4.965-2.381v14.826l3.747-4.604.753.666-5 6.112-5-6.101.737-.679 3.763 4.608v-14.828h1z\";\r\n ctrl.iconMap['filter'] = \"M23 0l-9 14.146v7.73l-3.996 2.124v-9.853l-9.004-14.147h22zm-20.249 1l8.253 12.853v8.491l1.996-1.071v-7.419l8.229-12.854h-18.478z\";\r\n ctrl.iconMap['check'] = \"M9 22l-10-10.598 2.798-2.859 7.149 7.473 13.144-14.016 2.909 2.806z\";\r\n ctrl.iconMap['settings'] = \"M12 8.666c-1.838 0-3.333 1.496-3.333 3.334s1.495 3.333 3.333 3.333 3.333-1.495 3.333-3.333-1.495-3.334-3.333-3.334m0 7.667c-2.39 0-4.333-1.943-4.333-4.333s1.943-4.334 4.333-4.334 4.333 1.944 4.333 4.334c0 2.39-1.943 4.333-4.333 4.333m-1.193 6.667h2.386c.379-1.104.668-2.451 2.107-3.05 1.496-.617 2.666.196 3.635.672l1.686-1.688c-.508-1.047-1.266-2.199-.669-3.641.567-1.369 1.739-1.663 3.048-2.099v-2.388c-1.235-.421-2.471-.708-3.047-2.098-.572-1.38.057-2.395.669-3.643l-1.687-1.686c-1.117.547-2.221 1.257-3.642.668-1.374-.571-1.656-1.734-2.1-3.047h-2.386c-.424 1.231-.704 2.468-2.099 3.046-.365.153-.718.226-1.077.226-.843 0-1.539-.392-2.566-.893l-1.687 1.686c.574 1.175 1.251 2.237.669 3.643-.571 1.375-1.734 1.654-3.047 2.098v2.388c1.226.418 2.468.705 3.047 2.098.581 1.403-.075 2.432-.669 3.643l1.687 1.687c1.45-.725 2.355-1.204 3.642-.669 1.378.572 1.655 1.738 2.1 3.047m3.094 1h-3.803c-.681-1.918-.785-2.713-1.773-3.123-1.005-.419-1.731.132-3.466.952l-2.689-2.689c.873-1.837 1.367-2.465.953-3.465-.412-.991-1.192-1.087-3.123-1.773v-3.804c1.906-.678 2.712-.782 3.123-1.773.411-.991-.071-1.613-.953-3.466l2.689-2.688c1.741.828 2.466 1.365 3.465.953.992-.412 1.082-1.185 1.775-3.124h3.802c.682 1.918.788 2.714 1.774 3.123 1.001.416 1.709-.119 3.467-.952l2.687 2.688c-.878 1.847-1.361 2.477-.952 3.465.411.992 1.192 1.087 3.123 1.774v3.805c-1.906.677-2.713.782-3.124 1.773-.403.975.044 1.561.954 3.464l-2.688 2.689c-1.728-.82-2.467-1.37-3.456-.955-.988.41-1.08 1.146-1.785 3.126\";\r\n ctrl.iconMap['clipboard'] = \"M 17 17 L 7 17 L 7 16 L 17 16 L 17 17 Z M 17 14 L 7 14 L 7 13 L 17 13 L 17 14 Z M 17 11 L 7 11 L 7 10 L 17 10 L 17 11 Z M 16 6 L 8 6 L 7 1 L 17 1 L 16 6 Z M 15.7 2 L 8.25 2 L 8.9 5 L 15.1 5 L 15.7 2 Z M 22 23 L 2 23 L 2 3 L 5.5 3 L 5.7 4 L 3 4 L 3 22 L 21 22 L 21 4 L 18.3 4 L 18.5 3 L 22 3 L 22 23 Z\";\r\n ctrl.iconMap['sort-desc'] = \"M11 21.883l-6.235-7.527-.765.644 7.521 9 7.479-9-.764-.645-6.236 7.529v-21.884h-1v21.883z\";\r\n ctrl.iconMap['sort-asc'] = \"M11 2.206l-6.235 7.528-.765-.645 7.521-9 7.479 9-.764.646-6.236-7.53v21.884h-1v-21.883z\";\r\n ctrl.iconMap['key'] = \"M12.451 17.337l-2.451 2.663h-2v2h-2v2h-6v-5l6.865-6.949c1.08 2.424 3.095 4.336 5.586 5.286zm11.549-9.337c0 4.418-3.582 8-8 8s-8-3.582-8-8 3.582-8 8-8 8 3.582 8 8zm-3-3c0-1.104-.896-2-2-2s-2 .896-2 2 .896 2 2 2 2-.896 2-2z\";\r\n ctrl.iconMap['gears'] = \"M17 10.645v-2.29c-1.17-.417-1.907-.533-2.28-1.431-.373-.9.07-1.512.6-2.625l-1.618-1.619c-1.105.525-1.723.974-2.626.6-.9-.373-1.017-1.116-1.431-2.28h-2.29c-.412 1.158-.53 1.907-1.431 2.28h-.001c-.9.374-1.51-.07-2.625-.6l-1.617 1.619c.527 1.11.973 1.724.6 2.625-.375.901-1.123 1.019-2.281 1.431v2.289c1.155.412 1.907.531 2.28 1.431.376.908-.081 1.534-.6 2.625l1.618 1.619c1.107-.525 1.724-.974 2.625-.6h.001c.9.373 1.018 1.118 1.431 2.28h2.289c.412-1.158.53-1.905 1.437-2.282h.001c.894-.372 1.501.071 2.619.602l1.618-1.619c-.525-1.107-.974-1.723-.601-2.625.374-.899 1.126-1.019 2.282-1.43zm-8.5 1.689c-1.564 0-2.833-1.269-2.833-2.834s1.269-2.834 2.833-2.834 2.833 1.269 2.833 2.834-1.269 2.834-2.833 2.834zm15.5 4.205v-1.077c-.55-.196-.897-.251-1.073-.673-.176-.424.033-.711.282-1.236l-.762-.762c-.52.248-.811.458-1.235.283-.424-.175-.479-.525-.674-1.073h-1.076c-.194.545-.25.897-.674 1.073-.424.176-.711-.033-1.235-.283l-.762.762c.248.523.458.812.282 1.236-.176.424-.528.479-1.073.673v1.077c.544.193.897.25 1.073.673.177.427-.038.722-.282 1.236l.762.762c.521-.248.812-.458 1.235-.283.424.175.479.526.674 1.073h1.076c.194-.545.25-.897.676-1.074h.001c.421-.175.706.034 1.232.284l.762-.762c-.247-.521-.458-.812-.282-1.235s.529-.481 1.073-.674zm-4 .794c-.736 0-1.333-.597-1.333-1.333s.597-1.333 1.333-1.333 1.333.597 1.333 1.333-.597 1.333-1.333 1.333zm-4 3.071v-.808c-.412-.147-.673-.188-.805-.505s.024-.533.212-.927l-.572-.571c-.389.186-.607.344-.926.212s-.359-.394-.506-.805h-.807c-.146.409-.188.673-.506.805-.317.132-.533-.024-.926-.212l-.572.571c.187.393.344.609.212.927-.132.318-.396.359-.805.505v.808c.408.145.673.188.805.505.133.32-.028.542-.212.927l.572.571c.39-.186.608-.344.926-.212.318.132.359.395.506.805h.807c.146-.409.188-.673.507-.805h.001c.315-.131.529.025.924.213l.572-.571c-.186-.391-.344-.609-.212-.927s.397-.361.805-.506zm-3 .596c-.552 0-1-.447-1-1s.448-1 1-1 1 .447 1 1-.448 1-1 1z\";\r\n ctrl.iconMap['wand'] = \"M4.908 2.081l-2.828 2.828 19.092 19.091 2.828-2.828-19.092-19.091zm2.121 6.363l-3.535-3.535 1.414-1.414 3.535 3.535-1.414 1.414zm1.731-5.845c1.232.376 2.197 1.341 2.572 2.573.377-1.232 1.342-2.197 2.573-2.573-1.231-.376-2.196-1.34-2.573-2.573-.375 1.232-1.34 2.197-2.572 2.573zm-5.348 6.954c-.498 1.635-1.777 2.914-3.412 3.413 1.635.499 2.914 1.777 3.412 3.411.499-1.634 1.778-2.913 3.412-3.411-1.634-.5-2.913-1.778-3.412-3.413zm9.553-3.165c.872.266 1.553.948 1.819 1.82.266-.872.948-1.554 1.819-1.82-.871-.266-1.553-.948-1.819-1.82-.266.871-.948 1.554-1.819 1.82zm4.426-6.388c-.303.994-1.082 1.772-2.075 2.076.995.304 1.772 1.082 2.077 2.077.303-.994 1.082-1.772 2.074-2.077-.992-.303-1.772-1.082-2.076-2.076z\";\r\n ctrl.iconMap['user'] = \"M19 7.001c0 3.865-3.134 7-7 7s-7-3.135-7-7c0-3.867 3.134-7.001 7-7.001s7 3.134 7 7.001zm-1.598 7.18c-1.506 1.137-3.374 1.82-5.402 1.82-2.03 0-3.899-.685-5.407-1.822-4.072 1.793-6.593 7.376-6.593 9.821h24c0-2.423-2.6-8.006-6.598-9.819z\"; \r\n ctrl.iconMap['version'] = \"M 14.9 1 L 12.293 1.005 L 16.507 7.18 L 16.5 23.1 L 18.5 23.1 L 18.5 6.4 L 14.9 1 Z M 10.4 1 L 1.581 1.004 L 1.584 23.13 L 15 23.1 L 15 7.7 L 10.4 1 Z M 16.8 1 L 20 5.8 L 20 23.1 L 22 23.1 L 22 4.9 L 19.3 1 L 16.8 1 Z\";\r\n ctrl.iconMap['group'] = \"M21.698 10.658l2.302 1.342-12.002 7-11.998-7 2.301-1.342 9.697 5.658 9.7-5.658zm-9.7 10.657l-9.697-5.658-2.301 1.343 11.998 7 12.002-7-2.302-1.342-9.7 5.657zm12.002-14.315l-12.002-7-11.998 7 11.998 7 12.002-7z\";\r\n ctrl.iconMap['artifact'] = \"M12,0 L2,12 L12,24 L22,12 L12,0z\";\r\n \r\n ctrl.$onInit = function() {\r\n ctrl.path = ctrl.iconMap[ctrl.shape];\r\n }\r\n}\r\n\r\n\r\nmodule.exports = DatabusIconController;\n\n//# sourceURL=webpack://databus-webapp/./js/components/databus-icon/databus-icon.js?"); + +/***/ }), + +/***/ "./js/components/entity-api-view/entity-api-view.js": +/*!**********************************************************!*\ + !*** ./js/components/entity-api-view/entity-api-view.js ***! + \**********************************************************/ +/***/ ((module) => { + +eval("function EntityApiViewController() {\r\n const ctrl = this;\r\n\r\n ctrl.copyToClipboard = function (text) {\r\n navigator.clipboard.writeText(text).then(() => {\r\n console.log(\"Copied to clipboard\");\r\n });\r\n };\r\n\r\n ctrl.register = async function () {\r\n ctrl.isRegistering = true;\r\n ctrl.isSuccess = false;\r\n ctrl.isError = false;\r\n \r\n if (ctrl.entity && ctrl.entity.register) {\r\n try {\r\n let response = await ctrl.entity.register();\r\n ctrl.log = response.data.log;\r\n ctrl.isSuccess = true;\r\n } catch(err) {\r\n ctrl.log = err.data.log;\r\n ctrl.isError = true;\r\n }\r\n }\r\n\r\n ctrl.isRegistering = false;\r\n };\r\n\r\n ctrl.setApiKeyName = function (name) {\r\n if (ctrl.entity && ctrl.entity.setApiKeyName) {\r\n ctrl.entity.setApiKeyName(name);\r\n }\r\n };\r\n}\r\n\r\nmodule.exports = EntityApiViewController;\r\n\n\n//# sourceURL=webpack://databus-webapp/./js/components/entity-api-view/entity-api-view.js?"); /***/ }), @@ -189,6 +199,26 @@ eval("const DatabusUtils = __webpack_require__(/*! ../../utils/databus-utils */ /***/ }), +/***/ "./js/components/entity-dropdown/entity-dropdown.js": +/*!**********************************************************!*\ + !*** ./js/components/entity-dropdown/entity-dropdown.js ***! + \**********************************************************/ +/***/ ((module) => { + +eval("function EntityDropdownController() {\r\n var ctrl = this;\r\n\r\n ctrl.showDrop = false;\r\n ctrl.selectedLabel = '';\r\n ctrl.searchQuery = '';\r\n ctrl.filteredItems = [];\r\n\r\n ctrl.$onInit = function () {\r\n ctrl.updateFilteredItems();\r\n ctrl.setSelectedLabel();\r\n };\r\n\r\n ctrl.$onChanges = function (changes) {\r\n if (changes.items || changes.selected) {\r\n ctrl.updateFilteredItems();\r\n\r\n if (ctrl.selected && Array.isArray(ctrl.items) && !ctrl.items?.some(i => i[ctrl.displayProperty] === ctrl.selected)) {\r\n ctrl.selected = null;\r\n ctrl.onSelect({ item: null }); \r\n }\r\n\r\n ctrl.setSelectedLabel();\r\n }\r\n };\r\n\r\n ctrl.toggleDropdown = function () {\r\n if (!ctrl.loading && ctrl.items && ctrl.items.length > 0) {\r\n ctrl.showDrop = !ctrl.showDrop;\r\n ctrl.searchQuery = '';\r\n ctrl.updateFilteredItems();\r\n }\r\n };\r\n\r\n ctrl.selectItem = function (item) {\r\n ctrl.selectedLabel = item[ctrl.displayProperty];\r\n ctrl.showDrop = false;\r\n ctrl.onSelect({ item: item });\r\n };\r\n\r\n ctrl.updateFilteredItems = function () {\r\n if (!ctrl.items || !ctrl.displayProperty) {\r\n ctrl.filteredItems = [];\r\n return;\r\n }\r\n\r\n ctrl.filteredItems = ctrl.items.filter(function (item) {\r\n var val = item[ctrl.displayProperty] || '';\r\n return val.toLowerCase().indexOf(ctrl.searchQuery.toLowerCase()) !== -1;\r\n });\r\n };\r\n\r\n ctrl.setSelectedLabel = function () {\r\n if (!ctrl.selected || !ctrl.displayProperty) {\r\n ctrl.selectedLabel = ctrl.placeholder || 'Please select...';\r\n return;\r\n }\r\n\r\n // Attempt to match selected value in the list\r\n var match = (ctrl.items || []).find(function (item) {\r\n return item[ctrl.displayProperty] === ctrl.selected;\r\n });\r\n\r\n if (match) {\r\n ctrl.selectedLabel = match[ctrl.displayProperty];\r\n } else {\r\n // fallback in case selected value is not in the list\r\n ctrl.selectedLabel = ctrl.placeholder || 'Please select...';\r\n // ctrl.onSelect(null);\r\n }\r\n };\r\n}\r\n\r\nmodule.exports = EntityDropdownController;\r\n\n\n//# sourceURL=webpack://databus-webapp/./js/components/entity-dropdown/entity-dropdown.js?"); + +/***/ }), + +/***/ "./js/components/error-notification/error-notifcation.js": +/*!***************************************************************!*\ + !*** ./js/components/error-notification/error-notifcation.js ***! + \***************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +eval("const { DatabusMsg } = __webpack_require__(/*! ../../utils/messages */ \"./js/utils/messages.js\");\r\n\r\nfunction ErrorNotificationController() {\r\n var ctrl = this;\r\n ctrl.expanded = false;\r\n\r\n ctrl.toggleExpand = function () {\r\n ctrl.expanded = !ctrl.expanded;\r\n };\r\n\r\n ctrl.get = function(key) {\r\n return DatabusMsg.get(key);\r\n }\r\n}\r\n\r\nmodule.exports = ErrorNotificationController;\r\n\n\n//# sourceURL=webpack://databus-webapp/./js/components/error-notification/error-notifcation.js?"); + +/***/ }), + /***/ "./js/components/expandable-arrow/expandable-arrow.js": /*!************************************************************!*\ !*** ./js/components/expandable-arrow/expandable-arrow.js ***! @@ -205,7 +235,7 @@ eval("// hinzufügen eines Controllers zum Modul\nfunction ExpandableArrowContro \**************************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("const QueryNode = __webpack_require__(/*! ../../query-builder/query-node */ \"./js/query-builder/query-node.js\");\nconst DatabusConstants = __webpack_require__(/*! ../../utils/databus-constants */ \"./js/utils/databus-constants.js\");\nconst DatabusUris = __webpack_require__(/*! ../../utils/databus-uris */ \"./js/utils/databus-uris.js\");\nconst DatabusUtils = __webpack_require__(/*! ../../utils/databus-utils */ \"./js/utils/databus-utils.js\");\n\n/**\n * Manages an array of facets with respect to a parent facets array.\n * Provides some convenient\\ce methods to write to the facets array and\n * read from the parents facets.\n * DO NOT change the parent facets array in here.\n */\nclass FacetSettings {\n\n /**\n * Locally manages a facets array with respect to a parent\n * facets array\n * @param {[type]} facets [description]\n * @param {[type]} parentFacets [description]\n */\n constructor(facets, parentFacets) {\n this.facets = facets;\n this.parentFacets = parentFacets;\n }\n\n /**\n * Change a setting (key, value) to a state (bool)\n * @param {[type]} key [description]\n * @param {[type]} value [description]\n * @param {[type]} setState [description]\n * @return {[type]} [description]\n */\n changeSetting(key, value, targetState) {\n var parentState = this.findParentSettingState(key, value);\n\n if (parentState != targetState) {\n this.createOrAddSetting(key, value, targetState);\n } else {\n this.removeSetting(key, value);\n }\n\n return targetState;\n }\n\n /**\n * Find the checked state specified in the parent setting array (if set)\n * based on a key and value\n * @param {[type]} key [description]\n * @param {[type]} value [description]\n * @return {[type]} [description]\n */\n findParentSettingState(key, value) {\n if (this.parentFacets == undefined) {\n return false;\n }\n\n for (var p in this.parentFacets) {\n var setting = this.parentFacets[p];\n if (setting.key == key && setting.value == value) {\n return setting.checked;\n }\n }\n\n return false;\n }\n\n findOwnSettingState(key, value) {\n for (var p in this.facets) {\n var setting = this.facets[p];\n if (setting.key == key && setting.value == value) {\n return setting.checked;\n }\n }\n\n return false;\n }\n\n isOverride(key, value, state) {\n var parentState = this.findParentSettingState(key, value);\n return parentState != state;\n }\n\n createOrAddSetting(key, value, state) {\n for (var p in this.facets) {\n var setting = this.facets[p];\n if (p == key && setting.value == value) {\n setting.checked = state;\n return;\n }\n }\n\n this.facets[key] = { value: value, checked: state };\n }\n\n removeSetting(key, value) {\n for (var p in this.facets) {\n var setting = this.facets[p];\n if (setting.key == key && setting.value == value) {\n this.facets.splice(p, 1);\n return;\n }\n }\n }\n\n}\n\nfunction FacetsViewController($http, $scope) {\n\n var ctrl = this;\n ctrl.$http = $http;\n ctrl.maxEntries = 6;\n\n ctrl.$onInit = function () {\n\n }\n\n ctrl.$onChanges = function () {\n // create the queries...\n ctrl.isLoading = true;\n\n // wrap the node in the query node class\n ctrl.node = QueryNode.createFrom(ctrl.node);\n ctrl.viewModel = {};\n\n if (ctrl.facets == undefined) {\n ctrl.facets = [];\n }\n\n var queryUri = ctrl.resourceType == 'version' ?\n ctrl.node.uri + '/' + ctrl.node.facetSettings[DatabusUris.DCT_HAS_VERSION][0].value\n : ctrl.node.uri;\n\n // Load the available resource facets\n // TODO: Remove resource type, can be derived from uri\n ctrl.$http.get('/app/utils/facets', {\n params: { uri: queryUri, type: ctrl.resourceType }\n }).then(function (result) {\n\n // Facets data has been loaded\n // Fix artifact facet values for groups\n if (ctrl.resourceType == 'group' && result.data[DatabusUris.DATABUS_ARTIFACT_PROPERTY] != undefined) {\n for (var i in result.data[DatabusUris.DATABUS_ARTIFACT_PROPERTY].values) {\n var value = result.data[DatabusUris.DATABUS_ARTIFACT_PROPERTY].values[i];\n result.data[DatabusUris.DATABUS_ARTIFACT_PROPERTY].values[i]\n = DatabusUtils.uriToName(value);\n }\n }\n\n // Facet setting in this view is\n\n // - SETTING\n // ---- VALUE\n // ---- IS_CHECKED\n\n // Prepare visible facet settings and autofill data based on the facet data returned by the API\n // Create key base entries (unset, not overriden)\n for (var key in result.data) {\n\n var facetData = result.data[key];\n\n // Create a view data object for each facet\n ctrl.viewModel[key] = {};\n ctrl.viewModel[key].key = key;\n ctrl.viewModel[key].label = facetData.label;\n ctrl.viewModel[key].visibleFacetSettings = [];\n ctrl.viewModel[key].autofill = {};\n ctrl.viewModel[key].autofill.values = facetData.values;\n ctrl.viewModel[key].autofill.selectedValues = [];\n ctrl.viewModel[key].autofill.input = '';\n\n\n for (var v in facetData.values) {\n\n var value = facetData.values[v];\n\n ctrl.viewModel[key].visibleFacetSettings.push({\n value: value,\n checked: false,\n isOverride: false\n });\n }\n\n ctrl.viewModel[key].visibleFacetSettings.sort(function(a, b) {\n const valueA = a.value.toUpperCase();\n const valueB = b.value.toUpperCase();\n if (valueA > valueB) {\n return 1;\n }\n if (valueA < valueB) {\n return -1;\n }\n\n return 0;\n });\n\n // Show latest versions first\n if(key == DatabusUris.DCT_HAS_VERSION) {\n ctrl.viewModel[key].visibleFacetSettings.reverse(); \n }\n\n // Only show the top few\n var length = ctrl.viewModel[key].visibleFacetSettings.length;\n ctrl.viewModel[key].visibleFacetSettings.length = Math.min(ctrl.maxEntries, length);\n }\n\n // If we show the browser for a version, remove the version facet\n if (ctrl.resourceType == 'version') {\n delete ctrl.viewModel[DatabusUris.DCT_HAS_VERSION];\n }\n\n // Add the \"Latest Version\" facet to the visible settings of the version facet\n if (ctrl.resourceType != 'version' && ctrl.viewModel[DatabusUris.DCT_HAS_VERSION] != undefined) {\n ctrl.viewModel[DatabusUris.DCT_HAS_VERSION].visibleFacetSettings.unshift({\n value: DatabusConstants.FACET_LATEST_VERSION_VALUE,\n checked: false,\n isOverride: false\n });\n\n // Apply the existing settings to the view model\n var fullFacets = ctrl.node.createFullFacetSettings();\n\n for (var key in fullFacets) {\n var facetSettingList = fullFacets[key];\n\n for (var i in facetSettingList) {\n var facetSetting = facetSettingList[i];\n\n var visibleFacetSetting = ctrl.getOrCreateVisibleFacetSetting(key, facetSetting.value);\n\n if (visibleFacetSetting != null) {\n visibleFacetSetting.checked = facetSetting.checked;\n visibleFacetSetting.isOverride = ctrl.node.isOverride(key, facetSetting.value, facetSetting.checked);\n }\n }\n }\n\n // If we're a group node, check for artifact nodes and add them as facets\n if (ctrl.resourceType == 'group') {\n for (var i in ctrl.node.childNodes) {\n var artifactNode = ctrl.node.childNodes[i];\n var facetValue = DatabusUtils.uriToName(artifactNode.uri)\n var visibleFacetSetting =\n ctrl.getOrCreateVisibleFacetSetting(DatabusUris.DATABUS_ARTIFACT_PROPERTY, facetValue);\n visibleFacetSetting.checked = true;\n visibleFacetSetting.isOverride = true;\n }\n\n if (ctrl.node.childNodes.length == 0) {\n\n // Add artifact nodes per default\n for (var v of ctrl.viewModel[DatabusUris.DATABUS_ARTIFACT_PROPERTY].visibleFacetSettings) {\n var childUri = ctrl.node.uri + '/' + v.value;\n var artifactNode = new QueryNode(childUri, 'databus:artifact');\n QueryNode.addChild(ctrl.node, artifactNode);\n }\n }\n }\n\n ctrl.onChange();\n ctrl.onLoaded();\n }\n\n ctrl.isLoading = false;\n });\n }\n\n ctrl.getFacetLabel = function (value) {\n if (value == DatabusConstants.FACET_LATEST_VERSION_VALUE) {\n return DatabusConstants.FACET_LATEST_VERSION_LABEL;\n }\n\n return value;\n }\n /**\n * Changes the value of a key value (also applies to facets)\n * @param {[type]} key [description]\n * @param {[type]} value [description]\n * @param {[type]} state [description]\n * @return {[type]} [description]\n */\n ctrl.changeFacetValueState = function (key, value, targetState) {\n\n if (ctrl.resourceType == 'group' && key == DatabusUris.DATABUS_ARTIFACT_PROPERTY) {\n\n var childUri = ctrl.node.uri + '/' + value;\n\n if (targetState) {\n var artifactNode = new QueryNode(childUri, 'databus:artifact');\n QueryNode.addChild(ctrl.node, artifactNode);\n } else {\n QueryNode.removeChildByUri(ctrl.node, childUri);\n }\n\n var visibleSetting = ctrl.getOrCreateVisibleFacetSetting(key, value);\n\n if (visibleSetting != null) {\n visibleSetting.checked = targetState;\n visibleSetting.isOverride = targetState;\n }\n }\n else {\n // apply change to view model\n ctrl.node.setFacet(key, value, targetState);\n\n var visibleSetting = ctrl.getOrCreateVisibleFacetSetting(key, value);\n\n if (visibleSetting != null) {\n visibleSetting.checked = targetState;\n visibleSetting.isOverride = ctrl.node.isOverride(key, value, targetState);\n }\n }\n\n if (ctrl.viewModel[key].autofill.selectedValues.length > 0) {\n ctrl.complete(ctrl.viewModel[key]);\n }\n\n ctrl.onChange();\n }\n\n /**\n * Gets or creates a new entry for a key value\n * for a given key and value\n * @param {[type]} key [description]\n * @param {[type]} value [description]\n * @return {[type]} [description]\n */\n ctrl.getOrCreateVisibleFacetSetting = function (key, value) {\n\n if (ctrl.viewModel[key] == undefined) {\n // This is a facet that the node does not have, but a parent has\n\n var label = DatabusUtils.uriToName(key);\n label = label[0].toUpperCase() + label.slice(1);\n\n ctrl.viewModel[key] = {};\n ctrl.viewModel[key].key = key;\n ctrl.viewModel[key].label = label;\n ctrl.viewModel[key].visibleFacetSettings = [];\n ctrl.viewModel[key].autofill = {};\n ctrl.viewModel[key].autofill.values = [];\n ctrl.viewModel[key].autofill.selectedValues = [];\n ctrl.viewModel[key].autofill.input = '';\n }\n\n for (var i in ctrl.viewModel[key].visibleFacetSettings) {\n var facetSetting = ctrl.viewModel[key].visibleFacetSettings[i];\n if (facetSetting.value == value) {\n return facetSetting; // ctrl.facetSettings[key];\n }\n }\n\n var visibleSetting = {\n value: value,\n };\n\n ctrl.viewModel[key].visibleFacetSettings.push(visibleSetting);\n return visibleSetting;\n }\n\n // Get all active facets of a certain key\n ctrl.getActiveFilters = function (key) {\n var activeFilters = [];\n\n for (var f in ctrl.facets[key].items) {\n var filter = ctrl.facets[key].items[f];\n if (filter.checked) {\n activeFilters.push(filter);\n }\n }\n\n return activeFilters;\n }\n\n // Checks whether any filter for a key is set\n ctrl.hasActiveFilters = function (key) {\n for (var f in ctrl.facets[key].items) {\n var filter = ctrl.facets[key].items[f];\n if (filter.checked) {\n return true;\n }\n }\n\n return false;\n }\n\n ctrl.complete = function (facetData) {\n facetData.autofill.selectedValues.length = 0;\n for (var a in facetData.autofill.values) {\n var e = facetData.autofill.values[a];\n if (e.toLowerCase().indexOf(facetData.autofill.input.toLowerCase()) >= 0) {\n\n var include = true;\n\n for (var v in facetData.visibleFacetSettings) {\n var visibleSettings = facetData.visibleFacetSettings[v];\n if (visibleSettings.value == e.toLowerCase()) {\n include = false;\n }\n }\n\n if (include) {\n facetData.autofill.selectedValues.push(e);\n }\n }\n }\n }\n\n // Clears the autofill lists\n ctrl.clearAutofill = function () {\n var self = ctrl;\n for (var f in self.viewModel) {\n var data = self.viewModel[f];\n data.autofill.selectedValues.length = 0;\n }\n }\n}\n\nmodule.exports = FacetsViewController;\n\n\n//# sourceURL=webpack://databus-webapp/./js/components/facets-view/facets-view.js?"); +eval("const QueryNode = __webpack_require__(/*! ../../query-builder/query-node */ \"./js/query-builder/query-node.js\");\nconst DatabusConstants = __webpack_require__(/*! ../../utils/databus-constants */ \"./js/utils/databus-constants.js\");\nconst DatabusUris = __webpack_require__(/*! ../../utils/databus-uris */ \"./js/utils/databus-uris.js\");\nconst DatabusUtils = __webpack_require__(/*! ../../utils/databus-utils */ \"./js/utils/databus-utils.js\");\n\n/**\n * Manages an array of facets with respect to a parent facets array.\n * Provides some convenient\\ce methods to write to the facets array and\n * read from the parents facets.\n * DO NOT change the parent facets array in here.\n */\nclass FacetSettings {\n\n /**\n * Locally manages a facets array with respect to a parent\n * facets array\n * @param {[type]} facets [description]\n * @param {[type]} parentFacets [description]\n */\n constructor(facets, parentFacets) {\n this.facets = facets;\n this.parentFacets = parentFacets;\n }\n\n /**\n * Change a setting (key, value) to a state (bool)\n * @param {[type]} key [description]\n * @param {[type]} value [description]\n * @param {[type]} setState [description]\n * @return {[type]} [description]\n */\n changeSetting(key, value, targetState) {\n var parentState = this.findParentSettingState(key, value);\n\n if (parentState != targetState) {\n this.createOrAddSetting(key, value, targetState);\n } else {\n this.removeSetting(key, value);\n }\n\n return targetState;\n }\n\n /**\n * Find the checked state specified in the parent setting array (if set)\n * based on a key and value\n * @param {[type]} key [description]\n * @param {[type]} value [description]\n * @return {[type]} [description]\n */\n findParentSettingState(key, value) {\n if (this.parentFacets == undefined) {\n return false;\n }\n\n for (var p in this.parentFacets) {\n var setting = this.parentFacets[p];\n if (setting.key == key && setting.value == value) {\n return setting.checked;\n }\n }\n\n return false;\n }\n\n findOwnSettingState(key, value) {\n for (var p in this.facets) {\n var setting = this.facets[p];\n if (setting.key == key && setting.value == value) {\n return setting.checked;\n }\n }\n\n return false;\n }\n\n isOverride(key, value, state) {\n var parentState = this.findParentSettingState(key, value);\n return parentState != state;\n }\n\n createOrAddSetting(key, value, state) {\n for (var p in this.facets) {\n var setting = this.facets[p];\n if (p == key && setting.value == value) {\n setting.checked = state;\n return;\n }\n }\n\n this.facets[key] = { value: value, checked: state };\n }\n\n removeSetting(key, value) {\n for (var p in this.facets) {\n var setting = this.facets[p];\n if (setting.key == key && setting.value == value) {\n this.facets.splice(p, 1);\n return;\n }\n }\n }\n\n}\n\nfunction FacetsViewController($http, $scope) {\n\n var ctrl = this;\n ctrl.$http = $http;\n ctrl.maxEntries = 6;\n\n ctrl.$onInit = function () {\n\n }\n\n ctrl.$onChanges = function () {\n // create the queries...\n ctrl.isLoading = true;\n\n // wrap the node in the query node class\n ctrl.node = QueryNode.createFrom(ctrl.node);\n\n // Holds the view state as json\n ctrl.viewModel = {};\n\n if (ctrl.facets == undefined) {\n ctrl.facets = [];\n }\n\n var queryUri = ctrl.resourceType == 'version' ?\n ctrl.node.uri + '/' + ctrl.node.facetSettings[DatabusUris.DCT_HAS_VERSION][0].value\n : ctrl.node.uri;\n\n // Load the available resource facets\n // TODO: Remove resource type, can be derived from uri\n ctrl.$http.get('/app/utils/facets', {\n params: { uri: queryUri, type: ctrl.resourceType }\n }).then(function (result) {\n\n // Facets data has been loaded\n ctrl.facetsData = result.data;\n\n // Fix artifact facet values for groups, change URIs into artifact names\n var artifactFacetData = ctrl.facetsData[DatabusUris.DATABUS_ARTIFACT_PROPERTY];\n\n if (artifactFacetData != null) {\n for (var i in artifactFacetData.values) {\n artifactFacetData.values[i] = DatabusUtils.uriToName(artifactFacetData.values[i]);\n }\n }\n\n // Facet setting in this view is\n\n // - SETTING\n // ---- VALUE\n // ---- IS_CHECKED\n\n // Prepare visible facet settings and autofill data based on the facet data returned by the API\n // Create key base entries (unset, not overriden)\n for (var key in ctrl.facetsData) {\n\n var facetData = ctrl.facetsData[key];\n\n // Create a view data object for each facet\n ctrl.viewModel[key] = {};\n ctrl.viewModel[key].key = key;\n ctrl.viewModel[key].label = facetData.label;\n ctrl.viewModel[key].visibleFacetSettings = [];\n ctrl.viewModel[key].autofill = {};\n ctrl.viewModel[key].autofill.values = facetData.values;\n ctrl.viewModel[key].autofill.selectedValues = [];\n ctrl.viewModel[key].autofill.input = '';\n\n\n for (var v in facetData.values) {\n var value = facetData.values[v];\n ctrl.viewModel[key].visibleFacetSettings.push({\n value: value,\n checked: false,\n isOverride: false\n });\n }\n\n ctrl.viewModel[key].visibleFacetSettings.sort(function (a, b) {\n const valueA = a.value.toUpperCase();\n const valueB = b.value.toUpperCase();\n if (valueA > valueB) {\n return 1;\n }\n if (valueA < valueB) {\n return -1;\n }\n\n return 0;\n });\n\n // Show latest versions first\n if (key == DatabusUris.DCT_HAS_VERSION) {\n ctrl.viewModel[key].visibleFacetSettings.reverse();\n }\n\n // Only show the top few\n var length = ctrl.viewModel[key].visibleFacetSettings.length;\n ctrl.viewModel[key].visibleFacetSettings.length = Math.min(ctrl.maxEntries, length);\n }\n\n // If we show the browser for a version, remove the version facet\n if (ctrl.resourceType == 'version') {\n delete ctrl.viewModel[DatabusUris.DCT_HAS_VERSION];\n }\n\n // Add the \"Latest Version\" facet to the visible settings of the version facet\n if (ctrl.resourceType != 'version' && ctrl.viewModel[DatabusUris.DCT_HAS_VERSION] != undefined) {\n ctrl.viewModel[DatabusUris.DCT_HAS_VERSION].visibleFacetSettings.unshift({\n value: DatabusConstants.FACET_LATEST_VERSION_VALUE,\n checked: false,\n isOverride: false\n });\n\n // Apply the existing settings to the view model\n var fullFacets = ctrl.node.createFullFacetSettings();\n\n for (var key in fullFacets) {\n var facetSettingList = fullFacets[key];\n\n for (var i in facetSettingList) {\n var facetSetting = facetSettingList[i];\n\n var visibleFacetSetting = ctrl.getOrCreateVisibleFacetSetting(key, facetSetting.value);\n\n if (visibleFacetSetting != null) {\n visibleFacetSetting.checked = facetSetting.checked;\n visibleFacetSetting.isOverride = ctrl.node.isOverride(key, facetSetting.value, facetSetting.checked);\n }\n }\n }\n\n // If we're a group node, check for artifact nodes and add them as facets\n if (ctrl.resourceType == 'group') {\n\n for (var i in ctrl.node.childNodes) {\n var artifactNode = ctrl.node.childNodes[i];\n var facetValue = DatabusUtils.uriToName(artifactNode.uri)\n var visibleFacetSetting =\n ctrl.getOrCreateVisibleFacetSetting(DatabusUris.DATABUS_ARTIFACT_PROPERTY, facetValue);\n visibleFacetSetting.checked = true;\n visibleFacetSetting.isOverride = true;\n }\n\n if (ctrl.node.childNodes.length == 0) {\n\n\n ctrl.updateArtifactFilters(ctrl.node);\n\n var artifactFacetData = ctrl.facetsData[DatabusUris.DATABUS_ARTIFACT_PROPERTY];\n\n if (artifactFacetData != null) {\n\n // Add artifact nodes \n for (var i in artifactFacetData.values) {\n artifactFacetData.values[i] = DatabusUtils.uriToName(artifactFacetData.values[i]);\n }\n }\n\n /*\n // Add artifact nodes per default\n for (var v of ctrl.viewModel[DatabusUris.DATABUS_ARTIFACT_PROPERTY].visibleFacetSettings) {\n var childUri = ctrl.node.uri + '/' + v.value;\n var artifactNode = new QueryNode(childUri, 'databus:artifact');\n QueryNode.addChild(ctrl.node, artifactNode);\n }*/\n\n\n }\n }\n\n ctrl.onChange();\n ctrl.onLoaded();\n }\n\n ctrl.isLoading = false;\n });\n }\n\n ctrl.updateArtifactFilters = function (groupNode) {\n\n // Clear all child nodes\n groupNode.childNodes.length = 0;\n\n var hasCheckedArtifactFacets = false;\n\n for (var setting of ctrl.viewModel[DatabusUris.DATABUS_ARTIFACT_PROPERTY].visibleFacetSettings) {\n hasCheckedArtifactFacets = hasCheckedArtifactFacets || setting.checked;\n }\n\n if (hasCheckedArtifactFacets) {\n\n for (var setting of ctrl.viewModel[DatabusUris.DATABUS_ARTIFACT_PROPERTY].visibleFacetSettings) {\n if (setting.checked) {\n var artifactUri = `${groupNode.uri}/${setting.value}`;\n if (QueryNode.findChildByUri(groupNode, artifactUri) == null) {\n var artifactNode = new QueryNode(artifactUri, 'databus:artifact');\n QueryNode.addChild(groupNode, artifactNode);\n }\n }\n }\n\n } else {\n\n var latestVersionSetting = QueryNode.findFacetSetting(groupNode,\n DatabusUris.DCT_HAS_VERSION,\n DatabusConstants.FACET_LATEST_VERSION_VALUE);\n\n if (latestVersionSetting != undefined && latestVersionSetting.checked) {\n\n var artifactFacetData = ctrl.facetsData[DatabusUris.DATABUS_ARTIFACT_PROPERTY];\n\n if (artifactFacetData != null) {\n\n // Add artifact nodes \n for (var value of artifactFacetData.values) {\n var artifactUri = `${groupNode.uri}/${value}`;\n if (QueryNode.findChildByUri(groupNode, artifactUri) == null) {\n var artifactNode = new QueryNode(artifactUri, 'databus:artifact');\n QueryNode.addChild(groupNode, artifactNode);\n }\n }\n\n }\n }\n }\n\n }\n\n\n ctrl.getFacetLabel = function (value) {\n if (value == DatabusConstants.FACET_LATEST_VERSION_VALUE) {\n return DatabusConstants.FACET_LATEST_VERSION_LABEL;\n }\n\n return value;\n }\n /**\n * Changes the value of a key value (also applies to facets)\n * @param {[type]} key [description]\n * @param {[type]} value [description]\n * @param {[type]} state [description]\n * @return {[type]} [description]\n */\n ctrl.changeFacetValueState = function (key, value, targetState) {\n\n if (ctrl.resourceType == 'group' && key == DatabusUris.DATABUS_ARTIFACT_PROPERTY) {\n\n var visibleSetting = ctrl.getOrCreateVisibleFacetSetting(key, value);\n\n if (visibleSetting != null) {\n visibleSetting.checked = targetState;\n visibleSetting.isOverride = targetState;\n }\n\n ctrl.updateArtifactFilters(ctrl.node);\n\n }\n else {\n // apply change to view model\n ctrl.node.setFacet(key, value, targetState);\n\n var visibleSetting = ctrl.getOrCreateVisibleFacetSetting(key, value);\n\n if (visibleSetting != null) {\n visibleSetting.checked = targetState;\n visibleSetting.isOverride = ctrl.node.isOverride(key, value, targetState);\n }\n }\n\n if (ctrl.viewModel[key].autofill.selectedValues.length > 0) {\n ctrl.complete(ctrl.viewModel[key]);\n }\n\n ctrl.onChange();\n }\n\n /**\n * Gets or creates a new entry for a key value\n * for a given key and value\n * @param {[type]} key [description]\n * @param {[type]} value [description]\n * @return {[type]} [description]\n */\n ctrl.getOrCreateVisibleFacetSetting = function (key, value) {\n\n if (ctrl.viewModel[key] == undefined) {\n // This is a facet that the node does not have, but a parent has\n\n var label = DatabusUtils.uriToName(key);\n label = label[0].toUpperCase() + label.slice(1);\n\n ctrl.viewModel[key] = {};\n ctrl.viewModel[key].key = key;\n ctrl.viewModel[key].label = label;\n ctrl.viewModel[key].visibleFacetSettings = [];\n ctrl.viewModel[key].autofill = {};\n ctrl.viewModel[key].autofill.values = [];\n ctrl.viewModel[key].autofill.selectedValues = [];\n ctrl.viewModel[key].autofill.input = '';\n }\n\n for (var i in ctrl.viewModel[key].visibleFacetSettings) {\n var facetSetting = ctrl.viewModel[key].visibleFacetSettings[i];\n if (facetSetting.value == value) {\n return facetSetting; // ctrl.facetSettings[key];\n }\n }\n\n var visibleSetting = {\n value: value,\n };\n\n ctrl.viewModel[key].visibleFacetSettings.push(visibleSetting);\n return visibleSetting;\n }\n\n // Get all active facets of a certain key\n ctrl.getActiveFilters = function (key) {\n var activeFilters = [];\n\n for (var f in ctrl.facets[key].items) {\n var filter = ctrl.facets[key].items[f];\n if (filter.checked) {\n activeFilters.push(filter);\n }\n }\n\n return activeFilters;\n }\n\n // Checks whether any filter for a key is set\n ctrl.hasActiveFilters = function (key) {\n for (var f in ctrl.facets[key].items) {\n var filter = ctrl.facets[key].items[f];\n if (filter.checked) {\n return true;\n }\n }\n\n return false;\n }\n\n ctrl.complete = function (facetData) {\n facetData.autofill.selectedValues.length = 0;\n for (var a in facetData.autofill.values) {\n var e = facetData.autofill.values[a];\n if (e.toLowerCase().indexOf(facetData.autofill.input.toLowerCase()) >= 0) {\n\n var include = true;\n\n for (var v in facetData.visibleFacetSettings) {\n var visibleSettings = facetData.visibleFacetSettings[v];\n if (visibleSettings.value == e.toLowerCase()) {\n include = false;\n }\n }\n\n if (include) {\n facetData.autofill.selectedValues.push(e);\n }\n }\n }\n }\n\n // Clears the autofill lists\n ctrl.clearAutofill = function () {\n var self = ctrl;\n for (var f in self.viewModel) {\n var data = self.viewModel[f];\n data.autofill.selectedValues.length = 0;\n }\n }\n}\n\nmodule.exports = FacetsViewController;\n\n\n//# sourceURL=webpack://databus-webapp/./js/components/facets-view/facets-view.js?"); /***/ }), @@ -229,6 +259,16 @@ eval("// hinzufügen eines Controllers zum Modul\nfunction MultiselectDropdownCo /***/ }), +/***/ "./js/components/nav-search/nav-search-controller.js": +/*!***********************************************************!*\ + !*** ./js/components/nav-search/nav-search-controller.js ***! + \***********************************************************/ +/***/ ((module) => { + +eval("\n\n// hinzufügen eines Controllers zum Modul\nfunction NavSearchController($http, $interval, $sce, searchManager) {\n\n var ctrl = this;\n\n // TODO: get search extensions from the logged in user\n\n ctrl.searchManager = searchManager;\n ctrl.results = [];\n\n ctrl.formatResult = function (result) {\n return $sce.trustAsHtml(result);\n }\n\n ctrl.toggleFilter = function (key) {\n ctrl.filterActive[key] = !ctrl.filterActive[key];\n ctrl.search();\n }\n\n ctrl.navigateTo = function(uri) {\n window.location = uri;\n }\n\n ctrl.hideDropdown = function() {\n\n }\n\n ctrl.availableResourceTypes = ['Collection', 'Artifact', 'Group', 'Account', 'Version' ];\n\n ctrl.$onInit = function () {\n\n ctrl.searchInput = '';\n ctrl.isSearching = false;\n ctrl.searchCooldown = 1000;\n\n\n ctrl.filterActive = {};\n ctrl.filterVisible = {};\n\n\n if (ctrl.settings == undefined) {\n ctrl.minRelevance = 0.01;\n ctrl.maxResults = 50;\n ctrl.searchFilter = \"\";\n ctrl.resourceTypes = null;\n ctrl.placeholder = \"Search the Databus...\"\n } else {\n ctrl.minRelevance = ctrl.settings.minRelevance;\n ctrl.maxResults = ctrl.settings.maxResults;\n ctrl.searchFilter = ctrl.settings.filter;\n ctrl.resourceTypes = ctrl.settings.resourceTypes;\n ctrl.placeholder = ctrl.settings.placeholder;\n }\n\n for (var resourceType of ctrl.availableResourceTypes) {\n ctrl.filterActive[resourceType] = false;\n ctrl.filterVisible[resourceType] = ctrl.resourceTypes == null;\n }\n\n ctrl.numFilters = 0;\n\n if (ctrl.resourceTypes != null) {\n for (var resourceType of ctrl.resourceTypes) {\n ctrl.filterVisible[resourceType] = true;\n ctrl.numFilters++;\n }\n }\n }\n\n ctrl.isAnyFilterActive = function () {\n\n for (var resourceType of ctrl.availableResourceTypes) {\n\n if (!ctrl.filterVisible[resourceType]) {\n continue;\n }\n\n if (ctrl.filterActive[resourceType]) {\n return true;\n }\n }\n\n return false;\n }\n\n ctrl.baseQueryFormatter = function(query) {\n return `?query=${query}${ctrl.searchFilter}${ctrl.baseFilters}${ctrl.typeFilters}`\n }\n\n $interval(function () {\n\n if (ctrl.searchChanged) {\n\n var baseFilters = `&minRelevance=${ctrl.minRelevance}&maxResults=${ctrl.maxResults}`;\n var typeFilters = ``;\n var isAnyFilterActive = ctrl.isAnyFilterActive();\n\n\n for (var resourceType of ctrl.availableResourceTypes) {\n\n if (!ctrl.filterVisible[resourceType]) {\n continue;\n }\n\n if (ctrl.filterActive[resourceType] || !isAnyFilterActive) {\n\n if (typeFilters == ``) {\n typeFilters = `&typeName=`;\n }\n\n typeFilters += ` ${resourceType}`;\n }\n }\n\n ctrl.baseFilters = baseFilters;\n ctrl.typeFilters = typeFilters;\n ctrl.searchManager.baseAdapter.queryFormatter = ctrl.baseQueryFormatter;\n\n ctrl.searchManager.search(ctrl.searchInput).then(function success(results) {\n \n for(var result of results) {\n\n if(result.abstract != null) {\n result.abstract = result.abstract[result.abstract.length - 1];\n }\n }\n \n ctrl.results = results;\n\n \n ctrl.isSearching = false;\n }, function error(response) {\n ctrl.isSearching = false;\n });\n\n ctrl.searchChanged = false;\n };\n }, ctrl.searchCooldown);\n\n ctrl.search = function () {\n ctrl.isSearching = true;\n ctrl.searchChanged = true;\n };\n\n};\n\nmodule.exports = NavSearchController;\n\n\n\n//# sourceURL=webpack://databus-webapp/./js/components/nav-search/nav-search-controller.js?"); + +/***/ }), + /***/ "./js/components/override-checkbox/override-checkbox.js": /*!**************************************************************!*\ !*** ./js/components/override-checkbox/override-checkbox.js ***! @@ -245,7 +285,7 @@ eval("\n\n// hinzufügen eines Controllers zum Modul\nfunction OverrideCheckboxC \***************************************************/ /***/ ((module) => { -eval("\n\n// hinzufügen eines Controllers zum Modul\nfunction SearchController($http, $interval, $sce, searchManager) {\n\n var ctrl = this;\n\n // TODO: get search extensions from the logged in user\n\n ctrl.searchManager = searchManager;\n ctrl.results = [];\n\n ctrl.formatResult = function (result) {\n return $sce.trustAsHtml(result);\n }\n\n ctrl.toggleFilter = function (key) {\n ctrl.filterActive[key] = !ctrl.filterActive[key];\n ctrl.search();\n }\n\n ctrl.availableResourceTypes = ['Collection', 'Artifact', 'Group', 'PersonalProfileDocument', 'Version' ];\n\n ctrl.$onInit = function () {\n\n ctrl.searchInput = '';\n ctrl.isSearching = false;\n ctrl.searchCooldown = 300;\n\n if (ctrl.settings == undefined) {\n ctrl.minRelevance = 0.01;\n ctrl.maxResults = 50;\n ctrl.searchFilter = \"\";\n ctrl.resourceTypes = null;\n ctrl.placeholder = \"Search the Databus...\"\n } else {\n ctrl.minRelevance = ctrl.settings.minRelevance;\n ctrl.maxResults = ctrl.settings.maxResults;\n ctrl.searchFilter = ctrl.settings.filter;\n ctrl.resourceTypes = ctrl.settings.resourceTypes;\n ctrl.placeholder = ctrl.settings.placeholder;\n }\n\n ctrl.filterActive = {};\n ctrl.filterVisible = {};\n\n for (var resourceType of ctrl.availableResourceTypes) {\n ctrl.filterActive[resourceType] = false;\n ctrl.filterVisible[resourceType] = ctrl.resourceTypes == null;\n }\n\n ctrl.numFilters = 0;\n\n if (ctrl.resourceTypes != null) {\n for (var resourceType of ctrl.resourceTypes) {\n ctrl.filterVisible[resourceType] = true;\n ctrl.numFilters++;\n }\n }\n }\n\n ctrl.isAnyFilterActive = function () {\n\n for (var resourceType of ctrl.availableResourceTypes) {\n\n if (!ctrl.filterVisible[resourceType]) {\n continue;\n }\n\n if (ctrl.filterActive[resourceType]) {\n return true;\n }\n }\n\n return false;\n }\n\n ctrl.baseQueryFormatter = function(query) {\n return `?query=${query}${ctrl.searchFilter}${ctrl.baseFilters}${ctrl.typeFilters}`\n }\n\n $interval(function () {\n\n if (ctrl.searchChanged) {\n\n var baseFilters = `&minRelevance=${ctrl.minRelevance}&maxResults=${ctrl.maxResults}`;\n var typeFilters = ``;\n var isAnyFilterActive = ctrl.isAnyFilterActive();\n\n\n for (var resourceType of ctrl.availableResourceTypes) {\n\n if (!ctrl.filterVisible[resourceType]) {\n continue;\n }\n\n if (ctrl.filterActive[resourceType] || !isAnyFilterActive) {\n\n if (typeFilters == ``) {\n typeFilters = `&typeName=`;\n }\n\n typeFilters += ` ${resourceType}`;\n }\n }\n\n ctrl.baseFilters = baseFilters;\n ctrl.typeFilters = typeFilters;\n ctrl.searchManager.baseAdapter.queryFormatter = ctrl.baseQueryFormatter;\n\n ctrl.searchManager.search(ctrl.searchInput).then(function success(results) {\n ctrl.results = results;\n ctrl.isSearching = false;\n }, function error(response) {\n ctrl.isSearching = false;\n });\n\n ctrl.searchChanged = false;\n };\n }, ctrl.searchCooldown);\n\n ctrl.search = function () {\n ctrl.isSearching = true;\n ctrl.searchChanged = true;\n };\n\n};\n\nmodule.exports = SearchController;\n\n\n\n//# sourceURL=webpack://databus-webapp/./js/components/search/search-controller.js?"); +eval("\n\n// hinzufügen eines Controllers zum Modul\nfunction SearchController($http, $interval, $sce, searchManager) {\n\n var ctrl = this;\n\n // TODO: get search extensions from the logged in user\n\n ctrl.searchManager = searchManager;\n ctrl.results = [];\n\n ctrl.formatResult = function (result) {\n return $sce.trustAsHtml(result);\n }\n\n ctrl.toggleFilter = function (key) {\n ctrl.filterActive[key] = !ctrl.filterActive[key];\n ctrl.search();\n }\n\n ctrl.availableResourceTypes = ['Collection', 'Artifact', 'Group', 'Account', 'Version' ];\n\n ctrl.$onInit = function () {\n\n ctrl.searchInput = '';\n ctrl.isSearching = false;\n ctrl.searchCooldown = 300;\n\n if (ctrl.settings == undefined) {\n ctrl.minRelevance = 0.01;\n ctrl.maxResults = 50;\n ctrl.searchFilter = \"\";\n ctrl.resourceTypes = null;\n ctrl.placeholder = \"Search the Databus...\"\n } else {\n ctrl.minRelevance = ctrl.settings.minRelevance;\n ctrl.maxResults = ctrl.settings.maxResults;\n ctrl.searchFilter = ctrl.settings.filter;\n ctrl.resourceTypes = ctrl.settings.resourceTypes;\n ctrl.placeholder = ctrl.settings.placeholder;\n }\n\n ctrl.filterActive = {};\n ctrl.filterVisible = {};\n\n for (var resourceType of ctrl.availableResourceTypes) {\n ctrl.filterActive[resourceType] = false;\n ctrl.filterVisible[resourceType] = ctrl.resourceTypes == null;\n }\n\n ctrl.numFilters = 0;\n\n if (ctrl.resourceTypes != null) {\n for (var resourceType of ctrl.resourceTypes) {\n ctrl.filterVisible[resourceType] = true;\n ctrl.numFilters++;\n }\n }\n }\n\n ctrl.isAnyFilterActive = function () {\n\n for (var resourceType of ctrl.availableResourceTypes) {\n\n if (!ctrl.filterVisible[resourceType]) {\n continue;\n }\n\n if (ctrl.filterActive[resourceType]) {\n return true;\n }\n }\n\n return false;\n }\n\n ctrl.baseQueryFormatter = function(query) {\n return `?query=${query}${ctrl.searchFilter}${ctrl.baseFilters}${ctrl.typeFilters}`\n }\n\n $interval(function () {\n\n if (ctrl.searchChanged) {\n\n var baseFilters = `&minRelevance=${ctrl.minRelevance}&maxResults=${ctrl.maxResults}`;\n var typeFilters = ``;\n var isAnyFilterActive = ctrl.isAnyFilterActive();\n\n\n for (var resourceType of ctrl.availableResourceTypes) {\n\n if (!ctrl.filterVisible[resourceType]) {\n continue;\n }\n\n if (ctrl.filterActive[resourceType] || !isAnyFilterActive) {\n\n if (typeFilters == ``) {\n typeFilters = `&typeName=`;\n }\n\n typeFilters += ` ${resourceType}`;\n }\n }\n\n ctrl.baseFilters = baseFilters;\n ctrl.typeFilters = typeFilters;\n ctrl.searchManager.baseAdapter.queryFormatter = ctrl.baseQueryFormatter;\n\n ctrl.searchManager.search(ctrl.searchInput).then(function success(results) {\n ctrl.results = results;\n ctrl.isSearching = false;\n }, function error(response) {\n ctrl.isSearching = false;\n });\n\n ctrl.searchChanged = false;\n };\n }, ctrl.searchCooldown);\n\n ctrl.search = function () {\n ctrl.isSearching = true;\n ctrl.searchChanged = true;\n };\n\n};\n\nmodule.exports = SearchController;\n\n\n\n//# sourceURL=webpack://databus-webapp/./js/components/search/search-controller.js?"); /***/ }), @@ -255,7 +295,7 @@ eval("\n\n// hinzufügen eines Controllers zum Modul\nfunction SearchController( \****************************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("const DatabusUtils = __webpack_require__(/*! ../../utils/databus-utils */ \"./js/utils/databus-utils.js\");\n\n// hinzufügen eines Controllers zum Modul\nfunction TableEditorController() {\n\n var ctrl = this;\n\n ctrl.$onInit = function() {\n\n ctrl.selection = {};\n ctrl.edit = {};\n\n if(ctrl.model.groupMode == undefined) {\n ctrl.model.groupMode = true;\n }\n\n ctrl.setupColumns();\n ctrl.updateViewModel();\n }\n\n ctrl.getSpanWidth = function(row, cv) {\n var span = $(`#${row}_${cv}`);\n var width = span.width();\n\n if(width == undefined) {\n return 0;\n }\n\n return width + 22;\n }\n\n ctrl.setupColumns = function() {\n\n ctrl.columns = [];\n ctrl.columns.push({ title:'File', width: 400, isReadonly : true });\n ctrl.columns.push({ title:'Format', width: 75, isReadonly : true });\n ctrl.columns.push({ title:'Compression', width: 115, isReadonly : true });\n\n for(var c in ctrl.model.contentVariants) {\n var cv = ctrl.model.contentVariants[c];\n ctrl.columns.push({ title: cv.label, width: 120, isReadonly : false });\n }\n\n ctrl.columns.push({ title:'Actions', width: 120, isReadonly : true });\n ctrl.progressWidth = (115 + 200) + 'px';\n }\n\n ctrl.toggleGroupMode = function() {\n ctrl.model.groupMode = ! ctrl.model.groupMode;\n ctrl.updateViewModel();\n }\n\n ctrl.onShowInput = function($event) {\n \n }\n\n ctrl.deselect = function() {\n ctrl.edit.x = undefined;\n ctrl.edit.y = undefined;\n ctrl.selection.x = undefined;\n ctrl.selection.y = undefined;\n ctrl.selection.width = 0;\n ctrl.selection.height = 0;\n }\n\n ctrl.selectCell = function($event, x, y) {\n\n if(ctrl.selection.x == x && ctrl.selection.y == y) {\n ctrl.edit.x = x;\n ctrl.edit.y = y; \n\n // $event.target.setSelectionRange(0, $event.target.value.length)\n // $event.target.selectionStart = 0;\n // $event.target.selectionEnd = $event.target.value.length;\n return;\n }\n\n ctrl.edit.x = undefined;\n ctrl.edit.y = undefined;\n ctrl.selection.x = x;\n ctrl.selection.y = y;\n ctrl.selection.width = 1;\n ctrl.selection.height = 1;\n \n }\n\n ctrl.analyzeFile = function(file) {\n ctrl.onAnalyzeFile({ file : file });\n }\n\n ctrl.onChangeCv = function(file, cv) {\n\n var index = ctrl.model.files.findIndex(f => f.uri == file.uri);\n \n for(var i = index + 1; i < index + file.rowspan; i++) {\n ctrl.model.files[i].contentVariants[cv.label] = file.contentVariants[cv.label];\n }\n \n ctrl.model.isConfigDirty = true\n }\n\n ctrl.updateViewModel = function() {\n\n\n for(var f in ctrl.model.files) {\n ctrl.model.files[f].rowspan = 1;\n }\n\n if(ctrl.model.groupMode) {\n\n var i = 0;\n var step = 1;\n\n while(i + step < ctrl.model.files.length) {\n\n if(ctrl.model.files[i].name == ctrl.model.files[i + step].name) {\n // Swallow the cv setting of the next row\n ctrl.model.files[i].rowspan++;\n ctrl.model.files[i + step].rowspan = 0;\n\n for(var c in ctrl.model.contentVariants) {\n var cv = ctrl.model.contentVariants[c];\n ctrl.model.files[i + step].contentVariants[cv.label] = ctrl.model.files[i].contentVariants[cv.label];\n }\n\n step++;\n } else {\n i += step;\n step = 1;\n }\n }\n }\n\n\n }\n /**\n * Removes a specific distribution from an artifact\n * @param {*} artifact \n * @param {*} file \n */\n ctrl.removeFileFromArtifact = function(file) {\n ctrl.onRemoveFile({ file : file });\n }\n\n ctrl.$doCheck = function() { \n\n var numFiles = DatabusUtils.objSize(ctrl.model.files);\n if(ctrl.numFiles != numFiles) {\n ctrl.updateViewModel();\n ctrl.numFiles = numFiles;\n }\n\n\n if(ctrl.columns == undefined) {\n return;\n }\n\n var columnCount = 4;\n\n for(var c in ctrl.model.contentVariants) {\n columnCount++;\n }\n\n ctrl.progressPosition = 45;\n for(var i = 0; i < columnCount - 3; i++) {\n ctrl.progressPosition += ctrl.columns[i].width;\n }\n ctrl.progressPosition = ctrl.progressPosition + 'px'\n\n if(ctrl.columns.length == columnCount) {\n return;\n }\n\n ctrl.setupColumns();\n }\n\n ctrl.change = function() {\n\n }\n};\n\nmodule.exports = TableEditorController;\n\n//# sourceURL=webpack://databus-webapp/./js/components/table-editor/table-editor.js?"); +eval("const DatabusUtils = __webpack_require__(/*! ../../utils/databus-utils */ \"./js/utils/databus-utils.js\");\r\n\r\n// hinzufügen eines Controllers zum Modul\r\nfunction TableEditorController() {\r\n\r\n var ctrl = this;\r\n\r\n ctrl.$onInit = function() {\r\n\r\n ctrl.selection = {};\r\n ctrl.edit = {};\r\n\r\n if(ctrl.model.groupMode == undefined) {\r\n ctrl.model.groupMode = true;\r\n }\r\n\r\n ctrl.setupColumns();\r\n ctrl.updateViewModel();\r\n }\r\n\r\n ctrl.getSpanWidth = function(row, cv) {\r\n var span = $(`#${row}_${cv}`);\r\n var width = span.width();\r\n\r\n if(width == undefined) {\r\n return 0;\r\n }\r\n\r\n return width + 22;\r\n }\r\n\r\n ctrl.editContentVariant = function(index) {\r\n\r\n ctrl.onEditContentVariant({ index: index});\r\n }\r\n\r\n ctrl.setupColumns = function() {\r\n\r\n ctrl.columns = [];\r\n ctrl.columns.push({ title:'File', width: 400, isReadonly : true });\r\n\r\n for(var c in ctrl.model.contentVariants) {\r\n var cv = ctrl.model.contentVariants[c];\r\n ctrl.columns.push({ title: cv.label, width: 120, isReadonly : false });\r\n }\r\n\r\n ctrl.columns.push({ title:'Actions', width: 120, isReadonly : true });\r\n ctrl.progressWidth = (115 + 200) + 'px';\r\n }\r\n\r\n ctrl.toggleGroupMode = function() {\r\n ctrl.model.groupMode = ! ctrl.model.groupMode;\r\n ctrl.updateViewModel();\r\n }\r\n\r\n ctrl.onShowInput = function($event) {\r\n \r\n }\r\n\r\n ctrl.deselect = function() {\r\n ctrl.edit.x = undefined;\r\n ctrl.edit.y = undefined;\r\n ctrl.selection.x = undefined;\r\n ctrl.selection.y = undefined;\r\n ctrl.selection.width = 0;\r\n ctrl.selection.height = 0;\r\n }\r\n\r\n ctrl.selectCell = function($event, x, y) {\r\n\r\n \r\n ctrl.edit.x = undefined;\r\n ctrl.edit.y = undefined;\r\n\r\n ctrl.edit.x = x;\r\n ctrl.edit.y = y; \r\n ctrl.selection.x = x;\r\n ctrl.selection.y = y;\r\n ctrl.selection.width = 1;\r\n ctrl.selection.height = 1;\r\n \r\n }\r\n\r\n ctrl.analyzeFile = function(file) {\r\n ctrl.onAnalyzeFile({ file : file });\r\n }\r\n\r\n ctrl.onChangeCv = function(file, cv) {\r\n\r\n var index = ctrl.model.files.findIndex(f => f.uri == file.uri);\r\n \r\n for(var i = index + 1; i < index + file.rowspan; i++) {\r\n ctrl.model.files[i].contentVariants[cv.id] = file.contentVariants[cv.id];\r\n }\r\n \r\n ctrl.model.onChange();\r\n }\r\n\r\n ctrl.updateViewModel = function() {\r\n\r\n for(var f in ctrl.model.files) {\r\n ctrl.model.files[f].rowspan = 1;\r\n }\r\n\r\n /*\r\n if(ctrl.model.groupMode) {\r\n\r\n var i = 0;\r\n var step = 1;\r\n\r\n while(i + step < ctrl.model.files.length) {\r\n\r\n if(ctrl.model.files[i].name == ctrl.model.files[i + step].name) {\r\n // Swallow the cv setting of the next row\r\n // ctrl.model.files[i].rowspan++;\r\n // ctrl.model.files[i + step].rowspan = 0;\r\n\r\n for(var c in ctrl.model.contentVariants) {\r\n var cv = ctrl.model.contentVariants[c];\r\n ctrl.model.files[i + step].contentVariants[cv.id] = ctrl.model.files[i].contentVariants[cv.id];\r\n }\r\n\r\n step++;\r\n } else {\r\n i += step;\r\n step = 1;\r\n }\r\n }\r\n }*/\r\n\r\n\r\n }\r\n /**\r\n * Removes a specific distribution from an artifact\r\n * @param {*} artifact \r\n * @param {*} file \r\n */\r\n ctrl.removeFileFromArtifact = function(file, index) {\r\n ctrl.onRemoveFile({ file : file, index: index});\r\n }\r\n\r\n ctrl.$doCheck = function() { \r\n\r\n var numFiles = DatabusUtils.objSize(ctrl.model.files);\r\n if(ctrl.numFiles != numFiles) {\r\n ctrl.updateViewModel();\r\n ctrl.numFiles = numFiles;\r\n }\r\n\r\n\r\n if(ctrl.columns == undefined) {\r\n return;\r\n }\r\n\r\n var columnCount = 4;\r\n\r\n for(var c in ctrl.model.contentVariants) {\r\n columnCount++;\r\n }\r\n\r\n ctrl.progressPosition = 45;\r\n for(var i = 0; i < columnCount - 3; i++) {\r\n ctrl.progressPosition += ctrl.columns[i].width;\r\n }\r\n ctrl.progressPosition = ctrl.progressPosition + 'px'\r\n\r\n if(ctrl.columns.length == columnCount) {\r\n return;\r\n }\r\n\r\n ctrl.setupColumns();\r\n }\r\n\r\n ctrl.change = function() {\r\n\r\n }\r\n};\r\n\r\nmodule.exports = TableEditorController;\n\n//# sourceURL=webpack://databus-webapp/./js/components/table-editor/table-editor.js?"); /***/ }), @@ -265,7 +305,7 @@ eval("const DatabusUtils = __webpack_require__(/*! ../../utils/databus-utils */ \********************************************/ /***/ ((module) => { -eval("// hinzufügen eines Controllers zum Modul\nfunction TypeTagController() {\n\n var ctrl = this;\n\n ctrl.typeMap = {};\n ctrl.typeMap[\"Artifact\"] = \"is-artifact\";\n ctrl.typeMap[\"Version\"] = \"is-version\";\n ctrl.typeMap[\"Group\"] = \"is-group\";\n ctrl.typeMap[\"Service\"] = \"is-service\";\n ctrl.typeMap[\"PersonalProfileDocument\"] = \"is-consumer\";\n ctrl.typeMap[\"Collection\"] = \"is-collection\";\n ctrl.typeMap[\"BlogEntry\"] = \"is-blog\";\n ctrl.typeMap[\"Databus\"] = \"is-version\";\n ctrl.typeMap[\"Sparql\"] = \"is-grey\";\n\n ctrl.iconMap = {};\n ctrl.iconMap[\"Artifact\"] = \"M12,0 L1,12 l11,12 l11,-12 L12,0z\"; \n ctrl.iconMap[\"Version\"] = \"M 14.9 1 L 12.293 1.005 L 16.507 7.18 L 16.5 23.1 L 18.5 23.1 L 18.5 6.4 L 14.9 1 Z M 10.4 1 L 1.581 1.004 L 1.584 23.13 L 15 23.1 L 15 7.7 L 10.4 1 Z M 16.8 1 L 20 5.8 L 20 23.1 L 22 23.1 L 22 4.9 L 19.3 1 L 16.8 1 Z\";\n ctrl.iconMap[\"Group\"] = \"M21.698 10.658l2.302 1.342-12.002 7-11.998-7 2.301-1.342 9.697 5.658 9.7-5.658zm-9.7 10.657l-9.697-5.658-2.301 1.343 11.998 7 12.002-7-2.302-1.342-9.7 5.657zm12.002-14.315l-12.002-7-11.998 7 11.998 7 12.002-7z\";\n ctrl.iconMap[\"Service\"] = \"M24 13.616v-3.232l-2.869-1.02c-.198-.687-.472-1.342-.811-1.955l1.308-2.751-2.285-2.285-2.751 1.307c-.613-.339-1.269-.613-1.955-.811l-1.021-2.869h-3.232l-1.021 2.869c-.686.198-1.342.471-1.955.811l-2.751-1.308-2.285 2.285 1.308 2.752c-.339.613-.614 1.268-.811 1.955l-2.869 1.02v3.232l2.869 1.02c.197.687.472 1.342.811 1.955l-1.308 2.751 2.285 2.286 2.751-1.308c.613.339 1.269.613 1.955.811l1.021 2.869h3.232l1.021-2.869c.687-.198 1.342-.472 1.955-.811l2.751 1.308 2.285-2.286-1.308-2.751c.339-.613.613-1.268.811-1.955l2.869-1.02zm-12 2.384c-2.209 0-4-1.791-4-4s1.791-4 4-4 4 1.791 4 4-1.791 4-4 4z\";\n ctrl.iconMap[\"PersonalProfileDocument\"] = \"M19 7.001c0 3.865-3.134 7-7 7s-7-3.135-7-7c0-3.867 3.134-7.001 7-7.001s7 3.134 7 7.001zm-1.598 7.18c-1.506 1.137-3.374 1.82-5.402 1.82-2.03 0-3.899-.685-5.407-1.822-4.072 1.793-6.593 7.376-6.593 9.821h24c0-2.423-2.6-8.006-6.598-9.819z\";\n ctrl.iconMap[\"Collection\"] = \"M11.499 12.03v11.971l-10.5-5.603v-11.835l10.5 5.467zm11.501 6.368l-10.501 5.602v-11.968l10.501-5.404v11.77zm-16.889-15.186l10.609 5.524-4.719 2.428-10.473-5.453 4.583-2.499zm16.362 2.563l-4.664 2.4-10.641-5.54 4.831-2.635 10.474 5.775z\";\n ctrl.iconMap[\"BlogEntry\"] = \"M21 9.662c-2.287.194-5.197 1.038-7 1.794v-1.064c1.933-.721 4.598-1.54 7-1.745v1.015zm0 2.031c-2.287.194-5.197 1.038-7 1.794v-1.064c1.933-.721 4.598-1.54 7-1.745v1.015zm0 2.031c-2.287.194-5.197 1.038-7 1.794v-1.064c1.933-.721 4.598-1.54 7-1.745v1.015zm0 2.031c-2.287.194-5.197 1.038-7 1.794v-1.064c1.933-.721 4.598-1.54 7-1.745v1.015zm0-9.951c-2.402.204-5.068 1.024-7 1.745v1.933c1.804-.756 4.713-1.6 7-1.794v-1.884zm-18 2.843c2.402.205 5.067 1.024 7 1.745v1.064c-1.803-.756-4.713-1.6-7-1.794v-1.015zm0 2.031c2.402.205 5.067 1.024 7 1.745v1.064c-1.803-.756-4.713-1.6-7-1.794v-1.015zm0 2.031c2.402.205 5.067 1.024 7 1.745v1.064c-1.803-.756-4.713-1.6-7-1.794v-1.015zm0 2.032c2.402.205 5.067 1.024 7 1.745v1.064c-1.803-.756-4.713-1.6-7-1.794v-1.015zm0-7.054c2.287.194 5.196 1.038 7 1.794v-1.933c-1.932-.72-4.598-1.54-7-1.744v1.883zm9-2.724c-3.063-1.671-7.776-2.755-12-2.963v17c4.289.206 8.195 1.249 12 3 3.805-1.751 7.711-2.794 12-3v-17c-4.224.208-8.937 1.292-12 2.963zm-10-.791c4.264.496 6.86 1.467 9 2.545v12.702c-2.968-1.184-5.939-1.95-9-2.271v-12.976zm20 12.975c-3.061.321-6.032 1.088-9 2.271v-12.701c2.187-1.103 4.757-2.051 9-2.544v12.974z\";\n ctrl.iconMap[\"Databus\"] = \"m 0.76949155,0.7702454 v 5.24959 l 29.33129045,-10e-4 6.27262,8.8675006 -0.002,32.0824 4.7212,-0.002 V 0.7702354 Z m 18.43511045,8.3952603 -5.68354,0.006 7.1979,10.5484003 -0.004,27.24663 4.70393,-0.002 0.0167,-28.60108 z m -9.4730904,0.002 -8.96510005,0.002 0.004,37.7960503 16.79563045,-0.004 0.001,-26.29103 z m 13.2512904,0 5.59825,8.2188903 -0.0396,29.57614 4.70307,-0.002 0.006,-31.09587 -4.55858,-6.6940403 z\";\n ctrl.iconMap[\"Sparql\"] = \"M383.476,267.343c-2.544-1.346-5.14-2.493-7.743-3.516l1.863-0.15c0,0-16.608-7.354-18.057-60.722 c-1.438-53.372,15.828-62.478,15.828-62.478l-2.48,0.109c13.045-6.69,24.265-17.267,31.669-31.216 c19.295-36.291,5.488-81.362-30.81-100.657C337.436-10.563,292.374,3.207,273.09,39.53c-7.927,14.899-10.178,31.273-7.677,46.733 l-0.851-1.306c0,0,4.373,19.365-41.032,47.55c-45.397,28.2-65.877,14.159-65.877,14.159l1.302,1.925 c-1.298-0.803-2.544-1.624-3.901-2.333c-36.306-19.294-81.38-5.509-100.667,30.804c-19.281,36.309-5.489,81.365,30.813,100.668 c27.064,14.364,58.974,10.36,81.461-7.655l-0.487,0.946c0,0,16.531-13.599,64.16,11.973c37.601,20.178,43.184,39.956,43.899,47.383 c-0.983,27.57,13.388,54.618,39.389,68.433c36.301,19.299,81.374,5.498,100.657-30.804 C433.571,331.704,419.786,286.624,383.476,267.343z M299.542,277.128c-6.018,2.129-23.203,4.487-59.389-14.921 c-39.187-21.04-45.005-38.615-45.855-43.891c0.557-6.401,0.202-12.791-0.891-19.02l0.239,0.359c0,0-3.189-17.096,41.65-44.943 c40.133-24.908,58.376-19.955,61.771-18.653c2.185,1.485,4.45,2.867,6.825,4.131c4.518,2.398,9.174,4.283,13.888,5.672 c5.52,5.257,15.678,20.178,16.733,59.413c1.078,39.535-10.533,54.779-16.865,60.168C311.122,268.399,305.022,272.34,299.542,277.128 z\";\n\n ctrl.$onInit = function() {\n ctrl.class = ctrl.typeMap[ctrl.type];\n ctrl.icon = ctrl.iconMap[ctrl.type];\n ctrl.style = {};\n ctrl.style.width = ctrl.width + \"px\";\n ctrl.style.height = ctrl.height + \"px\";\n ctrl.viewBox = \"0 0 24 24\";\n\n if(ctrl.type == 'Databus') {\n ctrl.viewBox = \"2 0 42 40\";\n }\n\n if(ctrl.type == 'Sparql') {\n ctrl.viewBox = \"40 0 430 420\";\n }\n }\n}\n\nmodule.exports = TypeTagController;\n\n//# sourceURL=webpack://databus-webapp/./js/components/type-tag/type-tag.js?"); +eval("// hinzufügen eines Controllers zum Modul\nfunction TypeTagController() {\n\n var ctrl = this;\n\n ctrl.typeMap = {};\n ctrl.typeMap[\"Artifact\"] = \"is-artifact\";\n ctrl.typeMap[\"Version\"] = \"is-version\";\n ctrl.typeMap[\"Group\"] = \"is-group\";\n ctrl.typeMap[\"Service\"] = \"is-service\";\n ctrl.typeMap[\"Account\"] = \"is-consumer\";\n ctrl.typeMap[\"Collection\"] = \"is-collection\";\n ctrl.typeMap[\"BlogEntry\"] = \"is-blog\";\n ctrl.typeMap[\"Databus\"] = \"is-version\";\n ctrl.typeMap[\"Sparql\"] = \"is-grey\";\n\n ctrl.iconMap = {};\n ctrl.iconMap[\"Artifact\"] = \"M12,0 L1,12 l11,12 l11,-12 L12,0z\"; \n ctrl.iconMap[\"Version\"] = \"M 14.9 1 L 12.293 1.005 L 16.507 7.18 L 16.5 23.1 L 18.5 23.1 L 18.5 6.4 L 14.9 1 Z M 10.4 1 L 1.581 1.004 L 1.584 23.13 L 15 23.1 L 15 7.7 L 10.4 1 Z M 16.8 1 L 20 5.8 L 20 23.1 L 22 23.1 L 22 4.9 L 19.3 1 L 16.8 1 Z\";\n ctrl.iconMap[\"Group\"] = \"M21.698 10.658l2.302 1.342-12.002 7-11.998-7 2.301-1.342 9.697 5.658 9.7-5.658zm-9.7 10.657l-9.697-5.658-2.301 1.343 11.998 7 12.002-7-2.302-1.342-9.7 5.657zm12.002-14.315l-12.002-7-11.998 7 11.998 7 12.002-7z\";\n ctrl.iconMap[\"Service\"] = \"M24 13.616v-3.232l-2.869-1.02c-.198-.687-.472-1.342-.811-1.955l1.308-2.751-2.285-2.285-2.751 1.307c-.613-.339-1.269-.613-1.955-.811l-1.021-2.869h-3.232l-1.021 2.869c-.686.198-1.342.471-1.955.811l-2.751-1.308-2.285 2.285 1.308 2.752c-.339.613-.614 1.268-.811 1.955l-2.869 1.02v3.232l2.869 1.02c.197.687.472 1.342.811 1.955l-1.308 2.751 2.285 2.286 2.751-1.308c.613.339 1.269.613 1.955.811l1.021 2.869h3.232l1.021-2.869c.687-.198 1.342-.472 1.955-.811l2.751 1.308 2.285-2.286-1.308-2.751c.339-.613.613-1.268.811-1.955l2.869-1.02zm-12 2.384c-2.209 0-4-1.791-4-4s1.791-4 4-4 4 1.791 4 4-1.791 4-4 4z\";\n ctrl.iconMap[\"Account\"] = \"M19 7.001c0 3.865-3.134 7-7 7s-7-3.135-7-7c0-3.867 3.134-7.001 7-7.001s7 3.134 7 7.001zm-1.598 7.18c-1.506 1.137-3.374 1.82-5.402 1.82-2.03 0-3.899-.685-5.407-1.822-4.072 1.793-6.593 7.376-6.593 9.821h24c0-2.423-2.6-8.006-6.598-9.819z\";\n ctrl.iconMap[\"Collection\"] = \"M11.499 12.03v11.971l-10.5-5.603v-11.835l10.5 5.467zm11.501 6.368l-10.501 5.602v-11.968l10.501-5.404v11.77zm-16.889-15.186l10.609 5.524-4.719 2.428-10.473-5.453 4.583-2.499zm16.362 2.563l-4.664 2.4-10.641-5.54 4.831-2.635 10.474 5.775z\";\n ctrl.iconMap[\"BlogEntry\"] = \"M21 9.662c-2.287.194-5.197 1.038-7 1.794v-1.064c1.933-.721 4.598-1.54 7-1.745v1.015zm0 2.031c-2.287.194-5.197 1.038-7 1.794v-1.064c1.933-.721 4.598-1.54 7-1.745v1.015zm0 2.031c-2.287.194-5.197 1.038-7 1.794v-1.064c1.933-.721 4.598-1.54 7-1.745v1.015zm0 2.031c-2.287.194-5.197 1.038-7 1.794v-1.064c1.933-.721 4.598-1.54 7-1.745v1.015zm0-9.951c-2.402.204-5.068 1.024-7 1.745v1.933c1.804-.756 4.713-1.6 7-1.794v-1.884zm-18 2.843c2.402.205 5.067 1.024 7 1.745v1.064c-1.803-.756-4.713-1.6-7-1.794v-1.015zm0 2.031c2.402.205 5.067 1.024 7 1.745v1.064c-1.803-.756-4.713-1.6-7-1.794v-1.015zm0 2.031c2.402.205 5.067 1.024 7 1.745v1.064c-1.803-.756-4.713-1.6-7-1.794v-1.015zm0 2.032c2.402.205 5.067 1.024 7 1.745v1.064c-1.803-.756-4.713-1.6-7-1.794v-1.015zm0-7.054c2.287.194 5.196 1.038 7 1.794v-1.933c-1.932-.72-4.598-1.54-7-1.744v1.883zm9-2.724c-3.063-1.671-7.776-2.755-12-2.963v17c4.289.206 8.195 1.249 12 3 3.805-1.751 7.711-2.794 12-3v-17c-4.224.208-8.937 1.292-12 2.963zm-10-.791c4.264.496 6.86 1.467 9 2.545v12.702c-2.968-1.184-5.939-1.95-9-2.271v-12.976zm20 12.975c-3.061.321-6.032 1.088-9 2.271v-12.701c2.187-1.103 4.757-2.051 9-2.544v12.974z\";\n ctrl.iconMap[\"Databus\"] = \"m 0.76949155,0.7702454 v 5.24959 l 29.33129045,-10e-4 6.27262,8.8675006 -0.002,32.0824 4.7212,-0.002 V 0.7702354 Z m 18.43511045,8.3952603 -5.68354,0.006 7.1979,10.5484003 -0.004,27.24663 4.70393,-0.002 0.0167,-28.60108 z m -9.4730904,0.002 -8.96510005,0.002 0.004,37.7960503 16.79563045,-0.004 0.001,-26.29103 z m 13.2512904,0 5.59825,8.2188903 -0.0396,29.57614 4.70307,-0.002 0.006,-31.09587 -4.55858,-6.6940403 z\";\n ctrl.iconMap[\"Sparql\"] = \"M383.476,267.343c-2.544-1.346-5.14-2.493-7.743-3.516l1.863-0.15c0,0-16.608-7.354-18.057-60.722 c-1.438-53.372,15.828-62.478,15.828-62.478l-2.48,0.109c13.045-6.69,24.265-17.267,31.669-31.216 c19.295-36.291,5.488-81.362-30.81-100.657C337.436-10.563,292.374,3.207,273.09,39.53c-7.927,14.899-10.178,31.273-7.677,46.733 l-0.851-1.306c0,0,4.373,19.365-41.032,47.55c-45.397,28.2-65.877,14.159-65.877,14.159l1.302,1.925 c-1.298-0.803-2.544-1.624-3.901-2.333c-36.306-19.294-81.38-5.509-100.667,30.804c-19.281,36.309-5.489,81.365,30.813,100.668 c27.064,14.364,58.974,10.36,81.461-7.655l-0.487,0.946c0,0,16.531-13.599,64.16,11.973c37.601,20.178,43.184,39.956,43.899,47.383 c-0.983,27.57,13.388,54.618,39.389,68.433c36.301,19.299,81.374,5.498,100.657-30.804 C433.571,331.704,419.786,286.624,383.476,267.343z M299.542,277.128c-6.018,2.129-23.203,4.487-59.389-14.921 c-39.187-21.04-45.005-38.615-45.855-43.891c0.557-6.401,0.202-12.791-0.891-19.02l0.239,0.359c0,0-3.189-17.096,41.65-44.943 c40.133-24.908,58.376-19.955,61.771-18.653c2.185,1.485,4.45,2.867,6.825,4.131c4.518,2.398,9.174,4.283,13.888,5.672 c5.52,5.257,15.678,20.178,16.733,59.413c1.078,39.535-10.533,54.779-16.865,60.168C311.122,268.399,305.022,272.34,299.542,277.128 z\";\n\n ctrl.$onInit = function() {\n ctrl.class = ctrl.typeMap[ctrl.type];\n ctrl.icon = ctrl.iconMap[ctrl.type];\n ctrl.style = {};\n ctrl.style.width = ctrl.width + \"px\";\n ctrl.style.height = ctrl.height + \"px\";\n ctrl.viewBox = \"0 0 24 24\";\n\n if(ctrl.type == 'Databus') {\n ctrl.viewBox = \"2 0 42 40\";\n }\n\n if(ctrl.type == 'Sparql') {\n ctrl.viewBox = \"40 0 430 420\";\n }\n }\n}\n\nmodule.exports = TypeTagController;\n\n//# sourceURL=webpack://databus-webapp/./js/components/type-tag/type-tag.js?"); /***/ }), @@ -305,7 +345,7 @@ eval("function YasrViewController($scope, $element) {\n\n var ctrl = this;\n\n \**************************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("const DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\nconst DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\nconst TabNavigation = __webpack_require__(/*! ../utils/tab-navigation */ \"./js/utils/tab-navigation.js\");\n\nvar DEFAULT_IMAGE = \"https://picsum.photos/id/223/320/320\";\n\n// Controller for the header section\nfunction AccountPageController($scope, $http, $location, collectionManager) {\n\n $scope.collectionManager = collectionManager;\n\n\n // Pick up the profile data\n $scope.auth = data.auth;\n $scope.location = $location;\n\n $scope.profileData = data.profile;\n\n // Exit if there is no profile\n if ($scope.profileData == undefined) {\n return;\n }\n\n $scope.profileData.isOwn = $scope.auth.authenticated && $scope.auth.info.accountName == $scope.profileData.accountName;\n\n // Create a tab navigation object for the tab navigation with locato\n $scope.tabNavigation = new TabNavigation($scope, $location, [\n 'data', 'collections', 'settings'\n ]);\n\n // Make some util functions available in the template\n $scope.utils = new DatabusWebappUtils($scope);\n\n\n $scope.dataSearchInput = '';\n $scope.dataSearchSettings = {\n minRelevance: 0.01,\n maxResults: 10,\n placeholder: `Search ${$scope.profileData.accountName}'s data...`,\n resourceTypes: ['Group', 'Artifact'],\n filter: `&publisher=${$scope.profileData.accountName}&typeNameWeight=0`\n };\n\n $scope.collectionSearchInput = '';\n $scope.collectionSearchSettings = {\n minRelevance: 0.01,\n maxResults: 10,\n placeholder: `Search ${$scope.profileData.accountName}'s collections...`,\n resourceTypes: ['Collection'],\n filter: `&publisher=${$scope.profileData.accountName}&publisherWeight=0&typeNameWeight=0`\n };\n\n \n // Wait for additional artifact data to arrive\n $scope.publishedData = {};\n $scope.publishedData.isLoading = true;\n\n $http.get(`/app/account/content?account=${encodeURIComponent($scope.profileData.accountName)}`)\n .then(function (response) {\n\n $scope.publishedData.isLoading = false;\n $scope.publishedData.groups = response.data.groups;\n $scope.publishedData.artifacts = response.data.artifacts;\n\n for (var artifact of $scope.publishedData.artifacts) {\n artifact.group = DatabusUtils.navigateUp(artifact.uri, 1);\n artifact.title = DatabusUtils.stringOrFallback(artifact.title, artifact.latestVersionTitle);\n artifact.abstract = DatabusUtils.stringOrFallback(artifact.abstract, artifact.latestVersionAbstract);\n artifact.description = DatabusUtils.stringOrFallback(artifact.description, artifact.latestVersionDescription);\n }\n\n for (var group of $scope.publishedData.groups) {\n group.artifacts = $scope.publishedData.artifacts.filter(function (a) {\n return a.group == group.uri;\n });\n }\n\n // Order by latest version date\n $scope.recentUploads = $scope.publishedData.artifacts.filter(function (v) {\n return v.latestVersionDate != null;\n });\n $scope.recentUploads.sort(function (a, b) {\n return new Date(b.latestVersionDate) - new Date(a.latestVersionDate);\n });\n\n $scope.recentUploads = $scope.recentUploads.slice(0, 3);\n\n $scope.refreshFeaturedContent();\n }, function (err) {\n console.log(err);\n });\n\n\n // Wait for stats data to arrive\n $scope.statsData = {};\n $scope.statsData.isLoading = true;\n\n $http.get(`/app/account/stats?account=${encodeURIComponent($scope.profileData.accountName)}`).then(function (response) {\n $scope.statsData.stats = response.data;\n $scope.statsData.isLoading = false;\n }, function (err) {\n console.log(err);\n });\n\n // Wait for activity chart data to arrive\n $scope.activityData = {};\n $scope.activityData.isLoading = true;\n\n $http.get(`/app/account/activity?account=${encodeURIComponent($scope.profileData.accountName)}`).then(function (response) {\n $scope.activityData.entries = response.data;\n $scope.activityData.isLoading = false;\n }, function (err) {\n console.log(err);\n });\n\n $scope.collectionsData = {};\n $scope.collectionsData.isLoading = true;\n\n if (!$scope.profileData.isOwn) {\n $http.get(`/app/account/collections?account=${encodeURIComponent($scope.profileData.accountName)}`)\n .then(function (response) {\n\n $scope.collectionsData.collections = response.data;\n $scope.collectionsData.isLoading = false;\n $scope.refreshFeaturedContent();\n }, function (err) {\n console.log(err);\n });\n }\n\n $scope.getImageUrl = function () {\n if ($scope.profileData.imageUrl == undefined) {\n return DEFAULT_IMAGE;\n } else {\n return $scope.profileData.imageUrl;\n }\n }\n\n /**\n * COLLECTION FUNCTIONS \n */\n\n // Collection List Search\n $scope.collectionSearch = {};\n $scope.collectionSearch.sortVisible = false;\n $scope.collectionSearch.sortProperty = 'title';\n $scope.collectionSearch.sortProperties = [ \n { key: 'title', label: 'Title' },\n { key: 'issued', label: 'Issued Date' },\n ];\n $scope.collectionSearch.sortReverse = false;\n $scope.collectionSearch.toggleSort = function(value) {\n if($scope.collectionSearch.sortProperty == value) {\n $scope.collectionSearch.sortReverse = !$scope.collectionSearch.sortReverse;\n } else {\n $scope.collectionSearch.sortProperty = value;\n }\n }\n\n /**\n * Pencil icon for edit pressed\n * @param {*} collection \n */\n $scope.onEditCollectionClicked = function (collection) {\n $scope.collectionManager.setActive(collection.uuid);\n window.location.href = '/app/collection-editor';\n }\n\n /**\n * Create new collection\n */\n $scope.createNewCollection = function () {\n $scope.collectionManager.createNew('New Collection', 'Replace this description with a description of your choice.',\n function (success) {\n window.location.href = '/app/collection-editor';\n });\n }\n\n /**\n * Create a copy of the clicked collection\n */\n $scope.createCopy = function(collection) {\n $scope.collectionManager.createCopy(collection);\n window.location.href = '/app/collection-editor';\n }\n\n\n $scope.findFeaturedContent = function (uri) {\n\n for (var g in $scope.publishedData.groups) {\n var group = $scope.publishedData.groups[g];\n\n if (uri == group.uri) {\n return {\n type: 'Group',\n title: group.title,\n uri: uri,\n description: group.description\n }\n }\n\n for (var a in group.artifacts) {\n var artifact = group.artifacts[a];\n\n if (uri == artifact.artifactUri) {\n return {\n type: 'Artifact',\n title: artifact.title,\n uri: uri,\n description: artifact.description\n }\n }\n }\n }\n\n for (var c in $scope.collectionsData.collections) {\n var collection = $scope.collectionsData.collections[c];\n\n if (uri == collection.uri) {\n return {\n type: 'Collection',\n title: collection.title,\n uri: uri,\n description: collection.description\n }\n }\n }\n\n }\n\n $scope.refreshFeaturedContent = function () {\n if ($scope.profileData.featuredContent == undefined) {\n return;\n }\n\n var featuredContentUris = $scope.profileData.featuredContent.split('\\n');\n $scope.featuredContent = [];\n\n for (var f in featuredContentUris) {\n var content = $scope.findFeaturedContent(featuredContentUris[f]);\n\n if (content != undefined) {\n $scope.featuredContent.push(content);\n }\n }\n }\n\n}\n\nmodule.exports = AccountPageController;\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/account-controller.js?"); +eval("const DatabusCollectionManager = __webpack_require__(/*! ../collections/databus-collection-manager */ \"./js/collections/databus-collection-manager.js\");\r\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\r\nconst DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\r\nconst TabNavigation = __webpack_require__(/*! ../utils/tab-navigation */ \"./js/utils/tab-navigation.js\");\r\n\r\nvar DEFAULT_IMAGE = \"https://picsum.photos/id/223/320/320\";\r\n\r\n// Controller for the header section\r\n\r\n/**\r\n * \r\n * @param {*} $scope \r\n * @param {*} $http \r\n * @param {*} $location \r\n * @param {DatabusCollectionManager} collectionManager \r\n * @returns \r\n */\r\nfunction AccountPageController($scope, $http, $location, collectionManager) {\r\n\r\n $scope.collectionManager = collectionManager;\r\n\r\n\r\n // Pick up the profile data\r\n $scope.auth = data.auth;\r\n $scope.location = $location;\r\n $scope.account = data.account;\r\n\r\n // Exit if there is no profile\r\n if ($scope.account == undefined) {\r\n return;\r\n }\r\n\r\n\r\n // Create a tab navigation object for the tab navigation with locato\r\n $scope.tabNavigation = new TabNavigation($scope, $location, [\r\n 'data', 'collections', 'settings'\r\n ]);\r\n\r\n // Make some util functions available in the template\r\n $scope.utils = new DatabusWebappUtils($scope);\r\n $scope.accountName = $scope.utils.getAccountName();\r\n $scope.account.isOwn = $scope.accountName != null; //.auth.authenticated && $scope.auth.info.accountName == $scope.account.accountName;\r\n\r\n\r\n $scope.dataSearchInput = '';\r\n $scope.dataSearchSettings = {\r\n minRelevance: 0.01,\r\n maxResults: 10,\r\n placeholder: `Search ${$scope.account.accountName}'s data...`,\r\n resourceTypes: ['Group', 'Artifact'],\r\n filter: `&publisher=${$scope.account.accountName}&typeNameWeight=0`\r\n };\r\n\r\n $scope.collectionSearchInput = '';\r\n $scope.collectionSearchSettings = {\r\n minRelevance: 0.01,\r\n maxResults: 10,\r\n placeholder: `Search ${$scope.account.accountName}'s collections...`,\r\n resourceTypes: ['Collection'],\r\n filter: `&publisher=${$scope.account.accountName}&publisherWeight=0&typeNameWeight=0`\r\n };\r\n\r\n\r\n // Wait for additional artifact data to arrive\r\n $scope.publishedData = {};\r\n $scope.publishedData.isLoading = true;\r\n\r\n $http.get(`/app/account/content?account=${encodeURIComponent($scope.account.accountName)}`)\r\n .then(function (response) {\r\n\r\n $scope.publishedData.isLoading = false;\r\n $scope.publishedData.groups = response.data.groups;\r\n $scope.publishedData.artifacts = response.data.artifacts;\r\n\r\n for (var artifact of $scope.publishedData.artifacts) {\r\n artifact.group = DatabusUtils.navigateUp(artifact.uri, 1);\r\n artifact.title = DatabusUtils.stringOrFallback(artifact.title, artifact.latestVersionTitle);\r\n artifact.abstract = DatabusUtils.stringOrFallback(artifact.abstract, artifact.latestVersionAbstract);\r\n artifact.description = DatabusUtils.stringOrFallback(artifact.description, artifact.latestVersionDescription);\r\n }\r\n\r\n for (var group of $scope.publishedData.groups) {\r\n group.artifacts = $scope.publishedData.artifacts.filter(function (a) {\r\n return a.group == group.uri;\r\n });\r\n }\r\n\r\n // Order by latest version date\r\n $scope.recentUploads = $scope.publishedData.artifacts.filter(function (v) {\r\n return v.latestVersionDate != null;\r\n });\r\n $scope.recentUploads.sort(function (a, b) {\r\n return new Date(b.latestVersionDate) - new Date(a.latestVersionDate);\r\n });\r\n\r\n $scope.recentUploads = $scope.recentUploads.slice(0, 3);\r\n\r\n $scope.refreshFeaturedContent();\r\n }, function (err) {\r\n console.log(err);\r\n });\r\n\r\n\r\n // Wait for stats data to arrive\r\n $scope.statsData = {};\r\n $scope.statsData.isLoading = true;\r\n\r\n $http.get(`/app/account/stats?account=${encodeURIComponent($scope.account.accountName)}`).then(function (response) {\r\n $scope.statsData.stats = response.data;\r\n $scope.statsData.isLoading = false;\r\n }, function (err) {\r\n console.log(err);\r\n });\r\n\r\n // Wait for activity chart data to arrive\r\n $scope.activityData = {};\r\n $scope.activityData.isLoading = true;\r\n\r\n $http.get(`/app/account/activity?account=${encodeURIComponent($scope.account.accountName)}`).then(function (response) {\r\n $scope.activityData.entries = response.data;\r\n $scope.activityData.isLoading = false;\r\n }, function (err) {\r\n console.log(err);\r\n });\r\n\r\n $scope.collectionsData = {};\r\n $scope.collectionsData.isLoading = true;\r\n\r\n if (!$scope.account.isOwn) {\r\n $http.get(`/app/account/collections?account=${encodeURIComponent($scope.account.accountName)}`)\r\n .then(function (response) {\r\n\r\n $scope.collectionsData.collections = response.data;\r\n $scope.collectionsData.isLoading = false;\r\n $scope.refreshFeaturedContent();\r\n }, function (err) {\r\n console.log(err);\r\n });\r\n } else {\r\n\r\n function onCollectionManagerInitialized() {\r\n for (let guid in $scope.collectionManager.local) {\r\n let collection = $scope.collectionManager.local[guid];\r\n\r\n if(collection.accountName == undefined && collection.uri != undefined) {\r\n collection.accountName = DatabusUtils.getFirstSegment(collection.uri);\r\n }\r\n\r\n if (collection.accountName == $scope.accountName) {\r\n $scope.collectionList.push(collection);\r\n }\r\n }\r\n }\r\n\r\n $scope.collectionList = [];\r\n\r\n if(collectionManager.isInitialized) {\r\n onCollectionManagerInitialized();\r\n } else {\r\n collectionManager.subscribeOnInitialized(onCollectionManagerInitialized);\r\n }\r\n }\r\n\r\n\r\n\r\n $scope.getImageUrl = function () {\r\n if ($scope.account.imageUrl == undefined) {\r\n return DEFAULT_IMAGE;\r\n } else {\r\n return $scope.account.imageUrl;\r\n }\r\n }\r\n\r\n /**\r\n * COLLECTION FUNCTIONS \r\n */\r\n\r\n // Collection List Search\r\n $scope.collectionSearch = {};\r\n $scope.collectionSearch.sortVisible = false;\r\n $scope.collectionSearch.sortProperty = 'title';\r\n $scope.collectionSearch.sortProperties = [\r\n { key: 'title', label: 'Title' },\r\n { key: 'issued', label: 'Issued Date' },\r\n ];\r\n $scope.collectionSearch.sortReverse = false;\r\n $scope.collectionSearch.toggleSort = function (value) {\r\n if ($scope.collectionSearch.sortProperty == value) {\r\n $scope.collectionSearch.sortReverse = !$scope.collectionSearch.sortReverse;\r\n } else {\r\n $scope.collectionSearch.sortProperty = value;\r\n }\r\n }\r\n\r\n /**\r\n * Pencil icon for edit pressed\r\n * @param {*} collection \r\n */\r\n $scope.onEditCollectionClicked = function (collection) {\r\n $scope.collectionManager.setActive(collection.uuid);\r\n window.location.href = `/app/collection-editor?uuid=${collection.uuid}`;\r\n }\r\n\r\n /**\r\n * Create new collection\r\n */\r\n $scope.createNewCollection = function () {\r\n $scope.collectionManager.createNew($scope.accountName, 'New Collection', 'Replace this description with a description of your choice.',\r\n function (collection) {\r\n window.location.href = `/app/collection-editor?uuid=${collection.uuid}`;\r\n });\r\n }\r\n\r\n /**\r\n * Create a copy of the clicked collection\r\n */\r\n $scope.createCopy = function (collection) {\r\n let copy = $scope.collectionManager.createCopy(collection);\r\n window.location.href = `/app/collection-editor?uuid=${copy.uuid}`;\r\n }\r\n\r\n\r\n $scope.findFeaturedContent = function (uri) {\r\n\r\n for (var g in $scope.publishedData.groups) {\r\n var group = $scope.publishedData.groups[g];\r\n\r\n if (uri == group.uri) {\r\n return {\r\n type: 'Group',\r\n title: group.title,\r\n uri: uri,\r\n description: group.description\r\n }\r\n }\r\n\r\n for (var a in group.artifacts) {\r\n var artifact = group.artifacts[a];\r\n\r\n if (uri == artifact.artifactUri) {\r\n return {\r\n type: 'Artifact',\r\n title: artifact.title,\r\n uri: uri,\r\n description: artifact.description\r\n }\r\n }\r\n }\r\n }\r\n\r\n for (var c in $scope.collectionsData.collections) {\r\n var collection = $scope.collectionsData.collections[c];\r\n\r\n if (uri == collection.uri) {\r\n return {\r\n type: 'Collection',\r\n title: collection.title,\r\n uri: uri,\r\n description: collection.description\r\n }\r\n }\r\n }\r\n\r\n }\r\n\r\n $scope.refreshFeaturedContent = function () {\r\n if ($scope.account.featuredContent == undefined) {\r\n return;\r\n }\r\n\r\n var featuredContentUris = $scope.account.featuredContent.split('\\n');\r\n $scope.featuredContent = [];\r\n\r\n for (var f in featuredContentUris) {\r\n var content = $scope.findFeaturedContent(featuredContentUris[f]);\r\n\r\n if (content != undefined) {\r\n $scope.featuredContent.push(content);\r\n }\r\n }\r\n }\r\n\r\n /** ACCOUNT MANAGEMENT FOR OWNER */\r\n\r\n}\r\n\r\nmodule.exports = AccountPageController;\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/account-controller.js?"); /***/ }), @@ -315,7 +355,7 @@ eval("const DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \". \***************************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("const DatabusCollectionWrapper = __webpack_require__(/*! ../collections/databus-collection-wrapper */ \"./js/collections/databus-collection-wrapper.js\");\nconst DatabusAlert = __webpack_require__(/*! ../components/databus-alert/databus-alert */ \"./js/components/databus-alert/databus-alert.js\");\nconst DataIdCreator = __webpack_require__(/*! ../publish/dataid-creator */ \"./js/publish/dataid-creator.js\");\nconst QueryBuilder = __webpack_require__(/*! ../query-builder/query-builder */ \"./js/query-builder/query-builder.js\");\nconst QueryNode = __webpack_require__(/*! ../query-builder/query-node */ \"./js/query-builder/query-node.js\");\nconst QueryTemplates = __webpack_require__(/*! ../query-builder/query-templates */ \"./js/query-builder/query-templates.js\");\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\nconst DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\nconst TabNavigation = __webpack_require__(/*! ../utils/tab-navigation */ \"./js/utils/tab-navigation.js\");\n\n// hinzufügen eines Controllers zum Modul\nfunction ArtifactPageController($scope, $http, $sce, $location, collectionManager) {\n\n $scope.collectionManager = collectionManager;\n $scope.authenticated = data.auth.authenticated;\n $scope.utils = new DatabusWebappUtils($scope, $sce);\n $scope.tabNavigation = new TabNavigation($scope, $location, [\n 'files', 'versions', 'edit'\n ]);\n\n $scope.versions = data.versions;\n $scope.artifact = data.artifact;\n $scope.accountName = DatabusUtils.uriToName(DatabusUtils.navigateUp($scope.artifact.uri, 2));\n $scope.canEdit = $scope.accountName == data.auth.info.accountName;\n $scope.pageTitle = DatabusUtils.stringOrFallback($scope.artifact.title,\n DatabusUtils.uriToTitle($scope.artifact.uri));\n\n if (data.auth.authenticated && $scope.canEdit) {\n\n $scope.formData = {};\n $scope.formData.group = {};\n $scope.formData.group.name = DatabusUtils.uriToName(DatabusUtils.navigateUp($scope.artifact.uri));\n $scope.formData.artifact = {};\n\n var abstract = DatabusUtils.createAbstractFromDescription($scope.artifact.description);\n $scope.formData.artifact.generateAbstract = abstract == $scope.artifact.abstract;\n $scope.formData.artifact.name = $scope.artifact.name;\n $scope.formData.artifact.title = $scope.artifact.title;\n $scope.formData.artifact.abstract = $scope.artifact.abstract;\n $scope.formData.artifact.description = $scope.artifact.description;\n\n $scope.dataidCreator = new DataIdCreator($scope.formData, data.auth.info.accountName);\n }\n\n $scope.fileSelector = {};\n $scope.fileSelector.config = {};\n $scope.fileSelector.config.columns = [];\n $scope.fileSelector.config.columns.push({ field: 'version', label: 'Version', width: '30%' });\n $scope.fileSelector.config.columns.push({ field: 'variant', label: 'Variant', width: '30%' });\n $scope.fileSelector.config.columns.push({ field: 'format', label: 'Format', width: '12%' });\n $scope.fileSelector.config.columns.push({ field: 'compression', label: 'Compression', width: '12%' });\n\n $scope.artifactNode = new QueryNode($scope.artifact.uri, 'databus:artifact');\n $scope.artifactNode.setFacet('http://purl.org/dc/terms/hasVersion', '$latest', true);\n\n $scope.groupNode = new QueryNode(DatabusUtils.navigateUp($scope.artifact.uri), 'databus:group');\n $scope.groupNode.addChild($scope.artifactNode);\n\n $scope.collectionWidgetSelectionData = {};\n $scope.collectionWidgetSelectionData.groupNode = $scope.groupNode;\n\n $scope.onFacetSettingsChanged = function () {\n $scope.fileSelector.query = QueryBuilder.build({\n node: $scope.artifactNode,\n template: QueryTemplates.DEFAULT_FILE_TEMPLATE,\n resourceBaseUrl: DATABUS_RESOURCE_BASE_URL\n });\n\n $scope.fileSelector.fullQuery = QueryBuilder.build({\n node: $scope.artifactNode,\n template: QueryTemplates.GROUP_PAGE_FILE_BROWSER_TEMPLATE,\n resourceBaseUrl: DATABUS_RESOURCE_BASE_URL\n });\n }\n\n $scope.onFacetSettingsChanged();\n\n\n $scope.onFileSelectionChanged = function (numFiles, totalSize) {\n $scope.fileSelector.numFiles = numFiles;\n $scope.fileSelector.totalSize = totalSize;\n };\n\n $scope.formatId = function (id) {\n return DatabusCollectionUtils.formatId(id);\n };\n\n $scope.addArtifactNodeToCollection = function () {\n\n if ($scope.collectionManager.activeCollection == null) {\n return;\n }\n\n var wrapper = new DatabusCollectionWrapper($scope.collectionManager.activeCollection);\n wrapper.addArtifactNode(\n $scope.artifact.uri,\n $scope.artifact.title,\n $scope.fileSelector.settings);\n\n $scope.collectionManager.saveLocally();\n $scope.statusCode = 1;\n };\n\n $scope.changeCollection = function (collection) {\n if (!$scope.authenticated) {\n return;\n }\n\n $scope.collectionManager.setActive(collection.uuid);\n }\n\n\n $scope.hideAutofill = function () {\n $scope.fileSelector.clearAutofill(function () {\n $scope.$apply();\n });\n }\n\n\n $scope.onDescriptionChanged = function () {\n if ($scope.formData == null) {\n return;\n }\n\n if (!$scope.formData.artifact.generateAbstract) {\n return;\n }\n\n $scope.formData.artifact.abstract =\n DatabusUtils.createAbstractFromDescription($scope.formData.artifact.description);\n }\n\n $scope.resetEdits = function () {\n $scope.formData.artifact.title = $scope.artifact.title;\n $scope.formData.artifact.abstract = $scope.artifact.abstract;\n $scope.formData.artifact.description = $scope.artifact.description;\n }\n\n $scope.saveArtifact = async function () {\n\n if ($scope.dataidCreator == null) {\n return;\n }\n\n var artifactUpdate = $scope.dataidCreator.createArtifactUpdate();\n\n var relativeUri = new URL($scope.artifact.uri).pathname;\n var response = await $http.put(relativeUri, artifactUpdate);\n\n if (response.status == 200) {\n $scope.artifact.title = $scope.formData.artifact.title;\n $scope.artifact.abstract = $scope.formData.artifact.abstract;\n $scope.artifact.description = $scope.formData.artifact.description;\n\n $scope.pageTitle = DatabusUtils.stringOrFallback($scope.artifact.title,\n DatabusUtils.uriToTitle($scope.artifact.uri));\n\n DatabusAlert.alert($scope, true, \"Artifact Saved!\");\n $scope.$apply();\n }\n }\n\n\n}\n\n\nmodule.exports = ArtifactPageController;\n\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/artifact-controller.js?"); +eval("const DatabusCollectionWrapper = __webpack_require__(/*! ../collections/databus-collection-wrapper */ \"./js/collections/databus-collection-wrapper.js\");\r\nconst DatabusAlert = __webpack_require__(/*! ../components/databus-alert/databus-alert */ \"./js/components/databus-alert/databus-alert.js\");\r\nconst DataIdCreator = __webpack_require__(/*! ../publish/dataid-creator */ \"./js/publish/dataid-creator.js\");\r\nconst QueryBuilder = __webpack_require__(/*! ../query-builder/query-builder */ \"./js/query-builder/query-builder.js\");\r\nconst QueryNode = __webpack_require__(/*! ../query-builder/query-node */ \"./js/query-builder/query-node.js\");\r\nconst QueryTemplates = __webpack_require__(/*! ../query-builder/query-templates */ \"./js/query-builder/query-templates.js\");\r\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\r\nconst DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\r\nconst TabNavigation = __webpack_require__(/*! ../utils/tab-navigation */ \"./js/utils/tab-navigation.js\");\r\n\r\n// hinzufügen eines Controllers zum Modul\r\nfunction ArtifactPageController($scope, $http, $sce, $location, collectionManager) {\r\n\r\n $scope.collectionManager = collectionManager;\r\n $scope.authenticated = data.auth.authenticated;\r\n $scope.auth = data.auth;\r\n $scope.utils = new DatabusWebappUtils($scope, $sce);\r\n $scope.accountName = $scope.utils.getAccountName();\r\n\r\n $scope.tabNavigation = new TabNavigation($scope, $location, [\r\n 'files', 'versions', 'edit'\r\n ]);\r\n\r\n $scope.versions = data.versions;\r\n $scope.artifact = data.artifact;\r\n $scope.accountName = DatabusUtils.uriToName(DatabusUtils.navigateUp($scope.artifact.uri, 2));\r\n $scope.canEdit = $scope.accountName != null;\r\n $scope.pageTitle = DatabusUtils.stringOrFallback($scope.artifact.title,\r\n DatabusUtils.uriToTitle($scope.artifact.uri));\r\n\r\n if (data.auth.authenticated && $scope.canEdit) {\r\n\r\n $scope.formData = {};\r\n $scope.formData.group = {};\r\n $scope.formData.group.name = DatabusUtils.uriToName(DatabusUtils.navigateUp($scope.artifact.uri));\r\n $scope.formData.artifact = {};\r\n\r\n var abstract = DatabusUtils.createAbstractFromDescription($scope.artifact.description);\r\n $scope.formData.artifact.generateAbstract = abstract == $scope.artifact.abstract;\r\n $scope.formData.artifact.name = $scope.artifact.name;\r\n $scope.formData.artifact.title = $scope.artifact.title;\r\n $scope.formData.artifact.abstract = $scope.artifact.abstract;\r\n $scope.formData.artifact.description = $scope.artifact.description;\r\n\r\n $scope.dataidCreator = new DataIdCreator($scope.formData, $scope.accountName);\r\n }\r\n\r\n $scope.fileSelector = {};\r\n $scope.fileSelector.config = {};\r\n $scope.fileSelector.config.authenticated = $scope.authenticated;\r\n $scope.fileSelector.config.columns = [];\r\n $scope.fileSelector.config.columns.push({ field: 'version', label: 'Version', width: '30%' });\r\n $scope.fileSelector.config.columns.push({ field: 'variant', label: 'Variant', width: '30%' });\r\n $scope.fileSelector.config.columns.push({ field: 'format', label: 'Format', width: '12%' });\r\n $scope.fileSelector.config.columns.push({ field: 'compression', label: 'Compression', width: '12%' });\r\n\r\n $scope.artifactNode = new QueryNode($scope.artifact.uri, 'databus:artifact');\r\n $scope.artifactNode.setFacet('http://purl.org/dc/terms/hasVersion', '$latest', true);\r\n\r\n $scope.groupNode = new QueryNode(DatabusUtils.navigateUp($scope.artifact.uri), 'databus:group');\r\n $scope.groupNode.addChild($scope.artifactNode);\r\n\r\n $scope.collectionWidgetSelectionData = {};\r\n $scope.collectionWidgetSelectionData.groupNode = $scope.groupNode;\r\n\r\n $scope.onFacetSettingsChanged = function () {\r\n $scope.fileSelector.query = QueryBuilder.build({\r\n node: $scope.artifactNode,\r\n template: QueryTemplates.DEFAULT_FILE_TEMPLATE,\r\n resourceBaseUrl: DATABUS_RESOURCE_BASE_URL\r\n });\r\n\r\n $scope.fileSelector.fullQuery = QueryBuilder.build({\r\n node: $scope.artifactNode,\r\n template: QueryTemplates.GROUP_PAGE_FILE_BROWSER_TEMPLATE,\r\n resourceBaseUrl: DATABUS_RESOURCE_BASE_URL\r\n });\r\n }\r\n\r\n $scope.onFacetSettingsChanged();\r\n\r\n\r\n $scope.onFileSelectionChanged = function (numFiles, totalSize) {\r\n $scope.fileSelector.numFiles = numFiles;\r\n $scope.fileSelector.totalSize = totalSize;\r\n };\r\n\r\n $scope.formatId = function (id) {\r\n return DatabusCollectionUtils.formatId(id);\r\n };\r\n\r\n $scope.addArtifactNodeToCollection = function () {\r\n\r\n if ($scope.collectionManager.activeCollection == null) {\r\n return;\r\n }\r\n\r\n var wrapper = new DatabusCollectionWrapper($scope.collectionManager.activeCollection);\r\n wrapper.addArtifactNode(\r\n $scope.artifact.uri,\r\n $scope.artifact.title,\r\n $scope.fileSelector.settings);\r\n\r\n $scope.collectionManager.saveLocally();\r\n $scope.statusCode = 1;\r\n };\r\n\r\n $scope.changeCollection = function (collection) {\r\n if (!$scope.authenticated) {\r\n return;\r\n }\r\n\r\n $scope.collectionManager.setActive(collection.uuid);\r\n }\r\n\r\n\r\n $scope.hideAutofill = function () {\r\n $scope.fileSelector.clearAutofill(function () {\r\n $scope.$apply();\r\n });\r\n }\r\n\r\n\r\n $scope.onDescriptionChanged = function () {\r\n if ($scope.formData == null) {\r\n return;\r\n }\r\n\r\n if (!$scope.formData.artifact.generateAbstract) {\r\n return;\r\n }\r\n\r\n $scope.formData.artifact.abstract =\r\n DatabusUtils.createAbstractFromDescription($scope.formData.artifact.description);\r\n }\r\n\r\n $scope.resetEdits = function () {\r\n $scope.formData.artifact.title = $scope.artifact.title;\r\n $scope.formData.artifact.abstract = $scope.artifact.abstract;\r\n $scope.formData.artifact.description = $scope.artifact.description;\r\n }\r\n\r\n $scope.saveArtifact = async function () {\r\n\r\n if ($scope.dataidCreator == null) {\r\n return;\r\n }\r\n\r\n var artifactUpdate = $scope.dataidCreator.createArtifactUpdate();\r\n\r\n var response = await $http.post(`/api/register`, artifactUpdate);\r\n\r\n if (response.status == 200) {\r\n $scope.artifact.title = $scope.formData.artifact.title;\r\n $scope.artifact.abstract = $scope.formData.artifact.abstract;\r\n $scope.artifact.description = $scope.formData.artifact.description;\r\n\r\n $scope.pageTitle = DatabusUtils.stringOrFallback($scope.artifact.title,\r\n DatabusUtils.uriToTitle($scope.artifact.uri));\r\n\r\n DatabusAlert.alert($scope, true, \"Artifact Saved!\");\r\n $scope.$apply();\r\n }\r\n }\r\n\r\n\r\n}\r\n\r\n\r\nmodule.exports = ArtifactPageController;\r\n\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/artifact-controller.js?"); /***/ }), @@ -325,7 +365,7 @@ eval("const DatabusCollectionWrapper = __webpack_require__(/*! ../collections/da \*****************************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("const DatabusCollectionUtils = __webpack_require__(/*! ../collections/databus-collection-utils */ \"./js/collections/databus-collection-utils.js\");\nconst DatabusCollectionWrapper = __webpack_require__(/*! ../collections/databus-collection-wrapper */ \"./js/collections/databus-collection-wrapper.js\");\nconst DatabusAlert = __webpack_require__(/*! ../components/databus-alert/databus-alert */ \"./js/components/databus-alert/databus-alert.js\");\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\nconst DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\n\nfunction CollectionController($scope, $sce, $http, collectionManager) {\n\n $scope.collection = new DatabusCollectionWrapper(data.collection);\n $scope.authenticated = data.auth.authenticated;\n $scope.activeTab = 0;\n $scope.collectionManager = collectionManager;\n\n // Make some util functions available in the template\n $scope.utils = new DatabusWebappUtils($scope, $sce);\n\n $scope.isOwn = false;\n\n if ($scope.authenticated) {\n\n $scope.collectionAccountName = DatabusUtils.uriToName(DatabusUtils.navigateUp($scope.collection.uri, 2));\n $scope.accountName = data.auth.info.accountName;\n\n $scope.isOwn = $scope.accountName === $scope.collectionAccountName;\n }\n\n $scope.editCollection = function () {\n $scope.collectionManager.setActive($scope.collection.uuid);\n window.location = `/app/collection-editor`;\n }\n\n $scope.collectionViewModel = {};\n $scope.collectionViewModel.downloadScript = [];\n $scope.collectionViewModel.downloadScript.length = 3;\n $scope.collectionViewModel.downloadScript[0] = `query=$(curl -H \"Accept:text/sparql\" ${$scope.collection.uri})`;\n $scope.collectionViewModel.downloadScript[1] = `files=$(curl -X POST -H \"Accept: text/csv\" --data-urlencode \"query=\\${query}\" ${DATABUS_RESOURCE_BASE_URL}/sparql | tail -n +2 | sed 's/\\\\r$//' | sed 's/\"//g')`;\n $scope.collectionViewModel.downloadScript[2] = `while IFS= read -r file ; do wget $file; done <<< \"$files\"`;\n\n $scope.collectionViewModel.downloadManual = 'To fetch the query via *curl* run \\n``` shell\\n'\n + $scope.collectionViewModel.downloadScript[0] + '\\n```'\n + '\\n\\n\\nTo download the files additionally run\\n``` shell\\n'\n + $scope.collectionViewModel.downloadScript[1] + '\\n'\n + $scope.collectionViewModel.downloadScript[2]\n + '\\n```';\n\n $scope.collectionQuery = $scope.collection.createQuery();\n $scope.collectionManager = collectionManager;\n $scope.collectionFiles = \"\";\n\n\n DatabusCollectionUtils.getCollectionFileURLs($http, $scope.collection).then(function (result) {\n $scope.collectionFiles = result;\n $scope.$apply();\n }, function (err) {\n console.log(err);\n });\n\n\n if ($scope.authenticated) {\n $scope.username = data.auth.info.username;\n }\n\n $scope.formatUploadSize = function (size) {\n return DatabusUtils.formatFileSize(size);\n };\n\n\n\n $scope.editCopy = function () {\n if (!$scope.collectionManager.isInitialized) {\n return;\n }\n\n let localCopy = $scope.collectionManager.createCopy($scope.collection);\n\n $scope.collectionManager.setActive(localCopy.uuid);\n window.location.href = '/app/collection-editor'\n }\n\n $scope.createSnapshot = function () {\n if (!$scope.collectionManager.isInitialized) {\n return;\n }\n\n\n let collectionSnapshot = $scope.collectionManager.createSnapshot($scope.collection);\n\n $scope.collectionManager.setActive(collectionSnapshot.uuid);\n window.location.href = '/app/collection-editor'\n }\n\n $scope.editCollection = function () {\n\n if (!$scope.collectionManager.isInitialized) {\n return;\n }\n\n let localCopy = $scope.collectionManager.getCollectionByUri($scope.collection.uri);\n\n /// TODO Fabian - das sollte nicht passieren!\n if (localCopy === null) {\n console.log(\"editCollection failed due there is no collection with that uri: \" + $scope.collection.uri)\n $scope.editCopy();\n return;\n }\n\n $scope.collectionManager.setActive(localCopy.uuid);\n window.location.href = '/app/collection-editor'\n }\n\n $scope.downloadAsJson = function () {\n DatabusCollectionUtils.exportToJsonFile($scope.collection);\n }\n\n $scope.queryToClipboard = function () {\n\n $scope.utils.copyToClipboard($scope.collectionQuery);\n DatabusAlert.alert($scope, true, DatabusMessages.GENERIC_COPIED_TO_CLIPBOARD);\n\n }\n\n $scope.openInYasgui = function () {\n window.location.href = 'https://databus.dbpedia.org/yasgui?query=' + encodeURIComponent($scope.collectionQuery);\n }\n\n $scope.bashScriptToClipboard = function () {\n\n var bashscript = `${$scope.collectionViewModel.downloadScript[0]}\n${$scope.collectionViewModel.downloadScript[1]}\n${$scope.collectionViewModel.downloadScript[2]}`\n\n $scope.utils.copyToClipboard(bashscript);\n DatabusAlert.alert($scope, true, DatabusMessages.GENERIC_COPIED_TO_CLIPBOARD);\n }\n\n\n $scope.filesToClipboard = function () {\n $scope.utils.copyToClipboard($scope.collectionFiles);\n DatabusAlert.alert($scope, true, DatabusMessages.GENERIC_COPIED_TO_CLIPBOARD);\n }\n\n}\n\nmodule.exports = CollectionController;\n\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/collection-controller.js?"); +eval("const DatabusCollectionUtils = __webpack_require__(/*! ../collections/databus-collection-utils */ \"./js/collections/databus-collection-utils.js\");\r\nconst DatabusCollectionWrapper = __webpack_require__(/*! ../collections/databus-collection-wrapper */ \"./js/collections/databus-collection-wrapper.js\");\r\nconst DatabusAlert = __webpack_require__(/*! ../components/databus-alert/databus-alert */ \"./js/components/databus-alert/databus-alert.js\");\r\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\r\nconst DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\r\n\r\nfunction CollectionController($scope, $sce, $http, collectionManager) {\r\n\r\n $scope.auth = data.auth;\r\n $scope.collection = new DatabusCollectionWrapper(data.collection);\r\n $scope.authenticated = data.auth.authenticated;\r\n $scope.activeTab = 0;\r\n $scope.collectionManager = collectionManager;\r\n\r\n // Make some util functions available in the template\r\n $scope.utils = new DatabusWebappUtils($scope, $sce);\r\n $scope.accountName = $scope.utils.getAccountName();\r\n\r\n $scope.isOwn = false;\r\n\r\n if ($scope.authenticated) {\r\n $scope.collectionAccountName = DatabusUtils.uriToName(DatabusUtils.navigateUp($scope.collection.uri, 2));\r\n $scope.isOwn = $scope.accountName === $scope.collectionAccountName;\r\n }\r\n\r\n\r\n $scope.collectionViewModel = {};\r\n $scope.collectionViewModel.downloadScript = [];\r\n $scope.collectionViewModel.downloadScript.length = 3;\r\n $scope.collectionViewModel.downloadScript[0] = `query=$(curl -H \"Accept:text/sparql\" ${$scope.collection.uri})`;\r\n $scope.collectionViewModel.downloadScript[1] = `files=$(curl -X POST -H \"Accept: text/csv\" --data-urlencode \"query=\\${query}\" ${DATABUS_RESOURCE_BASE_URL}/sparql | tail -n +2 | sed 's/\\\\r$//' | sed 's/\"//g')`;\r\n $scope.collectionViewModel.downloadScript[2] = `while IFS= read -r file ; do wget $file; done <<< \"$files\"`;\r\n\r\n $scope.collectionViewModel.downloadManual = 'To fetch the query via *curl* run \\n``` shell\\n'\r\n + $scope.collectionViewModel.downloadScript[0] + '\\n```'\r\n + '\\n\\n\\nTo download the files additionally run\\n``` shell\\n'\r\n + $scope.collectionViewModel.downloadScript[1] + '\\n'\r\n + $scope.collectionViewModel.downloadScript[2]\r\n + '\\n```';\r\n\r\n $scope.collectionQuery = $scope.collection.createQuery();\r\n $scope.collectionManager = collectionManager;\r\n $scope.collectionFiles = \"\";\r\n\r\n\r\n DatabusCollectionUtils.getCollectionFileURLs($http, $scope.collection).then(function (result) {\r\n $scope.collectionFiles = result;\r\n $scope.$apply();\r\n }, function (err) {\r\n console.log(err);\r\n });\r\n\r\n\r\n if ($scope.authenticated) {\r\n $scope.username = data.auth.info.username;\r\n }\r\n\r\n $scope.formatUploadSize = function (size) {\r\n return DatabusUtils.formatFileSize(size);\r\n };\r\n\r\n\r\n\r\n $scope.editCopy = function () {\r\n if (!$scope.collectionManager.isInitialized) {\r\n return;\r\n }\r\n\r\n let localCopy = $scope.collectionManager.createCopy($scope.collection);\r\n\r\n window.location.href = `/app/collection-editor?uuid${localCopy.uuid}`;\r\n }\r\n\r\n $scope.createSnapshot = function () {\r\n if (!$scope.collectionManager.isInitialized) {\r\n return;\r\n }\r\n\r\n\r\n let collectionSnapshot = $scope.collectionManager.createSnapshot($scope.collection);\r\n window.location.href = `/app/collection-editor?uuid${collectionSnapshot.uuid}`;\r\n }\r\n\r\n \r\n $scope.editCollection = function () {\r\n\r\n if (!$scope.collectionManager.isInitialized) {\r\n return;\r\n }\r\n\r\n let localCopy = $scope.collectionManager.getCollectionByUri($scope.collection.uri);\r\n\r\n /// TODO Fabian - das sollte nicht passieren!\r\n if (localCopy === null) {\r\n console.log(\"editCollection failed. There is no collection with that uri: \" + $scope.collection.uri)\r\n $scope.editCopy();\r\n return;\r\n }\r\n\r\n window.location.href = `/app/collection-editor?uuid=${localCopy.uuid}`;\r\n }\r\n\r\n\r\n $scope.downloadAsJson = function () {\r\n DatabusCollectionUtils.exportToJsonFile($scope.collection);\r\n }\r\n\r\n $scope.queryToClipboard = function () {\r\n\r\n $scope.utils.copyToClipboard($scope.collectionQuery);\r\n DatabusAlert.alert($scope, true, DatabusMessages.GENERIC_COPIED_TO_CLIPBOARD);\r\n\r\n }\r\n\r\n $scope.openInYasgui = function () {\r\n window.location.href = 'https://databus.dbpedia.org/yasgui?query=' + encodeURIComponent($scope.collectionQuery);\r\n }\r\n\r\n $scope.bashScriptToClipboard = function () {\r\n\r\n var bashscript = `${$scope.collectionViewModel.downloadScript[0]}\r\n${$scope.collectionViewModel.downloadScript[1]}\r\n${$scope.collectionViewModel.downloadScript[2]}`\r\n\r\n $scope.utils.copyToClipboard(bashscript);\r\n DatabusAlert.alert($scope, true, DatabusMessages.GENERIC_COPIED_TO_CLIPBOARD);\r\n }\r\n\r\n\r\n $scope.filesToClipboard = function () {\r\n $scope.utils.copyToClipboard($scope.collectionFiles);\r\n DatabusAlert.alert($scope, true, DatabusMessages.GENERIC_COPIED_TO_CLIPBOARD);\r\n }\r\n\r\n}\r\n\r\nmodule.exports = CollectionController;\r\n\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/collection-controller.js?"); /***/ }), @@ -335,7 +375,7 @@ eval("const DatabusCollectionUtils = __webpack_require__(/*! ../collections/data \*************************************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("const DatabusCollectionUtils = __webpack_require__(/*! ../collections/databus-collection-utils */ \"./js/collections/databus-collection-utils.js\");\nconst DatabusCollectionWrapper = __webpack_require__(/*! ../collections/databus-collection-wrapper */ \"./js/collections/databus-collection-wrapper.js\");\nconst DatabusAlert = __webpack_require__(/*! ../components/databus-alert/databus-alert */ \"./js/components/databus-alert/databus-alert.js\");\nconst QueryNode = __webpack_require__(/*! ../query-builder/query-node */ \"./js/query-builder/query-node.js\");\nconst DatabusMessages = __webpack_require__(/*! ../utils/databus-messages */ \"./js/utils/databus-messages.js\");\nconst DatabusUris = __webpack_require__(/*! ../utils/databus-uris */ \"./js/utils/databus-uris.js\");\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\nconst DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\nconst TabNavigation = __webpack_require__(/*! ../utils/tab-navigation */ \"./js/utils/tab-navigation.js\");\n\n/**\n * Controls the collection editor page\n * @param {*} $scope \n * @param {*} $timeout \n * @param {*} $http \n * @param {*} $location \n * @param {*} collectionManager \n * @returns \n */\nfunction CollectionsEditorController($scope, $timeout, $http, $location, collectionManager) {\n\n $scope.authenticated = data.auth.authenticated;\n $scope.baseUrl = DATABUS_RESOURCE_BASE_URL;\n\n // Check for proper authentication\n if (!$scope.authenticated) {\n return;\n }\n\n $scope.accountName = data.auth.info.accountName;\n $scope.hasAccount = $scope.accountName != undefined;\n\n if (!$scope.hasAccount) {\n return;\n }\n\n // Create a tab navigation object for the tab navigation with locato\n $scope.tabNavigation = new TabNavigation($scope, $location, [\n 'docu', 'content', 'preview', 'query', 'json', 'import'\n ]);\n\n // Make some util functions available in the template\n $scope.utils = new DatabusWebappUtils($scope);\n\n // Make the manager available in the template\n $scope.collectionManager = collectionManager;\n\n // Form data object for input errors and extra fields and toggles\n $scope.form = {};\n $scope.form.title = {};\n $scope.form.identifier = {};\n $scope.form.identifier.value = \"\";\n $scope.form.abstract = {};\n $scope.form.description = {};\n $scope.form.isHidden = $scope.collectionManager.activeCollection.issued == undefined;\n $scope.form.collectionPublishTag = '';\n var description = $scope.collectionManager.activeCollection.description;\n var generatedAbstract = DatabusUtils.createAbstractFromDescription(description);\n $scope.form.generateAbstract = $scope.collectionManager.activeCollection.abstract == generatedAbstract;\n\n /**\n * Triggered when the description field gets changed.\n * Generates an abstract from the description. \n * @returns \n */\n $scope.onDescriptionChanged = function () {\n if ($scope.form == null) {\n return;\n }\n\n if ($scope.form.generateAbstract) {\n var description = $scope.collectionManager.activeCollection.description;\n var generatedAbstract = DatabusUtils.createAbstractFromDescription(description);\n $scope.collectionManager.activeCollection.abstract = generatedAbstract;\n }\n\n // Triggers saving to the local storage\n $scope.onActiveCollectionChanged();\n }\n\n\n /**\n * Called whenever an input field or similar gets changed. Persists the local changes in the local storage\n */\n $scope.onActiveCollectionChanged = function () {\n\n let collection = $scope.collectionManager.activeCollection;\n\n // Save to storage\n if ($scope.collectionManager.isInitialized) {\n $scope.collectionManager.saveLocally();\n }\n\n // Refresh query and json representation\n $scope.collectionQuery = new DatabusCollectionWrapper(collection).createQuery();\n $scope.collectionJson = $scope.getCollectionJson();\n\n if (collection != null) {\n collection.hasLocalChanges = $scope.collectionManager.hasLocalChanges(collection);\n }\n\n DatabusCollectionUtils.checkCollectionForm($scope.form, collection)\n }\n\n $scope.getStatusMessage = function (code) {\n return DatabusResponse.Message[code];\n }\n\n $scope.getStatusSuccess = function () {\n return $scope.statusCode >= 2000 && $scope.statusCode < 3000;\n }\n\n $scope.resetStatus = function () {\n $scope.statusCode = 0;\n }\n\n $scope.preview = function () {\n if ($scope.collectionManager.activeCollection.isDraft) {\n return;\n }\n\n var identifier = DatabusUtils.uriToName($scope.collectionManager.activeCollection.uri);\n window.location.href = `/${$scope.accountName}/collections/${identifier}`;\n }\n\n /**\n * Saves the collection to the remote server\n * @returns \n */\n $scope.saveCollection = async function () {\n\n try {\n // Needs initialized CM\n if (!$scope.collectionManager.isInitialized) {\n return;\n }\n\n let collection = $scope.collectionManager.activeCollection;\n\n // Check whether the form values are correct\n if (!DatabusCollectionUtils.checkCollectionForm($scope.form, collection)) {\n return;\n }\n\n // Look for an existing identifier\n var identifier = undefined;\n\n // Either take the identifier from the form (draft) or the collection uri (published)\n if (collection.isDraft) {\n identifier = $scope.form.identifier.value;\n } else {\n identifier = DatabusUtils.uriToName($scope.collectionManager.activeCollection.uri);\n }\n\n $scope.isSaving = true;\n $scope.collectionManager.updateCollection($scope.accountName, identifier).then(function (response) {\n DatabusAlert.alert($scope, true, DatabusMessages.CEDIT_COLLECTION_SAVED);\n $scope.isSaving = false;\n $scope.$apply();\n }).catch(function (err) {\n console.log(err);\n DatabusAlert.alert($scope, false, DatabusMessages.CEDIT_COLLECTION_SAVE_FAILED);\n $scope.isSaving = false;\n $scope.$apply();\n });\n\n } catch (err) {\n console.log(err);\n DatabusAlert.alert($scope, false, err);\n }\n }\n\n $scope.unpublishCollection = async function () {\n\n if ($scope.collectionManager.activeCollection.isDraft) {\n return;\n }\n\n try {\n await $scope.collectionManager.unpublishActiveCollection();\n DatabusAlert.alert($scope, true, DatabusMessages.CEDIT_COLLECTION_UNPUBLISHED);\n } catch (err) {\n DatabusAlert.alert($scope, false, err);\n console.log(err);\n }\n }\n\n $scope.showDeleteModal = function () {\n $scope.deleteModalVisible = true;\n }\n\n $scope.hideDeleteModal = function () {\n $scope.deleteModalVisible = false;\n }\n\n $scope.deleteCollection = function () {\n if (!$scope.collectionManager.isInitialized) {\n return;\n }\n\n $scope.deleteModalVisible = false;\n\n $scope.collectionManager.deleteCollection($scope.username, $scope.form.identifier.value).then(function (response) {\n $scope.statusCode = response.code;\n $scope.collectionManager.selectFirstOrCreate();\n $scope.setActiveCollection($scope.collectionManager.activeCollection);\n $scope.$apply();\n $timeout($scope.resetStatus, $scope.modalTime);\n }).catch(function (err) {\n $scope.statusCode = err.code;\n $scope.$apply();\n $timeout($scope.resetStatus, $scope.modalTime);\n });\n }\n\n\n $scope.deleteLocally = function () {\n if (!$scope.collectionManager.isInitialized) {\n return;\n }\n\n if (!$scope.collectionManager.activeCollection.isDraft) {\n return;\n }\n\n $scope.collectionManager.deleteLocally();\n window.location.href = `/${$scope.accountName}/collections`;\n }\n\n $scope.downloadAsJson = function () {\n DatabusCollectionUtils.exportToJsonFile($scope.collectionManager.activeCollection);\n }\n\n /**\n * Discard local changes of the active collection and revert to the remote collection state\n * @returns \n */\n $scope.discardChanges = function () {\n\n if (!$scope.collectionManager.activeCollection.hasLocalChanges) {\n return;\n }\n\n if ($scope.collectionManager.activeCollection.isDraft) {\n return;\n }\n\n $scope.collectionManager.discardLocalChanges();\n DatabusAlert.alert($scope, true, DatabusMessages.CEDIT_LOCAL_CHANGES_DISCARDED);\n }\n\n\n $scope.showLoadFromJson = function () {\n $scope.isLoadFromJsonVisible = true;\n }\n\n $scope.hideLoadFromJson = function () {\n $scope.isLoadFromJsonVisible = false;\n }\n\n $scope.loadFromJsonString = '';\n\n $scope.loadFromJson = function (loadFromJsonString) {\n try {\n\n \n var toLoad = JSON.parse(loadFromJsonString);\n\n var target = $scope.collectionManager.activeCollection;\n\n if (toLoad.label != undefined) {\n target.title = toLoad.label;\n }\n\n if (toLoad.title != undefined) {\n target.title = toLoad.title;\n }\n\n target.description = toLoad.description;\n target.abstract = toLoad.abstract;\n\n if (toLoad.content.generatedQuery != undefined || toLoad.content.customQueries) {\n // Datbaus 1.0 Syntax detected\n var replacedJson = loadFromJsonString\n .replace(\"dataid:\", \"databus:\")\n .replace(\"http://dataid.dbpedia.org/ns/cv#\",\n DatabusUris.DATABUS_CONTENT_VARIANT_PREFIX);\n\n var toLoad = JSON.parse(replacedJson);\n var databusNode = new QueryNode(DATABUS_RESOURCE_BASE_URL, null);\n\n target.content.root = new QueryNode(null, null);\n target.content.root.addChild(databusNode);\n\n for(var groupNode of toLoad.content.generatedQuery.root.childNodes) {\n databusNode.addChild(groupNode);\n }\n\n for(var customNode of toLoad.content.customQueries) {\n\n var label = customNode.label;\n var query = customNode.query;\n\n databusNode.addChild(new QueryNode(label, query));\n }\n\n } else {\n target.content = toLoad.content;\n }\n\n DatabusAlert.alert($scope, true, DatabusMessages.CEDIT_COLLECTION_IMPORTED);\n $scope.isLoadFromJsonVisible = false;\n } catch (e) {\n $scope.statusCode = DatabusMessages.CEDIT_COLLECTION_IMPORT_FAILED;\n console.log(e);\n }\n }\n\n $scope.getCollectionJson = function () {\n var copy = DatabusCollectionUtils.createCleanCopy($scope.collectionManager.activeCollection);\n delete copy.uuid;\n return copy;\n }\n\n $scope.onActiveCollectionChanged();\n}\n\nmodule.exports = CollectionsEditorController;\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/collections-editor-controller.js?"); +eval("const DatabusCollectionUtils = __webpack_require__(/*! ../collections/databus-collection-utils */ \"./js/collections/databus-collection-utils.js\");\r\nconst DatabusCollectionWrapper = __webpack_require__(/*! ../collections/databus-collection-wrapper */ \"./js/collections/databus-collection-wrapper.js\");\r\nconst DatabusAlert = __webpack_require__(/*! ../components/databus-alert/databus-alert */ \"./js/components/databus-alert/databus-alert.js\");\r\nconst QueryNode = __webpack_require__(/*! ../query-builder/query-node */ \"./js/query-builder/query-node.js\");\r\nconst DatabusMessages = __webpack_require__(/*! ../utils/databus-messages */ \"./js/utils/databus-messages.js\");\r\nconst DatabusUris = __webpack_require__(/*! ../utils/databus-uris */ \"./js/utils/databus-uris.js\");\r\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\r\nconst DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\r\nconst TabNavigation = __webpack_require__(/*! ../utils/tab-navigation */ \"./js/utils/tab-navigation.js\");\r\n\r\n/**\r\n * Controls the collection editor page\r\n * @param {*} $scope \r\n * @param {*} $timeout \r\n * @param {*} $http \r\n * @param {*} $location \r\n * @param {*} collectionManager \r\n * @returns \r\n */\r\nasync function CollectionsEditorController($scope, $timeout, $http, $location, collectionManager) {\r\n\r\n $scope.auth = data.auth;\r\n $scope.authenticated = data.auth.authenticated;\r\n $scope.baseUrl = DATABUS_RESOURCE_BASE_URL;\r\n\r\n // Check for proper authentication\r\n if (!$scope.authenticated) {\r\n return;\r\n }\r\n\r\n const params = new URLSearchParams(window.location.search);\r\n $scope.uuid = params.get('uuid');\r\n\r\n // Make some util functions available in the template\r\n $scope.utils = new DatabusWebappUtils($scope);\r\n $scope.collectionManager = collectionManager;\r\n \r\n let collection = $scope.collectionManager.local[$scope.uuid];\r\n\r\n if(collection == null) {\r\n // No working copy found\r\n return;\r\n }\r\n\r\n try {\r\n let collection = $scope.collectionManager.local[$scope.uuid];\r\n await $scope.collectionManager.tryInitialize(collection.accountName);\r\n } catch(err) {\r\n\r\n }\r\n\r\n $scope.collectionManager.setActive($scope.uuid);\r\n let activeCollection = $scope.collectionManager.activeCollection;\r\n \r\n $scope.accountName = $scope.utils.getOwnedAccountName(activeCollection.accountName);\r\n $scope.hasAccount = $scope.accountName != undefined;\r\n\r\n //if (!$scope.hasAccount) {\r\n // return;\r\n //}\r\n\r\n // Create a tab navigation object for the tab navigation with locato\r\n $scope.tabNavigation = new TabNavigation($scope, $location, [\r\n 'docu', 'content', 'preview', 'query', 'json', 'import'\r\n ]);\r\n\r\n // Make the manager available in the template\r\n // $scope.collectionManager.setActiveCollection($scope.guid);\r\n\r\n // Form data object for input errors and extra fields and toggles\r\n $scope.form = {};\r\n $scope.form.title = {};\r\n $scope.form.identifier = {};\r\n $scope.form.identifier.value = \"\";\r\n $scope.form.abstract = {};\r\n $scope.form.description = {};\r\n $scope.form.isHidden = $scope.collectionManager.activeCollection.issued == undefined;\r\n $scope.form.collectionPublishTag = '';\r\n var description = $scope.collectionManager.activeCollection.description;\r\n var generatedAbstract = DatabusUtils.createAbstractFromDescription(description);\r\n $scope.form.generateAbstract = $scope.collectionManager.activeCollection.abstract == generatedAbstract;\r\n\r\n /**\r\n * Triggered when the description field gets changed.\r\n * Generates an abstract from the description. \r\n * @returns \r\n */\r\n $scope.onDescriptionChanged = function () {\r\n if ($scope.form == null) {\r\n return;\r\n }\r\n\r\n if ($scope.form.generateAbstract) {\r\n var description = $scope.collectionManager.activeCollection.description;\r\n var generatedAbstract = DatabusUtils.createAbstractFromDescription(description);\r\n $scope.collectionManager.activeCollection.abstract = generatedAbstract;\r\n }\r\n\r\n // Triggers saving to the local storage\r\n $scope.onActiveCollectionChanged();\r\n }\r\n\r\n\r\n /**\r\n * Called whenever an input field or similar gets changed. Persists the local changes in the local storage\r\n */\r\n $scope.onActiveCollectionChanged = function () {\r\n\r\n let collection = $scope.collectionManager.activeCollection;\r\n\r\n // Save to storage\r\n if ($scope.collectionManager.isInitialized) {\r\n $scope.collectionManager.saveLocally();\r\n }\r\n\r\n // Refresh query and json representation\r\n $scope.collectionQuery = new DatabusCollectionWrapper(collection).createQuery();\r\n $scope.collectionJson = $scope.getCollectionJson();\r\n\r\n if (collection != null) {\r\n collection.hasLocalChanges = $scope.collectionManager.hasLocalChanges(collection);\r\n }\r\n\r\n DatabusCollectionUtils.checkCollectionForm($scope.form, collection)\r\n }\r\n\r\n $scope.getStatusMessage = function (code) {\r\n return DatabusResponse.Message[code];\r\n }\r\n\r\n $scope.getStatusSuccess = function () {\r\n return $scope.statusCode >= 2000 && $scope.statusCode < 3000;\r\n }\r\n\r\n $scope.resetStatus = function () {\r\n $scope.statusCode = 0;\r\n }\r\n\r\n $scope.preview = function () {\r\n if ($scope.collectionManager.activeCollection.isDraft) {\r\n return;\r\n }\r\n\r\n var identifier = DatabusUtils.uriToName($scope.collectionManager.activeCollection.uri);\r\n window.location.href = `/${$scope.accountName}/collections/${identifier}`;\r\n }\r\n\r\n /**\r\n * Saves the collection to the remote server\r\n * @returns \r\n */\r\n $scope.saveCollection = async function () {\r\n\r\n try {\r\n // Needs initialized CM\r\n if (!$scope.collectionManager.isInitialized) {\r\n return;\r\n }\r\n\r\n let collection = $scope.collectionManager.activeCollection;\r\n\r\n // Check whether the form values are correct\r\n if (!DatabusCollectionUtils.checkCollectionForm($scope.form, collection)) {\r\n return;\r\n }\r\n\r\n // Look for an existing identifier\r\n var identifier = undefined;\r\n\r\n // Either take the identifier from the form (draft) or the collection uri (published)\r\n if (collection.isDraft) {\r\n identifier = $scope.form.identifier.value;\r\n } else {\r\n identifier = DatabusUtils.uriToName($scope.collectionManager.activeCollection.uri);\r\n }\r\n\r\n $scope.isSaving = true;\r\n $scope.collectionManager.updateCollection($scope.accountName, identifier).then(function (response) {\r\n DatabusAlert.alert($scope, true, DatabusMessages.CEDIT_COLLECTION_SAVED);\r\n $scope.isSaving = false;\r\n $scope.$apply();\r\n }).catch(function (err) {\r\n console.log(err);\r\n DatabusAlert.alert($scope, false, DatabusMessages.CEDIT_COLLECTION_SAVE_FAILED);\r\n $scope.isSaving = false;\r\n $scope.$apply();\r\n });\r\n\r\n } catch (err) {\r\n console.log(err);\r\n DatabusAlert.alert($scope, false, err);\r\n }\r\n }\r\n\r\n $scope.unpublishCollection = async function () {\r\n\r\n if ($scope.collectionManager.activeCollection.isDraft) {\r\n return;\r\n }\r\n\r\n try {\r\n await $scope.collectionManager.unpublishActiveCollection();\r\n DatabusAlert.alert($scope, true, DatabusMessages.CEDIT_COLLECTION_UNPUBLISHED);\r\n } catch (err) {\r\n DatabusAlert.alert($scope, false, err);\r\n console.log(err);\r\n }\r\n }\r\n\r\n $scope.showDeleteModal = function () {\r\n $scope.deleteModalVisible = true;\r\n }\r\n\r\n $scope.hideDeleteModal = function () {\r\n $scope.deleteModalVisible = false;\r\n }\r\n\r\n $scope.deleteCollection = function () {\r\n if (!$scope.collectionManager.isInitialized) {\r\n return;\r\n }\r\n\r\n $scope.deleteModalVisible = false;\r\n\r\n $scope.collectionManager.deleteCollection($scope.username, $scope.form.identifier.value).then(function (response) {\r\n $scope.statusCode = response.code;\r\n $scope.collectionManager.selectFirstOrCreate();\r\n $scope.setActiveCollection($scope.collectionManager.activeCollection);\r\n $scope.$apply();\r\n $timeout($scope.resetStatus, $scope.modalTime);\r\n }).catch(function (err) {\r\n $scope.statusCode = err.code;\r\n $scope.$apply();\r\n $timeout($scope.resetStatus, $scope.modalTime);\r\n });\r\n }\r\n\r\n\r\n $scope.deleteLocally = function () {\r\n if (!$scope.collectionManager.isInitialized) {\r\n return;\r\n }\r\n\r\n if (!$scope.collectionManager.activeCollection.isDraft) {\r\n return;\r\n }\r\n\r\n $scope.collectionManager.deleteLocally();\r\n window.location.href = `/${$scope.accountName}/collections`;\r\n }\r\n\r\n $scope.downloadAsJson = function () {\r\n DatabusCollectionUtils.exportToJsonFile($scope.collectionManager.activeCollection);\r\n }\r\n\r\n /**\r\n * Discard local changes of the active collection and revert to the remote collection state\r\n * @returns \r\n */\r\n $scope.discardChanges = function () {\r\n\r\n if (!$scope.collectionManager.activeCollection.hasLocalChanges) {\r\n return;\r\n }\r\n\r\n if ($scope.collectionManager.activeCollection.isDraft) {\r\n return;\r\n }\r\n\r\n $scope.collectionManager.discardLocalChanges();\r\n DatabusAlert.alert($scope, true, DatabusMessages.CEDIT_LOCAL_CHANGES_DISCARDED);\r\n }\r\n\r\n\r\n $scope.showLoadFromJson = function () {\r\n $scope.isLoadFromJsonVisible = true;\r\n }\r\n\r\n $scope.hideLoadFromJson = function () {\r\n $scope.isLoadFromJsonVisible = false;\r\n }\r\n\r\n $scope.loadFromJsonString = '';\r\n\r\n $scope.loadFromJson = function (loadFromJsonString) {\r\n try {\r\n\r\n \r\n var toLoad = JSON.parse(loadFromJsonString);\r\n\r\n var target = $scope.collectionManager.activeCollection;\r\n\r\n if (toLoad.label != undefined) {\r\n target.title = toLoad.label;\r\n }\r\n\r\n if (toLoad.title != undefined) {\r\n target.title = toLoad.title;\r\n }\r\n\r\n target.description = toLoad.description;\r\n target.abstract = toLoad.abstract;\r\n\r\n if (toLoad.content.generatedQuery != undefined || toLoad.content.customQueries) {\r\n // Datbaus 1.0 Syntax detected\r\n var replacedJson = loadFromJsonString\r\n .replace(\"dataid:\", \"databus:\")\r\n .replace(\"http://dataid.dbpedia.org/ns/cv#\",\r\n DatabusUris.DATABUS_CONTENT_VARIANT_PREFIX);\r\n\r\n var toLoad = JSON.parse(replacedJson);\r\n var databusNode = new QueryNode(DATABUS_RESOURCE_BASE_URL, null);\r\n\r\n target.content.root = new QueryNode(null, null);\r\n target.content.root.addChild(databusNode);\r\n\r\n for(var groupNode of toLoad.content.generatedQuery.root.childNodes) {\r\n databusNode.addChild(groupNode);\r\n }\r\n\r\n for(var customNode of toLoad.content.customQueries) {\r\n\r\n var label = customNode.label;\r\n var query = customNode.query;\r\n\r\n databusNode.addChild(new QueryNode(label, query));\r\n }\r\n\r\n } else {\r\n target.content = toLoad.content;\r\n }\r\n\r\n DatabusAlert.alert($scope, true, DatabusMessages.CEDIT_COLLECTION_IMPORTED);\r\n $scope.isLoadFromJsonVisible = false;\r\n } catch (e) {\r\n $scope.statusCode = DatabusMessages.CEDIT_COLLECTION_IMPORT_FAILED;\r\n console.log(e);\r\n }\r\n }\r\n\r\n $scope.getCollectionJson = function () {\r\n var copy = DatabusCollectionUtils.createCleanCopy($scope.collectionManager.activeCollection);\r\n delete copy.uuid;\r\n return copy;\r\n }\r\n\r\n $scope.onActiveCollectionChanged();\r\n}\r\n\r\nmodule.exports = CollectionsEditorController;\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/collections-editor-controller.js?"); /***/ }), @@ -345,7 +385,7 @@ eval("const DatabusCollectionUtils = __webpack_require__(/*! ../collections/data \****************************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("const DatabusUris = __webpack_require__(/*! ../utils/databus-uris */ \"./js/utils/databus-uris.js\");\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\nconst DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\n\n/**\n * Controller of the front page\n * @param {scope} $scope [description]\n * @param {http} $http [description]\n * @param {sce} $sce [description]\n */\nfunction FrontPageController($scope, $sce, $http) {\n\n $scope.databusName = DATABUS_NAME;\n\n $scope.auth = data.auth;\n\n $scope.activityChartData = {};\n $scope.activityChartData.isLoading = true;\n $scope.utils = new DatabusWebappUtils();\n\n $scope.searchQuery = \"\";\n $scope.searchSettings = {\n minRelevance: 20,\n maxResults: 25,\n placeholder: `Search the Databus...`,\n resourceTypes: undefined,\n filter: `&typeNameWeight=0`\n };\n\n $http.get(`/app/index/activity`).then(function (response) {\n $scope.activityChartData.entries = response.data;\n $scope.activityChartData.isLoading = false;\n }, function (err) {\n console.log(err);\n });\n\n $scope.uploadRankingData = {};\n $scope.uploadRankingData.isLoading = true;\n\n $http.get(`/app/index/ranking`).then(function (response) {\n $scope.uploadRankingData.data = response.data;\n $scope.uploadRankingData.isLoading = false;\n }, function (err) {\n console.log(err);\n });\n\n $scope.recentUploadsData = {};\n $scope.recentUploadsData.isLoading = true;\n\n $http.get(`/app/index/recent`).then(function (response) {\n $scope.recentUploadsData.data = response.data;\n $scope.recentUploadsData.isLoading = false;\n }, function (err) {\n console.log(err);\n });\n\n\n for(var d in $scope.uploadRankingData) {\n $scope.uploadRankingData[d].uploadSize = DatabusUtils.formatFileSize($scope.uploadRankingData[d].uploadSize);\n }\n\n for(var d in $scope.recentUploadsData) {\n $scope.recentUploadsData[d].date = DatabusUtils.formatDate($scope.recentUploadsData[d].date);\n }\n}\n\n\nmodule.exports = FrontPageController;\n\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/frontpage-controller.js?"); +eval("const DatabusUris = __webpack_require__(/*! ../utils/databus-uris */ \"./js/utils/databus-uris.js\");\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\nconst DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\n\n/**\n * Controller of the front page\n * @param {scope} $scope [description]\n * @param {http} $http [description]\n * @param {sce} $sce [description]\n */\nfunction FrontPageController($scope, $sce, $http) {\n\n $scope.databusName = DATABUS_NAME;\n\n $scope.auth = data.auth;\n\n $scope.activityChartData = {};\n $scope.activityChartData.isLoading = true;\n $scope.utils = new DatabusWebappUtils();\n\n $scope.searchQuery = \"\";\n $scope.searchSettings = {\n minRelevance: 20,\n maxResults: 25,\n placeholder: `Search the Databus...`,\n resourceTypes: undefined,\n filter: `&typeNameWeight=0`\n };\n\n $http.get(`/app/index/activity`).then(function (response) {\n $scope.activityChartData.entries = response.data;\n $scope.activityChartData.isLoading = false;\n }, function (err) {\n console.log(err);\n });\n\n $scope.uploadRankingData = {};\n $scope.uploadRankingData.isLoading = true;\n\n $http.get(`/app/index/ranking`).then(function (response) {\n $scope.uploadRankingData.data = response.data;\n $scope.uploadRankingData.isLoading = false;\n }, function (err) {\n console.log(err);\n });\n\n $scope.recentUploadsData = {};\n $scope.recentUploadsData.isLoading = true;\n\n $http.get(`/app/index/recent`).then(function (response) {\n $scope.recentUploadsData.data = response.data;\n $scope.recentUploadsData.isLoading = false;\n }, function (err) {\n console.log(err);\n });\n\n // Login function\n $scope.login = function () {\n window.location = '/app/login?redirectUrl=' + encodeURIComponent(window.location);\n }\n\n $scope.goToPage = function(path) {\n window.location = path;\n }\n\n $scope.account = function() {\n window.location = '/app/account';\n }\n\n for(var d in $scope.uploadRankingData) {\n $scope.uploadRankingData[d].uploadSize = DatabusUtils.formatFileSize($scope.uploadRankingData[d].uploadSize);\n }\n\n for(var d in $scope.recentUploadsData) {\n $scope.recentUploadsData[d].date = DatabusUtils.formatDate($scope.recentUploadsData[d].date);\n }\n}\n\n\nmodule.exports = FrontPageController;\n\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/frontpage-controller.js?"); /***/ }), @@ -355,7 +395,7 @@ eval("const DatabusUris = __webpack_require__(/*! ../utils/databus-uris */ \"./j \************************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("const DatabusCollectionWrapper = __webpack_require__(/*! ../collections/databus-collection-wrapper */ \"./js/collections/databus-collection-wrapper.js\");\nconst DatabusAlert = __webpack_require__(/*! ../components/databus-alert/databus-alert */ \"./js/components/databus-alert/databus-alert.js\");\nconst DataIdCreator = __webpack_require__(/*! ../publish/dataid-creator */ \"./js/publish/dataid-creator.js\");\nconst QueryBuilder = __webpack_require__(/*! ../query-builder/query-builder */ \"./js/query-builder/query-builder.js\");\nconst QueryNode = __webpack_require__(/*! ../query-builder/query-node */ \"./js/query-builder/query-node.js\");\nconst QueryTemplates = __webpack_require__(/*! ../query-builder/query-templates */ \"./js/query-builder/query-templates.js\");\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\nconst DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\nconst TabNavigation = __webpack_require__(/*! ../utils/tab-navigation */ \"./js/utils/tab-navigation.js\");\n\nfunction GroupPageController($scope, $http, $sce, $interval, $location, collectionManager) {\n\n $scope.group = data.group;\n $scope.accountName = DatabusUtils.uriToName(DatabusUtils.navigateUp($scope.group.uri));\n\n $scope.utils = new DatabusWebappUtils($scope, $sce);\n $scope.tabNavigation = new TabNavigation($scope, $location, [\n 'files', 'artifacts', 'edit'\n ]);\n\n $scope.dataSearchInput = \"\";\n $scope.dataSearchSettings = {\n minRelevance: 0.01,\n maxResults: 10,\n placeholder: `Search ${$scope.accountName}'s data...`,\n resourceTypes: ['Artifact'],\n filter: `&publisher=${$scope.accountName}&typeNameWeight=0&group=${$scope.group.name}`\n };\n\n\n $scope.group.hasData = false;\n $scope.group.hasArtifacts = false;\n $scope.isLoading = true;\n\n $http({\n method: 'GET',\n url: `/app/group/get-artifacts?uri=${encodeURIComponent($scope.group.uri)}`\n }).then(function successCallback(response) {\n\n $scope.artifacts = response.data;\n\n for (var artifact of $scope.artifacts) {\n if (artifact.latestVersionDate != undefined) {\n $scope.group.hasData = true;\n }\n\n artifact.title = DatabusUtils.stringOrFallback(artifact.title, artifact.latestVersionTitle);\n artifact.abstract = DatabusUtils.stringOrFallback(artifact.abstract, artifact.latestVersionAbstract);\n artifact.description = DatabusUtils.stringOrFallback(artifact.description, artifact.latestVersionDescription);\n }\n\n $scope.group.hasArtifacts = $scope.artifacts.length > 0;\n $scope.isLoading = false;\n }, function errorCallback(response) {\n $scope.isLoading = false;\n });\n\n\n $scope.pageTitle = DatabusUtils.stringOrFallback($scope.group.title,\n DatabusUtils.uriToTitle($scope.group.uri));\n\n\n $scope.canEdit = $scope.accountName == data.auth.info.accountName;\n\n if (data.auth.authenticated && $scope.canEdit) {\n\n var abstract = DatabusUtils.createAbstractFromDescription($scope.group.description);\n $scope.formData = {};\n $scope.formData.group = {};\n $scope.formData.group.generateAbstract = abstract == $scope.group.abstract;\n $scope.formData.group.name = $scope.group.name;\n $scope.formData.group.title = $scope.group.title;\n $scope.formData.group.abstract = $scope.group.abstract;\n $scope.formData.group.description = $scope.group.description;\n\n $scope.dataidCreator = new DataIdCreator($scope.formData, data.auth.info.accountName);\n }\n\n $scope.onDescriptionChanged = function () {\n if ($scope.formData == null) {\n return;\n }\n\n if (!$scope.formData.group.generateAbstract) {\n return;\n }\n\n $scope.formData.group.abstract =\n DatabusUtils.createAbstractFromDescription($scope.formData.group.description);\n }\n\n $scope.resetEdits = function () {\n $scope.formData.group.title = $scope.group.title;\n $scope.formData.group.abstract = $scope.group.abstract;\n $scope.formData.group.description = $scope.group.description;\n }\n\n $scope.saveGroup = async function () {\n\n if ($scope.dataidCreator == null) {\n return;\n }\n\n var groupUpdate = $scope.dataidCreator.createGroupUpdate();\n\n var relativeUri = new URL($scope.group.uri).pathname;\n var response = await $http.put(relativeUri, groupUpdate);\n\n if (response.status == 200) {\n $scope.group.title = $scope.formData.group.title;\n $scope.group.abstract = $scope.formData.group.abstract;\n $scope.group.description = $scope.formData.group.description;\n\n\n $scope.pageTitle = DatabusUtils.stringOrFallback($scope.group.title,\n DatabusUtils.uriToTitle($scope.group.uri));\n\n DatabusAlert.alert($scope, true, \"Group Saved!\");\n $scope.$apply();\n }\n }\n\n $scope.facetsView = {};\n $scope.facetsView.resourceUri = $scope.group.uri;\n $scope.facetsView.settings = [];\n $scope.facetsView.parentSettings = null;\n $scope.authenticated = data.auth.authenticated;\n $scope.selection = [];\n\n $scope.input = {};\n $scope.input.search = '';\n $scope.searchCooldown = 500;\n $scope.searchChanged = true;\n $scope.searchReady = true;\n\n $scope.fileSelector = {};\n $scope.fileSelector.config = {};\n $scope.fileSelector.config.columns = [];\n $scope.fileSelector.config.columns.push({ field: 'artifact', label: 'Artifact', width: '30%', uriToName: true });\n $scope.fileSelector.config.columns.push({ field: 'version', label: 'Version', width: '21%' });\n $scope.fileSelector.config.columns.push({ field: 'variant', label: 'Variant', width: '16%' });\n $scope.fileSelector.config.columns.push({ field: 'format', label: 'Format', width: '9%' });\n $scope.fileSelector.config.columns.push({ field: 'compression', label: 'Compression', width: '6%' });\n\n $scope.groupNode = new QueryNode($scope.group.uri, 'databus:group');\n $scope.groupNode.setFacet('http://purl.org/dc/terms/hasVersion', '$latest', true);\n\n $scope.onFacetSettingsChanged = function () {\n $scope.fileSelector.query = QueryBuilder.build({\n node: $scope.groupNode,\n template: QueryTemplates.DEFAULT_FILE_TEMPLATE,\n resourceBaseUrl: DATABUS_RESOURCE_BASE_URL\n });\n\n $scope.fileSelector.fullQuery = QueryBuilder.build({\n node: $scope.groupNode,\n template: QueryTemplates.GROUP_PAGE_FILE_BROWSER_TEMPLATE,\n resourceBaseUrl: DATABUS_RESOURCE_BASE_URL\n });\n }\n\n // $scope.onFacetSettingsChanged();\n\n $scope.collectionWidgetSelectionData = {};\n $scope.collectionWidgetSelectionData.groupNode = $scope.groupNode;\n\n $scope.onFileQueryResult = function (args) {\n if (args == null) return;\n $scope.collectionWidgetSelectionData.query = args.query;\n }\n\n $scope.collectionManager = collectionManager;\n\n $scope.findArtifact = function (uri) {\n return $scope.artifacts.find(function (a) { a.uri === uri; });\n }\n\n $scope.formatResult = function (result) {\n return $sce.trustAsHtml(result);\n }\n\n\n $scope.formatLicense = function (licenseUri) {\n var licenseName = DatabusUtils.uriToName(licenseUri);\n\n var html = '
' + licenseName + '
'\n return $sce.trustAsHtml(html);\n }\n\n for (var a in $scope.artifacts) {\n $scope.artifacts[a].date = $scope.formatDate($scope.artifacts[a].date);\n $scope.artifacts[a].licenseTag = $scope.formatLicense($scope.artifacts[a].license);\n }\n\n $scope.setSelectionStateAll = function (val) {\n if (val) {\n for (var a in $scope.artifacts) {\n $scope.select($scope.artifacts[a]);\n }\n } else {\n for (var a in $scope.artifacts) {\n $scope.deselect($scope.artifacts[a]);\n }\n }\n }\n\n $scope.toggleSelect = function (artifact) {\n if ($scope.isSelected(artifact)) {\n $scope.deselect(artifact);\n } else {\n $scope.select(artifact);\n }\n }\n\n $scope.select = function (artifact) {\n artifact.isSelected = true;\n $scope.selection.push(artifact.uri);\n }\n\n $scope.deselect = function (artifact) {\n artifact.isSelected = false;\n $scope.selection = $scope.selection.filter(function (value, index, arr) {\n return value !== artifact.uri;\n });\n }\n\n $scope.isSelected = function (artifact) {\n for (var s in $scope.selection) {\n if ($scope.selection[s] === artifact.uri) {\n return true;\n }\n }\n return false;\n }\n\n $scope.changeCollection = function (collection) {\n $scope.collectionManager.setActive(collection.uuid);\n $scope.search();\n }\n\n $scope.showCollectionModal = function () {\n $('#add-to-collection-modal').addClass('is-active');\n }\n\n $scope.hideCollectionModal = function () {\n $('#add-to-collection-modal').removeClass('is-active');\n }\n\n $scope.markdownToHtml = function (markdown) {\n\n var converter = window.markdownit();\n return $sce.trustAsHtml(converter.render(markdown));\n };\n\n\n $scope.invokeSearch = function () {\n if ($scope.searchReady) {\n $scope.search();\n $scope.searchReady = false;\n } else {\n $scope.searchChanged = true;\n }\n }\n\n $interval(function () {\n if ($scope.searchChanged) {\n $scope.search();\n $scope.searchChanged = false;\n }\n $scope.searchReady = true;\n }, $scope.searchCooldown);\n\n\n $scope.addSelectionToCollection = function () {\n\n if ($scope.collectionManager.activeCollection == null) {\n return;\n }\n\n var wrapper = new DatabusCollectionWrapper($scope.collectionManager.activeCollection);\n\n for (var s in $scope.selection) {\n var artifact = $scope.artifacts.find(function (a) { return a.uri === $scope.selection[s]; });\n wrapper.addArtifactNode(artifact.uri, artifact.label);\n }\n $scope.collectionManager.saveLocally();\n $scope.search();\n }\n\n $scope.updateArtifactState = function (wrapper, artifact) {\n artifact.alreadyAdded = wrapper.hasArtifact(artifact.uri);\n artifact.isSelected = artifact.alreadyAdded || $scope.selection.includes(artifact.uri);\n }\n\n\n\n $scope.search = function () {\n\n $scope.searchResult = [];\n\n var typeFilters = `&publisher=${$scope.accountName}&publisherWeight=0&typeName=Artifact&typeNameWeight=0&group=${$scope.group.name}&minRelevance=0.1`;\n\n $http({\n method: 'GET',\n url: '/api/search?query=' + $scope.input.search + typeFilters\n }).then(function successCallback(response) {\n\n for (var r in response.data.docs) {\n var result = response.data.docs[r];\n\n for (var artifact of $scope.artifacts) {\n if (result.resource[0] == artifact.uri) {\n $scope.searchResult.push(artifact);\n }\n }\n }\n }, function errorCallback(response) {\n });\n }\n}\n\nmodule.exports = GroupPageController;\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/group-controller.js?"); +eval("const DatabusCollectionWrapper = __webpack_require__(/*! ../collections/databus-collection-wrapper */ \"./js/collections/databus-collection-wrapper.js\");\r\nconst DatabusAlert = __webpack_require__(/*! ../components/databus-alert/databus-alert */ \"./js/components/databus-alert/databus-alert.js\");\r\nconst DataIdCreator = __webpack_require__(/*! ../publish/dataid-creator */ \"./js/publish/dataid-creator.js\");\r\nconst QueryBuilder = __webpack_require__(/*! ../query-builder/query-builder */ \"./js/query-builder/query-builder.js\");\r\nconst QueryNode = __webpack_require__(/*! ../query-builder/query-node */ \"./js/query-builder/query-node.js\");\r\nconst QueryTemplates = __webpack_require__(/*! ../query-builder/query-templates */ \"./js/query-builder/query-templates.js\");\r\nconst DatabusConstants = __webpack_require__(/*! ../utils/databus-constants */ \"./js/utils/databus-constants.js\");\r\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\r\nconst DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\r\nconst TabNavigation = __webpack_require__(/*! ../utils/tab-navigation */ \"./js/utils/tab-navigation.js\");\r\n\r\nfunction GroupPageController($scope, $http, $sce, $interval, $location, collectionManager) {\r\n\r\n $scope.group = data.group;\r\n // $scope.accountName = DatabusUtils.uriToName(DatabusUtils.navigateUp($scope.group.uri));\r\n $scope.auth = data.auth;\r\n\r\n $scope.utils = new DatabusWebappUtils($scope, $sce);\r\n $scope.accountName = $scope.utils.getAccountName();\r\n\r\n\r\n $scope.tabNavigation = new TabNavigation($scope, $location, [\r\n 'files', 'artifacts', 'edit'\r\n ]);\r\n\r\n $scope.dataSearchInput = \"\";\r\n $scope.dataSearchSettings = {\r\n minRelevance: 0.01,\r\n maxResults: 10,\r\n placeholder: `Search ${$scope.accountName}'s data...`,\r\n resourceTypes: ['Artifact'],\r\n filter: `&publisher=${$scope.accountName}&typeNameWeight=0&group=${$scope.group.name}`\r\n };\r\n\r\n\r\n $scope.group.hasData = false;\r\n $scope.group.hasArtifacts = false;\r\n $scope.isLoading = true;\r\n\r\n $http({\r\n method: 'GET',\r\n url: `/app/group/get-artifacts?uri=${encodeURIComponent($scope.group.uri)}`\r\n }).then(function successCallback(response) {\r\n\r\n $scope.artifacts = response.data;\r\n\r\n for (var artifact of $scope.artifacts) {\r\n if (artifact.latestVersionDate != undefined) {\r\n $scope.group.hasData = true;\r\n }\r\n\r\n artifact.title = DatabusUtils.stringOrFallback(artifact.title, artifact.latestVersionTitle);\r\n artifact.abstract = DatabusUtils.stringOrFallback(artifact.abstract, artifact.latestVersionAbstract);\r\n artifact.description = DatabusUtils.stringOrFallback(artifact.description, artifact.latestVersionDescription);\r\n }\r\n\r\n $scope.group.hasArtifacts = $scope.artifacts.length > 0;\r\n $scope.isLoading = false;\r\n }, function errorCallback(response) {\r\n $scope.isLoading = false;\r\n });\r\n\r\n\r\n $scope.pageTitle = DatabusUtils.stringOrFallback($scope.group.title,\r\n DatabusUtils.uriToTitle($scope.group.uri));\r\n\r\n\r\n $scope.canEdit = $scope.accountName != null;\r\n\r\n if (data.auth.authenticated && $scope.canEdit) {\r\n\r\n var abstract = DatabusUtils.createAbstractFromDescription($scope.group.description);\r\n $scope.formData = {};\r\n $scope.formData.group = {};\r\n $scope.formData.group.generateAbstract = abstract == $scope.group.abstract;\r\n $scope.formData.group.name = $scope.group.name;\r\n $scope.formData.group.title = $scope.group.title;\r\n $scope.formData.group.abstract = $scope.group.abstract;\r\n $scope.formData.group.description = $scope.group.description;\r\n\r\n $scope.dataidCreator = new DataIdCreator($scope.formData, $scope.accountName);\r\n }\r\n\r\n $scope.onDescriptionChanged = function () {\r\n if ($scope.formData == null) {\r\n return;\r\n }\r\n\r\n if (!$scope.formData.group.generateAbstract) {\r\n return;\r\n }\r\n\r\n $scope.formData.group.abstract =\r\n DatabusUtils.createAbstractFromDescription($scope.formData.group.description);\r\n }\r\n\r\n $scope.resetEdits = function () {\r\n $scope.formData.group.title = $scope.group.title;\r\n $scope.formData.group.abstract = $scope.group.abstract;\r\n $scope.formData.group.description = $scope.group.description;\r\n }\r\n\r\n $scope.saveGroup = async function () {\r\n\r\n if ($scope.dataidCreator == null) {\r\n return;\r\n }\r\n\r\n var groupUpdate = $scope.dataidCreator.createGroupUpdate();\r\n\r\n var relativeUri = new URL($scope.group.uri).pathname;\r\n var response = await $http.post('/api/register', groupUpdate);\r\n\r\n if (response.status == 200) {\r\n $scope.group.title = $scope.formData.group.title;\r\n $scope.group.abstract = $scope.formData.group.abstract;\r\n $scope.group.description = $scope.formData.group.description;\r\n\r\n\r\n $scope.pageTitle = DatabusUtils.stringOrFallback($scope.group.title,\r\n DatabusUtils.uriToTitle($scope.group.uri));\r\n\r\n DatabusAlert.alert($scope, true, \"Group Saved!\");\r\n $scope.$apply();\r\n }\r\n }\r\n\r\n $scope.facetsView = {};\r\n $scope.facetsView.resourceUri = $scope.group.uri;\r\n $scope.facetsView.settings = [];\r\n $scope.facetsView.parentSettings = null;\r\n $scope.authenticated = data.auth.authenticated;\r\n $scope.selection = [];\r\n\r\n $scope.input = {};\r\n $scope.input.search = '';\r\n $scope.searchCooldown = 500;\r\n $scope.searchChanged = true;\r\n $scope.searchReady = true;\r\n\r\n $scope.fileSelector = {};\r\n $scope.fileSelector.config = {};\r\n $scope.fileSelector.config.authenticated = $scope.authenticated;\r\n $scope.fileSelector.config.columns = [];\r\n $scope.fileSelector.config.columns.push({ field: 'artifact', label: 'Artifact', width: '30%', uriToName: true });\r\n $scope.fileSelector.config.columns.push({ field: 'version', label: 'Version', width: '21%' });\r\n $scope.fileSelector.config.columns.push({ field: 'variant', label: 'Variant', width: '16%' });\r\n $scope.fileSelector.config.columns.push({ field: 'format', label: 'Format', width: '9%' });\r\n $scope.fileSelector.config.columns.push({ field: 'compression', label: 'Compression', width: '6%' });\r\n\r\n $scope.groupNode = new QueryNode($scope.group.uri, 'databus:group');\r\n $scope.groupNode.setFacet('http://purl.org/dc/terms/hasVersion', DatabusConstants.FACET_LATEST_VERSION_VALUE, true);\r\n\r\n $scope.onFacetSettingsChanged = function () {\r\n $scope.fileSelector.query = QueryBuilder.build({\r\n node: $scope.groupNode,\r\n template: QueryTemplates.DEFAULT_FILE_TEMPLATE,\r\n resourceBaseUrl: DATABUS_RESOURCE_BASE_URL\r\n });\r\n\r\n $scope.fileSelector.fullQuery = QueryBuilder.build({\r\n node: $scope.groupNode,\r\n template: QueryTemplates.GROUP_PAGE_FILE_BROWSER_TEMPLATE,\r\n resourceBaseUrl: DATABUS_RESOURCE_BASE_URL\r\n });\r\n }\r\n\r\n // $scope.onFacetSettingsChanged();\r\n\r\n $scope.collectionWidgetSelectionData = {};\r\n $scope.collectionWidgetSelectionData.groupNode = $scope.groupNode;\r\n\r\n $scope.onFileQueryResult = function (args) {\r\n if (args == null) return;\r\n $scope.collectionWidgetSelectionData.query = args.query;\r\n }\r\n\r\n $scope.collectionManager = collectionManager;\r\n\r\n $scope.findArtifact = function (uri) {\r\n return $scope.artifacts.find(function (a) { a.uri === uri; });\r\n }\r\n\r\n $scope.formatResult = function (result) {\r\n return $sce.trustAsHtml(result);\r\n }\r\n\r\n\r\n $scope.formatLicense = function (licenseUri) {\r\n var licenseName = DatabusUtils.uriToName(licenseUri);\r\n\r\n var html = '
' + licenseName + '
'\r\n return $sce.trustAsHtml(html);\r\n }\r\n\r\n for (var a in $scope.artifacts) {\r\n $scope.artifacts[a].date = $scope.formatDate($scope.artifacts[a].date);\r\n $scope.artifacts[a].licenseTag = $scope.formatLicense($scope.artifacts[a].license);\r\n }\r\n\r\n $scope.setSelectionStateAll = function (val) {\r\n if (val) {\r\n for (var a in $scope.artifacts) {\r\n $scope.select($scope.artifacts[a]);\r\n }\r\n } else {\r\n for (var a in $scope.artifacts) {\r\n $scope.deselect($scope.artifacts[a]);\r\n }\r\n }\r\n }\r\n\r\n $scope.toggleSelect = function (artifact) {\r\n if ($scope.isSelected(artifact)) {\r\n $scope.deselect(artifact);\r\n } else {\r\n $scope.select(artifact);\r\n }\r\n }\r\n\r\n $scope.select = function (artifact) {\r\n artifact.isSelected = true;\r\n $scope.selection.push(artifact.uri);\r\n }\r\n\r\n $scope.deselect = function (artifact) {\r\n artifact.isSelected = false;\r\n $scope.selection = $scope.selection.filter(function (value, index, arr) {\r\n return value !== artifact.uri;\r\n });\r\n }\r\n\r\n $scope.isSelected = function (artifact) {\r\n for (var s in $scope.selection) {\r\n if ($scope.selection[s] === artifact.uri) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n\r\n $scope.changeCollection = function (collection) {\r\n $scope.collectionManager.setActive(collection.uuid);\r\n $scope.search();\r\n }\r\n\r\n $scope.showCollectionModal = function () {\r\n $('#add-to-collection-modal').addClass('is-active');\r\n }\r\n\r\n $scope.hideCollectionModal = function () {\r\n $('#add-to-collection-modal').removeClass('is-active');\r\n }\r\n\r\n $scope.markdownToHtml = function (markdown) {\r\n\r\n var converter = window.markdownit();\r\n return $sce.trustAsHtml(converter.render(markdown));\r\n };\r\n\r\n\r\n $scope.invokeSearch = function () {\r\n if ($scope.searchReady) {\r\n $scope.search();\r\n $scope.searchReady = false;\r\n } else {\r\n $scope.searchChanged = true;\r\n }\r\n }\r\n\r\n $interval(function () {\r\n if ($scope.searchChanged) {\r\n $scope.search();\r\n $scope.searchChanged = false;\r\n }\r\n $scope.searchReady = true;\r\n }, $scope.searchCooldown);\r\n\r\n\r\n $scope.addSelectionToCollection = function () {\r\n\r\n if ($scope.collectionManager.activeCollection == null) {\r\n return;\r\n }\r\n\r\n var wrapper = new DatabusCollectionWrapper($scope.collectionManager.activeCollection);\r\n\r\n for (var s in $scope.selection) {\r\n var artifact = $scope.artifacts.find(function (a) { return a.uri === $scope.selection[s]; });\r\n wrapper.addArtifactNode(artifact.uri, artifact.label);\r\n }\r\n $scope.collectionManager.saveLocally();\r\n $scope.search();\r\n }\r\n\r\n $scope.updateArtifactState = function (wrapper, artifact) {\r\n artifact.alreadyAdded = wrapper.hasArtifact(artifact.uri);\r\n artifact.isSelected = artifact.alreadyAdded || $scope.selection.includes(artifact.uri);\r\n }\r\n\r\n\r\n\r\n $scope.search = function () {\r\n\r\n $scope.searchResult = [];\r\n\r\n var typeFilters = `&publisher=${$scope.accountName}&publisherWeight=0&typeName=Artifact&typeNameWeight=0&group=${$scope.group.name}&minRelevance=0.1`;\r\n\r\n $http({\r\n method: 'GET',\r\n url: '/api/search?query=' + $scope.input.search + typeFilters\r\n }).then(function successCallback(response) {\r\n\r\n for (var r in response.data.docs) {\r\n var result = response.data.docs[r];\r\n\r\n for (var artifact of $scope.artifacts) {\r\n if (result.id[0] == artifact.uri) {\r\n $scope.searchResult.push(artifact);\r\n }\r\n }\r\n }\r\n }, function errorCallback(response) {\r\n });\r\n }\r\n}\r\n\r\nmodule.exports = GroupPageController;\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/group-controller.js?"); /***/ }), @@ -365,7 +405,7 @@ eval("const DatabusCollectionWrapper = __webpack_require__(/*! ../collections/da \*************************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("var DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\n\n// Controller for the header section\nfunction HeaderController($scope, $http, collectionManager, searchManager) {\n\n $scope.auth = data.auth;\n $scope.authenticated = data.auth.authenticated;\n\n $scope.accountName = null;\n $scope.utils = new DatabusWebappUtils($scope);\n \n if($scope.authenticated && $scope.auth.info != null) {\n $scope.accountName = $scope.auth.info.accountName;\n }\n\n // Check for cookie settings\n $scope.databusCookieConsentKey = 'databus_cookie_consent';\n let cookieConsent = window.localStorage.getItem($scope.databusCookieConsentKey);\n $scope.showCookieDialogue = cookieConsent === undefined;\n $scope.collectionManager = collectionManager;\n\n if ($scope.authenticated) {\n // Collection Manager Init\n var loadCollectionsFromServer = $scope.collectionManager.accountName != $scope.auth.info.accountName;\n\n $scope.collectionManager.tryInitialize($scope.auth.info.accountName, loadCollectionsFromServer);\n\n // Initialize search manager\n searchManager.initialize();\n } else {\n $scope.collectionManager.clearSession();\n }\n\n $scope.hideAccountMenu = function() {\n $scope.isAccountMenuActive = false;\n }\n\n $scope.showAccountMenu = function() {\n $scope.isAccountMenuActive = true;\n }\n\n // Finds a display name for the account\n $scope.getAccountName = function () {\n if ($scope.auth.info.accountName) {\n return $scope.auth.info.accountName;\n }\n\n if ($scope.auth.info.oidc_email) {\n return $scope.auth.info.oidc_email;\n }\n\n if ($scope.auth.info.oidc_name) {\n return $scope.auth.info.oidc_name;\n }\n\n return null;\n }\n\n $scope.isMenuActive = false;\n $scope.isAccountMenuActive = false;\n\n // Coookieees\n $scope.giveCookieConsent = function () {\n window.localStorage.setItem($scope.databusCookieConsentKey, true);\n $scope.showCookieDialogue = false;\n }\n\n // Login function\n $scope.login = function () {\n window.location = '/app/login?redirectUrl=' + encodeURIComponent(window.location);\n }\n\n // Logout function\n $scope.logout = function () {\n $scope.hideAccountMenu();\n window.location = '/app/logout?redirectUrl=' + encodeURIComponent(window.location);\n }\n\n // ???\n $scope.size = function () {\n if ($scope.collectionManager == null) {\n return \"\";\n }\n\n var first = $scope.collectionManager.current;\n return first != null ? first.elements.length : \"\";\n }\n}\n\nmodule.exports = HeaderController;\n\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/header-controller.js?"); +eval("var DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\r\n\r\n// Controller for the header section\r\nfunction HeaderController($scope, $http, collectionManager, searchManager) {\r\n\r\n $scope.auth = data.auth;\r\n $scope.authenticated = data.auth.authenticated;\r\n\r\n $scope.utils = new DatabusWebappUtils($scope);\r\n $scope.accountName = $scope.utils.getAccountName();\r\n \r\n\r\n // Check for cookie settings\r\n $scope.databusCookieConsentKey = 'databus_cookie_consent';\r\n let cookieConsent = window.localStorage.getItem($scope.databusCookieConsentKey);\r\n $scope.showCookieDialogue = cookieConsent === undefined;\r\n\r\n $scope.collectionManager = collectionManager;\r\n\r\n if ($scope.authenticated) {\r\n\r\n $scope.collectionManager.tryInitialize($scope.accountName);\r\n // Collection Manager Init\r\n // Initialize search manager\r\n searchManager.initialize();\r\n } else {\r\n $scope.collectionManager.clearSession();\r\n }\r\n\r\n $scope.hideAccountMenu = function() {\r\n $scope.isAccountMenuActive = false;\r\n }\r\n\r\n $scope.showAccountMenu = function() {\r\n $scope.isAccountMenuActive = true;\r\n }\r\n\r\n // Finds a display name for the account\r\n $scope.getAccountName = function () {\r\n if ($scope.auth.info.accountName) {\r\n return $scope.auth.info.accountName;\r\n }\r\n\r\n if ($scope.auth.info.oidc_email) {\r\n return $scope.auth.info.oidc_email;\r\n }\r\n\r\n if ($scope.auth.info.oidc_name) {\r\n return $scope.auth.info.oidc_name;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n $scope.isMenuActive = false;\r\n $scope.isAccountMenuActive = false;\r\n\r\n // Coookieees\r\n $scope.giveCookieConsent = function () {\r\n window.localStorage.setItem($scope.databusCookieConsentKey, true);\r\n $scope.showCookieDialogue = false;\r\n }\r\n\r\n // Login function\r\n $scope.login = function () {\r\n window.location = '/app/login?redirectUrl=' + encodeURIComponent(window.location);\r\n }\r\n\r\n // Logout function\r\n $scope.logout = function () {\r\n $scope.hideAccountMenu();\r\n window.location = '/app/logout?redirectUrl=' + encodeURIComponent(window.location);\r\n }\r\n\r\n // ???\r\n $scope.size = function () {\r\n if ($scope.collectionManager == null) {\r\n return \"\";\r\n }\r\n\r\n var first = $scope.collectionManager.current;\r\n return first != null ? first.elements.length : \"\";\r\n }\r\n}\r\n\r\nmodule.exports = HeaderController;\r\n\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/header-controller.js?"); /***/ }), @@ -375,7 +415,7 @@ eval("var DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-u \**************************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("const DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\nconst DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\nconst DatabusAlert = __webpack_require__(/*! ../components/databus-alert/databus-alert */ \"./js/components/databus-alert/databus-alert.js\");\nconst SearchAdapter = __webpack_require__(/*! ../search/search-adapter */ \"./js/search/search-adapter.js\");\nconst DatabusMessages = __webpack_require__(/*! ../utils/databus-messages */ \"./js/utils/databus-messages.js\");\nconst DatabusConstants = __webpack_require__(/*! ../utils/databus-constants */ \"./js/utils/databus-constants.js\");\nconst AppJsonFormatter = __webpack_require__(/*! ../utils/app-json-formatter */ \"./js/utils/app-json-formatter.js\");\n\nfunction ProfileController($scope, $http) {\n\n $scope.profileData = data.profile;\n $scope.auth = data.auth;\n $scope.preferredDatabusUsername = \"\";\n $scope.apiKeys = data.auth.info.apiKeys;\n $scope.createApiKeyName = \"\"\n $scope.createAccountError = \"\";\n $scope.createApiKeyError = \"\";\n $scope.addWebIdUri = \"\";\n $scope.grantAccessUri = \"\";\n $scope.adapters = SearchAdapter.list;\n $scope.utils = new DatabusWebappUtils($scope);\n\n $scope.personUri = `${DATABUS_RESOURCE_BASE_URL}/${$scope.auth.info.accountName}${DatabusConstants.WEBID_THIS}`;\n\n $scope.putProfile = function (accountName) {\n\n var accountJsonLd = AppJsonFormatter.createAccountData(DATABUS_RESOURCE_BASE_URL, \n accountName,\n accountName, \n null, \n null);\n\n $http.put(`/${accountName}`, accountJsonLd).then(function (result) {\n window.location.reload(true);\n }, function (err) {\n console.log(err);\n $scope.createAccountError = err.data;\n });\n }\n\n\n if ($scope.profileData == undefined) {\n\n $scope.createProfile = function () {\n if (!$scope.auth.authenticated) {\n return;\n }\n\n var accountName = $scope.preferredDatabusUsername;\n\n if (accountName == undefined || !DatabusUtils.isValidAccountName(accountName)) {\n $scope.createAccountError = \"Enter a valid account name.\"\n $scope.showAccountNameHints = true;\n return;\n }\n\n $scope.showAccountNameHints = false;\n $scope.putProfile(accountName);\n }\n\n return;\n }\n\n $scope.removeApiKey = function (key) {\n\n $http.post(`/api/account/api-key/delete?name=${key.keyname}`).then(function (result) {\n $scope.apiKeys = $scope.apiKeys.filter(function (k) {\n return k.keyname != key.keyname;\n });\n\n }, function (err) {\n console.log(err);\n $scope.createApiKeyError = err.data;\n });\n }\n\n $scope.onCreateApiKeyNameChanged = function () {\n var hasError = !DatabusUtils.isValidResourceLabel($scope.createApiKeyName, 3, 20);\n $scope.createApiKeyError = hasError ? \" API key name must have between 3 and 20 characters and match [A-Za-z0-9\\\\s_()\\\\.\\\\,\\\\-]*\" : \"\";\n }\n\n $scope.addApiKey = function () {\n\n $http.post(`/api/account/api-key/create?name=${encodeURIComponent($scope.createApiKeyName)}`).then(function (result) {\n\n if (result.data != null) {\n $scope.apiKeys.push(result.data);\n }\n\n DatabusAlert.alert($scope, true, DatabusMessages.ACCOUNT_API_KEY_CREATED);\n\n }, function (err) {\n console.log(err);\n $scope.createApiKeyError = err.data;\n });\n\n }\n\n $scope.removeSearchExtension = function(uri) {\n $http.post(`/api/account/mods/search-extensions/remove?uri=${encodeURIComponent(uri)}`)\n .then(function (result) {\n console.log(result);\n DatabusAlert.alert($scope, true, result.data);\n\n $scope.profileData.searchExtensions = $scope.profileData.searchExtensions.filter(function (e) {\n return e.endpointUri != uri;\n });\n\n }, function (err) {\n console.log(err);\n DatabusAlert.alert($scope, false, err.data);\n });\n }\n\n $scope.addSearchExtension = function () {\n var uri = $scope.modsSettings.searchExtensionURI;\n var adapter = $scope.modsSettings.searchExtensionAdapter.name;\n\n $http.post(`/api/account/mods/search-extensions/add?uri=${encodeURIComponent(uri)}&adapter=${adapter}`)\n .then(function (result) {\n console.log(result);\n DatabusAlert.alert($scope, true, result.data);\n $scope.profileData.searchExtensions.push({\n endpointUri: uri,\n adapter: adapter\n });\n }, function (err) {\n console.log(err);\n DatabusAlert.alert($scope, false, err.data);\n });\n }\n\n $scope.grantAccess = function () {\n $http.post(`/api/account/access/grant?uri=${encodeURIComponent($scope.grantAccessUri)}`).then(function (result) {\n $scope.profileData.authorizedAccounts.push($scope.grantAccessUri);\n }, function (err) {\n console.log(err);\n $scope.grantAccessError = err.data;\n });\n }\n\n $scope.revokeAccess = function (uri) {\n $http.post(`/api/account/access/revoke?uri=${encodeURIComponent(uri)}`).then(function (result) {\n $scope.profileData.authorizedAccounts = $scope.profileData.webIds.filter(function (value, index, arr) {\n return value != uri;\n });\n }, function (err) {\n console.log(err);\n $scope.grantAccessError = err.data;\n });\n }\n\n $scope.connectWebid = function () {\n\n $http.post(`/api/account/webid/add?uri=${encodeURIComponent($scope.addWebIdUri)}`).then(function (result) {\n $scope.profileData.webIds.push($scope.addWebIdUri);\n DatabusAlert.alert($scope, true, DatabusMessages.ACCOUNT_WEBID_LINKED);\n\n }, function (err) {\n console.log(err);\n $scope.addWebIdError = err.data;\n });\n }\n\n $scope.removeWebId = function (webIdToRemove) {\n\n $http.post(`/api/account/webid/remove?uri=${encodeURIComponent(webIdToRemove)}`).then(function (result) {\n\n $scope.profileData.webIds = $scope.profileData.webIds.filter(function (value, index, arr) {\n return value != webIdToRemove;\n });\n\n }, function (err) {\n console.log(err);\n $scope.addWebIdError = err.data;\n });\n }\n\n\n $scope.saveProfile = async function () {\n\n if (!$scope.auth.authenticated) {\n return;\n }\n\n var accountJsonLd = AppJsonFormatter.createAccountData(DATABUS_RESOURCE_BASE_URL, \n $scope.auth.info.accountName,\n $scope.editData.label, \n $scope.editData.about, \n $scope.editData.imageUrl);\n\n $http.put(`/${$scope.auth.info.accountName}`, accountJsonLd).then(function (result) {\n DatabusAlert.alert($scope, true, DatabusMessages.ACCOUT_PROFILE_SAVED);\n }, function (err) {\n console.log(err);\n });\n }\n\n // We have profile data in $scope.profileData!\n\n if (!$scope.profileData.isOwn) {\n return;\n }\n\n $scope.modsSettings = {}\n $scope.modsSettings.searchExtensionURI = \"\";\n $scope.modsSettings.searchExtensionAdapter = $scope.adapters[0];\n\n\n $scope.editData = DatabusUtils.createCleanCopy($scope.profileData);\n\n $scope.resetEdits = function () {\n $scope.editData = DatabusUtils.createCleanCopy($scope.profileData);\n }\n\n}\n\nmodule.exports = ProfileController;\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/profile-controller.js?"); +eval("const DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\r\nconst DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\r\nconst DatabusAlert = __webpack_require__(/*! ../components/databus-alert/databus-alert */ \"./js/components/databus-alert/databus-alert.js\");\r\nconst SearchAdapter = __webpack_require__(/*! ../search/search-adapter */ \"./js/search/search-adapter.js\");\r\nconst DatabusMessages = __webpack_require__(/*! ../utils/databus-messages */ \"./js/utils/databus-messages.js\");\r\nconst DatabusConstants = __webpack_require__(/*! ../utils/databus-constants */ \"./js/utils/databus-constants.js\");\r\nconst AppJsonFormatter = __webpack_require__(/*! ../utils/app-json-formatter */ \"./js/utils/app-json-formatter.js\");\r\n\r\nfunction ProfileController($scope, $http) {\r\n\r\n $scope.account = data.account;\r\n $scope.auth = data.auth;\r\n\r\n if (data.owner != null) {\r\n $scope.account.apiKeys = data.owner.apiKeys;\r\n }\r\n $scope.auth = data.auth;\r\n $scope.preferredDatabusUsername = \"\";\r\n $scope.createApiKeyName = \"\"\r\n $scope.createAccountError = \"\";\r\n $scope.createApiKeyError = \"\";\r\n $scope.addWebIdUri = \"\";\r\n $scope.deleteAccountName = \"\";\r\n $scope.grantAccessUri = \"\";\r\n $scope.adapters = SearchAdapter.list;\r\n $scope.utils = new DatabusWebappUtils($scope);\r\n\r\n $scope.accountName = $scope.utils.getAccountName();\r\n\r\n $scope.personUri = `${DATABUS_RESOURCE_BASE_URL}/${$scope.accountName}${DatabusConstants.WEBID_THIS}`;\r\n\r\n $scope.putProfile = function (accountName) {\r\n\r\n var accountUri = `${DATABUS_RESOURCE_BASE_URL}/${accountName}`;\r\n var accountJsonLd = AppJsonFormatter.createAccountData(\r\n accountUri,\r\n accountName,\r\n null,\r\n null);\r\n\r\n $http.post(`/api/register`, accountJsonLd).then(function (result) {\r\n window.location.reload(true);\r\n }, function (err) {\r\n console.log(err);\r\n $scope.createAccountError = err.data;\r\n });\r\n }\r\n\r\n\r\n if ($scope.account == undefined) {\r\n\r\n $scope.createProfile = function () {\r\n\r\n if ($scope.isSubmitting) {\r\n return;\r\n }\r\n\r\n $scope.isSubmitting = true;\r\n\r\n if (!$scope.auth.authenticated) {\r\n return;\r\n }\r\n\r\n var accountName = $scope.preferredDatabusUsername;\r\n\r\n if (accountName == undefined || !DatabusUtils.isValidAccountName(accountName)) {\r\n $scope.createAccountError = \"Enter a valid account name.\"\r\n $scope.showAccountNameHints = true;\r\n return;\r\n }\r\n\r\n $scope.showAccountNameHints = false;\r\n $scope.putProfile(accountName);\r\n }\r\n\r\n return;\r\n }\r\n\r\n $scope.addApiKey = async function () {\r\n // Validate the name input only\r\n\r\n if (!$scope.createApiKeyName) {\r\n DatabusAlert.alert(\"API key name must be provided.\");\r\n return;\r\n }\r\n\r\n let account = $scope.account;\r\n\r\n const postData = {\r\n accountName: account.accountName,\r\n keyname: $scope.createApiKeyName\r\n };\r\n\r\n try {\r\n // Send POST request to create the API key\r\n let response = await $http.post('/api/account/api-key/create', postData);\r\n\r\n if (response.data && response.data.apikey && response.data.keyname) {\r\n // Append new key to the list\r\n account.apiKeys.push({\r\n keyname: response.data.keyname,\r\n apikey: response.data.apikey\r\n });\r\n\r\n // Clear the name input field\r\n $scope.createApiKeyName = '';\r\n\r\n DatabusAlert.alert($scope, true, \"API key created.\");\r\n } else {\r\n DatabusAlert.alert($scope, false, \"Failed to create API key.\");\r\n }\r\n\r\n } catch (error) {\r\n console.error('Error creating API key:', error);\r\n const message = error.data || error.message || \"Unknown error occurred.\";\r\n DatabusAlert.alert($scope, false, message);\r\n }\r\n };\r\n\r\n\r\n $scope.deleteApiKey = async function (apiKey) {\r\n try {\r\n\r\n let account = $scope.account;\r\n // Find index of the account using accountName\r\n const index = account.apiKeys.findIndex(key => key.keyname === apiKey.keyname);\r\n\r\n if (index === -1) {\r\n throw new Error(`API key with name \"${apiKey.keyname}\" not found.`);\r\n }\r\n\r\n console.log(\"Deleting API key with keyname:\", apiKey.keyname);\r\n\r\n // Send delete request to server\r\n await $http.post(`/api/account/api-key/delete`, { accountName: account.accountName, keyname: apiKey.keyname });\r\n account.apiKeys.splice(index, 1);\r\n\r\n // Show success alert\r\n DatabusAlert.alert($scope, true, \"API key deleted.\");\r\n\r\n } catch (err) {\r\n console.error(err);\r\n\r\n\r\n\r\n const message = err.data || err.message || \"Unknown error occurred.\";\r\n DatabusAlert.alert($scope, false, message);\r\n }\r\n };\r\n\r\n $scope.addSecretary = function (account) {\r\n if (!$scope.editData.secretaries) {\r\n $scope.editData.secretaries = [];\r\n }\r\n\r\n $scope.editData.secretaries.push({\r\n accountName: '',\r\n hasWriteAccessTo: []\r\n });\r\n };\r\n\r\n $scope.removeSecretary = function (account, index) {\r\n $scope.editData.secretaries.splice(index, 1);\r\n };\r\n\r\n $scope.addNamespace = function (account, secIndex) {\r\n $scope.editData.secretaries[secIndex].hasWriteAccessTo.push('');\r\n };\r\n\r\n $scope.removeNamespace = function (account, secIndex, nsIndex) {\r\n $scope.editData.secretaries[secIndex].hasWriteAccessTo.splice(nsIndex, 1);\r\n };\r\n\r\n\r\n $scope.onCreateApiKeyNameChanged = function () {\r\n var hasError = !DatabusUtils.isValidResourceLabel($scope.createApiKeyName, 3, 20);\r\n $scope.createApiKeyError = hasError ? \" API key name must have between 3 and 20 characters and match [A-Za-z0-9\\\\s_()\\\\.\\\\,\\\\-]*\" : \"\";\r\n }\r\n\r\n\r\n $scope.removeSearchExtension = function (uri) {\r\n $http.post(`/api/account/mods/search-extensions/remove?uri=${encodeURIComponent(uri)}`)\r\n .then(function (result) {\r\n console.log(result);\r\n DatabusAlert.alert($scope, true, result.data);\r\n\r\n $scope.account.searchExtensions = $scope.account.searchExtensions.filter(function (e) {\r\n return e.endpointUri != uri;\r\n });\r\n\r\n }, function (err) {\r\n console.log(err);\r\n DatabusAlert.alert($scope, false, err.data);\r\n });\r\n }\r\n\r\n $scope.addSearchExtension = function () {\r\n var uri = $scope.modsSettings.searchExtensionURI;\r\n var adapter = $scope.modsSettings.searchExtensionAdapter.name;\r\n\r\n $http.post(`/api/account/mods/search-extensions/add?uri=${encodeURIComponent(uri)}&adapter=${adapter}`)\r\n .then(function (result) {\r\n console.log(result);\r\n DatabusAlert.alert($scope, true, result.data);\r\n $scope.account.searchExtensions.push({\r\n endpointUri: uri,\r\n adapter: adapter\r\n });\r\n }, function (err) {\r\n console.log(err);\r\n DatabusAlert.alert($scope, false, err.data);\r\n });\r\n }\r\n\r\n $scope.grantAccess = function () {\r\n $http.post(`/api/account/access/grant?uri=${encodeURIComponent($scope.grantAccessUri)}`).then(function (result) {\r\n $scope.account.authorizedAccounts.push($scope.grantAccessUri);\r\n }, function (err) {\r\n console.log(err);\r\n $scope.grantAccessError = err.data;\r\n });\r\n }\r\n\r\n $scope.revokeAccess = function (uri) {\r\n $http.post(`/api/account/access/revoke?uri=${encodeURIComponent(uri)}`).then(function (result) {\r\n $scope.account.authorizedAccounts = $scope.account.webIds.filter(function (value, index, arr) {\r\n return value != uri;\r\n });\r\n }, function (err) {\r\n console.log(err);\r\n $scope.grantAccessError = err.data;\r\n });\r\n }\r\n\r\n $scope.connectWebid = function () {\r\n\r\n $http.post(`/api/account/webid/add?uri=${encodeURIComponent($scope.addWebIdUri)}`).then(function (result) {\r\n $scope.account.webIds.push($scope.addWebIdUri);\r\n DatabusAlert.alert($scope, true, DatabusMessages.ACCOUNT_WEBID_LINKED);\r\n\r\n }, function (err) {\r\n console.log(err);\r\n $scope.addWebIdError = err.data;\r\n });\r\n }\r\n\r\n $scope.removeWebId = function (webIdToRemove) {\r\n\r\n $http.post(`/api/account/webid/remove?uri=${encodeURIComponent(webIdToRemove)}`).then(function (result) {\r\n\r\n $scope.account.webIds = $scope.account.webIds.filter(function (value, index, arr) {\r\n return value != webIdToRemove;\r\n });\r\n\r\n }, function (err) {\r\n console.log(err);\r\n $scope.addWebIdError = err.data;\r\n });\r\n }\r\n\r\n\r\n $scope.deleteAccount = async function () {\r\n let account = $scope.account;\r\n let name = $scope.deleteAccountName;\r\n\r\n try {\r\n let response = await $http.post(`/api/account/delete`, { accountName: name });\r\n\r\n window.location = `/app/user`;\r\n\r\n } catch (err) {\r\n console.error(err);\r\n DatabusAlert.alert($scope, false, err.data);\r\n }\r\n\r\n }\r\n\r\n $scope.updateAccount = async function () {\r\n\r\n if (!$scope.auth.authenticated) {\r\n return;\r\n }\r\n\r\n let account = {};\r\n account.uri = $scope.editData.uri;\r\n\r\n account.accountName = $scope.editData.accountName;\r\n account.label = $scope.editData.label;\r\n account.status = $scope.editData.about;\r\n account.imageUrl = $scope.editData.imageUrl;\r\n account.secretaries = $scope.editData.secretaries;\r\n\r\n\r\n try {\r\n await $http.post(`/api/account/update`, account);\r\n DatabusAlert.alert($scope, true, \"Account saved.\");\r\n\r\n } catch (err) {\r\n console.error(err);\r\n DatabusAlert.alert($scope, false, err.data);\r\n }\r\n }\r\n\r\n\r\n // We have profile data in $scope.account!\r\n\r\n if (!$scope.account.isOwn) {\r\n return;\r\n }\r\n\r\n $scope.modsSettings = {}\r\n $scope.modsSettings.searchExtensionURI = \"\";\r\n $scope.modsSettings.searchExtensionAdapter = $scope.adapters[0];\r\n\r\n\r\n $scope.editData = DatabusUtils.createCleanCopy($scope.account);\r\n\r\n $scope.resetEdits = function () {\r\n $scope.editData = DatabusUtils.createCleanCopy($scope.account);\r\n }\r\n\r\n}\r\n\r\nmodule.exports = ProfileController;\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/profile-controller.js?"); /***/ }), @@ -385,7 +425,7 @@ eval("const DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \". \*********************************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("const DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\nconst PublishSession = __webpack_require__(/*! ../publish/publish-session */ \"./js/publish/publish-session.js\");\n\n// Controller for the header section\nfunction PublishWizardController($scope, $http, $interval, focus, $q) {\n\n $scope.login = function () {\n window.location = '/app/login?redirectUrl=' + encodeURIComponent(window.location);\n }\n\n $scope.utils = new DatabusWebappUtils($scope);\n\n $scope.createAccount = function () {\n window.location = '/app/account';\n }\n\n $scope.authenticated = data.auth.authenticated;\n $scope.loadRequestCount = 0;\n $scope.texts = data.texts;\n\n $scope.nerdMode = {};\n $scope.nerdMode.enabled = false;\n $scope.nerdMode.customJson = \"\";\n $scope.nerdMode.logLevelOptions = ['error', 'info', 'debug'];\n $scope.nerdMode.logLevel = 'error';\n\n // controller does not work without authentication\n if (!$scope.authenticated) {\n return;\n }\n\n $scope.hasAccount = data.auth.info.accountName != undefined;;\n\n if (!$scope.hasAccount) {\n return;\n }\n\n /**\n * Fetches existing groups and artifacts\n */\n $scope.getContentForAccount = async function (accountName) {\n\n $scope.isAccountDataLoading = true;\n var uri = `/app/account/content?account=${encodeURIComponent(accountName)}`;\n var response = await $http.get(uri);\n $scope.isAccountDataLoading = false;\n\n // Put account artifacts, groups and name in one object\n var accountData = response.data;\n accountData.accountName = accountName;\n\n accountData.publisherUris = [];\n for (var p of data.publisherData) {\n accountData.publisherUris.push(p.publisherUri);\n }\n\n // Try to resume the session with the account data\n var session = PublishSession.resume($http, accountData);\n\n // Resume failed -> start new session\n if (session == null) {\n session = new PublishSession($http, null, accountData);\n }\n\n $scope.session = session;\n $scope.$watch('session', function () {\n $scope.session.onChange();\n }, true);\n\n $scope.$apply();\n }\n\n $scope.getContentForAccount(data.auth.info.accountName);\n\n /**\n * LICENSES\n */\n\n $scope.licenseQuery = \"\";\n\n $interval(function () {\n if ($scope.hasLicenseQueryChanged) {\n\n $http.get(`/app/publish-wizard/licenses?limit=30&keyword=${$scope.licenseQuery}`).then(function(response) {\n $scope.filteredLicenseList = response.data.results.bindings;\n });\n\n $scope.hasLicenseQueryChanged = false;\n }\n\n }, 300);\n\n $scope.filterLicenses = function (licenseQuery) {\n $scope.licenseQuery = licenseQuery;\n $scope.hasLicenseQueryChanged = true;\n }\n\n $scope.filterLicenses(\"\");\n\n $scope.addFile = function (input) {\n\n var session = $scope.session;\n\n if (input == undefined || input.length == 0) {\n return;\n }\n\n $scope.loadRequestCount++;\n\n $http.get('/app/publish-wizard/fetch-file?url=' + encodeURIComponent(input)).then(function (response) {\n\n $scope.loadRequestCount--;\n if (response.data == null || response.data == \"\" || response.status != 200) {\n return;\n }\n\n session.addFile(response.data);\n\n }, function (err) { });\n }\n\n $scope.objSize = function (obj) {\n return DatabusUtils.objSize(obj);\n }\n\n $scope.removeFile = function (fileGroup) {\n var files = $scope.session.formData.version.files;\n files.splice(files.findIndex(f => f.uri == fileGroup.uri), 1);\n $scope.session.formData.version.isConfigDirty = true;\n }\n\n $scope.hasError = function (errorList, error) {\n return errorList.includes(error);\n }\n\n // Fetch links using the fetch-links API of the Databus\n $scope.fetchFiles = function (parentUri) {\n\n $http.get('/app/publish-wizard/fetch-resource-page?url=' + encodeURIComponent(parentUri)).then(function (response) {\n for (var i in response.data) {\n var uri = response.data[i];\n $scope.addFile(uri);\n }\n }, function (err) {\n });\n }\n\n $scope.addFiles = function (input) {\n var lines = input.split('\\n');\n\n for (var line of lines) {\n if (line != undefined && line.length > 0) {\n $scope.addFile(line);\n }\n }\n }\n\n $scope.createTractate = function () {\n $scope.creatingTractate = true;\n $http.post('/api/tractate/v1/canonicalize', $scope.session.inputs.dataid).then(function (response) {\n $scope.session.formData.signature.tractate = response.data;\n $scope.creatingTractate = false;\n }, function (err) {\n $scope.creatingTractate = false;\n console.log(err);\n });\n }\n\n $scope.customPublish = async function () {\n var options = {}\n options.headers = {\n 'Accept': 'application/json, text/plain, */*',\n 'Content-Type': 'application/json',\n }\n\n\n $scope.isPublishing = true;\n $http.post(`/api/publish?fetch-file-properties=true&log-level=${$scope.nerdMode.logLevel}`, $scope.nerdMode.customJson, options)\n .then(function (response) {\n $scope.publishLog = response.data.log;\n $scope.isPublishing = false;\n }, function (err) {\n $scope.publishLog = err.data.log;\n $scope.isPublishing = false;\n console.log(err);\n });\n }\n\n\n $scope.publish = async function () {\n var options = {}\n options.headers = {\n 'Accept': 'application/json, text/plain, */*',\n 'Content-Type': 'application/json',\n }\n\n $scope.isPublishing = true;\n $http.post('/api/publish?fetch-file-properties=true&log-level=info', $scope.session.inputs.all, options)\n .then(function (response) {\n $scope.publishLog = response.data.log;\n $scope.isPublishing = false;\n }, function (err) {\n $scope.publishLog = err.data.log;\n $scope.isPublishing = false;\n console.log(err);\n });\n }\n}\n\n\nmodule.exports = PublishWizardController;\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/publish-wizard-controller.js?"); +eval("const DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\r\nconst PublishSession = __webpack_require__(/*! ../publish/publish-session */ \"./js/publish/publish-session.js\");\r\nconst TabNavigation = __webpack_require__(/*! ../utils/tab-navigation */ \"./js/utils/tab-navigation.js\");\r\n\r\n// Controller for the header section\r\nasync function PublishWizardController($scope, $http, $interval, focus, $q, $location) {\r\n\r\n $scope.login = function () {\r\n window.location = '/app/login?redirectUrl=' + encodeURIComponent(window.location);\r\n }\r\n\r\n $scope.utils = new DatabusWebappUtils($scope);\r\n\r\n $scope.tabNavigation = new TabNavigation($scope, $location, [\r\n '', 'group', 'artifact', 'version'\r\n ]);\r\n\r\n\r\n $scope.createAccount = function () {\r\n window.location = '/app/user';\r\n }\r\n\r\n // Login function\r\n $scope.login = function () {\r\n window.location = '/app/login?redirectUrl=' + encodeURIComponent(window.location);\r\n }\r\n\r\n $scope.authenticated = data.auth.authenticated;\r\n $scope.loadRequestCount = 0;\r\n $scope.texts = data.texts;\r\n\r\n $scope.nerdMode = {};\r\n $scope.nerdMode.enabled = false;\r\n $scope.nerdMode.customJson = \"\";\r\n $scope.nerdMode.logLevelOptions = ['error', 'info', 'debug'];\r\n $scope.nerdMode.logLevel = 'error';\r\n\r\n // controller does not work without authentication\r\n if (!$scope.authenticated) {\r\n return;\r\n }\r\n\r\n let accounts = data.auth.info.accounts;\r\n $scope.hasAccount = accounts != undefined && accounts.length > 0;\r\n\r\n $scope.accounts = [];\r\n\r\n for(let account of accounts) {\r\n $scope.accounts.push({\r\n accountName: account.accountName,\r\n apiKeys: account.apiKeys\r\n });\r\n }\r\n\r\n if (!$scope.hasAccount) {\r\n return;\r\n }\r\n\r\n // $scope.session = await PublishSession.createOrResume($http, data.auth.sub, $scope.accounts);\r\n\r\n $scope.session = new PublishSession($http, $interval, $scope.accounts, $scope.apiKeys);\r\n\r\n}\r\n /**\r\n * Fetches existing groups and artifacts\r\n \r\n $scope.getContentForAccount = async function (accountName) {\r\n\r\n $scope.isAccountDataLoading = true;\r\n var uri = `/app/account/content?account=${encodeURIComponent(accountName)}`;\r\n var response = await $http.get(uri);\r\n $scope.isAccountDataLoading = false;\r\n\r\n // Put account artifacts, groups and name in one object\r\n var accountData = response.data;\r\n accountData.accountName = accountName;\r\n\r\n accountData.publisherUris = [];\r\n for (var p of data.publisherData) {\r\n accountData.publisherUris.push(p.publisherUri);\r\n }\r\n\r\n $scope.session = new PublishSession($http);\r\n\r\n /*\r\n $scope.$watch('session', function () {\r\n $scope.session.onChange();\r\n }, true);\r\n\r\n $scope.$apply();\r\n \r\n }\r\n\r\n // $scope.getContentForAccount(data.auth.info.accounts[0]);\r\n\r\n /**\r\n * LICENSES\r\n \r\n\r\n \r\n\r\n $scope.addFile = function (input) {\r\n\r\n var session = $scope.session;\r\n\r\n if (input == undefined || input.length == 0) {\r\n return;\r\n }\r\n\r\n $scope.loadRequestCount++;\r\n\r\n $http.get('/app/publish-wizard/fetch-file?url=' + encodeURIComponent(input)).then(function (response) {\r\n\r\n $scope.loadRequestCount--;\r\n if (response.data == null || response.data == \"\" || response.status != 200) {\r\n return;\r\n }\r\n\r\n session.addFile(response.data);\r\n\r\n }, function (err) { });\r\n }\r\n\r\n $scope.objSize = function (obj) {\r\n return DatabusUtils.objSize(obj);\r\n }\r\n\r\n $scope.removeFile = function (fileGroup) {\r\n var files = $scope.session.formData.version.files;\r\n files.splice(files.findIndex(f => f.uri == fileGroup.uri), 1);\r\n $scope.session.formData.version.isConfigDirty = true;\r\n }\r\n\r\n $scope.hasError = function (errorList, error) {\r\n return errorList.includes(error);\r\n }\r\n\r\n // Fetch links using the fetch-links API of the Databus\r\n $scope.fetchFiles = function (parentUri) {\r\n\r\n $http.get('/app/publish-wizard/fetch-resource-page?url=' + encodeURIComponent(parentUri)).then(function (response) {\r\n for (var i in response.data) {\r\n var uri = response.data[i];\r\n $scope.addFile(uri);\r\n }\r\n }, function (err) {\r\n });\r\n }\r\n\r\n $scope.addFiles = function (input) {\r\n var lines = input.split('\\n');\r\n\r\n for (var line of lines) {\r\n if (line != undefined && line.length > 0) {\r\n $scope.addFile(line);\r\n }\r\n }\r\n }\r\n\r\n $scope.createTractate = function () {\r\n $scope.creatingTractate = true;\r\n $http.post('/api/tractate/v1/canonicalize', $scope.session.inputs.dataid).then(function (response) {\r\n $scope.session.formData.signature.tractate = response.data;\r\n $scope.creatingTractate = false;\r\n }, function (err) {\r\n $scope.creatingTractate = false;\r\n console.log(err);\r\n });\r\n }\r\n\r\n\r\n $scope.customPublish = async function () {\r\n var options = {}\r\n options.headers = {\r\n 'Accept': 'application/json, text/plain',\r\n 'Content-Type': 'application/json',\r\n }\r\n\r\n\r\n $scope.isPublishing = true;\r\n $http.post(`/api/publish?fetch-file-properties=true&log-level=${$scope.nerdMode.logLevel}`, $scope.nerdMode.customJson, options)\r\n .then(function (response) {\r\n $scope.publishLog = response.data.log;\r\n $scope.isPublishing = false;\r\n }, function (err) {\r\n $scope.publishLog = err.data.log;\r\n $scope.isPublishing = false;\r\n console.log(err);\r\n });\r\n }\r\n\r\n\r\n $scope.publish = async function () {\r\n var options = {}\r\n options.headers = {\r\n 'Accept': 'application/json, text/plain',\r\n 'Content-Type': 'application/json',\r\n }\r\n\r\n $scope.isPublishing = true;\r\n $http.post('/api/publish?fetch-file-properties=true&log-level=info', $scope.session.inputs.all, options)\r\n .then(function (response) {\r\n $scope.publishLog = response.data.log;\r\n $scope.isPublishing = false;\r\n }, function (err) {\r\n $scope.publishLog = err.data.log;\r\n $scope.isPublishing = false;\r\n console.log(err);\r\n });\r\n } */\r\n\r\n\r\n\r\n\r\nmodule.exports = PublishWizardController;\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/publish-wizard-controller.js?"); /***/ }), @@ -395,7 +435,17 @@ eval("const DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp \********************************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("var DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\nconst SparqlExamples = __webpack_require__(/*! ../utils/sparql-examples */ \"./js/utils/sparql-examples.js\");\n\n// Controller for the header section\nfunction SparqlEditorController($scope, $http) {\n\n\n $scope.storageKey = `${DATABUS_RESOURCE_BASE_URL}/sparql`;\n\n $scope.auth = data.auth;\n $scope.authenticated = data.auth.authenticated;\n $scope.utils = new DatabusWebappUtils($scope);\n\n\n $scope.editor = {};\n\n\n $scope.editor.exampleQueries = {};\n $scope.editor.exampleQueries.label = \"Databus Example Queries\";\n $scope.editor.exampleQueries.children = [];\n\n var simpleQueries = {\n label: \"Simple Queries\",\n children : []\n };\n\n\n var intermediateQueries = {\n label: \"Intermediate Queries\",\n children : []\n };\n\n\n simpleQueries.children.push({\n label: \"Select all Databus Groups\",\n query: `PREFIX databus: \nPREFIX rdf: \nPREFIX rdfs: \nPREFIX dct: \nPREFIX dcat: \nPREFIX sec: \nPREFIX cert: \nPREFIX foaf: \nPREFIX databus-cv: \nPREFIX dbo: \n\nSELECT DISTINCT * WHERE {\n ?s a databus:Group .\n}`\n });\n\n simpleQueries.children.push({\n label: \"Select all Databus Artifacts\",\n query: `PREFIX databus: \nPREFIX rdf: \nPREFIX rdfs: \nPREFIX dct: \nPREFIX dcat: \nPREFIX sec: \nPREFIX cert: \nPREFIX foaf: \nPREFIX databus-cv: \nPREFIX dbo: \n\nSELECT DISTINCT * WHERE {\n ?s a databus:Artifact .\n}`\n });\n\n simpleQueries.children.push({\n label: \"Select all Databus Versions\",\n query: `PREFIX databus: \nPREFIX rdf: \nPREFIX rdfs: \nPREFIX dct: \nPREFIX dcat: \nPREFIX sec: \nPREFIX cert: \nPREFIX foaf: \nPREFIX databus-cv: \nPREFIX dbo: \n\nSELECT DISTINCT * WHERE {\n ?s a databus:Version .\n}`\n });\n\n intermediateQueries.children.push({\n label: \"Latest Version of Artifact\",\n query: `PREFIX databus: \nPREFIX rdf: \nPREFIX rdfs: \nPREFIX dct: \nPREFIX dcat: \nPREFIX sec: \nPREFIX cert: \nPREFIX foaf: \nPREFIX databus-cv: \nPREFIX dbo: \n\nSELECT ?version WHERE\n{\n GRAPH ?g\n {\n ?version databus:artifact .\n ?version dct:hasVersion ?v . \n }\n} \nORDER BY DESC (STR(?v)) LIMIT 1`\n });\n\n\n $scope.editor.exampleQueries.children.push(simpleQueries);\n $scope.editor.exampleQueries.children.push(intermediateQueries);\n\n $scope.onExampleQueryClicked = function(node) {\n\n if(node.query == null) {\n return;\n }\n\n $scope.createQueryPage();\n\n var queryPage = $scope.queryData.pages[$scope.queryData.activeTab];\n\n queryPage.query = node.query;\n $scope.saveToStorage();\n }\n\n $scope.goToTab = function (index) {\n $scope.queryData.activeTab = index;\n $scope.saveToStorage();\n\n var queryPage = $scope.queryData.pages[$scope.queryData.activeTab];\n\n if ($scope.resultCache != null && $scope.resultCache[queryPage.name] != null) {\n $scope.editor.result = $scope.resultCache[queryPage.name];\n } else {\n $scope.editor.result = null;\n }\n }\n\n $scope.saveToStorage = function () {\n localStorage.setItem($scope.storageKey, JSON.stringify($scope.queryData));\n }\n\n $scope.deleteQueryPage = function ($index) {\n\n // Delete result cache entry\n var queryPage = $scope.queryData.pages[$scope.queryData.activeTab];\n if ($scope.resultCache != null && $scope.resultCache[queryPage.name] != null) {\n delete $scope.resultCache[queryPage.name];\n $scope.saveResultCache();\n }\n\n $scope.queryData.pages.splice($index, 1);\n\n if ($scope.queryData.pages.length == 0) {\n $scope.initialize();\n }\n else {\n var validTab = Math.min($scope.queryData.activeTab, $scope.queryData.pages.length - 1);\n\n if (validTab != $scope.queryData.activeTab) {\n $scope.goToTab(validTab);\n }\n }\n }\n\n $scope.createQueryPage = function () {\n\n var queryName = null;\n var queryNameIndex = 1;\n\n // find unoccupied name\n while (queryNameIndex < 100000) {\n\n // Create a candidate\n var hasName = true;\n queryName = `Query ${queryNameIndex}`;\n\n // Check if already in use\n for (var queryPage of $scope.queryData.pages) {\n if (queryPage.name == queryName) {\n hasName = false;\n }\n }\n\n // Found name, stop searching.\n if (hasName) {\n break;\n }\n\n queryNameIndex++;\n }\n\n $scope.queryData.pages.push({\n name: queryName,\n query: simpleQueries.children[0].query,\n endpoint: defaultEndpoint\n });\n\n $scope.goToTab($scope.queryData.pages.length - 1);\n\n $scope.saveToStorage();\n }\n\n $scope.saveResultCache = function () {\n sessionStorage.setItem($scope.storageKey, JSON.stringify($scope.resultCache));\n }\n\n $scope.initialize = function () {\n $scope.queryData = {};\n $scope.queryData.activeTab = 0;\n $scope.queryData.pages = [];\n $scope.createQueryPage();\n\n $scope.resultCache = {};\n $scope.saveResultCache();\n }\n\n var defaultEndpoint = `${DATABUS_RESOURCE_BASE_URL}/sparql`;\n\n var queryDataString = localStorage.getItem($scope.storageKey);\n var resultCacheString = sessionStorage.getItem($scope.storageKey);\n\n\n $scope.queryData = null;\n $scope.resultCache = JSON.parse(resultCacheString);\n\n try {\n $scope.queryData = JSON.parse(queryDataString);\n\n if ($scope.queryData == null || $scope.queryData.pages.length == 0) {\n $scope.initialize();\n }\n\n var queryPage = $scope.queryData.pages[$scope.queryData.activeTab];\n\n if ($scope.resultCache != null && $scope.resultCache[queryPage.name] != null) {\n $scope.editor.result = $scope.resultCache[queryPage.name];\n } else {\n $scope.editor.result = null;\n }\n\n }\n catch (e) {\n // Could not parse query data, create new!\n $scope.initialize();\n }\n\n $scope.editor.query = $scope.editor.exampleQueries[0];\n\n $scope.send = async function () {\n\n var queryPage = $scope.queryData.pages[$scope.queryData.activeTab];\n\n var res = await $http.post(queryPage.endpoint, { query: queryPage.query });\n\n if ($scope.resultCache == null) {\n $scope.resultCache = {};\n }\n\n $scope.resultCache[queryPage.name] = res.data;\n $scope.saveResultCache();\n\n $scope.editor.result = res.data;\n $scope.$apply();\n }\n\n $scope.insertExampleQuery = function (query) {\n $scope.editor.query = query;\n }\n\n}\n\nmodule.exports = SparqlEditorController;\n\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/sparql-editor-controller.js?"); +eval("var DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\r\nconst SparqlExamples = __webpack_require__(/*! ../utils/sparql-examples */ \"./js/utils/sparql-examples.js\");\r\n\r\n// Controller for the header section\r\nfunction SparqlEditorController($scope, $http, $location) {\r\n\r\n\r\n $scope.storageKey = `${DATABUS_RESOURCE_BASE_URL}/sparql`;\r\n\r\n $scope.auth = data.auth;\r\n $scope.authenticated = data.auth.authenticated;\r\n $scope.utils = new DatabusWebappUtils($scope);\r\n\r\n\r\n $scope.editor = {};\r\n\r\n\r\n\r\n $scope.$on('$locationChangeSuccess', function () {\r\n var hash = $location.hash();\r\n\r\n if (hash && hash.startsWith('query')) {\r\n var tabIndex = parseInt(hash.replace('query', '')) - 1;\r\n\r\n // Only change if the tab exists and is different from current\r\n if (!isNaN(tabIndex) &&\r\n tabIndex >= 0 &&\r\n tabIndex < $scope.queryData.pages.length &&\r\n $scope.queryData.activeTab !== tabIndex) {\r\n $scope.goToTab(tabIndex);\r\n $scope.$applyAsync();\r\n }\r\n }\r\n });\r\n\r\n $scope.editor.exampleQueries = {};\r\n $scope.editor.exampleQueries.label = \"Databus Example Queries\";\r\n $scope.editor.exampleQueries.children = [];\r\n\r\n var simpleQueries = {\r\n label: \"Simple Queries\",\r\n children: []\r\n };\r\n\r\n var intermediateQueries = {\r\n label: \"Intermediate Queries\",\r\n children: []\r\n };\r\n\r\n simpleQueries.children.push({\r\n label: \"Select all Databus Groups\",\r\n query: `PREFIX databus: \r\nPREFIX rdf: \r\nPREFIX rdfs: \r\nPREFIX dct: \r\nPREFIX dcat: \r\nPREFIX sec: \r\nPREFIX cert: \r\nPREFIX foaf: \r\nPREFIX databus-cv: \r\nPREFIX dbo: \r\n\r\nSELECT DISTINCT * WHERE {\r\n ?s a databus:Group .\r\n}`\r\n });\r\n\r\n simpleQueries.children.push({\r\n label: \"Select all Databus Artifacts\",\r\n query: `PREFIX databus: \r\nPREFIX rdf: \r\nPREFIX rdfs: \r\nPREFIX dct: \r\nPREFIX dcat: \r\nPREFIX sec: \r\nPREFIX cert: \r\nPREFIX foaf: \r\nPREFIX databus-cv: \r\nPREFIX dbo: \r\n\r\nSELECT DISTINCT * WHERE {\r\n ?s a databus:Artifact .\r\n}`\r\n });\r\n\r\n simpleQueries.children.push({\r\n label: \"Select all Databus Versions\",\r\n query: `PREFIX databus: \r\nPREFIX rdf: \r\nPREFIX rdfs: \r\nPREFIX dct: \r\nPREFIX dcat: \r\nPREFIX sec: \r\nPREFIX cert: \r\nPREFIX foaf: \r\nPREFIX databus-cv: \r\nPREFIX dbo: \r\n\r\nSELECT DISTINCT * WHERE {\r\n ?s a databus:Version .\r\n}`\r\n });\r\n\r\n intermediateQueries.children.push({\r\n label: \"Latest Version of Artifact\",\r\n query: `PREFIX databus: \r\nPREFIX rdf: \r\nPREFIX rdfs: \r\nPREFIX dct: \r\nPREFIX dcat: \r\nPREFIX sec: \r\nPREFIX cert: \r\nPREFIX foaf: \r\nPREFIX databus-cv: \r\nPREFIX dbo: \r\n\r\nSELECT ?version WHERE\r\n{\r\n GRAPH ?g\r\n {\r\n ?version databus:artifact .\r\n ?version dct:hasVersion ?v . \r\n }\r\n} \r\nORDER BY DESC (STR(?v)) LIMIT 1`\r\n });\r\n\r\n\r\n $scope.editor.exampleQueries.children.push(simpleQueries);\r\n $scope.editor.exampleQueries.children.push(intermediateQueries);\r\n\r\n $scope.onExampleQueryClicked = function (node) {\r\n\r\n if (node.query == null) {\r\n return;\r\n }\r\n\r\n $scope.createQueryPage();\r\n\r\n var queryPage = $scope.queryData.pages[$scope.queryData.activeTab];\r\n\r\n queryPage.query = node.query;\r\n $scope.saveToStorage();\r\n }\r\n\r\n $scope.goToTab = function (index) {\r\n $scope.queryData.activeTab = index;\r\n $scope.saveToStorage();\r\n\r\n $location.hash(`query${index + 1}`);\r\n\r\n\r\n var queryPage = $scope.queryData.pages[$scope.queryData.activeTab];\r\n\r\n if ($scope.resultCache != null && $scope.resultCache[queryPage.name] != null) {\r\n $scope.editor.result = $scope.resultCache[queryPage.name];\r\n } else {\r\n $scope.editor.result = null;\r\n }\r\n }\r\n\r\n $scope.saveToStorage = function () {\r\n localStorage.setItem($scope.storageKey, JSON.stringify($scope.queryData));\r\n }\r\n\r\n $scope.deleteQueryPage = function ($index) {\r\n\r\n // Delete result cache entry\r\n var queryPage = $scope.queryData.pages[$scope.queryData.activeTab];\r\n if ($scope.resultCache != null && $scope.resultCache[queryPage.name] != null) {\r\n delete $scope.resultCache[queryPage.name];\r\n $scope.saveResultCache();\r\n }\r\n\r\n $scope.queryData.pages.splice($index, 1);\r\n\r\n if ($scope.queryData.pages.length == 0) {\r\n $scope.initialize();\r\n }\r\n else {\r\n var validTab = Math.min($scope.queryData.activeTab, $scope.queryData.pages.length - 1);\r\n\r\n if (validTab != $scope.queryData.activeTab) {\r\n $scope.goToTab(validTab);\r\n }\r\n }\r\n }\r\n\r\n $scope.createQueryPage = function () {\r\n\r\n var queryName = null;\r\n var queryNameIndex = 1;\r\n\r\n // find unoccupied name\r\n while (queryNameIndex < 100000) {\r\n\r\n // Create a candidate\r\n var hasName = true;\r\n queryName = `Query ${queryNameIndex}`;\r\n\r\n // Check if already in use\r\n for (var queryPage of $scope.queryData.pages) {\r\n if (queryPage.name == queryName) {\r\n hasName = false;\r\n }\r\n }\r\n\r\n // Found name, stop searching.\r\n if (hasName) {\r\n break;\r\n }\r\n\r\n queryNameIndex++;\r\n }\r\n\r\n $scope.queryData.pages.push({\r\n name: queryName,\r\n query: simpleQueries.children[0].query,\r\n endpoint: defaultEndpoint\r\n });\r\n\r\n $scope.goToTab($scope.queryData.pages.length - 1);\r\n\r\n $scope.saveToStorage();\r\n }\r\n\r\n $scope.saveResultCache = function () {\r\n sessionStorage.setItem($scope.storageKey, JSON.stringify($scope.resultCache));\r\n }\r\n\r\n $scope.initialize = function () {\r\n $scope.queryData = {};\r\n $scope.queryData.activeTab = 0;\r\n $scope.queryData.pages = [];\r\n $scope.createQueryPage();\r\n\r\n $scope.resultCache = {};\r\n $scope.saveResultCache();\r\n }\r\n\r\n var defaultEndpoint = `${DATABUS_RESOURCE_BASE_URL}/sparql`;\r\n\r\n var queryDataString = localStorage.getItem($scope.storageKey);\r\n var resultCacheString = sessionStorage.getItem($scope.storageKey);\r\n\r\n\r\n $scope.queryData = null;\r\n $scope.resultCache = JSON.parse(resultCacheString);\r\n\r\n try {\r\n $scope.queryData = JSON.parse(queryDataString);\r\n\r\n if ($scope.queryData == null || $scope.queryData.pages.length == 0) {\r\n $scope.initialize();\r\n }\r\n\r\n var queryPage = $scope.queryData.pages[$scope.queryData.activeTab];\r\n\r\n if ($scope.resultCache != null && $scope.resultCache[queryPage.name] != null) {\r\n $scope.editor.result = $scope.resultCache[queryPage.name];\r\n } else {\r\n $scope.editor.result = null;\r\n }\r\n\r\n }\r\n\r\n\r\n catch (e) {\r\n // Could not parse query data, create new!\r\n $scope.initialize();\r\n }\r\n\r\n var initialHash = $location.hash();\r\n if (initialHash && initialHash.startsWith('query')) {\r\n var initialTab = parseInt(initialHash.replace('query', '')) - 1;\r\n if (!isNaN(initialTab) &&\r\n initialTab >= 0 &&\r\n initialTab < $scope.queryData.pages.length) {\r\n $scope.queryData.activeTab = initialTab;\r\n }\r\n }\r\n\r\n $scope.editor.query = $scope.editor.exampleQueries[0];\r\n\r\n $scope.send = async function () {\r\n\r\n var queryPage = $scope.queryData.pages[$scope.queryData.activeTab];\r\n\r\n try {\r\n\r\n var res = await $http.post(queryPage.endpoint, { query: queryPage.query });\r\n\r\n if ($scope.resultCache == null) {\r\n $scope.resultCache = {};\r\n }\r\n\r\n $scope.resultCache[queryPage.name] = res.data;\r\n $scope.saveResultCache();\r\n\r\n delete queryPage.err;\r\n $scope.editor.result = res.data;\r\n } catch (err) {\r\n console.log(err);\r\n queryPage.err = err;\r\n }\r\n\r\n $scope.$apply();\r\n }\r\n\r\n $scope.insertExampleQuery = function (query) {\r\n $scope.editor.query = query;\r\n }\r\n\r\n}\r\n\r\nmodule.exports = SparqlEditorController;\r\n\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/sparql-editor-controller.js?"); + +/***/ }), + +/***/ "./js/page-controller/user-settings-controller.js": +/*!********************************************************!*\ + !*** ./js/page-controller/user-settings-controller.js ***! + \********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +eval("const DatabusAlert = __webpack_require__(/*! ../components/databus-alert/databus-alert */ \"./js/components/databus-alert/databus-alert.js\");\r\nconst DatabusUris = __webpack_require__(/*! ../utils/databus-uris */ \"./js/utils/databus-uris.js\");\r\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\r\nconst JsonldUtils = __webpack_require__(/*! ../utils/jsonld-utils */ \"./js/utils/jsonld-utils.js\");\r\nconst TabNavigation = __webpack_require__(/*! ../utils/tab-navigation */ \"./js/utils/tab-navigation.js\");\r\n\r\nfunction UserSettingsController($scope, $http, $sce, $location) {\r\n $scope.auth = data.auth;\r\n $scope.accounts = data.accounts;\r\n\r\n $scope.inputs = {};\r\n\r\n $scope.inputs.newAccountLabel = \"\";\r\n $scope.inputs.newAccountName = \"\";\r\n $scope.inputs.newApiKeyName = \"\";\r\n\r\n $scope.tabNavigation = new TabNavigation($scope, $location, [\r\n ''\r\n ], function (index) {\r\n $scope.activeAccount = $scope.accounts[index - 1];\r\n });\r\n\r\n $scope.$watchCollection('accounts', function (newAccounts) {\r\n const accountNames = newAccounts.map(a => a.accountName);\r\n $scope.tabNavigation.tabKeys = [''].concat(accountNames);\r\n\r\n const currentHash = $location.hash();\r\n\r\n $scope.tabNavigation.onLocationHashChanged(currentHash, currentHash)\r\n\r\n if (currentHash && !$scope.tabNavigation.tabKeys.includes(currentHash)) {\r\n $location.hash('');\r\n }\r\n });\r\n\r\n // Iterate over each account and load its data\r\n $scope.accounts.forEach(function (account) {\r\n // Set loading state\r\n account.loading = true;\r\n\r\n var requestParams = {\r\n method: 'GET',\r\n url: '/' + encodeURIComponent(account.accountName),\r\n headers: {\r\n 'Accept': 'application/ld+json',\r\n 'X-Jsonld-Formatting': 'flatten'\r\n }\r\n }\r\n\r\n // Perform HTTP GET request to fetch additional data\r\n $http(requestParams)\r\n .then(function (response) {\r\n // Set loading to false when data is received\r\n account.loading = false;\r\n\r\n // Store additional info (stub)\r\n var graphs = response.data;\r\n var personGraph = JsonldUtils.getTypedGraph(graphs, DatabusUris.FOAF_PERSON);\r\n\r\n account.uri = `${DATABUS_RESOURCE_BASE_URL}/${account.accountName}`;\r\n account.label = JsonldUtils.getFirstProperty(personGraph, DatabusUris.FOAF_NAME);\r\n account.status = JsonldUtils.getFirstProperty(personGraph, DatabusUris.FOAF_STATUS);\r\n account.imageUrl = JsonldUtils.getProperty(personGraph, DatabusUris.FOAF_IMG);\r\n account.secretaries = [];\r\n\r\n let accountGraph = JsonldUtils.getTypedGraph(graphs, DatabusUris.DATABUS_ACCOUNT);\r\n let secretaryIds = JsonldUtils.getRefArrayProperty(accountGraph, DatabusUris.DATABUS_SECRETARY_PROPERTY);\r\n\r\n for (let secretaryId of secretaryIds) {\r\n let secretaryGraph = JsonldUtils.getGraphById(graphs, secretaryId);\r\n\r\n let secretary = {};\r\n secretary.accountName = DatabusUtils.uriToName(JsonldUtils.getProperty(secretaryGraph, DatabusUris.DATABUS_ACCOUNT_PROPERTY));\r\n secretary.hasWriteAccessTo = JsonldUtils.getRefArrayProperty(secretaryGraph, DatabusUris.DATABUS_HAS_WRITE_ACCESS_TO);\r\n\r\n account.secretaries.push(secretary);\r\n }\r\n\r\n })\r\n .catch(function (error) {\r\n // Handle error and set loading to false\r\n account.loading = false;\r\n console.error('Failed to load account data for', account.name, error);\r\n });\r\n });\r\n\r\n // Button click handler to add account\r\n $scope.addAccount = async function () {\r\n\r\n try {\r\n\r\n await $http.post(`/api/account/create`, {\r\n name: $scope.inputs.newAccountName,\r\n label: $scope.inputs.newAccountLabel\r\n });\r\n\r\n $scope.accounts.push({\r\n label: $scope.inputs.newAccountLabel,\r\n accountName: $scope.inputs.newAccountName,\r\n uri: `${DATABUS_RESOURCE_BASE_URL}/${$scope.inputs.newAccountName}`\r\n });\r\n\r\n DatabusAlert.alert($scope, true, \"Account created.\");\r\n\r\n } catch (err) {\r\n console.error(err);\r\n DatabusAlert.alert($scope, false, err.data);\r\n }\r\n };\r\n\r\n // Button click handler to save account\r\n $scope.saveAccount = async function (account) {\r\n try {\r\n await $http.post(`/api/account/update`, account);\r\n DatabusAlert.alert($scope, true, \"Account saved.\");\r\n\r\n } catch (err) {\r\n console.error(err);\r\n DatabusAlert.alert($scope, false, err.data);\r\n }\r\n\r\n };\r\n\r\n // Button click handler to delete account\r\n $scope.deleteAccount = async function (account) {\r\n try {\r\n // Find index of the account using accountName\r\n const index = $scope.accounts.findIndex(acc => acc.accountName === account.accountName);\r\n\r\n if (index === -1) {\r\n throw new Error(`Account with name \"${account.accountName}\" not found.`);\r\n }\r\n\r\n console.log(\"Deleting account with accountName:\", account.accountName);\r\n\r\n // Send delete request to server\r\n await $http.post(`/api/account/delete`, account);\r\n\r\n // Show success alert\r\n DatabusAlert.alert($scope, true, \"Account deleted.\");\r\n\r\n // Remove account from local array\r\n $scope.accounts.splice(index, 1);\r\n\r\n } catch (err) {\r\n console.error(err);\r\n const message = err.data || err.message || \"Unknown error occurred.\";\r\n DatabusAlert.alert($scope, false, message);\r\n }\r\n };\r\n\r\n\r\n\r\n\r\n $scope.goToUserSettings = function (accountName) {\r\n window.location.href = '/' + encodeURIComponent(accountName) + '#settings';\r\n }\r\n\r\n $scope.addWriteAccessUrl = function (account) {\r\n account.writeAccess.push('');\r\n };\r\n\r\n $scope.removeWriteAccessUrl = function (account, index) {\r\n account.writeAccess.splice(index, 1);\r\n };\r\n\r\n $scope.addApiKey = async function (account) {\r\n // Validate the name input only\r\n\r\n if (!$scope.inputs.newApiKeyName) {\r\n DatabusAlert.alert(\"API key name must be provided.\");\r\n return;\r\n }\r\n\r\n const postData = {\r\n accountName: account.accountName,\r\n name: $scope.inputs.newApiKeyName\r\n };\r\n\r\n try {\r\n // Send POST request to create the API key\r\n let response = await $http.post('/api/account/api-key/create', postData);\r\n\r\n if (response.data && response.data.apikey && response.data.keyname) {\r\n // Append new key to the list\r\n account.apiKeys.push({\r\n keyname: response.data.keyname,\r\n apikey: response.data.apikey\r\n });\r\n\r\n // Clear the name input field\r\n $scope.inputs.newApiKeyName = '';\r\n\r\n DatabusAlert.alert($scope, true, \"API key created.\");\r\n } else {\r\n DatabusAlert.alert($scope, false, \"Failed to create API key.\");\r\n }\r\n\r\n } catch (error) {\r\n console.error('Error creating API key:', error);\r\n const message = err.data || err.message || \"Unknown error occurred.\";\r\n DatabusAlert.alert($scope, false, message);\r\n }\r\n };\r\n\r\n\r\n $scope.deleteApiKey = async function (account, apiKey) {\r\n try {\r\n // Find index of the account using accountName\r\n const index = account.apiKeys.findIndex(key => key.keyname === apiKey.keyname);\r\n\r\n if (index === -1) {\r\n throw new Error(`API key with name \"${apiKey.keyname}\" not found.`);\r\n }\r\n\r\n console.log(\"Deleting API key with keyname:\", apiKey.keyname);\r\n\r\n // Send delete request to server\r\n await $http.post(`/api/account/api-key/delete`, { accountName: account.accountName, keyname: apiKey.keyname });\r\n account.apiKeys.splice(index, 1);\r\n\r\n // Show success alert\r\n DatabusAlert.alert($scope, true, \"API key deleted.\");\r\n\r\n } catch (err) {\r\n console.error(err);\r\n\r\n\r\n\r\n const message = err.data || err.message || \"Unknown error occurred.\";\r\n DatabusAlert.alert($scope, false, message);\r\n }\r\n };\r\n\r\n $scope.addSecretary = function (account) {\r\n if (!account.secretaries) {\r\n account.secretaries = [];\r\n }\r\n\r\n account.secretaries.push({\r\n accountName: '',\r\n hasWriteAccessTo: []\r\n });\r\n };\r\n\r\n $scope.removeSecretary = function (account, index) {\r\n account.secretaries.splice(index, 1);\r\n };\r\n\r\n $scope.addNamespace = function (account, secIndex) {\r\n account.secretaries[secIndex].hasWriteAccessTo.push('');\r\n };\r\n\r\n $scope.removeNamespace = function (account, secIndex, nsIndex) {\r\n account.secretaries[secIndex].hasWriteAccessTo.splice(nsIndex, 1);\r\n };\r\n}\r\n\r\nmodule.exports = UserSettingsController;\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/user-settings-controller.js?"); /***/ }), @@ -405,7 +455,27 @@ eval("var DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-u \**************************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("const DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\nconst JsonldUtils = __webpack_require__(/*! ../utils/jsonld-utils */ \"./js/utils/jsonld-utils.js\");\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\nconst DatabusAlert = __webpack_require__(/*! ../components/databus-alert/databus-alert */ \"./js/components/databus-alert/databus-alert.js\");\nconst QueryNode = __webpack_require__(/*! ../query-builder/query-node */ \"./js/query-builder/query-node.js\");\nconst TabNavigation = __webpack_require__(/*! ../utils/tab-navigation */ \"./js/utils/tab-navigation.js\");\nconst DatabusUris = __webpack_require__(/*! ../utils/databus-uris */ \"./js/utils/databus-uris.js\");\nconst DataIdCreator = __webpack_require__(/*! ../publish/dataid-creator */ \"./js/publish/dataid-creator.js\");\nconst QueryTemplates = __webpack_require__(/*! ../query-builder/query-templates */ \"./js/query-builder/query-templates.js\");\nconst DatabusCollectionWrapper = __webpack_require__(/*! ../collections/databus-collection-wrapper */ \"./js/collections/databus-collection-wrapper.js\");\nconst QueryBuilder = __webpack_require__(/*! ../query-builder/query-builder */ \"./js/query-builder/query-builder.js\");\nconst AppJsonFormatter = __webpack_require__(/*! ../utils/app-json-formatter */ \"./js/utils/app-json-formatter.js\");\n\nfunction VersionPageController($scope, $http, $sce, $location, collectionManager) {\n\n $scope.navigation = new TabNavigation($scope, $location, [\n 'files', 'mods', 'edit'\n ]);\n\n $scope.utils = new DatabusWebappUtils($scope, $sce);\n $scope.collectionManager = collectionManager;\n $scope.authenticated = data.auth.authenticated;\n $scope.versionGraph = data.graph;\n $scope.version = AppJsonFormatter.formatVersionData(data.graph);\n\n $scope.queryResult = {};\n $scope.addToCollectionQuery = \"\";\n $scope.collectionModalVisible = false;\n\n $scope.publisherName = DatabusUtils.uriToName(DatabusUtils.navigateUp($scope.version.uri, 3));\n $scope.canEdit = $scope.publisherName == data.auth.info.accountName;\n\n if (data.auth.authenticated && $scope.canEdit) {\n\n $scope.licenseQuery = \"\";\n $scope.filterLicenses = function (licenseQuery) {\n\n if (data.licenseData == null) {\n return;\n }\n\n // billo-suche mit lowercase und tokenization \n var tokens = licenseQuery.toLowerCase().split(' ');\n $scope.filteredLicenseList = data.licenseData.results.bindings.filter(function (l) {\n for (var token of tokens) {\n if (!l.title.value.toLowerCase().includes(token)) {\n return false;\n }\n }\n\n return true;\n });\n }\n\n $scope.filterLicenses(\"\");\n\n $scope.formData = {};\n\n $scope.formData.group = {};\n $scope.formData.group.name = DatabusUtils.uriToName(DatabusUtils.navigateUp($scope.version.uri, 2));\n\n $scope.formData.artifact = {};\n $scope.formData.artifact.name = DatabusUtils.uriToName(DatabusUtils.navigateUp($scope.version.uri, 1));\n\n var abstract = DatabusUtils.createAbstractFromDescription($scope.version.description);\n\n $scope.formData.version = {};\n $scope.formData.version.generateAbstract = abstract == $scope.version.abstract;\n $scope.formData.version.name = $scope.version.name;\n $scope.formData.version.title = $scope.version.title;\n $scope.formData.version.abstract = $scope.version.abstract;\n $scope.formData.version.description = $scope.version.description;\n $scope.formData.version.license = $scope.version.license;\n $scope.formData.version.attribution = $scope.version.attribution;\n $scope.formData.version.wasDerivedFrom = $scope.version.wasDerivedFrom;\n\n $scope.formData.signature = {};\n $scope.formData.signature.autoGenerateSignature = true;\n $scope.formData.signature.selectedPublisherUri = $scope.version.publisher;\n\n $scope.dataidCreator = new DataIdCreator($scope.formData, data.auth.info.accountName);\n }\n\n $scope.onDescriptionChanged = function () {\n if ($scope.formData == null) {\n return;\n }\n\n if (!$scope.formData.version.generateAbstract) {\n return;\n }\n\n $scope.formData.version.abstract =\n DatabusUtils.createAbstractFromDescription($scope.formData.version.description);\n }\n\n $scope.resetEdits = function () {\n $scope.formData.version.title = $scope.version.title;\n $scope.formData.version.abstract = $scope.version.abstract;\n $scope.formData.version.description = $scope.version.description;\n }\n\n $scope.saveVersion = async function () {\n\n try {\n if ($scope.dataidCreator == null) {\n return;\n }\n var relativeUri = new URL($scope.version.uri).pathname;\n\n var response = await $http({\n method: 'GET',\n url: relativeUri,\n headers: {\n 'Accept': 'application/ld+json',\n 'X-Jsonld-Formatting': 'flatten'\n }\n });\n\n var graphs = response.data;\n var versionGraph = JsonldUtils.getTypedGraph(graphs, DatabusUris.DATABUS_VERSION);\n\n JsonldUtils.setLiteral(versionGraph, DatabusUris.DCT_TITLE, DatabusUris.XSD_STRING,\n $scope.formData.version.title);\n JsonldUtils.setLiteral(versionGraph, DatabusUris.DCT_ABSTRACT, DatabusUris.XSD_STRING,\n $scope.formData.version.abstract);\n JsonldUtils.setLiteral(versionGraph, DatabusUris.DCT_DESCRIPTION, DatabusUris.XSD_STRING,\n $scope.formData.version.description);\n JsonldUtils.setLink(versionGraph, DatabusUris.DCT_LICENSE, $scope.formData.version.license);\n JsonldUtils.setLiteral(versionGraph, DatabusUris.DATABUS_ATTRIBUTION, DatabusUris.XSD_STRING,\n $scope.formData.version.attribution);\n\n if ($scope.formData.version.wasDerivedFrom) {\n JsonldUtils.setLink(versionGraph, DatabusUris.PROV_WAS_DERIVED_FROM,\n $scope.formData.version.wasDerivedFrom);\n }\n\n var relativeUri = new URL($scope.version.uri).pathname;\n var response = await $http.put(relativeUri, graphs);\n\n if (response.status == 200) {\n $scope.version.title = $scope.formData.version.title;\n $scope.version.abstract = $scope.formData.version.abstract;\n $scope.version.description = $scope.formData.version.description;\n $scope.version.license = $scope.formData.version.license;\n $scope.version.attribution = $scope.formData.version.attribution;\n $scope.version.wasDerivedFrom = $scope.formData.version.wasDerivedFrom;\n\n DatabusAlert.alert($scope, true, \"Version Saved!\");\n $scope.$apply();\n }\n } catch (err) {\n DatabusAlert.alert($scope, false, \"Failed to save version!\");\n }\n }\n\n $scope.modsAmountMinimized = 5;\n $scope.modsMaxAmount = $scope.modsAmountMinimized;\n\n $scope.showAllMods = function () {\n $scope.modsMaxAmount = 10000000;\n }\n\n $scope.hideAllMods = function () {\n $scope.modsMaxAmount = $scope.modsAmountMinimized;\n }\n\n $scope.fileSelector = {};\n $scope.fileSelector.config = {};\n $scope.fileSelector.config.columns = [];\n $scope.fileSelector.config.columns.push({ field: 'variant', label: 'Variant', width: '45%' });\n $scope.fileSelector.config.columns.push({ field: 'format', label: 'Format', width: '15%' });\n $scope.fileSelector.config.columns.push({ field: 'compression', label: 'Compression', width: '15%' });\n\n $scope.artifactNode = new QueryNode($scope.version.artifact, 'databus:artifact');\n $scope.artifactNode.setFacet('http://purl.org/dc/terms/hasVersion', $scope.version.name, true);\n\n $scope.groupNode = new QueryNode(DatabusUtils.navigateUp($scope.version.artifact), 'databus:group');\n $scope.groupNode.addChild($scope.artifactNode);\n\n $scope.collectionWidgetSelectionData = {};\n $scope.collectionWidgetSelectionData.groupNode = $scope.groupNode;\n\n $scope.onFacetSettingsChanged = function () {\n $scope.fileSelector.query = QueryBuilder.build({\n node: $scope.artifactNode,\n template: QueryTemplates.DEFAULT_FILE_TEMPLATE,\n resourceBaseUrl: DATABUS_RESOURCE_BASE_URL\n });\n\n $scope.fileSelector.fullQuery = QueryBuilder.build({\n node: $scope.artifactNode,\n template: QueryTemplates.GROUP_PAGE_FILE_BROWSER_TEMPLATE,\n resourceBaseUrl: DATABUS_RESOURCE_BASE_URL\n });\n }\n\n $scope.onFacetSettingsChanged();\n\n $scope.hideAutofill = function () {\n $scope.fileSelector.clearAutofill(function () {\n $scope.$apply();\n });\n }\n\n $scope.onFileSelectionChanged = function (numFiles, totalSize, query) {\n $scope.addToCollectionQuery = query;\n }\n\n $scope.showCollectionModal = function () {\n $scope.collectionModalVisible = true;\n }\n\n $scope.hideCollectionModal = function () {\n $scope.collectionModalVisible = false;\n }\n\n $scope.addFilter = function (selected, key) {\n $scope.fileSelector.addFilter(selected, key);\n $scope.updateQueryBuilder();\n }\n\n $scope.addQueryToCollection = function () {\n $scope.collectionManager.addElement($scope.queryBuilder.query);\n $scope.hideCollectionModal();\n };\n\n $scope.addQueryToCollection = function () {\n\n if ($scope.collectionManager.activeCollection == null) {\n return;\n }\n\n var wrapper = new DatabusCollectionWrapper($scope.collectionManager.activeCollection);\n wrapper.addCustomQueryNode('Select ' + $scope.versionData.label + ' files', $scope.addToCollectionQuery);\n $scope.collectionManager.saveLocally();\n $scope.collectionModalVisible = false;\n }\n\n $scope.formatMods = function (results) {\n var mods = results.replace(\",\", \" \");\n return $sce.trustAsHtml(mods);\n }\n\n $scope.formatModFile = function (uri) {\n return DatabusUtils.uriToName(uri);\n }\n\n $scope.downloadMetadataAsFile = async function() {\n var response = await $http({\n method: 'GET',\n url: $scope.version.uri,\n headers: {\n 'Accept': 'application/ld+json',\n }\n });\n\n $scope.download(`${$scope.version.name}.jsonld`, JSON.stringify(response.data, null, 3));\n }\n\n $scope.download = function(filename, text) {\n var element = document.createElement('a');\n element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));\n element.setAttribute('download', filename);\n element.style.display = 'none';\n document.body.appendChild(element);\n \n element.click();\n \n document.body.removeChild(element);\n }\n\n\n}\n\nmodule.exports = VersionPageController;\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/version-controller.js?"); +eval("const DatabusWebappUtils = __webpack_require__(/*! ../utils/databus-webapp-utils */ \"./js/utils/databus-webapp-utils.js\");\r\nconst JsonldUtils = __webpack_require__(/*! ../utils/jsonld-utils */ \"./js/utils/jsonld-utils.js\");\r\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\r\nconst DatabusAlert = __webpack_require__(/*! ../components/databus-alert/databus-alert */ \"./js/components/databus-alert/databus-alert.js\");\r\nconst QueryNode = __webpack_require__(/*! ../query-builder/query-node */ \"./js/query-builder/query-node.js\");\r\nconst TabNavigation = __webpack_require__(/*! ../utils/tab-navigation */ \"./js/utils/tab-navigation.js\");\r\nconst DatabusUris = __webpack_require__(/*! ../utils/databus-uris */ \"./js/utils/databus-uris.js\");\r\nconst DataIdCreator = __webpack_require__(/*! ../publish/dataid-creator */ \"./js/publish/dataid-creator.js\");\r\nconst QueryTemplates = __webpack_require__(/*! ../query-builder/query-templates */ \"./js/query-builder/query-templates.js\");\r\nconst DatabusCollectionWrapper = __webpack_require__(/*! ../collections/databus-collection-wrapper */ \"./js/collections/databus-collection-wrapper.js\");\r\nconst QueryBuilder = __webpack_require__(/*! ../query-builder/query-builder */ \"./js/query-builder/query-builder.js\");\r\nconst AppJsonFormatter = __webpack_require__(/*! ../utils/app-json-formatter */ \"./js/utils/app-json-formatter.js\");\r\n\r\nfunction VersionPageController($scope, $http, $sce, $location, collectionManager) {\r\n\r\n $scope.navigation = new TabNavigation($scope, $location, [\r\n 'files', 'mods', 'edit'\r\n ]);\r\n\r\n $scope.auth = data.auth;\r\n $scope.utils = new DatabusWebappUtils($scope, $sce);\r\n $scope.accountName = $scope.utils.getAccountName();\r\n\r\n $scope.collectionManager = collectionManager;\r\n $scope.authenticated = data.auth.authenticated;\r\n $scope.versionGraph = data.graph;\r\n $scope.version = AppJsonFormatter.formatVersionData(data.graph);\r\n\r\n $scope.queryResult = {};\r\n $scope.addToCollectionQuery = \"\";\r\n $scope.collectionModalVisible = false;\r\n\r\n $scope.publisherName = DatabusUtils.uriToName(DatabusUtils.navigateUp($scope.version.uri, 3));\r\n $scope.canEdit = $scope.accountName != null;\r\n\r\n if (data.auth.authenticated && $scope.canEdit) {\r\n\r\n $scope.licenseQuery = \"\";\r\n $scope.filterLicenses = function (licenseQuery) {\r\n\r\n if (data.licenseData == null) {\r\n return;\r\n }\r\n\r\n // billo-suche mit lowercase und tokenization \r\n var tokens = licenseQuery.toLowerCase().split(' ');\r\n $scope.filteredLicenseList = data.licenseData.results.bindings.filter(function (l) {\r\n for (var token of tokens) {\r\n if (!l.title.value.toLowerCase().includes(token)) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n });\r\n }\r\n\r\n $scope.filterLicenses(\"\");\r\n\r\n $scope.formData = {};\r\n\r\n $scope.formData.group = {};\r\n $scope.formData.group.name = DatabusUtils.uriToName(DatabusUtils.navigateUp($scope.version.uri, 2));\r\n\r\n $scope.formData.artifact = {};\r\n $scope.formData.artifact.name = DatabusUtils.uriToName(DatabusUtils.navigateUp($scope.version.uri, 1));\r\n\r\n var abstract = DatabusUtils.createAbstractFromDescription($scope.version.description);\r\n\r\n $scope.formData.version = {};\r\n $scope.formData.version.generateAbstract = abstract == $scope.version.abstract;\r\n $scope.formData.version.name = $scope.version.name;\r\n $scope.formData.version.title = $scope.version.title;\r\n $scope.formData.version.abstract = $scope.version.abstract;\r\n $scope.formData.version.description = $scope.version.description;\r\n $scope.formData.version.license = $scope.version.license;\r\n $scope.formData.version.attribution = $scope.version.attribution;\r\n $scope.formData.version.wasDerivedFrom = $scope.version.wasDerivedFrom;\r\n\r\n $scope.formData.signature = {};\r\n $scope.formData.signature.autoGenerateSignature = true;\r\n $scope.formData.signature.selectedPublisherUri = $scope.version.publisher;\r\n\r\n $scope.dataidCreator = new DataIdCreator($scope.formData, data.auth.info.accountName);\r\n }\r\n\r\n $scope.onDescriptionChanged = function () {\r\n if ($scope.formData == null) {\r\n return;\r\n }\r\n\r\n if (!$scope.formData.version.generateAbstract) {\r\n return;\r\n }\r\n\r\n $scope.formData.version.abstract =\r\n DatabusUtils.createAbstractFromDescription($scope.formData.version.description);\r\n }\r\n\r\n $scope.resetEdits = function () {\r\n $scope.formData.version.title = $scope.version.title;\r\n $scope.formData.version.abstract = $scope.version.abstract;\r\n $scope.formData.version.description = $scope.version.description;\r\n }\r\n\r\n $scope.saveVersion = async function () {\r\n\r\n try {\r\n if ($scope.dataidCreator == null) {\r\n return;\r\n }\r\n var relativeUri = new URL($scope.version.uri).pathname;\r\n\r\n var response = await $http({\r\n method: 'GET',\r\n url: relativeUri,\r\n headers: {\r\n 'Accept': 'application/ld+json',\r\n 'X-Jsonld-Formatting': 'flatten'\r\n }\r\n });\r\n\r\n var graphs = response.data;\r\n var versionGraph = JsonldUtils.getTypedGraph(graphs, DatabusUris.DATABUS_VERSION);\r\n\r\n JsonldUtils.setLiteral(versionGraph, DatabusUris.DCT_TITLE, DatabusUris.XSD_STRING,\r\n $scope.formData.version.title);\r\n JsonldUtils.setLiteral(versionGraph, DatabusUris.DCT_ABSTRACT, DatabusUris.XSD_STRING,\r\n $scope.formData.version.abstract);\r\n JsonldUtils.setLiteral(versionGraph, DatabusUris.DCT_DESCRIPTION, DatabusUris.XSD_STRING,\r\n $scope.formData.version.description);\r\n JsonldUtils.setLink(versionGraph, DatabusUris.DCT_LICENSE, $scope.formData.version.license);\r\n JsonldUtils.setLiteral(versionGraph, DatabusUris.DATABUS_ATTRIBUTION, DatabusUris.XSD_STRING,\r\n $scope.formData.version.attribution);\r\n\r\n if ($scope.formData.version.wasDerivedFrom) {\r\n JsonldUtils.setLink(versionGraph, DatabusUris.PROV_WAS_DERIVED_FROM,\r\n $scope.formData.version.wasDerivedFrom);\r\n }\r\n\r\n var response = await $http.put(`/api/register`, graphs);\r\n\r\n if (response.status == 200) {\r\n $scope.version.title = $scope.formData.version.title;\r\n $scope.version.abstract = $scope.formData.version.abstract;\r\n $scope.version.description = $scope.formData.version.description;\r\n $scope.version.license = $scope.formData.version.license;\r\n $scope.version.attribution = $scope.formData.version.attribution;\r\n $scope.version.wasDerivedFrom = $scope.formData.version.wasDerivedFrom;\r\n\r\n DatabusAlert.alert($scope, true, \"Version Saved!\");\r\n $scope.$apply();\r\n }\r\n } catch (err) {\r\n DatabusAlert.alert($scope, false, \"Failed to save version!\");\r\n }\r\n }\r\n\r\n $scope.modsAmountMinimized = 5;\r\n $scope.modsMaxAmount = $scope.modsAmountMinimized;\r\n\r\n $scope.showAllMods = function () {\r\n $scope.modsMaxAmount = 10000000;\r\n }\r\n\r\n $scope.hideAllMods = function () {\r\n $scope.modsMaxAmount = $scope.modsAmountMinimized;\r\n }\r\n\r\n $scope.fileSelector = {};\r\n $scope.fileSelector.config = {};\r\n $scope.fileSelector.config.authenticated = $scope.authenticated;\r\n $scope.fileSelector.config.columns = [];\r\n $scope.fileSelector.config.columns.push({ field: 'variant', label: 'Variant', width: '45%' });\r\n $scope.fileSelector.config.columns.push({ field: 'format', label: 'Format', width: '15%' });\r\n $scope.fileSelector.config.columns.push({ field: 'compression', label: 'Compression', width: '15%' });\r\n\r\n $scope.artifactNode = new QueryNode($scope.version.artifact, 'databus:artifact');\r\n $scope.artifactNode.setFacet('http://purl.org/dc/terms/hasVersion', $scope.version.name, true);\r\n\r\n $scope.groupNode = new QueryNode(DatabusUtils.navigateUp($scope.version.artifact), 'databus:group');\r\n $scope.groupNode.addChild($scope.artifactNode);\r\n\r\n $scope.collectionWidgetSelectionData = {};\r\n $scope.collectionWidgetSelectionData.groupNode = $scope.groupNode;\r\n\r\n $scope.onFacetSettingsChanged = function () {\r\n $scope.fileSelector.query = QueryBuilder.build({\r\n node: $scope.artifactNode,\r\n template: QueryTemplates.DEFAULT_FILE_TEMPLATE,\r\n resourceBaseUrl: DATABUS_RESOURCE_BASE_URL\r\n });\r\n\r\n $scope.fileSelector.fullQuery = QueryBuilder.build({\r\n node: $scope.artifactNode,\r\n template: QueryTemplates.GROUP_PAGE_FILE_BROWSER_TEMPLATE,\r\n resourceBaseUrl: DATABUS_RESOURCE_BASE_URL\r\n });\r\n }\r\n\r\n $scope.onFacetSettingsChanged();\r\n\r\n $scope.hideAutofill = function () {\r\n $scope.fileSelector.clearAutofill(function () {\r\n $scope.$apply();\r\n });\r\n }\r\n\r\n $scope.onFileSelectionChanged = function (numFiles, totalSize, query) {\r\n $scope.addToCollectionQuery = query;\r\n }\r\n\r\n $scope.showCollectionModal = function () {\r\n $scope.collectionModalVisible = true;\r\n }\r\n\r\n $scope.hideCollectionModal = function () {\r\n $scope.collectionModalVisible = false;\r\n }\r\n\r\n $scope.addFilter = function (selected, key) {\r\n $scope.fileSelector.addFilter(selected, key);\r\n $scope.updateQueryBuilder();\r\n }\r\n\r\n $scope.addQueryToCollection = function () {\r\n $scope.collectionManager.addElement($scope.queryBuilder.query);\r\n $scope.hideCollectionModal();\r\n };\r\n\r\n $scope.addQueryToCollection = function () {\r\n\r\n if ($scope.collectionManager.activeCollection == null) {\r\n return;\r\n }\r\n\r\n var wrapper = new DatabusCollectionWrapper($scope.collectionManager.activeCollection);\r\n wrapper.addCustomQueryNode('Select ' + $scope.versionData.label + ' files', $scope.addToCollectionQuery);\r\n $scope.collectionManager.saveLocally();\r\n $scope.collectionModalVisible = false;\r\n }\r\n\r\n $scope.formatMods = function (results) {\r\n var mods = results.replace(\",\", \" \");\r\n return $sce.trustAsHtml(mods);\r\n }\r\n\r\n $scope.formatModFile = function (uri) {\r\n return DatabusUtils.uriToName(uri);\r\n }\r\n\r\n $scope.downloadMetadataAsFile = async function () {\r\n var response = await $http({\r\n method: 'GET',\r\n url: $scope.version.uri,\r\n headers: {\r\n 'Accept': 'application/ld+json',\r\n }\r\n });\r\n\r\n $scope.download(`${$scope.version.name}.jsonld`, JSON.stringify(response.data, null, 3));\r\n }\r\n\r\n $scope.download = function (filename, text) {\r\n var element = document.createElement('a');\r\n element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));\r\n element.setAttribute('download', filename);\r\n element.style.display = 'none';\r\n document.body.appendChild(element);\r\n\r\n element.click();\r\n\r\n document.body.removeChild(element);\r\n }\r\n\r\n\r\n}\r\n\r\nmodule.exports = VersionPageController;\n\n//# sourceURL=webpack://databus-webapp/./js/page-controller/version-controller.js?"); + +/***/ }), + +/***/ "./js/publish/artifact-data.js": +/*!*************************************!*\ + !*** ./js/publish/artifact-data.js ***! + \*************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +eval("const EntityHandler = __webpack_require__(/*! ./entity-handler */ \"./js/publish/entity-handler.js\");\r\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\r\nconst DatabusUris = __webpack_require__(/*! ../utils/databus-uris */ \"./js/utils/databus-uris.js\");\r\nconst GroupData = __webpack_require__(/*! ./group-data */ \"./js/publish/group-data.js\");\r\n\r\nclass ArtifactData extends EntityHandler {\r\n constructor($http, accounts, apiKeys) {\r\n super('databus_registration_artifact_data', $http, null, accounts, apiKeys);\r\n }\r\n\r\n initialize(data) {\r\n const validAccount = data && this.accounts.some(acc => acc.accountName === data.accountName);\r\n\r\n if (validAccount) {\r\n Object.assign(this, data);\r\n } else {\r\n this.accountName = this.accounts[0]?.name;\r\n }\r\n\r\n if(this.apiKeyName == null && this.apiKeys != null && this.apiKeys.length > 0) {\r\n this.apiKeyName = this.apiKeys[0].keyname;\r\n }\r\n\r\n\r\n this.sendmode ??= 'register';\r\n this.onAccountNameChanged();\r\n this.onGroupNameChanged();\r\n }\r\n\r\n validate() {\r\n this.errors = [];\r\n this.warnings = [];\r\n\r\n if (!DatabusUtils.isValidArtifactName(this.name)) {\r\n this.errors.push('err_invalid_artifact_name');\r\n }\r\n\r\n if (!DatabusUtils.isValidGroupName(this.groupName)) {\r\n this.errors.push('err_no_group_selected');\r\n }\r\n\r\n const exists = this.artifactList?.some(a => a.name === this.name);\r\n if (exists) {\r\n this.warnings.push('warning_artifact_exists');\r\n }\r\n }\r\n \r\n getURI() {\r\n return `${DATABUS_RESOURCE_BASE_URL}/${this.accountName}/${this.groupName}/${this.name}`;\r\n }\r\n\r\n getSaveData() {\r\n return {\r\n accountName: this.accountName,\r\n groupName: this.groupName,\r\n name: this.name,\r\n title: this.title,\r\n abstract: this.abstract,\r\n description: this.description,\r\n sendmode: this.sendmode,\r\n apiKeyName: this.apiKeyName,\r\n };\r\n }\r\n\r\n\r\n async setGroupName(groupName) {\r\n if (this.groupName !== groupName) {\r\n this.groupName = groupName;\r\n await this.onGroupNameChanged();\r\n }\r\n }\r\n\r\n async onGroupNameChanged() {\r\n this.isLoadingArtifacts = true;\r\n this.artifactList = await this.sparqlClient.getArtifacts(this.accountName, this.groupName);\r\n this.isLoadingArtifacts = false;\r\n this.onChange();\r\n }\r\n\r\n \r\n\r\n updateOutputs() {\r\n const groupUri = `${DATABUS_RESOURCE_BASE_URL}/${GroupData.getStringOrMissing(this.accountName)}/${GroupData.getStringOrMissing(this.groupName)}`;\r\n\r\n this.postBody = {\r\n \"@context\": this.getContext(),\r\n \"@graph\": [\r\n {\r\n \"@id\": `${groupUri}/${GroupData.getStringOrMissing(this.name)}`,\r\n \"@type\": \"Artifact\",\r\n \"title\": this.getValidString(this.title),\r\n \"abstract\": this.getValidString(this.abstract),\r\n \"description\": this.getValidString(this.description),\r\n }\r\n ]\r\n };\r\n\r\n const payload = JSON.stringify(this.postBody, null, 2);\r\n const apiKey = this.apiKeys?.find(k => k.keyname === this.apiKeyName)?.apikey;\r\n\r\n this.curlCommand = [\r\n `curl -X POST ${DATABUS_RESOURCE_BASE_URL}/api/register \\\\`,\r\n ` -H \"X-API-KEY: ${GroupData.getStringOrMissing(apiKey)}\" \\\\`,\r\n ` -H \"Content-Type: application/json\" \\\\`,\r\n ` -d '${payload}'`\r\n ].join('\\n');\r\n }\r\n}\r\n\r\nmodule.exports = ArtifactData;\r\n\n\n//# sourceURL=webpack://databus-webapp/./js/publish/artifact-data.js?"); + +/***/ }), + +/***/ "./js/publish/databus-sparql-client.js": +/*!*********************************************!*\ + !*** ./js/publish/databus-sparql-client.js ***! + \*********************************************/ +/***/ ((module) => { + +eval("class DatabusSparqlClient {\r\n\r\n constructor($http) {\r\n this.$http = $http;\r\n }\r\n\r\n /**\r\n * Generic SPARQL query runner.\r\n * @param {string} query - SPARQL query string.\r\n * @returns {Promise} - Query result bindings.\r\n */\r\n async runQuery(query) {\r\n const config = {\r\n method: 'POST',\r\n url: `/sparql`,\r\n headers: {\r\n 'Accept': 'application/sparql-results+json',\r\n 'Content-Type': 'application/x-www-form-urlencoded'\r\n },\r\n data: `query=${encodeURIComponent(query)}`\r\n };\r\n\r\n try {\r\n const response = await this.$http(config);\r\n return response.data.results.bindings || [];\r\n } catch (err) {\r\n console.error('SPARQL query failed:', err);\r\n return [];\r\n }\r\n }\r\n\r\n /**\r\n * Fetches groups for a given Databus account.\r\n * @param {string} accountName - The account name (e.g., 'myaccount').\r\n * @returns {Promise} - List of groups with basic metadata.\r\n */\r\n async getGroups(accountName) {\r\n const query = `\r\n PREFIX databus: \r\n\r\n SELECT DISTINCT ?group WHERE {\r\n ?group a databus:Group .\r\n ?group databus:account <${DATABUS_RESOURCE_BASE_URL}/${accountName}> .\r\n }\r\n `;\r\n\r\n const bindings = await this.runQuery(query);\r\n\r\n return bindings.map(binding => ({\r\n uri: binding.group.value,\r\n name: binding.group.value.split('/').pop(),\r\n }));\r\n }\r\n\r\n async getArtifacts(accountName, groupName) {\r\n const query = `\r\n PREFIX databus: \r\n\r\n SELECT DISTINCT ?group WHERE {\r\n ?group a databus:Artifact .\r\n ?group databus:group <${DATABUS_RESOURCE_BASE_URL}/${accountName}/${groupName}> .\r\n }\r\n `;\r\n\r\n const bindings = await this.runQuery(query);\r\n\r\n return bindings.map(binding => ({\r\n uri: binding.group.value,\r\n name: binding.group.value.split('/').pop(),\r\n }));\r\n }\r\n\r\n async getVersions(accountName, groupName, artifactName) {\r\n const query = `\r\n PREFIX databus: \r\n\r\n SELECT DISTINCT ?group WHERE {\r\n ?group a databus:Version .\r\n ?group databus:artifact <${DATABUS_RESOURCE_BASE_URL}/${accountName}/${groupName}/${artifactName}> .\r\n }\r\n `;\r\n\r\n const bindings = await this.runQuery(query);\r\n\r\n return bindings.map(binding => ({\r\n uri: binding.group.value,\r\n name: binding.group.value.split('/').pop(),\r\n }));\r\n }\r\n}\r\n\r\nmodule.exports = DatabusSparqlClient;\r\n\n\n//# sourceURL=webpack://databus-webapp/./js/publish/databus-sparql-client.js?"); /***/ }), @@ -419,13 +489,33 @@ eval("const DatabusUris = __webpack_require__(/*! ../utils/databus-uris */ \"./j /***/ }), +/***/ "./js/publish/entity-handler.js": +/*!**************************************!*\ + !*** ./js/publish/entity-handler.js ***! + \**************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +eval("const DatabusUris = __webpack_require__(/*! ../utils/databus-uris */ \"./js/utils/databus-uris.js\");\r\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\r\nconst DatabusSparqlClient = __webpack_require__(/*! ./databus-sparql-client */ \"./js/publish/databus-sparql-client.js\");\r\n\r\nclass EntityHandler {\r\n constructor(storageKey, $http, $interval, accounts, apiKeys) {\r\n this.storageKey = storageKey;\r\n this.$http = $http;\r\n this.$interval = $interval;\r\n this.accounts = accounts;\r\n this.apiKeys = apiKeys;\r\n this.sparqlClient = new DatabusSparqlClient($http);\r\n\r\n const data = this._loadFromSession();\r\n this.initialize(data);\r\n }\r\n\r\n _loadFromSession() {\r\n try {\r\n const raw = window.sessionStorage.getItem(this.storageKey);\r\n return raw ? JSON.parse(raw) : null;\r\n } catch (e) {\r\n console.error(\"Failed to load session data:\", e);\r\n return null;\r\n }\r\n }\r\n\r\n save() {\r\n try {\r\n const data = this.getSaveData();\r\n const json = JSON.stringify(data);\r\n window.sessionStorage.setItem(this.storageKey, json);\r\n } catch (e) {\r\n console.error(\"Failed to save session data:\", e);\r\n }\r\n }\r\n\r\n // Abstract methods\r\n initialize(data) {\r\n throw new Error(\"Method 'initialize(data)' must be implemented.\");\r\n }\r\n\r\n getSaveData() {\r\n throw new Error(\"Method 'getSaveData()' must be implemented.\");\r\n }\r\n\r\n validate() {\r\n throw new Error(\"Method 'validate()' must be implemented.\");\r\n }\r\n\r\n updateOutputs() {\r\n throw new Error(\"Method 'updateOutputs()' must be implemented.\");\r\n }\r\n\r\n getURI() {\r\n throw new Error(\"Method 'updateOutputs()' must be implemented.\");\r\n }\r\n\r\n getValidString(value) {\r\n return value?.length > 0 ? value : undefined;\r\n }\r\n\r\n static getStringOrMissing(value) {\r\n return value?.length > 0 ? value : '!!!missing!!!';\r\n }\r\n\r\n hasError(errorKey) {\r\n return this.errors?.includes(errorKey) ?? false;\r\n }\r\n\r\n setSendMode(sendmode) {\r\n this.sendmode = sendmode;\r\n this.onChange();\r\n }\r\n\r\n getContext() {\r\n if (DATABUS_CONTEXT_URL && DatabusUtils.isValidHttpUrl(DATABUS_CONTEXT_URL)) {\r\n return DATABUS_CONTEXT_URL;\r\n }\r\n return DATABUS_CONTEXT[DatabusUris.JSONLD_CONTEXT];\r\n }\r\n\r\n async setAccountName(accountName) {\r\n if (this.accountName !== accountName) {\r\n this.accountName = accountName;\r\n await this.onAccountNameChanged();\r\n }\r\n }\r\n\r\n async onAccountNameChanged() {\r\n this.isLoadingGroups = true;\r\n this.groupList = await this.sparqlClient.getGroups(this.accountName);\r\n this.isLoadingGroups = false;\r\n this.onChange();\r\n }\r\n\r\n async setAccountName(accountName) {\r\n if (this.accountName !== accountName) {\r\n this.accountName = accountName;\r\n \r\n this.activeAccount = this.accounts?.find(a => a.accountName === this.accountName);\r\n await this.onAccountNameChanged();\r\n }\r\n }\r\n\r\n getAccount() {\r\n return this.activeAccount;\r\n \r\n }\r\n\r\n onChange() {\r\n this.updateOutputs();\r\n this.validate();\r\n this.save();\r\n }\r\n\r\n getApiKey() {\r\n return this.getAccount()?.apiKeys?.find(k => k.keyname === this.apiKeyName)?.apikey;\r\n }\r\n\r\n setApiKeyName(keyname) {\r\n this.apiKeyName = keyname;\r\n this.onChange();\r\n }\r\n\r\n async register() {\r\n try {\r\n const response = await this.$http({\r\n method: 'POST',\r\n url: `/api/register?log-level=info`,\r\n headers: {\r\n 'Accept': 'application/json',\r\n 'Content-Type': 'application/json'\r\n },\r\n data: this.postBody\r\n });\r\n\r\n return response;\r\n } catch (err) {\r\n console.error('Entity registration failed:', err);\r\n throw err;\r\n }\r\n }\r\n}\r\n\r\nmodule.exports = EntityHandler;\r\n\n\n//# sourceURL=webpack://databus-webapp/./js/publish/entity-handler.js?"); + +/***/ }), + +/***/ "./js/publish/group-data.js": +/*!**********************************!*\ + !*** ./js/publish/group-data.js ***! + \**********************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +eval("const EntityHandler = __webpack_require__(/*! ./entity-handler */ \"./js/publish/entity-handler.js\");\r\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\r\nconst DatabusUris = __webpack_require__(/*! ../utils/databus-uris */ \"./js/utils/databus-uris.js\");\r\nconst DatabusSparqlClient = __webpack_require__(/*! ./databus-sparql-client */ \"./js/publish/databus-sparql-client.js\");\r\n\r\nclass GroupData extends EntityHandler {\r\n constructor($http, accounts, apiKeys) {\r\n super('databus_registration_group_data', $http, null, accounts, apiKeys);\r\n \r\n }\r\n\r\n initialize(data) {\r\n const validAccount = data && this.accounts.some(acc => acc.accountName === data.accountName);\r\n \r\n if (validAccount) {\r\n Object.assign(this, data);\r\n } else {\r\n this.accountName = this.accounts[0]?.name;\r\n }\r\n\r\n if(this.apiKeyName == null && this.apiKeys != null && this.apiKeys.length > 0) {\r\n this.apiKeyName = this.apiKeys[0].keyname;\r\n }\r\n\r\n this.sendmode ??= 'register';\r\n this.onAccountNameChanged();\r\n }\r\n\r\n getSaveData() {\r\n return {\r\n accountName: this.accountName,\r\n name: this.name,\r\n title: this.title,\r\n abstract: this.abstract,\r\n description: this.description,\r\n sendmode: this.sendmode,\r\n apiKeyName: this.apiKeyName,\r\n };\r\n }\r\n\r\n getURI() {\r\n return `${DATABUS_RESOURCE_BASE_URL}/${this.accountName}/${this.name}`;\r\n }\r\n\r\n validate() {\r\n this.errors = [];\r\n this.warnings = [];\r\n\r\n if (!DatabusUtils.isValidGroupName(this.name)) {\r\n this.errors.push('err_invalid_group_name');\r\n }\r\n\r\n if(this.sendmode == 'curl' && !this.apiKeyName) {\r\n this.errors.push('err_no_api_key');\r\n }\r\n\r\n const exists = this.groupList?.some(g => g.name === this.name);\r\n if (exists) {\r\n this.warnings.push('warning_group_exists');\r\n }\r\n }\r\n\r\n updateOutputs() {\r\n const accountUri = `${DATABUS_RESOURCE_BASE_URL}/${GroupData.getStringOrMissing(this.accountName)}`;\r\n\r\n this.postBody = {\r\n \"@context\": this.getContext(),\r\n \"@graph\": [\r\n {\r\n \"@id\": `${accountUri}/${GroupData.getStringOrMissing(this.name)}`,\r\n \"@type\": \"Group\",\r\n \"title\": this.getValidString(this.title),\r\n \"abstract\": this.getValidString(this.abstract),\r\n \"description\": this.getValidString(this.description),\r\n }\r\n ]\r\n };\r\n\r\n const payload = JSON.stringify(this.postBody, null, 2);\r\n const apiKey = this.getApiKey();\r\n\r\n this.curlCommand = [\r\n `curl -X POST ${DATABUS_RESOURCE_BASE_URL}/api/register \\\\`,\r\n ` -H \"X-API-KEY: ${GroupData.getStringOrMissing(apiKey)}\" \\\\`,\r\n ` -H \"Content-Type: application/json\" \\\\`,\r\n ` -d '${payload}'`\r\n ].join('\\n');\r\n }\r\n}\r\n\r\nmodule.exports = GroupData;\r\n\n\n//# sourceURL=webpack://databus-webapp/./js/publish/group-data.js?"); + +/***/ }), + /***/ "./js/publish/publish-data.js": /*!************************************!*\ !*** ./js/publish/publish-data.js ***! \************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("const DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\n\n/**\n * Handles shasum creation (and possibly other file stats)\n */\nclass PublishData {\n\n constructor(data, accountData) {\n\n this.accountData = accountData;\n this.group = data != undefined ? data.group : {};\n this.artifact = data != undefined ? data.artifact : {};\n this.version = data != undefined ? data.version : {};\n this.files = data != undefined ? data.files : {};\n this.signature = data != undefined ? data.signature : undefined;\n\n if (data == null) {\n this.group.generateMetadata = 'create';\n this.group.generateAbstract = true;\n this.artifact.generateMetadata = 'create';\n this.artifact.generateAbstract = true;\n this.version.generateMetadata = 'create';\n this.version.generateAbstract = true;\n this.version.useArtifactTitle = true;\n this.signature = this.createSignatureData();\n }\n }\n\n createSignatureData() {\n var signature = {};\n signature.publisherUris = [];\n\n signature.publisherUris = this.accountData.publisherUris;\n signature.defaultPublisherUri = `${DATABUS_RESOURCE_BASE_URL}/${this.accountData.accountName}#this`\n signature.selectedPublisherUri = signature.defaultPublisherUri;\n signature.autoGenerateSignature = true;\n signature.autoGenerateSignatureLocked = false;\n signature.userSignature = '';\n\n return signature;\n }\n\n hasError(error) {\n\n }\n /**\n * Validates the tree\n */\n validate() {\n\n var hasErrors = false;\n this.group.errors = [];\n this.artifact.errors = [];\n this.version.errors = [];\n this.files.errors = [];\n this.group.warnings = [];\n this.artifact.warnings = [];\n this.version.warnings = [];\n\n\n if (!DatabusUtils.isValidGroupName(this.group.name)) {\n this.group.errors.push('err_invalid_group_title');\n hasErrors = true;\n }\n\n var self = this;\n\n var existingGroup = this.accountData.groups.filter(function (value) {\n return value.name == self.group.name;\n });\n\n if (existingGroup.length > 0 && this.group.generateMetadata == 'create') {\n this.group.warnings.push('warning_group_exists');\n }\n\n var existingArtifact = this.accountData.artifacts.filter(function (value) {\n return value.groupName == self.group.name && value.name == self.artifact.name;\n });\n\n if (existingArtifact.length > 0 && this.artifact.generateMetadata == 'create') {\n this.artifact.warnings.push('warning_artifact_exists');\n }\n\n if (this.group.generateAbstract) {\n this.group.abstract = DatabusUtils.createAbstractFromDescription(this.group.description);\n }\n\n if (this.version.generateAbstract) {\n this.version.abstract = DatabusUtils.createAbstractFromDescription(this.version.description);\n }\n\n if (this.version.useArtifactTitle) {\n this.version.title = this.artifact.title;\n }\n\n if (this.artifact.generateAbstract) {\n this.artifact.abstract = DatabusUtils.createAbstractFromDescription(this.artifact.description);\n }\n\n if (this.group.publishGroupOnly) {\n this.hasConfigurationError = hasErrors;\n return;\n }\n\n if (this.artifact.generateMetadata != 'none') {\n if (!DatabusUtils.isValidArtifactName(this.artifact.name)) {\n this.artifact.errors.push('err_invalid_artifact_title');\n hasErrors = true;\n }\n }\n\n var versionUri = `${DATABUS_RESOURCE_BASE_URL}/${this.accountData.accountName}/${this.group.name}/${this.artifact.name}/${this.version.name}`;\n\n var existingVersion = this.accountData.versions.filter(function (value) {\n return value == versionUri;\n });\n\n if (existingVersion.length > 0) {\n this.version.warnings.push('warning_version_exists');\n }\n\n if (this.version.generateMetadata != 'none') {\n\n if (!DatabusUtils.isValidVersionIdentifier(this.version.name)) {\n this.version.errors.push('err_invalid_version_title');\n hasErrors = true;\n }\n\n if (!DatabusUtils.isValidUrl(this.version.license)) {\n this.version.errors.push('err_invalid_version_license');\n hasErrors = true;\n }\n\n if (!DatabusUtils.isValidResourceText(this.version.abstract, 1)) {\n this.version.errors.push('err_invalid_version_abstract');\n hasErrors = true;\n }\n\n if (!DatabusUtils.isValidResourceText(this.version.description, 1)) {\n this.version.errors.push('err_invalid_version_description');\n hasErrors = true;\n }\n\n\n if (DatabusUtils.objSize(this.version.files) == 0) {\n this.files.errors.push('err_no_files');\n hasErrors = true;\n }\n\n if (this.version.isConfigDirty) {\n\n\n var files = [];\n for (var f in this.version.files) {\n this.version.files[f].errors = [];\n files.push(this.version.files[f]);\n }\n\n this.cvSplit(this.version, files, 0);\n this.version.isConfigDirty = false;\n }\n }\n\n this.hasConfigurationError = hasErrors;\n }\n\n addFile(file) {\n\n\n if (this.version.files == undefined) {\n this.version.files = [];\n }\n\n\n for (var f in this.version.files) {\n if (file.url == this.version.files[f].url) {\n return;\n }\n }\n\n var uri = file.url;\n var uriParts = uri.split('/');\n var name = uriParts.pop();\n var nameComponents = name.split('.');\n name = nameComponents[0];\n\n if (name.length > 50) {\n name = name.substr(0, 50) + '...';\n }\n\n name = decodeURIComponent(name);\n // Files with uri as key!!\n\n this.version.files.push({\n id: uri,\n uri: file.url,\n name: name,\n contentVariants: file.contentVariants != null ? file.contentVariants : {},\n compression: file.compression,\n formatExtension: file.formatExtension,\n rowspan: 1,\n });\n\n this.version.files.sort(function (a, b) {\n var nameA = a.name;\n var nameB = b.name;\n\n if (nameA < nameB) {\n return -1;\n }\n if (nameA > nameB) {\n return 1;\n }\n\n return 0;\n });\n\n this.version.isConfigDirty = true;\n }\n\n addContentVariant(variant) {\n\n if (variant == undefined || variant == '') {\n return;\n }\n\n if (this.version.contentVariants == undefined) {\n this.version.contentVariants = [];\n }\n\n for (var c in this.version.contentVariants) {\n if (this.version.contentVariants[c].id == variant) {\n return;\n }\n }\n\n this.version.contentVariants.push({\n label: variant,\n id: variant,\n fillRegex: '',\n toLower: true,\n pruneWhitespaces: true\n });\n\n this.version.isConfigDirty = true;\n }\n\n\n removeContentVariant(variant) {\n\n this.version.contentVariants = this.version.contentVariants.filter(function (d) {\n return d.id != variant.id;\n });\n\n for (var f in this.version.files) {\n var file = this.version.files[f];\n delete file.contentVariants[variant.id];\n }\n\n this.version.isConfigDirty = true;\n }\n\n fill(variant) {\n\n var val = variant.fillRegex;\n\n for (var file of this.version.files) {\n\n if (variant.toLower) {\n val = val.toLowerCase();\n }\n\n if (variant.pruneWhitespaces) {\n val = val.replaceAll(' ', '');\n }\n\n if (!variant.overwrite && file.contentVariants[variant.id] != undefined\n && file.contentVariants[variant.id].length > 0) {\n continue;\n }\n\n file.contentVariants[variant.id] = val;\n }\n\n this.version.isConfigDirty = true;\n }\n\n fillByRegex(variant) {\n var regex = new RegExp(variant.fillRegex);\n\n for (var f in this.version.files) {\n var file = this.version.files[f];\n var matches = file.name.match(regex);\n\n if (matches != null) {\n var val = matches[0];\n\n if (variant.toLower) {\n val = val.toLowerCase();\n }\n\n if (variant.pruneWhitespaces) {\n val = val.replaceAll(' ', '');\n }\n\n if (!variant.overwrite && file.contentVariants[variant.id] != undefined\n && file.contentVariants[variant.id].length > 0) {\n continue;\n }\n\n file.contentVariants[variant.id] = val;\n }\n }\n\n this.version.isConfigDirty = true;\n }\n\n createVersionName(v) {\n if (v == 0) {\n this.version.name = new Date().toISOString().slice(0, 10);\n }\n\n if (v == 1) {\n this.version.name = new Date().toISOString().slice(0, 13);\n }\n }\n\n getRowIndex(files, name) {\n var k = 1;\n for (var f in files) {\n if (files[f].name == name) {\n return k;\n }\n\n k++;\n }\n\n return -1;\n }\n\n\n cvSplit(artifact, files, cvIndex) {\n\n if (files.length <= 1) {\n return;\n }\n\n if (artifact.contentVariants == undefined) {\n artifact.contentVariants = [];\n }\n // if end of cvs, assign errors to all files if files.length > 1\n if (cvIndex - 2 >= artifact.contentVariants.length) {\n\n if (files.length > 1) {\n\n var cvHints = [];\n\n if (artifact.contentVariants.length == 0) {\n cvHints.push('No content variants have been added yet. Add content variants in the files panel in order to tag your files.');\n } else {\n for (var c in artifact.contentVariants) {\n var cv = artifact.contentVariants[c];\n var value = files[0].contentVariants[cv.id];\n\n if (value == undefined || value == '') {\n value = 'none';\n }\n\n cvHints.push(cv.id + ': ' + value);\n }\n }\n\n for (var f in files) {\n\n var index = 0;\n\n if (f == 0) {\n var index = this.getRowIndex(artifact.files, files[1].name);\n } else {\n var index = this.getRowIndex(artifact.files, files[0].name);\n }\n\n var errorMessage = 'The Databus requires any two files to be distinguishable by either their format, compression or any content variant. You have added a file with the exact same format, compression and content variants at row '\n + index + ' (' +\n cvHints.join(', ') + ').';\n\n files[f].errors.push({ key: 'err_duplicate_file', message: errorMessage });\n }\n }\n\n return;\n }\n\n // else create buckets and sort files into buckets\n var buckets = {};\n\n for (var f in files) {\n var file = files[f];\n\n var key = null;\n\n if (cvIndex == 0) {\n key = file.formatExtension;\n } else if (cvIndex == 1) {\n key = file.compression;\n } else {\n key = file.contentVariants[artifact.contentVariants[cvIndex - 2].id];\n }\n\n if (key == undefined || key == '') {\n key = '$_none$';\n }\n\n if (buckets[key] == undefined) {\n buckets[key] = [];\n }\n\n buckets[key].push(file);\n }\n\n // iterate buckets and call recursively\n for (var b in buckets) {\n this.cvSplit(artifact, buckets[b], cvIndex + 1);\n }\n }\n\n\n getOrCreateFileGroup(fileGroupId, name) {\n\n if (this.version.files == null) {\n this.version.files = {};\n }\n\n if (this.version.files[fileGroupId] == undefined) {\n\n this.version.files[fileGroupId] = {\n id: fileGroupId,\n name: name,\n contentVariants: {},\n distributions: [],\n artifactId: undefined,\n groupId: undefined,\n };\n }\n\n return this.version.files[fileGroupId];\n }\n\n}\n\nmodule.exports = PublishData;\n\n//# sourceURL=webpack://databus-webapp/./js/publish/publish-data.js?"); +eval("const DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\r\n\r\n/**\r\n * Handles shasum creation (and possibly other file stats)\r\n */\r\nclass PublishData {\r\n\r\n constructor(data) {\r\n\r\n if (data != null) {\r\n this.account = data.account ?? {};\r\n this.group = data.group ?? {};\r\n this.artifact = data.artifact ?? {};\r\n this.version = data.version ?? {};\r\n this.files = data.files ?? {};\r\n this.signature = data.signature;\r\n }\r\n\r\n if (data == null) {\r\n\r\n this.account = {};\r\n this.group = {};\r\n this.artifact = {};\r\n this.version = {};\r\n this.files = {};\r\n this.signature = undefined;\r\n\r\n this.group.generateMetadata = 'create';\r\n this.group.generateAbstract = true;\r\n this.artifact.generateMetadata = 'create';\r\n this.artifact.generateAbstract = true;\r\n this.version.generateMetadata = 'create';\r\n this.version.generateAbstract = true;\r\n this.version.useArtifactTitle = true;\r\n this.signature = this.createSignatureData();\r\n }\r\n }\r\n\r\n createSignatureData() {\r\n var signature = {};\r\n signature.publisherUris = [];\r\n\r\n signature.publisherUris = this.account.publisherUris;\r\n signature.defaultPublisherUri = `${DATABUS_RESOURCE_BASE_URL}/${this.account.accountName}#this`\r\n signature.selectedPublisherUri = signature.defaultPublisherUri;\r\n signature.autoGenerateSignature = true;\r\n signature.autoGenerateSignatureLocked = false;\r\n signature.userSignature = '';\r\n\r\n return signature;\r\n }\r\n\r\n hasError(error) {\r\n\r\n }\r\n\r\n clearErrors() {\r\n this.group.errors = [];\r\n this.artifact.errors = [];\r\n this.version.errors = [];\r\n this.files.errors = [];\r\n this.group.warnings = [];\r\n this.artifact.warnings = [];\r\n this.version.warnings = [];\r\n }\r\n /**\r\n * Validates the tree\r\n */\r\n validate() {\r\n\r\n var hasErrors = false;\r\n this.group.errors = [];\r\n this.artifact.errors = [];\r\n this.version.errors = [];\r\n this.files.errors = [];\r\n this.group.warnings = [];\r\n this.artifact.warnings = [];\r\n this.version.warnings = [];\r\n\r\n\r\n if (!DatabusUtils.isValidGroupName(this.group.name)) {\r\n this.group.errors.push('err_invalid_group_title');\r\n hasErrors = true;\r\n }\r\n\r\n var self = this;\r\n\r\n var existingGroup = this.account.groups.filter(function (value) {\r\n return value.name == self.group.name;\r\n });\r\n\r\n if (existingGroup.length > 0 && this.group.generateMetadata == 'create') {\r\n this.group.warnings.push('warning_group_exists');\r\n }\r\n\r\n var existingArtifact = this.account.artifacts.filter(function (value) {\r\n return value.groupName == self.group.name && value.name == self.artifact.name;\r\n });\r\n\r\n if (existingArtifact.length > 0 && this.artifact.generateMetadata == 'create') {\r\n this.artifact.warnings.push('warning_artifact_exists');\r\n }\r\n\r\n if (this.group.generateAbstract) {\r\n this.group.abstract = DatabusUtils.createAbstractFromDescription(this.group.description);\r\n }\r\n\r\n if (this.version.generateAbstract) {\r\n this.version.abstract = DatabusUtils.createAbstractFromDescription(this.version.description);\r\n }\r\n\r\n if (this.version.useArtifactTitle) {\r\n this.version.title = this.artifact.title;\r\n }\r\n\r\n if (this.artifact.generateAbstract) {\r\n this.artifact.abstract = DatabusUtils.createAbstractFromDescription(this.artifact.description);\r\n }\r\n\r\n if (this.group.publishGroupOnly) {\r\n this.hasConfigurationError = hasErrors;\r\n return;\r\n }\r\n\r\n if (this.artifact.generateMetadata != 'none') {\r\n if (!DatabusUtils.isValidArtifactName(this.artifact.name)) {\r\n this.artifact.errors.push('err_invalid_artifact_title');\r\n hasErrors = true;\r\n }\r\n }\r\n\r\n var versionUri = `${DATABUS_RESOURCE_BASE_URL}/${this.account.accountName}/${this.group.name}/${this.artifact.name}/${this.version.name}`;\r\n\r\n var existingVersion = this.account.versions.filter(function (value) {\r\n return value == versionUri;\r\n });\r\n\r\n if (existingVersion.length > 0) {\r\n this.version.warnings.push('warning_version_exists');\r\n }\r\n\r\n if (this.version.generateMetadata != 'none') {\r\n\r\n if (!DatabusUtils.isValidVersionIdentifier(this.version.name)) {\r\n this.version.errors.push('err_invalid_version_title');\r\n hasErrors = true;\r\n }\r\n\r\n if (!DatabusUtils.isValidUrl(this.version.license)) {\r\n this.version.errors.push('err_invalid_version_license');\r\n hasErrors = true;\r\n }\r\n\r\n if (!DatabusUtils.isValidResourceText(this.version.abstract, 1)) {\r\n this.version.errors.push('err_invalid_version_abstract');\r\n hasErrors = true;\r\n }\r\n\r\n if (!DatabusUtils.isValidResourceText(this.version.description, 1)) {\r\n this.version.errors.push('err_invalid_version_description');\r\n hasErrors = true;\r\n }\r\n\r\n\r\n if (DatabusUtils.objSize(this.version.files) == 0) {\r\n this.files.errors.push('err_no_files');\r\n hasErrors = true;\r\n }\r\n\r\n if (this.version.isConfigDirty) {\r\n\r\n\r\n var files = [];\r\n for (var f in this.version.files) {\r\n this.version.files[f].errors = [];\r\n files.push(this.version.files[f]);\r\n }\r\n\r\n this.cvSplit(this.version, files, 0);\r\n this.version.isConfigDirty = false;\r\n }\r\n }\r\n\r\n this.hasConfigurationError = hasErrors;\r\n }\r\n\r\n addFile(file) {\r\n\r\n\r\n if (this.version.files == undefined) {\r\n this.version.files = [];\r\n }\r\n\r\n\r\n for (var f in this.version.files) {\r\n if (file.url == this.version.files[f].url) {\r\n return;\r\n }\r\n }\r\n\r\n var uri = file.url;\r\n var uriParts = uri.split('/');\r\n var name = uriParts.pop();\r\n var nameComponents = name.split('.');\r\n name = nameComponents[0];\r\n\r\n if (name.length > 50) {\r\n name = name.substr(0, 50) + '...';\r\n }\r\n\r\n name = decodeURIComponent(name);\r\n // Files with uri as key!!\r\n\r\n this.version.files.push({\r\n id: uri,\r\n uri: file.url,\r\n name: name,\r\n contentVariants: file.contentVariants != null ? file.contentVariants : {},\r\n compression: file.compression,\r\n formatExtension: file.formatExtension,\r\n rowspan: 1,\r\n });\r\n\r\n this.version.files.sort(function (a, b) {\r\n var nameA = a.name;\r\n var nameB = b.name;\r\n\r\n if (nameA < nameB) {\r\n return -1;\r\n }\r\n if (nameA > nameB) {\r\n return 1;\r\n }\r\n\r\n return 0;\r\n });\r\n\r\n this.version.isConfigDirty = true;\r\n }\r\n\r\n addContentVariant(variant) {\r\n\r\n if (variant == undefined || variant == '') {\r\n return;\r\n }\r\n\r\n if (this.version.contentVariants == undefined) {\r\n this.version.contentVariants = [];\r\n }\r\n\r\n for (var c in this.version.contentVariants) {\r\n if (this.version.contentVariants[c].id == variant) {\r\n return;\r\n }\r\n }\r\n\r\n this.version.contentVariants.push({\r\n label: variant,\r\n id: variant,\r\n fillRegex: '',\r\n toLower: true,\r\n pruneWhitespaces: true\r\n });\r\n\r\n this.version.isConfigDirty = true;\r\n }\r\n\r\n\r\n removeContentVariant(variant) {\r\n\r\n this.version.contentVariants = this.version.contentVariants.filter(function (d) {\r\n return d.id != variant.id;\r\n });\r\n\r\n for (var f in this.version.files) {\r\n var file = this.version.files[f];\r\n delete file.contentVariants[variant.id];\r\n }\r\n\r\n this.version.isConfigDirty = true;\r\n }\r\n\r\n fill(variant) {\r\n\r\n var val = variant.fillRegex;\r\n\r\n for (var file of this.version.files) {\r\n\r\n if (variant.toLower) {\r\n val = val.toLowerCase();\r\n }\r\n\r\n if (variant.pruneWhitespaces) {\r\n val = val.replaceAll(' ', '');\r\n }\r\n\r\n if (!variant.overwrite && file.contentVariants[variant.id] != undefined\r\n && file.contentVariants[variant.id].length > 0) {\r\n continue;\r\n }\r\n\r\n file.contentVariants[variant.id] = val;\r\n }\r\n\r\n this.version.isConfigDirty = true;\r\n }\r\n\r\n fillByRegex(variant) {\r\n var regex = new RegExp(variant.fillRegex);\r\n\r\n for (var f in this.version.files) {\r\n var file = this.version.files[f];\r\n var matches = file.name.match(regex);\r\n\r\n if (matches != null) {\r\n var val = matches[0];\r\n\r\n if (variant.toLower) {\r\n val = val.toLowerCase();\r\n }\r\n\r\n if (variant.pruneWhitespaces) {\r\n val = val.replaceAll(' ', '');\r\n }\r\n\r\n if (!variant.overwrite && file.contentVariants[variant.id] != undefined\r\n && file.contentVariants[variant.id].length > 0) {\r\n continue;\r\n }\r\n\r\n file.contentVariants[variant.id] = val;\r\n }\r\n }\r\n\r\n this.version.isConfigDirty = true;\r\n }\r\n\r\n createVersionName(v) {\r\n if (v == 0) {\r\n this.version.name = new Date().toISOString().slice(0, 10);\r\n }\r\n\r\n if (v == 1) {\r\n this.version.name = new Date().toISOString().slice(0, 13);\r\n }\r\n }\r\n\r\n getRowIndex(files, name) {\r\n var k = 1;\r\n for (var f in files) {\r\n if (files[f].name == name) {\r\n return k;\r\n }\r\n\r\n k++;\r\n }\r\n\r\n return -1;\r\n }\r\n\r\n\r\n cvSplit(artifact, files, cvIndex) {\r\n\r\n if (files.length <= 1) {\r\n return;\r\n }\r\n\r\n if (artifact.contentVariants == undefined) {\r\n artifact.contentVariants = [];\r\n }\r\n // if end of cvs, assign errors to all files if files.length > 1\r\n if (cvIndex - 2 >= artifact.contentVariants.length) {\r\n\r\n if (files.length > 1) {\r\n\r\n var cvHints = [];\r\n\r\n if (artifact.contentVariants.length == 0) {\r\n cvHints.push('No content variants have been added yet. Add content variants in the files panel in order to tag your files.');\r\n } else {\r\n for (var c in artifact.contentVariants) {\r\n var cv = artifact.contentVariants[c];\r\n var value = files[0].contentVariants[cv.id];\r\n\r\n if (value == undefined || value == '') {\r\n value = 'none';\r\n }\r\n\r\n cvHints.push(cv.id + ': ' + value);\r\n }\r\n }\r\n\r\n for (var f in files) {\r\n\r\n var index = 0;\r\n\r\n if (f == 0) {\r\n var index = this.getRowIndex(artifact.files, files[1].name);\r\n } else {\r\n var index = this.getRowIndex(artifact.files, files[0].name);\r\n }\r\n\r\n var errorMessage = 'The Databus requires any two files to be distinguishable by either their format, compression or any content variant. You have added a file with the exact same format, compression and content variants at row '\r\n + index + ' (' +\r\n cvHints.join(', ') + ').';\r\n\r\n files[f].errors.push({ key: 'err_duplicate_file', message: errorMessage });\r\n }\r\n }\r\n\r\n return;\r\n }\r\n\r\n // else create buckets and sort files into buckets\r\n var buckets = {};\r\n\r\n for (var f in files) {\r\n var file = files[f];\r\n\r\n var key = null;\r\n\r\n if (cvIndex == 0) {\r\n key = file.formatExtension;\r\n } else if (cvIndex == 1) {\r\n key = file.compression;\r\n } else {\r\n key = file.contentVariants[artifact.contentVariants[cvIndex - 2].id];\r\n }\r\n\r\n if (key == undefined || key == '') {\r\n key = '$_none$';\r\n }\r\n\r\n if (buckets[key] == undefined) {\r\n buckets[key] = [];\r\n }\r\n\r\n buckets[key].push(file);\r\n }\r\n\r\n // iterate buckets and call recursively\r\n for (var b in buckets) {\r\n this.cvSplit(artifact, buckets[b], cvIndex + 1);\r\n }\r\n }\r\n\r\n\r\n getOrCreateFileGroup(fileGroupId, name) {\r\n\r\n if (this.version.files == null) {\r\n this.version.files = {};\r\n }\r\n\r\n if (this.version.files[fileGroupId] == undefined) {\r\n\r\n this.version.files[fileGroupId] = {\r\n id: fileGroupId,\r\n name: name,\r\n contentVariants: {},\r\n distributions: [],\r\n artifactId: undefined,\r\n groupId: undefined,\r\n };\r\n }\r\n\r\n return this.version.files[fileGroupId];\r\n }\r\n\r\n}\r\n\r\nmodule.exports = PublishData;\n\n//# sourceURL=webpack://databus-webapp/./js/publish/publish-data.js?"); /***/ }), @@ -435,7 +525,17 @@ eval("const DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \". \***************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\nconst DatabusUris = __webpack_require__(/*! ../utils/databus-uris */ \"./js/utils/databus-uris.js\");\nconst JsonldUtils = __webpack_require__(/*! ../utils/jsonld-utils */ \"./js/utils/jsonld-utils.js\");\nconst PublishData = __webpack_require__(/*! ./publish-data */ \"./js/publish/publish-data.js\");\nconst DataIdCreator = __webpack_require__(/*! ./dataid-creator */ \"./js/publish/dataid-creator.js\");\n\nclass PublishSession {\n\n static sessionStorageKey = 'databus_upload';\n static sessionStorageIgnoreKeys = [\n '$$hashKey',\n 'eventListeners',\n 'hasLocalChanges',\n 'fileFilterInput',\n 'fileSuggestions',\n 'progress',\n 'streamQueue'\n ];\n\n constructor($http, session, accountData) {\n\n this.$http = $http;\n this.accountData = accountData;\n\n for (var group of this.accountData.groups) {\n group.artifacts = this.accountData.artifacts.filter(function (value) {\n return value.groupName == group.name;\n });\n group.hasArtifacts = group.artifacts.length > 0;\n }\n\n this.formData = new PublishData(session != null ? session.formData : null, accountData);\n this.formData.validate();\n\n\n this.initializeField(session, 'showContext', false);\n this.initializeField(session, 'fetchFilesInput', \"\");\n this.initializeField(session, 'addFileInput', \"\");\n this.initializeField(session, 'isAccountDataLoading', true);\n this.initializeField(session, 'addFileInput', \"\");\n this.initializeField(session, 'currentArtifact', null);\n this.initializeField(session, 'currentGroup', null);\n\n\n this.availableGroups = [];\n this.availableArtifacts = [];\n this.availableVersions = [];\n\n\n this.isGroupLoading = false;\n this.isArtifactLoading = false;\n this.isVersionLoading = false;\n\n if (session != null) {\n\n if (session.currentGroup != null) {\n var group = accountData.groups.find(g => g.uri == session.currentGroup.uri);\n this.selectGroup(group);\n }\n\n if (session.currentArtifact != null) {\n var artifact = accountData.artifacts.find(a => a.uri == session.currentArtifact.uri);\n this.selectArtifact(artifact);\n }\n }\n\n\n this.dataIdCreator = new DataIdCreator(this.formData, this.accountData.accountName);\n this.inputs = this.dataIdCreator.createInputs();\n\n }\n\n selectGroup(targetGroup) {\n\n if (targetGroup == null) {\n return;\n }\n\n var group = this.formData.group;\n var artifact = this.formData.artifact;\n\n group.name = targetGroup.name;\n group.title = targetGroup.title;\n group.abstract = targetGroup.abstract;\n group.description = targetGroup.description;\n\n if (this.currentGroup == null || this.currentGroup.name != targetGroup.name) {\n this.currentGroup = targetGroup;\n\n if (this.formData.artifact.generateMetadata == 'existing') {\n this.currentArtifact = null;\n this.setCreateNewArtifact('create');\n }\n }\n }\n\n selectArtifact(targetArtifact) {\n if (targetArtifact == null) {\n return;\n }\n\n var artifact = this.formData.artifact;\n artifact.name = targetArtifact.name;\n artifact.title = targetArtifact.title;\n artifact.abstract = targetArtifact.abstract;\n artifact.description = targetArtifact.description;\n this.currentArtifact = targetArtifact;\n\n this.availableVersions = this.accountData.versions.filter(function (v) {\n return v.startsWith(targetArtifact.uri);\n });\n }\n\n selectVersion = function (versionUri) {\n\n try {\n var relativeUri = new URL(versionUri).pathname;\n var options = {\n method: 'GET',\n url: relativeUri,\n headers: {\n 'Accept': 'application/ld+json',\n 'X-Jsonld-Formatting': 'flatten'\n }\n };\n\n var version = this.formData.version;\n version.isLoading = true;\n\n var self = this;\n\n this.$http(options).then(function (response) {\n\n var version = self.formData.version;\n version.isLoading = false;\n\n var versionData = response.data;\n var versionGraph = JsonldUtils.getTypedGraph(versionData, DatabusUris.DATABUS_VERSION);\n\n version.name = DatabusUtils.uriToName(versionGraph[DatabusUris.JSONLD_ID]);\n version.title = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_TITLE);\n version.abstract = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_ABSTRACT);\n version.description = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_DESCRIPTION);\n version.attribution = JsonldUtils.getProperty(versionGraph, DatabusUris.DATABUS_ATTRIBUTION);\n version.license = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_LICENSE);\n version.derivedFrom = JsonldUtils.getProperty(versionGraph, DatabusUris.PROV_WAS_DERIVED_FROM);\n version.contentVariants = [];\n\n var contentVariantGraphs = JsonldUtils.getTypedGraphs(versionData, DatabusUris.RDF_PROPERTY);\n\n for(var contentVariantGraph of contentVariantGraphs) {\n\n var variantName = DatabusUtils.uriToName(contentVariantGraph[DatabusUris.JSONLD_ID]);\n self.formData.addContentVariant(variantName);\n }\n\n // Add Files!\n var fileGraphs = JsonldUtils.getTypedGraphs(versionData, DatabusUris.DATABUS_PART);\n version.files = [];\n\n for (var fileGraph of fileGraphs) {\n\n var fileUri = JsonldUtils.getProperty(fileGraph, DatabusUris.DCAT_DOWNLOAD_URL);\n\n \n\n var file = {\n id: fileUri,\n url: fileUri,\n name: DatabusUtils.uriToName(fileUri),\n compression: JsonldUtils.getProperty(fileGraph, DatabusUris.DATABUS_COMPRESSION),\n formatExtension: JsonldUtils.getProperty(fileGraph, DatabusUris.DATABUS_FORMAT_EXTENSION),\n contentVariants: {}\n }\n\n for(var contentVariant of version.contentVariants) {\n var variantUri = `${DatabusUris.DATABUS_CONTENT_VARIANT_PREFIX}${contentVariant.id}`;\n var variantValue = JsonldUtils.getProperty(fileGraph, variantUri);\n\n if(variantValue != null) {\n file.contentVariants[contentVariant.id] = variantValue;\n }\n }\n\n self.formData.addFile(file);\n }\n\n\n // Save the preset values\n delete version.preset;\n version.preset = JSON.parse(JSON.stringify(version));\n });\n\n\n } catch (err) {\n console.log(err);\n }\n }\n\n addFile(file) {\n this.formData.addFile(file);\n }\n\n\n setCreateNewGroup(value) {\n this.formData.group.generateMetadata = value;\n if (value == 'create') {\n this.formData.group.name = \"\";\n this.formData.group.title = \"\";\n this.formData.group.abstract = \"\";\n this.formData.group.description = \"\";\n this.formData.group.generateAbstract = true;\n this.currentGroup = null;\n\n if (this.formData.artifact.generateMetadata == 'existing') {\n this.setCreateNewArtifact('create');\n }\n } else if (value == 'existing') {\n var hasGroups = DatabusUtils.objSize(this.accountData.groups) > 0;\n\n if (!hasGroups) {\n this.setCreateNewGroup('create');\n return;\n }\n\n if (this.currentGroup == null) {\n for (var group of this.accountData.groups) {\n this.selectGroup(group);\n break;\n }\n }\n }\n }\n\n setCreateNewArtifact(value) {\n this.formData.artifact.generateMetadata = value;\n\n if (value == 'create') {\n\n this.availableVersions = [];\n this.formData.artifact.name = \"\";\n this.formData.artifact.title = \"\";\n this.formData.artifact.description = \"\";\n this.currentArtifact = null;\n\n if (this.formData.version.generateMetadata == 'existing') {\n this.setCreateNewVersion('create');\n }\n\n } else if (value == 'existing') {\n\n if (!this.currentGroup.hasArtifacts) {\n this.setCreateNewArtifact('create');\n return;\n }\n\n if (this.currentArtifact == null) {\n this.selectArtifact(this.currentGroup.artifacts[0]);\n }\n } else {\n\n this.availableVersions = [];\n if (this.formData.version.generateMetadata != 'none') {\n this.setCreateNewVersion('none');\n }\n }\n }\n\n setCreateNewVersion(value) {\n this.formData.version.generateMetadata = value;\n\n if (value == 'create') {\n\n\n } else if (value == 'existing') {\n\n if (this.availableVersions.length == 0) {\n this.setCreateNewVersion('create');\n return;\n }\n\n this.selectVersion(this.availableVersions[0]);\n }\n\n }\n currentGroupHasArtifacts() {\n if (this.formData.group.generateMetadata == 'create') {\n return false;\n }\n\n return this.currentGroup.artifacts != null && this.currentGroup.artifacts.length > 0;\n }\n\n initializeField(source, name, defaultValue) {\n this[name] = source != null ? source[name] : defaultValue;\n }\n\n save() {\n try {\n var sessionDataString = JSON.stringify(this, function (key, value) {\n if (PublishSession.sessionStorageIgnoreKeys.includes(key)) {\n return undefined;\n }\n return value;\n });\n\n window.sessionStorage.setItem(PublishSession.sessionStorageKey, sessionDataString);\n } catch (e) {\n console.log(e);\n }\n }\n\n static resume($http, accountData) {\n\n var sessionData = JSON.parse(window.sessionStorage.getItem(PublishSession.sessionStorageKey));\n\n if (sessionData == null || sessionData.accountData == null) {\n return null;\n }\n\n if (sessionData.accountData.accountName != accountData.accountName) {\n return null;\n }\n\n var publishSession = new PublishSession($http, sessionData, accountData);\n\n return publishSession;\n }\n\n onChange() {\n this.formData.validate();\n this.inputs = this.dataIdCreator.createInputs();\n this.save();\n\n if (this.dataIdCreator != undefined) {\n this.inputs = this.dataIdCreator.createInputs();\n\n this.isReadyForUpload =\n !this.formData.artifact.errors.length > 0 &&\n !this.formData.group.errors.length > 0 &&\n !this.formData.version.errors.length > 0 &&\n !this.formData.files.errors.length > 0;\n }\n }\n}\n\nmodule.exports = PublishSession;\n\n\n//# sourceURL=webpack://databus-webapp/./js/publish/publish-session.js?"); +eval("\r\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\r\nconst DatabusUris = __webpack_require__(/*! ../utils/databus-uris */ \"./js/utils/databus-uris.js\");\r\nconst JsonldUtils = __webpack_require__(/*! ../utils/jsonld-utils */ \"./js/utils/jsonld-utils.js\");\r\nconst PublishData = __webpack_require__(/*! ./publish-data */ \"./js/publish/publish-data.js\");\r\nconst DataIdCreator = __webpack_require__(/*! ./dataid-creator */ \"./js/publish/dataid-creator.js\");\r\nconst DatabusSparqlClient = __webpack_require__(/*! ./databus-sparql-client */ \"./js/publish/databus-sparql-client.js\");\r\nconst GroupHandler = __webpack_require__(/*! ./group-data */ \"./js/publish/group-data.js\");\r\nconst ArtifactHandler = __webpack_require__(/*! ./artifact-data */ \"./js/publish/artifact-data.js\");\r\nconst VersionHandler = __webpack_require__(/*! ./version-handler */ \"./js/publish/version-handler.js\");\r\n\r\nclass PublishSession {\r\n\r\n static sessionStorageKey = 'databus_upload';\r\n static sessionStorageIgnoreKeys = [\r\n '$$hashKey',\r\n 'eventListeners',\r\n 'hasLocalChanges',\r\n 'fileFilterInput',\r\n 'fileSuggestions',\r\n 'progress',\r\n 'streamQueue'\r\n ];\r\n\r\n\r\n constructor($http, $interval, accounts, apiKeys) {\r\n\r\n this.$http = $http;\r\n this.accounts = accounts;\r\n this.sparqlClient = new DatabusSparqlClient($http);\r\n this.formData = new PublishData();\r\n\r\n this.group = new GroupHandler($http, accounts, apiKeys);\r\n this.artifact = new ArtifactHandler($http, accounts, apiKeys);\r\n this.version = new VersionHandler($http, $interval, accounts, apiKeys);\r\n\r\n this.reset();\r\n }\r\n\r\n\r\n\r\n reset() {\r\n this.accountData = {};\r\n this.groupData = {};\r\n this.artifactData = {};\r\n this.versionData = {};\r\n }\r\n\r\n update() {\r\n this.validate();\r\n this.save();\r\n }\r\n\r\n async selectAccount(account) {\r\n this.accountData = {\r\n name: account.name,\r\n isValid: true\r\n };\r\n\r\n // Fetch groups for account here:\r\n this.groups = await this.sparqlClient.getGroups(this.accountData.name);\r\n \r\n\r\n this.save();\r\n }\r\n\r\n async selectGroup(targetGroup) {\r\n\r\n if (targetGroup == null) {\r\n return;\r\n }\r\n\r\n var group = this.formData.group;\r\n var artifact = this.formData.artifact;\r\n\r\n group.name = targetGroup.name;\r\n group.title = targetGroup.title;\r\n group.abstract = targetGroup.abstract;\r\n group.description = targetGroup.description;\r\n\r\n if (this.currentGroup == null || this.currentGroup.name != targetGroup.name) {\r\n this.currentGroup = targetGroup;\r\n\r\n if (this.formData.artifact.generateMetadata == 'existing') {\r\n this.currentArtifact = null;\r\n this.setCreateNewArtifact('create');\r\n }\r\n }\r\n }\r\n\r\n createNewGroup() {\r\n this.formData.group.name = \"\";\r\n this.formData.group.title = \"\";\r\n this.formData.group.abstract = \"\";\r\n this.formData.group.description = \"\";\r\n\r\n this.save();\r\n }\r\n\r\n selectArtifact(targetArtifact) {\r\n if (targetArtifact == null) {\r\n return;\r\n }\r\n\r\n var artifact = this.formData.artifact;\r\n artifact.name = targetArtifact.name;\r\n artifact.title = targetArtifact.title;\r\n artifact.abstract = targetArtifact.abstract;\r\n artifact.description = targetArtifact.description;\r\n this.currentArtifact = targetArtifact;\r\n\r\n this.availableVersions = this.accountData.versions.filter(function (v) {\r\n return v.startsWith(targetArtifact.uri);\r\n });\r\n }\r\n\r\n selectVersion = function (versionUri) {\r\n\r\n try {\r\n var relativeUri = new URL(versionUri).pathname;\r\n var options = {\r\n method: 'GET',\r\n url: relativeUri,\r\n headers: {\r\n 'Accept': 'application/ld+json',\r\n 'X-Jsonld-Formatting': 'flatten'\r\n }\r\n };\r\n\r\n var version = this.formData.version;\r\n version.isLoading = true;\r\n\r\n var self = this;\r\n\r\n this.$http(options).then(function (response) {\r\n\r\n var version = self.formData.version;\r\n version.isLoading = false;\r\n\r\n var versionData = response.data;\r\n var versionGraph = JsonldUtils.getTypedGraph(versionData, DatabusUris.DATABUS_VERSION);\r\n\r\n version.name = DatabusUtils.uriToName(versionGraph[DatabusUris.JSONLD_ID]);\r\n version.title = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_TITLE);\r\n version.abstract = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_ABSTRACT);\r\n version.description = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_DESCRIPTION);\r\n version.attribution = JsonldUtils.getProperty(versionGraph, DatabusUris.DATABUS_ATTRIBUTION);\r\n version.license = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_LICENSE);\r\n version.derivedFrom = JsonldUtils.getProperty(versionGraph, DatabusUris.PROV_WAS_DERIVED_FROM);\r\n version.contentVariants = [];\r\n\r\n var contentVariantGraphs = JsonldUtils.getTypedGraphs(versionData, DatabusUris.RDF_PROPERTY);\r\n\r\n for (var contentVariantGraph of contentVariantGraphs) {\r\n\r\n var variantName = DatabusUtils.uriToName(contentVariantGraph[DatabusUris.JSONLD_ID]);\r\n self.formData.addContentVariant(variantName);\r\n }\r\n\r\n // Add Files!\r\n var fileGraphs = JsonldUtils.getTypedGraphs(versionData, DatabusUris.DATABUS_PART);\r\n version.files = [];\r\n\r\n for (var fileGraph of fileGraphs) {\r\n\r\n var fileUri = JsonldUtils.getProperty(fileGraph, DatabusUris.DCAT_DOWNLOAD_URL);\r\n\r\n\r\n\r\n var file = {\r\n id: fileUri,\r\n url: fileUri,\r\n name: DatabusUtils.uriToName(fileUri),\r\n compression: JsonldUtils.getProperty(fileGraph, DatabusUris.DATABUS_COMPRESSION),\r\n formatExtension: JsonldUtils.getProperty(fileGraph, DatabusUris.DATABUS_FORMAT_EXTENSION),\r\n contentVariants: {}\r\n }\r\n\r\n for (var contentVariant of version.contentVariants) {\r\n var variantUri = `${DatabusUris.DATABUS_CONTENT_VARIANT_PREFIX}${contentVariant.id}`;\r\n var variantValue = JsonldUtils.getProperty(fileGraph, variantUri);\r\n\r\n if (variantValue != null) {\r\n file.contentVariants[contentVariant.id] = variantValue;\r\n }\r\n }\r\n\r\n self.formData.addFile(file);\r\n }\r\n\r\n\r\n // Save the preset values\r\n delete version.preset;\r\n version.preset = JSON.parse(JSON.stringify(version));\r\n });\r\n\r\n\r\n } catch (err) {\r\n console.log(err);\r\n }\r\n }\r\n\r\n addFile(file) {\r\n this.formData.addFile(file);\r\n }\r\n\r\n\r\n setCreateNewGroup(value) {\r\n this.formData.group.generateMetadata = value;\r\n if (value == 'create') {\r\n this.formData.group.name = \"\";\r\n this.formData.group.title = \"\";\r\n this.formData.group.abstract = \"\";\r\n this.formData.group.description = \"\";\r\n this.formData.group.generateAbstract = true;\r\n this.currentGroup = null;\r\n\r\n if (this.formData.artifact.generateMetadata == 'existing') {\r\n this.setCreateNewArtifact('create');\r\n }\r\n } else if (value == 'existing') {\r\n var hasGroups = DatabusUtils.objSize(this.accountData.groups) > 0;\r\n\r\n if (!hasGroups) {\r\n this.setCreateNewGroup('create');\r\n return;\r\n }\r\n\r\n if (this.currentGroup == null) {\r\n for (var group of this.accountData.groups) {\r\n this.selectGroup(group);\r\n break;\r\n }\r\n }\r\n }\r\n }\r\n\r\n setCreateNewArtifact(value) {\r\n this.formData.artifact.generateMetadata = value;\r\n\r\n if (value == 'create') {\r\n\r\n this.availableVersions = [];\r\n this.formData.artifact.name = \"\";\r\n this.formData.artifact.title = \"\";\r\n this.formData.artifact.description = \"\";\r\n this.currentArtifact = null;\r\n\r\n if (this.formData.version.generateMetadata == 'existing') {\r\n this.setCreateNewVersion('create');\r\n }\r\n\r\n } else if (value == 'existing') {\r\n\r\n if (!this.currentGroup.hasArtifacts) {\r\n this.setCreateNewArtifact('create');\r\n return;\r\n }\r\n\r\n if (this.currentArtifact == null) {\r\n this.selectArtifact(this.currentGroup.artifacts[0]);\r\n }\r\n } else {\r\n\r\n this.availableVersions = [];\r\n if (this.formData.version.generateMetadata != 'none') {\r\n this.setCreateNewVersion('none');\r\n }\r\n }\r\n }\r\n\r\n setCreateNewVersion(value) {\r\n this.formData.version.generateMetadata = value;\r\n\r\n if (value == 'create') {\r\n\r\n\r\n } else if (value == 'existing') {\r\n\r\n if (this.availableVersions.length == 0) {\r\n this.setCreateNewVersion('create');\r\n return;\r\n }\r\n\r\n this.selectVersion(this.availableVersions[0]);\r\n }\r\n\r\n }\r\n currentGroupHasArtifacts() {\r\n if (this.formData.group.generateMetadata == 'create') {\r\n return false;\r\n }\r\n\r\n return this.currentGroup.artifacts != null && this.currentGroup.artifacts.length > 0;\r\n }\r\n\r\n initializeField(source, name, defaultValue) {\r\n this[name] = source != null ? source[name] : defaultValue;\r\n }\r\n\r\n save() {\r\n\r\n let data = {\r\n accountData: this.accountData,\r\n groupData: this.groupData,\r\n artifactData: this.artifactData,\r\n versionData: this.versionData,\r\n formData: this.formData,\r\n }\r\n\r\n\r\n try {\r\n var sessionDataString = JSON.stringify(data, function (key, value) {\r\n if (PublishSession.sessionStorageIgnoreKeys.includes(key)) {\r\n return undefined;\r\n }\r\n return value;\r\n });\r\n\r\n window.sessionStorage.setItem(PublishSession.sessionStorageKey, sessionDataString);\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n }\r\n\r\n static resume($http, sub, accountData) {\r\n\r\n var sessionData = JSON.parse(window.sessionStorage.getItem(PublishSession.sessionStorageKey));\r\n\r\n if (sessionData == null || sessionData.sub == null) {\r\n return null;\r\n }\r\n\r\n if (sub != sessionData.sub) {\r\n return null;\r\n }\r\n\r\n var publishSession = new PublishSession($http, sessionData, accountData);\r\n\r\n return publishSession;\r\n }\r\n\r\n onChange() {\r\n this.validate();\r\n this.inputs = this.dataIdCreator.createInputs();\r\n this.save();\r\n\r\n if (this.dataIdCreator != undefined) {\r\n this.inputs = this.dataIdCreator.createInputs();\r\n\r\n this.isReadyForUpload =\r\n !this.formData.artifact.errors.length > 0 &&\r\n !this.formData.group.errors.length > 0 &&\r\n !this.formData.version.errors.length > 0 &&\r\n !this.formData.files.errors.length > 0;\r\n }\r\n }\r\n\r\n onChangeGroup() {\r\n\r\n let group = this.formData.group;\r\n\r\n group.errors = [];\r\n group.warnings = [];\r\n\r\n if (!DatabusUtils.isValidGroupName(group.name)) {\r\n group.errors.push('err_invalid_group_name');\r\n }\r\n\r\n var existingGroup = this.groups.filter(function (value) {\r\n return value.name == self.group.name;\r\n });\r\n\r\n if (existingGroup.length > 0 && group.mode == 'create') {\r\n group.warnings.push('warning_group_exists');\r\n }\r\n\r\n this.save();\r\n }\r\n\r\n getValidString(value) {\r\n if (value == undefined || value.length == 0) {\r\n return undefined;\r\n }\r\n\r\n return value;\r\n }\r\n\r\n updateGroupBody() {\r\n var accountUri = `${DATABUS_RESOURCE_BASE_URL}/${this.accountData.name}`;\r\n\r\n this.groupBody = {\r\n \"@context\": this.getContext(),\r\n \"@graph\": [\r\n {\r\n \"@id\": `${accountUri}/${this.formData.group.name}`,\r\n \"@type\": \"Group\",\r\n \"title\": this.getValidString(this.formData.group.title),\r\n \"abstract\": this.getValidString(this.formData.group.abstract),\r\n \"description\": this.getValidString(this.formData.group.description)\r\n }\r\n ]\r\n };\r\n }\r\n\r\n getContext() {\r\n if (DATABUS_CONTEXT_URL != undefined && DatabusUtils.isValidHttpUrl(DATABUS_CONTEXT_URL)) {\r\n return DATABUS_CONTEXT_URL;\r\n }\r\n\r\n return DATABUS_CONTEXT[DatabusUris.JSONLD_CONTEXT];\r\n }\r\n\r\n onChangeArtifact() {\r\n\r\n let artifact = this.formData.artifact;\r\n\r\n artifact.errors = [];\r\n artifact.warnings = [];\r\n\r\n if (!DatabusUtils.isValidArtifactName(artifact.name)) {\r\n artifact.errors.push('err_invalid_artifact_name');\r\n }\r\n\r\n if (this.artifacts != null) {\r\n var existingArtifact = this.artifacts.filter(function (value) {\r\n return value.name == self.group.name;\r\n });\r\n\r\n if (existingArtifact.length > 0 && artifact.mode == 'create') {\r\n artifact.warnings.push('warning_group_exists');\r\n }\r\n }\r\n\r\n this.save();\r\n\r\n }\r\n}\r\n\r\nmodule.exports = PublishSession;\r\n\n\n//# sourceURL=webpack://databus-webapp/./js/publish/publish-session.js?"); + +/***/ }), + +/***/ "./js/publish/version-handler.js": +/*!***************************************!*\ + !*** ./js/publish/version-handler.js ***! + \***************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +eval("const EntityHandler = __webpack_require__(/*! ./entity-handler */ \"./js/publish/entity-handler.js\");\r\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\r\nconst DatabusUris = __webpack_require__(/*! ../utils/databus-uris */ \"./js/utils/databus-uris.js\");\r\nconst GroupData = __webpack_require__(/*! ./group-data */ \"./js/publish/group-data.js\");\r\n\r\nclass VersionHandler extends EntityHandler {\r\n constructor($http, $interval, accounts, apiKeys) {\r\n super('databus_registration_version_data', $http, $interval, accounts, apiKeys);\r\n }\r\n\r\n initialize(data) {\r\n const validAccount = data && this.accounts.some(acc => acc.accountName === data.accountName);\r\n\r\n if (validAccount) {\r\n Object.assign(this, data);\r\n } else {\r\n this.accountName = this.accounts[0]?.name;\r\n }\r\n\r\n if (this.apiKeyName == null && this.apiKeys != null && this.apiKeys.length > 0) {\r\n this.apiKeyName = this.apiKeys[0].keyname;\r\n }\r\n\r\n this.pageIndex ??= 0;\r\n this.sendmode ??= 'register';\r\n this.files ??= [];\r\n this.contentVariants ??= [];\r\n\r\n if (!this.contentVariants.some(v => v.id == 'formatExtension')) {\r\n this.contentVariants.push({\r\n label: 'Format',\r\n id: 'formatExtension',\r\n custom: false\r\n });\r\n }\r\n\r\n if (!this.contentVariants.some(v => v.id == 'compression')) {\r\n this.contentVariants.push({\r\n label: 'Compression',\r\n id: 'compression',\r\n custom: false\r\n });\r\n }\r\n\r\n let self = this;\r\n\r\n this.$interval(function () {\r\n if (self.hasLicenseQueryChanged) {\r\n\r\n self.$http.get(`/app/publish-wizard/licenses?limit=30&keyword=${self.licenseQuery}`)\r\n .then(function (response) {\r\n self.filteredLicenseList = response.data.results.bindings;\r\n });\r\n\r\n self.hasLicenseQueryChanged = false;\r\n }\r\n\r\n }, 300);\r\n\r\n this.licenseQuery = \"\";\r\n this.filterLicenses();\r\n this.onAccountNameChanged();\r\n this.onGroupNameChanged();\r\n this.onArtifactNameChanged();\r\n }\r\n\r\n getURI() {\r\n return `${DATABUS_RESOURCE_BASE_URL}/${this.accountName}/${this.groupName}/${this.artifactName}/${this.name}`;\r\n }\r\n\r\n setLicense(license) {\r\n this.license = license;\r\n this.onChange();\r\n }\r\n\r\n filterLicenses() {\r\n this.hasLicenseQueryChanged = true;\r\n }\r\n\r\n validate() {\r\n this.errors = [];\r\n this.warnings = [];\r\n\r\n if (!DatabusUtils.isValidVersionIdentifier(this.name)) {\r\n this.errors.push('err_invalid_version_name');\r\n }\r\n\r\n if (!DatabusUtils.isValidGroupName(this.groupName)) {\r\n this.errors.push('err_no_group_selected');\r\n }\r\n\r\n if (!DatabusUtils.isValidArtifactName(this.artifactName)) {\r\n this.errors.push('err_no_artifact_selected');\r\n }\r\n\r\n if (!DatabusUtils.isValidResourceLabel(this.title)) {\r\n this.errors.push('err_invalid_version_title');\r\n }\r\n\r\n if (!DatabusUtils.isValidResourceText(this.abstract, 1)) {\r\n this.errors.push('err_invalid_version_abstract');\r\n }\r\n\r\n if (!DatabusUtils.isValidResourceText(this.description, 1)) {\r\n this.errors.push('err_invalid_version_description');\r\n }\r\n\r\n if (!DatabusUtils.isValidUrl(this.license)) {\r\n this.errors.push('err_invalid_version_license');\r\n }\r\n\r\n if (this.files.length == 0) {\r\n this.errors.push('err_no_files');\r\n }\r\n\r\n for (let file of this.files) {\r\n file.errors = [];\r\n }\r\n\r\n this.fileErrors = [];\r\n\r\n this.cvSplit(this.files, 0);\r\n\r\n for (let file of this.files) {\r\n for (let error of file.errors) {\r\n this.errors.push(error);\r\n this.fileErrors.push(error);\r\n }\r\n }\r\n\r\n const exists = this.artifactList?.some(a => a.name === this.name);\r\n if (exists) {\r\n this.warnings.push('warning_artifact_exists');\r\n }\r\n }\r\n\r\n getSaveData() {\r\n return {\r\n accountName: this.accountName,\r\n groupName: this.groupName,\r\n artifactName: this.artifactName,\r\n name: this.name,\r\n title: this.title,\r\n abstract: this.abstract,\r\n description: this.description,\r\n sendmode: this.sendmode,\r\n apiKeyName: this.apiKeyName,\r\n wasDerivedFrom: this.wasDerivedFrom,\r\n attribution: this.attribution,\r\n license: this.license,\r\n pageIndex: this.pageIndex,\r\n contentVariants: this.contentVariants,\r\n files: this.files\r\n };\r\n }\r\n\r\n\r\n async setGroupName(groupName) {\r\n if (this.groupName !== groupName) {\r\n this.groupName = groupName;\r\n await this.onGroupNameChanged();\r\n }\r\n }\r\n\r\n async onGroupNameChanged() {\r\n this.isLoadingArtifacts = true;\r\n this.artifactList = await this.sparqlClient.getArtifacts(this.accountName, this.groupName);\r\n this.isLoadingArtifacts = false;\r\n this.onChange();\r\n }\r\n\r\n async setArtifactName(artifactName) {\r\n if (this.artifactName !== artifactName) {\r\n this.artifactName = artifactName;\r\n await this.onArtifactNameChanged();\r\n }\r\n }\r\n\r\n async onArtifactNameChanged() {\r\n this.isLoadingVersions = true;\r\n this.versionList = await this.sparqlClient.getVersions(this.accountName, this.groupName, this.artifactName);\r\n this.isLoadingVersions = false;\r\n this.onChange();\r\n }\r\n\r\n\r\n updateOutputs() {\r\n\r\n const artifactUri = `${DATABUS_RESOURCE_BASE_URL}/${GroupData.getStringOrMissing(this.accountName)}/${GroupData.getStringOrMissing(this.groupName)}/${GroupData.getStringOrMissing(this.artifactName)}`;\r\n let versionUri = `${artifactUri}/${GroupData.getStringOrMissing(this.name)}`;\r\n\r\n let graph = {\r\n \"@id\": versionUri,\r\n \"@type\": \"Version\",\r\n \"title\": this.getValidString(this.title),\r\n \"abstract\": this.getValidString(this.abstract),\r\n \"description\": this.getValidString(this.description),\r\n \"license\": this.getValidString(this.license),\r\n \"attribution\": this.getValidString(this.attribution),\r\n \"wasDerivedFrom\": this.getValidString(this.wasDerivedFrom),\r\n }\r\n\r\n graph.distribution = [];\r\n\r\n\r\n let customVariants = [];\r\n\r\n for (var fg in this.files) {\r\n\r\n var file = this.files[fg];\r\n\r\n var variantSuffix = '';\r\n for (var c in this.contentVariants) {\r\n var cv = this.contentVariants[c];\r\n var value = file.contentVariants[cv.id];\r\n\r\n if (value == undefined || value == \"\") {\r\n continue;\r\n }\r\n\r\n variantSuffix += '_' + cv.id + '=' + value;\r\n }\r\n\r\n let fileName = this.artifactName;\r\n\r\n var distributionUri = `${versionUri}#${fileName}`;\r\n var fileUri = `${versionUri}/${fileName}${variantSuffix}`;\r\n\r\n distributionUri += variantSuffix;\r\n\r\n let formatExtension = this.getValidString(file.contentVariants['formatExtension']);\r\n\r\n if (formatExtension == undefined) {\r\n formatExtension = 'none';\r\n }\r\n\r\n if (formatExtension != 'none') {\r\n distributionUri += '.' + formatExtension;\r\n fileUri += '.' + formatExtension;\r\n }\r\n\r\n let compression = this.getValidString(file.contentVariants['compression']);\r\n\r\n if (compression == undefined) {\r\n compression = 'none';\r\n }\r\n\r\n if (compression != 'none') {\r\n distributionUri += '.' + compression;\r\n fileUri += '.' + compression;\r\n }\r\n\r\n var distribution = {\r\n \"@type\": \"Part\",\r\n \"formatExtension\": formatExtension,\r\n \"compression\": compression,\r\n \"downloadURL\": file.uri,\r\n \"byteSize\": file.byteSize,\r\n \"sha256sum\": file.sha256sum,\r\n };\r\n\r\n for (var c in this.contentVariants) {\r\n var cv = this.contentVariants[c];\r\n\r\n if (!cv.custom) {\r\n continue;\r\n }\r\n\r\n var value = file.contentVariants[cv.id];\r\n\r\n if (value == undefined || value == \"\") {\r\n continue;\r\n }\r\n\r\n distribution['dcv:' + cv.label] = value;\r\n\r\n if (!customVariants.includes(cv.id)) {\r\n customVariants.push(cv.id);\r\n }\r\n }\r\n\r\n graph.distribution.push(distribution);\r\n }\r\n\r\n\r\n\r\n\r\n this.postBody = {\r\n \"@context\": this.getContext(),\r\n \"@graph\": [\r\n graph\r\n ]\r\n };\r\n\r\n\r\n\r\n const payload = JSON.stringify(this.postBody, null, 2);\r\n const apiKey = this.apiKeys?.find(k => k.keyname === this.apiKeyName)?.apikey;\r\n\r\n this.curlCommand = [\r\n `curl -X POST ${DATABUS_RESOURCE_BASE_URL}/api/register \\\\`,\r\n ` -H \"X-API-KEY: ${GroupData.getStringOrMissing(apiKey)}\" \\\\`,\r\n ` -H \"Content-Type: application/json\" \\\\`,\r\n ` -d '${payload}'`\r\n ].join('\\n');\r\n }\r\n\r\n createVersionName(v) {\r\n if (v == 0) {\r\n this.name = new Date().toISOString().slice(0, 10);\r\n }\r\n\r\n if (v == 1) {\r\n this.name = new Date().toISOString().slice(0, 13);\r\n }\r\n\r\n this.onChange();\r\n }\r\n\r\n changePage(diff) {\r\n this.pageIndex = Math.max(0, this.pageIndex + diff);\r\n this.onChange();\r\n };\r\n\r\n addContentVariant(variant) {\r\n\r\n if (variant == undefined || variant == '') {\r\n return;\r\n }\r\n\r\n if (this.contentVariants == undefined) {\r\n this.contentVariants = [];\r\n }\r\n\r\n for (var c in this.contentVariants) {\r\n if (this.contentVariants[c].id == variant) {\r\n return;\r\n }\r\n }\r\n\r\n this.contentVariants.push({\r\n label: variant,\r\n id: DatabusUtils.uuidv4(),\r\n fillRegex: '',\r\n toLower: true,\r\n pruneWhitespaces: true,\r\n custom: true,\r\n });\r\n\r\n this.onChange();\r\n }\r\n\r\n\r\n removeContentVariant(variant) {\r\n this.contentVariants = this.contentVariants.filter(function (d) {\r\n return d.id != variant.id;\r\n });\r\n\r\n for (var f in this.files) {\r\n var file = this.files[f];\r\n delete file.contentVariants[variant.id];\r\n }\r\n\r\n this.editContentVariant = null;\r\n this.onChange();\r\n }\r\n\r\n addFiles(input) {\r\n var lines = input.split('\\n');\r\n for (var line of lines) {\r\n if (line != undefined && line.length > 0) {\r\n this.addFile(line);\r\n }\r\n }\r\n }\r\n\r\n addFile(file) {\r\n\r\n if (typeof file === 'string') {\r\n file = { url: file };\r\n }\r\n\r\n if (this.files == undefined) {\r\n this.files = [];\r\n }\r\n\r\n // Check if already added\r\n for (var f in this.files) {\r\n if (file.url == this.files[f].url) {\r\n return;\r\n }\r\n }\r\n\r\n var uri = file.url;\r\n var uriParts = uri.split('/');\r\n var name = uriParts.pop();\r\n var nameComponents = name.split('.');\r\n name = nameComponents[0];\r\n\r\n if (name.length > 50) {\r\n name = name.substr(0, 50) + '...';\r\n }\r\n\r\n name = decodeURIComponent(name);\r\n // Files with uri as key!!\r\n\r\n this.files.push({\r\n id: uri,\r\n uri: file.url,\r\n name: name,\r\n contentVariants: file.contentVariants != null ? file.contentVariants : {},\r\n rowspan: 1,\r\n });\r\n\r\n this.files.sort(function (a, b) {\r\n var nameA = a.name;\r\n var nameB = b.name;\r\n\r\n if (nameA < nameB) {\r\n return -1;\r\n }\r\n if (nameA > nameB) {\r\n return 1;\r\n }\r\n\r\n return 0;\r\n });\r\n\r\n let k = 1;\r\n\r\n for (let file of this.files) {\r\n file.rowIndex = k++;\r\n }\r\n\r\n this.onChange();\r\n }\r\n\r\n removeFile = function (file, index) {\r\n this.files.splice(index, 1);\r\n this.onChange();\r\n }\r\n\r\n\r\n fill(variant) {\r\n\r\n var val = variant.fillRegex;\r\n\r\n for (var file of this.files) {\r\n\r\n if (variant.toLower) {\r\n val = val.toLowerCase();\r\n }\r\n\r\n if (variant.pruneWhitespaces) {\r\n val = val.replaceAll(' ', '');\r\n }\r\n\r\n if (!variant.overwrite && file.contentVariants[variant.id] != undefined\r\n && file.contentVariants[variant.id].length > 0) {\r\n continue;\r\n }\r\n\r\n file.contentVariants[variant.id] = val;\r\n }\r\n\r\n this.onChange();\r\n }\r\n\r\n fillByRegex(variant) {\r\n var regex = new RegExp(variant.fillRegex);\r\n\r\n for (var file of this.files) {\r\n var matches = file.name.match(regex);\r\n\r\n if (matches != null) {\r\n var val = matches[0];\r\n\r\n if (variant.toLower) {\r\n val = val.toLowerCase();\r\n }\r\n\r\n if (variant.pruneWhitespaces) {\r\n val = val.replaceAll(' ', '');\r\n }\r\n\r\n if (!variant.overwrite && file.contentVariants[variant.id] != undefined\r\n && file.contentVariants[variant.id].length > 0) {\r\n continue;\r\n }\r\n\r\n file.contentVariants[variant.id] = val;\r\n }\r\n }\r\n\r\n this.onChange();\r\n }\r\n\r\n getRowIndex(files, name) {\r\n var k = 1;\r\n for (var f in files) {\r\n if (files[f].name == name) {\r\n return k;\r\n }\r\n\r\n k++;\r\n }\r\n\r\n return -1;\r\n }\r\n\r\n\r\n cvSplit(files, cvIndex) {\r\n\r\n if (files.length <= 1) {\r\n return;\r\n }\r\n\r\n if (this.contentVariants == undefined) {\r\n this.contentVariants = [];\r\n }\r\n // if end of cvs, assign errors to all files if files.length > 1\r\n if (cvIndex - 2 >= this.contentVariants.length) {\r\n\r\n if (files.length > 1) {\r\n\r\n var cvHints = [];\r\n\r\n if (this.contentVariants.length == 0) {\r\n cvHints.push('No content variants have been added yet. Add content variants in the files panel in order to tag your files.');\r\n } else {\r\n for (var c in this.contentVariants) {\r\n var cv = this.contentVariants[c];\r\n var value = files[0].contentVariants[cv.id];\r\n\r\n if (value == undefined || value == '') {\r\n value = 'none';\r\n }\r\n\r\n cvHints.push(cv.id + ': ' + value);\r\n }\r\n }\r\n\r\n for (let file of files) {\r\n\r\n var errorMessage = 'Row ' + file.rowIndex + ' (' +\r\n cvHints.join(', ') + ').';\r\n\r\n file.errors.push({ key: 'err_duplicate_file', message: errorMessage });\r\n }\r\n }\r\n\r\n return;\r\n }\r\n\r\n // else create buckets and sort files into buckets\r\n var buckets = {};\r\n\r\n for (var f in files) {\r\n var file = files[f];\r\n\r\n var key = null;\r\n\r\n if (cvIndex == 0) {\r\n key = file.formatExtension;\r\n } else if (cvIndex == 1) {\r\n key = file.compression;\r\n } else {\r\n key = file.contentVariants[this.contentVariants[cvIndex - 2].id];\r\n }\r\n\r\n if (key == undefined || key == '') {\r\n key = '$_none$';\r\n }\r\n\r\n if (buckets[key] == undefined) {\r\n buckets[key] = [];\r\n }\r\n\r\n buckets[key].push(file);\r\n }\r\n\r\n // iterate buckets and call recursively\r\n for (var b in buckets) {\r\n this.cvSplit(buckets[b], cvIndex + 1);\r\n }\r\n }\r\n\r\n onEditContentVariant(index) {\r\n this.editContentVariant = this.contentVariants[index];\r\n }\r\n\r\n}\r\n\r\nmodule.exports = VersionHandler;\r\n\n\n//# sourceURL=webpack://databus-webapp/./js/publish/version-handler.js?"); /***/ }), @@ -485,7 +585,7 @@ eval("\n\n\n\nclass SearchAdapter {\n\n static list = [\n { \n \*************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("const AppJsonFormatter = __webpack_require__(/*! ../utils/app-json-formatter */ \"./js/utils/app-json-formatter.js\");\nconst SearchAdapter = __webpack_require__(/*! ./search-adapter */ \"./js/search/search-adapter.js\");\n\nclass SearchManager {\n\n constructor($http, $interval) {\n this.http = $http;\n this.searchExtensions = [];\n\n this.baseAdapter = SearchAdapter.lookup(this.http, `/api/search`);\n this.searchExtensions.push({\n endpointUri: `/api/search`,\n adapterName: `lookup`,\n adapter: this.baseAdapter\n });\n }\n\n mergeResults(results, documents) {\n for(var document of documents) {\n results.push(document);\n }\n\n return results;\n }\n\n async search(queryUrl, documentFilter) {\n\n var results = [];\n\n for (var searchExtension of this.searchExtensions) {\n\n try {\n\n var documents = await searchExtension.adapter.search(queryUrl);\n\n if(documentFilter != undefined) {\n documents = documents.filter(documentFilter);\n }\n \n results = this.mergeResults(results, documents);\n\n } catch(err) {\n\n }\n }\n\n return results;\n }\n\n async initialize() {\n\n var auth = data.auth;\n\n if (!auth.authenticated) {\n return;\n }\n\n if(auth.info.accountName == undefined) {\n return;\n }\n\n var options = {\n method: 'GET',\n url: `/${ auth.info.accountName } `,\n headers: {\n 'Accept': 'application/ld+json',\n 'X-Jsonld-Formatting': 'flatten'\n }\n }\n\n var response = await this.http(options);\n var accountData = AppJsonFormatter.formatAccountData(response.data);\n var extensions = JSON.parse(JSON.stringify(accountData.searchExtensions));\n\n for (var searchExtension of extensions) {\n\n switch (searchExtension.adapterName) {\n case 'lookup':\n searchExtension.adapter = SearchAdapter.lookup(this.http, searchExtension.endpointUri);\n break;\n }\n\n this.searchExtensions.push(searchExtension);\n\n }\n }\n}\n\nmodule.exports = SearchManager;\n\n\n//# sourceURL=webpack://databus-webapp/./js/search/search-manager.js?"); +eval("const AppJsonFormatter = __webpack_require__(/*! ../utils/app-json-formatter */ \"./js/utils/app-json-formatter.js\");\nconst SearchAdapter = __webpack_require__(/*! ./search-adapter */ \"./js/search/search-adapter.js\");\n\nclass SearchManager {\n\n constructor($http, $interval) {\n this.http = $http;\n this.searchExtensions = [];\n\n this.baseAdapter = SearchAdapter.lookup(this.http, `/api/search`);\n this.searchExtensions.push({\n endpointUri: `/api/search`,\n adapterName: `lookup`,\n adapter: this.baseAdapter\n });\n }\n\n mergeResults(results, documents) {\n for(var document of documents) {\n results.push(document);\n }\n\n return results;\n }\n\n async search(queryUrl, documentFilter) {\n\n var results = [];\n\n for (var searchExtension of this.searchExtensions) {\n\n try {\n\n var documents = await searchExtension.adapter.search(queryUrl);\n\n if(documentFilter != undefined) {\n documents = documents.filter(documentFilter);\n }\n \n results = this.mergeResults(results, documents);\n\n } catch(err) {\n\n }\n }\n\n return results;\n }\n\n async initialize() {\n\n var auth = data.auth;\n\n if (!auth.authenticated) {\n return;\n }\n\n if(auth.info.accountName == undefined) {\n return;\n }\n\n /*\n\n var options = {\n method: 'GET',\n url: `/${ auth.info.accountName }`,\n headers: {\n 'Accept': 'application/ld+json',\n 'X-Jsonld-Formatting': 'flatten',\n 'Cache-Control': 'no-cache',\n 'Pragma': 'no-cache'\n }\n }\n\n var response = await this.http(options);\n var accountData = AppJsonFormatter.formatAccountData(response.data);\n var extensions = JSON.parse(JSON.stringify(accountData.searchExtensions));\n\n for (var searchExtension of extensions) {\n\n switch (searchExtension.adapterName) {\n case 'lookup':\n searchExtension.adapter = SearchAdapter.lookup(this.http, searchExtension.endpointUri);\n break;\n }\n\n this.searchExtensions.push(searchExtension);\n\n })*/\n }\n}\n\nmodule.exports = SearchManager;\n\n\n//# sourceURL=webpack://databus-webapp/./js/search/search-manager.js?"); /***/ }), @@ -495,7 +595,7 @@ eval("const AppJsonFormatter = __webpack_require__(/*! ../utils/app-json-formatt \****************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("const DatabusConstants = __webpack_require__(/*! ./databus-constants */ \"./js/utils/databus-constants.js\");\nconst DatabusUris = __webpack_require__(/*! ./databus-uris */ \"./js/utils/databus-uris.js\");\nconst DatabusUtils = __webpack_require__(/*! ./databus-utils */ \"./js/utils/databus-utils.js\");\nconst JsonldUtils = __webpack_require__(/*! ./jsonld-utils */ \"./js/utils/jsonld-utils.js\");\n\n/**\n * Translates expanded jsonld into web-app compatible json\n */\nclass AppJsonFormatter {\n\n static createAccountData(resourceBaseUrl, accountName, accountLabel, accountStatus, accountImage) {\n\n var accountUri = `${resourceBaseUrl}/${accountName}`;\n var profileUri = `${resourceBaseUrl}/${accountName}${DatabusConstants.WEBID_DOCUMENT}`;\n var personUri = `${resourceBaseUrl}/${accountName}${DatabusConstants.WEBID_THIS}`;\n\n var accountJsonLd = {};\n\n var personGraph = {};\n personGraph[DatabusUris.JSONLD_ID] = personUri;\n personGraph[DatabusUris.JSONLD_TYPE] = [\n DatabusUris.FOAF_PERSON,\n DatabusUris.DBP_DBPEDIAN\n ];\n personGraph[DatabusUris.FOAF_NAME] = accountLabel;\n\n if (accountStatus != null) {\n personGraph[DatabusUris.FOAF_STATUS] = accountStatus;\n }\n\n personGraph[DatabusUris.FOAF_ACCOUNT] = {};\n personGraph[DatabusUris.FOAF_ACCOUNT][DatabusUris.JSONLD_ID] = accountUri;\n\n if (accountImage != null) {\n personGraph[DatabusUris.FOAF_IMG] = {};\n personGraph[DatabusUris.FOAF_IMG][DatabusUris.JSONLD_ID] = accountImage;\n }\n\n var profileGraph = {};\n profileGraph[DatabusUris.JSONLD_ID] = profileUri;\n profileGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.FOAF_PERSONAL_PROFILE_DOCUMENT;\n profileGraph[DatabusUris.FOAF_PRIMARY_TOPIC] = {};\n profileGraph[DatabusUris.FOAF_PRIMARY_TOPIC][DatabusUris.JSONLD_ID] = personUri;\n profileGraph[DatabusUris.FOAF_MAKER] = {};\n profileGraph[DatabusUris.FOAF_MAKER][DatabusUris.JSONLD_ID] = personUri;\n\n accountJsonLd[DatabusUris.JSONLD_GRAPH] = [\n personGraph,\n profileGraph\n ];\n\n return accountJsonLd;\n }\n\n static formatGroupData(graphs) {\n var result = {};\n\n // ?uri ?title ?abstract ?description\n var groupGraph = JsonldUtils.getTypedGraph(graphs, DatabusUris.DATABUS_GROUP);\n\n result.uri = groupGraph[DatabusUris.JSONLD_ID];\n result.title = JsonldUtils.getProperty(groupGraph, DatabusUris.DCT_TITLE);\n result.abstract = JsonldUtils.getProperty(groupGraph, DatabusUris.DCT_ABSTRACT);\n result.description = JsonldUtils.getProperty(groupGraph, DatabusUris.DCT_DESCRIPTION);\n result.name = DatabusUtils.uriToResourceName(result.uri);\n return result;\n }\n\n static formatArtifactData(graphs) {\n var result = {};\n // ?uri ?title ?abstract ?description\n var artifactGraph = JsonldUtils.getTypedGraph(graphs, DatabusUris.DATABUS_ARTIFACT);\n\n result.uri = artifactGraph[DatabusUris.JSONLD_ID];\n result.title = JsonldUtils.getProperty(artifactGraph, DatabusUris.DCT_TITLE);\n result.abstract = JsonldUtils.getProperty(artifactGraph, DatabusUris.DCT_ABSTRACT);\n result.description = JsonldUtils.getProperty(artifactGraph, DatabusUris.DCT_DESCRIPTION);\n result.name = DatabusUtils.uriToResourceName(result.uri);\n return result;\n\n\n }\n\n static formatAccountData(graphs) {\n var result = {};\n\n var profileGraph = JsonldUtils.getTypedGraph(graphs, DatabusUris.FOAF_PERSONAL_PROFILE_DOCUMENT);\n var personGraph = JsonldUtils.getTypedGraph(graphs, DatabusUris.FOAF_PERSON);\n\n result.uri = profileGraph[DatabusUris.JSONLD_ID];\n result.accountName = DatabusUtils.uriToResourceName(result.uri);\n result.label = JsonldUtils.getProperty(personGraph, DatabusUris.FOAF_NAME);\n result.webIds = [];\n result.searchExtensions = [];\n\n var extensionGraphs = JsonldUtils.getTypedGraphs(graphs, DatabusUris.DATABUS_SEARCH_EXTENSION);\n\n for (var extensionGraph of extensionGraphs) {\n result.searchExtensions.push({\n endpointUri: JsonldUtils.getProperty(extensionGraph, DatabusUris.DATABUS_SEARCH_EXTENSION_ENDPOINT),\n adapterName: JsonldUtils.getProperty(extensionGraph, DatabusUris.DATABUS_SEARCH_EXTENSION_ADAPTER),\n });\n }\n\n for (var graph of graphs) {\n\n if (graph[DatabusUris.JSONLD_ID] == personGraph[DatabusUris.JSONLD_ID]) {\n continue;\n }\n\n if (graph[DatabusUris.FOAF_ACCOUNT] != undefined) {\n result.webIds.push(graph[DatabusUris.JSONLD_ID]);\n }\n }\n\n return result;\n }\n\n static formatVersionData(versionGraph) {\n\n\n var version = {};\n version.uri = versionGraph[DatabusUris.JSONLD_ID];\n version.title = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_TITLE);\n version.abstract = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_ABSTRACT);\n version.description = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_DESCRIPTION);\n version.artifact = JsonldUtils.getProperty(versionGraph, DatabusUris.DATABUS_ARTIFACT_PROPERTY);\n version.license = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_LICENSE);\n version.attribution = JsonldUtils.getProperty(versionGraph, DatabusUris.DATABUS_ATTRIBUTION);\n version.wasDerivedFrom = JsonldUtils.getProperty(versionGraph, DatabusUris.PROV_WAS_DERIVED_FROM);\n version.issued = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_ISSUED);\n version.name = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_HAS_VERSION);\n\n return version;\n }\n\n static formatCollectionData(graphs) {\n var collectionGraph = JsonldUtils.getTypedGraph(graphs, DatabusUris.DATABUS_COLLECTION);\n\n var result = {};\n\n result.uri = collectionGraph[DatabusUris.JSONLD_ID];\n result.title = JsonldUtils.getProperty(collectionGraph, DatabusUris.DCT_TITLE);\n result.abstract = JsonldUtils.getProperty(collectionGraph, DatabusUris.DCT_ABSTRACT);\n result.description = JsonldUtils.getProperty(collectionGraph, DatabusUris.DCT_DESCRIPTION);\n result.issued = JsonldUtils.getProperty(collectionGraph, DatabusUris.DCT_ISSUED);\n result.publisher = JsonldUtils.getProperty(collectionGraph, DatabusUris.DCT_PUBLISHER);\n\n var content = JsonldUtils.getProperty(collectionGraph, DatabusUris.DATABUS_COLLECTION_CONTENT)\n result.content = DatabusUtils.tryParseJson(unescape(content));\n\n return result;\n }\n}\n\nmodule.exports = AppJsonFormatter;\n\n\n//# sourceURL=webpack://databus-webapp/./js/utils/app-json-formatter.js?"); +eval("const DatabusConstants = __webpack_require__(/*! ./databus-constants */ \"./js/utils/databus-constants.js\");\r\nconst DatabusUris = __webpack_require__(/*! ./databus-uris */ \"./js/utils/databus-uris.js\");\r\nconst DatabusUtils = __webpack_require__(/*! ./databus-utils */ \"./js/utils/databus-utils.js\");\r\nconst JsonldUtils = __webpack_require__(/*! ./jsonld-utils */ \"./js/utils/jsonld-utils.js\");\r\n\r\n/**\r\n * Translates expanded jsonld into web-app compatible json\r\n */\r\nclass AppJsonFormatter {\r\n\r\n static async createAccountGraphs(uri, name, label, img, secretaries, status) {\r\n var name = UriUtils.uriToName(uri);\r\n \r\n var rsaKeyGraph = {};\r\n rsaKeyGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.CERT_RSA_PUBLIC_KEY;\r\n rsaKeyGraph[DatabusUris.RDFS_LABEL] = DatabusConstants.WEBID_SHARED_PUBLIC_KEY_LABEL;\r\n rsaKeyGraph[DatabusUris.CERT_MODULUS] = signer.getModulus();\r\n rsaKeyGraph[DatabusUris.CERT_EXPONENT] = 65537;\r\n \r\n var personUri = `${uri}${DatabusConstants.WEBID_THIS}`;\r\n\r\n var personGraph = {};\r\n personGraph[DatabusUris.JSONLD_ID] = personUri;\r\n personGraph[DatabusUris.JSONLD_TYPE] = [ DatabusUris.FOAF_PERSON, DatabusUris.DBP_DBPEDIAN ];\r\n personGraph[DatabusUris.FOAF_ACCOUNT] = JsonldUtils.refTo(uri);\r\n personGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY] = uri;\r\n personGraph[DatabusUris.CERT_KEY] = [ rsaKeyGraph ];\r\n personGraph[DatabusUris.FOAF_NAME] = label;\r\n\r\n if(img != null) {\r\n personGraph[DatabusUris.FOAF_IMG] = img;\r\n }\r\n\r\n if(status != null) {\r\n personGraph[DatabusUris.FOAF_STATUS] = status;\r\n }\r\n\r\n var profileUri = `${uri}${DatabusConstants.WEBID_DOCUMENT}`;\r\n \r\n var profileDocumentGraph = {};\r\n profileDocumentGraph[DatabusUris.JSONLD_ID] = profileUri;\r\n profileDocumentGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.FOAF_PERSONAL_PROFILE_DOCUMENT;\r\n profileDocumentGraph[DatabusUris.FOAF_MAKER] = JsonldUtils.refTo(personUri);\r\n profileDocumentGraph[DatabusUris.FOAF_PRIMARY_TOPIC] = JsonldUtils.refTo(personUri);\r\n \r\n var accountGraph = {}\r\n accountGraph[DatabusUris.JSONLD_ID] = uri;\r\n accountGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.DATABUS_ACCOUNT;\r\n accountGraph[DatabusUris.FOAF_ACCOUNT_NAME] = name;\r\n accountGraph[DatabusUris.DATABUS_NAME] = name;\r\n\r\n if(secretaries != null) {\r\n\r\n accountGraph[DatabusUris.DATABUS_SECRETARY_PROPERTY] = [];\r\n\r\n for(var secretary of secretaries) {\r\n\r\n let secretaryAccountUri = `${secretary.accountName}`;\r\n\r\n let secretaryGraph = {};\r\n secretaryGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.DATABUS_SECRETARY;\r\n secretaryGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY] = JsonldUtils.refTo(secretaryAccountUri);\r\n\r\n if(secretary.hasWriteAccessTo != undefined) {\r\n secretaryGraph[DatabusUris.DATABUS_HAS_WRITE_ACCESS_TO] = [];\r\n\r\n for(var writeAccess of secretary.hasWriteAccessTo) {\r\n secretaryGraph[DatabusUris.DATABUS_HAS_WRITE_ACCESS_TO].push(JsonldUtils.refTo(writeAccess));\r\n }\r\n }\r\n\r\n accountGraph[DatabusUris.DATABUS_SECRETARY_PROPERTY].push(secretaryGraph);\r\n }\r\n }\r\n\r\n let expandedGraphs = [\r\n accountGraph,\r\n personGraph,\r\n profileDocumentGraph\r\n ];\r\n \r\n return await jsonld.compact(expandedGraphs, JsonldLoader.DEFAULT_CONTEXT_URL);\r\n }\r\n \r\n static createAccountData(accountUri, accountLabel, accountStatus, accountImage) {\r\n\r\n var personUri = `${accountUri}${DatabusConstants.WEBID_THIS}`;\r\n\r\n var accountJsonLd = {};\r\n\r\n var accountGraph = {};\r\n accountGraph[DatabusUris.JSONLD_ID] = accountUri;\r\n accountGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.DATABUS_ACCOUNT;\r\n\r\n var personGraph = {};\r\n personGraph[DatabusUris.JSONLD_ID] = personUri;\r\n personGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.FOAF_PERSON;\r\n personGraph[DatabusUris.FOAF_NAME] = accountLabel;\r\n personGraph[DatabusUris.FOAF_ACCOUNT] = JsonldUtils.refTo(accountUri);\r\n\r\n if (accountStatus != null) {\r\n personGraph[DatabusUris.FOAF_STATUS] = accountStatus;\r\n }\r\n\r\n if (accountImage != null) {\r\n personGraph[DatabusUris.FOAF_IMG] = JsonldUtils.refTo(accountImage);\r\n }\r\n\r\n\r\n return [\r\n accountGraph,\r\n personGraph\r\n ];\r\n }\r\n\r\n static formatGroupData(graphs) {\r\n var result = {};\r\n\r\n // ?uri ?title ?abstract ?description\r\n var groupGraph = JsonldUtils.getTypedGraph(graphs, DatabusUris.DATABUS_GROUP);\r\n\r\n result.uri = groupGraph[DatabusUris.JSONLD_ID];\r\n result.title = JsonldUtils.getProperty(groupGraph, DatabusUris.DCT_TITLE);\r\n result.abstract = JsonldUtils.getProperty(groupGraph, DatabusUris.DCT_ABSTRACT);\r\n result.description = JsonldUtils.getProperty(groupGraph, DatabusUris.DCT_DESCRIPTION);\r\n result.name = DatabusUtils.uriToResourceName(result.uri);\r\n return result;\r\n }\r\n\r\n static formatArtifactData(graphs) {\r\n var result = {};\r\n // ?uri ?title ?abstract ?description\r\n var artifactGraph = JsonldUtils.getTypedGraph(graphs, DatabusUris.DATABUS_ARTIFACT);\r\n\r\n result.uri = artifactGraph[DatabusUris.JSONLD_ID];\r\n result.title = JsonldUtils.getProperty(artifactGraph, DatabusUris.DCT_TITLE);\r\n result.abstract = JsonldUtils.getProperty(artifactGraph, DatabusUris.DCT_ABSTRACT);\r\n result.description = JsonldUtils.getProperty(artifactGraph, DatabusUris.DCT_DESCRIPTION);\r\n result.name = DatabusUtils.uriToResourceName(result.uri);\r\n return result;\r\n\r\n\r\n }\r\n\r\n static formatAccountData(graphs) {\r\n var result = {};\r\n\r\n var accountGraph = JsonldUtils.getTypedGraph(graphs, DatabusUris.DATABUS_ACCOUNT);\r\n var personGraph = JsonldUtils.getTypedGraph(graphs, DatabusUris.FOAF_PERSON);\r\n\r\n result.uri = accountGraph[DatabusUris.JSONLD_ID];\r\n result.accountName = DatabusUtils.uriToResourceName(result.uri);\r\n result.label = JsonldUtils.getFirstProperty(personGraph, DatabusUris.FOAF_NAME);\r\n result.imageUrl = JsonldUtils.getFirstProperty(personGraph, DatabusUris.FOAF_IMG);\r\n result.about = JsonldUtils.getFirstProperty(personGraph, DatabusUris.FOAF_STATUS);\r\n result.webIds = [];\r\n result.searchExtensions = [];\r\n\r\n var extensionGraphs = JsonldUtils.getTypedGraphs(graphs, DatabusUris.DATABUS_SEARCH_EXTENSION);\r\n\r\n for (var extensionGraph of extensionGraphs) {\r\n result.searchExtensions.push({\r\n endpointUri: JsonldUtils.getProperty(extensionGraph, DatabusUris.DATABUS_SEARCH_EXTENSION_ENDPOINT),\r\n adapterName: JsonldUtils.getProperty(extensionGraph, DatabusUris.DATABUS_SEARCH_EXTENSION_ADAPTER),\r\n });\r\n }\r\n\r\n for (var graph of graphs) {\r\n\r\n if (graph[DatabusUris.JSONLD_ID] == personGraph[DatabusUris.JSONLD_ID]) {\r\n continue;\r\n }\r\n\r\n if (graph[DatabusUris.FOAF_ACCOUNT] != undefined) {\r\n result.webIds.push(graph[DatabusUris.JSONLD_ID]);\r\n }\r\n }\r\n\r\n result.secretaries = [];\r\n var secretaryGraphs = JsonldUtils.getTypedGraphs(graphs, DatabusUris.DATABUS_SECRETARY);\r\n\r\n for (var secretaryGraph of secretaryGraphs) {\r\n var secretaryData = {\r\n accountName: JsonldUtils.getProperty(secretaryGraph, DatabusUris.DATABUS_ACCOUNT_PROPERTY),\r\n hasWriteAccessTo: []\r\n };\r\n\r\n var writeAccessUris = secretaryGraph[DatabusUris.DATABUS_HAS_WRITE_ACCESS_TO];\r\n\r\n if (Array.isArray(writeAccessUris)) {\r\n for (var item of writeAccessUris) {\r\n if (typeof item === 'object' && item['@id']) {\r\n secretaryData.hasWriteAccessTo.push(item['@id']);\r\n } else if (typeof item === 'string') {\r\n secretaryData.hasWriteAccessTo.push(item);\r\n }\r\n }\r\n }\r\n\r\n result.secretaries.push(secretaryData);\r\n }\r\n\r\n return result;\r\n }\r\n\r\n static formatVersionData(versionGraph) {\r\n\r\n\r\n var version = {};\r\n version.uri = versionGraph[DatabusUris.JSONLD_ID];\r\n version.title = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_TITLE);\r\n version.abstract = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_ABSTRACT);\r\n version.description = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_DESCRIPTION);\r\n version.artifact = JsonldUtils.getProperty(versionGraph, DatabusUris.DATABUS_ARTIFACT_PROPERTY);\r\n version.license = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_LICENSE);\r\n version.attribution = JsonldUtils.getProperty(versionGraph, DatabusUris.DATABUS_ATTRIBUTION);\r\n version.wasDerivedFrom = JsonldUtils.getProperty(versionGraph, DatabusUris.PROV_WAS_DERIVED_FROM);\r\n version.issued = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_ISSUED);\r\n version.name = JsonldUtils.getProperty(versionGraph, DatabusUris.DCT_HAS_VERSION);\r\n\r\n return version;\r\n }\r\n\r\n static formatCollectionData(graphs) {\r\n var collectionGraph = JsonldUtils.getTypedGraph(graphs, DatabusUris.DATABUS_COLLECTION);\r\n\r\n var result = {};\r\n\r\n result.uri = collectionGraph[DatabusUris.JSONLD_ID];\r\n result.title = JsonldUtils.getProperty(collectionGraph, DatabusUris.DCT_TITLE);\r\n result.abstract = JsonldUtils.getProperty(collectionGraph, DatabusUris.DCT_ABSTRACT);\r\n result.description = JsonldUtils.getProperty(collectionGraph, DatabusUris.DCT_DESCRIPTION);\r\n result.issued = JsonldUtils.getProperty(collectionGraph, DatabusUris.DCT_ISSUED);\r\n result.publisher = JsonldUtils.getProperty(collectionGraph, DatabusUris.DCT_PUBLISHER);\r\n result.account = JsonldUtils.getProperty(collectionGraph, DatabusUris.DATABUS_ACCOUNT_PROPERTY);\r\n\r\n var content = JsonldUtils.getProperty(collectionGraph, DatabusUris.DATABUS_COLLECTION_CONTENT)\r\n result.content = DatabusUtils.tryParseJson(unescape(content));\r\n\r\n return result;\r\n }\r\n}\r\n\r\nmodule.exports = AppJsonFormatter;\r\n\n\n//# sourceURL=webpack://databus-webapp/./js/utils/app-json-formatter.js?"); /***/ }), @@ -525,7 +625,7 @@ eval("const { DATABUS_CONTENT_VARIANT_PREFIX } = __webpack_require__(/*! ./datab \**************************************/ /***/ ((module) => { -eval("\nclass DatabusMessages {\n\n // Collection Editor\n static CEDIT_INVALID_IDENTIFIER = 'The identifier must match the following regular expression: #REGEX#';\n static CEDIT_INVALID_TITLE = 'The title must match the following regular expression: #REGEX#';\n static CEDIT_INVALID_ABSTRACT = 'The abstract must match the following regular expression: #REGEX#';\n static CEDIT_INVALID_DESCRIPTION = 'The description must match the following regular expression: #REGEX#';\n static CEDIT_COLLECTION_IMPORT_FAILED = 'Failed to import the collection';\n static CEDIT_COLLECTION_IMPORTED = 'Collection imported successfully';\n static CEDIT_COLLECTION_SAVED = 'Collection saved successfully';\n static CEDIT_COLLECTION_SAVE_FAILED = 'Failed to save the collection';\n static CEDIT_COLLECTION_UNPUBLISHED = 'Collection unpublished successfully';\n static CEDIT_LOCAL_CHANGES_DISCARDED = 'Local changes discarded';\n\n // Generic\n static GENERIC_COPIED_TO_CLIPBOARD = 'Copied to clipboard!';\n\n // Account \n static ACCOUT_PROFILE_SAVED = 'Profile changes have been saved';\n\n static ACCOUNT_API_KEY_CREATED = 'API key created';\n\n static ACCOUNT_WEBID_LINKED = 'External WebId has been linked to your profile';\n}\n\n module.exports = DatabusMessages;\n\n\n//# sourceURL=webpack://databus-webapp/./js/utils/databus-messages.js?"); +eval("\r\nclass DatabusMessages {\r\n\r\n // Collection Editor\r\n static CEDIT_INVALID_IDENTIFIER = 'The identifier must match the following regular expression: #REGEX#';\r\n static CEDIT_INVALID_TITLE = 'The title must match the following regular expression: #REGEX#';\r\n static CEDIT_INVALID_ABSTRACT = 'The abstract must match the following regular expression: #REGEX#';\r\n static CEDIT_INVALID_DESCRIPTION = 'The description must match the following regular expression: #REGEX#';\r\n static CEDIT_COLLECTION_IMPORT_FAILED = 'Failed to import the collection';\r\n static CEDIT_COLLECTION_IMPORTED = 'Collection imported successfully';\r\n static CEDIT_COLLECTION_SAVED = 'Collection saved successfully';\r\n static CEDIT_COLLECTION_SAVE_FAILED = 'Failed to save the collection';\r\n static CEDIT_COLLECTION_UNPUBLISHED = 'Collection unpublished successfully';\r\n static CEDIT_LOCAL_CHANGES_DISCARDED = 'Local changes discarded';\r\n\r\n // Generic\r\n static GENERIC_COPIED_TO_CLIPBOARD = 'Copied to clipboard!';\r\n\r\n // Account \r\n static ACCOUT_PROFILE_SAVED = 'Profile changes have been saved';\r\n\r\n static ACCOUNT_API_KEY_CREATED = 'API key created';\r\n\r\n static ACCOUNT_WEBID_LINKED = 'External WebId has been linked to your profile';\r\n\r\n \r\n}\r\n\r\n module.exports = DatabusMessages;\r\n\n\n//# sourceURL=webpack://databus-webapp/./js/utils/databus-messages.js?"); /***/ }), @@ -535,7 +635,7 @@ eval("\nclass DatabusMessages {\n\n // Collection Editor\n static CEDIT_INVALI \**********************************/ /***/ ((module) => { -eval("\nclass DatabusUris {\n\n // JSONLD\n static JSONLD_TYPE = '@type';\n static JSONLD_ID = '@id';\n static JSONLD_VALUE = '@value';\n static JSONLD_LANGUAGE = '@language';\n static JSONLD_CONTEXT = '@context';\n static JSONLD_GRAPH = '@graph';\n\n // Databus\n static DATABUS_DATABUS = 'https://dataid.dbpedia.org/databus#Databus';\n static DATABUS_PART = 'https://dataid.dbpedia.org/databus#Part';\n static DATABUS_VERSION = 'https://dataid.dbpedia.org/databus#Version';\n static DATABUS_GROUP = 'https://dataid.dbpedia.org/databus#Group';\n static DATABUS_ARTIFACT = 'https://dataid.dbpedia.org/databus#Artifact';\n static DATABUS_VERSION_PROPERTY = 'https://dataid.dbpedia.org/databus#version';\n static DATABUS_GROUP_PROPERTY = 'https://dataid.dbpedia.org/databus#group';\n static DATABUS_ACCOUNT_PROPERTY = 'https://dataid.dbpedia.org/databus#account';\n static DATABUS_ARTIFACT_PROPERTY = 'https://dataid.dbpedia.org/databus#artifact';\n static DATABUS_FORMAT = 'https://dataid.dbpedia.org/databus#format';\n static DATABUS_FORMAT_EXTENSION = 'https://dataid.dbpedia.org/databus#formatExtension';\n static DATABUS_CONTENT_VARIANT = 'https://dataid.dbpedia.org/databus#contentVariant';\n static DATABUS_CONTENT_VARIANT_PREFIX = 'https://dataid.dbpedia.org/databus-cv#';\n static DATABUS_SHASUM = 'https://dataid.dbpedia.org/databus#sha256sum';\n static DATABUS_COLLECTION = 'https://dataid.dbpedia.org/databus#Collection';\n static DATABUS_FILE = 'https://dataid.dbpedia.org/databus#file';\n static DATABUS_COMPRESSION = 'https://dataid.dbpedia.org/databus#compression';\n static DATABUS_ATTRIBUTION = 'https://dataid.dbpedia.org/databus#attribution';\n static DATABUS_PREVIEW = 'https://dataid.dbpedia.org/databus#preview';\n static DATABUS_COLLECTION_CONTENT = 'https://dataid.dbpedia.org/databus#collectionContent';\n static DATABUS_TRACTATE_V1 = 'https://dataid.dbpedia.org/databus#DatabusTractateV1';\n static DATABUS_PLUGIN = 'https://dataid.dbpedia.org/databus#Plugin';\n static DATABUS_SEARCH_EXTENSION = 'https://dataid.dbpedia.org/databus#SearchExtension';\n static DATABUS_SEARCH_EXTENSION_ADAPTER = 'https://dataid.dbpedia.org/databus#searchExtensionAdapter';\n static DATABUS_SEARCH_EXTENSION_ENDPOINT = 'https://dataid.dbpedia.org/databus#searchExtensionEndpoint';\n static DATABUS_EXTENDS = 'https://dataid.dbpedia.org/databus#extends';\n \n // DCT\n static DCT_PUBLISHER = 'http://purl.org/dc/terms/publisher';\n static DCT_HAS_VERSION = 'http://purl.org/dc/terms/hasVersion';\n static DCT_ISSUED = 'http://purl.org/dc/terms/issued';\n static DCT_CREATED = 'http://purl.org/dc/terms/created';\n static DCT_MODIFIED = 'http://purl.org/dc/terms/modified';\n static DCT_DISTRIBUTION = 'http://purl.org/dc/terms/distribution';\n static DCT_SUBJECT = 'http://purl.org/dc/terms/subject';\n static DCT_CREATOR = 'http://purl.org/dc/terms/creator'\n static DCT_TITLE = 'http://purl.org/dc/terms/title'\n static DCT_ABSTRACT = 'http://purl.org/dc/terms/abstract'\n static DCT_DESCRIPTION = 'http://purl.org/dc/terms/description'\n static DCT_LICENSE = 'http://purl.org/dc/terms/license';\n\n // DCAT\n static DCAT_DOWNLOAD_URL = 'http://www.w3.org/ns/dcat#downloadURL';\n static DCAT_BYTESIZE = 'http://www.w3.org/ns/dcat#byteSize';\n static DCAT_DISTRIBUTION = 'http://www.w3.org/ns/dcat#distribution';\n\n\n // SEC\n static SEC_PROOF = 'https://w3id.org/security#proof';\n static SEC_SIGNATURE = 'https://w3id.org/security#signature';\n\n // RDF\n static RDF_PROPERTY = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Property';\n\n // RDFS\n static RDFS_SUB_PROPERTY_OF = 'http://www.w3.org/2000/01/rdf-schema#subPropertyOf';\n static RDFS_LABEL = 'http://www.w3.org/2000/01/rdf-schema#label';\n\n // XSD\n static XSD_DATE_TIME = 'http://www.w3.org/2001/XMLSchema#dateTime';\n static XSD_DECIMAL = 'http://www.w3.org/2001/XMLSchema#decimal';\n static XSD_STRING = 'http://www.w3.org/2001/XMLSchema#string';\n\n // SHACL\n static SHACL_VALIDATION_REPORT = 'http://www.w3.org/ns/shacl#ValidationReport';\n static SHACL_VALIDATION_RESULT = 'http://www.w3.org/ns/shacl#ValidationResult';\n static SHACL_CONFORMS = 'http://www.w3.org/ns/shacl#conforms';\n static SHACL_RESULT_MESSAGE = 'http://www.w3.org/ns/shacl#resultMessage';\n\n // FOAF\n static FOAF_PERSONAL_PROFILE_DOCUMENT = 'http://xmlns.com/foaf/0.1/PersonalProfileDocument';\n static FOAF_ACCOUNT = 'http://xmlns.com/foaf/0.1/account';\n static FOAF_NAME = 'http://xmlns.com/foaf/0.1/name';\n static FOAF_STATUS = 'http://xmlns.com/foaf/0.1/status';\n static FOAF_PERSON = 'http://xmlns.com/foaf/0.1/Person';\n static FOAF_PRIMARY_TOPIC = 'http://xmlns.com/foaf/0.1/primaryTopic';\n static FOAF_MAKER = 'http://xmlns.com/foaf/0.1/maker';\n static FOAF_IMG = 'http://xmlns.com/foaf/0.1/img';\n\n // S4AC\n static S4AC_ACCESS_POLICY = 'http://ns.inria.fr/s4ac/v2#AccessPolicy';\n static S4AC_ACCESS_CREATE = 'http://ns.inria.fr/s4ac/v2#Create';\n static S4AC_HAS_ACCESS_PRIVILEGE = 'http://ns.inria.fr/s4ac/v2#hasAccessPrivilege';\n\n // CERT\n static CERT_KEY = 'http://www.w3.org/ns/auth/cert#key';\n static CERT_MODULUS = 'http://www.w3.org/ns/auth/cert#modulus';\n static CERT_EXPONENT = 'http://www.w3.org/ns/auth/cert#exponent';\n static CERT_RSA_PUBLIC_KEY = 'http://www.w3.org/ns/auth/cert#RSAPublicKey';\n\n // PROV\n static PROV_WAS_DERIVED_FROM = 'http://www.w3.org/ns/prov-o#wasDerivedFrom';\n\n // DBP\n static DBP_DBPEDIAN = 'http://dbpedia.org/ontology/DBpedian';\n}\n\nmodule.exports = DatabusUris;\n\n\n//# sourceURL=webpack://databus-webapp/./js/utils/databus-uris.js?"); +eval("\r\nclass DatabusUris {\r\n\r\n // JSONLD\r\n static JSONLD_TYPE = '@type';\r\n static JSONLD_ID = '@id';\r\n static JSONLD_VALUE = '@value';\r\n static JSONLD_LANGUAGE = '@language';\r\n static JSONLD_CONTEXT = '@context';\r\n static JSONLD_GRAPH = '@graph';\r\n\r\n // Databus\r\n static DATABUS_DATABUS = 'https://dataid.dbpedia.org/databus#Databus';\r\n static DATABUS_PART = 'https://dataid.dbpedia.org/databus#Part';\r\n static DATABUS_VERSION = 'https://dataid.dbpedia.org/databus#Version';\r\n static DATABUS_GROUP = 'https://dataid.dbpedia.org/databus#Group';\r\n static DATABUS_ACCOUNT = 'https://dataid.dbpedia.org/databus#Account';\r\n static DATABUS_ARTIFACT = 'https://dataid.dbpedia.org/databus#Artifact';\r\n static DATABUS_VERSION_PROPERTY = 'https://dataid.dbpedia.org/databus#version';\r\n static DATABUS_GROUP_PROPERTY = 'https://dataid.dbpedia.org/databus#group';\r\n static DATABUS_ACCOUNT_PROPERTY = 'https://dataid.dbpedia.org/databus#account';\r\n static DATABUS_HAS_ARTIFACT = 'https://dataid.dbpedia.org/databus#hasArtifact';\r\n static DATABUS_HAS_VERSION = 'https://dataid.dbpedia.org/databus#hasVersion';\r\n static DATABUS_NAME = 'https://dataid.dbpedia.org/databus#name';\r\n \r\n static DATABUS_SECRETARY_PROPERTY = 'https://dataid.dbpedia.org/databus#secretary';\r\n static DATABUS_SECRETARY = 'https://dataid.dbpedia.org/databus#Secretary';\r\n static DATABUS_HAS_WRITE_ACCESS_TO = 'https://dataid.dbpedia.org/databus#hasWriteAccessTo';\r\n\r\n static DATABUS_ARTIFACT_PROPERTY = 'https://dataid.dbpedia.org/databus#artifact';\r\n static DATABUS_FORMAT = 'https://dataid.dbpedia.org/databus#format';\r\n static DATABUS_FORMAT_EXTENSION = 'https://dataid.dbpedia.org/databus#formatExtension';\r\n static DATABUS_CONTENT_VARIANT = 'https://dataid.dbpedia.org/databus#contentVariant';\r\n static DATABUS_CONTENT_VARIANT_PREFIX = 'https://dataid.dbpedia.org/databus-cv#';\r\n static DATABUS_SHASUM = 'https://dataid.dbpedia.org/databus#sha256sum';\r\n static DATABUS_COLLECTION = 'https://dataid.dbpedia.org/databus#Collection';\r\n static DATABUS_FILE = 'https://dataid.dbpedia.org/databus#file';\r\n static DATABUS_COMPRESSION = 'https://dataid.dbpedia.org/databus#compression';\r\n static DATABUS_ATTRIBUTION = 'https://dataid.dbpedia.org/databus#attribution';\r\n static DATABUS_PREVIEW = 'https://dataid.dbpedia.org/databus#preview';\r\n static DATABUS_COLLECTION_CONTENT = 'https://dataid.dbpedia.org/databus#collectionContent';\r\n static DATABUS_TRACTATE_V1 = 'https://dataid.dbpedia.org/databus#DatabusTractateV1';\r\n static DATABUS_PLUGIN = 'https://dataid.dbpedia.org/databus#Plugin';\r\n static DATABUS_SEARCH_EXTENSION = 'https://dataid.dbpedia.org/databus#SearchExtension';\r\n static DATABUS_SEARCH_EXTENSION_ADAPTER = 'https://dataid.dbpedia.org/databus#searchExtensionAdapter';\r\n static DATABUS_SEARCH_EXTENSION_ENDPOINT = 'https://dataid.dbpedia.org/databus#searchExtensionEndpoint';\r\n static DATABUS_EXTENDS = 'https://dataid.dbpedia.org/databus#extends';\r\n \r\n // DCT\r\n static DCT_PUBLISHER = 'http://purl.org/dc/terms/publisher';\r\n static DCT_HAS_VERSION = 'http://purl.org/dc/terms/hasVersion';\r\n static DCT_ISSUED = 'http://purl.org/dc/terms/issued';\r\n static DCT_CREATED = 'http://purl.org/dc/terms/created';\r\n static DCT_MODIFIED = 'http://purl.org/dc/terms/modified';\r\n static DCT_DISTRIBUTION = 'http://purl.org/dc/terms/distribution';\r\n static DCT_SUBJECT = 'http://purl.org/dc/terms/subject';\r\n static DCT_CREATOR = 'http://purl.org/dc/terms/creator'\r\n static DCT_TITLE = 'http://purl.org/dc/terms/title'\r\n static DCT_ABSTRACT = 'http://purl.org/dc/terms/abstract'\r\n static DCT_DESCRIPTION = 'http://purl.org/dc/terms/description'\r\n static DCT_LICENSE = 'http://purl.org/dc/terms/license';\r\n\r\n // DCAT\r\n static DCAT_DOWNLOAD_URL = 'http://www.w3.org/ns/dcat#downloadURL';\r\n static DCAT_BYTESIZE = 'http://www.w3.org/ns/dcat#byteSize';\r\n static DCAT_DISTRIBUTION = 'http://www.w3.org/ns/dcat#distribution';\r\n\r\n\r\n // SEC\r\n static SEC_PROOF = 'https://w3id.org/security#proof';\r\n static SEC_SIGNATURE = 'https://w3id.org/security#signature';\r\n\r\n // RDF\r\n static RDF_PROPERTY = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Property';\r\n\r\n // RDFS\r\n static RDFS_SUB_PROPERTY_OF = 'http://www.w3.org/2000/01/rdf-schema#subPropertyOf';\r\n static RDFS_LABEL = 'http://www.w3.org/2000/01/rdf-schema#label';\r\n\r\n // XSD\r\n static XSD_DATE_TIME = 'http://www.w3.org/2001/XMLSchema#dateTime';\r\n static XSD_DECIMAL = 'http://www.w3.org/2001/XMLSchema#decimal';\r\n static XSD_STRING = 'http://www.w3.org/2001/XMLSchema#string';\r\n\r\n // SHACL\r\n static SHACL_VALIDATION_REPORT = 'http://www.w3.org/ns/shacl#ValidationReport';\r\n static SHACL_VALIDATION_RESULT = 'http://www.w3.org/ns/shacl#ValidationResult';\r\n static SHACL_CONFORMS = 'http://www.w3.org/ns/shacl#conforms';\r\n static SHACL_RESULT_MESSAGE = 'http://www.w3.org/ns/shacl#resultMessage';\r\n\r\n // FOAF\r\n static FOAF_PERSONAL_PROFILE_DOCUMENT = 'http://xmlns.com/foaf/0.1/PersonalProfileDocument';\r\n static FOAF_ACCOUNT = 'http://xmlns.com/foaf/0.1/account';\r\n static FOAF_NAME = 'http://xmlns.com/foaf/0.1/name';\r\n static FOAF_STATUS = 'http://xmlns.com/foaf/0.1/status';\r\n static FOAF_PERSON = 'http://xmlns.com/foaf/0.1/Person';\r\n static FOAF_PRIMARY_TOPIC = 'http://xmlns.com/foaf/0.1/primaryTopic';\r\n static FOAF_MAKER = 'http://xmlns.com/foaf/0.1/maker';\r\n static FOAF_ACCOUNT_NAME = 'http://xmlns.com/foaf/0.1/accountName';\r\n static FOAF_IMG = 'http://xmlns.com/foaf/0.1/img';\r\n\r\n // S4AC\r\n static S4AC_ACCESS_POLICY = 'http://ns.inria.fr/s4ac/v2#AccessPolicy';\r\n static S4AC_ACCESS_CREATE = 'http://ns.inria.fr/s4ac/v2#Create';\r\n static S4AC_HAS_ACCESS_PRIVILEGE = 'http://ns.inria.fr/s4ac/v2#hasAccessPrivilege';\r\n\r\n // CERT\r\n static CERT_KEY = 'http://www.w3.org/ns/auth/cert#key';\r\n static CERT_MODULUS = 'http://www.w3.org/ns/auth/cert#modulus';\r\n static CERT_EXPONENT = 'http://www.w3.org/ns/auth/cert#exponent';\r\n static CERT_RSA_PUBLIC_KEY = 'http://www.w3.org/ns/auth/cert#RSAPublicKey';\r\n\r\n // PROV\r\n static PROV_WAS_DERIVED_FROM = 'http://www.w3.org/ns/prov-o#wasDerivedFrom';\r\n\r\n // DBP\r\n static DBP_DBPEDIAN = 'http://dbpedia.org/ontology/DBpedian';\r\n}\r\n\r\nmodule.exports = DatabusUris;\r\n\n\n//# sourceURL=webpack://databus-webapp/./js/utils/databus-uris.js?"); /***/ }), @@ -545,7 +645,7 @@ eval("\nclass DatabusUris {\n\n // JSONLD\n static JSONLD_TYPE = '@type';\n s \***********************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("const DatabusCollectionUtils = __webpack_require__(/*! ../collections/databus-collection-utils */ \"./js/collections/databus-collection-utils.js\");\nvar markdownit = __webpack_require__(/*! markdown-it */ \"markdown-it\");\nconst moment = __webpack_require__(/*! moment/moment */ \"moment/moment\");\nconst DatabusUris = __webpack_require__(/*! ./databus-uris */ \"./js/utils/databus-uris.js\");\n\nclass DatabusUtils {\n\n static stringOrFallback(value, fallback) {\n if (value != null && value.length > 0) {\n return value;\n }\n\n return fallback;\n }\n\n static isValidResourceIdentifier(identifier, min) {\n var identifierRegex = /^[a-z-]+$/;\n return this.checkField(identifier, identifierRegex, min, 50);\n }\n\n static formatQuery(query, placeholderMappings) {\n\n if (placeholderMappings == undefined) {\n return query;\n }\n \n for (var placeholder in placeholderMappings) {\n var re = new RegExp('%' + placeholder + '%', \"g\");\n query = query.replace(re, placeholderMappings[placeholder]);\n }\n \n return query;\n }\n\n static isValidVersionIdentifier(identifier) {\n var labelRegex = /^[A-Za-z0-9_\\.\\-]*$/;\n return this.checkField(identifier, labelRegex, 3, 50);\n }\n\n static isValidResourceText(value, min, max) {\n var textRegex = /^[\\x00-\\x7F\\n]*$/;\n return this.checkField(value, textRegex, min, max);\n }\n\n static isValidAccountName(identifier) {\n var labelRegex = /^[a-z][0-9a-z_\\-]+[0-9a-z]$/;\n return this.checkField(identifier, labelRegex, 3, 15);\n }\n\n static timeStringNow() {\n return new Date(Date.now()).toISOString();\n }\n\n static isValidGroupName(name) {\n var labelRegex = /[a-zA-Z0-9_\\-\\.]{3,50}$/;\n return this.checkField(name, labelRegex, 3, 50);\n }\n\n static isValidArtifactName(name) {\n var labelRegex = /[a-zA-Z0-9_\\-\\.]{3,50}$/;\n return this.checkField(name, labelRegex, 3, 50);\n }\n\n static isValidUrl(value) {\n var textRegex = /https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)/g;\n return textRegex.test(value);\n }\n\n static isValidResourceLabel(value, min, max) {\n var labelRegex = /^[A-Za-z0-9\\s_()\\.\\,\\-]*$/;\n return this.checkField(value, labelRegex, min, max);\n }\n\n static objSize(obj) {\n var size = 0, key;\n for (key in obj) {\n if (obj.hasOwnProperty(key)) size++;\n }\n return size;\n }\n\n static uniqueList(arr) {\n var u = {}, a = [];\n for (var i = 0, l = arr.length; i < l; ++i) {\n if (!u.hasOwnProperty(arr[i])) {\n a.push(arr[i]);\n u[arr[i]] = 1;\n }\n }\n return a;\n }\n\n\n static formatFileSize(size) {\n if (size == undefined) {\n return '0 KB'\n }\n\n if (size < 1024) return size + \" B\";\n else if (size < 1048576) return Math.round(size / 1024) + \" KB\";\n else if (size < 1073741824) return (Math.round(10 * size / 1048576) / 10) + \" MB\";\n else return (Math.round(100 * size / 1073741824) / 100) + \" GB\";\n };\n\n static checkField(value, regex, min, max) {\n if (value == undefined) {\n return false;\n }\n\n if (max > 0 && value.length > max) {\n return false;\n }\n\n if (value.length < min) {\n return false;\n }\n\n return regex.test(value);\n }\n\n // Creates a v4 uuid\n static uuidv4() {\n return '___xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\n var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n }\n\n static tryParseJson(str) {\n return JSON.parse(str);\n }\n\n static uriToTitle(uri) {\n if (uri == null) {\n return null;\n }\n\n var result = uri.substr(uri.lastIndexOf('/') + 1);\n result = result.substr(result.lastIndexOf('#') + 1);\n\n return result.charAt(0).toUpperCase() + result.slice(1);\n }\n\n static uriToName(uri) {\n if (uri == null) {\n return null;\n }\n\n var result = uri.substr(uri.lastIndexOf('/') + 1);\n result = result.substr(result.lastIndexOf('#') + 1);\n\n if (result.includes('.')) {\n result = result.substr(0, result.lastIndexOf('.'));\n }\n\n return result;\n }\n\n static uriToResourceName(uri) {\n if (uri == null) {\n return null;\n }\n\n var result = uri.substr(uri.lastIndexOf('/') + 1);\n\n if(result.includes('#')) {\n result = result.substr(0, result.indexOf('#'));\n }\n\n return result;\n }\n\n static isValidHttpUrl(string) {\n let url;\n\n try {\n url = new URL(string);\n } catch (_) {\n return false;\n }\n\n return url.protocol === \"http:\" || url.protocol === \"https:\";\n }\n\n static isValidHttpsUrl(string) {\n let url;\n\n try {\n url = new URL(string);\n } catch (_) {\n return false;\n }\n\n return url.protocol === \"https:\";\n }\n\n\n static navigateUp(uri, steps) {\n\n if (steps == undefined) {\n steps = 1;\n }\n\n for (var i = 0; i < steps; i++) {\n uri = uri.substr(0, uri.lastIndexOf('/'));\n }\n\n if (uri.includes('#')) {\n uri = uri.substr(0, uri.lastIndexOf('#'));\n }\n\n return uri;\n }\n\n static copyStringToClipboard(str) {\n // Create new element\n var el = document.createElement('textarea');\n // Set value (string to be copied)\n el.value = str;\n // Set non-editable to avoid focus and move outside of view\n el.setAttribute('readonly', '');\n el.style = { position: 'absolute', left: '-9999px' };\n document.body.appendChild(el);\n // Select text inside element\n el.select();\n // Copy text to clipboard\n document.execCommand('copy');\n // Remove temporary element\n document.body.removeChild(el);\n }\n\n static serialize(collectionObject, ignoreKeys) {\n\n if (ignoreKeys == undefined) {\n ignoreKeys = [\n 'parent',\n '$$hashKey',\n 'expanded',\n 'files',\n 'eventListeners',\n 'hasLocalChanges',\n 'published'\n ];\n }\n\n return JSON.stringify(collectionObject, function (key, value) {\n if (ignoreKeys.includes(key)) {\n return undefined;\n }\n\n return value;\n });\n }\n\n static createCleanCopy(jsonData) {\n var data = JSON.parse(DatabusCollectionUtils.serialize(jsonData));\n return data;\n }\n\n static lineCount(text) {\n return (text.match(/^\\s*\\S/gm) || \"\").length\n }\n\n\n static getResourcePathLength(uri) {\n var parts = DatabusUtils.splitResourceUri(uri);\n\n if (parts.length == 1 && parts[0] == \"\") {\n return 0;\n }\n\n return parts.length;\n }\n\n static splitResourceUri(uri) {\n\n var url = new URL(uri);\n uri = url.pathname;\n\n if (uri.startsWith('/')) {\n uri = uri.substr(1);\n }\n if (uri.endsWith('/')) {\n uri = uri.substr(0, uri.length - 1);\n }\n\n return uri.split('/');\n }\n\n static formatDate(date) {\n return moment(date).format('MMM Do YYYY') + \" (\" + moment(date).fromNow() + \")\";\n }\n\n static exportToJsonFile(jsonData) {\n\n var ignoreKeys = [\n 'parent',\n '$$hashKey',\n 'expanded',\n 'files',\n 'eventListeners',\n 'hasLocalChanges',\n 'published',\n 'uuid'\n ];\n\n let dataStr = DatabusCollectionUtils.serialize(jsonData, ignoreKeys);\n let dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(dataStr);\n\n let exportFileDefaultName = 'data.json';\n\n let linkElement = document.createElement('a');\n linkElement.setAttribute('href', dataUri);\n linkElement.setAttribute('download', exportFileDefaultName);\n linkElement.click();\n }\n\n static async parseN3(data, maxQuads) {\n return new Promise((resolve, reject) => {\n\n const quads = [];\n const prefixes = [];\n\n const parser = new N3.Parser();\n\n parser.parse(data, (e, q, p) => {\n if (e) {\n reject(e);\n return;\n }\n\n if (quads.length > maxQuads || q == null) {\n resolve({ quads: quads, prefixes: prefixes });\n }\n\n if (q) {\n quads.push(q);\n }\n });\n });\n }\n\n static async parseDatabusManifest(data) {\n\n var parsedData = await DatabusUtils.parseN3(data, 100);\n\n for (var quad of parsedData.quads) {\n\n if (quad.predicate.id == `http://www.w3.org/1999/02/22-rdf-syntax-ns#type`\n && quad.object.id == DatabusUris.DATABUS_DATABUS) {\n\n return {\n uri: quad.subject.id\n }\n }\n }\n\n return undefined;\n }\n\n static parseMarkdown(markdown) {\n\n if(markdown == null) {\n return null;\n }\n\n var markdownParser = markdownit();\n return markdownParser.parse(markdown);\n }\n\n static renderMarkdown(markdown) {\n\n if(markdown == null) {\n return null;\n }\n\n var markdownParser = markdownit();\n return markdownParser.render(markdown);\n }\n\n /**\n * Create a dct:abstract from the content of a dct:description\n * @param {*} description \n */\n static createAbstractFromDescription(description) {\n\n if(description == null) {\n return null;\n }\n\n try {\n var tokens = this.parseMarkdown(description);\n\n \n var paragraphFound = false;\n var result = \"\";\n\n if(tokens == null) {\n return result; \n }\n\n var firstParagraphText = null;\n\n for (var i = 0; i < tokens.length; i++) {\n\n var token = tokens[i];\n var appendText = null;\n\n if (token.type == 'inline' && tokens[i - 1].type == 'paragraph_open' && token.level == 1) {\n result = token.content;\n break;\n }\n\n }\n\n return result;\n\n } catch (err) {\n console.log(err);\n return undefined;\n }\n }\n\n /**\n * Find groups files that are not distinguishable\n * @param {Array of file URIs} files \n * @param {Array of content variant names} contentVariants \n * @param {Index in the array of content variants} index \n * @returns \n */\n static cvSplit(distributionGraphs, contentVariantUris, contentVariantIndex) {\n\n var errorList = [];\n\n if (distributionGraphs.length <= 1) {\n return errorList;\n }\n\n if (contentVariantIndex >= contentVariantUris.length) {\n\n // Check buckets for double entries if (files.length > 1) {\n if (distributionGraphs.length > 1) {\n\n var error = {};\n error.downloadURLs = [];\n\n for (var distribution of distributionGraphs) {\n\n error.downloadURLs.push(distribution[DatabusUris.DCAT_DOWNLOAD_URL][0][DatabusUris.JSONLD_ID]);\n }\n\n error[DatabusUris.DATABUS_FORMAT_EXTENSION] =\n distributionGraphs[0][DatabusUris.DATABUS_FORMAT_EXTENSION][0][DatabusUris.JSONLD_VALUE];\n\n error[DatabusUris.DATABUS_COMPRESSION] =\n distributionGraphs[0][DatabusUris.DATABUS_COMPRESSION][0][DatabusUris.JSONLD_VALUE];\n\n for (var contentVariantUri of contentVariantUris) {\n error[contentVariantUri] = distributionGraphs[0][contentVariantUri] != null ?\n distributionGraphs[0][contentVariantUri][0][DatabusUris.JSONLD_VALUE] : 'none'\n }\n\n errorList.push(error);\n }\n } else {\n\n var contentVariantUri = contentVariantUris[contentVariantIndex];\n\n // else create buckets and sort files into buckets\n var buckets = {};\n\n for (var distribution of distributionGraphs) {\n\n var variantValue = distribution[contentVariantUri];\n\n if (variantValue != undefined) {\n variantValue = variantValue[0]['@value'];\n }\n\n if (variantValue == undefined || variantValue == '') {\n variantValue = '$_none$';\n }\n\n if (buckets[variantValue] == undefined) {\n buckets[variantValue] = [];\n }\n\n buckets[variantValue].push(distribution);\n }\n\n\n // iterate buckets and call recursively\n for (var b in buckets) {\n\n for (var error of DatabusUtils.cvSplit(buckets[b],\n contentVariantUris, contentVariantIndex + 1, errorList)) {\n errorList.push(error);\n }\n }\n }\n\n return errorList;\n }\n\n}\n\n// export default DatabusUtils;\n\nmodule.exports = DatabusUtils;\n\n\n//# sourceURL=webpack://databus-webapp/./js/utils/databus-utils.js?"); +eval("const DatabusCollectionUtils = __webpack_require__(/*! ../collections/databus-collection-utils */ \"./js/collections/databus-collection-utils.js\");\nvar markdownit = __webpack_require__(/*! markdown-it */ \"markdown-it\");\nconst moment = __webpack_require__(/*! moment/moment */ \"moment/moment\");\nconst DatabusUris = __webpack_require__(/*! ./databus-uris */ \"./js/utils/databus-uris.js\");\nconst ApiError = __webpack_require__(/*! ../../../server/app/common/utils/api-error */ \"../server/app/common/utils/api-error.js\");\n\nclass DatabusUtils {\n\n static stringOrFallback(value, fallback) {\n if (value != null && value.length > 0) {\n return value;\n }\n\n return fallback;\n }\n\n static resemblesTrue(value) {\n if (typeof value === 'boolean') {\n return value;\n }\n\n if (typeof value === 'string') {\n const normalized = value.trim().toLowerCase();\n return ['true', '1', 'yes', 'on'].includes(normalized);\n }\n\n if (typeof value === 'number') {\n return value === 1;\n }\n\n return false;\n }\n\n static isValidResourceIdentifier(identifier, min) {\n var identifierRegex = /^[a-z-]+$/;\n return this.checkField(identifier, identifierRegex, min, 50);\n }\n\n static formatQuery(query, placeholderMappings) {\n\n if (placeholderMappings == undefined) {\n return query;\n }\n\n for (var placeholder in placeholderMappings) {\n var re = new RegExp('%' + placeholder + '%', \"g\");\n query = query.replace(re, placeholderMappings[placeholder]);\n }\n\n return query;\n }\n\n static isValidVersionIdentifier(identifier) {\n var labelRegex = /^[A-Za-z0-9_\\.\\-]*$/;\n return this.checkField(identifier, labelRegex, 3, 50);\n }\n\n static isValidResourceText(value, min, max) {\n var textRegex = /^[\\x00-\\x7F\\n]*$/;\n return this.checkField(value, textRegex, min, max);\n }\n\n static isValidAccountName(identifier) {\n var labelRegex = /^[a-z][0-9a-z_\\-]+[0-9a-z]$/;\n return this.checkField(identifier, labelRegex, 3, 15);\n }\n\n static timeStringNow() {\n return new Date(Date.now()).toISOString();\n }\n\n static isValidGroupName(name) {\n var labelRegex = /[a-zA-Z0-9_\\-\\.]{3,50}$/;\n return this.checkField(name, labelRegex, 3, 50);\n }\n\n static isValidArtifactName(name) {\n var labelRegex = /[a-zA-Z0-9_\\-\\.]{3,50}$/;\n return this.checkField(name, labelRegex, 3, 50);\n }\n\n static isValidUrl(value) {\n var textRegex = /https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)/g;\n return textRegex.test(value);\n }\n\n static isValidResourceLabel(value, min, max) {\n var labelRegex = /^[A-Za-z0-9\\s_()\\.\\,\\-]*$/;\n return this.checkField(value, labelRegex, min, max);\n }\n\n static objSize(obj) {\n var size = 0, key;\n for (key in obj) {\n if (obj.hasOwnProperty(key)) size++;\n }\n return size;\n }\n\n static uniqueList(arr) {\n var u = {}, a = [];\n for (var i = 0, l = arr.length; i < l; ++i) {\n if (!u.hasOwnProperty(arr[i])) {\n a.push(arr[i]);\n u[arr[i]] = 1;\n }\n }\n return a;\n }\n\n\n static formatFileSize(size) {\n if (size == undefined) {\n return '0 KB'\n }\n\n if (size < 1024) return size + \" B\";\n else if (size < 1048576) return Math.round(size / 1024) + \" KB\";\n else if (size < 1073741824) return (Math.round(10 * size / 1048576) / 10) + \" MB\";\n else return (Math.round(100 * size / 1073741824) / 100) + \" GB\";\n };\n\n static checkField(value, regex, min, max) {\n if (value == undefined) {\n return false;\n }\n\n if (max > 0 && value.length > max) {\n return false;\n }\n\n if (value.length < min) {\n return false;\n }\n\n return regex.test(value);\n }\n\n // Creates a v4 uuid\n static uuidv4() {\n return '___xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\n var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n }\n\n static tryParseJson(str) {\n return JSON.parse(str);\n }\n\n static uriToTitle(uri) {\n if (uri == null) {\n return null;\n }\n\n var result = uri.substr(uri.lastIndexOf('/') + 1);\n result = result.substr(result.lastIndexOf('#') + 1);\n\n return result.charAt(0).toUpperCase() + result.slice(1);\n }\n\n static uriToName(uri) {\n if (uri == null) {\n return null;\n }\n\n var result = uri.substr(uri.lastIndexOf('/') + 1);\n result = result.substr(result.lastIndexOf('#') + 1);\n\n if (result.includes('.')) {\n result = result.substr(0, result.lastIndexOf('.'));\n }\n\n return result;\n }\n\n static uriToResourceName(uri) {\n if (uri == null) {\n return null;\n }\n\n var result = uri.substr(uri.lastIndexOf('/') + 1);\n\n if (result.includes('#')) {\n result = result.substr(0, result.indexOf('#'));\n }\n\n return result;\n }\n\n static isValidHttpUrl(string) {\n let url;\n\n try {\n url = new URL(string);\n } catch (_) {\n return false;\n }\n\n return url.protocol === \"http:\" || url.protocol === \"https:\";\n }\n\n static isValidHttpsUrl(string) {\n let url;\n\n try {\n url = new URL(string);\n } catch (_) {\n return false;\n }\n\n return url.protocol === \"https:\";\n }\n\n\n static navigateUp(uri, steps) {\n\n if (steps == undefined) {\n steps = 1;\n }\n\n for (var i = 0; i < steps; i++) {\n uri = uri.substr(0, uri.lastIndexOf('/'));\n }\n\n if (uri.includes('#')) {\n uri = uri.substr(0, uri.lastIndexOf('#'));\n }\n\n return uri;\n }\n\n static copyStringToClipboard(str) {\n // Create new element\n var el = document.createElement('textarea');\n // Set value (string to be copied)\n el.value = str;\n // Set non-editable to avoid focus and move outside of view\n el.setAttribute('readonly', '');\n el.style = { position: 'absolute', left: '-9999px' };\n document.body.appendChild(el);\n // Select text inside element\n el.select();\n // Copy text to clipboard\n document.execCommand('copy');\n // Remove temporary element\n document.body.removeChild(el);\n }\n\n static serialize(collectionObject, ignoreKeys) {\n\n if (ignoreKeys == undefined) {\n ignoreKeys = [\n 'parent',\n '$$hashKey',\n 'expanded',\n 'files',\n 'eventListeners',\n 'hasLocalChanges',\n 'published'\n ];\n }\n\n return JSON.stringify(collectionObject, function (key, value) {\n if (ignoreKeys.includes(key)) {\n return undefined;\n }\n\n return value;\n });\n }\n\n static createCleanCopy(jsonData) {\n var data = JSON.parse(DatabusCollectionUtils.serialize(jsonData));\n return data;\n }\n\n static lineCount(text) {\n return (text.match(/^\\s*\\S/gm) || \"\").length\n }\n\n\n static getResourcePathLength(uri) {\n var parts = DatabusUtils.splitResourceUri(uri);\n\n if (parts.length == 1 && parts[0] == \"\") {\n return 0;\n }\n\n return parts.length;\n }\n\n static splitResourceUri(uri) {\n\n var url = new URL(uri);\n uri = url.pathname;\n\n if (uri.startsWith('/')) {\n uri = uri.substr(1);\n }\n if (uri.endsWith('/')) {\n uri = uri.substr(0, uri.length - 1);\n }\n\n return uri.split('/');\n }\n\n static formatDate(date) {\n return moment(date).format('MMM Do YYYY') + \" (\" + moment(date).fromNow() + \")\";\n }\n\n static exportToJsonFile(jsonData) {\n\n var ignoreKeys = [\n 'parent',\n '$$hashKey',\n 'expanded',\n 'files',\n 'eventListeners',\n 'hasLocalChanges',\n 'published',\n 'uuid'\n ];\n\n let dataStr = DatabusCollectionUtils.serialize(jsonData, ignoreKeys);\n let dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(dataStr);\n\n let exportFileDefaultName = 'data.json';\n\n let linkElement = document.createElement('a');\n linkElement.setAttribute('href', dataUri);\n linkElement.setAttribute('download', exportFileDefaultName);\n linkElement.click();\n }\n\n static async parseN3(data, maxQuads) {\n return new Promise((resolve, reject) => {\n\n const quads = [];\n const prefixes = [];\n\n const parser = new N3.Parser();\n\n parser.parse(data, (e, q, p) => {\n if (e) {\n reject(e);\n return;\n }\n\n if (quads.length > maxQuads || q == null) {\n resolve({ quads: quads, prefixes: prefixes });\n }\n\n if (q) {\n quads.push(q);\n }\n });\n });\n }\n\n static async parseDatabusManifest(data) {\n\n var parsedData = await DatabusUtils.parseN3(data, 100);\n\n for (var quad of parsedData.quads) {\n\n if (quad.predicate.id == `http://www.w3.org/1999/02/22-rdf-syntax-ns#type`\n && quad.object.id == DatabusUris.DATABUS_DATABUS) {\n\n return {\n uri: quad.subject.id\n }\n }\n }\n\n return undefined;\n }\n\n static getFirstSegment(uri) {\n try {\n const url = new URL(uri);\n return url.pathname.split('/').filter(Boolean)[0] || null;\n } catch {\n return null;\n }\n }\n\n static parseMarkdown(markdown) {\n\n if (markdown == null) {\n return null;\n }\n\n var markdownParser = markdownit();\n return markdownParser.parse(markdown);\n }\n\n static renderMarkdown(markdown) {\n\n if (markdown == null) {\n return null;\n }\n\n var markdownParser = markdownit();\n return markdownParser.render(markdown);\n }\n\n /**\n * Create a dct:abstract from the content of a dct:description\n * @param {*} description \n */\n static createAbstractFromDescription(description) {\n\n if (description == null) {\n return null;\n }\n\n try {\n var tokens = this.parseMarkdown(description);\n\n\n var paragraphFound = false;\n var result = \"\";\n\n if (tokens == null) {\n return result;\n }\n\n var firstParagraphText = null;\n\n for (var i = 0; i < tokens.length; i++) {\n\n var token = tokens[i];\n var appendText = null;\n\n if (token.type == 'inline' && tokens[i - 1].type == 'paragraph_open' && token.level == 1) {\n result = token.content;\n break;\n }\n\n }\n\n return result;\n\n } catch (err) {\n console.log(err);\n return undefined;\n }\n }\n\n /**\n * Find groups files that are not distinguishable\n * @param {Array of file URIs} files \n * @param {Array of content variant names} contentVariants \n * @param {Index in the array of content variants} index \n * @returns \n */\n static cvSplit(distributionGraphs, contentVariantUris, contentVariantIndex) {\n\n var errorList = [];\n\n if (distributionGraphs.length <= 1) {\n return errorList;\n }\n\n if (contentVariantIndex >= contentVariantUris.length) {\n\n // Check buckets for double entries if (files.length > 1) {\n if (distributionGraphs.length > 1) {\n\n var error = {};\n error.downloadURLs = [];\n\n for (var distribution of distributionGraphs) {\n\n error.downloadURLs.push(distribution[DatabusUris.DCAT_DOWNLOAD_URL][0][DatabusUris.JSONLD_ID]);\n }\n\n error[DatabusUris.DATABUS_FORMAT_EXTENSION] =\n distributionGraphs[0][DatabusUris.DATABUS_FORMAT_EXTENSION][0][DatabusUris.JSONLD_VALUE];\n\n error[DatabusUris.DATABUS_COMPRESSION] =\n distributionGraphs[0][DatabusUris.DATABUS_COMPRESSION][0][DatabusUris.JSONLD_VALUE];\n\n for (var contentVariantUri of contentVariantUris) {\n error[contentVariantUri] = distributionGraphs[0][contentVariantUri] != null ?\n distributionGraphs[0][contentVariantUri][0][DatabusUris.JSONLD_VALUE] : 'none'\n }\n\n errorList.push(error);\n }\n } else {\n\n var contentVariantUri = contentVariantUris[contentVariantIndex];\n\n // else create buckets and sort files into buckets\n var buckets = {};\n\n for (var distribution of distributionGraphs) {\n\n var variantValue = distribution[contentVariantUri];\n\n if (variantValue != undefined) {\n variantValue = variantValue[0]['@value'];\n }\n\n if (variantValue == undefined || variantValue == '') {\n variantValue = '$_none$';\n }\n\n if (buckets[variantValue] == undefined) {\n buckets[variantValue] = [];\n }\n\n buckets[variantValue].push(distribution);\n }\n\n\n // iterate buckets and call recursively\n for (var b in buckets) {\n\n for (var error of DatabusUtils.cvSplit(buckets[b],\n contentVariantUris, contentVariantIndex + 1, errorList)) {\n errorList.push(error);\n }\n }\n }\n\n return errorList;\n }\n\n}\n\nmodule.exports = DatabusUtils;\n\n\n//# sourceURL=webpack://databus-webapp/./js/utils/databus-utils.js?"); /***/ }), @@ -555,7 +655,7 @@ eval("const DatabusCollectionUtils = __webpack_require__(/*! ../collections/data \******************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("const DatabusAlert = __webpack_require__(/*! ../components/databus-alert/databus-alert */ \"./js/components/databus-alert/databus-alert.js\");\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\nconst DatabusMessages = __webpack_require__(/*! ./databus-messages */ \"./js/utils/databus-messages.js\");\n\nclass DatabusWebappUtils {\n\n constructor($scope, $sce) {\n this.scope = $scope;\n this.sce = $sce;\n }\n\n createAccount() {\n window.location = '/app/account';\n }\n\n login() {\n window.location = '/app/login?redirectUrl=' + encodeURIComponent(window.location);\n }\n\n logout() {\n window.location = '/app/logout?redirectUrl=' + encodeURIComponent(window.location);\n }\n\n formatDateFromNow(date) {\n return moment(date).fromNow();\n }\n\n markdownToHtml(markdown) {\n\n if(this.sce == null) {\n return markdown;\n }\n\n var markdown = DatabusUtils.renderMarkdown(markdown);\n\n return this.sce.trustAsHtml(markdown);\n };\n\n formatDate(date) {\n return DatabusUtils.formatDate(date); // moment(date).format('MMM Do YYYY') + \" (\" + moment(date).fromNow() + \")\";\n }\n\n formatLongDate(longString) {\n var number = new Number(longString);\n var dateTime = new Date(number);\n return this.formatDate(dateTime);\n }\n\n formatFileSize (size) {\n return DatabusUtils.formatFileSize(size);\n }\n\n getPathname(uri) {\n var url = new URL(uri);\n return url.pathname;\n }\n\n objSize(obj) {\n return DatabusUtils.objSize(obj);\n }\n\n navigateUp(uri) {\n return DatabusUtils.navigateUp(uri);\n }\n\n uriToName(uri) {\n return DatabusUtils.uriToName(uri); \n }\n\n uriToResourceName(uri) {\n return DatabusUtils.uriToResourceName(uri);\n }\n\n isValidHttpsUrl(url) {\n return DatabusUtils.isValidHttpsUrl(url);\n }\n\n copyToClipboard(str) {\n\n if(typeof str === 'object') {\n str = JSON.stringify(str, null, 3);\n }\n\n // Create new element\n var el = document.createElement('textarea');\n // Set value (string to be copied)\n el.value = str;\n // Set non-editable to avoid focus and move outside of view\n el.setAttribute('readonly', '');\n el.style = { position: 'absolute', left: '-9999px' };\n document.body.appendChild(el);\n // Select text inside element\n el.select();\n // Copy text to clipboard\n document.execCommand('copy');\n // Remove temporary element\n document.body.removeChild(el);\n\n DatabusAlert.alert(this.scope, true, DatabusMessages.GENERIC_COPIED_TO_CLIPBOARD);\n }\n}\n\nmodule.exports = DatabusWebappUtils;\n\n\n//# sourceURL=webpack://databus-webapp/./js/utils/databus-webapp-utils.js?"); +eval("const DatabusAlert = __webpack_require__(/*! ../components/databus-alert/databus-alert */ \"./js/components/databus-alert/databus-alert.js\");\r\nconst DatabusUtils = __webpack_require__(/*! ../utils/databus-utils */ \"./js/utils/databus-utils.js\");\r\nconst DatabusMessages = __webpack_require__(/*! ./databus-messages */ \"./js/utils/databus-messages.js\");\r\n\r\nclass DatabusWebappUtils {\r\n\r\n constructor($scope, $sce) {\r\n this.scope = $scope;\r\n this.sce = $sce;\r\n }\r\n\r\n goTo(page) {\r\n window.location = page;\r\n }\r\n \r\n createAccount() {\r\n window.location = '/app/account';\r\n }\r\n\r\n \r\n getAccountName() {\r\n\r\n let accountName = window.location.pathname.split('/')[1];\r\n\r\n if(accountName.length < 4) {\r\n return null;\r\n }\r\n\r\n return this.getOwnedAccountName(accountName);\r\n }\r\n\r\n getOwnedAccountName(accountName) {\r\n if(!this.scope.auth.authenticated || this.scope.auth.info == null) {\r\n return null;\r\n }\r\n\r\n let userInfo = this.scope.auth.info;\r\n\r\n if(!Array.isArray(userInfo.accounts) || userInfo.accounts.length == 0) {\r\n return null;\r\n }\r\n\r\n let account = userInfo.accounts.find(a => a.accountName == accountName);\r\n\r\n if(account == null) {\r\n return null;\r\n }\r\n\r\n return account.accountName;\r\n }\r\n\r\n login() {\r\n window.location = '/app/login?redirectUrl=' + encodeURIComponent(window.location);\r\n }\r\n\r\n logout() {\r\n window.location = '/app/logout?redirectUrl=' + encodeURIComponent(window.location);\r\n }\r\n\r\n formatDateFromNow(date) {\r\n return moment(date).fromNow();\r\n }\r\n\r\n markdownToHtml(markdown) {\r\n\r\n if(this.sce == null) {\r\n return markdown;\r\n }\r\n\r\n var markdown = DatabusUtils.renderMarkdown(markdown);\r\n\r\n return this.sce.trustAsHtml(markdown);\r\n };\r\n\r\n formatDate(date) {\r\n return DatabusUtils.formatDate(date); // moment(date).format('MMM Do YYYY') + \" (\" + moment(date).fromNow() + \")\";\r\n }\r\n\r\n formatLongDate(longString) {\r\n var number = new Number(longString);\r\n var dateTime = new Date(number);\r\n return this.formatDate(dateTime);\r\n }\r\n\r\n formatFileSize (size) {\r\n return DatabusUtils.formatFileSize(size);\r\n }\r\n\r\n getPathname(uri) {\r\n var url = new URL(uri);\r\n return url.pathname;\r\n }\r\n\r\n objSize(obj) {\r\n return DatabusUtils.objSize(obj);\r\n }\r\n\r\n navigateUp(uri) {\r\n return DatabusUtils.navigateUp(uri);\r\n }\r\n\r\n uriToName(uri) {\r\n return DatabusUtils.uriToName(uri); \r\n }\r\n\r\n uriToResourceName(uri) {\r\n return DatabusUtils.uriToResourceName(uri);\r\n }\r\n\r\n isValidHttpsUrl(url) {\r\n return DatabusUtils.isValidHttpsUrl(url);\r\n }\r\n\r\n copyToClipboard(str) {\r\n\r\n if(typeof str === 'object') {\r\n str = JSON.stringify(str, null, 3);\r\n }\r\n\r\n // Create new element\r\n var el = document.createElement('textarea');\r\n // Set value (string to be copied)\r\n el.value = str;\r\n // Set non-editable to avoid focus and move outside of view\r\n el.setAttribute('readonly', '');\r\n el.style = { position: 'absolute', left: '-9999px' };\r\n document.body.appendChild(el);\r\n // Select text inside element\r\n el.select();\r\n // Copy text to clipboard\r\n document.execCommand('copy');\r\n // Remove temporary element\r\n document.body.removeChild(el);\r\n\r\n DatabusAlert.alert(this.scope, true, DatabusMessages.GENERIC_COPIED_TO_CLIPBOARD);\r\n }\r\n}\r\n\r\nmodule.exports = DatabusWebappUtils;\r\n\n\n//# sourceURL=webpack://databus-webapp/./js/utils/databus-webapp-utils.js?"); /***/ }), @@ -565,7 +665,18 @@ eval("const DatabusAlert = __webpack_require__(/*! ../components/databus-alert/d \**********************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { -eval("/* module decorator */ module = __webpack_require__.nmd(module);\nconst DatabusUris = __webpack_require__(/*! ./databus-uris */ \"./js/utils/databus-uris.js\");\n\n\nclass JsonldUtils {\n\n static getTypedGraph(graphs, graphType) {\n\n for (var g in graphs) {\n var graph = graphs[g];\n\n if (graph[DatabusUris.JSONLD_TYPE] != undefined && graph[DatabusUris.JSONLD_TYPE].includes(graphType)) {\n return graph;\n }\n }\n\n return null;\n }\n\n static setLiteral(graph, property, type, value) {\n graph[property] = [];\n\n var entry = {};\n entry[DatabusUris.JSONLD_TYPE] = type;\n entry[DatabusUris.JSONLD_VALUE] = value;\n\n graph[property].push(entry);\n }\n\n static setLink(graph, property, uri) {\n graph[property] = [];\n\n var entry = {};\n entry[DatabusUris.JSONLD_ID] = uri;\n\n graph[property].push(entry);\n }\n\n static getProperty(graph, property) {\n if (graph[property] == undefined) {\n return null;\n }\n\n if (graph[property].length == 1) {\n var value = graph[property][0];\n\n if (value[DatabusUris.JSONLD_VALUE] != null) {\n return value[DatabusUris.JSONLD_VALUE];\n }\n\n if (value[DatabusUris.JSONLD_ID] != null) {\n return value[DatabusUris.JSONLD_ID];\n }\n\n return null;\n } else {\n var result = [];\n\n for (var value of graph[property]) {\n\n if (value[DatabusUris.JSONLD_VALUE] != null) {\n result.push(value[DatabusUris.JSONLD_VALUE]);\n }\n\n if (value[DatabusUris.JSONLD_ID] != null) {\n result.push(value[DatabusUris.JSONLD_ID]);\n }\n }\n\n if (result.length > 0) {\n return result;\n }\n }\n\n return null;\n }\n\n static getGraphById(graphs, id) {\n for (var g in graphs) {\n var graph = graphs[g];\n\n if (graph[DatabusUris.JSONLD_ID] != undefined && graph[DatabusUris.JSONLD_ID] == id) {\n return graph;\n }\n }\n\n return null;\n\n\n }\n\n static getTypedGraphs(graphs, graphType) {\n var result = [];\n\n for (var g in graphs) {\n var graph = graphs[g];\n\n if (graph[DatabusUris.JSONLD_TYPE] != undefined &&\n graph[DatabusUris.JSONLD_TYPE].includes(graphType)) {\n result.push(graph);\n }\n }\n\n return result;\n }\n\n static getSubPropertyGraphs(graphs, propertyUri) {\n\n var result = [];\n\n for (var graph of graphs) {\n if (graph[DatabusUris.RDFS_SUB_PROPERTY_OF] == undefined) {\n continue;\n }\n\n for (var property of graph[DatabusUris.RDFS_SUB_PROPERTY_OF]) {\n if (property[DatabusUris.JSONLD_ID] == propertyUri) {\n result.push(graph);\n }\n }\n }\n\n return result;\n }\n\n\n static getFirstObject(graph, key) {\n var obj = graph[key];\n\n if (obj == undefined || obj.length < 1) {\n return null;\n }\n\n return obj[0];\n }\n\n static getFirstObjectUri(graph, key) {\n var obj = graph[key];\n\n if (obj == undefined || obj.length < 1) {\n return null;\n }\n\n return obj[0][DatabusUris.JSONLD_ID];\n }\n}\n\n\n\nif ( true && module && module.exports)\n module.exports = JsonldUtils;\n\n//# sourceURL=webpack://databus-webapp/./js/utils/jsonld-utils.js?"); +eval("/* module decorator */ module = __webpack_require__.nmd(module);\nconst DatabusUris = __webpack_require__(/*! ./databus-uris */ \"./js/utils/databus-uris.js\");\r\n\r\n\r\nclass JsonldUtils {\r\n\r\n static refTo(uri) {\r\n var result = {};\r\n result[DatabusUris.JSONLD_ID] = uri;\r\n return result;\r\n }\r\n\r\n static getTypedGraph(graphs, graphType) {\r\n\r\n for (var g in graphs) {\r\n var graph = graphs[g];\r\n\r\n if (graph[DatabusUris.JSONLD_TYPE] != undefined && graph[DatabusUris.JSONLD_TYPE].includes(graphType)) {\r\n return graph;\r\n }\r\n }\r\n\r\n return null;\r\n }\r\n\r\n static setLiteral(graph, property, type, value) {\r\n graph[property] = [];\r\n\r\n var entry = {};\r\n entry[DatabusUris.JSONLD_TYPE] = type;\r\n entry[DatabusUris.JSONLD_VALUE] = value;\r\n\r\n graph[property].push(entry);\r\n }\r\n\r\n static setLink(graph, property, uri) {\r\n graph[property] = [];\r\n\r\n var entry = {};\r\n entry[DatabusUris.JSONLD_ID] = uri;\r\n\r\n graph[property].push(entry);\r\n }\r\n\r\n static getGraphById = function (graphs, id) {\r\n return graphs.find(g => g[DatabusUris.JSONLD_ID] === id);\r\n };\r\n\r\n static getRefArrayProperty = function (graph, propertyUri) {\r\n const val = graph[propertyUri];\r\n if (!val) return [];\r\n return val.map(v => v[DatabusUris.JSONLD_ID]);\r\n };\r\n\r\n static getProperty(graph, property) {\r\n if (graph[property] == undefined) {\r\n return null;\r\n }\r\n\r\n if (graph[property].length == 1) {\r\n var value = graph[property][0];\r\n\r\n if (value[DatabusUris.JSONLD_VALUE] != null) {\r\n return value[DatabusUris.JSONLD_VALUE];\r\n }\r\n\r\n if (value[DatabusUris.JSONLD_ID] != null) {\r\n return value[DatabusUris.JSONLD_ID];\r\n }\r\n\r\n return null;\r\n } else {\r\n var result = [];\r\n\r\n for (var value of graph[property]) {\r\n\r\n if (value[DatabusUris.JSONLD_VALUE] != null) {\r\n result.push(value[DatabusUris.JSONLD_VALUE]);\r\n }\r\n\r\n if (value[DatabusUris.JSONLD_ID] != null) {\r\n result.push(value[DatabusUris.JSONLD_ID]);\r\n }\r\n }\r\n\r\n if (result.length > 0) {\r\n return result;\r\n }\r\n }\r\n\r\n return null;\r\n }\r\n\r\n static getFirstProperty(graph, property) {\r\n if (graph[property] == undefined) {\r\n return null;\r\n }\r\n\r\n const values = graph[property];\r\n\r\n if (values.length === 0) {\r\n return null;\r\n }\r\n\r\n if (values.length === 1) {\r\n const value = values[0];\r\n\r\n if (value[DatabusUris.JSONLD_VALUE] != null) {\r\n return value[DatabusUris.JSONLD_VALUE];\r\n }\r\n\r\n if (value[DatabusUris.JSONLD_ID] != null) {\r\n return value[DatabusUris.JSONLD_ID];\r\n }\r\n\r\n return null;\r\n }\r\n\r\n for (const value of values) {\r\n if (value[DatabusUris.JSONLD_VALUE] != null) {\r\n return value[DatabusUris.JSONLD_VALUE];\r\n }\r\n\r\n if (value[DatabusUris.JSONLD_ID] != null) {\r\n return value[DatabusUris.JSONLD_ID];\r\n }\r\n }\r\n\r\n return null;\r\n }\r\n\r\n\r\n static getGraphById(graphs, id) {\r\n for (var g in graphs) {\r\n var graph = graphs[g];\r\n\r\n if (graph[DatabusUris.JSONLD_ID] != undefined && graph[DatabusUris.JSONLD_ID] == id) {\r\n return graph;\r\n }\r\n }\r\n\r\n return null;\r\n }\r\n\r\n static getTypedGraphs(graphs, graphType) {\r\n var result = [];\r\n\r\n for (var g in graphs) {\r\n var graph = graphs[g];\r\n\r\n if (graph[DatabusUris.JSONLD_TYPE] != undefined &&\r\n graph[DatabusUris.JSONLD_TYPE].includes(graphType)) {\r\n result.push(graph);\r\n }\r\n }\r\n\r\n return result;\r\n }\r\n\r\n static getSubPropertyGraphs(graphs, propertyUri) {\r\n\r\n var result = [];\r\n\r\n for (var graph of graphs) {\r\n if (graph[DatabusUris.RDFS_SUB_PROPERTY_OF] == undefined) {\r\n continue;\r\n }\r\n\r\n for (var property of graph[DatabusUris.RDFS_SUB_PROPERTY_OF]) {\r\n if (property[DatabusUris.JSONLD_ID] == propertyUri) {\r\n result.push(graph);\r\n }\r\n }\r\n }\r\n\r\n return result;\r\n }\r\n\r\n\r\n static getFirstObject(graph, key) {\r\n var obj = graph[key];\r\n\r\n if (obj == undefined || obj.length < 1) {\r\n return null;\r\n }\r\n\r\n return obj[0];\r\n }\r\n\r\n static getFirstObjectUri(graph, property) {\r\n // Get the object \r\n const obj = graph[property];\r\n\r\n // Not found -> null\r\n if (!obj) {\r\n return null;\r\n }\r\n\r\n // If it is an array...\r\n if (Array.isArray(obj)) {\r\n for (const item of obj) {\r\n if (item && typeof item === 'object' && DatabusUris.JSONLD_ID in item) {\r\n return item[DatabusUris.JSONLD_ID];\r\n }\r\n }\r\n } else if (typeof obj === 'object' && DatabusUris.JSONLD_ID in obj) {\r\n return obj[DatabusUris.JSONLD_ID];\r\n }\r\n\r\n return null;\r\n }\r\n}\r\n\r\n\r\n\r\nif ( true && module && module.exports)\r\n module.exports = JsonldUtils;\n\n//# sourceURL=webpack://databus-webapp/./js/utils/jsonld-utils.js?"); + +/***/ }), + +/***/ "./js/utils/messages.js": +/*!******************************!*\ + !*** ./js/utils/messages.js ***! + \******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ DatabusMsg: () => (/* binding */ DatabusMsg)\n/* harmony export */ });\nclass DatabusMsg {\r\n static messages = {\r\n err_invalid_group_name: \"Please enter between 3 to 50 characters. \\nRegex: [a-zA-Z0-9_\\\\-\\\\.]{3,50}$\",\r\n err_no_group_selected: \"Please select a group\",\r\n err_no_artifact_selected: \"Please select an artifact\",\r\n \r\n err_invalid_artifact_name: \"Please enter between 3 to 50 characters. \\nRegex: [a-zA-Z0-9_\\\\-\\\\.]{3,50}$\",\r\n err_invalid_version_name: \"Please enter between 3 to 50 characters. \\nRegex: [a-zA-Z0-9_\\\\-\\\\.]{3,50}$\",\r\n err_invalid_version_title: \"The version title is missing.\",\r\n err_invalid_version_abstract: \"The version abstract is missing.\",\r\n err_invalid_version_description: \"The version description is missing.\",\r\n err_invalid_version_license: \"The license is invalid. Please enter a license URI.\",\r\n err_no_files: \"You have to upload at least one file.\",\r\n err_not_analyzed: \"This file has not been analzyed yet.\",\r\n warning_group_exists: \"A group with this name already exists. Publishing will overwrite its metadata.\",\r\n warning_artifact_exists: \"An artifact with this name already exists. Publishing will overwrite its metadata.\",\r\n warning_version_exists: \"A version with this name already exists. Publishing will overwrite its metadata. This is not recommended, as other users might use your version identifier as a data dependency.\"\r\n };\r\n\r\n static get(key) {\r\n return this.messages[key] || \"Unknown validation key.\";\r\n }\r\n}\r\n\n\n//# sourceURL=webpack://databus-webapp/./js/utils/messages.js?"); /***/ }), @@ -585,7 +696,17 @@ eval("/**\n * Query Templates can be defined as object with the fields:\n * > se \************************************/ /***/ ((module) => { -eval("\nclass TabNavigation {\n\n constructor($scope, $location, tabKeys) {\n this.location = $location;\n this.tabKeys = tabKeys;\n this.activeTab = 0;\n\n var self = this;\n // Watch the location hash and tell the tabnavigation that it changed\n $scope.$watch(function () {\n return $location.hash();\n }, function (newVal, oldVal) {\n self.onLocationHashChanged(newVal, oldVal)\n }, false);\n }\n\n\n onLocationHashChanged(value, oldVal) {\n for (var i in this.tabKeys) {\n var tabKey = this.tabKeys[i];\n if (value == tabKey) {\n this.activeTab = i;\n return;\n }\n }\n }\n\n /**\n * Change the tab - set location hash and scroll up\n * @param {*} value \n */\n navigateTo(value, scrollToTop) {\n this.location.hash(value);\n\n if(scrollToTop == true) {\n window.scrollTo(0, 0);\n }\n }\n\n}\n\nmodule.exports = TabNavigation;\n\n//# sourceURL=webpack://databus-webapp/./js/utils/tab-navigation.js?"); +eval("\r\nclass TabNavigation {\r\n\r\n constructor($scope, $location, tabKeys, onNavigateCallback) {\r\n this.location = $location;\r\n this.tabKeys = tabKeys;\r\n this.activeTab = 0;\r\n this.onNavigateCallback = onNavigateCallback;\r\n\r\n var self = this;\r\n // Watch the location hash and tell the tabnavigation that it changed\r\n $scope.$watch(function () {\r\n return $location.hash();\r\n }, function (newVal, oldVal) {\r\n self.onLocationHashChanged(newVal, oldVal)\r\n }, false);\r\n }\r\n\r\n\r\n onLocationHashChanged(value, oldVal) {\r\n for (var i in this.tabKeys) {\r\n var tabKey = this.tabKeys[i];\r\n if (value == tabKey) {\r\n this.activeTab = i;\r\n\r\n if(this.onNavigateCallback != null) {\r\n this.onNavigateCallback(this.activeTab);\r\n }\r\n return;\r\n }\r\n }\r\n\r\n this.activeTab = 0;\r\n if(this.onNavigateCallback != null) {\r\n this.onNavigateCallback(this.activeTab);\r\n }\r\n }\r\n\r\n /**\r\n * Change the tab - set location hash and scroll up\r\n * @param {*} value \r\n */\r\n navigateTo(value, scrollToTop) {\r\n this.location.hash(value);\r\n\r\n if(scrollToTop == true) {\r\n window.scrollTo(0, 0);\r\n }\r\n }\r\n\r\n}\r\n\r\nmodule.exports = TabNavigation;\n\n//# sourceURL=webpack://databus-webapp/./js/utils/tab-navigation.js?"); + +/***/ }), + +/***/ "../server/app/common/utils/api-error.js": +/*!***********************************************!*\ + !*** ../server/app/common/utils/api-error.js ***! + \***********************************************/ +/***/ ((module) => { + +eval("class ApiError extends Error {\r\n constructor(statusCode, resource, message, body) {\r\n super(message);\r\n this.name = \"ApiError\";\r\n this.statusCode = statusCode;\r\n this.resource = resource;\r\n this.body = body;\r\n }\r\n}\r\n\r\nmodule.exports = ApiError;\n\n//# sourceURL=webpack://databus-webapp/../server/app/common/utils/api-error.js?"); /***/ }), @@ -641,6 +762,34 @@ module.exports = self["moment"]; /******/ } /******/ /************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ /******/ /* webpack/runtime/node module decorator */ /******/ (() => { /******/ __webpack_require__.nmd = (module) => { diff --git a/public/fonts/Inter-Black.ttf b/public/fonts/Inter-Black.ttf new file mode 100644 index 00000000..b27822ba Binary files /dev/null and b/public/fonts/Inter-Black.ttf differ diff --git a/public/fonts/Inter-Bold.ttf b/public/fonts/Inter-Bold.ttf new file mode 100644 index 00000000..fe23eeb9 Binary files /dev/null and b/public/fonts/Inter-Bold.ttf differ diff --git a/public/fonts/Inter-ExtraBold.ttf b/public/fonts/Inter-ExtraBold.ttf new file mode 100644 index 00000000..874b1b0d Binary files /dev/null and b/public/fonts/Inter-ExtraBold.ttf differ diff --git a/public/fonts/Inter-ExtraLight.ttf b/public/fonts/Inter-ExtraLight.ttf new file mode 100644 index 00000000..c993e822 Binary files /dev/null and b/public/fonts/Inter-ExtraLight.ttf differ diff --git a/public/fonts/Inter-Light.ttf b/public/fonts/Inter-Light.ttf new file mode 100644 index 00000000..71188f5c Binary files /dev/null and b/public/fonts/Inter-Light.ttf differ diff --git a/public/fonts/Inter-Medium.ttf b/public/fonts/Inter-Medium.ttf new file mode 100644 index 00000000..a01f3777 Binary files /dev/null and b/public/fonts/Inter-Medium.ttf differ diff --git a/public/fonts/Inter-Regular.ttf b/public/fonts/Inter-Regular.ttf new file mode 100644 index 00000000..5e4851f0 Binary files /dev/null and b/public/fonts/Inter-Regular.ttf differ diff --git a/public/fonts/Inter-SemiBold.ttf b/public/fonts/Inter-SemiBold.ttf new file mode 100644 index 00000000..ecc7041e Binary files /dev/null and b/public/fonts/Inter-SemiBold.ttf differ diff --git a/public/fonts/Inter-Thin.ttf b/public/fonts/Inter-Thin.ttf new file mode 100644 index 00000000..fe77243f Binary files /dev/null and b/public/fonts/Inter-Thin.ttf differ diff --git a/public/fonts/Roboto-Bold.ttf b/public/fonts/Roboto-Bold.ttf new file mode 100644 index 00000000..43da14d8 Binary files /dev/null and b/public/fonts/Roboto-Bold.ttf differ diff --git a/public/fonts/Roboto-Regular.ttf b/public/fonts/Roboto-Regular.ttf new file mode 100644 index 00000000..ddf4bfac Binary files /dev/null and b/public/fonts/Roboto-Regular.ttf differ diff --git a/public/js/angular-application.js b/public/js/angular-application.js index 93a692f7..9a808824 100644 --- a/public/js/angular-application.js +++ b/public/js/angular-application.js @@ -8,6 +8,7 @@ const GroupPageController = require("./page-controller/group-controller"); const ProfileController = require("./page-controller/profile-controller"); const PublishWizardController = require("./page-controller/publish-wizard-controller"); const VersionPageController = require("./page-controller/version-controller"); +const UserSettingsController = require("./page-controller/user-settings-controller"); const DatabusCollectionManager = require("./collections/databus-collection-manager"); const SearchManager = require("./search/search-manager"); const SearchController = require("./components/search/search-controller"); @@ -35,6 +36,10 @@ const CollectionDataTableController = require("./components/collection-data-tabl const AccountHistoryController = require("./components/account-history/account-history"); const SparqlEditorController = require("./page-controller/sparql-editor-controller"); const BetterDropdownController = require("./components/better-dropdown/better-dropdown"); +const NavSearchController = require("./components/nav-search/nav-search-controller"); +const EntityDropdownController = require("./components/entity-dropdown/entity-dropdown"); +const EntityApiViewController = require("./components/entity-api-view/entity-api-view"); +const ErrorNotificationController = require("./components/error-notification/error-notifcation"); var databusApplication = angular.module("databusApplication", []) .controller("HeaderController", ["$scope", "$http", "collectionManager", HeaderController]) @@ -49,6 +54,7 @@ var databusApplication = angular.module("databusApplication", []) }); }; }]) + .controller("UserSettingsController", [ "$scope", "$http", "$sce", "$location", UserSettingsController]) .controller("HeaderController", ["$scope", "$http", "collectionManager", "searchManager", HeaderController]) .controller("AccountPageController", ["$scope", "$http", "$location", "collectionManager", AccountPageController]) .controller("FrontPageController", ["$scope", "$sce", "$http", FrontPageController]) @@ -57,8 +63,8 @@ var databusApplication = angular.module("databusApplication", []) .controller("CollectionsEditorController", ["$scope", "$timeout", "$http", "$location", "collectionManager", CollectionsEditorController]) .controller("GroupPageController", ["$scope", "$http", "$sce", "$interval", "$location", "collectionManager", GroupPageController]) .controller("ProfileController", ["$scope", "$http", ProfileController]) - .controller("SparqlEditorController", ["$scope", "$http", SparqlEditorController]) - .controller("PublishWizardController", ["$scope", "$http", "$interval", "focus", "$q", PublishWizardController]) + .controller("SparqlEditorController", ["$scope", "$http", "$location", SparqlEditorController]) + .controller("PublishWizardController", ["$scope", "$http", "$interval", "focus", "$q", "$location", PublishWizardController]) .controller("VersionPageController", ["$scope", "$http", "$sce", "$location", "collectionManager", VersionPageController]) .directive('uploadRanking', function () { return { @@ -121,6 +127,42 @@ databusApplication.component('overrideCheckbox', { } }); +databusApplication.component('errorTag', { + controller: ErrorNotificationController, + templateUrl: '/js/components/error-notification/error-notification.html', + bindings: { + entity: '<', + key: '@', + texts: '<' + } +}); + +databusApplication.component('entityDropdown', { + bindings: { + placeholder: '@', + items: '<', + displayProperty: '@', + loading: '<', + selected: '<', + onSelect: '&' + }, + controller: EntityDropdownController, + templateUrl: '/js/components/entity-dropdown/entity-dropdown.html' +}); + +databusApplication.component('entityApiView', { + bindings: { + entity: '<', + apiKeys: '<', + texts: '<', + publishLog: '<' + }, + controller: EntityApiViewController, + templateUrl: '/js/components/entity-api-view/entity-api-view.html' + }); + + + databusApplication.component('accountHistory', { templateUrl: '/js/components/account-history/account-history.html', controller: [ '$http', AccountHistoryController ], @@ -158,6 +200,16 @@ databusApplication.component('search', { } }); + +databusApplication.component('navSearch', { + templateUrl: '/js/components/nav-search/nav-search.html', + controller: ['$http', '$interval', '$sce', 'searchManager', NavSearchController], + bindings: { + searchInput: '=', + settings: '<', + } +}); + /* databusApplication.component('databusSearch', { templateUrl: '/js/components/databus-search/databus-search.html', @@ -387,6 +439,7 @@ databusApplication.component('tableEditor', { bindings: { model: '=', onRemoveFile: '&', + onEditContentVariant: '&', onAnalyzeFile: '&', analysisProcesses: '<' } diff --git a/public/js/collections/databus-collection-manager.js b/public/js/collections/databus-collection-manager.js index 584074e1..156fc8ca 100644 --- a/public/js/collections/databus-collection-manager.js +++ b/public/js/collections/databus-collection-manager.js @@ -1,3 +1,4 @@ +const AppJsonFormatter = require("../utils/app-json-formatter"); const DatabusUris = require("../utils/databus-uris"); const DatabusUtils = require("../utils/databus-utils"); const DatabusCollectionUtils = require("./databus-collection-utils"); @@ -25,21 +26,30 @@ class DatabusCollectionManager { // Ja? -> Lade async, uberschreibe remote entry // Nein? -> Lade async, setze remote und local entry + _isInitialized = false; + _initSubscribers = []; + constructor($http, $interval, storageKey) { try { - this.storageKeyPrefix = `${storageKey}_${encodeURI(DATABUS_RESOURCE_BASE_URL)}`; + this.storageKeyPrefix = `${encodeURI(DATABUS_RESOURCE_BASE_URL)}`; // window.sessionStorage.removeItem(`${this.storageKeyPrefix}_session`); - + this.sessionInfo = JSON.parse(window.sessionStorage.getItem(`${this.storageKeyPrefix}_session`)); - + if (this.sessionInfo == undefined) { this.sessionInfo = {}; } - } catch(err) { + + window.sessionStorage.setItem(`${this.storageKeyPrefix}_session`, JSON.stringify(this.sessionInfo)); + this.storageKey = `${this.storageKeyPrefix}__collections`; + this.local = this.loadCollectionsFromStorage(true); + this.remote = {}; + + } catch (err) { this.sessionInfo = {}; } - + this.http = $http; this.interval = $interval; @@ -54,20 +64,71 @@ class DatabusCollectionManager { return this.sessionInfo != undefined ? this.sessionInfo.accountName : undefined; } - async tryInitialize(accountName, loadFromServer) { + getLocalCollectionByUri(uri) { + for (let guid in this.local) { + let localCollection = this.local[guid]; + + if (localCollection.uri == uri) { + return localCollection; + } + } + + return undefined; + } + + /** + * Set up the collection mananger for a specific account. + * 1) Load ALL the collections of this account from the remote + * 2) Save to remote map + * 3) Create local working copies if local has no entry for remote collection + * @param {*} accountName + * @returns + */ + async tryInitialize(accountName) { - if(accountName == undefined) { + // Needs an account name to set up + if (accountName == undefined) { return; } + // this.remote = this.loadCollectionsFromStorage(false); this.sessionInfo.accountName = accountName; - window.sessionStorage.setItem(`${this.storageKeyPrefix}_session`, JSON.stringify(this.sessionInfo)); - this.storageKey = `${this.storageKeyPrefix}__${accountName}`; - this.remote = this.loadCollectionsFromStorage(false); - this.local = this.loadCollectionsFromStorage(true); + + + let collectionListResponse = await this.http.get(`/app/account/collections?account=${encodeURIComponent(accountName)}`); + let remoteCollections = collectionListResponse.data; + + + let wasLocalCollectionAdded = false; + + for (let collectionUri in remoteCollections) { + let remoteCollection = remoteCollections[collectionUri]; + let localCollection = this.getLocalCollectionByUri(collectionUri); + + // Create local copy if not exist + if (localCollection == undefined) { + localCollection = JSON.parse(JSON.stringify(remoteCollection)); + localCollection.uuid = DatabusCollectionUtils.uuidv4(); + this.local[localCollection.uuid] = localCollection; + wasLocalCollectionAdded = true; + } + + this.remote[localCollection.uuid] = remoteCollection; + this.remote[localCollection.uuid].isHidden = this.remote[localCollection.uuid].issued == undefined; + + if(this.local[localCollection.uuid].isHidden == undefined) { + this.local[localCollection.uuid].isHidden = this.remote[localCollection.uuid].isHidden; + } + } + + if(wasLocalCollectionAdded) { + this.saveLocally(); + } + this.findActive(); + /* if (loadFromServer) { try { @@ -79,6 +140,7 @@ class DatabusCollectionManager { console.log(e); } } + */ var self = this; @@ -103,6 +165,31 @@ class DatabusCollectionManager { } } }, 300); + + + this._isInitialized = true; + this._notifyInitialized(); + } + + get isInitialized() { + return this._isInitialized; + } + + subscribeOnInitialized(callback) { + if (this._isInitialized) { + callback(); + } else { + this._initSubscribers.push(callback); + } + } + + _notifyInitialized() { + this._initSubscribers.forEach(cb => cb()); + this._initSubscribers = []; + } + + get hasAccountName() { + return this.accountName != null; } // Setze das remote array und update local array @@ -184,36 +271,31 @@ class DatabusCollectionManager { } */ - // Call this always in header-controller.js - if (this.activeCollection == null) { - // select first or create a new draft if we don't have any local drafts yet - this.selectFirstOrCreate(); - } + // QueryNode.assignParents(this.activeCollection.content.root); // Save locally in case we created any local working copies + this.saveLocally(); - if (this.initialized !== undefined) { - this.initialized(); + // Call this always in header-controller.js + if (this.activeCollection == null) { + // select first or create a new draft if we don't have any local drafts yet + this.selectFirstOrCreate(); } - } findActive() { - if (!this.isInitialized) throw "Databus-Collection-Manager is not initialized1."; + if (!this.hasAccountName) throw "Databus-Collection-Manager is not initialized1."; if (this.activeCollection == undefined) { this.selectFirstOrCreate(); } } - get isInitialized() { - return this.accountName != null; - } loadCollectionsFromStorage(local = true) { let collections; @@ -231,6 +313,8 @@ class DatabusCollectionManager { for (let identifier in collections) { if (identifier === undefined || identifier === "undefined") { delete (collections[identifier]); + } else if (collections[identifier].accountName == null) { + delete (collections[identifier]); } else { //enable Collection Utils for all collections in local storage collections[identifier] = new DatabusCollectionWrapper(collections[identifier]); @@ -243,7 +327,7 @@ class DatabusCollectionManager { /** * Selects the first collection in the local list or creates a new draft */ - selectFirstOrCreate() { + selectFirstOrCreate(accountName) { for (let identifier in this.local) { this.setActive(identifier); @@ -252,12 +336,12 @@ class DatabusCollectionManager { // Create new collection if current is null if (this.activeCollection == null) { - this.createNew("Unnamed Collection", "", function (response) { }); + this.createNew(accountName, "Unnamed Collection", "", function (response) { }); } } setActive(uuid) { - if (!this.isInitialized) throw "Databus-Collection-Manager is not initialized1."; + if (!this.hasAccountName) throw "Databus-Collection-Manager is not initialized1."; this.convertCollectionContentToTree(uuid); @@ -326,9 +410,10 @@ class DatabusCollectionManager { } createSnapshot(source) { // convert each version="latest" to actual latest version - if (!this.isInitialized) throw "Databus-Collection-Manager is not initialized."; + if (!this.hasAccountName) throw "Databus-Collection-Manager is not initialized."; let collection = DatabusCollectionWrapper.createNew(); + collection.accountName = this.accountName; collection.content = DatabusCollectionUtils.createCleanCopy(source.content); let root = collection.content.root; @@ -378,7 +463,7 @@ class DatabusCollectionManager { } saveLocally() { - if (!this.isInitialized) throw "Databus-Collection-Manager is not initialized."; + if (!this.hasAccountName) throw "Databus-Collection-Manager is not initialized."; if (this.activeCollection != null) { this.activeCollection.hasLocalChanges = this.hasLocalChanges(this.activeCollection); @@ -389,6 +474,12 @@ class DatabusCollectionManager { window.localStorage.setItem(`${this.storageKey}_hash`, hash); + for (let identifier in this.local) { + if (this.local[identifier].accountName == null) { + delete (this.local[identifier]); + } + } + try { //write local collections to local storage window.localStorage.setItem(this.storageKey, DatabusCollectionUtils.serialize(this.local)); @@ -426,7 +517,7 @@ class DatabusCollectionManager { discardLocalChanges() { - if (!this.isInitialized) throw "Databus-Collection-Manager is not initialized."; + if (!this.hasAccountName) throw "Databus-Collection-Manager is not initialized."; let uuid = this.activeCollection.uuid; @@ -469,8 +560,8 @@ class DatabusCollectionManager { - createNew(title, description, callback) { - if (!this.isInitialized) throw "Databus-Collection-Manager is not initialized."; + createNew(accountName, title, description, callback) { + if (!this.hasAccountName) throw "Databus-Collection-Manager is not initialized."; let reg = /^\w+[\w\s]*$/; @@ -479,17 +570,17 @@ class DatabusCollectionManager { return; } - let collection = DatabusCollectionWrapper.createNew(title, description, DATABUS_RESOURCE_BASE_URL); + let collection = DatabusCollectionWrapper.createNew(title, description, DATABUS_RESOURCE_BASE_URL, accountName); this.local[collection.uuid] = new DatabusCollectionWrapper(collection); this.setActive(collection.uuid); this.saveLocally(); - callback(true); + callback(collection); } createDraft(callback) { - if (!this.isInitialized) { + if (!this.hasAccountName) { return; } @@ -502,13 +593,14 @@ class DatabusCollectionManager { } createCopy(source) { - if (!this.isInitialized) throw "Databus-Collection-Manager is not initialized."; + if (!this.hasAccountName) throw "Databus-Collection-Manager is not initialized."; let collection = DatabusCollectionWrapper.createNew(); collection.content = DatabusCollectionUtils.createCleanCopy(source.content); collection.title = `Copy of ${source.title}`; collection.abstract = source.abstract; collection.description = source.description; + collection.accountName = this.accountName; this.local[collection.uuid] = new DatabusCollectionWrapper(collection); this.saveLocally(); @@ -555,7 +647,7 @@ class DatabusCollectionManager { async changeCollection(username, collectionUri) { try { - if (!this.isInitialized) throw "Databus-Collection-Manager is not initialized."; + if (!this.hasAccountName) throw "Databus-Collection-Manager is not initialized."; this.saveLocally(); @@ -600,8 +692,10 @@ class DatabusCollectionManager { try { - var relativeUri = new URL(collectionUri).pathname; - response = await this.http.put(relativeUri, collectionJsonLd); + // var relativeUri = new URL(collectionUri).pathname; + // response = await this.http.put(relativeUri, collectionJsonLd); + + response = await this.http.post('/api/register', collectionJsonLd); } catch (errResponse) { console.log(errResponse); @@ -639,6 +733,7 @@ class DatabusCollectionManager { this.local[pushIdentifier].hasLocalChanges = false; this.local[pushIdentifier].modified = remoteGraph.modified; this.local[pushIdentifier].issued = remoteGraph.issued; + this.local[pushIdentifier].isHidden = remoteGraph.issued == null; // this.local[pushIdentifier].created = remoteGraph.created; //Update remote data @@ -688,7 +783,7 @@ class DatabusCollectionManager { */ async fetchCollection(uri) { try { - if (!this.isInitialized) throw "Databus-Collection-Manager is not initialized."; + if (!this.hasAccountName) throw "Databus-Collection-Manager is not initialized."; var req = { method: 'GET', @@ -713,7 +808,7 @@ class DatabusCollectionManager { async deleteCollection(username, collectionTag) { try { - if (!this.isInitialized) throw "Databus-Collection-Manager is not initialized."; + if (!this.hasAccountName) throw "Databus-Collection-Manager is not initialized."; @@ -745,7 +840,7 @@ class DatabusCollectionManager { */ async unpublishActiveCollection() { - if (!this.isInitialized) throw "Databus-Collection-Manager is not initialized."; + if (!this.hasAccountName) throw "Databus-Collection-Manager is not initialized."; if (this.activeCollection.isDraft) { throw "Cannot unpublish an unpublished draft"; diff --git a/public/js/collections/databus-collection-wrapper.js b/public/js/collections/databus-collection-wrapper.js index c6e2f9c8..3d2d2990 100644 --- a/public/js/collections/databus-collection-wrapper.js +++ b/public/js/collections/databus-collection-wrapper.js @@ -70,11 +70,12 @@ class DatabusCollectionWrapper { } - static createNew(title, description, source) { + static createNew(title, description, source, accountName) { var data = {}; data.uuid = DatabusUtils.uuidv4(); data.title = title; data.description = description; + data.accountName = accountName; data.abstract = description; data.content = {}; data.content.root = new QueryNode(null, null); diff --git a/public/js/components/collection-hierarchy-two/collection-hierarchy.html b/public/js/components/collection-hierarchy-two/collection-hierarchy.html index 5e57ad52..e72fe994 100644 --- a/public/js/components/collection-hierarchy-two/collection-hierarchy.html +++ b/public/js/components/collection-hierarchy-two/collection-hierarchy.html @@ -333,7 +333,7 @@ + uri="result.id[0].value" absolute="true" desc="result.comment[0].highlight"> @@ -556,7 +556,7 @@ + uri="result.id[0].value" absolute="true" desc="result.comment[0].highlight"> diff --git a/public/js/components/collection-hierarchy-two/collection-hierarchy.js b/public/js/components/collection-hierarchy-two/collection-hierarchy.js index 758ed09b..755dc641 100644 --- a/public/js/components/collection-hierarchy-two/collection-hierarchy.js +++ b/public/js/components/collection-hierarchy-two/collection-hierarchy.js @@ -190,18 +190,18 @@ SELECT ?file WHERE { ctrl.addToCollection = function (source, view, result) { if (ctrl.isInCollection(result)) { - QueryNode.removeChildByUri(ctrl.root, result.resource[0].value); + QueryNode.removeChildByUri(ctrl.root, result.id[0].value); } else { if (result.typeName[0].value == 'Group') { - let node = new QueryNode(result.resource[0].value, DATAID_GROUP_PROPERTY); + let node = new QueryNode(result.id[0].value, DATAID_GROUP_PROPERTY); source.childNodes.push(node); } if (result.typeName[0].value == 'Artifact') { - var artifactUri = result.resource[0].value; + var artifactUri = result.id[0].value; let groupUri = DatabusUtils.navigateUp(artifactUri); let groupNode = QueryNode.findChildByUri(ctrl.root, groupUri); @@ -295,7 +295,7 @@ SELECT ?file WHERE { } ctrl.isInCollection = function (result) { - let uri = result.resource[0].value; + let uri = result.id[0].value; let node = QueryNode.findChildByUri(ctrl.root, uri); return node != null; } diff --git a/public/js/components/collection-search/collection-search.html b/public/js/components/collection-search/collection-search.html index 23b5a89c..dcdfb1f6 100644 --- a/public/js/components/collection-search/collection-search.html +++ b/public/js/components/collection-search/collection-search.html @@ -42,7 +42,7 @@
+ uri="result.id[0].value" desc="result.comment[0].highlight">
diff --git a/public/js/components/collection-search/collection-search.js b/public/js/components/collection-search/collection-search.js index 980a611b..ccffc3d2 100644 --- a/public/js/components/collection-search/collection-search.js +++ b/public/js/components/collection-search/collection-search.js @@ -48,7 +48,7 @@ function CollectionSearchController(collectionManager, $http, $interval, $sce) { // TODO Fabian ctrl.isInCollection = function (result) { - let uri = result.resource[0].value; + let uri = result.id[0].value; let node = QueryNode.findChildByUri(ctrl.root, uri); return node != null; @@ -61,11 +61,11 @@ function CollectionSearchController(collectionManager, $http, $interval, $sce) { var sourceNode = QueryNode.findChildByUri(ctrl.root, currentSource); if (result.inCollection) { - QueryNode.removeChildByUri(ctrl.root, result.resource[0].value); + QueryNode.removeChildByUri(ctrl.root, result.id[0].value); } else { if (result.typeName[0].value == 'Group') { - let node = new QueryNode(result.resource[0].value, 'databus:group'); + let node = new QueryNode(result.id[0].value, 'databus:group'); sourceNode.addChild(node); ctrl.onComponentAdded(); @@ -73,7 +73,7 @@ function CollectionSearchController(collectionManager, $http, $interval, $sce) { if (result.typeName[0].value == 'Artifact') { - var artifactUri = result.resource[0].value; + var artifactUri = result.id[0].value; let groupUri = DatabusUtils.navigateUp(artifactUri); let groupNode = QueryNode.findChildByUri(ctrl.root, groupUri); diff --git a/public/js/components/databus-icon/databus-icon.html b/public/js/components/databus-icon/databus-icon.html index 36b70c66..1e6f6859 100644 --- a/public/js/components/databus-icon/databus-icon.html +++ b/public/js/components/databus-icon/databus-icon.html @@ -1,5 +1,6 @@
+
+ +
+
+
+
+
This won't work!
+
+ Fix all errors displayed in the form to create a valid API input +
+
+ +
+
+ +
+
{{ $ctrl.entity.postBody | json }}
+
+
+ + + +
+ + +
+ +
+
+ +
+ + + + + + +
{{ + entry.msg }}
+
{{ entry.msg }}
+ + +
+
+
{{ entry.payload | json }}
+
+
+
+
+
+ + +
+ + + + Select an API key. No API key yet? Create one here. +
+ +
+
This won't work!
+ +
+ Fix all errors displayed in the form to create a valid API input +
+ +
+ +
+
+ +
+
{{ $ctrl.entity.curlCommand }}
+
+ + + +
+
+ + + +
\ No newline at end of file diff --git a/public/js/components/entity-api-view/entity-api-view.js b/public/js/components/entity-api-view/entity-api-view.js new file mode 100644 index 00000000..67e29fa3 --- /dev/null +++ b/public/js/components/entity-api-view/entity-api-view.js @@ -0,0 +1,36 @@ +function EntityApiViewController() { + const ctrl = this; + + ctrl.copyToClipboard = function (text) { + navigator.clipboard.writeText(text).then(() => { + console.log("Copied to clipboard"); + }); + }; + + ctrl.register = async function () { + ctrl.isRegistering = true; + ctrl.isSuccess = false; + ctrl.isError = false; + + if (ctrl.entity && ctrl.entity.register) { + try { + let response = await ctrl.entity.register(); + ctrl.log = response.data.log; + ctrl.isSuccess = true; + } catch(err) { + ctrl.log = err.data.log; + ctrl.isError = true; + } + } + + ctrl.isRegistering = false; + }; + + ctrl.setApiKeyName = function (name) { + if (ctrl.entity && ctrl.entity.setApiKeyName) { + ctrl.entity.setApiKeyName(name); + } + }; +} + +module.exports = EntityApiViewController; diff --git a/public/js/components/entity-card/entity-card.html b/public/js/components/entity-card/entity-card.html index e07ecef4..0c04e85b 100644 --- a/public/js/components/entity-card/entity-card.html +++ b/public/js/components/entity-card/entity-card.html @@ -1,10 +1,10 @@ -
+
-

+

diff --git a/public/js/components/entity-dropdown/entity-dropdown.html b/public/js/components/entity-dropdown/entity-dropdown.html new file mode 100644 index 00000000..1a424987 --- /dev/null +++ b/public/js/components/entity-dropdown/entity-dropdown.html @@ -0,0 +1,55 @@ + diff --git a/public/js/components/entity-dropdown/entity-dropdown.js b/public/js/components/entity-dropdown/entity-dropdown.js new file mode 100644 index 00000000..37bd0332 --- /dev/null +++ b/public/js/components/entity-dropdown/entity-dropdown.js @@ -0,0 +1,74 @@ +function EntityDropdownController() { + var ctrl = this; + + ctrl.showDrop = false; + ctrl.selectedLabel = ''; + ctrl.searchQuery = ''; + ctrl.filteredItems = []; + + ctrl.$onInit = function () { + ctrl.updateFilteredItems(); + ctrl.setSelectedLabel(); + }; + + ctrl.$onChanges = function (changes) { + if (changes.items || changes.selected) { + ctrl.updateFilteredItems(); + + if (ctrl.selected && Array.isArray(ctrl.items) && !ctrl.items?.some(i => i[ctrl.displayProperty] === ctrl.selected)) { + ctrl.selected = null; + ctrl.onSelect({ item: null }); + } + + ctrl.setSelectedLabel(); + } + }; + + ctrl.toggleDropdown = function () { + if (!ctrl.loading && ctrl.items && ctrl.items.length > 0) { + ctrl.showDrop = !ctrl.showDrop; + ctrl.searchQuery = ''; + ctrl.updateFilteredItems(); + } + }; + + ctrl.selectItem = function (item) { + ctrl.selectedLabel = item[ctrl.displayProperty]; + ctrl.showDrop = false; + ctrl.onSelect({ item: item }); + }; + + ctrl.updateFilteredItems = function () { + if (!ctrl.items || !ctrl.displayProperty) { + ctrl.filteredItems = []; + return; + } + + ctrl.filteredItems = ctrl.items.filter(function (item) { + var val = item[ctrl.displayProperty] || ''; + return val.toLowerCase().indexOf(ctrl.searchQuery.toLowerCase()) !== -1; + }); + }; + + ctrl.setSelectedLabel = function () { + if (!ctrl.selected || !ctrl.displayProperty) { + ctrl.selectedLabel = ctrl.placeholder || 'Please select...'; + return; + } + + // Attempt to match selected value in the list + var match = (ctrl.items || []).find(function (item) { + return item[ctrl.displayProperty] === ctrl.selected; + }); + + if (match) { + ctrl.selectedLabel = match[ctrl.displayProperty]; + } else { + // fallback in case selected value is not in the list + ctrl.selectedLabel = ctrl.placeholder || 'Please select...'; + // ctrl.onSelect(null); + } + }; +} + +module.exports = EntityDropdownController; diff --git a/public/js/components/error-notification/error-notifcation.js b/public/js/components/error-notification/error-notifcation.js new file mode 100644 index 00000000..ec2bf782 --- /dev/null +++ b/public/js/components/error-notification/error-notifcation.js @@ -0,0 +1,16 @@ +const { DatabusMsg } = require("../../utils/messages"); + +function ErrorNotificationController() { + var ctrl = this; + ctrl.expanded = false; + + ctrl.toggleExpand = function () { + ctrl.expanded = !ctrl.expanded; + }; + + ctrl.get = function(key) { + return DatabusMsg.get(key); + } +} + +module.exports = ErrorNotificationController; diff --git a/public/js/components/error-notification/error-notification.html b/public/js/components/error-notification/error-notification.html new file mode 100644 index 00000000..ddc2e376 --- /dev/null +++ b/public/js/components/error-notification/error-notification.html @@ -0,0 +1,2 @@ +
{{ + $ctrl.get($ctrl.key) }}
diff --git a/public/js/components/facets-view/facets-view.js b/public/js/components/facets-view/facets-view.js index 38eefc00..318046ad 100644 --- a/public/js/components/facets-view/facets-view.js +++ b/public/js/components/facets-view/facets-view.js @@ -119,6 +119,8 @@ function FacetsViewController($http, $scope) { // wrap the node in the query node class ctrl.node = QueryNode.createFrom(ctrl.node); + + // Holds the view state as json ctrl.viewModel = {}; if (ctrl.facets == undefined) { @@ -136,12 +138,14 @@ function FacetsViewController($http, $scope) { }).then(function (result) { // Facets data has been loaded - // Fix artifact facet values for groups - if (ctrl.resourceType == 'group' && result.data[DatabusUris.DATABUS_ARTIFACT_PROPERTY] != undefined) { - for (var i in result.data[DatabusUris.DATABUS_ARTIFACT_PROPERTY].values) { - var value = result.data[DatabusUris.DATABUS_ARTIFACT_PROPERTY].values[i]; - result.data[DatabusUris.DATABUS_ARTIFACT_PROPERTY].values[i] - = DatabusUtils.uriToName(value); + ctrl.facetsData = result.data; + + // Fix artifact facet values for groups, change URIs into artifact names + var artifactFacetData = ctrl.facetsData[DatabusUris.DATABUS_ARTIFACT_PROPERTY]; + + if (artifactFacetData != null) { + for (var i in artifactFacetData.values) { + artifactFacetData.values[i] = DatabusUtils.uriToName(artifactFacetData.values[i]); } } @@ -153,9 +157,9 @@ function FacetsViewController($http, $scope) { // Prepare visible facet settings and autofill data based on the facet data returned by the API // Create key base entries (unset, not overriden) - for (var key in result.data) { + for (var key in ctrl.facetsData) { - var facetData = result.data[key]; + var facetData = ctrl.facetsData[key]; // Create a view data object for each facet ctrl.viewModel[key] = {}; @@ -169,9 +173,7 @@ function FacetsViewController($http, $scope) { for (var v in facetData.values) { - var value = facetData.values[v]; - ctrl.viewModel[key].visibleFacetSettings.push({ value: value, checked: false, @@ -179,7 +181,7 @@ function FacetsViewController($http, $scope) { }); } - ctrl.viewModel[key].visibleFacetSettings.sort(function(a, b) { + ctrl.viewModel[key].visibleFacetSettings.sort(function (a, b) { const valueA = a.value.toUpperCase(); const valueB = b.value.toUpperCase(); if (valueA > valueB) { @@ -193,8 +195,8 @@ function FacetsViewController($http, $scope) { }); // Show latest versions first - if(key == DatabusUris.DCT_HAS_VERSION) { - ctrl.viewModel[key].visibleFacetSettings.reverse(); + if (key == DatabusUris.DCT_HAS_VERSION) { + ctrl.viewModel[key].visibleFacetSettings.reverse(); } // Only show the top few @@ -235,6 +237,7 @@ function FacetsViewController($http, $scope) { // If we're a group node, check for artifact nodes and add them as facets if (ctrl.resourceType == 'group') { + for (var i in ctrl.node.childNodes) { var artifactNode = ctrl.node.childNodes[i]; var facetValue = DatabusUtils.uriToName(artifactNode.uri) @@ -246,12 +249,28 @@ function FacetsViewController($http, $scope) { if (ctrl.node.childNodes.length == 0) { + + ctrl.updateArtifactFilters(ctrl.node); + + var artifactFacetData = ctrl.facetsData[DatabusUris.DATABUS_ARTIFACT_PROPERTY]; + + if (artifactFacetData != null) { + + // Add artifact nodes + for (var i in artifactFacetData.values) { + artifactFacetData.values[i] = DatabusUtils.uriToName(artifactFacetData.values[i]); + } + } + + /* // Add artifact nodes per default for (var v of ctrl.viewModel[DatabusUris.DATABUS_ARTIFACT_PROPERTY].visibleFacetSettings) { var childUri = ctrl.node.uri + '/' + v.value; var artifactNode = new QueryNode(childUri, 'databus:artifact'); QueryNode.addChild(ctrl.node, artifactNode); - } + }*/ + + } } @@ -263,6 +282,57 @@ function FacetsViewController($http, $scope) { }); } + ctrl.updateArtifactFilters = function (groupNode) { + + // Clear all child nodes + groupNode.childNodes.length = 0; + + var hasCheckedArtifactFacets = false; + + for (var setting of ctrl.viewModel[DatabusUris.DATABUS_ARTIFACT_PROPERTY].visibleFacetSettings) { + hasCheckedArtifactFacets = hasCheckedArtifactFacets || setting.checked; + } + + if (hasCheckedArtifactFacets) { + + for (var setting of ctrl.viewModel[DatabusUris.DATABUS_ARTIFACT_PROPERTY].visibleFacetSettings) { + if (setting.checked) { + var artifactUri = `${groupNode.uri}/${setting.value}`; + if (QueryNode.findChildByUri(groupNode, artifactUri) == null) { + var artifactNode = new QueryNode(artifactUri, 'databus:artifact'); + QueryNode.addChild(groupNode, artifactNode); + } + } + } + + } else { + + var latestVersionSetting = QueryNode.findFacetSetting(groupNode, + DatabusUris.DCT_HAS_VERSION, + DatabusConstants.FACET_LATEST_VERSION_VALUE); + + if (latestVersionSetting != undefined && latestVersionSetting.checked) { + + var artifactFacetData = ctrl.facetsData[DatabusUris.DATABUS_ARTIFACT_PROPERTY]; + + if (artifactFacetData != null) { + + // Add artifact nodes + for (var value of artifactFacetData.values) { + var artifactUri = `${groupNode.uri}/${value}`; + if (QueryNode.findChildByUri(groupNode, artifactUri) == null) { + var artifactNode = new QueryNode(artifactUri, 'databus:artifact'); + QueryNode.addChild(groupNode, artifactNode); + } + } + + } + } + } + + } + + ctrl.getFacetLabel = function (value) { if (value == DatabusConstants.FACET_LATEST_VERSION_VALUE) { return DatabusConstants.FACET_LATEST_VERSION_LABEL; @@ -281,21 +351,15 @@ function FacetsViewController($http, $scope) { if (ctrl.resourceType == 'group' && key == DatabusUris.DATABUS_ARTIFACT_PROPERTY) { - var childUri = ctrl.node.uri + '/' + value; - - if (targetState) { - var artifactNode = new QueryNode(childUri, 'databus:artifact'); - QueryNode.addChild(ctrl.node, artifactNode); - } else { - QueryNode.removeChildByUri(ctrl.node, childUri); - } - var visibleSetting = ctrl.getOrCreateVisibleFacetSetting(key, value); if (visibleSetting != null) { visibleSetting.checked = targetState; visibleSetting.isOverride = targetState; } + + ctrl.updateArtifactFilters(ctrl.node); + } else { // apply change to view model diff --git a/public/js/components/file-browser/file-browser.html b/public/js/components/file-browser/file-browser.html index 7c19b67a..0ab3bd01 100644 --- a/public/js/components/file-browser/file-browser.html +++ b/public/js/components/file-browser/file-browser.html @@ -34,7 +34,7 @@
{{ $ctrl.numFiles }} file(s) / {{ $ctrl.totalSize }}
- +
diff --git a/public/js/components/nav-search/nav-search-controller.js b/public/js/components/nav-search/nav-search-controller.js new file mode 100644 index 00000000..afd3e42f --- /dev/null +++ b/public/js/components/nav-search/nav-search-controller.js @@ -0,0 +1,150 @@ + + +// hinzufügen eines Controllers zum Modul +function NavSearchController($http, $interval, $sce, searchManager) { + + var ctrl = this; + + // TODO: get search extensions from the logged in user + + ctrl.searchManager = searchManager; + ctrl.results = []; + + ctrl.formatResult = function (result) { + return $sce.trustAsHtml(result); + } + + ctrl.toggleFilter = function (key) { + ctrl.filterActive[key] = !ctrl.filterActive[key]; + ctrl.search(); + } + + ctrl.navigateTo = function(uri) { + window.location = uri; + } + + ctrl.hideDropdown = function() { + + } + + ctrl.availableResourceTypes = ['Collection', 'Artifact', 'Group', 'Account', 'Version' ]; + + ctrl.$onInit = function () { + + ctrl.searchInput = ''; + ctrl.isSearching = false; + ctrl.searchCooldown = 1000; + + + ctrl.filterActive = {}; + ctrl.filterVisible = {}; + + + if (ctrl.settings == undefined) { + ctrl.minRelevance = 0.01; + ctrl.maxResults = 50; + ctrl.searchFilter = ""; + ctrl.resourceTypes = null; + ctrl.placeholder = "Search the Databus..." + } else { + ctrl.minRelevance = ctrl.settings.minRelevance; + ctrl.maxResults = ctrl.settings.maxResults; + ctrl.searchFilter = ctrl.settings.filter; + ctrl.resourceTypes = ctrl.settings.resourceTypes; + ctrl.placeholder = ctrl.settings.placeholder; + } + + for (var resourceType of ctrl.availableResourceTypes) { + ctrl.filterActive[resourceType] = false; + ctrl.filterVisible[resourceType] = ctrl.resourceTypes == null; + } + + ctrl.numFilters = 0; + + if (ctrl.resourceTypes != null) { + for (var resourceType of ctrl.resourceTypes) { + ctrl.filterVisible[resourceType] = true; + ctrl.numFilters++; + } + } + } + + ctrl.isAnyFilterActive = function () { + + for (var resourceType of ctrl.availableResourceTypes) { + + if (!ctrl.filterVisible[resourceType]) { + continue; + } + + if (ctrl.filterActive[resourceType]) { + return true; + } + } + + return false; + } + + ctrl.baseQueryFormatter = function(query) { + return `?query=${query}${ctrl.searchFilter}${ctrl.baseFilters}${ctrl.typeFilters}` + } + + $interval(function () { + + if (ctrl.searchChanged) { + + var baseFilters = `&minRelevance=${ctrl.minRelevance}&maxResults=${ctrl.maxResults}`; + var typeFilters = ``; + var isAnyFilterActive = ctrl.isAnyFilterActive(); + + + for (var resourceType of ctrl.availableResourceTypes) { + + if (!ctrl.filterVisible[resourceType]) { + continue; + } + + if (ctrl.filterActive[resourceType] || !isAnyFilterActive) { + + if (typeFilters == ``) { + typeFilters = `&typeName=`; + } + + typeFilters += ` ${resourceType}`; + } + } + + ctrl.baseFilters = baseFilters; + ctrl.typeFilters = typeFilters; + ctrl.searchManager.baseAdapter.queryFormatter = ctrl.baseQueryFormatter; + + ctrl.searchManager.search(ctrl.searchInput).then(function success(results) { + + for(var result of results) { + + if(result.abstract != null) { + result.abstract = result.abstract[result.abstract.length - 1]; + } + } + + ctrl.results = results; + + + ctrl.isSearching = false; + }, function error(response) { + ctrl.isSearching = false; + }); + + ctrl.searchChanged = false; + }; + }, ctrl.searchCooldown); + + ctrl.search = function () { + ctrl.isSearching = true; + ctrl.searchChanged = true; + }; + +}; + +module.exports = NavSearchController; + diff --git a/public/js/components/nav-search/nav-search.html b/public/js/components/nav-search/nav-search.html new file mode 100644 index 00000000..8350d3f5 --- /dev/null +++ b/public/js/components/nav-search/nav-search.html @@ -0,0 +1,80 @@ +
+ +
+ +
+ +
+
+ + + + + +
+ + + +
+ +
+ + +
+ + +
+ +
Results ({{ $ctrl.results.length }}):
+ +
+ + +

Filters:

+ +
+ + +
+ +
+ +
+ +
+
+
+
+
+
+
+
+ +
+ +
Your search did not match any + resource.
+ + +
+ +
+
+

+
+ +
+ +
+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/public/js/components/search/search-controller.js b/public/js/components/search/search-controller.js index 6cfd5c58..163d21d1 100644 --- a/public/js/components/search/search-controller.js +++ b/public/js/components/search/search-controller.js @@ -19,7 +19,7 @@ function SearchController($http, $interval, $sce, searchManager) { ctrl.search(); } - ctrl.availableResourceTypes = ['Collection', 'Artifact', 'Group', 'PersonalProfileDocument', 'Version' ]; + ctrl.availableResourceTypes = ['Collection', 'Artifact', 'Group', 'Account', 'Version' ]; ctrl.$onInit = function () { diff --git a/public/js/components/search/search.html b/public/js/components/search/search.html index 6cb00b7e..553d3393 100644 --- a/public/js/components/search/search.html +++ b/public/js/components/search/search.html @@ -38,7 +38,7 @@ resource.
- diff --git a/public/js/components/table-editor/table-editor.html b/public/js/components/table-editor/table-editor.html index 905bd309..fd7ad440 100644 --- a/public/js/components/table-editor/table-editor.html +++ b/public/js/components/table-editor/table-editor.html @@ -85,52 +85,143 @@ .edit-table .edit:focus { outline: none; } + + + .flex-table { + border: 1px solid #dbdbdb; + overflow-x: scroll; + } + + .flex-table .row { + display: flex; + min-width: max-content; + border-bottom: 1px solid #dbdbdb; + } + + .flex-table .row.header { + background-color: #f6f6f6; + font-weight: 600; + } + + .flex-table .cell { + min-width: 140px; + flex: 1; + height: 48px; + display: flex; + align-items: center; + padding-left: 1em; + } + + .flex-table .cell.tiny { + min-width: 48px; + width: 48px; + flex: 0; + } + + .flex-table .cell.main { + flex: 1.5; + min-width: 215px; + max-width: 215px; + } + + .flex-table .cell.readonly { + background-color: #f6f6f6; + } + + .flex-table .cell.edit { + cursor: text; + border: none; + background-color: inherit; + font-family: inherit; + outline: none; + margin: 0; + width: 0; + } + + .flex-table .cell.edit { + cursor: text; + border: none; + background-color: inherit; + font-family: inherit; + outline: none; + margin: 0; + width: 0; + } + + .flex-table .cell:focus { + background-color: #e2f5ff; + } + + .flex-table .cell:not(:first-child) { + border-left: 1px solid #dbdbdb; + } + + .flex-table .cell input { + min-width: 100%; + width: 100px; + } + + .flex-table .cell.cv:nth-child(3) { + min-width: 120px; + } + + .flex-table .cell.cv:nth-child(4) { + min-width: 170px; + } + +
+
+
+
File
+
+ {{ cv.label }} + +
+
+
+
{{ $index + 1 }}
+ + + + +
+ +
+ + +
-
Files
-
[GROUP]
+
File
- Format - Compression - {{ cv.label }} - Status/Actions + {{ cv.label }} - {{ $index + 1 }} {{ file.name }} - - {{ file.formatExtension }} - {{ file.compression }} - - - - - +
@@ -146,10 +237,23 @@
- +
+ + + + + + + @@ -167,8 +271,8 @@ -
+-->
\ No newline at end of file diff --git a/public/js/components/table-editor/table-editor.js b/public/js/components/table-editor/table-editor.js index 6b352be4..2d9d3c99 100644 --- a/public/js/components/table-editor/table-editor.js +++ b/public/js/components/table-editor/table-editor.js @@ -29,12 +29,15 @@ function TableEditorController() { return width + 22; } + ctrl.editContentVariant = function(index) { + + ctrl.onEditContentVariant({ index: index}); + } + ctrl.setupColumns = function() { ctrl.columns = []; ctrl.columns.push({ title:'File', width: 400, isReadonly : true }); - ctrl.columns.push({ title:'Format', width: 75, isReadonly : true }); - ctrl.columns.push({ title:'Compression', width: 115, isReadonly : true }); for(var c in ctrl.model.contentVariants) { var cv = ctrl.model.contentVariants[c]; @@ -65,18 +68,12 @@ function TableEditorController() { ctrl.selectCell = function($event, x, y) { - if(ctrl.selection.x == x && ctrl.selection.y == y) { - ctrl.edit.x = x; - ctrl.edit.y = y; - - // $event.target.setSelectionRange(0, $event.target.value.length) - // $event.target.selectionStart = 0; - // $event.target.selectionEnd = $event.target.value.length; - return; - } - + ctrl.edit.x = undefined; ctrl.edit.y = undefined; + + ctrl.edit.x = x; + ctrl.edit.y = y; ctrl.selection.x = x; ctrl.selection.y = y; ctrl.selection.width = 1; @@ -93,19 +90,19 @@ function TableEditorController() { var index = ctrl.model.files.findIndex(f => f.uri == file.uri); for(var i = index + 1; i < index + file.rowspan; i++) { - ctrl.model.files[i].contentVariants[cv.label] = file.contentVariants[cv.label]; + ctrl.model.files[i].contentVariants[cv.id] = file.contentVariants[cv.id]; } - ctrl.model.isConfigDirty = true + ctrl.model.onChange(); } ctrl.updateViewModel = function() { - for(var f in ctrl.model.files) { ctrl.model.files[f].rowspan = 1; } + /* if(ctrl.model.groupMode) { var i = 0; @@ -115,12 +112,12 @@ function TableEditorController() { if(ctrl.model.files[i].name == ctrl.model.files[i + step].name) { // Swallow the cv setting of the next row - ctrl.model.files[i].rowspan++; - ctrl.model.files[i + step].rowspan = 0; + // ctrl.model.files[i].rowspan++; + // ctrl.model.files[i + step].rowspan = 0; for(var c in ctrl.model.contentVariants) { var cv = ctrl.model.contentVariants[c]; - ctrl.model.files[i + step].contentVariants[cv.label] = ctrl.model.files[i].contentVariants[cv.label]; + ctrl.model.files[i + step].contentVariants[cv.id] = ctrl.model.files[i].contentVariants[cv.id]; } step++; @@ -129,7 +126,7 @@ function TableEditorController() { step = 1; } } - } + }*/ } @@ -138,8 +135,8 @@ function TableEditorController() { * @param {*} artifact * @param {*} file */ - ctrl.removeFileFromArtifact = function(file) { - ctrl.onRemoveFile({ file : file }); + ctrl.removeFileFromArtifact = function(file, index) { + ctrl.onRemoveFile({ file : file, index: index}); } ctrl.$doCheck = function() { diff --git a/public/js/components/type-tag/type-tag.js b/public/js/components/type-tag/type-tag.js index 94c849a0..ec3142af 100644 --- a/public/js/components/type-tag/type-tag.js +++ b/public/js/components/type-tag/type-tag.js @@ -8,7 +8,7 @@ function TypeTagController() { ctrl.typeMap["Version"] = "is-version"; ctrl.typeMap["Group"] = "is-group"; ctrl.typeMap["Service"] = "is-service"; - ctrl.typeMap["PersonalProfileDocument"] = "is-consumer"; + ctrl.typeMap["Account"] = "is-consumer"; ctrl.typeMap["Collection"] = "is-collection"; ctrl.typeMap["BlogEntry"] = "is-blog"; ctrl.typeMap["Databus"] = "is-version"; @@ -19,7 +19,7 @@ function TypeTagController() { ctrl.iconMap["Version"] = "M 14.9 1 L 12.293 1.005 L 16.507 7.18 L 16.5 23.1 L 18.5 23.1 L 18.5 6.4 L 14.9 1 Z M 10.4 1 L 1.581 1.004 L 1.584 23.13 L 15 23.1 L 15 7.7 L 10.4 1 Z M 16.8 1 L 20 5.8 L 20 23.1 L 22 23.1 L 22 4.9 L 19.3 1 L 16.8 1 Z"; ctrl.iconMap["Group"] = "M21.698 10.658l2.302 1.342-12.002 7-11.998-7 2.301-1.342 9.697 5.658 9.7-5.658zm-9.7 10.657l-9.697-5.658-2.301 1.343 11.998 7 12.002-7-2.302-1.342-9.7 5.657zm12.002-14.315l-12.002-7-11.998 7 11.998 7 12.002-7z"; ctrl.iconMap["Service"] = "M24 13.616v-3.232l-2.869-1.02c-.198-.687-.472-1.342-.811-1.955l1.308-2.751-2.285-2.285-2.751 1.307c-.613-.339-1.269-.613-1.955-.811l-1.021-2.869h-3.232l-1.021 2.869c-.686.198-1.342.471-1.955.811l-2.751-1.308-2.285 2.285 1.308 2.752c-.339.613-.614 1.268-.811 1.955l-2.869 1.02v3.232l2.869 1.02c.197.687.472 1.342.811 1.955l-1.308 2.751 2.285 2.286 2.751-1.308c.613.339 1.269.613 1.955.811l1.021 2.869h3.232l1.021-2.869c.687-.198 1.342-.472 1.955-.811l2.751 1.308 2.285-2.286-1.308-2.751c.339-.613.613-1.268.811-1.955l2.869-1.02zm-12 2.384c-2.209 0-4-1.791-4-4s1.791-4 4-4 4 1.791 4 4-1.791 4-4 4z"; - ctrl.iconMap["PersonalProfileDocument"] = "M19 7.001c0 3.865-3.134 7-7 7s-7-3.135-7-7c0-3.867 3.134-7.001 7-7.001s7 3.134 7 7.001zm-1.598 7.18c-1.506 1.137-3.374 1.82-5.402 1.82-2.03 0-3.899-.685-5.407-1.822-4.072 1.793-6.593 7.376-6.593 9.821h24c0-2.423-2.6-8.006-6.598-9.819z"; + ctrl.iconMap["Account"] = "M19 7.001c0 3.865-3.134 7-7 7s-7-3.135-7-7c0-3.867 3.134-7.001 7-7.001s7 3.134 7 7.001zm-1.598 7.18c-1.506 1.137-3.374 1.82-5.402 1.82-2.03 0-3.899-.685-5.407-1.822-4.072 1.793-6.593 7.376-6.593 9.821h24c0-2.423-2.6-8.006-6.598-9.819z"; ctrl.iconMap["Collection"] = "M11.499 12.03v11.971l-10.5-5.603v-11.835l10.5 5.467zm11.501 6.368l-10.501 5.602v-11.968l10.501-5.404v11.77zm-16.889-15.186l10.609 5.524-4.719 2.428-10.473-5.453 4.583-2.499zm16.362 2.563l-4.664 2.4-10.641-5.54 4.831-2.635 10.474 5.775z"; ctrl.iconMap["BlogEntry"] = "M21 9.662c-2.287.194-5.197 1.038-7 1.794v-1.064c1.933-.721 4.598-1.54 7-1.745v1.015zm0 2.031c-2.287.194-5.197 1.038-7 1.794v-1.064c1.933-.721 4.598-1.54 7-1.745v1.015zm0 2.031c-2.287.194-5.197 1.038-7 1.794v-1.064c1.933-.721 4.598-1.54 7-1.745v1.015zm0 2.031c-2.287.194-5.197 1.038-7 1.794v-1.064c1.933-.721 4.598-1.54 7-1.745v1.015zm0-9.951c-2.402.204-5.068 1.024-7 1.745v1.933c1.804-.756 4.713-1.6 7-1.794v-1.884zm-18 2.843c2.402.205 5.067 1.024 7 1.745v1.064c-1.803-.756-4.713-1.6-7-1.794v-1.015zm0 2.031c2.402.205 5.067 1.024 7 1.745v1.064c-1.803-.756-4.713-1.6-7-1.794v-1.015zm0 2.031c2.402.205 5.067 1.024 7 1.745v1.064c-1.803-.756-4.713-1.6-7-1.794v-1.015zm0 2.032c2.402.205 5.067 1.024 7 1.745v1.064c-1.803-.756-4.713-1.6-7-1.794v-1.015zm0-7.054c2.287.194 5.196 1.038 7 1.794v-1.933c-1.932-.72-4.598-1.54-7-1.744v1.883zm9-2.724c-3.063-1.671-7.776-2.755-12-2.963v17c4.289.206 8.195 1.249 12 3 3.805-1.751 7.711-2.794 12-3v-17c-4.224.208-8.937 1.292-12 2.963zm-10-.791c4.264.496 6.86 1.467 9 2.545v12.702c-2.968-1.184-5.939-1.95-9-2.271v-12.976zm20 12.975c-3.061.321-6.032 1.088-9 2.271v-12.701c2.187-1.103 4.757-2.051 9-2.544v12.974z"; ctrl.iconMap["Databus"] = "m 0.76949155,0.7702454 v 5.24959 l 29.33129045,-10e-4 6.27262,8.8675006 -0.002,32.0824 4.7212,-0.002 V 0.7702354 Z m 18.43511045,8.3952603 -5.68354,0.006 7.1979,10.5484003 -0.004,27.24663 4.70393,-0.002 0.0167,-28.60108 z m -9.4730904,0.002 -8.96510005,0.002 0.004,37.7960503 16.79563045,-0.004 0.001,-26.29103 z m 13.2512904,0 5.59825,8.2188903 -0.0396,29.57614 4.70307,-0.002 0.006,-31.09587 -4.55858,-6.6940403 z"; diff --git a/public/js/page-controller/account-controller.js b/public/js/page-controller/account-controller.js index 76a012b7..660c842a 100644 --- a/public/js/page-controller/account-controller.js +++ b/public/js/page-controller/account-controller.js @@ -1,3 +1,4 @@ +const DatabusCollectionManager = require("../collections/databus-collection-manager"); const DatabusUtils = require("../utils/databus-utils"); const DatabusWebappUtils = require("../utils/databus-webapp-utils"); const TabNavigation = require("../utils/tab-navigation"); @@ -5,6 +6,15 @@ const TabNavigation = require("../utils/tab-navigation"); var DEFAULT_IMAGE = "https://picsum.photos/id/223/320/320"; // Controller for the header section + +/** + * + * @param {*} $scope + * @param {*} $http + * @param {*} $location + * @param {DatabusCollectionManager} collectionManager + * @returns + */ function AccountPageController($scope, $http, $location, collectionManager) { $scope.collectionManager = collectionManager; @@ -13,15 +23,13 @@ function AccountPageController($scope, $http, $location, collectionManager) { // Pick up the profile data $scope.auth = data.auth; $scope.location = $location; - - $scope.profileData = data.profile; + $scope.account = data.account; // Exit if there is no profile - if ($scope.profileData == undefined) { + if ($scope.account == undefined) { return; } - $scope.profileData.isOwn = $scope.auth.authenticated && $scope.auth.info.accountName == $scope.profileData.accountName; // Create a tab navigation object for the tab navigation with locato $scope.tabNavigation = new TabNavigation($scope, $location, [ @@ -30,32 +38,34 @@ function AccountPageController($scope, $http, $location, collectionManager) { // Make some util functions available in the template $scope.utils = new DatabusWebappUtils($scope); + $scope.accountName = $scope.utils.getAccountName(); + $scope.account.isOwn = $scope.accountName != null; //.auth.authenticated && $scope.auth.info.accountName == $scope.account.accountName; $scope.dataSearchInput = ''; $scope.dataSearchSettings = { minRelevance: 0.01, maxResults: 10, - placeholder: `Search ${$scope.profileData.accountName}'s data...`, + placeholder: `Search ${$scope.account.accountName}'s data...`, resourceTypes: ['Group', 'Artifact'], - filter: `&publisher=${$scope.profileData.accountName}&typeNameWeight=0` + filter: `&publisher=${$scope.account.accountName}&typeNameWeight=0` }; $scope.collectionSearchInput = ''; $scope.collectionSearchSettings = { minRelevance: 0.01, maxResults: 10, - placeholder: `Search ${$scope.profileData.accountName}'s collections...`, + placeholder: `Search ${$scope.account.accountName}'s collections...`, resourceTypes: ['Collection'], - filter: `&publisher=${$scope.profileData.accountName}&publisherWeight=0&typeNameWeight=0` + filter: `&publisher=${$scope.account.accountName}&publisherWeight=0&typeNameWeight=0` }; - + // Wait for additional artifact data to arrive $scope.publishedData = {}; $scope.publishedData.isLoading = true; - $http.get(`/app/account/content?account=${encodeURIComponent($scope.profileData.accountName)}`) + $http.get(`/app/account/content?account=${encodeURIComponent($scope.account.accountName)}`) .then(function (response) { $scope.publishedData.isLoading = false; @@ -95,7 +105,7 @@ function AccountPageController($scope, $http, $location, collectionManager) { $scope.statsData = {}; $scope.statsData.isLoading = true; - $http.get(`/app/account/stats?account=${encodeURIComponent($scope.profileData.accountName)}`).then(function (response) { + $http.get(`/app/account/stats?account=${encodeURIComponent($scope.account.accountName)}`).then(function (response) { $scope.statsData.stats = response.data; $scope.statsData.isLoading = false; }, function (err) { @@ -106,7 +116,7 @@ function AccountPageController($scope, $http, $location, collectionManager) { $scope.activityData = {}; $scope.activityData.isLoading = true; - $http.get(`/app/account/activity?account=${encodeURIComponent($scope.profileData.accountName)}`).then(function (response) { + $http.get(`/app/account/activity?account=${encodeURIComponent($scope.account.accountName)}`).then(function (response) { $scope.activityData.entries = response.data; $scope.activityData.isLoading = false; }, function (err) { @@ -116,8 +126,8 @@ function AccountPageController($scope, $http, $location, collectionManager) { $scope.collectionsData = {}; $scope.collectionsData.isLoading = true; - if (!$scope.profileData.isOwn) { - $http.get(`/app/account/collections?account=${encodeURIComponent($scope.profileData.accountName)}`) + if (!$scope.account.isOwn) { + $http.get(`/app/account/collections?account=${encodeURIComponent($scope.account.accountName)}`) .then(function (response) { $scope.collectionsData.collections = response.data; @@ -126,13 +136,38 @@ function AccountPageController($scope, $http, $location, collectionManager) { }, function (err) { console.log(err); }); + } else { + + function onCollectionManagerInitialized() { + for (let guid in $scope.collectionManager.local) { + let collection = $scope.collectionManager.local[guid]; + + if(collection.accountName == undefined && collection.uri != undefined) { + collection.accountName = DatabusUtils.getFirstSegment(collection.uri); + } + + if (collection.accountName == $scope.accountName) { + $scope.collectionList.push(collection); + } + } + } + + $scope.collectionList = []; + + if(collectionManager.isInitialized) { + onCollectionManagerInitialized(); + } else { + collectionManager.subscribeOnInitialized(onCollectionManagerInitialized); + } } + + $scope.getImageUrl = function () { - if ($scope.profileData.imageUrl == undefined) { + if ($scope.account.imageUrl == undefined) { return DEFAULT_IMAGE; } else { - return $scope.profileData.imageUrl; + return $scope.account.imageUrl; } } @@ -144,13 +179,13 @@ function AccountPageController($scope, $http, $location, collectionManager) { $scope.collectionSearch = {}; $scope.collectionSearch.sortVisible = false; $scope.collectionSearch.sortProperty = 'title'; - $scope.collectionSearch.sortProperties = [ + $scope.collectionSearch.sortProperties = [ { key: 'title', label: 'Title' }, { key: 'issued', label: 'Issued Date' }, ]; $scope.collectionSearch.sortReverse = false; - $scope.collectionSearch.toggleSort = function(value) { - if($scope.collectionSearch.sortProperty == value) { + $scope.collectionSearch.toggleSort = function (value) { + if ($scope.collectionSearch.sortProperty == value) { $scope.collectionSearch.sortReverse = !$scope.collectionSearch.sortReverse; } else { $scope.collectionSearch.sortProperty = value; @@ -163,25 +198,25 @@ function AccountPageController($scope, $http, $location, collectionManager) { */ $scope.onEditCollectionClicked = function (collection) { $scope.collectionManager.setActive(collection.uuid); - window.location.href = '/app/collection-editor'; + window.location.href = `/app/collection-editor?uuid=${collection.uuid}`; } /** * Create new collection */ $scope.createNewCollection = function () { - $scope.collectionManager.createNew('New Collection', 'Replace this description with a description of your choice.', - function (success) { - window.location.href = '/app/collection-editor'; + $scope.collectionManager.createNew($scope.accountName, 'New Collection', 'Replace this description with a description of your choice.', + function (collection) { + window.location.href = `/app/collection-editor?uuid=${collection.uuid}`; }); } /** * Create a copy of the clicked collection */ - $scope.createCopy = function(collection) { - $scope.collectionManager.createCopy(collection); - window.location.href = '/app/collection-editor'; + $scope.createCopy = function (collection) { + let copy = $scope.collectionManager.createCopy(collection); + window.location.href = `/app/collection-editor?uuid=${copy.uuid}`; } @@ -229,11 +264,11 @@ function AccountPageController($scope, $http, $location, collectionManager) { } $scope.refreshFeaturedContent = function () { - if ($scope.profileData.featuredContent == undefined) { + if ($scope.account.featuredContent == undefined) { return; } - var featuredContentUris = $scope.profileData.featuredContent.split('\n'); + var featuredContentUris = $scope.account.featuredContent.split('\n'); $scope.featuredContent = []; for (var f in featuredContentUris) { @@ -245,6 +280,8 @@ function AccountPageController($scope, $http, $location, collectionManager) { } } + /** ACCOUNT MANAGEMENT FOR OWNER */ + } module.exports = AccountPageController; \ No newline at end of file diff --git a/public/js/page-controller/artifact-controller.js b/public/js/page-controller/artifact-controller.js index ce6a8d05..e20bb00c 100644 --- a/public/js/page-controller/artifact-controller.js +++ b/public/js/page-controller/artifact-controller.js @@ -13,7 +13,10 @@ function ArtifactPageController($scope, $http, $sce, $location, collectionManage $scope.collectionManager = collectionManager; $scope.authenticated = data.auth.authenticated; + $scope.auth = data.auth; $scope.utils = new DatabusWebappUtils($scope, $sce); + $scope.accountName = $scope.utils.getAccountName(); + $scope.tabNavigation = new TabNavigation($scope, $location, [ 'files', 'versions', 'edit' ]); @@ -21,7 +24,7 @@ function ArtifactPageController($scope, $http, $sce, $location, collectionManage $scope.versions = data.versions; $scope.artifact = data.artifact; $scope.accountName = DatabusUtils.uriToName(DatabusUtils.navigateUp($scope.artifact.uri, 2)); - $scope.canEdit = $scope.accountName == data.auth.info.accountName; + $scope.canEdit = $scope.accountName != null; $scope.pageTitle = DatabusUtils.stringOrFallback($scope.artifact.title, DatabusUtils.uriToTitle($scope.artifact.uri)); @@ -39,11 +42,12 @@ function ArtifactPageController($scope, $http, $sce, $location, collectionManage $scope.formData.artifact.abstract = $scope.artifact.abstract; $scope.formData.artifact.description = $scope.artifact.description; - $scope.dataidCreator = new DataIdCreator($scope.formData, data.auth.info.accountName); + $scope.dataidCreator = new DataIdCreator($scope.formData, $scope.accountName); } $scope.fileSelector = {}; $scope.fileSelector.config = {}; + $scope.fileSelector.config.authenticated = $scope.authenticated; $scope.fileSelector.config.columns = []; $scope.fileSelector.config.columns.push({ field: 'version', label: 'Version', width: '30%' }); $scope.fileSelector.config.columns.push({ field: 'variant', label: 'Variant', width: '30%' }); @@ -144,8 +148,7 @@ function ArtifactPageController($scope, $http, $sce, $location, collectionManage var artifactUpdate = $scope.dataidCreator.createArtifactUpdate(); - var relativeUri = new URL($scope.artifact.uri).pathname; - var response = await $http.put(relativeUri, artifactUpdate); + var response = await $http.post(`/api/register`, artifactUpdate); if (response.status == 200) { $scope.artifact.title = $scope.formData.artifact.title; diff --git a/public/js/page-controller/collection-controller.js b/public/js/page-controller/collection-controller.js index b0138dda..aad53a2b 100644 --- a/public/js/page-controller/collection-controller.js +++ b/public/js/page-controller/collection-controller.js @@ -6,6 +6,7 @@ const DatabusWebappUtils = require("../utils/databus-webapp-utils"); function CollectionController($scope, $sce, $http, collectionManager) { + $scope.auth = data.auth; $scope.collection = new DatabusCollectionWrapper(data.collection); $scope.authenticated = data.auth.authenticated; $scope.activeTab = 0; @@ -13,21 +14,15 @@ function CollectionController($scope, $sce, $http, collectionManager) { // Make some util functions available in the template $scope.utils = new DatabusWebappUtils($scope, $sce); + $scope.accountName = $scope.utils.getAccountName(); $scope.isOwn = false; if ($scope.authenticated) { - $scope.collectionAccountName = DatabusUtils.uriToName(DatabusUtils.navigateUp($scope.collection.uri, 2)); - $scope.accountName = data.auth.info.accountName; - $scope.isOwn = $scope.accountName === $scope.collectionAccountName; } - $scope.editCollection = function () { - $scope.collectionManager.setActive($scope.collection.uuid); - window.location = `/app/collection-editor`; - } $scope.collectionViewModel = {}; $scope.collectionViewModel.downloadScript = []; @@ -73,8 +68,7 @@ function CollectionController($scope, $sce, $http, collectionManager) { let localCopy = $scope.collectionManager.createCopy($scope.collection); - $scope.collectionManager.setActive(localCopy.uuid); - window.location.href = '/app/collection-editor' + window.location.href = `/app/collection-editor?uuid${localCopy.uuid}`; } $scope.createSnapshot = function () { @@ -84,11 +78,10 @@ function CollectionController($scope, $sce, $http, collectionManager) { let collectionSnapshot = $scope.collectionManager.createSnapshot($scope.collection); - - $scope.collectionManager.setActive(collectionSnapshot.uuid); - window.location.href = '/app/collection-editor' + window.location.href = `/app/collection-editor?uuid${collectionSnapshot.uuid}`; } + $scope.editCollection = function () { if (!$scope.collectionManager.isInitialized) { @@ -99,15 +92,15 @@ function CollectionController($scope, $sce, $http, collectionManager) { /// TODO Fabian - das sollte nicht passieren! if (localCopy === null) { - console.log("editCollection failed due there is no collection with that uri: " + $scope.collection.uri) + console.log("editCollection failed. There is no collection with that uri: " + $scope.collection.uri) $scope.editCopy(); return; } - $scope.collectionManager.setActive(localCopy.uuid); - window.location.href = '/app/collection-editor' + window.location.href = `/app/collection-editor?uuid=${localCopy.uuid}`; } + $scope.downloadAsJson = function () { DatabusCollectionUtils.exportToJsonFile($scope.collection); } diff --git a/public/js/page-controller/collections-editor-controller.js b/public/js/page-controller/collections-editor-controller.js index 5ca723a4..4f4ca9f7 100644 --- a/public/js/page-controller/collections-editor-controller.js +++ b/public/js/page-controller/collections-editor-controller.js @@ -17,8 +17,9 @@ const TabNavigation = require("../utils/tab-navigation"); * @param {*} collectionManager * @returns */ -function CollectionsEditorController($scope, $timeout, $http, $location, collectionManager) { +async function CollectionsEditorController($scope, $timeout, $http, $location, collectionManager) { + $scope.auth = data.auth; $scope.authenticated = data.auth.authenticated; $scope.baseUrl = DATABUS_RESOURCE_BASE_URL; @@ -27,23 +28,44 @@ function CollectionsEditorController($scope, $timeout, $http, $location, collect return; } - $scope.accountName = data.auth.info.accountName; - $scope.hasAccount = $scope.accountName != undefined; + const params = new URLSearchParams(window.location.search); + $scope.uuid = params.get('uuid'); + + // Make some util functions available in the template + $scope.utils = new DatabusWebappUtils($scope); + $scope.collectionManager = collectionManager; + + let collection = $scope.collectionManager.local[$scope.uuid]; - if (!$scope.hasAccount) { + if(collection == null) { + // No working copy found return; } + try { + let collection = $scope.collectionManager.local[$scope.uuid]; + await $scope.collectionManager.tryInitialize(collection.accountName); + } catch(err) { + + } + + $scope.collectionManager.setActive($scope.uuid); + let activeCollection = $scope.collectionManager.activeCollection; + + $scope.accountName = $scope.utils.getOwnedAccountName(activeCollection.accountName); + $scope.hasAccount = $scope.accountName != undefined; + + //if (!$scope.hasAccount) { + // return; + //} + // Create a tab navigation object for the tab navigation with locato $scope.tabNavigation = new TabNavigation($scope, $location, [ 'docu', 'content', 'preview', 'query', 'json', 'import' ]); - // Make some util functions available in the template - $scope.utils = new DatabusWebappUtils($scope); - // Make the manager available in the template - $scope.collectionManager = collectionManager; + // $scope.collectionManager.setActiveCollection($scope.guid); // Form data object for input errors and extra fields and toggles $scope.form = {}; diff --git a/public/js/page-controller/frontpage-controller.js b/public/js/page-controller/frontpage-controller.js index b1b8fe58..daa7102e 100644 --- a/public/js/page-controller/frontpage-controller.js +++ b/public/js/page-controller/frontpage-controller.js @@ -54,6 +54,18 @@ function FrontPageController($scope, $sce, $http) { console.log(err); }); + // Login function + $scope.login = function () { + window.location = '/app/login?redirectUrl=' + encodeURIComponent(window.location); + } + + $scope.goToPage = function(path) { + window.location = path; + } + + $scope.account = function() { + window.location = '/app/account'; + } for(var d in $scope.uploadRankingData) { $scope.uploadRankingData[d].uploadSize = DatabusUtils.formatFileSize($scope.uploadRankingData[d].uploadSize); diff --git a/public/js/page-controller/group-controller.js b/public/js/page-controller/group-controller.js index be962aa8..a844e130 100644 --- a/public/js/page-controller/group-controller.js +++ b/public/js/page-controller/group-controller.js @@ -4,6 +4,7 @@ const DataIdCreator = require("../publish/dataid-creator"); const QueryBuilder = require("../query-builder/query-builder"); const QueryNode = require("../query-builder/query-node"); const QueryTemplates = require("../query-builder/query-templates"); +const DatabusConstants = require("../utils/databus-constants"); const DatabusUtils = require("../utils/databus-utils"); const DatabusWebappUtils = require("../utils/databus-webapp-utils"); const TabNavigation = require("../utils/tab-navigation"); @@ -11,9 +12,13 @@ const TabNavigation = require("../utils/tab-navigation"); function GroupPageController($scope, $http, $sce, $interval, $location, collectionManager) { $scope.group = data.group; - $scope.accountName = DatabusUtils.uriToName(DatabusUtils.navigateUp($scope.group.uri)); + // $scope.accountName = DatabusUtils.uriToName(DatabusUtils.navigateUp($scope.group.uri)); + $scope.auth = data.auth; $scope.utils = new DatabusWebappUtils($scope, $sce); + $scope.accountName = $scope.utils.getAccountName(); + + $scope.tabNavigation = new TabNavigation($scope, $location, [ 'files', 'artifacts', 'edit' ]); @@ -60,7 +65,7 @@ function GroupPageController($scope, $http, $sce, $interval, $location, collecti DatabusUtils.uriToTitle($scope.group.uri)); - $scope.canEdit = $scope.accountName == data.auth.info.accountName; + $scope.canEdit = $scope.accountName != null; if (data.auth.authenticated && $scope.canEdit) { @@ -73,7 +78,7 @@ function GroupPageController($scope, $http, $sce, $interval, $location, collecti $scope.formData.group.abstract = $scope.group.abstract; $scope.formData.group.description = $scope.group.description; - $scope.dataidCreator = new DataIdCreator($scope.formData, data.auth.info.accountName); + $scope.dataidCreator = new DataIdCreator($scope.formData, $scope.accountName); } $scope.onDescriptionChanged = function () { @@ -104,7 +109,7 @@ function GroupPageController($scope, $http, $sce, $interval, $location, collecti var groupUpdate = $scope.dataidCreator.createGroupUpdate(); var relativeUri = new URL($scope.group.uri).pathname; - var response = await $http.put(relativeUri, groupUpdate); + var response = await $http.post('/api/register', groupUpdate); if (response.status == 200) { $scope.group.title = $scope.formData.group.title; @@ -135,6 +140,7 @@ function GroupPageController($scope, $http, $sce, $interval, $location, collecti $scope.fileSelector = {}; $scope.fileSelector.config = {}; + $scope.fileSelector.config.authenticated = $scope.authenticated; $scope.fileSelector.config.columns = []; $scope.fileSelector.config.columns.push({ field: 'artifact', label: 'Artifact', width: '30%', uriToName: true }); $scope.fileSelector.config.columns.push({ field: 'version', label: 'Version', width: '21%' }); @@ -143,7 +149,7 @@ function GroupPageController($scope, $http, $sce, $interval, $location, collecti $scope.fileSelector.config.columns.push({ field: 'compression', label: 'Compression', width: '6%' }); $scope.groupNode = new QueryNode($scope.group.uri, 'databus:group'); - $scope.groupNode.setFacet('http://purl.org/dc/terms/hasVersion', '$latest', true); + $scope.groupNode.setFacet('http://purl.org/dc/terms/hasVersion', DatabusConstants.FACET_LATEST_VERSION_VALUE, true); $scope.onFacetSettingsChanged = function () { $scope.fileSelector.query = QueryBuilder.build({ @@ -309,7 +315,7 @@ function GroupPageController($scope, $http, $sce, $interval, $location, collecti var result = response.data.docs[r]; for (var artifact of $scope.artifacts) { - if (result.resource[0] == artifact.uri) { + if (result.id[0] == artifact.uri) { $scope.searchResult.push(artifact); } } diff --git a/public/js/page-controller/header-controller.js b/public/js/page-controller/header-controller.js index e4dfd847..68565892 100644 --- a/public/js/page-controller/header-controller.js +++ b/public/js/page-controller/header-controller.js @@ -6,25 +6,21 @@ function HeaderController($scope, $http, collectionManager, searchManager) { $scope.auth = data.auth; $scope.authenticated = data.auth.authenticated; - $scope.accountName = null; $scope.utils = new DatabusWebappUtils($scope); + $scope.accountName = $scope.utils.getAccountName(); - if($scope.authenticated && $scope.auth.info != null) { - $scope.accountName = $scope.auth.info.accountName; - } // Check for cookie settings $scope.databusCookieConsentKey = 'databus_cookie_consent'; let cookieConsent = window.localStorage.getItem($scope.databusCookieConsentKey); $scope.showCookieDialogue = cookieConsent === undefined; + $scope.collectionManager = collectionManager; if ($scope.authenticated) { - // Collection Manager Init - var loadCollectionsFromServer = $scope.collectionManager.accountName != $scope.auth.info.accountName; - - $scope.collectionManager.tryInitialize($scope.auth.info.accountName, loadCollectionsFromServer); + $scope.collectionManager.tryInitialize($scope.accountName); + // Collection Manager Init // Initialize search manager searchManager.initialize(); } else { diff --git a/public/js/page-controller/profile-controller.js b/public/js/page-controller/profile-controller.js index e547b5db..c0d97865 100644 --- a/public/js/page-controller/profile-controller.js +++ b/public/js/page-controller/profile-controller.js @@ -8,29 +8,37 @@ const AppJsonFormatter = require("../utils/app-json-formatter"); function ProfileController($scope, $http) { - $scope.profileData = data.profile; + $scope.account = data.account; + $scope.auth = data.auth; + + if (data.owner != null) { + $scope.account.apiKeys = data.owner.apiKeys; + } $scope.auth = data.auth; $scope.preferredDatabusUsername = ""; - $scope.apiKeys = data.auth.info.apiKeys; $scope.createApiKeyName = "" $scope.createAccountError = ""; $scope.createApiKeyError = ""; $scope.addWebIdUri = ""; + $scope.deleteAccountName = ""; $scope.grantAccessUri = ""; $scope.adapters = SearchAdapter.list; $scope.utils = new DatabusWebappUtils($scope); - $scope.personUri = `${DATABUS_RESOURCE_BASE_URL}/${$scope.auth.info.accountName}${DatabusConstants.WEBID_THIS}`; + $scope.accountName = $scope.utils.getAccountName(); + + $scope.personUri = `${DATABUS_RESOURCE_BASE_URL}/${$scope.accountName}${DatabusConstants.WEBID_THIS}`; $scope.putProfile = function (accountName) { - var accountJsonLd = AppJsonFormatter.createAccountData(DATABUS_RESOURCE_BASE_URL, + var accountUri = `${DATABUS_RESOURCE_BASE_URL}/${accountName}`; + var accountJsonLd = AppJsonFormatter.createAccountData( + accountUri, accountName, - accountName, - null, + null, null); - $http.put(`/${accountName}`, accountJsonLd).then(function (result) { + $http.post(`/api/register`, accountJsonLd).then(function (result) { window.location.reload(true); }, function (err) { console.log(err); @@ -39,9 +47,16 @@ function ProfileController($scope, $http) { } - if ($scope.profileData == undefined) { + if ($scope.account == undefined) { $scope.createProfile = function () { + + if ($scope.isSubmitting) { + return; + } + + $scope.isSubmitting = true; + if (!$scope.auth.authenticated) { return; } @@ -61,55 +76,122 @@ function ProfileController($scope, $http) { return; } - $scope.removeApiKey = function (key) { + $scope.addApiKey = async function () { + // Validate the name input only - $http.post(`/api/account/api-key/delete?name=${key.keyname}`).then(function (result) { - $scope.apiKeys = $scope.apiKeys.filter(function (k) { - return k.keyname != key.keyname; - }); + if (!$scope.createApiKeyName) { + DatabusAlert.alert("API key name must be provided."); + return; + } - }, function (err) { - console.log(err); - $scope.createApiKeyError = err.data; - }); - } + let account = $scope.account; - $scope.onCreateApiKeyNameChanged = function () { - var hasError = !DatabusUtils.isValidResourceLabel($scope.createApiKeyName, 3, 20); - $scope.createApiKeyError = hasError ? " API key name must have between 3 and 20 characters and match [A-Za-z0-9\\s_()\\.\\,\\-]*" : ""; - } + const postData = { + accountName: account.accountName, + keyname: $scope.createApiKeyName + }; - $scope.addApiKey = function () { + try { + // Send POST request to create the API key + let response = await $http.post('/api/account/api-key/create', postData); - $http.post(`/api/account/api-key/create?name=${encodeURIComponent($scope.createApiKeyName)}`).then(function (result) { + if (response.data && response.data.apikey && response.data.keyname) { + // Append new key to the list + account.apiKeys.push({ + keyname: response.data.keyname, + apikey: response.data.apikey + }); + + // Clear the name input field + $scope.createApiKeyName = ''; - if (result.data != null) { - $scope.apiKeys.push(result.data); + DatabusAlert.alert($scope, true, "API key created."); + } else { + DatabusAlert.alert($scope, false, "Failed to create API key."); } - DatabusAlert.alert($scope, true, DatabusMessages.ACCOUNT_API_KEY_CREATED); + } catch (error) { + console.error('Error creating API key:', error); + const message = error.data || error.message || "Unknown error occurred."; + DatabusAlert.alert($scope, false, message); + } + }; - }, function (err) { - console.log(err); - $scope.createApiKeyError = err.data; + + $scope.deleteApiKey = async function (apiKey) { + try { + + let account = $scope.account; + // Find index of the account using accountName + const index = account.apiKeys.findIndex(key => key.keyname === apiKey.keyname); + + if (index === -1) { + throw new Error(`API key with name "${apiKey.keyname}" not found.`); + } + + console.log("Deleting API key with keyname:", apiKey.keyname); + + // Send delete request to server + await $http.post(`/api/account/api-key/delete`, { accountName: account.accountName, keyname: apiKey.keyname }); + account.apiKeys.splice(index, 1); + + // Show success alert + DatabusAlert.alert($scope, true, "API key deleted."); + + } catch (err) { + console.error(err); + + + + const message = err.data || err.message || "Unknown error occurred."; + DatabusAlert.alert($scope, false, message); + } + }; + + $scope.addSecretary = function (account) { + if (!$scope.editData.secretaries) { + $scope.editData.secretaries = []; + } + + $scope.editData.secretaries.push({ + accountName: '', + hasWriteAccessTo: [] }); + }; + + $scope.removeSecretary = function (account, index) { + $scope.editData.secretaries.splice(index, 1); + }; + + $scope.addNamespace = function (account, secIndex) { + $scope.editData.secretaries[secIndex].hasWriteAccessTo.push(''); + }; + + $scope.removeNamespace = function (account, secIndex, nsIndex) { + $scope.editData.secretaries[secIndex].hasWriteAccessTo.splice(nsIndex, 1); + }; + + $scope.onCreateApiKeyNameChanged = function () { + var hasError = !DatabusUtils.isValidResourceLabel($scope.createApiKeyName, 3, 20); + $scope.createApiKeyError = hasError ? " API key name must have between 3 and 20 characters and match [A-Za-z0-9\\s_()\\.\\,\\-]*" : ""; } - $scope.removeSearchExtension = function(uri) { + + $scope.removeSearchExtension = function (uri) { $http.post(`/api/account/mods/search-extensions/remove?uri=${encodeURIComponent(uri)}`) - .then(function (result) { - console.log(result); - DatabusAlert.alert($scope, true, result.data); + .then(function (result) { + console.log(result); + DatabusAlert.alert($scope, true, result.data); - $scope.profileData.searchExtensions = $scope.profileData.searchExtensions.filter(function (e) { - return e.endpointUri != uri; - }); + $scope.account.searchExtensions = $scope.account.searchExtensions.filter(function (e) { + return e.endpointUri != uri; + }); - }, function (err) { - console.log(err); - DatabusAlert.alert($scope, false, err.data); - }); + }, function (err) { + console.log(err); + DatabusAlert.alert($scope, false, err.data); + }); } $scope.addSearchExtension = function () { @@ -120,7 +202,7 @@ function ProfileController($scope, $http) { .then(function (result) { console.log(result); DatabusAlert.alert($scope, true, result.data); - $scope.profileData.searchExtensions.push({ + $scope.account.searchExtensions.push({ endpointUri: uri, adapter: adapter }); @@ -132,7 +214,7 @@ function ProfileController($scope, $http) { $scope.grantAccess = function () { $http.post(`/api/account/access/grant?uri=${encodeURIComponent($scope.grantAccessUri)}`).then(function (result) { - $scope.profileData.authorizedAccounts.push($scope.grantAccessUri); + $scope.account.authorizedAccounts.push($scope.grantAccessUri); }, function (err) { console.log(err); $scope.grantAccessError = err.data; @@ -141,7 +223,7 @@ function ProfileController($scope, $http) { $scope.revokeAccess = function (uri) { $http.post(`/api/account/access/revoke?uri=${encodeURIComponent(uri)}`).then(function (result) { - $scope.profileData.authorizedAccounts = $scope.profileData.webIds.filter(function (value, index, arr) { + $scope.account.authorizedAccounts = $scope.account.webIds.filter(function (value, index, arr) { return value != uri; }); }, function (err) { @@ -153,7 +235,7 @@ function ProfileController($scope, $http) { $scope.connectWebid = function () { $http.post(`/api/account/webid/add?uri=${encodeURIComponent($scope.addWebIdUri)}`).then(function (result) { - $scope.profileData.webIds.push($scope.addWebIdUri); + $scope.account.webIds.push($scope.addWebIdUri); DatabusAlert.alert($scope, true, DatabusMessages.ACCOUNT_WEBID_LINKED); }, function (err) { @@ -166,7 +248,7 @@ function ProfileController($scope, $http) { $http.post(`/api/account/webid/remove?uri=${encodeURIComponent(webIdToRemove)}`).then(function (result) { - $scope.profileData.webIds = $scope.profileData.webIds.filter(function (value, index, arr) { + $scope.account.webIds = $scope.account.webIds.filter(function (value, index, arr) { return value != webIdToRemove; }); @@ -177,28 +259,52 @@ function ProfileController($scope, $http) { } - $scope.saveProfile = async function () { + $scope.deleteAccount = async function () { + let account = $scope.account; + let name = $scope.deleteAccountName; + + try { + let response = await $http.post(`/api/account/delete`, { accountName: name }); + + window.location = `/app/user`; + + } catch (err) { + console.error(err); + DatabusAlert.alert($scope, false, err.data); + } + + } + + $scope.updateAccount = async function () { if (!$scope.auth.authenticated) { return; } - var accountJsonLd = AppJsonFormatter.createAccountData(DATABUS_RESOURCE_BASE_URL, - $scope.auth.info.accountName, - $scope.editData.label, - $scope.editData.about, - $scope.editData.imageUrl); + let account = {}; + account.uri = $scope.editData.uri; - $http.put(`/${$scope.auth.info.accountName}`, accountJsonLd).then(function (result) { - DatabusAlert.alert($scope, true, DatabusMessages.ACCOUT_PROFILE_SAVED); - }, function (err) { - console.log(err); - }); + account.accountName = $scope.editData.accountName; + account.label = $scope.editData.label; + account.status = $scope.editData.about; + account.imageUrl = $scope.editData.imageUrl; + account.secretaries = $scope.editData.secretaries; + + + try { + await $http.post(`/api/account/update`, account); + DatabusAlert.alert($scope, true, "Account saved."); + + } catch (err) { + console.error(err); + DatabusAlert.alert($scope, false, err.data); + } } - // We have profile data in $scope.profileData! - if (!$scope.profileData.isOwn) { + // We have profile data in $scope.account! + + if (!$scope.account.isOwn) { return; } @@ -207,10 +313,10 @@ function ProfileController($scope, $http) { $scope.modsSettings.searchExtensionAdapter = $scope.adapters[0]; - $scope.editData = DatabusUtils.createCleanCopy($scope.profileData); + $scope.editData = DatabusUtils.createCleanCopy($scope.account); $scope.resetEdits = function () { - $scope.editData = DatabusUtils.createCleanCopy($scope.profileData); + $scope.editData = DatabusUtils.createCleanCopy($scope.account); } } diff --git a/public/js/page-controller/publish-wizard-controller.js b/public/js/page-controller/publish-wizard-controller.js index 21df61ae..b857f7e4 100644 --- a/public/js/page-controller/publish-wizard-controller.js +++ b/public/js/page-controller/publish-wizard-controller.js @@ -1,8 +1,9 @@ const DatabusWebappUtils = require("../utils/databus-webapp-utils"); const PublishSession = require("../publish/publish-session"); +const TabNavigation = require("../utils/tab-navigation"); // Controller for the header section -function PublishWizardController($scope, $http, $interval, focus, $q) { +async function PublishWizardController($scope, $http, $interval, focus, $q, $location) { $scope.login = function () { window.location = '/app/login?redirectUrl=' + encodeURIComponent(window.location); @@ -10,8 +11,18 @@ function PublishWizardController($scope, $http, $interval, focus, $q) { $scope.utils = new DatabusWebappUtils($scope); + $scope.tabNavigation = new TabNavigation($scope, $location, [ + '', 'group', 'artifact', 'version' + ]); + + $scope.createAccount = function () { - window.location = '/app/account'; + window.location = '/app/user'; + } + + // Login function + $scope.login = function () { + window.location = '/app/login?redirectUrl=' + encodeURIComponent(window.location); } $scope.authenticated = data.auth.authenticated; @@ -29,15 +40,30 @@ function PublishWizardController($scope, $http, $interval, focus, $q) { return; } - $scope.hasAccount = data.auth.info.accountName != undefined;; + let accounts = data.auth.info.accounts; + $scope.hasAccount = accounts != undefined && accounts.length > 0; + + $scope.accounts = []; + + for(let account of accounts) { + $scope.accounts.push({ + accountName: account.accountName, + apiKeys: account.apiKeys + }); + } if (!$scope.hasAccount) { return; } + // $scope.session = await PublishSession.createOrResume($http, data.auth.sub, $scope.accounts); + + $scope.session = new PublishSession($http, $interval, $scope.accounts, $scope.apiKeys); + +} /** * Fetches existing groups and artifacts - */ + $scope.getContentForAccount = async function (accountName) { $scope.isAccountDataLoading = true; @@ -54,48 +80,24 @@ function PublishWizardController($scope, $http, $interval, focus, $q) { accountData.publisherUris.push(p.publisherUri); } - // Try to resume the session with the account data - var session = PublishSession.resume($http, accountData); - - // Resume failed -> start new session - if (session == null) { - session = new PublishSession($http, null, accountData); - } + $scope.session = new PublishSession($http); - $scope.session = session; + /* $scope.$watch('session', function () { $scope.session.onChange(); }, true); $scope.$apply(); + } - $scope.getContentForAccount(data.auth.info.accountName); + // $scope.getContentForAccount(data.auth.info.accounts[0]); /** * LICENSES - */ + - $scope.licenseQuery = ""; - - $interval(function () { - if ($scope.hasLicenseQueryChanged) { - - $http.get(`/app/publish-wizard/licenses?limit=30&keyword=${$scope.licenseQuery}`).then(function(response) { - $scope.filteredLicenseList = response.data.results.bindings; - }); - - $scope.hasLicenseQueryChanged = false; - } - - }, 300); - - $scope.filterLicenses = function (licenseQuery) { - $scope.licenseQuery = licenseQuery; - $scope.hasLicenseQueryChanged = true; - } - - $scope.filterLicenses(""); + $scope.addFile = function (input) { @@ -166,10 +168,11 @@ function PublishWizardController($scope, $http, $interval, focus, $q) { }); } + $scope.customPublish = async function () { var options = {} options.headers = { - 'Accept': 'application/json, text/plain, */*', + 'Accept': 'application/json, text/plain', 'Content-Type': 'application/json', } @@ -190,7 +193,7 @@ function PublishWizardController($scope, $http, $interval, focus, $q) { $scope.publish = async function () { var options = {} options.headers = { - 'Accept': 'application/json, text/plain, */*', + 'Accept': 'application/json, text/plain', 'Content-Type': 'application/json', } @@ -204,8 +207,9 @@ function PublishWizardController($scope, $http, $interval, focus, $q) { $scope.isPublishing = false; console.log(err); }); - } -} + } */ + + module.exports = PublishWizardController; \ No newline at end of file diff --git a/public/js/page-controller/sparql-editor-controller.js b/public/js/page-controller/sparql-editor-controller.js index c14cd051..cc6357c5 100644 --- a/public/js/page-controller/sparql-editor-controller.js +++ b/public/js/page-controller/sparql-editor-controller.js @@ -2,7 +2,7 @@ var DatabusWebappUtils = require("../utils/databus-webapp-utils"); const SparqlExamples = require("../utils/sparql-examples"); // Controller for the header section -function SparqlEditorController($scope, $http) { +function SparqlEditorController($scope, $http, $location) { $scope.storageKey = `${DATABUS_RESOURCE_BASE_URL}/sparql`; @@ -15,22 +15,38 @@ function SparqlEditorController($scope, $http) { $scope.editor = {}; + + $scope.$on('$locationChangeSuccess', function () { + var hash = $location.hash(); + + if (hash && hash.startsWith('query')) { + var tabIndex = parseInt(hash.replace('query', '')) - 1; + + // Only change if the tab exists and is different from current + if (!isNaN(tabIndex) && + tabIndex >= 0 && + tabIndex < $scope.queryData.pages.length && + $scope.queryData.activeTab !== tabIndex) { + $scope.goToTab(tabIndex); + $scope.$applyAsync(); + } + } + }); + $scope.editor.exampleQueries = {}; $scope.editor.exampleQueries.label = "Databus Example Queries"; $scope.editor.exampleQueries.children = []; var simpleQueries = { label: "Simple Queries", - children : [] + children: [] }; - var intermediateQueries = { label: "Intermediate Queries", - children : [] + children: [] }; - simpleQueries.children.push({ label: "Select all Databus Groups", query: `PREFIX databus: @@ -113,9 +129,9 @@ ORDER BY DESC (STR(?v)) LIMIT 1` $scope.editor.exampleQueries.children.push(simpleQueries); $scope.editor.exampleQueries.children.push(intermediateQueries); - $scope.onExampleQueryClicked = function(node) { + $scope.onExampleQueryClicked = function (node) { - if(node.query == null) { + if (node.query == null) { return; } @@ -131,6 +147,9 @@ ORDER BY DESC (STR(?v)) LIMIT 1` $scope.queryData.activeTab = index; $scope.saveToStorage(); + $location.hash(`query${index + 1}`); + + var queryPage = $scope.queryData.pages[$scope.queryData.activeTab]; if ($scope.resultCache != null && $scope.resultCache[queryPage.name] != null) { @@ -244,27 +263,47 @@ ORDER BY DESC (STR(?v)) LIMIT 1` } } + + catch (e) { // Could not parse query data, create new! $scope.initialize(); } + var initialHash = $location.hash(); + if (initialHash && initialHash.startsWith('query')) { + var initialTab = parseInt(initialHash.replace('query', '')) - 1; + if (!isNaN(initialTab) && + initialTab >= 0 && + initialTab < $scope.queryData.pages.length) { + $scope.queryData.activeTab = initialTab; + } + } + $scope.editor.query = $scope.editor.exampleQueries[0]; $scope.send = async function () { var queryPage = $scope.queryData.pages[$scope.queryData.activeTab]; - var res = await $http.post(queryPage.endpoint, { query: queryPage.query }); + try { - if ($scope.resultCache == null) { - $scope.resultCache = {}; - } + var res = await $http.post(queryPage.endpoint, { query: queryPage.query }); - $scope.resultCache[queryPage.name] = res.data; - $scope.saveResultCache(); + if ($scope.resultCache == null) { + $scope.resultCache = {}; + } + + $scope.resultCache[queryPage.name] = res.data; + $scope.saveResultCache(); + + delete queryPage.err; + $scope.editor.result = res.data; + } catch (err) { + console.log(err); + queryPage.err = err; + } - $scope.editor.result = res.data; $scope.$apply(); } diff --git a/public/js/page-controller/user-settings-controller.js b/public/js/page-controller/user-settings-controller.js new file mode 100644 index 00000000..1f8ddc58 --- /dev/null +++ b/public/js/page-controller/user-settings-controller.js @@ -0,0 +1,259 @@ +const DatabusAlert = require("../components/databus-alert/databus-alert"); +const DatabusUris = require("../utils/databus-uris"); +const DatabusUtils = require("../utils/databus-utils"); +const JsonldUtils = require("../utils/jsonld-utils"); +const TabNavigation = require("../utils/tab-navigation"); + +function UserSettingsController($scope, $http, $sce, $location) { + $scope.auth = data.auth; + $scope.accounts = data.accounts; + + $scope.inputs = {}; + + $scope.inputs.newAccountLabel = ""; + $scope.inputs.newAccountName = ""; + $scope.inputs.newApiKeyName = ""; + + $scope.tabNavigation = new TabNavigation($scope, $location, [ + '' + ], function (index) { + $scope.activeAccount = $scope.accounts[index - 1]; + }); + + $scope.$watchCollection('accounts', function (newAccounts) { + const accountNames = newAccounts.map(a => a.accountName); + $scope.tabNavigation.tabKeys = [''].concat(accountNames); + + const currentHash = $location.hash(); + + $scope.tabNavigation.onLocationHashChanged(currentHash, currentHash) + + if (currentHash && !$scope.tabNavigation.tabKeys.includes(currentHash)) { + $location.hash(''); + } + }); + + // Iterate over each account and load its data + $scope.accounts.forEach(function (account) { + // Set loading state + account.loading = true; + + var requestParams = { + method: 'GET', + url: '/' + encodeURIComponent(account.accountName), + headers: { + 'Accept': 'application/ld+json', + 'X-Jsonld-Formatting': 'flatten' + } + } + + // Perform HTTP GET request to fetch additional data + $http(requestParams) + .then(function (response) { + // Set loading to false when data is received + account.loading = false; + + // Store additional info (stub) + var graphs = response.data; + var personGraph = JsonldUtils.getTypedGraph(graphs, DatabusUris.FOAF_PERSON); + + account.uri = `${DATABUS_RESOURCE_BASE_URL}/${account.accountName}`; + account.label = JsonldUtils.getFirstProperty(personGraph, DatabusUris.FOAF_NAME); + account.status = JsonldUtils.getFirstProperty(personGraph, DatabusUris.FOAF_STATUS); + account.imageUrl = JsonldUtils.getProperty(personGraph, DatabusUris.FOAF_IMG); + account.secretaries = []; + + let accountGraph = JsonldUtils.getTypedGraph(graphs, DatabusUris.DATABUS_ACCOUNT); + let secretaryIds = JsonldUtils.getRefArrayProperty(accountGraph, DatabusUris.DATABUS_SECRETARY_PROPERTY); + + for (let secretaryId of secretaryIds) { + let secretaryGraph = JsonldUtils.getGraphById(graphs, secretaryId); + + let secretary = {}; + secretary.accountName = DatabusUtils.uriToName(JsonldUtils.getProperty(secretaryGraph, DatabusUris.DATABUS_ACCOUNT_PROPERTY)); + secretary.hasWriteAccessTo = JsonldUtils.getRefArrayProperty(secretaryGraph, DatabusUris.DATABUS_HAS_WRITE_ACCESS_TO); + + account.secretaries.push(secretary); + } + + }) + .catch(function (error) { + // Handle error and set loading to false + account.loading = false; + console.error('Failed to load account data for', account.name, error); + }); + }); + + // Button click handler to add account + $scope.addAccount = async function () { + + try { + + await $http.post(`/api/account/create`, { + name: $scope.inputs.newAccountName, + label: $scope.inputs.newAccountLabel + }); + + $scope.accounts.push({ + label: $scope.inputs.newAccountLabel, + accountName: $scope.inputs.newAccountName, + uri: `${DATABUS_RESOURCE_BASE_URL}/${$scope.inputs.newAccountName}` + }); + + DatabusAlert.alert($scope, true, "Account created."); + + } catch (err) { + console.error(err); + DatabusAlert.alert($scope, false, err.data); + } + }; + + // Button click handler to save account + $scope.saveAccount = async function (account) { + try { + await $http.post(`/api/account/update`, account); + DatabusAlert.alert($scope, true, "Account saved."); + + } catch (err) { + console.error(err); + DatabusAlert.alert($scope, false, err.data); + } + + }; + + // Button click handler to delete account + $scope.deleteAccount = async function (account) { + try { + // Find index of the account using accountName + const index = $scope.accounts.findIndex(acc => acc.accountName === account.accountName); + + if (index === -1) { + throw new Error(`Account with name "${account.accountName}" not found.`); + } + + console.log("Deleting account with accountName:", account.accountName); + + // Send delete request to server + await $http.post(`/api/account/delete`, account); + + // Show success alert + DatabusAlert.alert($scope, true, "Account deleted."); + + // Remove account from local array + $scope.accounts.splice(index, 1); + + } catch (err) { + console.error(err); + const message = err.data || err.message || "Unknown error occurred."; + DatabusAlert.alert($scope, false, message); + } + }; + + + + + $scope.goToUserSettings = function (accountName) { + window.location.href = '/' + encodeURIComponent(accountName) + '#settings'; + } + + $scope.addWriteAccessUrl = function (account) { + account.writeAccess.push(''); + }; + + $scope.removeWriteAccessUrl = function (account, index) { + account.writeAccess.splice(index, 1); + }; + + $scope.addApiKey = async function (account) { + // Validate the name input only + + if (!$scope.inputs.newApiKeyName) { + DatabusAlert.alert("API key name must be provided."); + return; + } + + const postData = { + accountName: account.accountName, + name: $scope.inputs.newApiKeyName + }; + + try { + // Send POST request to create the API key + let response = await $http.post('/api/account/api-key/create', postData); + + if (response.data && response.data.apikey && response.data.keyname) { + // Append new key to the list + account.apiKeys.push({ + keyname: response.data.keyname, + apikey: response.data.apikey + }); + + // Clear the name input field + $scope.inputs.newApiKeyName = ''; + + DatabusAlert.alert($scope, true, "API key created."); + } else { + DatabusAlert.alert($scope, false, "Failed to create API key."); + } + + } catch (error) { + console.error('Error creating API key:', error); + const message = err.data || err.message || "Unknown error occurred."; + DatabusAlert.alert($scope, false, message); + } + }; + + + $scope.deleteApiKey = async function (account, apiKey) { + try { + // Find index of the account using accountName + const index = account.apiKeys.findIndex(key => key.keyname === apiKey.keyname); + + if (index === -1) { + throw new Error(`API key with name "${apiKey.keyname}" not found.`); + } + + console.log("Deleting API key with keyname:", apiKey.keyname); + + // Send delete request to server + await $http.post(`/api/account/api-key/delete`, { accountName: account.accountName, keyname: apiKey.keyname }); + account.apiKeys.splice(index, 1); + + // Show success alert + DatabusAlert.alert($scope, true, "API key deleted."); + + } catch (err) { + console.error(err); + + + + const message = err.data || err.message || "Unknown error occurred."; + DatabusAlert.alert($scope, false, message); + } + }; + + $scope.addSecretary = function (account) { + if (!account.secretaries) { + account.secretaries = []; + } + + account.secretaries.push({ + accountName: '', + hasWriteAccessTo: [] + }); + }; + + $scope.removeSecretary = function (account, index) { + account.secretaries.splice(index, 1); + }; + + $scope.addNamespace = function (account, secIndex) { + account.secretaries[secIndex].hasWriteAccessTo.push(''); + }; + + $scope.removeNamespace = function (account, secIndex, nsIndex) { + account.secretaries[secIndex].hasWriteAccessTo.splice(nsIndex, 1); + }; +} + +module.exports = UserSettingsController; \ No newline at end of file diff --git a/public/js/page-controller/version-controller.js b/public/js/page-controller/version-controller.js index 23275527..4d7b0540 100644 --- a/public/js/page-controller/version-controller.js +++ b/public/js/page-controller/version-controller.js @@ -17,7 +17,10 @@ function VersionPageController($scope, $http, $sce, $location, collectionManager 'files', 'mods', 'edit' ]); + $scope.auth = data.auth; $scope.utils = new DatabusWebappUtils($scope, $sce); + $scope.accountName = $scope.utils.getAccountName(); + $scope.collectionManager = collectionManager; $scope.authenticated = data.auth.authenticated; $scope.versionGraph = data.graph; @@ -28,7 +31,7 @@ function VersionPageController($scope, $http, $sce, $location, collectionManager $scope.collectionModalVisible = false; $scope.publisherName = DatabusUtils.uriToName(DatabusUtils.navigateUp($scope.version.uri, 3)); - $scope.canEdit = $scope.publisherName == data.auth.info.accountName; + $scope.canEdit = $scope.accountName != null; if (data.auth.authenticated && $scope.canEdit) { @@ -135,8 +138,7 @@ function VersionPageController($scope, $http, $sce, $location, collectionManager $scope.formData.version.wasDerivedFrom); } - var relativeUri = new URL($scope.version.uri).pathname; - var response = await $http.put(relativeUri, graphs); + var response = await $http.put(`/api/register`, graphs); if (response.status == 200) { $scope.version.title = $scope.formData.version.title; @@ -167,6 +169,7 @@ function VersionPageController($scope, $http, $sce, $location, collectionManager $scope.fileSelector = {}; $scope.fileSelector.config = {}; + $scope.fileSelector.config.authenticated = $scope.authenticated; $scope.fileSelector.config.columns = []; $scope.fileSelector.config.columns.push({ field: 'variant', label: 'Variant', width: '45%' }); $scope.fileSelector.config.columns.push({ field: 'format', label: 'Format', width: '15%' }); @@ -246,7 +249,7 @@ function VersionPageController($scope, $http, $sce, $location, collectionManager return DatabusUtils.uriToName(uri); } - $scope.downloadMetadataAsFile = async function() { + $scope.downloadMetadataAsFile = async function () { var response = await $http({ method: 'GET', url: $scope.version.uri, @@ -258,15 +261,15 @@ function VersionPageController($scope, $http, $sce, $location, collectionManager $scope.download(`${$scope.version.name}.jsonld`, JSON.stringify(response.data, null, 3)); } - $scope.download = function(filename, text) { + $scope.download = function (filename, text) { var element = document.createElement('a'); element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); element.setAttribute('download', filename); element.style.display = 'none'; document.body.appendChild(element); - + element.click(); - + document.body.removeChild(element); } diff --git a/public/js/publish/artifact-data.js b/public/js/publish/artifact-data.js new file mode 100644 index 00000000..1428e595 --- /dev/null +++ b/public/js/publish/artifact-data.js @@ -0,0 +1,110 @@ +const EntityHandler = require('./entity-handler'); +const DatabusUtils = require('../utils/databus-utils'); +const DatabusUris = require('../utils/databus-uris'); +const GroupData = require('./group-data'); + +class ArtifactData extends EntityHandler { + constructor($http, accounts, apiKeys) { + super('databus_registration_artifact_data', $http, null, accounts, apiKeys); + } + + initialize(data) { + const validAccount = data && this.accounts.some(acc => acc.accountName === data.accountName); + + if (validAccount) { + Object.assign(this, data); + } else { + this.accountName = this.accounts[0]?.name; + } + + if(this.apiKeyName == null && this.apiKeys != null && this.apiKeys.length > 0) { + this.apiKeyName = this.apiKeys[0].keyname; + } + + + this.sendmode ??= 'register'; + this.onAccountNameChanged(); + this.onGroupNameChanged(); + } + + validate() { + this.errors = []; + this.warnings = []; + + if (!DatabusUtils.isValidArtifactName(this.name)) { + this.errors.push('err_invalid_artifact_name'); + } + + if (!DatabusUtils.isValidGroupName(this.groupName)) { + this.errors.push('err_no_group_selected'); + } + + const exists = this.artifactList?.some(a => a.name === this.name); + if (exists) { + this.warnings.push('warning_artifact_exists'); + } + } + + getURI() { + return `${DATABUS_RESOURCE_BASE_URL}/${this.accountName}/${this.groupName}/${this.name}`; + } + + getSaveData() { + return { + accountName: this.accountName, + groupName: this.groupName, + name: this.name, + title: this.title, + abstract: this.abstract, + description: this.description, + sendmode: this.sendmode, + apiKeyName: this.apiKeyName, + }; + } + + + async setGroupName(groupName) { + if (this.groupName !== groupName) { + this.groupName = groupName; + await this.onGroupNameChanged(); + } + } + + async onGroupNameChanged() { + this.isLoadingArtifacts = true; + this.artifactList = await this.sparqlClient.getArtifacts(this.accountName, this.groupName); + this.isLoadingArtifacts = false; + this.onChange(); + } + + + + updateOutputs() { + const groupUri = `${DATABUS_RESOURCE_BASE_URL}/${GroupData.getStringOrMissing(this.accountName)}/${GroupData.getStringOrMissing(this.groupName)}`; + + this.postBody = { + "@context": this.getContext(), + "@graph": [ + { + "@id": `${groupUri}/${GroupData.getStringOrMissing(this.name)}`, + "@type": "Artifact", + "title": this.getValidString(this.title), + "abstract": this.getValidString(this.abstract), + "description": this.getValidString(this.description), + } + ] + }; + + const payload = JSON.stringify(this.postBody, null, 2); + const apiKey = this.apiKeys?.find(k => k.keyname === this.apiKeyName)?.apikey; + + this.curlCommand = [ + `curl -X POST ${DATABUS_RESOURCE_BASE_URL}/api/register \\`, + ` -H "X-API-KEY: ${GroupData.getStringOrMissing(apiKey)}" \\`, + ` -H "Content-Type: application/json" \\`, + ` -d '${payload}'` + ].join('\n'); + } +} + +module.exports = ArtifactData; diff --git a/public/js/publish/databus-sparql-client.js b/public/js/publish/databus-sparql-client.js new file mode 100644 index 00000000..c014598e --- /dev/null +++ b/public/js/publish/databus-sparql-client.js @@ -0,0 +1,92 @@ +class DatabusSparqlClient { + + constructor($http) { + this.$http = $http; + } + + /** + * Generic SPARQL query runner. + * @param {string} query - SPARQL query string. + * @returns {Promise} - Query result bindings. + */ + async runQuery(query) { + const config = { + method: 'POST', + url: `/sparql`, + headers: { + 'Accept': 'application/sparql-results+json', + 'Content-Type': 'application/x-www-form-urlencoded' + }, + data: `query=${encodeURIComponent(query)}` + }; + + try { + const response = await this.$http(config); + return response.data.results.bindings || []; + } catch (err) { + console.error('SPARQL query failed:', err); + return []; + } + } + + /** + * Fetches groups for a given Databus account. + * @param {string} accountName - The account name (e.g., 'myaccount'). + * @returns {Promise} - List of groups with basic metadata. + */ + async getGroups(accountName) { + const query = ` + PREFIX databus: + + SELECT DISTINCT ?group WHERE { + ?group a databus:Group . + ?group databus:account <${DATABUS_RESOURCE_BASE_URL}/${accountName}> . + } + `; + + const bindings = await this.runQuery(query); + + return bindings.map(binding => ({ + uri: binding.group.value, + name: binding.group.value.split('/').pop(), + })); + } + + async getArtifacts(accountName, groupName) { + const query = ` + PREFIX databus: + + SELECT DISTINCT ?group WHERE { + ?group a databus:Artifact . + ?group databus:group <${DATABUS_RESOURCE_BASE_URL}/${accountName}/${groupName}> . + } + `; + + const bindings = await this.runQuery(query); + + return bindings.map(binding => ({ + uri: binding.group.value, + name: binding.group.value.split('/').pop(), + })); + } + + async getVersions(accountName, groupName, artifactName) { + const query = ` + PREFIX databus: + + SELECT DISTINCT ?group WHERE { + ?group a databus:Version . + ?group databus:artifact <${DATABUS_RESOURCE_BASE_URL}/${accountName}/${groupName}/${artifactName}> . + } + `; + + const bindings = await this.runQuery(query); + + return bindings.map(binding => ({ + uri: binding.group.value, + name: binding.group.value.split('/').pop(), + })); + } +} + +module.exports = DatabusSparqlClient; diff --git a/public/js/publish/entity-handler.js b/public/js/publish/entity-handler.js new file mode 100644 index 00000000..97e1d0e6 --- /dev/null +++ b/public/js/publish/entity-handler.js @@ -0,0 +1,146 @@ +const DatabusUris = require("../utils/databus-uris"); +const DatabusUtils = require("../utils/databus-utils"); +const DatabusSparqlClient = require("./databus-sparql-client"); + +class EntityHandler { + constructor(storageKey, $http, $interval, accounts, apiKeys) { + this.storageKey = storageKey; + this.$http = $http; + this.$interval = $interval; + this.accounts = accounts; + this.apiKeys = apiKeys; + this.sparqlClient = new DatabusSparqlClient($http); + + const data = this._loadFromSession(); + this.initialize(data); + } + + _loadFromSession() { + try { + const raw = window.sessionStorage.getItem(this.storageKey); + return raw ? JSON.parse(raw) : null; + } catch (e) { + console.error("Failed to load session data:", e); + return null; + } + } + + save() { + try { + const data = this.getSaveData(); + const json = JSON.stringify(data); + window.sessionStorage.setItem(this.storageKey, json); + } catch (e) { + console.error("Failed to save session data:", e); + } + } + + // Abstract methods + initialize(data) { + throw new Error("Method 'initialize(data)' must be implemented."); + } + + getSaveData() { + throw new Error("Method 'getSaveData()' must be implemented."); + } + + validate() { + throw new Error("Method 'validate()' must be implemented."); + } + + updateOutputs() { + throw new Error("Method 'updateOutputs()' must be implemented."); + } + + getURI() { + throw new Error("Method 'updateOutputs()' must be implemented."); + } + + getValidString(value) { + return value?.length > 0 ? value : undefined; + } + + static getStringOrMissing(value) { + return value?.length > 0 ? value : '!!!missing!!!'; + } + + hasError(errorKey) { + return this.errors?.includes(errorKey) ?? false; + } + + setSendMode(sendmode) { + this.sendmode = sendmode; + this.onChange(); + } + + getContext() { + if (DATABUS_CONTEXT_URL && DatabusUtils.isValidHttpUrl(DATABUS_CONTEXT_URL)) { + return DATABUS_CONTEXT_URL; + } + return DATABUS_CONTEXT[DatabusUris.JSONLD_CONTEXT]; + } + + async setAccountName(accountName) { + if (this.accountName !== accountName) { + this.accountName = accountName; + await this.onAccountNameChanged(); + } + } + + async onAccountNameChanged() { + this.isLoadingGroups = true; + this.groupList = await this.sparqlClient.getGroups(this.accountName); + this.isLoadingGroups = false; + this.onChange(); + } + + async setAccountName(accountName) { + if (this.accountName !== accountName) { + this.accountName = accountName; + + this.activeAccount = this.accounts?.find(a => a.accountName === this.accountName); + await this.onAccountNameChanged(); + } + } + + getAccount() { + return this.activeAccount; + + } + + onChange() { + this.updateOutputs(); + this.validate(); + this.save(); + } + + getApiKey() { + return this.getAccount()?.apiKeys?.find(k => k.keyname === this.apiKeyName)?.apikey; + } + + setApiKeyName(keyname) { + this.apiKeyName = keyname; + this.onChange(); + } + + async register() { + try { + const response = await this.$http({ + method: 'POST', + url: `/api/register?log-level=info`, + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + data: this.postBody + }); + + return response; + } catch (err) { + console.error('Entity registration failed:', err); + throw err; + } + } +} + +module.exports = EntityHandler; diff --git a/public/js/publish/group-data.js b/public/js/publish/group-data.js new file mode 100644 index 00000000..a76f582f --- /dev/null +++ b/public/js/publish/group-data.js @@ -0,0 +1,91 @@ +const EntityHandler = require('./entity-handler'); +const DatabusUtils = require('../utils/databus-utils'); +const DatabusUris = require('../utils/databus-uris'); +const DatabusSparqlClient = require('./databus-sparql-client'); + +class GroupData extends EntityHandler { + constructor($http, accounts, apiKeys) { + super('databus_registration_group_data', $http, null, accounts, apiKeys); + + } + + initialize(data) { + const validAccount = data && this.accounts.some(acc => acc.accountName === data.accountName); + + if (validAccount) { + Object.assign(this, data); + } else { + this.accountName = this.accounts[0]?.name; + } + + if(this.apiKeyName == null && this.apiKeys != null && this.apiKeys.length > 0) { + this.apiKeyName = this.apiKeys[0].keyname; + } + + this.sendmode ??= 'register'; + this.onAccountNameChanged(); + } + + getSaveData() { + return { + accountName: this.accountName, + name: this.name, + title: this.title, + abstract: this.abstract, + description: this.description, + sendmode: this.sendmode, + apiKeyName: this.apiKeyName, + }; + } + + getURI() { + return `${DATABUS_RESOURCE_BASE_URL}/${this.accountName}/${this.name}`; + } + + validate() { + this.errors = []; + this.warnings = []; + + if (!DatabusUtils.isValidGroupName(this.name)) { + this.errors.push('err_invalid_group_name'); + } + + if(this.sendmode == 'curl' && !this.apiKeyName) { + this.errors.push('err_no_api_key'); + } + + const exists = this.groupList?.some(g => g.name === this.name); + if (exists) { + this.warnings.push('warning_group_exists'); + } + } + + updateOutputs() { + const accountUri = `${DATABUS_RESOURCE_BASE_URL}/${GroupData.getStringOrMissing(this.accountName)}`; + + this.postBody = { + "@context": this.getContext(), + "@graph": [ + { + "@id": `${accountUri}/${GroupData.getStringOrMissing(this.name)}`, + "@type": "Group", + "title": this.getValidString(this.title), + "abstract": this.getValidString(this.abstract), + "description": this.getValidString(this.description), + } + ] + }; + + const payload = JSON.stringify(this.postBody, null, 2); + const apiKey = this.getApiKey(); + + this.curlCommand = [ + `curl -X POST ${DATABUS_RESOURCE_BASE_URL}/api/register \\`, + ` -H "X-API-KEY: ${GroupData.getStringOrMissing(apiKey)}" \\`, + ` -H "Content-Type: application/json" \\`, + ` -d '${payload}'` + ].join('\n'); + } +} + +module.exports = GroupData; diff --git a/public/js/publish/publish-data.js b/public/js/publish/publish-data.js index 93913c85..b0e0723d 100644 --- a/public/js/publish/publish-data.js +++ b/public/js/publish/publish-data.js @@ -5,16 +5,26 @@ const DatabusUtils = require("../utils/databus-utils"); */ class PublishData { - constructor(data, accountData) { - - this.accountData = accountData; - this.group = data != undefined ? data.group : {}; - this.artifact = data != undefined ? data.artifact : {}; - this.version = data != undefined ? data.version : {}; - this.files = data != undefined ? data.files : {}; - this.signature = data != undefined ? data.signature : undefined; + constructor(data) { + + if (data != null) { + this.account = data.account ?? {}; + this.group = data.group ?? {}; + this.artifact = data.artifact ?? {}; + this.version = data.version ?? {}; + this.files = data.files ?? {}; + this.signature = data.signature; + } if (data == null) { + + this.account = {}; + this.group = {}; + this.artifact = {}; + this.version = {}; + this.files = {}; + this.signature = undefined; + this.group.generateMetadata = 'create'; this.group.generateAbstract = true; this.artifact.generateMetadata = 'create'; @@ -30,8 +40,8 @@ class PublishData { var signature = {}; signature.publisherUris = []; - signature.publisherUris = this.accountData.publisherUris; - signature.defaultPublisherUri = `${DATABUS_RESOURCE_BASE_URL}/${this.accountData.accountName}#this` + signature.publisherUris = this.account.publisherUris; + signature.defaultPublisherUri = `${DATABUS_RESOURCE_BASE_URL}/${this.account.accountName}#this` signature.selectedPublisherUri = signature.defaultPublisherUri; signature.autoGenerateSignature = true; signature.autoGenerateSignatureLocked = false; @@ -43,6 +53,16 @@ class PublishData { hasError(error) { } + + clearErrors() { + this.group.errors = []; + this.artifact.errors = []; + this.version.errors = []; + this.files.errors = []; + this.group.warnings = []; + this.artifact.warnings = []; + this.version.warnings = []; + } /** * Validates the tree */ @@ -65,7 +85,7 @@ class PublishData { var self = this; - var existingGroup = this.accountData.groups.filter(function (value) { + var existingGroup = this.account.groups.filter(function (value) { return value.name == self.group.name; }); @@ -73,7 +93,7 @@ class PublishData { this.group.warnings.push('warning_group_exists'); } - var existingArtifact = this.accountData.artifacts.filter(function (value) { + var existingArtifact = this.account.artifacts.filter(function (value) { return value.groupName == self.group.name && value.name == self.artifact.name; }); @@ -109,9 +129,9 @@ class PublishData { } } - var versionUri = `${DATABUS_RESOURCE_BASE_URL}/${this.accountData.accountName}/${this.group.name}/${this.artifact.name}/${this.version.name}`; + var versionUri = `${DATABUS_RESOURCE_BASE_URL}/${this.account.accountName}/${this.group.name}/${this.artifact.name}/${this.version.name}`; - var existingVersion = this.accountData.versions.filter(function (value) { + var existingVersion = this.account.versions.filter(function (value) { return value == versionUri; }); diff --git a/public/js/publish/publish-session.js b/public/js/publish/publish-session.js index 8bf4dd4a..f152b08a 100644 --- a/public/js/publish/publish-session.js +++ b/public/js/publish/publish-session.js @@ -4,6 +4,10 @@ const DatabusUris = require("../utils/databus-uris"); const JsonldUtils = require("../utils/jsonld-utils"); const PublishData = require("./publish-data"); const DataIdCreator = require("./dataid-creator"); +const DatabusSparqlClient = require("./databus-sparql-client"); +const GroupHandler = require("./group-data"); +const ArtifactHandler = require("./artifact-data"); +const VersionHandler = require("./version-handler"); class PublishSession { @@ -18,60 +22,49 @@ class PublishSession { 'streamQueue' ]; - constructor($http, session, accountData) { - this.$http = $http; - this.accountData = accountData; - - for (var group of this.accountData.groups) { - group.artifacts = this.accountData.artifacts.filter(function (value) { - return value.groupName == group.name; - }); - group.hasArtifacts = group.artifacts.length > 0; - } - - this.formData = new PublishData(session != null ? session.formData : null, accountData); - this.formData.validate(); - - - this.initializeField(session, 'showContext', false); - this.initializeField(session, 'fetchFilesInput', ""); - this.initializeField(session, 'addFileInput', ""); - this.initializeField(session, 'isAccountDataLoading', true); - this.initializeField(session, 'addFileInput', ""); - this.initializeField(session, 'currentArtifact', null); - this.initializeField(session, 'currentGroup', null); + constructor($http, $interval, accounts, apiKeys) { + this.$http = $http; + this.accounts = accounts; + this.sparqlClient = new DatabusSparqlClient($http); + this.formData = new PublishData(); - this.availableGroups = []; - this.availableArtifacts = []; - this.availableVersions = []; + this.group = new GroupHandler($http, accounts, apiKeys); + this.artifact = new ArtifactHandler($http, accounts, apiKeys); + this.version = new VersionHandler($http, $interval, accounts, apiKeys); + this.reset(); + } - this.isGroupLoading = false; - this.isArtifactLoading = false; - this.isVersionLoading = false; - if (session != null) { - if (session.currentGroup != null) { - var group = accountData.groups.find(g => g.uri == session.currentGroup.uri); - this.selectGroup(group); - } + reset() { + this.accountData = {}; + this.groupData = {}; + this.artifactData = {}; + this.versionData = {}; + } - if (session.currentArtifact != null) { - var artifact = accountData.artifacts.find(a => a.uri == session.currentArtifact.uri); - this.selectArtifact(artifact); - } - } + update() { + this.validate(); + this.save(); + } + async selectAccount(account) { + this.accountData = { + name: account.name, + isValid: true + }; - this.dataIdCreator = new DataIdCreator(this.formData, this.accountData.accountName); - this.inputs = this.dataIdCreator.createInputs(); + // Fetch groups for account here: + this.groups = await this.sparqlClient.getGroups(this.accountData.name); + + this.save(); } - selectGroup(targetGroup) { + async selectGroup(targetGroup) { if (targetGroup == null) { return; @@ -95,6 +88,15 @@ class PublishSession { } } + createNewGroup() { + this.formData.group.name = ""; + this.formData.group.title = ""; + this.formData.group.abstract = ""; + this.formData.group.description = ""; + + this.save(); + } + selectArtifact(targetArtifact) { if (targetArtifact == null) { return; @@ -149,7 +151,7 @@ class PublishSession { var contentVariantGraphs = JsonldUtils.getTypedGraphs(versionData, DatabusUris.RDF_PROPERTY); - for(var contentVariantGraph of contentVariantGraphs) { + for (var contentVariantGraph of contentVariantGraphs) { var variantName = DatabusUtils.uriToName(contentVariantGraph[DatabusUris.JSONLD_ID]); self.formData.addContentVariant(variantName); @@ -163,7 +165,7 @@ class PublishSession { var fileUri = JsonldUtils.getProperty(fileGraph, DatabusUris.DCAT_DOWNLOAD_URL); - + var file = { id: fileUri, @@ -174,11 +176,11 @@ class PublishSession { contentVariants: {} } - for(var contentVariant of version.contentVariants) { + for (var contentVariant of version.contentVariants) { var variantUri = `${DatabusUris.DATABUS_CONTENT_VARIANT_PREFIX}${contentVariant.id}`; var variantValue = JsonldUtils.getProperty(fileGraph, variantUri); - if(variantValue != null) { + if (variantValue != null) { file.contentVariants[contentVariant.id] = variantValue; } } @@ -297,8 +299,18 @@ class PublishSession { } save() { + + let data = { + accountData: this.accountData, + groupData: this.groupData, + artifactData: this.artifactData, + versionData: this.versionData, + formData: this.formData, + } + + try { - var sessionDataString = JSON.stringify(this, function (key, value) { + var sessionDataString = JSON.stringify(data, function (key, value) { if (PublishSession.sessionStorageIgnoreKeys.includes(key)) { return undefined; } @@ -311,15 +323,15 @@ class PublishSession { } } - static resume($http, accountData) { + static resume($http, sub, accountData) { var sessionData = JSON.parse(window.sessionStorage.getItem(PublishSession.sessionStorageKey)); - if (sessionData == null || sessionData.accountData == null) { + if (sessionData == null || sessionData.sub == null) { return null; } - if (sessionData.accountData.accountName != accountData.accountName) { + if (sub != sessionData.sub) { return null; } @@ -329,7 +341,7 @@ class PublishSession { } onChange() { - this.formData.validate(); + this.validate(); this.inputs = this.dataIdCreator.createInputs(); this.save(); @@ -343,6 +355,86 @@ class PublishSession { !this.formData.files.errors.length > 0; } } + + onChangeGroup() { + + let group = this.formData.group; + + group.errors = []; + group.warnings = []; + + if (!DatabusUtils.isValidGroupName(group.name)) { + group.errors.push('err_invalid_group_name'); + } + + var existingGroup = this.groups.filter(function (value) { + return value.name == self.group.name; + }); + + if (existingGroup.length > 0 && group.mode == 'create') { + group.warnings.push('warning_group_exists'); + } + + this.save(); + } + + getValidString(value) { + if (value == undefined || value.length == 0) { + return undefined; + } + + return value; + } + + updateGroupBody() { + var accountUri = `${DATABUS_RESOURCE_BASE_URL}/${this.accountData.name}`; + + this.groupBody = { + "@context": this.getContext(), + "@graph": [ + { + "@id": `${accountUri}/${this.formData.group.name}`, + "@type": "Group", + "title": this.getValidString(this.formData.group.title), + "abstract": this.getValidString(this.formData.group.abstract), + "description": this.getValidString(this.formData.group.description) + } + ] + }; + } + + getContext() { + if (DATABUS_CONTEXT_URL != undefined && DatabusUtils.isValidHttpUrl(DATABUS_CONTEXT_URL)) { + return DATABUS_CONTEXT_URL; + } + + return DATABUS_CONTEXT[DatabusUris.JSONLD_CONTEXT]; + } + + onChangeArtifact() { + + let artifact = this.formData.artifact; + + artifact.errors = []; + artifact.warnings = []; + + if (!DatabusUtils.isValidArtifactName(artifact.name)) { + artifact.errors.push('err_invalid_artifact_name'); + } + + if (this.artifacts != null) { + var existingArtifact = this.artifacts.filter(function (value) { + return value.name == self.group.name; + }); + + if (existingArtifact.length > 0 && artifact.mode == 'create') { + artifact.warnings.push('warning_group_exists'); + } + } + + this.save(); + + } } module.exports = PublishSession; diff --git a/public/js/publish/publish-wizard-strings.js b/public/js/publish/publish-wizard-strings.js new file mode 100644 index 00000000..e69de29b diff --git a/public/js/publish/version-handler.js b/public/js/publish/version-handler.js new file mode 100644 index 00000000..4b6ecd71 --- /dev/null +++ b/public/js/publish/version-handler.js @@ -0,0 +1,594 @@ +const EntityHandler = require('./entity-handler'); +const DatabusUtils = require('../utils/databus-utils'); +const DatabusUris = require('../utils/databus-uris'); +const GroupData = require('./group-data'); + +class VersionHandler extends EntityHandler { + constructor($http, $interval, accounts, apiKeys) { + super('databus_registration_version_data', $http, $interval, accounts, apiKeys); + } + + initialize(data) { + const validAccount = data && this.accounts.some(acc => acc.accountName === data.accountName); + + if (validAccount) { + Object.assign(this, data); + } else { + this.accountName = this.accounts[0]?.name; + } + + if (this.apiKeyName == null && this.apiKeys != null && this.apiKeys.length > 0) { + this.apiKeyName = this.apiKeys[0].keyname; + } + + this.pageIndex ??= 0; + this.sendmode ??= 'register'; + this.files ??= []; + this.contentVariants ??= []; + + if (!this.contentVariants.some(v => v.id == 'formatExtension')) { + this.contentVariants.push({ + label: 'Format', + id: 'formatExtension', + custom: false + }); + } + + if (!this.contentVariants.some(v => v.id == 'compression')) { + this.contentVariants.push({ + label: 'Compression', + id: 'compression', + custom: false + }); + } + + let self = this; + + this.$interval(function () { + if (self.hasLicenseQueryChanged) { + + self.$http.get(`/app/publish-wizard/licenses?limit=30&keyword=${self.licenseQuery}`) + .then(function (response) { + self.filteredLicenseList = response.data.results.bindings; + }); + + self.hasLicenseQueryChanged = false; + } + + }, 300); + + this.licenseQuery = ""; + this.filterLicenses(); + this.onAccountNameChanged(); + this.onGroupNameChanged(); + this.onArtifactNameChanged(); + } + + getURI() { + return `${DATABUS_RESOURCE_BASE_URL}/${this.accountName}/${this.groupName}/${this.artifactName}/${this.name}`; + } + + setLicense(license) { + this.license = license; + this.onChange(); + } + + filterLicenses() { + this.hasLicenseQueryChanged = true; + } + + validate() { + this.errors = []; + this.warnings = []; + + if (!DatabusUtils.isValidVersionIdentifier(this.name)) { + this.errors.push('err_invalid_version_name'); + } + + if (!DatabusUtils.isValidGroupName(this.groupName)) { + this.errors.push('err_no_group_selected'); + } + + if (!DatabusUtils.isValidArtifactName(this.artifactName)) { + this.errors.push('err_no_artifact_selected'); + } + + if (!DatabusUtils.isValidResourceLabel(this.title)) { + this.errors.push('err_invalid_version_title'); + } + + if (!DatabusUtils.isValidResourceText(this.abstract, 1)) { + this.errors.push('err_invalid_version_abstract'); + } + + if (!DatabusUtils.isValidResourceText(this.description, 1)) { + this.errors.push('err_invalid_version_description'); + } + + if (!DatabusUtils.isValidUrl(this.license)) { + this.errors.push('err_invalid_version_license'); + } + + if (this.files.length == 0) { + this.errors.push('err_no_files'); + } + + for (let file of this.files) { + file.errors = []; + } + + this.fileErrors = []; + + this.cvSplit(this.files, 0); + + for (let file of this.files) { + for (let error of file.errors) { + this.errors.push(error); + this.fileErrors.push(error); + } + } + + const exists = this.artifactList?.some(a => a.name === this.name); + if (exists) { + this.warnings.push('warning_artifact_exists'); + } + } + + getSaveData() { + return { + accountName: this.accountName, + groupName: this.groupName, + artifactName: this.artifactName, + name: this.name, + title: this.title, + abstract: this.abstract, + description: this.description, + sendmode: this.sendmode, + apiKeyName: this.apiKeyName, + wasDerivedFrom: this.wasDerivedFrom, + attribution: this.attribution, + license: this.license, + pageIndex: this.pageIndex, + contentVariants: this.contentVariants, + files: this.files + }; + } + + + async setGroupName(groupName) { + if (this.groupName !== groupName) { + this.groupName = groupName; + await this.onGroupNameChanged(); + } + } + + async onGroupNameChanged() { + this.isLoadingArtifacts = true; + this.artifactList = await this.sparqlClient.getArtifacts(this.accountName, this.groupName); + this.isLoadingArtifacts = false; + this.onChange(); + } + + async setArtifactName(artifactName) { + if (this.artifactName !== artifactName) { + this.artifactName = artifactName; + await this.onArtifactNameChanged(); + } + } + + async onArtifactNameChanged() { + this.isLoadingVersions = true; + this.versionList = await this.sparqlClient.getVersions(this.accountName, this.groupName, this.artifactName); + this.isLoadingVersions = false; + this.onChange(); + } + + + updateOutputs() { + + const artifactUri = `${DATABUS_RESOURCE_BASE_URL}/${GroupData.getStringOrMissing(this.accountName)}/${GroupData.getStringOrMissing(this.groupName)}/${GroupData.getStringOrMissing(this.artifactName)}`; + let versionUri = `${artifactUri}/${GroupData.getStringOrMissing(this.name)}`; + + let graph = { + "@id": versionUri, + "@type": "Version", + "title": this.getValidString(this.title), + "abstract": this.getValidString(this.abstract), + "description": this.getValidString(this.description), + "license": this.getValidString(this.license), + "attribution": this.getValidString(this.attribution), + "wasDerivedFrom": this.getValidString(this.wasDerivedFrom), + } + + graph.distribution = []; + + + let customVariants = []; + + for (var fg in this.files) { + + var file = this.files[fg]; + + var variantSuffix = ''; + for (var c in this.contentVariants) { + var cv = this.contentVariants[c]; + var value = file.contentVariants[cv.id]; + + if (value == undefined || value == "") { + continue; + } + + variantSuffix += '_' + cv.id + '=' + value; + } + + let fileName = this.artifactName; + + var distributionUri = `${versionUri}#${fileName}`; + var fileUri = `${versionUri}/${fileName}${variantSuffix}`; + + distributionUri += variantSuffix; + + let formatExtension = this.getValidString(file.contentVariants['formatExtension']); + + if (formatExtension == undefined) { + formatExtension = 'none'; + } + + if (formatExtension != 'none') { + distributionUri += '.' + formatExtension; + fileUri += '.' + formatExtension; + } + + let compression = this.getValidString(file.contentVariants['compression']); + + if (compression == undefined) { + compression = 'none'; + } + + if (compression != 'none') { + distributionUri += '.' + compression; + fileUri += '.' + compression; + } + + var distribution = { + "@type": "Part", + "formatExtension": formatExtension, + "compression": compression, + "downloadURL": file.uri, + "byteSize": file.byteSize, + "sha256sum": file.sha256sum, + }; + + for (var c in this.contentVariants) { + var cv = this.contentVariants[c]; + + if (!cv.custom) { + continue; + } + + var value = file.contentVariants[cv.id]; + + if (value == undefined || value == "") { + continue; + } + + distribution['dcv:' + cv.label] = value; + + if (!customVariants.includes(cv.id)) { + customVariants.push(cv.id); + } + } + + graph.distribution.push(distribution); + } + + + + + this.postBody = { + "@context": this.getContext(), + "@graph": [ + graph + ] + }; + + + + const payload = JSON.stringify(this.postBody, null, 2); + const apiKey = this.apiKeys?.find(k => k.keyname === this.apiKeyName)?.apikey; + + this.curlCommand = [ + `curl -X POST ${DATABUS_RESOURCE_BASE_URL}/api/register \\`, + ` -H "X-API-KEY: ${GroupData.getStringOrMissing(apiKey)}" \\`, + ` -H "Content-Type: application/json" \\`, + ` -d '${payload}'` + ].join('\n'); + } + + createVersionName(v) { + if (v == 0) { + this.name = new Date().toISOString().slice(0, 10); + } + + if (v == 1) { + this.name = new Date().toISOString().slice(0, 13); + } + + this.onChange(); + } + + changePage(diff) { + this.pageIndex = Math.max(0, this.pageIndex + diff); + this.onChange(); + }; + + addContentVariant(variant) { + + if (variant == undefined || variant == '') { + return; + } + + if (this.contentVariants == undefined) { + this.contentVariants = []; + } + + for (var c in this.contentVariants) { + if (this.contentVariants[c].id == variant) { + return; + } + } + + this.contentVariants.push({ + label: variant, + id: DatabusUtils.uuidv4(), + fillRegex: '', + toLower: true, + pruneWhitespaces: true, + custom: true, + }); + + this.onChange(); + } + + + removeContentVariant(variant) { + this.contentVariants = this.contentVariants.filter(function (d) { + return d.id != variant.id; + }); + + for (var f in this.files) { + var file = this.files[f]; + delete file.contentVariants[variant.id]; + } + + this.editContentVariant = null; + this.onChange(); + } + + addFiles(input) { + var lines = input.split('\n'); + for (var line of lines) { + if (line != undefined && line.length > 0) { + this.addFile(line); + } + } + } + + addFile(file) { + + if (typeof file === 'string') { + file = { url: file }; + } + + if (this.files == undefined) { + this.files = []; + } + + // Check if already added + for (var f in this.files) { + if (file.url == this.files[f].url) { + return; + } + } + + var uri = file.url; + var uriParts = uri.split('/'); + var name = uriParts.pop(); + var nameComponents = name.split('.'); + name = nameComponents[0]; + + if (name.length > 50) { + name = name.substr(0, 50) + '...'; + } + + name = decodeURIComponent(name); + // Files with uri as key!! + + this.files.push({ + id: uri, + uri: file.url, + name: name, + contentVariants: file.contentVariants != null ? file.contentVariants : {}, + rowspan: 1, + }); + + this.files.sort(function (a, b) { + var nameA = a.name; + var nameB = b.name; + + if (nameA < nameB) { + return -1; + } + if (nameA > nameB) { + return 1; + } + + return 0; + }); + + let k = 1; + + for (let file of this.files) { + file.rowIndex = k++; + } + + this.onChange(); + } + + removeFile = function (file, index) { + this.files.splice(index, 1); + this.onChange(); + } + + + fill(variant) { + + var val = variant.fillRegex; + + for (var file of this.files) { + + if (variant.toLower) { + val = val.toLowerCase(); + } + + if (variant.pruneWhitespaces) { + val = val.replaceAll(' ', ''); + } + + if (!variant.overwrite && file.contentVariants[variant.id] != undefined + && file.contentVariants[variant.id].length > 0) { + continue; + } + + file.contentVariants[variant.id] = val; + } + + this.onChange(); + } + + fillByRegex(variant) { + var regex = new RegExp(variant.fillRegex); + + for (var file of this.files) { + var matches = file.name.match(regex); + + if (matches != null) { + var val = matches[0]; + + if (variant.toLower) { + val = val.toLowerCase(); + } + + if (variant.pruneWhitespaces) { + val = val.replaceAll(' ', ''); + } + + if (!variant.overwrite && file.contentVariants[variant.id] != undefined + && file.contentVariants[variant.id].length > 0) { + continue; + } + + file.contentVariants[variant.id] = val; + } + } + + this.onChange(); + } + + getRowIndex(files, name) { + var k = 1; + for (var f in files) { + if (files[f].name == name) { + return k; + } + + k++; + } + + return -1; + } + + + cvSplit(files, cvIndex) { + + if (files.length <= 1) { + return; + } + + if (this.contentVariants == undefined) { + this.contentVariants = []; + } + // if end of cvs, assign errors to all files if files.length > 1 + if (cvIndex - 2 >= this.contentVariants.length) { + + if (files.length > 1) { + + var cvHints = []; + + if (this.contentVariants.length == 0) { + cvHints.push('No content variants have been added yet. Add content variants in the files panel in order to tag your files.'); + } else { + for (var c in this.contentVariants) { + var cv = this.contentVariants[c]; + var value = files[0].contentVariants[cv.id]; + + if (value == undefined || value == '') { + value = 'none'; + } + + cvHints.push(cv.id + ': ' + value); + } + } + + for (let file of files) { + + var errorMessage = 'Row ' + file.rowIndex + ' (' + + cvHints.join(', ') + ').'; + + file.errors.push({ key: 'err_duplicate_file', message: errorMessage }); + } + } + + return; + } + + // else create buckets and sort files into buckets + var buckets = {}; + + for (var f in files) { + var file = files[f]; + + var key = null; + + if (cvIndex == 0) { + key = file.formatExtension; + } else if (cvIndex == 1) { + key = file.compression; + } else { + key = file.contentVariants[this.contentVariants[cvIndex - 2].id]; + } + + if (key == undefined || key == '') { + key = '$_none$'; + } + + if (buckets[key] == undefined) { + buckets[key] = []; + } + + buckets[key].push(file); + } + + // iterate buckets and call recursively + for (var b in buckets) { + this.cvSplit(buckets[b], cvIndex + 1); + } + } + + onEditContentVariant(index) { + this.editContentVariant = this.contentVariants[index]; + } + +} + +module.exports = VersionHandler; diff --git a/public/js/search/search-manager.js b/public/js/search/search-manager.js index 79ba18c1..e5ea9fea 100644 --- a/public/js/search/search-manager.js +++ b/public/js/search/search-manager.js @@ -59,12 +59,16 @@ class SearchManager { return; } + /* + var options = { method: 'GET', - url: `/${ auth.info.accountName } `, + url: `/${ auth.info.accountName }`, headers: { 'Accept': 'application/ld+json', - 'X-Jsonld-Formatting': 'flatten' + 'X-Jsonld-Formatting': 'flatten', + 'Cache-Control': 'no-cache', + 'Pragma': 'no-cache' } } @@ -82,7 +86,7 @@ class SearchManager { this.searchExtensions.push(searchExtension); - } + })*/ } } diff --git a/public/js/utils/app-json-formatter.js b/public/js/utils/app-json-formatter.js index d02193da..6915e234 100644 --- a/public/js/utils/app-json-formatter.js +++ b/public/js/utils/app-json-formatter.js @@ -8,48 +8,109 @@ const JsonldUtils = require("./jsonld-utils"); */ class AppJsonFormatter { - static createAccountData(resourceBaseUrl, accountName, accountLabel, accountStatus, accountImage) { + static async createAccountGraphs(uri, name, label, img, secretaries, status) { + var name = UriUtils.uriToName(uri); + + var rsaKeyGraph = {}; + rsaKeyGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.CERT_RSA_PUBLIC_KEY; + rsaKeyGraph[DatabusUris.RDFS_LABEL] = DatabusConstants.WEBID_SHARED_PUBLIC_KEY_LABEL; + rsaKeyGraph[DatabusUris.CERT_MODULUS] = signer.getModulus(); + rsaKeyGraph[DatabusUris.CERT_EXPONENT] = 65537; + + var personUri = `${uri}${DatabusConstants.WEBID_THIS}`; - var accountUri = `${resourceBaseUrl}/${accountName}`; - var profileUri = `${resourceBaseUrl}/${accountName}${DatabusConstants.WEBID_DOCUMENT}`; - var personUri = `${resourceBaseUrl}/${accountName}${DatabusConstants.WEBID_THIS}`; + var personGraph = {}; + personGraph[DatabusUris.JSONLD_ID] = personUri; + personGraph[DatabusUris.JSONLD_TYPE] = [ DatabusUris.FOAF_PERSON, DatabusUris.DBP_DBPEDIAN ]; + personGraph[DatabusUris.FOAF_ACCOUNT] = JsonldUtils.refTo(uri); + personGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY] = uri; + personGraph[DatabusUris.CERT_KEY] = [ rsaKeyGraph ]; + personGraph[DatabusUris.FOAF_NAME] = label; + + if(img != null) { + personGraph[DatabusUris.FOAF_IMG] = img; + } + + if(status != null) { + personGraph[DatabusUris.FOAF_STATUS] = status; + } + + var profileUri = `${uri}${DatabusConstants.WEBID_DOCUMENT}`; + + var profileDocumentGraph = {}; + profileDocumentGraph[DatabusUris.JSONLD_ID] = profileUri; + profileDocumentGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.FOAF_PERSONAL_PROFILE_DOCUMENT; + profileDocumentGraph[DatabusUris.FOAF_MAKER] = JsonldUtils.refTo(personUri); + profileDocumentGraph[DatabusUris.FOAF_PRIMARY_TOPIC] = JsonldUtils.refTo(personUri); + + var accountGraph = {} + accountGraph[DatabusUris.JSONLD_ID] = uri; + accountGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.DATABUS_ACCOUNT; + accountGraph[DatabusUris.FOAF_ACCOUNT_NAME] = name; + accountGraph[DatabusUris.DATABUS_NAME] = name; + + if(secretaries != null) { + + accountGraph[DatabusUris.DATABUS_SECRETARY_PROPERTY] = []; + + for(var secretary of secretaries) { + + let secretaryAccountUri = `${secretary.accountName}`; + + let secretaryGraph = {}; + secretaryGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.DATABUS_SECRETARY; + secretaryGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY] = JsonldUtils.refTo(secretaryAccountUri); + + if(secretary.hasWriteAccessTo != undefined) { + secretaryGraph[DatabusUris.DATABUS_HAS_WRITE_ACCESS_TO] = []; + + for(var writeAccess of secretary.hasWriteAccessTo) { + secretaryGraph[DatabusUris.DATABUS_HAS_WRITE_ACCESS_TO].push(JsonldUtils.refTo(writeAccess)); + } + } + + accountGraph[DatabusUris.DATABUS_SECRETARY_PROPERTY].push(secretaryGraph); + } + } + + let expandedGraphs = [ + accountGraph, + personGraph, + profileDocumentGraph + ]; + + return await jsonld.compact(expandedGraphs, JsonldLoader.DEFAULT_CONTEXT_URL); + } + + static createAccountData(accountUri, accountLabel, accountStatus, accountImage) { + + var personUri = `${accountUri}${DatabusConstants.WEBID_THIS}`; var accountJsonLd = {}; + var accountGraph = {}; + accountGraph[DatabusUris.JSONLD_ID] = accountUri; + accountGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.DATABUS_ACCOUNT; + var personGraph = {}; personGraph[DatabusUris.JSONLD_ID] = personUri; - personGraph[DatabusUris.JSONLD_TYPE] = [ - DatabusUris.FOAF_PERSON, - DatabusUris.DBP_DBPEDIAN - ]; + personGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.FOAF_PERSON; personGraph[DatabusUris.FOAF_NAME] = accountLabel; + personGraph[DatabusUris.FOAF_ACCOUNT] = JsonldUtils.refTo(accountUri); if (accountStatus != null) { personGraph[DatabusUris.FOAF_STATUS] = accountStatus; } - personGraph[DatabusUris.FOAF_ACCOUNT] = {}; - personGraph[DatabusUris.FOAF_ACCOUNT][DatabusUris.JSONLD_ID] = accountUri; - if (accountImage != null) { - personGraph[DatabusUris.FOAF_IMG] = {}; - personGraph[DatabusUris.FOAF_IMG][DatabusUris.JSONLD_ID] = accountImage; + personGraph[DatabusUris.FOAF_IMG] = JsonldUtils.refTo(accountImage); } - var profileGraph = {}; - profileGraph[DatabusUris.JSONLD_ID] = profileUri; - profileGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.FOAF_PERSONAL_PROFILE_DOCUMENT; - profileGraph[DatabusUris.FOAF_PRIMARY_TOPIC] = {}; - profileGraph[DatabusUris.FOAF_PRIMARY_TOPIC][DatabusUris.JSONLD_ID] = personUri; - profileGraph[DatabusUris.FOAF_MAKER] = {}; - profileGraph[DatabusUris.FOAF_MAKER][DatabusUris.JSONLD_ID] = personUri; - accountJsonLd[DatabusUris.JSONLD_GRAPH] = [ - personGraph, - profileGraph + return [ + accountGraph, + personGraph ]; - - return accountJsonLd; } static formatGroupData(graphs) { @@ -84,12 +145,14 @@ class AppJsonFormatter { static formatAccountData(graphs) { var result = {}; - var profileGraph = JsonldUtils.getTypedGraph(graphs, DatabusUris.FOAF_PERSONAL_PROFILE_DOCUMENT); + var accountGraph = JsonldUtils.getTypedGraph(graphs, DatabusUris.DATABUS_ACCOUNT); var personGraph = JsonldUtils.getTypedGraph(graphs, DatabusUris.FOAF_PERSON); - result.uri = profileGraph[DatabusUris.JSONLD_ID]; + result.uri = accountGraph[DatabusUris.JSONLD_ID]; result.accountName = DatabusUtils.uriToResourceName(result.uri); - result.label = JsonldUtils.getProperty(personGraph, DatabusUris.FOAF_NAME); + result.label = JsonldUtils.getFirstProperty(personGraph, DatabusUris.FOAF_NAME); + result.imageUrl = JsonldUtils.getFirstProperty(personGraph, DatabusUris.FOAF_IMG); + result.about = JsonldUtils.getFirstProperty(personGraph, DatabusUris.FOAF_STATUS); result.webIds = []; result.searchExtensions = []; @@ -113,6 +176,30 @@ class AppJsonFormatter { } } + result.secretaries = []; + var secretaryGraphs = JsonldUtils.getTypedGraphs(graphs, DatabusUris.DATABUS_SECRETARY); + + for (var secretaryGraph of secretaryGraphs) { + var secretaryData = { + accountName: JsonldUtils.getProperty(secretaryGraph, DatabusUris.DATABUS_ACCOUNT_PROPERTY), + hasWriteAccessTo: [] + }; + + var writeAccessUris = secretaryGraph[DatabusUris.DATABUS_HAS_WRITE_ACCESS_TO]; + + if (Array.isArray(writeAccessUris)) { + for (var item of writeAccessUris) { + if (typeof item === 'object' && item['@id']) { + secretaryData.hasWriteAccessTo.push(item['@id']); + } else if (typeof item === 'string') { + secretaryData.hasWriteAccessTo.push(item); + } + } + } + + result.secretaries.push(secretaryData); + } + return result; } @@ -145,6 +232,7 @@ class AppJsonFormatter { result.description = JsonldUtils.getProperty(collectionGraph, DatabusUris.DCT_DESCRIPTION); result.issued = JsonldUtils.getProperty(collectionGraph, DatabusUris.DCT_ISSUED); result.publisher = JsonldUtils.getProperty(collectionGraph, DatabusUris.DCT_PUBLISHER); + result.account = JsonldUtils.getProperty(collectionGraph, DatabusUris.DATABUS_ACCOUNT_PROPERTY); var content = JsonldUtils.getProperty(collectionGraph, DatabusUris.DATABUS_COLLECTION_CONTENT) result.content = DatabusUtils.tryParseJson(unescape(content)); diff --git a/public/js/utils/databus-messages.js b/public/js/utils/databus-messages.js index 8155d0f1..cc046240 100644 --- a/public/js/utils/databus-messages.js +++ b/public/js/utils/databus-messages.js @@ -22,6 +22,8 @@ class DatabusMessages { static ACCOUNT_API_KEY_CREATED = 'API key created'; static ACCOUNT_WEBID_LINKED = 'External WebId has been linked to your profile'; + + } module.exports = DatabusMessages; diff --git a/public/js/utils/databus-uris.js b/public/js/utils/databus-uris.js index 34b00743..8f80cdd2 100644 --- a/public/js/utils/databus-uris.js +++ b/public/js/utils/databus-uris.js @@ -14,10 +14,19 @@ class DatabusUris { static DATABUS_PART = 'https://dataid.dbpedia.org/databus#Part'; static DATABUS_VERSION = 'https://dataid.dbpedia.org/databus#Version'; static DATABUS_GROUP = 'https://dataid.dbpedia.org/databus#Group'; + static DATABUS_ACCOUNT = 'https://dataid.dbpedia.org/databus#Account'; static DATABUS_ARTIFACT = 'https://dataid.dbpedia.org/databus#Artifact'; static DATABUS_VERSION_PROPERTY = 'https://dataid.dbpedia.org/databus#version'; static DATABUS_GROUP_PROPERTY = 'https://dataid.dbpedia.org/databus#group'; static DATABUS_ACCOUNT_PROPERTY = 'https://dataid.dbpedia.org/databus#account'; + static DATABUS_HAS_ARTIFACT = 'https://dataid.dbpedia.org/databus#hasArtifact'; + static DATABUS_HAS_VERSION = 'https://dataid.dbpedia.org/databus#hasVersion'; + static DATABUS_NAME = 'https://dataid.dbpedia.org/databus#name'; + + static DATABUS_SECRETARY_PROPERTY = 'https://dataid.dbpedia.org/databus#secretary'; + static DATABUS_SECRETARY = 'https://dataid.dbpedia.org/databus#Secretary'; + static DATABUS_HAS_WRITE_ACCESS_TO = 'https://dataid.dbpedia.org/databus#hasWriteAccessTo'; + static DATABUS_ARTIFACT_PROPERTY = 'https://dataid.dbpedia.org/databus#artifact'; static DATABUS_FORMAT = 'https://dataid.dbpedia.org/databus#format'; static DATABUS_FORMAT_EXTENSION = 'https://dataid.dbpedia.org/databus#formatExtension'; @@ -87,6 +96,7 @@ class DatabusUris { static FOAF_PERSON = 'http://xmlns.com/foaf/0.1/Person'; static FOAF_PRIMARY_TOPIC = 'http://xmlns.com/foaf/0.1/primaryTopic'; static FOAF_MAKER = 'http://xmlns.com/foaf/0.1/maker'; + static FOAF_ACCOUNT_NAME = 'http://xmlns.com/foaf/0.1/accountName'; static FOAF_IMG = 'http://xmlns.com/foaf/0.1/img'; // S4AC diff --git a/public/js/utils/databus-utils.js b/public/js/utils/databus-utils.js index ac82096f..a35d0886 100644 --- a/public/js/utils/databus-utils.js +++ b/public/js/utils/databus-utils.js @@ -2,6 +2,7 @@ const DatabusCollectionUtils = require("../collections/databus-collection-utils" var markdownit = require('markdown-it'); const moment = require("moment/moment"); const DatabusUris = require("./databus-uris"); +const ApiError = require("../../../server/app/common/utils/api-error"); class DatabusUtils { @@ -13,6 +14,23 @@ class DatabusUtils { return fallback; } + static resemblesTrue(value) { + if (typeof value === 'boolean') { + return value; + } + + if (typeof value === 'string') { + const normalized = value.trim().toLowerCase(); + return ['true', '1', 'yes', 'on'].includes(normalized); + } + + if (typeof value === 'number') { + return value === 1; + } + + return false; + } + static isValidResourceIdentifier(identifier, min) { var identifierRegex = /^[a-z-]+$/; return this.checkField(identifier, identifierRegex, min, 50); @@ -21,16 +39,16 @@ class DatabusUtils { static formatQuery(query, placeholderMappings) { if (placeholderMappings == undefined) { - return query; + return query; } - + for (var placeholder in placeholderMappings) { - var re = new RegExp('%' + placeholder + '%', "g"); - query = query.replace(re, placeholderMappings[placeholder]); + var re = new RegExp('%' + placeholder + '%', "g"); + query = query.replace(re, placeholderMappings[placeholder]); } - + return query; - } + } static isValidVersionIdentifier(identifier) { var labelRegex = /^[A-Za-z0-9_\.\-]*$/; @@ -163,7 +181,7 @@ class DatabusUtils { var result = uri.substr(uri.lastIndexOf('/') + 1); - if(result.includes('#')) { + if (result.includes('#')) { result = result.substr(0, result.indexOf('#')); } @@ -358,9 +376,18 @@ class DatabusUtils { return undefined; } + static getFirstSegment(uri) { + try { + const url = new URL(uri); + return url.pathname.split('/').filter(Boolean)[0] || null; + } catch { + return null; + } + } + static parseMarkdown(markdown) { - if(markdown == null) { + if (markdown == null) { return null; } @@ -370,7 +397,7 @@ class DatabusUtils { static renderMarkdown(markdown) { - if(markdown == null) { + if (markdown == null) { return null; } @@ -384,19 +411,19 @@ class DatabusUtils { */ static createAbstractFromDescription(description) { - if(description == null) { + if (description == null) { return null; } try { var tokens = this.parseMarkdown(description); - + var paragraphFound = false; var result = ""; - if(tokens == null) { - return result; + if (tokens == null) { + return result; } var firstParagraphText = null; @@ -504,6 +531,4 @@ class DatabusUtils { } -// export default DatabusUtils; - module.exports = DatabusUtils; diff --git a/public/js/utils/databus-webapp-utils.js b/public/js/utils/databus-webapp-utils.js index dbf6b86f..06311d0d 100644 --- a/public/js/utils/databus-webapp-utils.js +++ b/public/js/utils/databus-webapp-utils.js @@ -9,10 +9,46 @@ class DatabusWebappUtils { this.sce = $sce; } + goTo(page) { + window.location = page; + } + createAccount() { window.location = '/app/account'; } + + getAccountName() { + + let accountName = window.location.pathname.split('/')[1]; + + if(accountName.length < 4) { + return null; + } + + return this.getOwnedAccountName(accountName); + } + + getOwnedAccountName(accountName) { + if(!this.scope.auth.authenticated || this.scope.auth.info == null) { + return null; + } + + let userInfo = this.scope.auth.info; + + if(!Array.isArray(userInfo.accounts) || userInfo.accounts.length == 0) { + return null; + } + + let account = userInfo.accounts.find(a => a.accountName == accountName); + + if(account == null) { + return null; + } + + return account.accountName; + } + login() { window.location = '/app/login?redirectUrl=' + encodeURIComponent(window.location); } diff --git a/public/js/utils/jsonld-utils.js b/public/js/utils/jsonld-utils.js index 4926da81..965b47e1 100644 --- a/public/js/utils/jsonld-utils.js +++ b/public/js/utils/jsonld-utils.js @@ -3,6 +3,12 @@ const DatabusUris = require("./databus-uris"); class JsonldUtils { + static refTo(uri) { + var result = {}; + result[DatabusUris.JSONLD_ID] = uri; + return result; + } + static getTypedGraph(graphs, graphType) { for (var g in graphs) { @@ -35,6 +41,16 @@ class JsonldUtils { graph[property].push(entry); } + static getGraphById = function (graphs, id) { + return graphs.find(g => g[DatabusUris.JSONLD_ID] === id); + }; + + static getRefArrayProperty = function (graph, propertyUri) { + const val = graph[propertyUri]; + if (!val) return []; + return val.map(v => v[DatabusUris.JSONLD_ID]); + }; + static getProperty(graph, property) { if (graph[property] == undefined) { return null; @@ -74,6 +90,45 @@ class JsonldUtils { return null; } + static getFirstProperty(graph, property) { + if (graph[property] == undefined) { + return null; + } + + const values = graph[property]; + + if (values.length === 0) { + return null; + } + + if (values.length === 1) { + const value = values[0]; + + if (value[DatabusUris.JSONLD_VALUE] != null) { + return value[DatabusUris.JSONLD_VALUE]; + } + + if (value[DatabusUris.JSONLD_ID] != null) { + return value[DatabusUris.JSONLD_ID]; + } + + return null; + } + + for (const value of values) { + if (value[DatabusUris.JSONLD_VALUE] != null) { + return value[DatabusUris.JSONLD_VALUE]; + } + + if (value[DatabusUris.JSONLD_ID] != null) { + return value[DatabusUris.JSONLD_ID]; + } + } + + return null; + } + + static getGraphById(graphs, id) { for (var g in graphs) { var graph = graphs[g]; @@ -84,8 +139,6 @@ class JsonldUtils { } return null; - - } static getTypedGraphs(graphs, graphType) { @@ -133,14 +186,27 @@ class JsonldUtils { return obj[0]; } - static getFirstObjectUri(graph, key) { - var obj = graph[key]; + static getFirstObjectUri(graph, property) { + // Get the object + const obj = graph[property]; - if (obj == undefined || obj.length < 1) { + // Not found -> null + if (!obj) { return null; } - return obj[0][DatabusUris.JSONLD_ID]; + // If it is an array... + if (Array.isArray(obj)) { + for (const item of obj) { + if (item && typeof item === 'object' && DatabusUris.JSONLD_ID in item) { + return item[DatabusUris.JSONLD_ID]; + } + } + } else if (typeof obj === 'object' && DatabusUris.JSONLD_ID in obj) { + return obj[DatabusUris.JSONLD_ID]; + } + + return null; } } diff --git a/public/js/utils/messages.js b/public/js/utils/messages.js new file mode 100644 index 00000000..cbf8ff4a --- /dev/null +++ b/public/js/utils/messages.js @@ -0,0 +1,23 @@ +export class DatabusMsg { + static messages = { + err_invalid_group_name: "Please enter between 3 to 50 characters. \nRegex: [a-zA-Z0-9_\\-\\.]{3,50}$", + err_no_group_selected: "Please select a group", + err_no_artifact_selected: "Please select an artifact", + + err_invalid_artifact_name: "Please enter between 3 to 50 characters. \nRegex: [a-zA-Z0-9_\\-\\.]{3,50}$", + err_invalid_version_name: "Please enter between 3 to 50 characters. \nRegex: [a-zA-Z0-9_\\-\\.]{3,50}$", + err_invalid_version_title: "The version title is missing.", + err_invalid_version_abstract: "The version abstract is missing.", + err_invalid_version_description: "The version description is missing.", + err_invalid_version_license: "The license is invalid. Please enter a license URI.", + err_no_files: "You have to upload at least one file.", + err_not_analyzed: "This file has not been analzyed yet.", + warning_group_exists: "A group with this name already exists. Publishing will overwrite its metadata.", + warning_artifact_exists: "An artifact with this name already exists. Publishing will overwrite its metadata.", + warning_version_exists: "A version with this name already exists. Publishing will overwrite its metadata. This is not recommended, as other users might use your version identifier as a data dependency." + }; + + static get(key) { + return this.messages[key] || "Unknown validation key."; + } +} diff --git a/public/js/utils/tab-navigation.js b/public/js/utils/tab-navigation.js index 8ef980b5..26ce0703 100644 --- a/public/js/utils/tab-navigation.js +++ b/public/js/utils/tab-navigation.js @@ -1,10 +1,11 @@ class TabNavigation { - constructor($scope, $location, tabKeys) { + constructor($scope, $location, tabKeys, onNavigateCallback) { this.location = $location; this.tabKeys = tabKeys; this.activeTab = 0; + this.onNavigateCallback = onNavigateCallback; var self = this; // Watch the location hash and tell the tabnavigation that it changed @@ -21,9 +22,18 @@ class TabNavigation { var tabKey = this.tabKeys[i]; if (value == tabKey) { this.activeTab = i; + + if(this.onNavigateCallback != null) { + this.onNavigateCallback(this.activeTab); + } return; } } + + this.activeTab = 0; + if(this.onNavigateCallback != null) { + this.onNavigateCallback(this.activeTab); + } } /** diff --git a/public/templates/404.ejs b/public/templates/404.ejs index 1a959725..807ba4b5 100644 --- a/public/templates/404.ejs +++ b/public/templates/404.ejs @@ -1,6 +1,6 @@ <%- include('header') -%>
-
+
diff --git a/public/templates/account.ejs b/public/templates/account.ejs index c7cc4021..7b0f1531 100644 --- a/public/templates/account.ejs +++ b/public/templates/account.ejs @@ -37,15 +37,15 @@ d="M19 7.001c0 3.865-3.134 7-7 7s-7-3.135-7-7c0-3.867 3.134-7.001 7-7.001s7 3.134 7 7.001zm-1.598 7.18c-1.506 1.137-3.374 1.82-5.402 1.82-2.03 0-3.899-.685-5.407-1.822-4.072 1.793-6.593 7.376-6.593 9.821h24c0-2.423-2.6-8.006-6.598-9.819z" />
-

{{ profileData.label }}

- +

{{ account.label }}

+
-
+
@@ -57,8 +57,7 @@ ng-click="tabNavigation.navigateTo('collections');">
-
-
-
-
-

Unlock your account by entering a username (your publishing namespace) - and clicking the - button below.
Choose your username carefully as you will not be able to change it later.

-

-
-
-
- -
- -
-
-
- {{ createAccountError }} -
    -
  • Between 4 and 15 characters
  • -
  • Starts and ends with a lowercase letter
  • -
  • Only lowercase letters, numbers, underscores and dashes allowed
  • -
-
- -
-
-
+
+ +
+ <%- include('footer') -%> \ No newline at end of file diff --git a/public/templates/publish-wizard.ejs b/public/templates/publish-wizard.ejs index b54a9aba..a3dffb24 100644 --- a/public/templates/publish-wizard.ejs +++ b/public/templates/publish-wizard.ejs @@ -4,964 +4,697 @@
+ + .form-column:not(:first-child) { + border-left: 1px solid rgb(225 228 232); + } -
+ .wiz-container { + max-width: 1600px; + margin: auto; + } -
- - -
+ .menu-card { + border: 1px solid rgb(225 228 232); + background-color: white; + border-radius: 12px; + padding: 2rem; + width: 240px; + text-align: center; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); + cursor: pointer; + transition: all 0.2s ease; + } -
+ .menu-card:hover { + background-color: #f9f9f9; + } + .dropdown { + margin-bottom: 1rem; + } + .topbar { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 1rem; + margin-bottom: 0.5rem + } - -
-
-

You need to be logged in to publish data.

-
-

Don't have an account yet? Create one now!

-

-
- -
-
-
-
-
-

You need to create a user account in order to publish data.

-
-
- -
-
-
+ .form-layout { + display: flex; + flex-wrap: wrap; + gap: 2rem; + min-height: 500px; + padding: 1rem 0; + } - -
-
-
-
-
-
-
-
-
-
- -
-
-
-
-

Publish Wizard

-

Nerd mode. You know what to do. You can send JSON bodies to the publish API directly and choose a log level.

-
-
PUBLISH
-
- -
-
-
JSON Body
-
- -
-
-
-
Log Level
-
- -
-
+ .form-panel, + .jsonld-panel { + border: 1px solid #dbdbdb; + background-color: white; + min-height: 460px; + } + .form-panel .field .label { + text-transform: uppercase; + margin-left: 4px; + color: #888; + font-size: 0.9em; + letter-spacing: 0.1em; + font-weight: 500; + margin-bottom: 2px; + } -
-
-
- - -
-

- -

-
-
-
-
+ .form-panel .field { + margin-bottom: 1.5em; + } + .jsonld-panel pre { + background: #f9f9f9; + border-radius: 8px; + padding: 1rem; + white-space: pre-wrap; + overflow-x: auto; + max-height: 300px; + } -
-
- -
-

Publish Wizard

-

This form will help you set up a DataId. A DataId is a metadata document containing - important information about your files. This metadata document will make your data more understandable and - reusable for you and other users.

-
+ .background { + background-color: #fafbfe; + } - -
GROUP INFO
-
+ .code-block { + font-family: monospace; + background: #f5f5f5; + overflow: hidden; + margin-top: 1em; + } -
-
- - -
-
- - - -
-
-
-
-
Group Metadata
- -
- - -
+ .code-block-header { + display: flex; + justify-content: right; + align-items: center; + background: #f5f5f5; + padding-top: 0.5em; + padding-right: 0.5em; + font-size: 0.9em; + } -
-
-
Name
-
- -
-
-
-
Name
-
- - {{ - texts.errors['err_invalid_group_title'] }} - {{ - texts.errors['warning_group_exists'] }} -
-
-
-
Title
-
- -
-
-
-
Abstract
-
-
-
- - -
- +
+
+
+
+
You need to create an account in order to register metadata
+ +
+
+
+
+
+ + + + +
+
+
+ +
+
+
+
+
+

Group Info

+
+ + + +
+
+ + + + +
+
+ + +
+
+ + +
+
+ +
-
-
Description
-
- -
+
+

API Input

+
+
+
-
+ +
+
+
+ +
+
+
+
+
+

Artifact Info

- -
ARTIFACT INFO
-
-
-
- - -
-
- - -
-
    -
  • - {{ texts.errors[error] }} -
  • -
-
-
-
- - -
-
    -
  • {{ hint }}
  • -
-
-
-
-
-
-
-
Artifact Metadata
-
- - - -
+
+ + +
-
-
Name
-
-
-
-
Name
-
- - {{ - texts.errors['err_invalid_artifact_title'] }} - {{ - texts.errors['warning_artifact_exists'] }} -
-
-
-
Title
-
- - {{ - texts.errors['err_invalid_artifact_label'] }} -
-
- -
-
Abstract
-
-
-
- - -
- -
-
-
- -
-
Description
-
- - {{ - texts.errors['err_invalid_group_description'] }} -
+ +
+ + +
-
-
-
- -
VERSION INFO
-
+
+ + +
-
+
+ + +
-
- - -
-
- - -
- -
- - -
-
+
+ + +
-
-
Version Metadata
-
- - -
- -
-
Name
-
- - {{ - texts.errors['warning_version_exists'] }} - {{ - texts.errors['err_invalid_version_name'] }} -
+
+

API Input

+
+
+
+
+
-
-
Name
-
-
-
<%- include('footer') -%> \ No newline at end of file diff --git a/public/templates/sparql-editor.ejs b/public/templates/sparql-editor.ejs index cc48bffa..5559f452 100644 --- a/public/templates/sparql-editor.ejs +++ b/public/templates/sparql-editor.ejs @@ -1,5 +1,10 @@ <%- include('header') -%> + +
@@ -23,26 +28,26 @@
-
- -
+
+ +
-
- - -
+ + +
-
- + - -
+ +
@@ -55,22 +60,33 @@ margin-top: 2em; "> + ng-change="saveToStorage()" style="width: 600px;" placeholder="SPARQL endpoint URL"> - +
- + + + +
+
+

{{ queryData.pages[queryData.activeTab].err.statusText }}

+
{{ queryData.pages[queryData.activeTab].err.data }}
+
- +
+ + +
<%- include('footer') -%> \ No newline at end of file diff --git a/public/templates/unauthorized.ejs b/public/templates/unauthorized.ejs index 99803cc1..19d97e54 100644 --- a/public/templates/unauthorized.ejs +++ b/public/templates/unauthorized.ejs @@ -1,9 +1,9 @@ <%- include('header') -%>
-
+
-
+
@@ -14,7 +14,7 @@
-

401: Unauthorized

+

<%= data.code %>: <%= data.message %>

@@ -27,7 +27,7 @@


-

You are not allowed to view this page. Please log in to your Databus account.

+

<%= data.description %>

diff --git a/public/templates/user-settings.ejs b/public/templates/user-settings.ejs new file mode 100644 index 00000000..1ab7cef5 --- /dev/null +++ b/public/templates/user-settings.ejs @@ -0,0 +1,276 @@ +<%- include('header') -%> + +
+ + + + + +
+
+
+
+
+

User Settings

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

Accounts

+ +
+ +
+
+
Name
+
+ +
+
+ +
+
Label
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+

You don't have any accounts yet.

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

Edit Account: {{activeAccount.accountName}}

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

API Keys

+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+
+
+
+ + +
+ + <%- include('footer') -%> \ No newline at end of file diff --git a/public/templates/variables.ejs b/public/templates/variables.ejs new file mode 100644 index 00000000..c0afff39 --- /dev/null +++ b/public/templates/variables.ejs @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/search/Dockerfile b/search/Dockerfile deleted file mode 100644 index eda7c9d5..00000000 --- a/search/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM docker.io/tomcat:9.0.35-jdk11-openjdk - -COPY ./lookup-application.war /usr/local/tomcat/webapps/ -ENTRYPOINT [ "/usr/local/tomcat/bin/catalina.sh" ] -CMD [ "run" ] diff --git a/search/docker-compose.yml b/search/docker-compose.yml deleted file mode 100644 index c960d971..00000000 --- a/search/docker-compose.yml +++ /dev/null @@ -1,10 +0,0 @@ -version: "3.0" -services: - lookup-servlet: - #image: lookup-servlet - build: . - ports: - - 8080:8080 - volumes: - - ./app-config-servlet.yml:/root/app-config.yml - - ./index:/databus/search/index diff --git a/search/gstore-helper.js b/search/gstore-helper.js deleted file mode 100644 index 26849273..00000000 --- a/search/gstore-helper.js +++ /dev/null @@ -1,72 +0,0 @@ -var rp = require('request-promise'); -const Constants = require('../constants'); -const DatabusMessage = require('../databus-message'); - -var prefix = encodeURIComponent(`${process.env.DATABUS_RESOURCE_BASE_URL}/`); - - -class GstoreHelper { - - - - static async read(repo, path) { - - let options = { - url: `${process.env.DATABUS_DATABASE_URL}/graph/read?repo=${repo}&path=${path}`, - headers: { - 'Accept': 'application/ld+json' - }, - json: true - }; - - try { - var res = await rp.get(options); - return res; - - } catch (err) { - return null; - } - } - - static async save(repo, path, content) { - - - try { - var options = { - uri: `${process.env.DATABUS_DATABASE_URL}/graph/save?repo=${repo}&prefix=${prefix}&path=${path}`, - body: content, - json: true - }; - - await rp.post(options); - - return { isSuccess: true }; - } catch (err) { - console.log(err); - return { isSuccess: false }; - } - - } - - static async delete(repo, path) { - - try { - - var uri = `${process.env.DATABUS_DATABASE_URL}/graph/delete?repo=${repo}&prefix=${prefix}&path=${path}`; - var res = await rp.delete(uri); - - process.send({ - id: DatabusMessage.REQUEST_SEARCH_INDEX_REBUILD - }); - - return { isSuccess: true }; - } catch (err) { - console.log(err); - return { isSuccess: false }; - } - - } - -} - -module.exports = GstoreHelper; \ No newline at end of file diff --git a/search/index-config.yml b/search/index-config.yml index f116a051..c926dfde 100644 --- a/search/index-config.yml +++ b/search/index-config.yml @@ -34,6 +34,34 @@ indexFields: } %VALUES% } + - fieldName: abstract + resourceName: resource + query: > + SELECT DISTINCT ?resource ?abstract WHERE { + { + GRAPH ?g1 { + ?resource a . + } + GRAPH ?g2 { + ?resource ?abstract. + } + } + %VALUES% + } + - fieldName: abstract + resourceName: resource + query: > + SELECT DISTINCT ?resource ?abstract WHERE { + { + GRAPH ?g1 { + ?resource a . + } + GRAPH ?g2 { + ?resource ?abstract. + } + } + %VALUES% + } - fieldName: label resourceName: resource query: > @@ -48,6 +76,34 @@ indexFields: } %VALUES% } + - fieldName: label + resourceName: resource + query: > + SELECT DISTINCT ?resource ?label WHERE { + { + GRAPH ?g1 { + ?resource a . + } + GRAPH ?g2 { + ?resource ?label. + } + } + %VALUES% + } + - fieldName: abstract + resourceName: resource + query: > + SELECT DISTINCT ?resource ?abstract WHERE { + { + GRAPH ?g1 { + ?resource a . + } + GRAPH ?g2 { + ?resource ?abstract. + } + } + %VALUES% + } - fieldName: label resourceName: artifact query: > diff --git a/search/indexer/account.yml b/search/indexer/account.yml new file mode 100644 index 00000000..b93b3e2b --- /dev/null +++ b/search/indexer/account.yml @@ -0,0 +1,38 @@ +version: "1.0" +indexMode: INDEX_SPARQL_ENDPOINT +sparqlEndpoint: http://virtuoso:8890/sparql +indexFields: + # ACCOUNT LABEL + - fieldName: label + documentVariable: account + query: > + SELECT DISTINCT ?account ?label WHERE { + GRAPH ?g { + ?person ?account . + ?person ?label. + } + #VALUES# + } + # ACCOUNT NAME + - fieldName: part + documentVariable: account + query: > + SELECT ?account ?part WHERE { + GRAPH ?g { + ?account a . + ?account ?part . + } + #VALUES# + } + # ACCOUNT TYPE NAME + - fieldName: typeName + type: string + documentVariable: account + query: > + SELECT DISTINCT ?account ?typeName WHERE { + GRAPH ?g { + ?account a . + BIND("Account" AS ?typeName) + } + #VALUES# + } diff --git a/search/indexer/artifact.yml b/search/indexer/artifact.yml new file mode 100644 index 00000000..507256b4 --- /dev/null +++ b/search/indexer/artifact.yml @@ -0,0 +1,75 @@ +version: "1.0" +indexMode: INDEX_SPARQL_ENDPOINT +sparqlEndpoint: http://virtuoso:8890/sparql +indexFields: + # ARTIFACT LABEL + - fieldName: label + documentVariable: artifact + query: > + SELECT DISTINCT ?artifact ?label WHERE { + GRAPH ?g { + ?artifact a . + ?artifact ?label. + } + #VALUES# + } + # ARTIFACT ABSTRACT + - fieldName: abstract + documentVariable: artifact + query: > + SELECT DISTINCT ?artifact ?abstract WHERE { + GRAPH ?g { + ?artifact a . + ?artifact ?abstract. + } + #VALUES# + } + # ARTIFACT NAME + - fieldName: part + documentVariable: artifact + query: > + SELECT ?artifact ?part WHERE { + GRAPH ?g { + ?artifact a . + ?artifact ?part . + } + #VALUES# + } + # ARTIFACT GROUP + - fieldName: group + documentVariable: artifact + type: uri + query: > + SELECT ?artifact (REPLACE(STR(?groupUri), "(.*)(/)", "") AS ?group) WHERE { + GRAPH ?g { + ?artifact a . + ?artifact ?groupUri . + } + #VALUES# + } + # ARTIFACT TYPE NAME + - fieldName: typeName + type: string + documentVariable: artifact + query: > + SELECT DISTINCT ?artifact ?typeName WHERE { + GRAPH ?g { + ?artifact a . + BIND("Artifact" AS ?typeName) + } + #VALUES# + } + # PUBLISHER ACCOUNT NAME + - fieldName: publisher + documentVariable: artifact + query: > + SELECT ?artifact ?publisher WHERE { + GRAPH ?g1 { + ?artifact a . + ?artifact ?account . + } + GRAPH ?g2 { + ?account ?publisher . + } + #VALUES# + } diff --git a/search/indexer/collection.yml b/search/indexer/collection.yml new file mode 100644 index 00000000..f7a65e67 --- /dev/null +++ b/search/indexer/collection.yml @@ -0,0 +1,64 @@ +version: "1.0" +indexMode: INDEX_SPARQL_ENDPOINT +sparqlEndpoint: http://virtuoso:8890/sparql +indexFields: + # COLLECTION LABEL + - fieldName: label + documentVariable: collection + query: > + SELECT DISTINCT ?collection ?label WHERE { + GRAPH ?g { + ?collection a . + ?collection ?label. + } + #VALUES# + } + # COLLECTION ABSTRACT + - fieldName: abstract + documentVariable: collection + query: > + SELECT DISTINCT ?collection ?abstract WHERE { + GRAPH ?g { + ?collection a . + ?collection ?abstract. + } + #VALUES# + } + + # COLLECTION NAME + - fieldName: part + documentVariable: collection + query: > + SELECT ?collection ?part WHERE { + GRAPH ?g { + ?collection a . + ?collection ?part . + } + #VALUES# + } + # PUBLISHER ACCOUNT NAME + - fieldName: publisher + documentVariable: collection + query: > + SELECT ?collection ?publisher WHERE { + GRAPH ?g1 { + ?collection a . + ?collection ?account . + } + GRAPH ?g2 { + ?account ?publisher . + } + #VALUES# + } + # COLLECTIOn TYPE NAME + - fieldName: typeName + type: string + documentVariable: collection + query: > + SELECT DISTINCT ?collection ?typeName WHERE { + GRAPH ?g { + ?collection a . + BIND("Collection" AS ?typeName) + } + #VALUES# + } diff --git a/search/indexer/group.yml b/search/indexer/group.yml new file mode 100644 index 00000000..f88589c6 --- /dev/null +++ b/search/indexer/group.yml @@ -0,0 +1,64 @@ +version: "1.0" +indexMode: INDEX_SPARQL_ENDPOINT +sparqlEndpoint: http://virtuoso:8890/sparql +indexFields: + # GROUP LABEL + - fieldName: label + documentVariable: group + query: > + SELECT DISTINCT ?group ?label WHERE { + GRAPH ?g { + ?group a . + ?group ?label. + } + #VALUES# + } + # GROUP ABSTRACT + - fieldName: abstract + documentVariable: group + query: > + SELECT DISTINCT ?group ?abstract WHERE { + GRAPH ?g { + ?group a . + ?group ?abstract. + } + #VALUES# + } + + # GROUP NAME + - fieldName: part + documentVariable: group + query: > + SELECT ?group ?part WHERE { + GRAPH ?g { + ?group a . + ?group ?part . + } + #VALUES# + } + # GROUP TYPE NAME + - fieldName: typeName + type: string + documentVariable: group + query: > + SELECT DISTINCT ?group ?typeName WHERE { + GRAPH ?g { + ?group a . + BIND("Group" AS ?typeName) + } + #VALUES# + } + # PUBLISHER ACCOUNT NAME + - fieldName: publisher + documentVariable: group + query: > + SELECT ?group ?publisher WHERE { + GRAPH ?g1 { + ?group a . + ?group ?account . + } + GRAPH ?g2 { + ?account ?publisher . + } + #VALUES# + } diff --git a/search/indexer/version.yml b/search/indexer/version.yml new file mode 100644 index 00000000..5d891975 --- /dev/null +++ b/search/indexer/version.yml @@ -0,0 +1,76 @@ +version: "1.0" +indexMode: INDEX_SPARQL_ENDPOINT +sparqlEndpoint: http://virtuoso:8890/sparql +indexFields: + # VERSION LABEL + - fieldName: label + documentVariable: version + query: > + SELECT DISTINCT ?version ?label WHERE { + GRAPH ?g { + ?version a . + ?version ?label. + } + #VALUES# + } + # VERSION ABSTRACT + - fieldName: abstract + documentVariable: version + query: > + SELECT DISTINCT ?version ?abstract WHERE { + GRAPH ?g { + ?version a . + ?version ?abstract. + } + #VALUES# + } + + # VERSION NAME + - fieldName: part + documentVariable: version + query: > + SELECT ?version ?part WHERE { + GRAPH ?g { + ?version a . + ?version ?part . + } + #VALUES# + } + # ARTIFACT NAME + - fieldName: part + documentVariable: version + query: > + SELECT ?version ?part WHERE { + GRAPH ?g { + ?version a . + ?version ?artifact . + ?artifact ?part . + } + #VALUES# + } + # VERSION TYPE NAME + - fieldName: typeName + type: string + documentVariable: version + query: > + SELECT DISTINCT ?version ?typeName WHERE { + GRAPH ?g { + ?version a . + BIND("Version" AS ?typeName) + } + #VALUES# + } + # PUBLISHER ACCOUNT NAME + - fieldName: publisher + documentVariable: version + query: > + SELECT ?version ?publisher WHERE { + GRAPH ?g1 { + ?version a . + ?version ?account . + } + GRAPH ?g2 { + ?account ?publisher . + } + #VALUES# + } diff --git a/search/lookup-indexer.jar b/search/lookup-indexer.jar deleted file mode 100644 index a45364f5..00000000 Binary files a/search/lookup-indexer.jar and /dev/null differ diff --git a/search/lookup-server.jar b/search/lookup-server.jar deleted file mode 100644 index eb79b915..00000000 Binary files a/search/lookup-server.jar and /dev/null differ diff --git a/search/servlet-config.yml b/search/servlet-config.yml index b058e08b..fc5318a8 100644 --- a/search/servlet-config.yml +++ b/search/servlet-config.yml @@ -1,23 +1,41 @@ version: "1.0" indexPath: ./index -exactMatchBoost: 10 +maxBufferedDocs: 1000000 +logInterval: 10000 +exactMatchBoost: 6 prefixMatchBoost: 5 fuzzyMatchBoost: 2 fuzzyEditDistance: 2 fuzzyPrefixLength: 2 maxResults: 100 format: JSON -minRelevanceScore: 0.1 -queryFields: - - fieldName: label +minScore: 0.1 +lookupFields: + - name: label weight: 10 tokenize: true + highlight: true queryByDefault: true - - fieldName: part + allowPartialMatch: true + - name: abstract + weight: 1 + tokenize: true + highlight: true + queryByDefault: true + allowPartialMatch: true + - name: typeName + allowPartialMatch: true + weight: 0 + highlight: false + required: true + exact: true + tokenize: true + - name: part weight: 10 tokenize: true + allowPartialMatch: true queryByDefault: true - - fieldName: publisher + - name: publisher weight: 0 tokenize: true queryByDefault: false @@ -25,7 +43,7 @@ queryFields: allowPartialMatch: true required: true highlight: false - - fieldName: group + - name: group weight: 0 tokenize: true queryByDefault: false @@ -33,10 +51,4 @@ queryFields: allowPartialMatch: true required: true highlight: false - - fieldName: typeName - allowPartialMatch: true - weight: 1.0 - highlight: false - required: true - exact: true - tokenize: true \ No newline at end of file + \ No newline at end of file diff --git a/server/app/api/lib/account-writer.js b/server/app/api/lib/account-writer.js new file mode 100644 index 00000000..9b3a208e --- /dev/null +++ b/server/app/api/lib/account-writer.js @@ -0,0 +1,109 @@ +const JsonldUtils = require('../../../../public/js/utils/jsonld-utils.js'); +const DatabusUris = require('../../../../public/js/utils/databus-uris.js'); +const DatabusConstants = require('../../../../public/js/utils/databus-constants.js'); +const UriUtils = require('../../common/utils/uri-utils.js'); +const ApiError = require('../../common/utils/api-error.js'); +const ResourceWriter = require('./resource-writer.js'); + +var signer = require('./databus-tractate-suite.js'); + +class AccountWriter extends ResourceWriter { + + constructor(createUserCallback, logger) { + super(logger); + + this.createUserCallback = createUserCallback; + } + + async onValidateUser() { + + if(this.userData.accountName == null) { + + console.log("Account name is not set, creating user"); + + if(!this.resource.isAccount()) { + throw new ApiError(400, this.uri, `Identifier <${this.uri}> is not a valid account URI.`, null); + } + + var accountName = this.resource.getAccount(); + console.log(`Desired account name is ${accountName}`); + + if(accountName.length < 4) { + throw new ApiError(400, this.uri, `Specified account name ('${accountName}') must be at least 4 characters long.`, null); + } + + this.userData = await this.createUserCallback(this.userData.sub, accountName); + } + + super.onValidateUser(); + } + + getInputPersonGraph() { + for(var graph of this.inputGraphs) { + + var foafAccount = JsonldUtils.getFirstObjectUri(graph, DatabusUris.FOAF_ACCOUNT); + + if(foafAccount === this.uri) { + return graph; + } + } + + return null; + } + + async onCreateGraphs() { + + var accountUri = this.uri; + var accountName = UriUtils.uriToName(accountUri); + var accountGraph = JsonldUtils.getGraphById(this.inputGraphs, accountUri); + + var rsaKeyGraph = {}; + rsaKeyGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.CERT_RSA_PUBLIC_KEY; + rsaKeyGraph[DatabusUris.RDFS_LABEL] = DatabusConstants.WEBID_SHARED_PUBLIC_KEY_LABEL + "ASDF"; + rsaKeyGraph[DatabusUris.CERT_MODULUS] = signer.getModulus(); + rsaKeyGraph[DatabusUris.CERT_EXPONENT] = 65537; + + var personUri = `${accountUri}${DatabusConstants.WEBID_THIS}`; + + var personGraph = {}; + personGraph[DatabusUris.JSONLD_ID] = personUri; + personGraph[DatabusUris.JSONLD_TYPE] = [ DatabusUris.FOAF_PERSON, DatabusUris.DBP_DBPEDIAN ]; + personGraph[DatabusUris.FOAF_ACCOUNT] = JsonldUtils.refTo(this.uri); + personGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY] = this.uri; + personGraph[DatabusUris.CERT_KEY] = [ rsaKeyGraph ]; + + var inputPersonGraph = this.getInputPersonGraph(); + + if(inputPersonGraph != null) { + personGraph[DatabusUris.FOAF_NAME] = inputPersonGraph[DatabusUris.FOAF_NAME]; + personGraph[DatabusUris.FOAF_IMG] = inputPersonGraph[DatabusUris.FOAF_IMG]; + personGraph[DatabusUris.FOAF_STATUS] = inputPersonGraph[DatabusUris.FOAF_STATUS]; + } + + var profileUri = `${accountUri}${DatabusConstants.WEBID_DOCUMENT}`; + + var profileDocumentGraph = {}; + profileDocumentGraph[DatabusUris.JSONLD_ID] = profileUri; + profileDocumentGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.FOAF_PERSONAL_PROFILE_DOCUMENT; + profileDocumentGraph[DatabusUris.FOAF_MAKER] = JsonldUtils.refTo(personUri); + profileDocumentGraph[DatabusUris.FOAF_PRIMARY_TOPIC] = JsonldUtils.refTo(personUri); + + var accountGraph = {} + accountGraph[DatabusUris.JSONLD_ID] = accountUri; + accountGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.DATABUS_ACCOUNT; + accountGraph[DatabusUris.FOAF_ACCOUNT_NAME] = accountName; + accountGraph[DatabusUris.DATABUS_NAME] = accountName; + + return [ + accountGraph, + personGraph, + profileDocumentGraph + ] + } + + getSHACLFilePath() { + return './res/shacl/account.shacl' + } +} + +module.exports = AccountWriter; diff --git a/server/app/api/lib/artifact-writer.js b/server/app/api/lib/artifact-writer.js new file mode 100644 index 00000000..f15f0bcf --- /dev/null +++ b/server/app/api/lib/artifact-writer.js @@ -0,0 +1,52 @@ +const DatabusUris = require('../../../../public/js/utils/databus-uris'); +const DatabusUtils = require('../../../../public/js/utils/databus-utils.js'); +const JsonldUtils = require('../../../../public/js/utils/jsonld-utils.js'); +const ResourceWriter = require('./resource-writer.js'); + +class ArtifactWriter extends ResourceWriter { + + constructor(logger) { + super(logger); + } + + async onCreateGraphs() { + + var inputArtifactGraph = JsonldUtils.getGraphById(this.inputGraphs, this.uri); + + var artifactGraph = {}; + artifactGraph[DatabusUris.JSONLD_ID] = this.uri; + artifactGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.DATABUS_ARTIFACT; + artifactGraph[DatabusUris.DATABUS_NAME] = this.resource.getArtifact(); + artifactGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY] = JsonldUtils.refTo(this.resource.getAccountURI()); + artifactGraph[DatabusUris.DATABUS_GROUP_PROPERTY] = JsonldUtils.refTo(this.resource.getGroupURI()); + + if(inputArtifactGraph[DatabusUris.DCT_TITLE] != null) { + artifactGraph[DatabusUris.DCT_TITLE] = inputArtifactGraph[DatabusUris.DCT_TITLE]; + } + + if(inputArtifactGraph[DatabusUris.DCT_DESCRIPTION] != null) { + artifactGraph[DatabusUris.DCT_DESCRIPTION] = inputArtifactGraph[DatabusUris.DCT_DESCRIPTION]; + } + + if(inputArtifactGraph[DatabusUris.DCT_ABSTRACT] != null) { + artifactGraph[DatabusUris.DCT_ABSTRACT] = inputArtifactGraph[DatabusUris.DCT_ABSTRACT]; + } else if (artifactGraph[DatabusUris.DCT_DESCRIPTION] != null) { + artifactGraph[DatabusUris.DCT_DESCRIPTION] = DatabusUtils.createAbstractFromDescription(artifactGraph[DatabusUris.DCT_DESCRIPTION]); + } + + var groupGraph = {}; + groupGraph[DatabusUris.JSONLD_ID] = this.resource.getGroupURI(); + groupGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.DATABUS_GROUP; + + return [ + artifactGraph, + groupGraph + ]; + } + + getSHACLFilePath() { + return './res/shacl/artifact.shacl' + } +} + +module.exports = ArtifactWriter; diff --git a/server/app/api/lib/collection-writer.js b/server/app/api/lib/collection-writer.js new file mode 100644 index 00000000..fab0cd3f --- /dev/null +++ b/server/app/api/lib/collection-writer.js @@ -0,0 +1,68 @@ +const DatabusUris = require('../../../../public/js/utils/databus-uris'); +const DatabusUtils = require('../../../../public/js/utils/databus-utils.js'); +const JsonldUtils = require('../../../../public/js/utils/jsonld-utils.js'); +const ResourceWriter = require('./resource-writer.js'); + +class CollectionWriter extends ResourceWriter { + + constructor(logger) { + super(logger); + } + + async onCreateGraphs() { + + var inputCollectionGraph = JsonldUtils.getGraphById(this.inputGraphs, this.uri); + + var collectionGraph = {}; + collectionGraph[DatabusUris.JSONLD_ID] = this.uri; + collectionGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.DATABUS_COLLECTION; + collectionGraph[DatabusUris.DATABUS_NAME] = this.resource.getArtifact(); + collectionGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY] = JsonldUtils.refTo(this.resource.getAccountURI()); + collectionGraph[DatabusUris.DATABUS_COLLECTION_CONTENT] = inputCollectionGraph[DatabusUris.DATABUS_COLLECTION_CONTENT]; + + if(inputCollectionGraph[DatabusUris.DCT_TITLE] != null) { + collectionGraph[DatabusUris.DCT_TITLE] = inputCollectionGraph[DatabusUris.DCT_TITLE]; + } + + if(inputCollectionGraph[DatabusUris.DCT_DESCRIPTION] != null) { + collectionGraph[DatabusUris.DCT_DESCRIPTION] = inputCollectionGraph[DatabusUris.DCT_DESCRIPTION]; + } + + if(inputCollectionGraph[DatabusUris.DCT_ISSUED] != null) { + collectionGraph[DatabusUris.DCT_ISSUED] = inputCollectionGraph[DatabusUris.DCT_ISSUED]; + } + + var timeString = DatabusUtils.timeStringNow(); + + // Set times + if (collectionGraph[DatabusUris.DCT_CREATED] == undefined) { + collectionGraph[DatabusUris.DCT_CREATED] = [{}]; + collectionGraph[DatabusUris.DCT_CREATED][0][DatabusUris.JSONLD_TYPE] = DatabusUris.XSD_DATE_TIME; + collectionGraph[DatabusUris.DCT_CREATED][0][DatabusUris.JSONLD_VALUE] = timeString; + } + + if (collectionGraph[DatabusUris.DCT_MODIFIED] == undefined) { + collectionGraph[DatabusUris.DCT_MODIFIED] = [{}]; + collectionGraph[DatabusUris.DCT_MODIFIED][0][DatabusUris.JSONLD_TYPE] = DatabusUris.XSD_DATE_TIME; + collectionGraph[DatabusUris.DCT_MODIFIED][0][DatabusUris.JSONLD_VALUE] = timeString; + } + + collectionGraph[DatabusUris.DCT_MODIFIED] = inputCollectionGraph[DatabusUris.DCT_ISSUED]; + + if(inputCollectionGraph[DatabusUris.DCT_ABSTRACT] != null) { + collectionGraph[DatabusUris.DCT_ABSTRACT] = inputCollectionGraph[DatabusUris.DCT_ABSTRACT]; + } else if (collectionGraph[DatabusUris.DCT_DESCRIPTION] != null) { + collectionGraph[DatabusUris.DCT_ABSTRACT] = DatabusUtils.createAbstractFromDescription(collectionGraph[DatabusUris.DCT_DESCRIPTION]); + } + + return [ + collectionGraph + ]; + } + + getSHACLFilePath() { + return './res/shacl/collection.shacl' + } +} + +module.exports = CollectionWriter; diff --git a/server/app/api/lib/databus-tractate-suite.js b/server/app/api/lib/databus-tractate-suite.js index 9aef1850..0bda3811 100644 --- a/server/app/api/lib/databus-tractate-suite.js +++ b/server/app/api/lib/databus-tractate-suite.js @@ -1,6 +1,4 @@ -const rdfParser = require("rdf-parse").default; const fs = require('fs'); -var rp = require('request-promise'); const NodeRSA = require('node-rsa'); var JsonldUtils = require('../../../../public/js/utils/jsonld-utils'); var jsonld = require('jsonld'); @@ -10,6 +8,8 @@ const Constants = require('../../common/constants'); var fileAnalyzer = require('../../common/file-analyzer'); var GstoreHelper = require('../../common/utils/gstore-helper'); var requestRdf = require('../../common/request-rdf'); +const GstoreResource = require('./gstore-resource'); +const pem2jwk = require('pem-jwk').pem2jwk; var tractateConfig = { header: 'Databus Tractate Version 1.0' @@ -135,14 +135,12 @@ signer.validate = async function (canonicalized, proof) { var quads = []; if (isInternalWebId) { - // Parse the WebId URL - var webIdURL = new URL(publisherUri); - // Extract the pathname of the URL without leading slash - var repo = webIdURL.pathname.substring(1); + // Read the WebId directly from the Gstore to avoid access problems in private mode + var gstoreResource = new GstoreResource(publisherUri); + await gstoreResource.read(); - var webIdJsonLd = await GstoreHelper.read(repo, Constants.DATABUS_FILE_WEBID); - var flattenedGraphs = await jsonld.flatten(webIdJsonLd); + var flattenedGraphs = await jsonld.flatten(gstoreResource.content); quads = await requestRdf.parseRdf(Constants.HTTP_CONTENT_TYPE_JSONLD, JSON.stringify(flattenedGraphs)) @@ -202,6 +200,13 @@ signer.sign = function (canonicalized) { return signature; }; +signer.getModulus = function() { + var pkeyPEM = fs.readFileSync(__dirname + '/../../../keypair/public-key.pem', 'utf-8'); + var publicKeyInfo = pem2jwk(pkeyPEM); + let buff = Buffer.from(publicKeyInfo.n, 'base64'); + return buff.toString('hex'); +} + diff --git a/server/app/api/lib/dataid-autocomplete.js b/server/app/api/lib/dataid-autocomplete.js index feda1126..14bfa3c4 100644 --- a/server/app/api/lib/dataid-autocomplete.js +++ b/server/app/api/lib/dataid-autocomplete.js @@ -6,6 +6,7 @@ const ArrayUtils = require('../../common/utils/array-utils'); const DatabusUtils = require('../../../../public/js/utils/databus-utils'); const { JSONLD_VALUE } = require('../../../../public/js/utils/databus-uris'); const knownCompressionExtensions = require('../../common/config/compression-extensions.json'); +const DatabusConstants = require('../../../../public/js/utils/databus-constants'); var autocompleter = {}; @@ -25,8 +26,8 @@ function autofillFileIdentifiers(datasetUri, fileGraph) { for (var cv of contentVariants) { var facet = UriUtils.uriToName(cv.key); var value = cv.value; - segment += `_${facet}=${value}`; - } + segment += `_${facet}=${encodeURIComponent(value)}`; + } var format = undefined; var compression = undefined; @@ -52,8 +53,8 @@ function autofillFileIdentifiers(datasetUri, fileGraph) { segment += `.${compression}`; } - fileGraph[DatabusUris.DATABUS_FILE] = []; - fileGraph[DatabusUris.DATABUS_FILE].push({ '@id': `${baseUri}/${segment}` }); + fileGraph[DatabusUris.DATABUS_FILE] = [{}]; + fileGraph[DatabusUris.DATABUS_FILE][0][DatabusUris.JSONLD_ID] = `${baseUri}/${segment}`; fileGraph[DatabusUris.JSONLD_ID] = `${baseUri}#${segment}`; } @@ -86,10 +87,9 @@ autocompleter.autocomplete = function (expandedGraph, logger) { if (publisherUri == null) { versionGraph[DatabusUris.DCT_PUBLISHER] = [{}]; - versionGraph[DatabusUris.DCT_PUBLISHER][0][DatabusUris.JSONLD_ID] = `${accountUri}#this`; + versionGraph[DatabusUris.DCT_PUBLISHER][0][DatabusUris.JSONLD_ID] = `${accountUri}${DatabusConstants.WEBID_THIS}`; } - var timeString = DatabusUtils.timeStringNow(); if (versionGraph[DatabusUris.DCT_ISSUED] == undefined) { @@ -106,11 +106,14 @@ autocompleter.autocomplete = function (expandedGraph, logger) { versionGraph[DatabusUris.DCT_ABSTRACT][0][JSONLD_VALUE] = DatabusUtils.createAbstractFromDescription(description); } + + var versionName = UriUtils.uriToName(datasetUri); + versionGraph[DatabusUris.DATABUS_NAME] = [{}]; + versionGraph[DatabusUris.DATABUS_NAME][0][DatabusUris.JSONLD_VALUE] =versionName; if (versionGraph[DatabusUris.DCT_HAS_VERSION] == undefined) { versionGraph[DatabusUris.DCT_HAS_VERSION] = [{}]; - versionGraph[DatabusUris.DCT_HAS_VERSION][0][DatabusUris.JSONLD_VALUE] = - UriUtils.uriToName(datasetUri); + versionGraph[DatabusUris.DCT_HAS_VERSION][0][DatabusUris.JSONLD_VALUE] = versionName; } versionGraph[DatabusUris.DCT_MODIFIED] = [{}]; @@ -132,7 +135,8 @@ autocompleter.autocomplete = function (expandedGraph, logger) { artifactGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY][0][DatabusUris.JSONLD_ID] = accountUri; artifactGraph[DatabusUris.DATABUS_GROUP_PROPERTY] = [{}]; artifactGraph[DatabusUris.DATABUS_GROUP_PROPERTY][0][DatabusUris.JSONLD_ID] = groupUri; - + artifactGraph[DatabusUris.DATABUS_NAME] = [{}]; + artifactGraph[DatabusUris.DATABUS_NAME][0][DatabusUris.JSONLD_VALUE] = UriUtils.uriToName(artifactUri); var groupGraph = JsonldUtils.getTypedGraph(expandedGraph, DatabusUris.DATABUS_GROUP); @@ -145,6 +149,9 @@ autocompleter.autocomplete = function (expandedGraph, logger) { groupGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY] = [{}]; groupGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY][0][DatabusUris.JSONLD_ID] = accountUri; + groupGraph[DatabusUris.DATABUS_NAME] = [{}]; + groupGraph[DatabusUris.DATABUS_NAME][0][DatabusUris.JSONLD_VALUE] = UriUtils.uriToName(groupUri); + var fileGraphs = JsonldUtils.getTypedGraphs(expandedGraph, DatabusUris.DATABUS_PART); @@ -199,9 +206,10 @@ autocompleter.autocomplete = function (expandedGraph, logger) { for (var fileGraph of fileGraphs) { - versionGraph[DatabusUris.DCAT_DISTRIBUTION].push({ - '@id': fileGraph[DatabusUris.JSONLD_ID] - }); + var distributionReference = {}; + distributionReference[DatabusUris.JSONLD_ID] = fileGraph[DatabusUris.JSONLD_ID]; + + versionGraph[DatabusUris.DCAT_DISTRIBUTION].push(distributionReference); for (var contentVariantProperty of contentVariantProperties) { @@ -216,48 +224,5 @@ autocompleter.autocomplete = function (expandedGraph, logger) { } -autocompleter.autocompleteArtifact = function (expandedGraphs) { - - var artifactGraph = JsonldUtils.getTypedGraph(expandedGraphs, DatabusUris.DATABUS_ARTIFACT); - var artifactUri = artifactGraph[DatabusUris.JSONLD_ID]; - var groupUri = UriUtils.navigateUp(artifactUri, 1); - var accountUri = UriUtils.navigateUp(artifactUri, 2); - - - expandedGraphs.push({ '@id': groupUri, '@type': DatabusUris.DATABUS_GROUP }); - - artifactGraph[DatabusUris.DATABUS_GROUP_PROPERTY] = [{}]; - artifactGraph[DatabusUris.DATABUS_GROUP_PROPERTY][0][DatabusUris.JSONLD_ID] = groupUri; - - artifactGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY] = [{}]; - artifactGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY][0][DatabusUris.JSONLD_ID] = accountUri; - - if (artifactGraph[DatabusUris.DCT_ABSTRACT] == undefined - && artifactGraph[DatabusUris.DCT_DESCRIPTION] != undefined) { - var description = artifactGraph[DatabusUris.DCT_DESCRIPTION][0][DatabusUris.JSONLD_VALUE]; - artifactGraph[DatabusUris.DCT_ABSTRACT] = [{}]; - artifactGraph[DatabusUris.DCT_ABSTRACT][0][JSONLD_VALUE] = - DatabusUtils.createAbstractFromDescription(description); - } -} - -autocompleter.autocompleteGroup = function (expandedGraphs) { - - var groupGraph = JsonldUtils.getTypedGraph(expandedGraphs, DatabusUris.DATABUS_GROUP); - var groupUri = groupGraph[DatabusUris.JSONLD_ID]; - var accountUri = UriUtils.navigateUp(groupUri, 1); - - groupGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY] = [{}]; - groupGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY][0][DatabusUris.JSONLD_ID] = accountUri; - - if (groupGraph[DatabusUris.DCT_ABSTRACT] == undefined - && groupGraph[DatabusUris.DCT_DESCRIPTION] != undefined) { - - var description = groupGraph[DatabusUris.DCT_DESCRIPTION][0][DatabusUris.JSONLD_VALUE]; - groupGraph[DatabusUris.DCT_ABSTRACT] = [{}]; - groupGraph[DatabusUris.DCT_ABSTRACT][0][JSONLD_VALUE] = - DatabusUtils.createAbstractFromDescription(description); - } -} module.exports = autocompleter; \ No newline at end of file diff --git a/server/app/api/lib/group-writer.js b/server/app/api/lib/group-writer.js new file mode 100644 index 00000000..19b3c528 --- /dev/null +++ b/server/app/api/lib/group-writer.js @@ -0,0 +1,46 @@ +const DatabusUris = require('../../../../public/js/utils/databus-uris'); +const DatabusUtils = require('../../../../public/js/utils/databus-utils.js'); +const JsonldUtils = require('../../../../public/js/utils/jsonld-utils.js'); +const ResourceWriter = require('./resource-writer.js'); + +class GroupWriter extends ResourceWriter { + + constructor(logger) { + super(logger); + } + + async onCreateGraphs() { + + var inputGroupGraph = JsonldUtils.getGraphById(this.inputGraphs, this.uri); + + var groupGraph = {}; + groupGraph[DatabusUris.JSONLD_ID] = this.uri; + groupGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.DATABUS_GROUP; + groupGraph[DatabusUris.DATABUS_NAME] = this.resource.getGroup(); + groupGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY] = JsonldUtils.refTo(this.resource.getAccountURI()); + + if(inputGroupGraph[DatabusUris.DCT_TITLE] != null) { + groupGraph[DatabusUris.DCT_TITLE] = inputGroupGraph[DatabusUris.DCT_TITLE]; + } + + if(inputGroupGraph[DatabusUris.DCT_DESCRIPTION] != null) { + groupGraph[DatabusUris.DCT_DESCRIPTION] = inputGroupGraph[DatabusUris.DCT_DESCRIPTION]; + } + + if(inputGroupGraph[DatabusUris.DCT_ABSTRACT] != null) { + groupGraph[DatabusUris.DCT_ABSTRACT] = inputGroupGraph[DatabusUris.DCT_ABSTRACT]; + } else if (groupGraph[DatabusUris.DCT_DESCRIPTION] != null) { + groupGraph[DatabusUris.DCT_DESCRIPTION] = DatabusUtils.createAbstractFromDescription(groupGraph[DatabusUris.DCT_DESCRIPTION]); + } + + return [ + groupGraph + ]; + } + + getSHACLFilePath() { + return './res/shacl/group.shacl' + } +} + +module.exports = GroupWriter; diff --git a/server/app/api/lib/gstore-resource.js b/server/app/api/lib/gstore-resource.js new file mode 100644 index 00000000..11584791 --- /dev/null +++ b/server/app/api/lib/gstore-resource.js @@ -0,0 +1,108 @@ +const axios = require('axios'); +const { URL, URLSearchParams } = require('url'); +const Constants = require('../../common/constants'); + +class GstoreResource { + static DOCUMENT_READ_ENDPOINT = '/document/read'; + static DOCUMENT_WRITE_ENDPOINT = '/document/save'; + static DOCUMENT_DELETE_ENDPOINT = '/document/delete'; + static REQ_PARAM_REPO = 'repo'; + static REQ_PARAM_PATH = 'path'; + static REQ_PARAM_PREFIX = "prefix"; + static METADATA_FILENAME = 'metadata.jsonld'; + static GSTORE_BASE_URL = process.env.DATABUS_DATABASE_URL || 'http://localhost:8080'; + static PREFIX = `${process.env.DATABUS_RESOURCE_BASE_URL}/`; + + constructor(uriString, content = null) { + this.initialize(uriString); + this.content = content; + } + + initialize(uriString) { + const uri = new URL(uriString); + let relativePath = uri.pathname.replace(/^\/+|\/+$/g, ''); + if (!relativePath) throw new Error('URI path is empty.'); + + const parts = relativePath.split('/'); + if (parts.length === 0) throw new Error('URI path does not contain any segments.'); + + this.repo = parts.shift(); + if (!this.repo) throw new Error('Repo is null or empty.'); + + parts.push(GstoreResource.METADATA_FILENAME); + + this.path = parts.join('/'); + } + + getRequestURL(operation) { + let endpoint; + switch (operation) { + case 'Write': endpoint = GstoreResource.DOCUMENT_WRITE_ENDPOINT; break; + case 'Read': endpoint = GstoreResource.DOCUMENT_READ_ENDPOINT; break; + case 'Delete': endpoint = GstoreResource.DOCUMENT_DELETE_ENDPOINT; break; + default: throw new Error('Invalid operation'); + } + + const url = new URL(GstoreResource.GSTORE_BASE_URL + endpoint); + url.search = new URLSearchParams({ + [GstoreResource.REQ_PARAM_REPO]: this.repo, + [GstoreResource.REQ_PARAM_PATH]: this.path, + [GstoreResource.REQ_PARAM_PREFIX]: GstoreResource.PREFIX, + }).toString(); + + return url.toString(); + } + + async save() { + if (!this.content) { + throw new Error('No content provided for saving.'); + } + + try { + var url = this.getRequestURL('Write'); + const formattedContent = JSON.stringify(this.content, null, 2); + + const response = await axios.post(url, formattedContent, { + headers: { 'Content-Type': Constants.HTTP_CONTENT_TYPE_JSONLD } + }); + return response.status; + } catch (error) { + var responseData = error.response.data; + console.error('Error saving document:', this.content, responseData); + throw error; + } + } + + async read() { + try { + const response = await axios.get(this.getRequestURL('Read'), { headers: { 'Accept': 'application/ld+json' } }); + this.content = response.data; + return response.data; + } catch (error) { + console.error('Error reading document:', error); + return null; + } + } + + async exists() { + try { + const response = await axios.head(this.getRequestURL('Read')); + return response.status >= 200 && response.status < 300; + } catch (error) { + console.error('Error checking resource existence:', error); + return false; + } + } + + async delete() { + try { + const response = await axios.delete(this.getRequestURL('Delete')); + return response; + } catch (error) { + console.error('Error deleting resource:', error); + return error.response; + } + } +} + +module.exports = GstoreResource; diff --git a/server/app/api/lib/publish-account.js b/server/app/api/lib/publish-account.js deleted file mode 100644 index c9863a0f..00000000 --- a/server/app/api/lib/publish-account.js +++ /dev/null @@ -1,162 +0,0 @@ -const JsonldUtils = require('../../../../public/js/utils/jsonld-utils'); -var GstoreHelper = require('../../common/utils/gstore-helper'); -const DatabusUris = require('../../../../public/js/utils/databus-uris'); -const Constants = require('../../common/constants'); -const DatabusConstants = require('../../../../public/js/utils/databus-constants'); - -var shaclTester = require('../../common/shacl-tester'); -var jsonld = require('jsonld'); -var fs = require('fs'); -const pem2jwk = require('pem-jwk').pem2jwk; -const exec = require('../../common/execute-query'); - -const defaultContext = require('../../common/res/context.jsonld'); - -var constructor = require('../../common/execute-construct.js'); -var constructAccountQuery = require('../../common/queries/constructs/construct-account.sparql'); -const UriUtils = require('../../common/utils/uri-utils'); -const DatabusMessage = require('../../common/databus-message'); - - - -async function accountExists(accountName) { - let accountUri = UriUtils.createResourceUri([accountName]); - return await exec.executeAsk(`ASK { ?s <${DatabusUris.FOAF_ACCOUNT}> <${accountUri}> . }`); -} - -module.exports = async function publishAccount(accountName, body) { - - var result = {}; - result.isSuccess = false; - result.message = ""; - result.statusCode = 403; - - try { - - // Get the accountName from the protected request - if (accountName.length < 4) { - result.message = `Account name is too short. An account name should contain at least 4 characters.\n`; - return result; - } - - // Validate the group RDF with the shacl validation tool - var shaclResult = await shaclTester.validateWebIdRDF(body); - - // Return failure - if (!shaclResult.isSuccess) { - var response = 'SHACL validation error:\n'; - for (var m in shaclResult.messages) { - response += `>>> ${shaclResult.messages[m]}\n` - } - - result.message = response; - result.statusCode = 400; - return result; - } - - var triples = await constructor.executeConstruct(body, constructAccountQuery); - var expandedGraphs = await jsonld.flatten(await jsonld.fromRDF(triples)); - - if (expandedGraphs.length == 0) { - result.message = `The following construct query did not yield any triples:\n\n${constructAccountQuery}\n`; - result.statusCode = 400; - return result; - } - - // Expected uris - var accountUri = `${process.env.DATABUS_RESOURCE_BASE_URL}/${accountName}`; - - var personUri = `${accountUri}${DatabusConstants.WEBID_THIS}`; - var profileUri = `${accountUri}${DatabusConstants.WEBID_DOCUMENT}`; - - // Compare the specified id to the actual person uri - var personGraph = JsonldUtils.getTypedGraph(expandedGraphs, DatabusUris.FOAF_PERSON); - - if (personGraph == undefined) { - result.message = `No person graph found`; - result.statusCode = 400; - return result; - } - - // Mismatch gives error - if (personGraph[DatabusUris.JSONLD_ID] != personUri) { - result.message = `The specified uri of the foaf:Person does not match the expected value. (specified: ${personGraph['@id']}, expected: ${personUri})\n`; - result.statusCode = 400; - return result; - } - - // Compare the specified id to the actual person uri - var profileGraph = JsonldUtils.getTypedGraph(expandedGraphs, DatabusUris.FOAF_PERSONAL_PROFILE_DOCUMENT); - - if (profileGraph == undefined) { - result.message = `No profile graph found`; - result.statusCode = 400; - return result; - } - - // Mismatch gives error - if (profileGraph[DatabusUris.JSONLD_ID] != profileUri) { - result.message = `The specified uri of the foaf:PersonalProfileDocument graph does not match the expected value. (specified: ${profileGraph['@id']}, expected: ${profileUri})\n`; - result.statusCode = 400; - return result; - } - - var pkeyPEM = fs.readFileSync(__dirname + '/../../../keypair/public-key.pem', 'utf-8'); - var publicKeyInfo = pem2jwk(pkeyPEM); - let buff = Buffer.from(publicKeyInfo.n, 'base64'); - var modulus = buff.toString('hex'); - - var rsaKeyGraph = {}; - rsaKeyGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.CERT_RSA_PUBLIC_KEY; - rsaKeyGraph[DatabusUris.RDFS_LABEL] = DatabusConstants.WEBID_SHARED_PUBLIC_KEY_LABEL; - rsaKeyGraph[DatabusUris.CERT_MODULUS] = modulus; - rsaKeyGraph[DatabusUris.CERT_EXPONENT] = 65537; - - personGraph[DatabusUris.CERT_KEY] = [rsaKeyGraph]; - - - var insertGraphs = expandedGraphs; - var compactedGraph = await jsonld.compact(insertGraphs, defaultContext); - - if (process.env.DATABUS_CONTEXT_URL != null) { - compactedGraph[DatabusUris.JSONLD_CONTEXT] = process.env.DATABUS_CONTEXT_URL; - } - - var targetPath = Constants.DATABUS_FILE_WEBID; - - - var exists = await accountExists(accountName); - - - - // Save the data using the database manager - var saveResult = await GstoreHelper.save(accountName, targetPath, compactedGraph); - - if (!saveResult.isSuccess) { - // return with Forbidden - result.message = 'Internal database error.\n'; - result.statusCode = 500; - return result; - } - - if(process.send != undefined) { - process.send({ - id: DatabusMessage.REQUEST_SEARCH_INDEX_REBUILD, - resources : accountUri - }); - } - - result.isSuccess = true; - result.statusCode = exists ? 200 : 201; - result.message = 'Account saved successfully.\n'; - return result; - - } catch (err) { - // return 500 with error - console.log('User creation failed!'); - console.log(err); - result.message = err; - result.statusCode = 500; - return result; - } -} \ No newline at end of file diff --git a/server/app/api/lib/publish-artifact.js b/server/app/api/lib/publish-artifact.js deleted file mode 100644 index c836b3dc..00000000 --- a/server/app/api/lib/publish-artifact.js +++ /dev/null @@ -1,111 +0,0 @@ -const UriUtils = require('../../common/utils/uri-utils'); -const DatabusUris = require('../../../../public/js/utils/databus-uris'); -const Constants = require('../../common/constants'); - -var shaclTester = require('../../common/shacl-tester'); -var GstoreHelper = require('../../common/utils/gstore-helper'); -var jsonld = require('jsonld'); -var constructor = require('../../common/execute-construct.js'); -var constructArtifactQuery = require('../../common/queries/constructs/construct-artifact.sparql'); -var defaultContext = require('../../../../model/generated/context.json'); -const DatabusUtils = require('../../../../public/js/utils/databus-utils'); -var autocompleter = require('./dataid-autocomplete'); -const DatabusMessage = require('../../common/databus-message'); - -module.exports = async function publishArtifact(accountName, graph, logger) { - - - try { - - var artifactUri = graph[DatabusUris.JSONLD_ID]; - logger.debug(artifactUri, `Processing artifact <${artifactUri}>`, graph); - - // Check for namespace violation - var expectedUriPrefix = `${process.env.DATABUS_RESOURCE_BASE_URL}/${accountName}/`; - - if (!artifactUri.startsWith(expectedUriPrefix)) { - logger.error(artifactUri, `Not allowed to access namespace of artifact identifier <${artifactUri}>.`); - return 403; - } - - var artifactSegment = UriUtils.cleanSegment(artifactUri.replace(expectedUriPrefix, "")); - - if (UriUtils.getPathLength(artifactSegment) != 2) { - logger.error(artifactUri, `Artifact uri <${artifactUri}> must have exactly 3 path segments relative to the Databus base url <${process.env.DATABUS_RESOURCE_BASE_URL}> (found ${UriUtils.getPathLength(artifactSegment) + 1})`, null); - return 400; - } - - var segments = artifactSegment.split("/"); - var groupName = segments[0]; - var artifactName = segments[1]; - - if (groupName == Constants.DATABUS_COLLECTIONS_GROUP_IDENTIFIER) { - logger.error(artifactUri, `Cannot create artifact with group name "${Constants.DATABUS_COLLECTIONS_GROUP_IDENTIFIER}" as it is reserved for Databus collections.`); - return 400; - } - - // Get the desired triples from the data via construct query - var triples = await constructor.executeConstruct(graph, constructArtifactQuery); - var tripleCount = DatabusUtils.lineCount(triples); - - if (tripleCount == 0) { - logger.info(artifactUri, `Construct query did not yield any triples. Nothing to publish.`, graph); - return 200; - } - - logger.debug(artifactUri, `${tripleCount} triples selected via construct query.`, triples); - var expandedGraphs = await jsonld.flatten(await jsonld.fromRDF(triples)); - - // Auto-complete - autocompleter.autocompleteArtifact(expandedGraphs); - logger.debug(artifactUri, `Input has been processed by the auto-completer`, expandedGraphs); - - // Validate the artifact RDF with the shacl validation tool - var shaclResult = await shaclTester.validateArtifactRDF(expandedGraphs); - - // Return failure with SHACL validation message - if (!shaclResult.isSuccess) { - logger.error(artifactUri, `SHACL validation error`, shaclResult); - return 400; - } - - logger.debug(artifactUri, `SHACL validation successful`, shaclResult); - - - // Compact graph, determine target path - var compactedGraph = await jsonld.compact(expandedGraphs, defaultContext); - - if (process.env.DATABUS_CONTEXT_URL != null) { - compactedGraph[DatabusUris.JSONLD_CONTEXT] = process.env.DATABUS_CONTEXT_URL; - logger.debug(artifactUri, `Context has been resubstituted with <${process.env.DATABUS_CONTEXT_URL}>`); - } - - var targetPath = `${groupName}/${artifactName}/${Constants.DATABUS_FILE_ARTIFACT}`; - logger.debug(artifactUri, `Saving artifact <${artifactUri}> to ${accountName}:${targetPath}`, compactedGraph); - - // Save the RDF with the current path using the database manager - var publishResult = await GstoreHelper.save(accountName, targetPath, compactedGraph); - - // Return failure - if (!publishResult.isSuccess) { - logger.error(artifactUri, `Internal database error`, null); - return 500; - } - - if(process.send != undefined) { - process.send({ - id: DatabusMessage.REQUEST_SEARCH_INDEX_REBUILD, - resource: artifactUri - }); - } - - logger.info(artifactUri, `Successfully published artifact <${artifactUri}>.`, compactedGraph); - return 200; - - } catch (err) { - console.log(`Unexpected Databus error when processing artifact data`); - console.log(err); - logger.error(null, `Unexpected Databus error when processing artifact data`, null); - return 500; - } -} diff --git a/server/app/api/lib/publish-group.js b/server/app/api/lib/publish-group.js deleted file mode 100644 index 8f6eea9d..00000000 --- a/server/app/api/lib/publish-group.js +++ /dev/null @@ -1,113 +0,0 @@ -const JsonldUtils = require('../../../../public/js/utils/jsonld-utils'); -const UriUtils = require('../../common/utils/uri-utils'); -const DatabusUris = require('../../../../public/js/utils/databus-uris'); -const Constants = require('../../common/constants'); -const GstoreHelper = require('../../common/utils/gstore-helper'); -const DatabusUtils = require('../../../../public/js/utils/databus-utils'); -var shaclTester = require('../../common/shacl-tester'); -var jsonld = require('jsonld'); -var constructor = require('../../common/execute-construct.js'); -var constructGroupQuery = require('../../common/queries/constructs/construct-group.sparql'); -var defaultContext = require('./../../common/res/context.jsonld'); -var autocompleter = require('./dataid-autocomplete'); -const DatabusMessage = require('../../common/databus-message'); - -module.exports = async function publishGroup(accountName, graph, logger) { - - try { - - var groupUri = graph[DatabusUris.JSONLD_ID]; - logger.debug(groupUri, `Processing group <${groupUri}>`, graph); - - // Check for namespace violation - if (!groupUri.startsWith(process.env.DATABUS_RESOURCE_BASE_URL)) { - logger.error(groupUri, `Group identifier does not start with the resource base url of this Databus (${process.env.DATABUS_RESOURCE_BASE_URL})`); - return 403; - } - - // Check for namespace violation - var expectedUriPrefix = `${process.env.DATABUS_RESOURCE_BASE_URL}/${accountName}/`; - if (!groupUri.startsWith(expectedUriPrefix)) { - logger.error(groupUri, `Not allowed to access namespace of group identifier <${groupUri}>.`); - return 403; - } - - var groupName = UriUtils.cleanSegment(groupUri.replace(expectedUriPrefix, "")); - - if (UriUtils.getPathLength(groupName) != 1) { - logger.error(groupUri, `Group uri <${groupUri}> must have exactly 2 path segments relative to the Databus base url <${process.env.DATABUS_RESOURCE_BASE_URL}> (found ${UriUtils.getPathLength(groupName) + 1})`, null); - return 400; - } - - if (groupName == Constants.DATABUS_COLLECTIONS_GROUP_IDENTIFIER) { - logger.error(groupUri, `Cannot create group with name "${Constants.DATABUS_COLLECTIONS_GROUP_IDENTIFIER}" as it is reserved for Databus collections.`); - return 400; - } - - // Get the desired triples from the data via construct query - var triples = await constructor.executeConstruct(graph, constructGroupQuery); - var tripleCount = DatabusUtils.lineCount(triples); - - if (tripleCount == 0) { - logger.info(groupUri, `Construct query did not yield any triples. Nothing to publish.`, graph); - return 200; - } - - logger.debug(groupUri, `${tripleCount} triples selected via construct query.`, triples); - var expandedGraphs = await jsonld.flatten(await jsonld.fromRDF(triples)); - - // Auto-complete - autocompleter.autocompleteGroup(expandedGraphs); - logger.debug(groupUri, `Input has been processed by the auto-completer`, expandedGraphs); - - - // Validate the group RDF with the shacl validation tool - var shaclResult = await shaclTester.validateGroupRDF(expandedGraphs); - - // Return failure with SHACL validation message - if (!shaclResult.isSuccess) { - logger.error(groupUri, `SHACL validation error`, shaclResult); - return 400; - } - - logger.debug(groupUri, `SHACL validation successful`, shaclResult); - - - // Compact graph, determine target path - var compactedGraph = await jsonld.compact(expandedGraphs, defaultContext); - - if(process.env.DATABUS_CONTEXT_URL != null) { - compactedGraph[DatabusUris.JSONLD_CONTEXT] = process.env.DATABUS_CONTEXT_URL; - logger.debug(groupUri, `Context has been resubstituted with <${process.env.DATABUS_CONTEXT_URL}>`); - } - - var targetPath = `${groupName}/${Constants.DATABUS_FILE_GROUP}`; - logger.debug(groupUri, `Saving group <${groupUri}> to ${accountName}:${targetPath}`, compactedGraph); - - - // Save the RDF with the current path using the database manager - var publishResult = await GstoreHelper.save(accountName, targetPath, compactedGraph); - - // Return failure - if (!publishResult.isSuccess) { - logger.error(groupUri, `Internal database error`, null); - return 500; - } - - if(process.send != undefined) { - process.send({ - id: DatabusMessage.REQUEST_SEARCH_INDEX_REBUILD, - resource: groupUri - }); - } - - logger.info(groupUri, `Successfully published group <${groupUri}>.`, compactedGraph); - return 200; - - } catch (err) { - console.log(`Unexpected Databus error when processing group data`); - console.log(err); - logger.error(null, `Unexpected Databus error when processing group data`, null); - return 500; - } -} diff --git a/server/app/api/lib/publish-version.js b/server/app/api/lib/publish-version.js index c90d8004..2bf0a549 100644 --- a/server/app/api/lib/publish-version.js +++ b/server/app/api/lib/publish-version.js @@ -2,20 +2,19 @@ const JsonldUtils = require('../../../../public/js/utils/jsonld-utils.js'); const UriUtils = require('../../common/utils/uri-utils.js'); const DatabusUris = require('../../../../public/js/utils/databus-uris.js'); const Constants = require('../../common/constants.js'); -const fs = require('fs'); var signer = require('./databus-tractate-suite.js'); var shaclTester = require('../../common/shacl-tester.js'); var GstoreHelper = require('../../common/utils/gstore-helper.js'); var jsonld = require('jsonld'); var sparql = require('../../common/queries/sparql.js'); -var defaultContext = require('../../../../model/generated/context.json'); var constructor = require('../../common/execute-construct.js'); var constructVersionQuery = require('../../common/queries/constructs/construct-version.sparql'); var autocompleter = require('./dataid-autocomplete.js'); var fileAnalyzer = require('../../common/file-analyzer.js'); const DatabusUtils = require('../../../../public/js/utils/databus-utils.js'); const DatabusMessage = require('../../common/databus-message.js'); +const DatabusResource = require('../../common/databus-resource.js'); async function verifyDataidParts(dataidGraphs, alwaysFetch, logger) { @@ -81,60 +80,66 @@ async function verifyDataidParts(dataidGraphs, alwaysFetch, logger) { */ async function constructInput(expandedGraph, versionGraphUri, logger) { - var versionGraph = JsonldUtils.getGraphById(expandedGraph, versionGraphUri); - var cvGraphs = JsonldUtils.getSubPropertyGraphs(expandedGraph, DatabusUris.DATABUS_CONTENT_VARIANT); - logger.debug(versionGraphUri, `Detected CV-graphs`, cvGraphs); + try { + var versionGraph = JsonldUtils.getGraphById(expandedGraph, versionGraphUri); + var cvGraphs = JsonldUtils.getSubPropertyGraphs(expandedGraph, DatabusUris.DATABUS_CONTENT_VARIANT); + logger.debug(versionGraphUri, `Detected CV-graphs`, cvGraphs); - var distributionUris = versionGraph[DatabusUris.DCAT_DISTRIBUTION]; + var distributionUris = versionGraph[DatabusUris.DCAT_DISTRIBUTION]; - var dataIdGraphs = []; - dataIdGraphs.push(JSON.parse(JSON.stringify(versionGraph))); - versionGraph[DatabusUris.DCAT_DISTRIBUTION] = []; + var dataIdGraphs = []; + dataIdGraphs.push(JSON.parse(JSON.stringify(versionGraph))); + versionGraph[DatabusUris.DCAT_DISTRIBUTION] = []; - var totalTripleCount = 0; - var step = 100; + var totalTripleCount = 0; + var step = 100; - var versionGraphCopy = JSON.parse(JSON.stringify(versionGraph)); - var distributionlessGraphs = [versionGraphCopy].concat(cvGraphs); + var versionGraphCopy = JSON.parse(JSON.stringify(versionGraph)); + var distributionlessGraphs = [versionGraphCopy].concat(cvGraphs); - // Create sub-dataids with only 100 parts at a time - // This will avoid long running or failing construct queries for large inputs - for (var i = 0; i < distributionUris.length; i += step) { + // Create sub-dataids with only 100 parts at a time + // This will avoid long running or failing construct queries for large inputs + for (var i = 0; i < distributionUris.length; i += step) { - var distributionSubset = distributionUris.slice(i, Math.min(distributionUris.length, i + step)) - var slice = Array.from(distributionlessGraphs); + var distributionSubset = distributionUris.slice(i, Math.min(distributionUris.length, i + step)) + var slice = Array.from(distributionlessGraphs); - // Add links from Dataset graph to each entry in the subset - versionGraphCopy[DatabusUris.DCAT_DISTRIBUTION] = []; - for (var j = 0; j < distributionSubset.length; j++) { - versionGraphCopy[DatabusUris.DCAT_DISTRIBUTION].push(distributionSubset[j]); - slice.push(JsonldUtils.getGraphById(expandedGraph, distributionSubset[j][DatabusUris.JSONLD_ID])); - } + // Add links from Dataset graph to each entry in the subset + versionGraphCopy[DatabusUris.DCAT_DISTRIBUTION] = []; + for (var j = 0; j < distributionSubset.length; j++) { + versionGraphCopy[DatabusUris.DCAT_DISTRIBUTION].push(distributionSubset[j]); + slice.push(JsonldUtils.getGraphById(expandedGraph, distributionSubset[j][DatabusUris.JSONLD_ID])); + } + + var triples = await constructor.executeConstruct(slice, constructVersionQuery); + var tripleCount = DatabusUtils.lineCount(triples); + logger.debug(versionGraphUri, `Construct fetched ${tripleCount} triples from subgraph`); - var triples = await constructor.executeConstruct(slice, constructVersionQuery); - var tripleCount = DatabusUtils.lineCount(triples); - logger.debug(versionGraphUri, `Construct fetched ${tripleCount} triples from subgraph`); + totalTripleCount += tripleCount; - totalTripleCount += tripleCount; + var unflattenedJsonLd = await jsonld.fromRDF(triples); - var subGraphs = await jsonld.flatten(await jsonld.fromRDF(triples)); - subGraphs = JsonldUtils.getTypedGraphs(subGraphs, DatabusUris.DATABUS_PART); + var subGraphs = await jsonld.flatten(unflattenedJsonLd); + subGraphs = JsonldUtils.getTypedGraphs(subGraphs, DatabusUris.DATABUS_PART); - // Add the constructed graphs to the result graph - for (var subGraph of subGraphs) { - dataIdGraphs.push(subGraph); - var distributionGraphEntry = {}; - distributionGraphEntry[DatabusUris.JSONLD_ID] = subGraph[DatabusUris.JSONLD_ID]; - versionGraph[DatabusUris.DCAT_DISTRIBUTION].push(distributionGraphEntry); + // Add the constructed graphs to the result graph + for (var subGraph of subGraphs) { + dataIdGraphs.push(subGraph); + var distributionGraphEntry = {}; + distributionGraphEntry[DatabusUris.JSONLD_ID] = subGraph[DatabusUris.JSONLD_ID]; + versionGraph[DatabusUris.DCAT_DISTRIBUTION].push(distributionGraphEntry); + } } - } - if (totalTripleCount == 0) { - return null; - } + if (totalTripleCount == 0) { + return null; + } - logger.debug(versionGraphUri, `${tripleCount} triples selected via construct query.`, dataIdGraphs); - return dataIdGraphs; + logger.debug(versionGraphUri, `${tripleCount} triples selected via construct query.`, dataIdGraphs); + return dataIdGraphs; + } catch(err) { + throw Error(err); + } } function validateDatasetUri(dataidGraphs, accountUri, logger) { @@ -204,6 +209,8 @@ async function createOrValidateSignature(dataidGraphs, accountUri, logger) { generatingSignature = true; proofGraph = signer.createProof(dataidGraphs); + logger.debug(versionGraphUri, `Generated proof graph.`, proofGraph); + versionGraph[DatabusUris.SEC_PROOF] = [proofGraph]; dataidGraphs = await jsonld.flatten(dataidGraphs); @@ -221,7 +228,7 @@ async function createOrValidateSignature(dataidGraphs, accountUri, logger) { //console.log(proofGraph); // Validate the proof //console.log(dataidGraphs); - + var validationSuccess = await signer.validate(signer.canonicalize(dataidGraphs), proofGraph); if (!validationSuccess) { @@ -238,13 +245,31 @@ async function createOrValidateSignature(dataidGraphs, accountUri, logger) { return 200; } -module.exports = async function publishVersion(accountName, expandedGraph, versionGraphUri, fetchFileProperties, logger) { +module.exports = async function publishVersion(accounts, expandedGraph, versionGraphUri, fetchFileProperties, logger) { try { + let versionResource = new DatabusResource(versionGraphUri); + + if(!accounts.some(a => a.accountName == versionResource.account)) { + logger.error(versionGraphUri, `No write access to resource URI.`, null); + return 401; + } + + let accountName = versionResource.account; + var versionGraph = JsonldUtils.getGraphById(expandedGraph, versionGraphUri); logger.debug(versionGraphUri, `Processing version <${versionGraphUri}>`, versionGraph); + // Run SHACL validation + var shaclResult = await shaclTester.validateVersionInputRDF(expandedGraph); + + console.log(shaclResult); + // Return failure with SHACL validation message + if (!shaclResult.isSuccess) { + logger.error(versionGraphUri, `Input validation failed`, shaclResult); + return 400; + } // Run construct query var dataidGraphs = await constructInput(expandedGraph, versionGraphUri, logger); @@ -268,10 +293,8 @@ module.exports = async function publishVersion(accountName, expandedGraph, versi autocompleter.autocomplete(dataidGraphs, logger); logger.debug(versionGraphUri, `Input after auto-completion`, dataidGraphs); - logger.debug(versionGraphUri, `fetch-file-properties is set to ${fetchFileProperties}`, null); - // Verify parts: SHA256SUM, BYTESIZE, etc if (fetchFileProperties == true || fetchFileProperties == null) { @@ -285,8 +308,8 @@ module.exports = async function publishVersion(accountName, expandedGraph, versi var cvGraphs = JsonldUtils.getSubPropertyGraphs(dataidGraphs, DatabusUris.DATABUS_CONTENT_VARIANT); // Apply data fix for byteSize, so omitting DECIMAL passes the SHACL test - for(var distribution of distributionGraphs) { - if(distribution[DatabusUris.DCAT_BYTESIZE] != null && distribution[DatabusUris.DCAT_BYTESIZE].length > 0) { + for (var distribution of distributionGraphs) { + if (distribution[DatabusUris.DCAT_BYTESIZE] != null && distribution[DatabusUris.DCAT_BYTESIZE].length > 0) { distribution[DatabusUris.DCAT_BYTESIZE][0][DatabusUris.JSONLD_TYPE] = DatabusUris.XSD_DECIMAL; } } @@ -307,15 +330,6 @@ module.exports = async function publishVersion(accountName, expandedGraph, versi return 400; } - // Run SHACL validation - var shaclResult = await shaclTester.validateVersionRDF(dataidGraphs); - - // Return failure with SHACL validation message - if (!shaclResult.isSuccess) { - logger.error(versionGraphUri, `SHACL validation failed`, shaclResult); - return 400; - } - logger.debug(versionGraphUri, `SHACL validation successful`, shaclResult); dataidGraphs = await jsonld.flatten(dataidGraphs); validationCode = await createOrValidateSignature(dataidGraphs, accountUri, logger); @@ -326,14 +340,23 @@ module.exports = async function publishVersion(accountName, expandedGraph, versi logger.debug(versionGraphUri, `Signature validation successful.`, null); - // Create compacted graph - var compactedGraph = await jsonld.compact(dataidGraphs, defaultContext); + // Run SHACL validation + var shaclResult = await shaclTester.validateVersionRDF(dataidGraphs); - if (process.env.DATABUS_CONTEXT_URL != null) { - compactedGraph[DatabusUris.JSONLD_CONTEXT] = process.env.DATABUS_CONTEXT_URL; - logger.debug(versionGraphUri, `Context has been resubstituted with <${process.env.DATABUS_CONTEXT_URL}>`); + // Return failure with SHACL validation message + if (!shaclResult.isSuccess) { + logger.error(versionGraphUri, `SHACL validation failed`, shaclResult); + return 400; } + // Create compacted graph + var compactedGraph = await jsonld.compact(dataidGraphs, process.env.DATABUS_CONTEXT_URL); + // logger.debug(versionGraphUri, `Context has been resubstituted with <${process.env.DATABUS_CONTEXT_URL}>`); + + //if (process.env.DATABUS_CONTEXT_URL != null) { + // compactedGraph[DatabusUris.JSONLD_CONTEXT] = process.env.DATABUS_CONTEXT_URL; + //} + // Create the target path for the gstore var targetPath = UriUtils.getPrunedPath(`${versionGraphUri}/${Constants.DATABUS_FILE_DATAID}`); logger.debug(versionGraphUri, `Saving dataset to ${accountName}:${targetPath}`, compactedGraph); @@ -343,11 +366,13 @@ module.exports = async function publishVersion(accountName, expandedGraph, versi // Return failure if (!publishResult.isSuccess) { - logger.error(versionGraphUri, `Internal database error`, null); - return 500; + var statusCode = publishResult.statusCode; + delete publishResult.statusCode; + logger.error(versionGraphUri, `Failed to save version to database.`, publishResult); + return statusCode; } - if(process.send != undefined) { + if (process.send != undefined) { process.send({ id: DatabusMessage.REQUEST_SEARCH_INDEX_REBUILD, resource: versionGraphUri diff --git a/server/app/api/lib/resource-writer.js b/server/app/api/lib/resource-writer.js new file mode 100644 index 00000000..5a688b96 --- /dev/null +++ b/server/app/api/lib/resource-writer.js @@ -0,0 +1,178 @@ + +const DatabusUtils = require('../../../../public/js/utils/databus-utils'); +const DatabusMessage = require('../../common/databus-message'); +const ApiError = require('../../common/utils/api-error'); +const GstoreResource = require('./gstore-resource'); +const shaclTester = require('../../common/shacl-tester'); +const jsonld = require('jsonld'); +const axios = require('axios'); +const JsonldLoader = require('../../common/utils/jsonld-loader'); +const DatabusResource = require('../../common/databus-resource'); +const DatabusUris = require('../../../../public/js/utils/databus-uris'); + +/** + * Base class for all writers: + * CollectionWriter + * GroupWriter + * AccountWriter + * ArtifactWriter + * TODO: VersionWriter + */ +class ResourceWriter { + + constructor(logger) { + this.logger = logger; + } + + /** + * + * @param {user data with account name and OIDC sub} userData + * @param {the input JSONLD graphs} inputGraphs + * @param {uri of the resource to write} uri + */ + async writeResource(req, userData, inputGraphs, uri) { + this.userData = userData; + this.inputGraphs = inputGraphs; + this.uri = uri; + + // Create Databus resource object for segment parsing + this.resource = new DatabusResource(uri); + + var baseURL = process.env.DATABUS_RESOURCE_BASE_URL; + + this.logger.debug(`Processing resource <${uri}}>"...`); + + // First base URL prefix check + if (!uri.startsWith(baseURL)) { + let message = `Identifier <${uri}> does not start with the resource base url <${baseURL}> of this Databus.`; + throw new ApiError(400, uri, message, null); + } + + // Validate the user - checks URI prefix and account name + await this.onValidateUser(req); + + // Create the graphs - abstract method implemented by the different writers + var graphs = await this.onCreateGraphs(); + + console.log("INPUT FOR SHACL TEST"); + console.log(JSON.stringify(graphs, null, 3)); + + + // Do SHACL validation - calls abstract getSHACLFilePath() + var shaclResult = await shaclTester.validateJsonld(graphs, this.getSHACLFilePath()); + + console.log("SHACL RESULT"); + console.log(JSON.stringify(shaclResult, null, 3)); + + if (!shaclResult.isSuccess) { + var message = 'SHACL validation error:\n'; + for (var m in shaclResult.messages) { + message += `>>> ${shaclResult.messages[m]}\n` + } + + throw new ApiError(400, this.uri, message, shaclResult.report); + } + + // Compact the graph with the default context + var compactedGraph = await jsonld.compact(graphs, JsonldLoader.DEFAULT_CONTEXT_URL); + this.logger.debug(this.uri, `Compacted with context <${JsonldLoader.DEFAULT_CONTEXT_URL}>`); + + try { + + // Save the compacted graph to the gstore + this.logger.debug(this.uri, `Saving to gstore.`); + var gstoreResource = new GstoreResource(this.uri, compactedGraph); + + + await gstoreResource.save(); + + this.logger.info(this.uri, `Successfully published ${this.resource.getTypeName()} <${this.uri}>.`, compactedGraph); + } catch (err) { + + console.log(JSON.stringify(err, null, 3)); + console.log(JSON.stringify(compactedGraph, null, 3)); + let message = `Failed to save to gstore: ${err.message}`; + throw new ApiError(500, this.uri, message, compactedGraph); + } + + // Send a message to invoke the resource indexer + if (process.send != undefined) { + process.send({ + id: DatabusMessage.REQUEST_SEARCH_INDEX_REBUILD, + resource: this.uri + }); + } + + } + + /** + * ABSTRACT - implemented in the different writers + * @returns the file path of the SHACL file for graph validation + */ + getSHACLFilePath() { + return ''; + } + + /** + * VIRTUAL - overriden in AccountWriter to allow registering of new users + * Validates user account name against the resource identifiers + */ + async onValidateUser(req) { + var accountName = this.resource.account; + var accountUri = this.resource.getAccountURI(); + + + if (this.userData.accounts && this.userData.accounts.some(acc => acc.accountName == accountName)) { + return; + } + + const onBehalfOf = req.headers['x-on-behalf-of']; + + if (onBehalfOf && onBehalfOf == accountUri) { + try { + const response = await axios.get(onBehalfOf, { + headers: { + 'Content-Type': 'application/ld+json', + 'Accept': 'application/ld+json' + } + }); + + const expanded = await jsonld.expand(response.data); + + const secretaries = expanded.flatMap(e => + e[DatabusUris.DATABUS_SECRETARY_PROPERTY] || [] + ).map(a => a[DatabusUris.DATABUS_ACCOUNT_PROPERTY][0]); + + for(var secretary of secretaries) { + var accountResource = new DatabusResource(secretary[DatabusUris.JSONLD_ID]); + + if(!accountResource.isAccount()) { + continue; + } + + let secretaryName = accountResource.getAccount(); + + if (this.userData.accounts && this.userData.accounts.some(acc => acc.accountName == secretaryName)) { + return; + } + } + + } catch (_) { + // fall through to error below + } + } + + const message = `Authenticated user does not have write access to the account <${accountName}>.`; + throw new ApiError(403, this.uri, message, null); + } + + /** + * ABSTRACT - implemented in the different writers + * @returns a list of graphs to save to the database + */ + async onCreateGraphs() { + return []; + } +} + +module.exports = ResourceWriter; \ No newline at end of file diff --git a/server/app/api/module.js b/server/app/api/module.js index 6ac9f94f..3e52d150 100644 --- a/server/app/api/module.js +++ b/server/app/api/module.js @@ -16,7 +16,4 @@ module.exports = function (router, protector, locals, webdav) { var manifest = require('../../manifest.ttl'); res.status(200).send(`${manifest}`); }); - - - } \ No newline at end of file diff --git a/server/app/api/routes/account.js b/server/app/api/routes/account.js index d6e127bd..790a4917 100644 --- a/server/app/api/routes/account.js +++ b/server/app/api/routes/account.js @@ -1,88 +1,259 @@ -const ServerUtils = require('../../common/utils/server-utils'); -const DatabusCache = require('../../common/cache/databus-cache'); -const JsonldUtils = require('../../../../public/js/utils/jsonld-utils'); -const DatabusUtils = require('../../../../public/js/utils/databus-utils'); - -var GstoreHelper = require('../../common/utils/gstore-helper'); -var shaclTester = require('../../common/shacl-tester'); -var request = require('request'); var jsonld = require('jsonld'); -var fs = require('fs'); -const pem2jwk = require('pem-jwk').pem2jwk; const requestRDF = require('../../common/request-rdf'); - const defaultContext = require('../../common/res/context.jsonld'); const getLinkedData = require("../../common/get-linked-data"); +var cors = require('cors'); +var signer = require('../lib/databus-tractate-suite.js'); -var constructor = require('../../common/execute-construct.js'); -var constructAccountQuery = require('../../common/queries/constructs/construct-account.sparql'); +const ServerUtils = require('../../common/utils/server-utils'); +const JsonldUtils = require('../../../../public/js/utils/jsonld-utils'); +const DatabusUtils = require('../../../../public/js/utils/databus-utils'); +var GstoreHelper = require('../../common/utils/gstore-helper'); const DatabusUris = require('../../../../public/js/utils/databus-uris'); const Constants = require('../../common/constants'); const UriUtils = require('../../common/utils/uri-utils'); const DatabusConstants = require('../../../../public/js/utils/databus-constants'); -const publishAccount = require('../lib/publish-account'); +const GstoreResource = require('../lib/gstore-resource'); +const JsonldLoader = require('../../common/utils/jsonld-loader.js'); +const DatabusMessage = require('../../common/databus-message.js'); + + module.exports = function (router, protector) { + async function createAccountGraphs(uri, name, label, img, secretaries, status) { + var name = UriUtils.uriToName(uri); - router.put('/:account', protector.protect(), async function (req, res, next) { + var rsaKeyGraph = {}; + rsaKeyGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.CERT_RSA_PUBLIC_KEY; + rsaKeyGraph[DatabusUris.RDFS_LABEL] = DatabusConstants.WEBID_SHARED_PUBLIC_KEY_LABEL; + rsaKeyGraph[DatabusUris.CERT_MODULUS] = signer.getModulus(); + rsaKeyGraph[DatabusUris.CERT_EXPONENT] = 65537; - // requesting user does not have an account yet - if (req.databus.accountName == undefined) { + var personUri = `${uri}${DatabusConstants.WEBID_THIS}`; - // oidc not set correctly? - if (req.oidc.user == undefined) { - res.status(403).send(`Forbidden.\n`); - return; - } + var personGraph = {}; + personGraph[DatabusUris.JSONLD_ID] = personUri; + personGraph[DatabusUris.JSONLD_TYPE] = [DatabusUris.FOAF_PERSON, DatabusUris.DBP_DBPEDIAN]; + personGraph[DatabusUris.FOAF_ACCOUNT] = JsonldUtils.refTo(uri); + personGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY] = uri; + personGraph[DatabusUris.CERT_KEY] = [rsaKeyGraph]; + personGraph[DatabusUris.FOAF_NAME] = label; - // trying to be that guy? - if (req.params.account == `sparql`) { - res.status(403).send(`Forbidden.\n`); - return; - } + if (img != null) { + personGraph[DatabusUris.FOAF_IMG] = img; + } - // account taken? - var accountExists = await protector.hasUser(req.params.account); + if (status != null) { + personGraph[DatabusUris.FOAF_STATUS] = status; + } - if (accountExists) { - // deny, this account name is taken - res.status(401).send(`This account name is taken.\n`); - return; - } else { - // allow write to the account namespace - req.databus.accountName = req.params.account; + var profileUri = `${uri}${DatabusConstants.WEBID_DOCUMENT}`; - try { - await protector.addUser(req.oidc.user.sub, req.params.account, req.params.account); - } catch(err) { - res.status(500).send(`Failed to write to user database`); - return; - } - /** - var result = await publishAccount(req.databus.accountName, req.body); + var profileDocumentGraph = {}; + profileDocumentGraph[DatabusUris.JSONLD_ID] = profileUri; + profileDocumentGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.FOAF_PERSONAL_PROFILE_DOCUMENT; + profileDocumentGraph[DatabusUris.FOAF_MAKER] = JsonldUtils.refTo(personUri); + profileDocumentGraph[DatabusUris.FOAF_PRIMARY_TOPIC] = JsonldUtils.refTo(personUri); - if (result.isSuccess) { - await protector.addUser(req.oidc.user.sub, req.params.account, req.params.account); - res.status(201).send(result.message); - return; + var accountGraph = {} + accountGraph[DatabusUris.JSONLD_ID] = uri; + accountGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.DATABUS_ACCOUNT; + accountGraph[DatabusUris.FOAF_ACCOUNT_NAME] = name; + accountGraph[DatabusUris.DATABUS_NAME] = name; + + if (secretaries != null) { + + accountGraph[DatabusUris.DATABUS_SECRETARY_PROPERTY] = []; + + for (var secretary of secretaries) { + + let secretaryAccountUri = `${secretary.accountName}`; + + let secretaryGraph = {}; + secretaryGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.DATABUS_SECRETARY; + secretaryGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY] = JsonldUtils.refTo(secretaryAccountUri); + + if (secretary.hasWriteAccessTo != undefined) { + secretaryGraph[DatabusUris.DATABUS_HAS_WRITE_ACCESS_TO] = []; + + for (var writeAccess of secretary.hasWriteAccessTo) { + secretaryGraph[DatabusUris.DATABUS_HAS_WRITE_ACCESS_TO].push(JsonldUtils.refTo(writeAccess)); + } } - res.status(result.statusCode).send(result.message);*/ + accountGraph[DatabusUris.DATABUS_SECRETARY_PROPERTY].push(secretaryGraph); + } + } + + let expandedGraphs = [ + accountGraph, + personGraph, + profileDocumentGraph + ]; + + return await jsonld.compact(expandedGraphs, JsonldLoader.DEFAULT_CONTEXT_URL); + } + + + + router.post('/api/account/create', protector.protect(), async function (req, res, next) { + + let userId = req.databus.userId; + let userdb = protector.userdb; + + if (userId == undefined) { + return res.status(401).send('No User Id!'); + } + + let accountName = req.body.name; + let accountLabel = req.body.label; + + if (!accountName) { + return res.status(400).send('Missing account name or label.'); + } + + if (!accountLabel) { + accountLabel = accountName; + } + + var accountExists = await userdb.hasAccount(accountName); + + if (accountExists) { + res.status(403).send(`Account name ${accountName} taken.`); + return; + } + + if (!await userdb.addAccount(userId, accountName)) { + res.status(500).send('Failed to write to user database.'); + return; + } + + + try { + let accountUri = `${process.env.DATABUS_RESOURCE_BASE_URL}/${accountName}`; + let content = await createAccountGraphs(accountUri, accountName, accountLabel, null, null, null); + + let gstoreResource = new GstoreResource(accountUri, content); + let status = await gstoreResource.save(); + + console.log(status); + res.status(200).send('Account created.'); + + if (process.send != undefined) { + process.send({ + id: DatabusMessage.REQUEST_SEARCH_INDEX_REBUILD, + resource: accountUri + }); } + + } catch (err) { + await userdb.deleteAccount(accountName); + res.status(500).send('Failed to write to gstore.'); + } + }); + + router.post('/api/account/update', protector.protect(), async function (req, res, next) { + + let userId = req.databus.userId; + let userdb = protector.userdb; + + if (userId == undefined) { + res.status(401).send('No User Id!'); + return; + } + + const accountName = req.body.accountName; + var existingAccount = await userdb.getAccount(accountName); + + if (existingAccount == null) { + res.status(400).send('Account does not exist.'); + return; } - if(req.databus.accountName != req.params.account) { - res.status(403).send(`Trying to write to an unowned account namespace.\n`); + if (existingAccount.id != userId) { + res.status(403).send('You do not own this account.'); return; } - var result = await publishAccount(req.databus.accountName, req.body); - res.status(result.statusCode).send(result.message); + let input = req.body; + + try { + var accountLabel = req.body.label; + var accountStatus = req.body.status; + var imageUrl = req.body.imageUrl; + var secretaries = req.body.secretaries; + let accountUri = `${process.env.DATABUS_RESOURCE_BASE_URL}/${accountName}`; + let content = await createAccountGraphs(accountUri, accountName, accountLabel, imageUrl, secretaries, accountStatus); + + let gstoreResource = new GstoreResource(accountUri, content); + let status = await gstoreResource.save(); + + console.log(status); + res.status(200).send('Account saved.'); + + if (process.send != undefined) { + process.send({ + id: DatabusMessage.REQUEST_SEARCH_INDEX_REBUILD, + resource: accountUri + }); + } + + } catch (err) { + res.status(500).send('Failed to write to gstore.'); + return; + } }); - + router.post('/api/account/delete', protector.protect(), async function (req, res, next) { + + // Get id of authenticated user + let userId = req.databus.userId; + + if (userId == undefined) { + return res.status(401).send('No User Id!'); + } + + // Get account to delete from database + let accountName = req.body.accountName; + let userdb = protector.userdb; + var account = await userdb.getAccount(accountName); + + if (account == null) { + res.status(404).send('Account does not exist.'); + return; + } + + // Check if account belongs to authenticated + if (account.id != userId) { + return res.status(401).send('Account not owned.'); + } + + if (!await userdb.deleteAccount(accountName)) { + res.status(500).send('Failed to delete user from database.'); + return; + } + + try { + let accountUri = `${process.env.DATABUS_RESOURCE_BASE_URL}/${accountName}`; + let gstoreResource = new GstoreResource(accountUri); + + await gstoreResource.delete(); + res.status(200).send('Account deleted.'); + + if (process.send != undefined) { + process.send({ + id: DatabusMessage.REQUEST_SEARCH_INDEX_REBUILD, + resource: accountUri + }); + } + + } catch (err) { + await userdb.addAccount(account.id, account.accountName); + res.status(500).send('Failed to write to gstore.'); + } + }); router.post('/api/account/webid/remove', protector.protect(), async function (req, res, next) { try { @@ -354,16 +525,16 @@ module.exports = function (router, protector) { // Create api key for user var auth = ServerUtils.getAuthInfoFromRequest(req); + var keyname = decodeURIComponent(req.body.keyname); + var accountName = decodeURIComponent(req.body.accountName); - if (auth.info.accountName == undefined) { - res.status(403).send('Account name is missing. Please claim an account name first.'); + if (!auth.info.accounts.some(a => a.accountName == accountName)) { + res.status(403).send(`Not allowed to create API key for account '${accountName}'`); return; } - var keyName = decodeURIComponent(req.query.name); - - if (!DatabusUtils.isValidResourceLabel(keyName, 3, 20)) { - res.status(403).send('Invalid API key name. API key name should match [A-Za-z0-9\\s_()\\.\\,\\-]{3,20}'); + if (!DatabusUtils.isValidResourceLabel(keyname, 3, 20)) { + res.status(400).send('Invalid API key name. API key name should match [A-Za-z0-9\\s_()\\.\\,\\-]{3,20}'); return; } @@ -372,7 +543,7 @@ module.exports = function (router, protector) { return; } - var apiKey = await protector.addApiKey(req.databus.sub, keyName); + var apiKey = await protector.addApiKey(accountName, keyname); if (apiKey == null) { res.status(400).send("Failed to create API key. You might already have an API key with that name."); @@ -387,13 +558,10 @@ module.exports = function (router, protector) { // Create api key for user var auth = ServerUtils.getAuthInfoFromRequest(req); - if (auth.info.accountName == undefined) { - res.status(403).send('Account name is missing.'); - return; - } + var keyname = decodeURIComponent(req.body.keyname); + var accountName = decodeURIComponent(req.body.accountName); - var keyName = decodeURIComponent(req.query.name); - var found = await protector.removeApiKey(req.databus.sub, keyName); + var found = await protector.removeApiKey(accountName, keyname); if (found) { res.status(200).send(); @@ -403,7 +571,7 @@ module.exports = function (router, protector) { }); /* GET an account. */ - router.get('/:account', ServerUtils.NOT_HTML_ACCEPTED, async function (req, res, next) { + router.get('/:account', ServerUtils.NOT_HTML_ACCEPTED, cors(), async function (req, res, next) { if (req.params.account.length < 4) { next('route'); diff --git a/server/app/api/routes/artifact.js b/server/app/api/routes/artifact.js index 48747b3f..6f0839c5 100644 --- a/server/app/api/routes/artifact.js +++ b/server/app/api/routes/artifact.js @@ -1,23 +1,20 @@ const ServerUtils = require("../../common/utils/server-utils"); -const DatabusUris = require("../../../../public/js/utils/databus-uris"); const Constants = require("../../common/constants"); const GstoreHelper = require('../../common/utils/gstore-helper'); const JsonldUtils = require("../../../../public/js/utils/jsonld-utils"); const DatabusLogger = require("../../common/databus-logger"); const UriUtils = require("../../common/utils/uri-utils"); - -const publishArtifact = require('../lib/publish-artifact'); -const defaultContext = require('../../common/res/context.jsonld'); const getLinkedData = require("../../common/get-linked-data"); +const ArtifactWriter = require("../lib/artifact-writer"); const jsonld = require('jsonld'); - +var cors = require('cors'); const sparql = require("../../common/queries/sparql"); module.exports = function (router, protector) { /** * Publishing of artifacts via PUT request - */ + router.put('/:account/:group/:artifact', protector.protect(true), async function (req, res, next) { try { @@ -29,20 +26,9 @@ module.exports = function (router, protector) { ]); - // Requesting a PUT on an uri outside of one's namespace is rejected - if (req.params.account != req.databus.accountName) { - res.status(403).send(MESSAGE_WRONG_NAMESPACE); - return; - } - var logger = new DatabusLogger(req.query['log-level']); var graph = req.body; - if (graph[DatabusUris.JSONLD_CONTEXT] == process.env.DATABUS_DEFAULT_CONTEXT_URL) { - graph[DatabusUris.JSONLD_CONTEXT] = defaultContext; - logger.debug(null, `Context "${graph[DatabusUris.JSONLD_CONTEXT]}" replaced with cached resolved context`, defaultContext); - } - // Expand JSONLD! var expandedGraph = await jsonld.flatten(graph); @@ -55,18 +41,26 @@ module.exports = function (router, protector) { return; } - logger.debug(null, `Found graph ${artifactUri} in the input.`, artifactGraph); + try { + var artifactWriter = new ArtifactWriter(logger); + await artifactWriter.writeResource(req.databus, expandedGraph, artifactUri); + } + catch (apiError) { + logger.error(apiError.resource, apiError.message, apiError.body); + res.status(apiError.statusCode).json(logger.getReport()); + return; + } - var code = await publishArtifact(req.params.account, artifactGraph, logger); - res.status(code).json(logger.getReport()); + res.status(200).json(logger.getReport()) } catch (err) { console.log(err); res.status(500).send(err); } }); + */ - router.get('/:account/:group/:artifact', ServerUtils.NOT_HTML_ACCEPTED, async function (req, res, next) { + router.get('/:account/:group/:artifact', ServerUtils.NOT_HTML_ACCEPTED, cors(), async function (req, res, next) { if (req.params.account.length < 4) { next('route'); @@ -103,12 +97,19 @@ module.exports = function (router, protector) { } // Delete from gstore and return result - var gstorePath = `${req.params.group}/${req.params.artifact}/${Constants.DATABUS_FILE_ARTIFACT}`; + var gstorePath = `${req.params.group}/${req.params.artifact}/metadata.jsonld`; var result = await GstoreHelper.delete(req.params.account, gstorePath); if (!result.isSuccess) { - res.status(500).send(`Internal database error. Failed to delete artifact <${artifactUri}>.`); - return; + + gstorePath = `${req.params.group}/${req.params.artifact}/artifact.jsonld`; + result = await GstoreHelper.delete(req.params.account, gstorePath); + + if (!result.isSuccess) { + res.status(result.error.status).send(`Failed to delete artifact <${artifactUri}>: ${result.error.message}`); + return; + } + } res.status(204).send(`The artifact <${artifactUri}> has been deleted.`); diff --git a/server/app/api/routes/collection.js b/server/app/api/routes/collection.js index 59de9d95..0e7bed08 100644 --- a/server/app/api/routes/collection.js +++ b/server/app/api/routes/collection.js @@ -4,7 +4,7 @@ const Constants = require('../../common/constants.js'); const DatabusUris = require('../../../../public/js/utils/databus-uris'); var sparql = require('../../common/queries/sparql'); var GstoreHelper = require('../../common/utils/gstore-helper'); -var sparql = require('../../common/queries/sparql'); +var exec = require('../../common/execute-query'); var shaclTester = require('../../common/shacl-tester'); var jsonld = require('jsonld'); var constructor = require('../../common/execute-construct.js'); @@ -16,6 +16,9 @@ var defaultContext = require('./../../common/res/context.jsonld'); const DatabusConstants = require('../../../../public/js/utils/databus-constants'); const DatabusUtils = require('../../../../public/js/utils/databus-utils'); const DatabusMessage = require('../../common/databus-message'); +var cors = require('cors'); +const QueryBuilder = require('../../../../public/js/query-builder/query-builder.js'); +const QueryTemplates = require('../../../../public/js/query-builder/query-templates.js'); module.exports = function (router, protector) { @@ -72,7 +75,7 @@ module.exports = function (router, protector) { // Set publisher collectionGraph[DatabusUris.DCT_PUBLISHER] = [{}]; collectionGraph[DatabusUris.DCT_PUBLISHER][0][DatabusUris.JSONLD_ID] = publisherUri; - + // Set account collectionGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY] = [{}]; collectionGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY][0][DatabusUris.JSONLD_ID] = accountUri; @@ -121,7 +124,7 @@ module.exports = function (router, protector) { console.log(`Saving collection <${collectionUri}> to ${req.params.account}:${targetPath}`); var publishResult = await GstoreHelper.save(req.params.account, targetPath, compactedGraph); - if(process.send != undefined) { + if (process.send != undefined) { process.send({ id: DatabusMessage.REQUEST_SEARCH_INDEX_REBUILD, resource: collectionUri @@ -155,13 +158,12 @@ module.exports = function (router, protector) { router.delete('/:account/collections/:collection', protector.protect(), async function (req, res, next) { try { - if (req.params.account != req.databus.accountName) { + if (!ServerUtils.hasWriteAccess(req, req.params.account)) { res.status(403).send('You cannot edit collections in a foreign namespace.\n'); return; } - var targetPath = `collections/${req.params.collection}/collection.jsonld`; - + var targetPath = `collections/${req.params.collection}/metadata.jsonld`; var resource = await GstoreHelper.read(req.params.account, targetPath); if (resource == null) { @@ -177,7 +179,7 @@ module.exports = function (router, protector) { } // Return success - res.status(200).send('Collection deleted successfully.\n'); + res.status(204).send('Collection deleted successfully.\n'); } catch (err) { console.log(err); @@ -185,21 +187,40 @@ module.exports = function (router, protector) { } }); - router.get('/:account/collections/:collection', ServerUtils.SPARQL_ACCEPTED, async function (req, res, next) { + router.get('/:account/collections/:collection', ServerUtils.SPARQL_ACCEPTED, cors(), async function (req, res, next) { + + try { + var collectionUri = UriUtils.createResourceUri([req.params.account, 'collections', req.params.collection]); + + var queryOptions = {}; + queryOptions.COLLECTION_URI = collectionUri; + + var selectQuery = require('../../common/queries/sparql/get-collection-content.sparql'); + selectQuery = exec.formatQuery(selectQuery, queryOptions); + + var entry = await exec.executeSelect(selectQuery); - sparql.collections.getCollectionQuery(req.params.account, req.params.collection).then(function (result) { - if (result != null) { - res.status(200).send(result); - } else { - next('route'); + if (entry.length == 0) { + res.status(404).send(); + return; } - }, function (err) { + + var root = JSON.parse(unescape(entry[0].content)).root; + + var sparqlQuery = QueryBuilder.build({ + node: root, + template: QueryTemplates.DEFAULT_FILE_TEMPLATE, + resourceBaseUrl: process.env.DATABUS_RESOURCE_BASE_URL + }); + + res.status(200).send(sparqlQuery); + } catch (err) { console.log(err); res.status(500).send(); - }); + } }); - router.get('/:account/collections/:collection', ServerUtils.NOT_HTML_ACCEPTED, function (req, res, next) { + router.get('/:account/collections/:collection', ServerUtils.NOT_HTML_ACCEPTED, cors(), function (req, res, next) { if (req.params.account.length < 4) { next('route'); diff --git a/server/app/api/routes/general.js b/server/app/api/routes/general.js index f9d65f0f..af192602 100644 --- a/server/app/api/routes/general.js +++ b/server/app/api/routes/general.js @@ -1,14 +1,22 @@ var http = require('http'); var request = require('request'); +const jsonld = require('jsonld'); var cors = require('cors'); -var defaultContext = require('../../common/res/context.jsonld'); -const publishGroup = require('../lib/publish-group'); const publishVersion = require('../lib/publish-version'); -const DatabusUris = require('../../../../public/js/utils/databus-uris'); -const publishArtifact = require('../lib/publish-artifact'); const JsonldUtils = require('../../../../public/js/utils/jsonld-utils'); -var jsonld = require('jsonld'); +const DatabusUris = require('../../../../public/js/utils/databus-uris'); const DatabusLogger = require('../../common/databus-logger'); +const GroupWriter = require('../lib/group-writer'); +const ArtifactWriter = require('../lib/artifact-writer'); +const CollectionWriter = require('../lib/collection-writer'); +const ApiError = require('../../common/utils/api-error'); +var SparqlParser = require('sparqljs').Parser; + +const ALLOWED_QUERY_TYPES = [ + "SELECT", "ASK", "DESCRIBE", "CONSTRUCT" +] + +const MSG_NO_GRAPH_FOUND = `No processable graphs found in the input. Your input has to contain at least one graph of either type databus:Group, databus:Artifact or databus:Version.` module.exports = function (router, protector, webdav) { @@ -31,11 +39,22 @@ module.exports = function (router, protector, webdav) { var query = req.body.query; - - - var sparqlEndpoint = `${process.env.DATABUS_DATABASE_URL}/sparql`; var accept = req.headers['accept'] + + try { + var parser = new SparqlParser({ skipValidation: true }); + var parsedQuery = parser.parse(query); + + if(!ALLOWED_QUERY_TYPES.includes(parsedQuery.queryType)) { + res.status(403).send("FORBIDDEN: SPARQL updates are disabled. Please use the API for write operations."); + return; + } + + } + catch(err) { + // Do nothing and let the virtuoso endpoint handle error reporting + } if (accept == undefined) { @@ -55,12 +74,52 @@ module.exports = function (router, protector, webdav) { request.post(options).pipe(res); }); - router.post('/api/publish', protector.protect(true), async function (req, res, next) { + router.post('/api/register', protector.protect(true), registerData); + router.post('/api/publish', protector.protect(true), registerData); + + /** + * Tries to create a new user in the user database + * @param {subect of the logged in user} sub + * @param {account name of the logged in user} accountName + * @returns + */ + async function createUser(sub, accountName) { + + var accountExists = await protector.hasUser(accountName); + console.log(`Account does not exist yet!`); + + if(accountExists) { + throw new ApiError(401, accountName, `Account <${accountName}> already exists.`, null); + } + + try { + + console.log(`Adding to user database...`); + await protector.addUser(sub, accountName, accountName); + + return { + sub: sub, + accountName: accountName + }; + } catch(err) { + console.log(err); + throw new ApiError(500, accountName, `Failed to write to user database`, null); + } + } + + + async function registerData(req, res, next) { try { // Get the account namespace - var account = req.databus.accountName; + var accounts = req.databus.accounts; + + var userData = { + sub: req.databus.sub, + accounts: req.databus.accounts + }; + var verifyParts = null; if(req.query['fetch-file-properties'] == "false") { @@ -72,55 +131,59 @@ module.exports = function (router, protector, webdav) { } var logger = new DatabusLogger(req.query['log-level']); - var graph = req.body; - var processedResources = 0; + var expandedGraphs = await jsonld.flatten(req.body); + + try { + // Publish collections + var collectionGraphs = JsonldUtils.getTypedGraphs(expandedGraphs, DatabusUris.DATABUS_COLLECTION); + logger.debug(null, `Found ${collectionGraphs.length} collection graphs.`, null); + + for (var collectionGraph of collectionGraphs) { + processedResources++; + var collectionWriter = new CollectionWriter(logger); + await collectionWriter.writeResource(req, userData, expandedGraphs, collectionGraph[DatabusUris.JSONLD_ID]); + } - if (graph[DatabusUris.JSONLD_CONTEXT] == process.env.DATABUS_DEFAULT_CONTEXT_URL) { - graph[DatabusUris.JSONLD_CONTEXT] = defaultContext; - logger.debug(null, `Context "${graph[DatabusUris.JSONLD_CONTEXT]}" replaced with cached resolved context`, defaultContext); - } + // Publish groups + var groupGraphs = JsonldUtils.getTypedGraphs(expandedGraphs, DatabusUris.DATABUS_GROUP); + logger.debug(null, `Found ${groupGraphs.length} group graphs.`, null); - // Expand JSONLD! - var expandedGraph = await jsonld.flatten(graph); + for (var collectionGraph of groupGraphs) { + processedResources++; + var groupWriter = new GroupWriter(logger); + await groupWriter.writeResource(req, userData, expandedGraphs, collectionGraph[DatabusUris.JSONLD_ID]); + } - // Publish groups - var groupGraphs = JsonldUtils.getTypedGraphs(expandedGraph, DatabusUris.DATABUS_GROUP); - logger.debug(null, `Found ${groupGraphs.length} group graphs.`, null); - processedResources += groupGraphs.length; - // console.log(groupGraphs); + // Publish artifacts + var artifactGraphs = JsonldUtils.getTypedGraphs(expandedGraphs, DatabusUris.DATABUS_ARTIFACT); + logger.debug(null, `Found ${artifactGraphs.length} artifact graphs.`, null); - for (var groupGraph of groupGraphs) { - var resultCode = await publishGroup(account, groupGraph, logger); + for (var artifactGraph of artifactGraphs) { + processedResources++; - if (resultCode != 200) { - res.status(resultCode).json(logger.getReport()); - return; + var artifactWriter = new ArtifactWriter(logger); + await artifactWriter.writeResource(req, userData, expandedGraphs, artifactGraph[DatabusUris.JSONLD_ID]); } - } - - // Publish artifacts - var artifactGraphs = JsonldUtils.getTypedGraphs(expandedGraph, DatabusUris.DATABUS_ARTIFACT); - logger.debug(null, `Found ${artifactGraphs.length} artifact graphs.`, null); - processedResources += artifactGraphs.length; - for (var artifactGraph of artifactGraphs) { - var resultCode = await publishArtifact(account, artifactGraph, logger); + // Publish version - if (resultCode != 200) { - res.status(resultCode).json(logger.getReport()); - return; - } + + } + catch(apiError) { + logger.error(apiError.resource, apiError.message, apiError.body); + res.status(apiError.statusCode).json(logger.getReport()); + return; } // Publish versions - var datasetGraphs = JsonldUtils.getTypedGraphs(expandedGraph, DatabusUris.DATABUS_VERSION); + var datasetGraphs = JsonldUtils.getTypedGraphs(expandedGraphs, DatabusUris.DATABUS_VERSION); logger.debug(null, `Found ${datasetGraphs.length} version graphs.`, null); processedResources += datasetGraphs.length; for (var datasetGraph of datasetGraphs) { var datasetGraphUri = datasetGraph[DatabusUris.JSONLD_ID]; - var resultCode = await publishVersion(account, expandedGraph, datasetGraphUri, verifyParts, logger); + var resultCode = await publishVersion(accounts, expandedGraphs, datasetGraphUri, verifyParts, logger); if (resultCode != 200) { res.status(resultCode).json(logger.getReport()); @@ -129,7 +192,7 @@ module.exports = function (router, protector, webdav) { } if(processedResources == 0) { - logger.error(null, `No processable graphs found in the input.`, req.body); + logger.error(null, MSG_NO_GRAPH_FOUND, req.body); res.status(400).json(logger.getReport()); return; } @@ -139,9 +202,8 @@ module.exports = function (router, protector, webdav) { } catch (err) { console.log(err); res.status(500).send(err); - } - }); - + } + } router.get('/api/search', cors(), function (req, res, next) { @@ -153,7 +215,7 @@ module.exports = function (router, protector, webdav) { first = false; } - var search = `http://localhost:8082/api/search${queryString}`; + var search = `${process.env.LOOKUP_BASE_URL}/api/search${queryString}`; http.get(search, function (response) { response.setEncoding('utf8'); diff --git a/server/app/api/routes/group.js b/server/app/api/routes/group.js index 9db11333..be6a0d24 100644 --- a/server/app/api/routes/group.js +++ b/server/app/api/routes/group.js @@ -5,19 +5,19 @@ const GstoreHelper = require('../../common/utils/gstore-helper'); const JsonldUtils = require("../../../../public/js/utils/jsonld-utils"); const DatabusLogger = require("../../common/databus-logger"); const UriUtils = require("../../common/utils/uri-utils"); -const publishGroup = require('../lib/publish-group'); const jsonld = require('jsonld'); const getLinkedData = require("../../common/get-linked-data"); -const defaultContext = require('../../common/res/context.jsonld'); +var cors = require('cors'); const sparql = require("../../common/queries/sparql"); +const GroupWriter = require("../lib/group-writer"); module.exports = function (router, protector) { /** * Publishing of groups via PUT request - */ + router.put('/:account/:group', protector.protectAccount(true), async function (req, res, next) { try { @@ -30,34 +30,35 @@ module.exports = function (router, protector) { var logger = new DatabusLogger(req.query['log-level']); var graph = req.body; - if (graph[DatabusUris.JSONLD_CONTEXT] == process.env.DATABUS_DEFAULT_CONTEXT_URL) { - graph[DatabusUris.JSONLD_CONTEXT] = defaultContext; - logger.debug(null, `Context "${graph[DatabusUris.JSONLD_CONTEXT]}" replaced with cached resolved context`, defaultContext); - } - - var expandedGraph = await jsonld.flatten(graph); - - // Publish groups + let expandedGraph = await jsonld.expand(graph); var groupGraph = JsonldUtils.getGraphById(expandedGraph, groupUri); - + if (groupGraph == null) { logger.error(null, `No graph ${groupUri} found in the input.`, null); res.status(400).json(logger.getReport()); return; } - logger.debug(null, `Found graph ${groupUri} in the input.`, groupGraph); - - var code = await publishGroup(req.params.account, groupGraph, logger); - res.status(code).json(logger.getReport()); + try { + var groupWriter = new GroupWriter(logger); + await groupWriter.writeResource(req.databus, expandedGraph, groupUri); + } + + catch (apiError) { + logger.error(apiError.resource, apiError.message, apiError.body); + res.status(apiError.statusCode).json(logger.getReport()); + return; + } + + res.status(200).json(logger.getReport()) } catch (err) { console.log(err); res.status(500).send(err); } - }); + }); */ - router.get('/:account/:group', ServerUtils.NOT_HTML_ACCEPTED, async function (req, res, next) { + router.get('/:account/:group', ServerUtils.NOT_HTML_ACCEPTED, cors(), async function (req, res, next) { if (req.params.account.length < 4) { next('route'); @@ -94,12 +95,19 @@ module.exports = function (router, protector) { } // Delete from gstore and return result - var gstorePath = `${req.params.group}/${Constants.DATABUS_FILE_GROUP}`; + var gstorePath = `${req.params.group}/metadata.jsonld`; var result = await GstoreHelper.delete(req.params.account, gstorePath); if (!result.isSuccess) { - res.status(500).send(`Internal database error. Failed to delete the group <${groupUri}>.`); - return; + + gstorePath = `${req.params.group}/group.jsonld`; + result = await GstoreHelper.delete(req.params.account, gstorePath); + + if (!result.isSuccess) { + res.status(500).send(`Internal database error. Failed to delete the group <${groupUri}>.`); + return; + } + } res.status(204).send(`The group <${groupUri}> has been deleted.`); diff --git a/server/app/api/routes/version.js b/server/app/api/routes/version.js index dd94ae44..cf921de4 100644 --- a/server/app/api/routes/version.js +++ b/server/app/api/routes/version.js @@ -2,10 +2,10 @@ const Constants = require('../../common/constants.js'); const ServerUtils = require('../../common/utils/server-utils.js'); const DatabusUris = require('../../../../public/js/utils/databus-uris.js'); -const publishDataId = require('../lib/publish-version.js'); +const publishVersion = require('../lib/publish-version.js'); var sparql = require('../../common/queries/sparql.js'); -var request = require('request'); +const axios = require('axios'); var GstoreHelper = require('../../common/utils/gstore-helper.js'); var defaultContext = require('../../common/res/context.jsonld'); const UriUtils = require('../../common/utils/uri-utils.js'); @@ -13,6 +13,10 @@ const getLinkedData = require('../../common/get-linked-data.js'); const DatabusLogger = require('../../common/databus-logger.js'); const JsonldUtils = require('../../../../public/js/utils/jsonld-utils.js'); const jsonld = require('jsonld'); +var cors = require('cors'); +const DatabusMessage = require('../../common/databus-message.js'); +const DatabusResource = require('../../common/databus-resource.js'); + module.exports = function (router, protector) { @@ -49,7 +53,7 @@ module.exports = function (router, protector) { logger.debug(null, `Found graph ${versionUri} in input`, versionGraph); - var code = await publishDataId(req.params.account, expandedGraph, versionUri, null, logger); + var code = await publishVersion(req.params.account, expandedGraph, versionUri, null, logger); res.status(code).json(logger.getReport()); } catch (err) { @@ -58,7 +62,7 @@ module.exports = function (router, protector) { } }); - router.get('/:account/:group/:artifact/:version', ServerUtils.NOT_HTML_ACCEPTED, async function (req, res, next) { + router.get('/:account/:group/:artifact/:version', ServerUtils.NOT_HTML_ACCEPTED, cors(), async function (req, res, next) { if (req.params.account.length < 4) { next('route'); @@ -77,24 +81,34 @@ module.exports = function (router, protector) { }); router.get('/:account/:group/:artifact/:version/:file', async function (req, res, next) { - + // Return dataids? if (req.params.file == Constants.DATABUS_FILE_DATAID) { - var repo = req.params.account; - var path = `${req.params.group}/${req.params.artifact}/${req.params.version}/${req.params.file}`; + const repo = req.params.account; + const path = `${req.params.group}/${req.params.artifact}/${req.params.version}/${req.params.file}`; + const url = `${process.env.DATABUS_DATABASE_URL}/graph/read?repo=${repo}&path=${path}`; - let options = { - url: `${process.env.DATABUS_DATABASE_URL}/graph/read?repo=${repo}&path=${path}`, - headers: { - 'Accept': 'application/ld+json' - }, - json: true - }; + try { + console.log(`Piping to ${url}`); - console.log(`Piping to ${options.url}`); - request(options).pipe(res); - return; + // Use axios to fetch data + const response = await axios.get(url, { + headers: { + 'Accept': 'application/ld+json' + } + }); + + // Pipe the response data to the client + res.setHeader('Content-Type', 'application/ld+json'); + res.send(response.data); + return; + + } catch (err) { + console.log(err); + res.status(500).send('Error fetching data from Databus database.'); + return; + } } try { @@ -106,6 +120,19 @@ module.exports = function (router, protector) { return; } + // Send message to master to register metrics + if (process.send != undefined) { + const fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl; + + process.send({ + id: DatabusMessage.FILE_DOWNLOADED, + body: { + url : fullUrl, + target : result.downloadUrl + } + }); + } + res.redirect(307, result.downloadUrl); } catch (err) { console.log(err); @@ -115,12 +142,7 @@ module.exports = function (router, protector) { router.delete('/:account/:group/:artifact/:version', protector.protectAccount(true), async function (req, res, next) { - var versionUri = UriUtils.createResourceUri([ - req.params.account, - req.params.group, - req.params.artifact, - req.params.version - ]); + var versionUri = UriUtils.fromRequest(req); // Check if the artifact exists var exists = await sparql.dataid.hasVersion( @@ -139,7 +161,7 @@ module.exports = function (router, protector) { var result = await GstoreHelper.delete(req.params.account, gstorePath); if (!result.isSuccess) { - res.status(500).send(`Internal database error. Failed to delete version <${versionUri}>.`); + res.status(result.error.status).send(`Failed to delete version <${versionUri}>: ${result.error.message}`); return; } diff --git a/server/app/api/statistics/metrics-manager.js b/server/app/api/statistics/metrics-manager.js new file mode 100644 index 00000000..c1f26bc0 --- /dev/null +++ b/server/app/api/statistics/metrics-manager.js @@ -0,0 +1,55 @@ +const client = require('prom-client'); +const express = require('express'); + +class MetricsManager { + static register; + static routeCounter; + + static initialize() { + + MetricsManager.register = new client.Registry(); + MetricsManager.routeCounter = new client.Counter({ + name: 'databus_downloads', + help: 'Download requests on Databus file URLs.', + labelNames: ['url', 'target'], + }); + + // Enable the default metrics collection + client.collectDefaultMetrics({ register: MetricsManager.register }); + + // Register custom metrics + MetricsManager.register.registerMetric(MetricsManager.routeCounter); + } + + static registerRequest(requestInfo) { + try { + MetricsManager.routeCounter.inc(requestInfo); + } catch(err) { + console.log(`Tracking download ${requestInfo} failed: ${err}`); + } + } + + static async getMetrics() { + return MetricsManager.register.metrics(); + } + + static startServer(port) { + + try { + const app = express(); + app.set('port', port); + app.get('/metrics', async (req, res) => { + res.set('Content-Type', MetricsManager.register.contentType); + res.end(await MetricsManager.getMetrics()); + }); + + app.listen(port, () => { + console.log(`Metrics server is running on port ${port}`); + }); + } catch(err) { + console.log(`Failed to start metrics server on port ${port}: ${err}`); + } + } +} + +module.exports = MetricsManager; \ No newline at end of file diff --git a/server/app/api/swagger-client.js b/server/app/api/swagger-client.js index 7b5aa4aa..3350899b 100644 --- a/server/app/api/swagger-client.js +++ b/server/app/api/swagger-client.js @@ -1,78 +1,43 @@ -var optionsRegex = /options\s=\s(.|\n|\r)*?;/gm; - -var optionsString = window.onload.toString().match(optionsRegex)[0].replace('options = ', '').slice(0, -1); - -function onLoad() { - init(data.auth.info.accountName); +function replacePlaceholders(spec, replacements) { + let json = JSON.stringify(spec); + for (const key in replacements) { + const re = new RegExp(`%${key}%`, 'g'); + json = json.replace(re, replacements[key]); + } + return JSON.parse(json); } -function init(username) { - // Build a system - var url = window.location.search.match(/url=([^&]+)/); - if (url && url.length > 1) { - url = decodeURIComponent(url[1]); - } else { - url = window.location.origin; - } +async function init() { + const options = window.swaggerDynamicOptions?.templateOptions || {}; + const spec1 = options.swaggerDoc; + const url = options.swaggerUrl || window.location.origin; + const urls = options.swaggerUrls; + const customOptions = options.customOptions || {}; + const replacements = options.replacements || {}; - //var options = JSON.parse(optionsString.replace(/%USERNAME%/g, username).replace(/%GROUP%/g, "test_group")); - var options = JSON.parse(optionsString.replace(/%USERNAME%/g, username).replace(/%GROUP%/g, "test_group").replace(/%ARTIFACT%/g, "test_artifact").replace(/%VERSION%/g, "2022-02-09")); + const replacedSpec = replacePlaceholders(spec1, replacements); - url = options.swaggerUrl || url - var urls = options.swaggerUrls - var customOptions = options.customOptions - var spec1 = options.swaggerDoc - var swaggerOptions = { - spec: spec1, + const swaggerOptions = { + spec: replacedSpec, url: url, urls: urls, dom_id: '#swagger-ui', deepLinking: true, - presets: [ - SwaggerUIBundle.presets.apis, - SwaggerUIStandalonePreset - ], - plugins: [ - SwaggerUIBundle.plugins.DownloadUrl - ], - layout: "StandaloneLayout" - } - for (var attrname in customOptions) { - swaggerOptions[attrname] = customOptions[attrname]; - } - var ui = SwaggerUIBundle(swaggerOptions) + presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset], + plugins: [SwaggerUIBundle.plugins.DownloadUrl], + layout: 'StandaloneLayout' + }; - if (customOptions.oauth) { - ui.initOAuth(customOptions.oauth) + for (const key in customOptions) { + swaggerOptions[key] = customOptions[key]; } - if (customOptions.authAction) { - ui.authActions.authorize(customOptions.authAction) - } + const ui = SwaggerUIBundle(swaggerOptions); - window.ui = ui + if (customOptions.oauth) ui.initOAuth(customOptions.oauth); + if (customOptions.authAction) ui.authActions.authorize(customOptions.authAction); - // Custom UI - var container = document.getElementsByClassName('schemes wrapper')[0]; - - - var input = document.createElement("input"); - input.type = "text"; - input.id = "username"; - input.value = username; - - - var text = document.createElement("div"); - text.innerHTML = "Account Name:"; - text.style.alignSelf = "center"; - text.style.marginRight = "8px"; - - container.prepend(input); - container.prepend(text); - - input.onchange = function() { - init(input.value); - } + window.ui = ui; } -window.onload = onLoad; +window.addEventListener('load', init); diff --git a/server/app/api/swagger-page.js b/server/app/api/swagger-page.js index cfefd2af..603fbabf 100644 --- a/server/app/api/swagger-page.js +++ b/server/app/api/swagger-page.js @@ -1,60 +1,81 @@ const swaggerUi = require('swagger-ui-express'); const YAML = require('yamljs'); const fs = require('fs'); - -const databusHeaderTemplate = require('../../../public/templates/header.ejs'); const ejs = require('ejs'); const ServerUtils = require('../common/utils/server-utils'); -var options = { - customCss: '.swagger-ui .topbar { display: none }', -}; - +const databusHeaderTemplate = require('../../../public/templates/header.ejs'); -module.exports = function (router, protector, locals) { +const options = { + customCss: ` + .swagger-ui .topbar { display: none !important; } + .renderedMarkdown ul { + list-style: disc; + margin-left: 2em; + } + ` +}; - var swaggerYaml = fs.readFileSync(__dirname + '/swagger.yml', ['utf8']).toString(); - var swaggerCss = ``; - var customJs = fs.readFileSync(__dirname + '/swagger-client.js', ['utf8']).toString(); +module.exports = function(router, protector, locals) { + const swaggerYaml = fs.readFileSync(__dirname + '/swagger.yml', 'utf8'); + const swaggerDocument = YAML.parse( + swaggerYaml.replace(/%DATABUS_RESOURCE_BASE_URL%/g, process.env.DATABUS_RESOURCE_BASE_URL) + ); - var opts = JSON.parse(JSON.stringify(locals)); - opts.title = "API Documentation"; - opts.data = {}; + const swaggerCss = ``; + const customJs = fs.readFileSync(__dirname + '/swagger-client.js', 'utf8').toString(); - - swaggerYaml = swaggerYaml - .replace(/%DATABUS_RESOURCE_BASE_URL%/g, process.env.DATABUS_RESOURCE_BASE_URL); + const data = JSON.parse(JSON.stringify(locals)); + data.title = 'API Documentation'; + data.data = {}; - var swaggerDocument = YAML.parse(swaggerYaml); + const opts = { views: ['./../public/templates'] }; - // Hack in the header! function hackTheHeader(req, res, next) { - - if (req.url == '/') { - - opts.data.auth = ServerUtils.getAuthInfoFromRequest(req); - var databusHeader = ejs.render(databusHeaderTemplate, opts); - - var write = res.send; - res.send = function (chunk) { - - chunk instanceof Buffer && (chunk = chunk.toString()); - - var index = chunk.indexOf(""); - chunk = databusHeader + chunk.substr(index + 6); - chunk = chunk.replace(``, `${swaggerCss}`); - chunk = chunk.replace(``, ``); - - res.setHeader('Content-Length', chunk.length); - write.apply(this, arguments); + if (req.url === '/') { + data.data.auth = ServerUtils.getAuthInfoFromRequest(req); + opts.title = 'API Documentation'; + const databusHeader = ejs.render(databusHeaderTemplate, data, opts); + + const write = res.send; + res.send = function(chunk) { + chunk = chunk instanceof Buffer ? chunk.toString() : chunk; + + const index = chunk.indexOf(''); + if (index !== -1) chunk = databusHeader + chunk.substr(index + 6); + + // Dynamic replacement values for placeholders + const swaggerOptions = { + swaggerUrl: process.env.SWAGGER_BASE_URL || '', + swaggerDoc: swaggerDocument, + swaggerUrls: null, + customOptions: {}, + replacements: { + GROUP: process.env.DEFAULT_GROUP || 'test_group', + ARTIFACT: process.env.DEFAULT_ARTIFACT || 'test_artifact', + VERSION: process.env.DEFAULT_VERSION || '2022-02-09' + } + }; + + const injectScript = ` + + + `; + + chunk = chunk.replace('', `${swaggerCss}`); + chunk = chunk.replace('', `${injectScript}`); + + res.setHeader('Content-Length', Buffer.byteLength(chunk)); + write.call(this, chunk); }; } - next(); } router.use('/api', protector.checkSso(), hackTheHeader, swaggerUi.serve); router.get('/api', swaggerUi.setup(swaggerDocument, options)); - - -} +}; diff --git a/server/app/api/swagger.yml b/server/app/api/swagger.yml index b9290e81..bb2b1c92 100644 --- a/server/app/api/swagger.yml +++ b/server/app/api/swagger.yml @@ -39,46 +39,22 @@ paths: @prefix dct: . <> a databus:Databus . - <> dct:hasVersion "<2.1.0-rc.1>" . + <> dct:hasVersion "<2.1.1>" . - /api/publish: + /api/register: post: summary: "Publish Metadata" - description: " - Publish metadata Json-LD (DataID) on the Databus. + description: | + Register metadata on the Databus. The metadata must be sent as the request body + in RDF JSON-LD format. The RDF may contain one or more Databus entities: - - A [DataId](http://dataid.dbpedia.org/ns/core.html) is an RDF metadata document with an entity of rdf:type [dataid:Dataset](http://dataid.dbpedia.org/ns/core#Dataset) at its heart. The document contains information about the corresponding dataset: - - * The containing files and their metadata - - * A Databus version - - * A Databus artifact - - * A Databus group - - - The server will try to auto-complete your input and can optionally calculate the required sha256sum and byteSize properties. - - - The input data must be supplied as Json-LD. - - - The input data will be filtered with the following construct queries: - - * [Construct group](./server/app/common/queries/constructs/construct-group.sparql) query - - * [Construct version](./server/app/common/queries/constructs/construct-version.sparql) query - - - Each construct query result will then be validated with their corresponding SHACL shape (e.g. group construct query result validated by group SHACL shapes): - - * [Group SHACL](./server/app/common/shacl/group-shacl.ttl) shapes - - * [Version SHACL](./server/app/common/shacl/dataid-shacl.ttl) shapes - " - operationId: "publish" + * **Group** — validated against the SHACL shape: [group.shacl](/res/shacl/group.shacl) + * **Artifact** — validated against the SHACL shape: [artifact.shacl](/res/shacl/artifact.shacl) + * **Version** — validated against the SHACL shape: [version.shacl](/res/shacl/version.shacl) + * **Collection** — validated against the SHACL shape: [collection.shacl](/res/shacl/collection.shacl) + + **Hint:** Replace `[YOUR_ACCOUNT_NAME]` in the example body with your actual Databus account name. + operationId: "register" tags: - Main API security: @@ -97,7 +73,7 @@ paths: type: string description: "Desired detail of the publish report. Can be set to either 'error', 'info' or 'debug'." required: false - example: "info" + example: "error" requestBody: description: "Metadata in JSONLD. May contain any number of groups, artifacts and datasets" required: true @@ -107,14 +83,20 @@ paths: type: object example: | { - "@context": "https://downloads.dbpedia.org/databus/context.jsonld", + "@context": "%DATABUS_RESOURCE_BASE_URL%/res/context.jsonld", "@graph": [ { "@type": "Version", +<<<<<<< HEAD + "@id": "%DATABUS_RESOURCE_BASE_URL%/[YOUR_ACCOUNT_NAME]/test_group/test_artifact/0.0.1", + "title": "Test Data", + "description": "Test data from the swagger API. This can probably be deleted.", +======= "@id": "https://dev.databus.dbpedia.org//test_group/test_artifact/2022-02-09", "hasVersion": "2022-02-09", "title": "test_group test_artifact", "description": "version of the test_artifact dataset from DBpedia", +>>>>>>> master "license": "https://dalicc.net/licenselibrary/Apache-2.0", "distribution": [ { @@ -136,44 +118,7 @@ paths: type: object example: | { - "logLevel": "info", - "log": [ - { - "resource": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%", - "msg": "Successfully published group <%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%>.", - "payload": { - "@context": "https://downloads.dbpedia.org/databus/context.jsonld", - "@id": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%", - "@type": "Group", - "abstract": "%GROUP% datasets from DBpedia", - "description": "%GROUP% datasets from DBpedia", - "title": "%GROUP% Data" - }, - "level": "info" - }, - { - "resource": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%/%ARTIFACT%", - "msg": "Successfully published artifact <%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%/%ARTIFACT%>.", - "payload": { - "@context": "https://downloads.dbpedia.org/databus/context.jsonld", - "@graph": [ - { - "@id": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%/%ARTIFACT%", - "@type": "Artifact", - "group": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%", - "abstract": "%ARTIFACT% data from DBpedia", - "description": "%ARTIFACT% data from DBpedia", - "title": "%ARTIFACT%" - }, - { - "@id": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%", - "@type": "Group" - } - ] - }, - "level": "info" - } - ] + "log": [] } "403": description: "Error: Forbidden. The account failed to authenticate." @@ -186,8 +131,8 @@ paths: "logLevel": "info", "log": [ { - "resource": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%", - "msg": "Not allowed to access namespace of group identifier <%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%>.", + "resource": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]/%GROUP%", + "msg": "Not allowed to access namespace of group identifier <%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]/%GROUP%>.", "level": "error" } ] @@ -279,119 +224,19 @@ paths: schema: type: string required: true - example: '%ARTIFACT%' + example: 'Test Data' - name: typeName in: query description: The type of entities to return. White space separated list of one or multiples of + * Account * Group * Artifact + * Version * Collection - * PersonalProfileDocument - schema: - type: string - required: false - example: 'Artifact' - - name: part - in: query - description: A query for one or multiple parta of the entity uri (white space separated list). schema: type: string required: false - example: '' - - name: partRequired - in: query - description: If set to true, results without a match on the part field are omitted. - schema: - type: boolean - required: false - example: 'true' - responses: - "200": - description: "OK" - content: - application/json: - schema: - type: object - example: '{ - "docs": [ - { - "typeName": [ - "Artifact" - ], - "score": [ - "337.63354" - ], - "label": [ - "%GROUP%s %ARTIFACT%" - ], - "resource": [ - "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%/%ARTIFACT%" - ], - "part": [ - "%USERNAME%", - "%GROUP%", - "%ARTIFACT%" - ] - } - ], - "query": "%ARTIFACT%" - - }' - "404": - description: "Error: Not Found." - /{account}/{group}: - put: - summary: "Create / Update Group" - description: " - Creates or updates a Databus Group. - - * The input data must be supplied as Json-LD - - * The input data will be filtered with this [construct query](./server/app/common/queries/constructs/construct-group.sparql) - - * The **filtered data** must conform to these [SHACL shapes](./server/app/common/shacl/group-shacl.ttl) - - * The uri of the dataid:Group has to match the request uri. - - - Note that the *@id* of the supplied graph has to be the same as the request uri. Additionally, the uri has to be in user's namespace. - " - operationId: "create-group" - tags: - - Group - security: - - ApiKeyAuth: [] - parameters: - - name: account - in: path - schema: - type: string - description: account namespace - required: true - example: '%USERNAME%' - - name: group - in: path - description: group identifier - schema: - type: string - required: true - example: "%GROUP%" - requestBody: - description: "Group data as JSONLD." - required: true - content: - application/ld+json: - schema: - type: object - example: '{ - "@context": "https://downloads.dbpedia.org/databus/context.jsonld", - "@graph": { - "@id": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%", - "@type": "Group", - "title": "%GROUP% Data", - "description": "Various %GROUP% datasets extracted from DBpedia using AI." - } - }' + example: 'Version' responses: "200": description: "OK" @@ -399,34 +244,37 @@ paths: application/json: schema: type: object - example: '{ - "logLevel": "error", - "log": [] - }' - "400": - description: "Error: Bad Request" - content: - application/json: - schema: - type: object - example: '{ - "logLevel": "error", - "log": [ + example: | + { + "docs": [ { - "resource": null, - "msg": "No graph with id <%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%> found in the input.", - "payload": null, - "level": "error" + "score": [ + "346.7309" + ], + "part": [ + "2022-02-09", + "test_artifact" + ], + "typeName": [ + "Version" + ], + "publisher": [ + "[YOUR_ACCOUNT_NAME]" + ], + "id": [ + "%DATABUS_RESOURCE_BASE_URL%/[YOUR_ACCOUNT_NAME]/test_group/test_artifact/2022-02-09" + ], + "label": [ + "Test Data" + ], + "abstract": [ + "Test data from the swagger API. This can probably be deleted." + ] } - ] - }' - "403": - description: "Error: Forbidden" - content: - text/plain: - schema: - type: string - example: "You cannot perform this action in a foreign namespace." + ], + "query": "Test Data" + } + /{account}/{group}: get: summary: "Get Group" description: "Retrieve a Databus Group as JSONLD." @@ -440,7 +288,7 @@ paths: schema: type: string required: true - example: '%USERNAME%' + example: '[MY_ACCOUNT_NAME]' - name: group in: path description: target group identifier @@ -457,8 +305,8 @@ paths: type: object example: | { - "@context": "https://downloads.dbpedia.org/databus/context.jsonld", - "@id": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%", + "@context": "%DATABUS_RESOURCE_BASE_URL%/res/context.jsonld", + "@id": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]/%GROUP%", "@type": "Group" } "404": @@ -484,7 +332,7 @@ paths: schema: type: string required: true - example: '%USERNAME%' + example: '[MY_ACCOUNT_NAME]' - name: group in: path description: target group identifier @@ -507,88 +355,6 @@ paths: "409": description: "Conflict - There are still versions or artifacts associated with this group." /{account}/{group}/{artifact}: - put: - summary: "Create / Update Artifact" - description: "Creates or updates a Databus Artifact." - operationId: "create-artifact" - tags: - - Artifact - security: - - ApiKeyAuth: [] - parameters: - - name: account - in: path - description: account name - schema: - type: string - required: true - example: '%USERNAME%' - - name: group - in: path - description: target group name - schema: - type: string - required: true - example: "%GROUP%" - - name: artifact - in: path - description: target artifact name - schema: - type: string - required: true - example: "%ARTIFACT%" - requestBody: - description: "Artifact data as JSONLD." - required: true - content: - application/ld+json: - schema: - type: object - example: | - { - "@context": "https://downloads.dbpedia.org/databus/context.jsonld", - "@graph": { - "@id": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%/%ARTIFACT%", - "@type": "Artifact", - "title": "%ARTIFACT% Data", - "description": "%ARTIFACT% description." - } - } - responses: - "200": - description: "OK" - content: - application/json: - schema: - type: object - example: '{ - "logLevel": "error", - "log": [] - }' - "400": - description: "Error: Bad Request" - content: - application/json: - schema: - type: object - example: '{ - "logLevel": "error", - "log": [ - { - "resource": null, - "msg": "No graph %DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%/%ARTIFACT% found in the input.", - "payload": null, - "level": "error" - } - ] - }' - "403": - description: "Error: Forbidden" - content: - text/plain: - schema: - type: string - example: "You cannot perform this action in a foreign namespace." get: summary: "Get Artifact" description: "Retrieve a Databus Artifact as JSONLD." @@ -602,7 +368,7 @@ paths: schema: type: string required: true - example: '%USERNAME%' + example: '[MY_ACCOUNT_NAME]' - name: group in: path description: group identifier @@ -626,7 +392,7 @@ paths: type: object example: | { - "@context": "https://downloads.dbpedia.org/databus/context.jsonld", + "@context": "%DATABUS_RESOURCE_BASE_URL%/res/context.jsonld", "@graph": [ { "@id": "https://dev.databus.dbpedia.org/dnkg/raw-exports/dnb", @@ -637,7 +403,7 @@ paths: "@type": "Artifact" }, { - "@id": "https://dev.databus.dbpedia.org/%USERNAME%/cleaned/geonames", + "@id": "https://dev.databus.dbpedia.org/[MY_ACCOUNT_NAME]/cleaned/geonames", "@type": "Artifact" }, { @@ -705,7 +471,7 @@ paths: schema: type: string required: true - example: '%USERNAME%' + example: '[MY_ACCOUNT_NAME]' - name: group in: path description: target group identifier @@ -734,111 +500,10 @@ paths: example: "You cannot perform this action in a foreign namespace." /{account}/{group}/{artifact}/{version}: - put: - summary: "Create / Update DataId" - description: " - Create or update a DataId on the Databus - - * The input data must be supplied as Json-LD - - * The input data will be filtered with this [construct query](./server/app/common/queries/constructs/construct-version.sparql) - - * The **filtered data** must conform to these [SHACL shapes](./server/app/common/shacl/dataid-shacl.ttl) - - " - operationId: "create-dataid" - tags: - - DataID (version) - security: - - ApiKeyAuth: [] - parameters: - - name: account - in: path - description: account namespace - schema: - type: string - required: true - example: '%USERNAME%' - - name: group - in: path - description: group identifier - schema: - type: string - required: true - example: "%GROUP%" - - name: artifact - in: path - description: artifact identifier - schema: - type: string - required: true - example: "%ARTIFACT%" - - name: version - in: path - description: version identifier - schema: - type: string - required: true - example: "%VERSION%" - requestBody: - description: "Metadata in JSONLD. May contain at most one group and one version metadata graph." - required: true - content: - application/ld+json: - schema: - type: object - example: | - { - "@context": "https://downloads.dbpedia.org/databus/context.jsonld", - "@graph": { - "@type": [ "Version", "Dataset" ], - "@id": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%/%ARTIFACT%/%VERSION%", - "hasVersion": "%VERSION%", - "title": "Test Version", - "description": "Data for testing purposes.", - "license": "http://creativecommons.org/licenses/by/4.0/", - "distribution": [ - { - "@type": "Part", - "formatExtension": "ttl", - "compression": "none", - "downloadURL": "https://holycrab13.github.io/webid.ttl", - "byteSize": 2730, - "sha256sum": "d61a05ca4810367f361f17500304a168aab27a3119c93a18c00bce1775dfd6b1" - } - ] - } - } - responses: - "200": - description: "OK" - content: - application/json: - schema: - type: object - example: | - { - "logLevel": "error", - "log": [] - } - "400": - description: "Error. Bad Request." - content: - text/plain: - schema: - type: string - example: "Graph with id %DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%/%ARTIFACT%/%VERSION% not found in input." - "403": - description: "Error: Forbidden" - content: - text/plain: - schema: - type: string - example: "You cannot perform this action in a foreign namespace." get: - summary: "Get DataId" + summary: "Get Version" tags: - - DataID (version) + - Version description: "Retrieve a DataId as JSONLD" operationId: "get-dataid" parameters: @@ -848,7 +513,7 @@ paths: schema: type: string required: true - example: '%USERNAME%' + example: '[MY_ACCOUNT_NAME]' - name: group in: path description: group identifier @@ -879,7 +544,7 @@ paths: type: object example: | { - "@context": "https://downloads.dbpedia.org/databus/context.jsonld", + "@context": "%DATABUS_RESOURCE_BASE_URL%/res/context.jsonld", "@graph": [ { "@id": "https://dev.databus.dbpedia.org/denis/lodcloud-crawl/judaicalink/2022-10-28#judaicalink_distinctid=id1_status=500_type=other.file", @@ -956,7 +621,7 @@ paths: description: "Delete a DataId from the Databus." operationId: "delete-dataid" tags: - - DataID (version) + - Version security: - ApiKeyAuth: [] parameters: @@ -966,7 +631,7 @@ paths: schema: type: string required: true - example: '%USERNAME%' + example: '[MY_ACCOUNT_NAME]' - name: group in: path description: group identifier @@ -1001,7 +666,7 @@ paths: description: "Get a file from the Databus. The request URI is the Databus file identifier." operationId: "get-file" tags: - - DataID (version) + - File parameters: - name: account in: path @@ -1009,7 +674,7 @@ paths: schema: type: string required: true - example: '%USERNAME%' + example: '[MY_ACCOUNT_NAME]' - name: group in: path description: group identifier @@ -1049,95 +714,6 @@ paths: type: string /{account}/collections/{id}: - put: - summary: "Create / Update Collection" - description: "Creates or updates a Databus Collection." - operationId: "create-collection" - tags: - - Collection - security: - - ApiKeyAuth: [] - parameters: - - name: account - in: path - description: account namespace - schema: - type: string - required: true - example: '%USERNAME%' - - name: id - in: path - description: The collection identifier - schema: - type: string - required: true - example: "%GROUP%-%ARTIFACT%" - requestBody: - description: "Collection data as JSONLD." - required: true - content: - application/ld+json: - schema: - type: object - example: '{ - "@context": "https://downloads.dbpedia.org/databus/context.jsonld", - "@graph": [ - { - "@id": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/collections/%GROUP%-%ARTIFACT%", - "@type": "databus:Collection", - "publisher": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%#this", - "title": "%GROUP% %ARTIFACT%", - "description": "%GROUP% %ARTIFACT% Data (latest version) from DBpedia.", - "databus:collectionContent": "%7B%22root%22%3A%7B%22uri%22%3Anull%2C%22property%22%3Anull%2C%22childNodes%22%3A%5B%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3000%22%2C%22property%22%3Anull%2C%22childNodes%22%3A%5B%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3000%2F%USERNAME%%2F%GROUP%%22%2C%22property%22%3A%22dataid%3Agroup%22%2C%22childNodes%22%3A%5B%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3000%2F%USERNAME%%2F%GROUP%%2F%ARTIFACT%%22%2C%22property%22%3A%22dataid%3Aartifact%22%2C%22childNodes%22%3A%5B%5D%2C%22facetSettings%22%3A%7B%22http%3A%2F%2Fpurl.org%2Fdc%2Fterms%2FhasVersion%22%3A%5B%7B%22value%22%3A%22%VERSION%%22%2C%22checked%22%3Atrue%7D%5D%7D%7D%5D%2C%22facetSettings%22%3A%7B%7D%7D%5D%2C%22facetSettings%22%3A%7B%7D%7D%5D%2C%22facetSettings%22%3A%7B%7D%7D%2C%22generatedQuery%22%3A%7B%22root%22%3A%7B%22uri%22%3Anull%2C%22property%22%3Anull%2C%22childNodes%22%3A%5B%5D%2C%22facetSettings%22%3A%7B%7D%7D%7D%7D" - } - ] - }' - responses: - "200": - description: "OK" - content: - application/json: - schema: - type: object - example: '{ - "success": true, - "message": "Collection saved successfully.", - "data": [ - { - "@id": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/collections/%GROUP%-%ARTIFACT%", - "@type": [ - "http://dataid.dbpedia.org/ns/core#Collection" - ], - "http://dataid.dbpedia.org/ns/core#content": [ - { - "@value": "%7B%22root%22%3A%7B%22uri%22%3Anull%2C%22property%22%3Anull%2C%22childNodes%22%3A%5B%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3000%22%2C%22property%22%3Anull%2C%22childNodes%22%3A%5B%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3000%2F%USERNAME%%2F%GROUP%%22%2C%22property%22%3A%22dataid%3Agroup%22%2C%22childNodes%22%3A%5B%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3000%2F%USERNAME%%2F%GROUP%%2F%ARTIFACT%%22%2C%22property%22%3A%22dataid%3Aartifact%22%2C%22childNodes%22%3A%5B%5D%2C%22facetSettings%22%3A%7B%7D%7D%5D%2C%22facetSettings%22%3A%7B%7D%7D%5D%2C%22facetSettings%22%3A%7B%7D%7D%5D%2C%22facetSettings%22%3A%7B%7D%7D%2C%22generatedQuery%22%3A%7B%22root%22%3A%7B%22uri%22%3Anull%2C%22property%22%3Anull%2C%22childNodes%22%3A%5B%5D%2C%22facetSettings%22%3A%7B%7D%7D%7D%7D" - } - ], - "http://purl.org/dc/terms/description": [ - { - "@value": "%GROUP% %ARTIFACT% (version %VERSION%) from DBpedia." - } - ], - "http://purl.org/dc/terms/publisher": [ - { - "@id": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%#this" - } - ], - "http://purl.org/dc/terms/title": [ - { - "@value": "%GROUP% %ARTIFACT%" - } - ] - } - ] - }' - "403": - description: "Forbidden. The account failed to authenticate." - content: - text/plain: - schema: - type: string - example: "Error: Forbidden" get: summary: "Get Collection" description: "Retrieve a Databus Collection." @@ -1151,7 +727,7 @@ paths: schema: type: string required: true - example: '%USERNAME%' + example: '[MY_ACCOUNT_NAME]' - name: id in: path description: the collection identifier @@ -1178,9 +754,9 @@ paths: GRAPH ?g { { - ?dataset databus:group <%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/yourCollection> . + ?dataset databus:group <%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]/yourCollection> . { - ?dataset databus:artifact <%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/yourCollection/first> . + ?dataset databus:artifact <%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]/yourCollection/first> . } } ?dataset dcat:distribution ?distribution . @@ -1192,7 +768,7 @@ paths: schema: type: object example: '{ - "@id": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%", + "@id": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]/%GROUP%", "@type": "databus:Group", "description": "%GROUP% datasets from DBpedia", "title": "%GROUP% Data", @@ -1236,7 +812,7 @@ paths: schema: type: string required: true - example: '%USERNAME%' + example: '[MY_ACCOUNT_NAME]' - name: id in: path description: the collection identifier @@ -1263,7 +839,7 @@ paths: schema: type: string required: true - example: '%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/collections/%GROUP%-%ARTIFACT%' + example: '%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]/collections/%GROUP%-%ARTIFACT%' responses: "200": description: "OK" @@ -1298,7 +874,7 @@ paths: schema: type: string required: true - example: '%USERNAME%' + example: '[MY_ACCOUNT_NAME]' requestBody: description: "Account data as JSONLD." required: true @@ -1307,23 +883,23 @@ paths: schema: type: object example: '{ - "@context": "https://downloads.dbpedia.org/databus/context.jsonld", + "@context": "%DATABUS_RESOURCE_BASE_URL%/res/context.jsonld", "@graph": [ { - "@id": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%", + "@id": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]", "@type": "foaf:PersonalProfileDocument", - "maker": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%#this", - "primaryTopic": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%#this" + "maker": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]#this", + "primaryTopic": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]#this" }, { - "@id": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%#this", + "@id": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]#this", "@type": [ "dbo:DBpedian", "foaf:Person" ], - "name": "%USERNAME%", + "name": "[MY_ACCOUNT_NAME]", "rdfs:comment": "Hello Databus!", - "account": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%" + "account": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]" } ] }' @@ -1355,7 +931,7 @@ paths: schema: type: string required: true - example: '%USERNAME%' + example: '[MY_ACCOUNT_NAME]' responses: "200": description: "OK" @@ -1419,21 +995,21 @@ paths: "modulus": "a1e12228e4d931cc1cc00f53822f3c759fe3a66b692fab0df625f704ee4a03e25ec6bd6af7408cb070702dd4b5245a438c697c8e0243989459dc4bd90269116355a763d9f48ba23d4cfec5d6189606b160dd42ed5deb5a515306df2684d8070bac3e61d5f91c481b81163630877be42207547523ebe378ce44df836a5db28ca50b3cda007f1a9a8734c73a620e6aabd2abda8c3ba52ecd41fba866a66c17f6d713abf24983ef5074bb9eac8abf9e36979cd8f87441e8d500889c2efba793b7d0009cc6b14b8dc1466c6e244a0d907181f708e69a02e417fce2786ecb11403e455abf458a99131ca62a31981757e3eb7b00ff30942b397b879b9485d4fbdf7c43" }, { - "@id": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%", + "@id": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]", "@type": "foaf:PersonalProfileDocument", - "maker": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%#this", - "primaryTopic": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%#this" + "maker": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]#this", + "primaryTopic": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]#this" }, { - "@id": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%#this", + "@id": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]#this", "@type": [ "foaf:Person", "dbo:DBpedian" ], "comment": "Hello Databus!", "key": "_:b0", - "account": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%", - "name": "%USERNAME%" + "account": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]", + "name": "[MY_ACCOUNT_NAME]" } ] } @@ -1576,7 +1152,7 @@ paths: type: object example: | { - "@context": "https://downloads.dbpedia.org/databus/context.jsonld", + "@context": "%DATABUS_RESOURCE_BASE_URL%/res/context.jsonld", "@graph": { "@type": [ "Version", "Dataset" ], "@id": "%DATABUS_RESOURCE_BASE_URL%/%ACCOUNT%/%GROUP%/%ARTIFACT%/%VERSION%", @@ -1604,8 +1180,8 @@ paths: schema: type: string example: 'Databus Tractate Version 1.0 - %DATABUS_RESOURCE_BASE_URL%/%USERNAME%#this - %DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%/%ARTIFACT%/%VERSION% + %DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]#this + %DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]/%GROUP%/%ARTIFACT%/%VERSION% http://creativecommons.org/licenses/by/4.0/ d61a05ca4810367f361f17500304a168aab27a3119c93a18c00bce1775dfd6b1' /api/tractate/v1/verify: @@ -1623,7 +1199,7 @@ paths: schema: type: object example: '{ - "@context": "https://downloads.dbpedia.org/databus/context.jsonld", + "@context": "%DATABUS_RESOURCE_BASE_URL%/res/context.jsonld", "@graph": [ { "@id": "_:b0", @@ -1634,36 +1210,36 @@ paths: } }, { - "@id": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%/%ARTIFACT%", + "@id": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]/%GROUP%/%ARTIFACT%", "@type": "dataid:Artifact" }, { - "@id": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%/%ARTIFACT%/%VERSION%", + "@id": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]/%GROUP%/%ARTIFACT%/%VERSION%", "@type": "dataid:Version" }, { - "@id": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%/%ARTIFACT%/%VERSION%#Dataset", + "@id": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]/%GROUP%/%ARTIFACT%/%VERSION%#Dataset", "@type": "Dataset", - "artifact": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%/%ARTIFACT%", - "group": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%", - "version": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%/%ARTIFACT%/%VERSION%", + "artifact": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]/%GROUP%/%ARTIFACT%", + "group": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]/%GROUP%", + "version": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]/%GROUP%/%ARTIFACT%/%VERSION%", "description": "%GROUP% version of the %ARTIFACT% dataset from DBpedia", "hasVersion": "%VERSION%", "issued": "2022-02-11T13:28:58.132Z", "license": "http://creativecommons.org/licenses/by/4.0/", "modified": "2022-02-11T13:28:58.132Z", - "publisher": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%#this", + "publisher": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]#this", "title": "%GROUP% %ARTIFACT%", - "distribution": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%/%ARTIFACT%/%VERSION%#%ARTIFACT%.ttl", + "distribution": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]/%GROUP%/%ARTIFACT%/%VERSION%#%ARTIFACT%.ttl", "proof": { "@id": "_:b0" } }, { - "@id": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%/%ARTIFACT%/%VERSION%#%ARTIFACT%.ttl", + "@id": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]/%GROUP%/%ARTIFACT%/%VERSION%#%ARTIFACT%.ttl", "@type": "Part", "compression": "none", - "file": "%DATABUS_RESOURCE_BASE_URL%/%USERNAME%/%GROUP%/%ARTIFACT%/%VERSION%/%ARTIFACT%.ttl", + "file": "%DATABUS_RESOURCE_BASE_URL%/[MY_ACCOUNT_NAME]/%GROUP%/%ARTIFACT%/%VERSION%/%ARTIFACT%.ttl", "formatExtension": "ttl", "formatExtension": "ttl", "sha256sum": "d61a05ca4810367f361f17500304a168aab27a3119c93a18c00bce1775dfd6b1", diff --git a/server/app/webdav.js b/server/app/api/webdav.js similarity index 83% rename from server/app/webdav.js rename to server/app/api/webdav.js index d210b0fb..7584cd72 100644 --- a/server/app/webdav.js +++ b/server/app/api/webdav.js @@ -1,6 +1,6 @@ const webdav = require('webdav-server').v2; -const ServerUtils = require('./common/utils/server-utils'); -const DatabusUtils = require("../../public/js/utils/databus-utils"); +const ServerUtils = require('../common/utils/server-utils'); +const DatabusUtils = require("../../../public/js/utils/databus-utils"); const path = require('path'); const fs = require('fs'); @@ -96,12 +96,18 @@ class DatabusWebDAV { return authTokens[0]; } + getAccountNameFromRequest(req) { + const match = req.url.match(/^\/([^/]+)\//); + return match ? match[1] : null; + }; + davAuth() { var self = this; return async function (req, res, next) { if (req.method == "GET" || req.method == "HEAD") { - next("route"); + console.log('got the get!'); + next(); return; } @@ -128,6 +134,8 @@ class DatabusWebDAV { } */ + + var auth = ServerUtils.getAuthInfoFromRequest(req); if (!auth.authenticated) { @@ -135,13 +143,21 @@ class DatabusWebDAV { return; } - var davUser = await self.getDavUser(auth.info.accountName); + let accountName = self.getAccountNameFromRequest(req); + + if(auth.info.accounts == undefined || !auth.info.accounts.some(a => a.accountName === accountName)) { + res.status(401).send(); + return; + } + + + var davUser = await self.getDavUser(accountName); if (davUser == null) { - self.addWebDavUser(auth.info.accountName); + self.addWebDavUser(accountName); } - var token = btoa(`${auth.info.accountName}:${self.sessionPass}`) + var token = btoa(`${accountName}:${self.sessionPass}`) req.headers['Authorization'] = `Basic ${token}`; next("route"); diff --git a/server/app/app.js b/server/app/app.js index 3f42cd46..f104a222 100644 --- a/server/app/app.js +++ b/server/app/app.js @@ -12,18 +12,23 @@ const ServerUtils = require('./common/utils/server-utils'); const webdav = require('webdav-server').v2; const DatabusUtils = require("../../public/js/utils/databus-utils"); var config = require("../config.json"); -const DatabusWebDAV = require("./webdav"); +const DatabusWebDAV = require("./api/webdav"); var DatabusProtect = require('./common/protect/middleware'); -const { env } = require("process"); var serveIndex = require('serve-index'); +var cors = require('cors'); +const JsonldLoader = require("./common/utils/jsonld-loader"); +const handle404 = require('./common/404'); -// Creation of the mighty server app +// Create the main Express application instance var app = express(); +// Load the JSON-LD context and stringify it with indentation for readability var context = JSON.stringify(require('../app/common/res/context.jsonld'), null, 3); -var titleColor = DatabusUtils.stringOrFallback(process.env.DATABUS_TITLE_COLOR, config.defaultColors.title); + +// Determine banner color from environment variable or fallback to config default var bannerColor = DatabusUtils.stringOrFallback(process.env.DATABUS_BANNER_COLOR, config.defaultColors.banner); +// Set global app variables accessible in views and throughout the application app.locals = { databus: { version: config.version, @@ -31,6 +36,7 @@ app.locals = { banner: bannerColor }, name: process.env.DATABUS_NAME, + abstract: process.env.DATABUS_ABSTRACT, iconUrl: process.env.DATABUS_ORG_ICON, contextUrl: process.env.DATABUS_CONTEXT_URL, context: context, @@ -43,105 +49,98 @@ app.locals = { } }; -// Create a session memory store (server cache) +// Create an in-memory session store to hold session data var memoryStore = new session.MemoryStore(); + +// Initialize the custom protection middleware with the session store +// All authentication and user data fetching happens in DatabusProtect var protector = new DatabusProtect(memoryStore); + +// Initialize the Databus WebDAV server module var webDAVModule = new DatabusWebDAV(); -// Initialize the express app +// Call the initialization function and configure routes upon completion initialize(app, memoryStore).then(function () { try { - // Add webDAV + // Register the WebDAV route, protected with SSO and authentication middleware app.use('/dav', protector.checkSso(), webDAVModule.davAuth(), webdav.extensions.express('/', webDAVModule.webDAVServer)); - // Fav Icon + // Serve the site favicon app.use(favicon(path.join(__dirname, '../../public/img', 'favicon.ico'))); - + // Create a new Express router for routing requests + // router is the root object that all HTTP routes are attached to var router = new express.Router(); - // Use protection + // Apply authentication middleware globally app.use(protector.auth()); + // Enable private mode protection if specified in environment variables if (process.env.DATABUS_PRIVATE_MODE == "true") { console.log(`Started cluster node in private mode`); app.all('*', protector.protect(true, function (req, res) { - if (protector.isBrowserRequest(req)) { - var data = {} - data.auth = ServerUtils.getAuthInfoFromRequest(req); - res.status(401).render('unauthorized', { - title: 'Unauthorized', - data: data, - }); - } else { - res.status(401).send(); - } + protector.sendError(req, res, 401, 'Unauthorized', "This Databus is private. Please log in."); })); } + // Load API route module and attach routes to the router + // Code for the API server is located in the ./api folder + require('./api/module')(router, protector, app.locals); + // Load page-rendering route module and attach to the same router + // Code for the HTML webapp is located in the ./webapp folder + require('./webapp/module')(router, protector); - // Attach modules to router - require('./api/module')(router, protector, app.locals); // API handlers - require('./pages/module')(router, protector);// Web App handlers - - - // Attach router + // Attach the configured router to the main app app.use('/', router); - // Handle 404 - app.use(protector.checkSso(), function (req, res, next) { - res.status(404); - - // respond with html page - if (req.accepts('html')) { - var data = {} - data.auth = ServerUtils.getAuthInfoFromRequest(req); - res.render('404', { title: 'Not found', data: data }); - return; - } - - // respond with json - if (req.accepts('json')) { - res.json({ error: 'Not found', message: "The requested resource could not be found." }); - return; - } - - // default to plain-text. send() - res.type('txt').send('The requested resource could not be found.\n'); - }); + // Handle 404 - resource not found + app.use(protector.checkSso(), handle404); } catch (err) { + // Log any errors that occur during initialization console.log(err); } - - }); /** - * Express app initialization - * @param {the express app} app + * Initializes the Express application with middleware and configuration + * @param {object} app - The Express app instance + * @param {object} memoryStore - The session memory store */ async function initialize(app, memoryStore) { + // Initialize JSON-LD context loader used for linked data parsing + JsonldLoader.initialize(); + try { - app.set('trust proxy', 'loopback'); + // Enable trust proxy so Express knows it's behind a reverse proxy (e.g. nginx) + app.set('trust proxy', true); + + // Configure body parsing for form data and JSON, with large request limits app.use(bodyParser.urlencoded({ extended: true, limit: '50mb' })); app.use(bodyParser.json({ limit: '50mb', type: ['application/json', 'application/ld+json'] })); - // view engine setup + // Set view engine and templates directory for rendering dynamic HTML app.set('views', path.join(__dirname, '../../public/templates')); app.set('view engine', 'ejs'); + + // Use logging middleware for HTTP request logging app.use(logger('dev')); + + // Add built-in body parsers for JSON and URL-encoded data app.use(express.json()); app.use(express.urlencoded({ extended: false })); + + // Add cookie parsing middleware app.use(cookieParser()); + + // Serve static assets from the public directory (e.g., CSS, JS, images) app.use(express.static(path.join(__dirname, '../../public'))); - app.use(logger('dev')); - // Setup the session memory store + // Set up session management using in-memory store and a hardcoded secret app.use(session({ secret: 'asdifjasdf8asewj2aef23jdlkjs', resave: false, @@ -149,28 +148,31 @@ async function initialize(app, memoryStore) { store: memoryStore })); + // Define the path to the local resource directory (SHACL, JSON-LD files, etc.) var resourceDirectory = `${__dirname}/common/res/`; console.log(resourceDirectory); - // Serve jsonld and shacl resources in the resource directory - app.use('/res', express.static(resourceDirectory, { + // Serve files in the /res route with CORS enabled and proper content-type headers + app.use('/res', cors(), express.static(resourceDirectory, { setHeaders : function(res, path, stat) { - if(path.endsWith('.shacl')) { res.setHeader('Content-Type', 'text/turtle'); } - if(path.endsWith('.jsonld')) { res.setHeader('Content-Type', 'application/ld+json'); } } - }), serveIndex(resourceDirectory, { + }), + // Enable directory listing for /res with a custom stylesheet + serveIndex(resourceDirectory, { stylesheet: `${__dirname}/../../public/css/serve-index.css` })); } catch (err) { + // Log initialization errors console.log(err); } } +// Export the configured app instance for use in other modules module.exports = app; diff --git a/server/app/common/404.js b/server/app/common/404.js new file mode 100644 index 00000000..dfcbbb77 --- /dev/null +++ b/server/app/common/404.js @@ -0,0 +1,32 @@ +// common/middleware/404.js + +const ServerUtils = require('./utils/server-utils'); + +/** + * Middleware to handle 404 Not Found errors. + * Responds with HTML, JSON, or plain text depending on the request's Accept header. + */ +function handle404(req, res, next) { + res.status(404); + + // HTML response + if (req.accepts('html')) { + const data = { + auth: ServerUtils.getAuthInfoFromRequest(req) + }; + return res.render('404', { title: 'Not found', data }); + } + + // JSON response + if (req.accepts('json')) { + return res.json({ + error: 'Not found', + message: 'The requested resource could not be found.' + }); + } + + // Plain text fallback + res.type('txt').send('The requested resource could not be found.\n'); +} + +module.exports = handle404; diff --git a/server/app/common/constants.js b/server/app/common/constants.js index aafa1a23..ef253276 100644 --- a/server/app/common/constants.js +++ b/server/app/common/constants.js @@ -56,8 +56,11 @@ class Constants { static HTTP_CONTENT_TYPE_TURTLE = 'text/turtle'; static HTTP_CONTENT_TYPE_JSON = 'application/json'; static HTTP_CONTENT_TYPE_TEXT = 'text/plain'; + static HTTP_CONTENT_TYPE_FORM = 'application/x-www-form-urlencoded'; static HTTP_ACCEPT_RDF = 'text/turtle, application/ld+json, text/plain, application/rdf+xml, application/x-turtle'; + + static SHARED_DATABUS_PUBLIC_KEY = 'Databus Public Key'; } module.exports = Constants; diff --git a/server/app/common/databus-logger.js b/server/app/common/databus-logger.js index 7de4d0b0..42968fb0 100644 --- a/server/app/common/databus-logger.js +++ b/server/app/common/databus-logger.js @@ -16,7 +16,6 @@ class DatabusLogger { getReport() { return { - logLevel: this.level, log: this.entries }; } @@ -49,7 +48,7 @@ class DatabusLogger { debug(resource, message, payload) { - console.log(message); + // console.log(message); if (this.level != DatabusLogLevel.DEBUG) { return; } diff --git a/server/app/common/databus-message.js b/server/app/common/databus-message.js index 08cd1acc..711b6aff 100644 --- a/server/app/common/databus-message.js +++ b/server/app/common/databus-message.js @@ -2,6 +2,7 @@ class DatabusMessage { static NOTIFY_DATABUS_USER_ADDED = 'msg_notify_databus_user_added'; + static FILE_DOWNLOADED = 'msg_download'; static DATABUS_USER_ADDED = 'msg_databus_user_added'; static REQUEST_SEARCH_INDEX_REBUILD = 133703 diff --git a/server/app/common/databus-resource.js b/server/app/common/databus-resource.js new file mode 100644 index 00000000..87e4c3f7 --- /dev/null +++ b/server/app/common/databus-resource.js @@ -0,0 +1,121 @@ + +class DatabusResource { + + static DATABUS_COLLECTION_GROUP = "collections"; + + constructor(uri) { + + let url = new URL(uri); + this.baseURI = url.origin; + let segments = url.pathname.split("/"); + + // Assign fields based on URI segments + // Assuming URI follows the structure: //// + if (segments.length > 1) { + this.account = segments[1]; + } + if (segments.length > 2) { + this.group = segments[2]; + } + if (segments.length > 3) { + this.artifact = segments[3]; + } + if (segments.length > 4) { + this.version = segments[4]; + } + } + + // Getters for the fields + getAccount() { + return this.account; + } + + getGroup() { + return this.group; + } + + getArtifact() { + return this.artifact; + } + + getVersion() { + return this.version; + } + + // Getters for URIs + getAccountURI() { + return this.account != null ? `${this.baseURI}/${this.account}` : null; + } + + getGroupURI() { + return this.group != null + ? `${this.baseURI}/${this.account}/${this.group}` + : null; + } + + getArtifactURI() { + return this.artifact != null + ? `${this.baseURI}/${this.account}/${this.group}/${this.artifact}` + : null; + } + + getCollectionURI() { + return this.artifact != null + ? `${this.baseURI}/${this.account}/${DatabusResource.DATABUS_COLLECTION_GROUP}/${this.artifact}` + : null; + } + + getVersionURI() { + return this.version != null + ? `${this.baseURI}/${this.account}/${this.group}/${this.artifact}/${this.version}` + : null; + } + + getBaseURI() { + return this.baseURI; + } + + isVersion() { + return this.version != null; + } + + isArtifact() { + return this.artifact != null && this.version == null && DatabusResource.DATABUS_COLLECTION_GROUP !== this.group; + } + + isCollection() { + return this.artifact != null && this.version == null && DatabusResource.DATABUS_COLLECTION_GROUP === this.group; + } + + isGroup() { + return this.group != null && this.artifact == null; + } + + isAccount() { + return this.account != null && this.group == null; + } + + getTypeName() { + if(this.isVersion()) { + return "version"; + } + + if(this.isArtifact()) { + return "artifact"; + } + + if(this.isCollection()) { + return "collection" + } + + if(this.isGroup()) { + return "group"; + } + + if(this.isAccount()) { + return "account" + } + } +} + +module.exports = DatabusResource; \ No newline at end of file diff --git a/server/app/common/execute-construct.js b/server/app/common/execute-construct.js index eeae9d30..59a27aa7 100644 --- a/server/app/common/execute-construct.js +++ b/server/app/common/execute-construct.js @@ -1,6 +1,6 @@ var rdfstore = require('rdfstore'); var self = {}; - +var jsonld = require('jsonld'); // Possibly exec stuff on CLI const { exec } = require('child_process'); @@ -14,13 +14,19 @@ function escapeQuotes(value) { * @param {*} jsonld * @param {*} query */ -self.executeConstruct = async function (jsonld, query) { +self.executeConstruct = async function (data, query) { try { var store = await self.createStore(); - await self.loadJsonld(store, jsonld); + var nquads = await jsonld.toRDF(data, { + format: 'application/n-quads', eventHandler: function (event) { + console.log(event); + } + }); + + await self.loadJsonld(store, nquads); var quads = await self.queryStore(store, query); var triples = self.convertToN3(quads); @@ -81,7 +87,8 @@ self.queryStore = function (store, query) { self.loadJsonld = function (store, jsonld) { return new Promise(function (resolve, reject) { try { - store.load("application/ld+json", jsonld, function (err, results) { + + store.load("text/turtle", jsonld, function (err, results) { if (err != undefined) { reject(err); } else { @@ -93,6 +100,7 @@ self.loadJsonld = function (store, jsonld) { reject(err); } }); + } self.createStore = function () { diff --git a/server/app/common/execute-query.js b/server/app/common/execute-query.js index 80db8de4..54b72903 100644 --- a/server/app/common/execute-query.js +++ b/server/app/common/execute-query.js @@ -1,157 +1,119 @@ -var rp = require('request-promise'); +const axios = require('axios'); const Constants = require('./constants'); -var sparql = {}; -var sparqlEndpoint = (process.env.DATABUS_DATABASE_URL || Constants.DEFAULT_DATABASE_URL) + '/sparql'; +var sparql = {}; +const sparqlEndpoint = (process.env.DATABUS_DATABASE_URL || Constants.DEFAULT_DATABASE_URL) + '/sparql'; /** - * Uses the request-promise package to send a select query against the sparql endpoint - * and return a set of bindings or null - * @param {[type]} query [description] - * @return {[type]} [description] + * Executes a SELECT query against the SPARQL endpoint and returns a set of bindings or null + * @param {string} query - The SPARQL query to execute + * @return {Array|null} - The bindings or null if an error occurs */ sparql.executeSelect = async function (query) { - - try { - - // Do a POST request with the passed query - var options = { - method: 'POST', - uri: sparqlEndpoint + '?timeout=10000', - body: "query=" + encodeURIComponent(query), - json: true, - headers: { - "Content-type": "application/x-www-form-urlencoded", - "Accept": "application/json" - }, - }; - - // Await the response - var response = await rp(options); - - bindings = response.results.bindings; - - // Prune the returned bindings - for (var i in bindings) { - bindings[i] = reduceBinding(bindings[i]); + try { + // Prepare the POST request data + const data = `query=${encodeURIComponent(query)}`; + + const response = await axios.post(sparqlEndpoint + '?timeout=10000', data, { + headers: { + 'Content-type': Constants.HTTP_CONTENT_TYPE_FORM, + 'Accept': Constants.HTTP_CONTENT_TYPE_JSON } + }); - return bindings; + let bindings = response.data.results.bindings; - } catch (err) { - console.log(err); - return null; - } -} + // Prune the returned bindings + for (let i in bindings) { + bindings[i] = reduceBinding(bindings[i]); + } + + return bindings; + } catch (err) { + console.log(err); + return null; + } +}; +/** + * Executes a CONSTRUCT query against the SPARQL endpoint and returns the result or null + * @param {string} query - The SPARQL query to execute + * @param {string} format - The desired response format + * @return {Object|null} - The response data or null if an error occurs + */ sparql.executeConstruct = async function (query, format) { - try { - - // Do a POST request with the passed query - var options = { - method: 'POST', - uri: `${sparqlEndpoint}?timeout=10000`, - body: `query=${encodeURIComponent(query)}`, - json: true, - headers: { - "Content-type": "application/x-www-form-urlencoded", - "Accept": format - }, - }; - - // Await the response - var response = await rp(options); - return response; - - } catch (err) { - console.log(err); - return null; - } -} + try { + // Prepare the POST request data + const data = `query=${encodeURIComponent(query)}`; + + const response = await axios.post(`${sparqlEndpoint}?timeout=10000`, data, { + headers: { + 'Content-type': Constants.HTTP_CONTENT_TYPE_FORM, + 'Accept': format + } + }); + return response.data; + } catch (err) { + console.log(err); + return null; + } +}; /** - * Uses the request-promise package to send a select query against the sparql endpoint - * and return a set of bindings or null - * @param {[type]} query [description] - * @return {[type]} [description] + * Executes an ASK query against the SPARQL endpoint and returns the boolean result or null + * @param {string} query - The SPARQL query to execute + * @return {boolean|null} - The boolean result or null if an error occurs */ sparql.executeAsk = async function (query) { + try { + // Prepare the POST request data + const data = `query=${encodeURIComponent(query)}`; - try { - - // Do a POST request with the passed query - var options = { - method: 'POST', - uri: sparqlEndpoint + '?timeout=10000', - body: "query=" + encodeURIComponent(query), - json: true, - headers: { - "Content-type": "application/x-www-form-urlencoded" - }, - }; - - // Await the response - var response = await rp(options); - return response.boolean; - - } catch (err) { - console.log(err); - return null; - } -} + const response = await axios.post(sparqlEndpoint + '?timeout=10000', data, { + headers: { + 'Content-type': Constants.HTTP_CONTENT_TYPE_FORM + } + }); + + return response.data.boolean; + } catch (err) { + console.log(err); + return null; + } +}; /** - * reduce the binding entry objects to their value - * @param {[type]} binding [description] - * @return {[type]} [description] + * Reduces the binding entry objects to their value + * @param {Object} binding - The binding object to reduce + * @return {Object} - The reduced binding object */ function reduceBinding(binding) { - for (var key in binding) { - binding[key] = binding[key].value; - } + for (let key in binding) { + binding[key] = binding[key].value; + } - return binding; + return binding; } - /** - * Execute an UPDATE query with auth string - * Throws an exception! - -sparql.executeUpdate = async function(query, username, password) { - var digestRequest = require('request-digest')(username, password); - await digestRequest.requestAsync({ - host: 'http://localhost', - path: '/sparql-auth', - port: config.sparqlEndpointPort, - method: 'POST', - json: true, - body: "query=" + encodeURIComponent(query), - headers: { - "Content-type" : "application/x-www-form-urlencoded" - } - }); - }*/ - -/** - * placeHolderMappings is a map string => string. This function replaces all - * occurrances of %key% in query with value. + * Replaces all occurrences of %key% in the query with the corresponding value from placeholderMappings + * @param {string} query - The query to format + * @param {Object} placeholderMappings - A map of placeholders and their replacement values + * @return {string} - The formatted query */ sparql.formatQuery = function (query, placeholderMappings) { + if (placeholderMappings === undefined) { + return query; + } - if (placeholderMappings == undefined) { - return query; - } - - for (var placeholder in placeholderMappings) { - var re = new RegExp('%' + placeholder + '%', "g"); - query = query.replace(re, placeholderMappings[placeholder]); - } - - return query; -} + for (let placeholder in placeholderMappings) { + const re = new RegExp('%' + placeholder + '%', 'g'); + query = query.replace(re, placeholderMappings[placeholder]); + } + return query; +}; -module.exports = sparql +module.exports = sparql; \ No newline at end of file diff --git a/server/app/common/get-jsonld.js b/server/app/common/get-jsonld.js index 8b8929f4..2c0a2007 100644 --- a/server/app/common/get-jsonld.js +++ b/server/app/common/get-jsonld.js @@ -1,54 +1,61 @@ -const defaultContext = require('./res/context.jsonld'); -const rp = require('request-promise'); -const request = require('request'); +const axios = require('axios'); const jsonld = require('jsonld'); const exec = require('./execute-query'); const ServerUtils = require('./utils/server-utils'); const DatabusUris = require('../../../public/js/utils/databus-uris'); const HttpStrings = require('./http-strings'); - +const JsonldLoader = require('./utils/jsonld-loader'); module.exports = async function getJsonLd(resourceUri, template, formatting) { try { - var exists = await exec.executeAsk(`ASK { <${resourceUri}> ?p ?o }`); + // Check if resource exists + const exists = await exec.executeAsk(`ASK { <${resourceUri}> ?p ?o }`); if (!exists) { return null; } - var query = ServerUtils.formatQuery(template, { + // Format the query + const query = ServerUtils.formatQuery(template, { RESOURCE_URI: resourceUri }); - var headers = {}; - headers[HttpStrings.HEADER_CONTENT_TYPE] = HttpStrings.CONTENT_TYPE_FORM_URL_ENCODED; - headers[HttpStrings.HEADER_ACCEPT] = HttpStrings.CONTENT_TYPE_JSONLD; + console.log(query); + + + const headers = { + [HttpStrings.HEADER_CONTENT_TYPE]: HttpStrings.CONTENT_TYPE_FORM_URL_ENCODED, + [HttpStrings.HEADER_ACCEPT]: HttpStrings.CONTENT_TYPE_JSONLD + }; - var options = { - method: HttpStrings.METHOD_POST, - uri: `${process.env.DATABUS_DATABASE_URL}/sparql?timeout=10000`, - body: `query=${encodeURIComponent(query)}`, + // Prepare request options + const options = { + method: 'POST', + url: `${process.env.DATABUS_DATABASE_URL}/sparql?timeout=10000`, headers: headers, + data: `query=${encodeURIComponent(query)}` }; - var result = JSON.parse(await rp(options)); + // Send the request using axios + const response = await axios(options); - if (formatting == undefined || formatting == 'compacted' || formatting == 'compact') { - // Single out jsonld in order to compact the result with the databus context - var result = await jsonld.compact(result, defaultContext); - if (process.env.DATABUS_CONTEXT_URL != undefined) { - result[DatabusUris.JSONLD_CONTEXT] = process.env.DATABUS_CONTEXT_URL; - } - } - else if (formatting == 'flatten') { - var result = await jsonld.flatten(result); + let result = response.data; + + // Format the result based on the requested formatting + if (formatting === undefined || formatting === 'compacted' || formatting === 'compact') { + // Compact the result with the Databus context + result = await jsonld.compact(result, JsonldLoader.DEFAULT_CONTEXT_URL); + } else if (formatting === 'flatten') { + // Flatten the result + result = await jsonld.flatten(result); } return result; + } catch (err) { console.log(err); return null; } -} \ No newline at end of file +}; diff --git a/server/app/common/get-linked-data.js b/server/app/common/get-linked-data.js index b5c06a65..ff3a0add 100644 --- a/server/app/common/get-linked-data.js +++ b/server/app/common/get-linked-data.js @@ -25,38 +25,56 @@ module.exports = async function getLinkedData(req, res, next, resourceUri, templ accept = HttpStrings.CONTENT_TYPE_JSONLD; } - if(accept == HttpStrings.CONTENT_TYPE_JSONLD) { + let firstAccept = accept; + + let indexOfComma = accept.indexOf(","); + + if (indexOfComma >= 0) { + firstAccept = accept.substring(0, indexOfComma); + } + + // console.log(firstAccept); + // console.log(HttpStrings.CONTENT_TYPE_JSONLD); + + + if (firstAccept == HttpStrings.CONTENT_TYPE_JSONLD) { // Handle JSONLD separately var jsonld = await getJsonld(resourceUri, template, formatting); - if(jsonld != null) { + if (jsonld != null) { res.set(HttpStrings.HEADER_CONTENT_TYPE, HttpStrings.CONTENT_TYPE_JSONLD); res.status(200).send(`${JSON.stringify(jsonld, null, 3)}\n`); return; } - } else { - - // Format the query - var query = ServerUtils.formatQuery(template, { - RESOURCE_URI: resourceUri - }); - - // Prepare OPTIONS for database request - var headers = {}; - headers[HttpStrings.HEADER_CONTENT_TYPE] = HttpStrings.CONTENT_TYPE_FORM_URL_ENCODED; - headers[HttpStrings.HEADER_ACCEPT] = accept; - - var options = { - method: HttpStrings.METHOD_POST, - uri: `${process.env.DATABUS_DATABASE_URL}/sparql?timeout=10000`, - body: `query=${encodeURIComponent(query)}`, - headers: headers, - }; - - // Pipe the request - request(options).pipe(res); - return; } - res.status(500).send('Encountered a database error when trying to fetch the resource.'); + // Format the query + var query = ServerUtils.formatQuery(template, { + RESOURCE_URI: resourceUri + }); + + var headers = {}; + headers[HttpStrings.HEADER_CONTENT_TYPE] = HttpStrings.CONTENT_TYPE_FORM_URL_ENCODED; + headers[HttpStrings.HEADER_ACCEPT] = accept; + + var options = { + method: HttpStrings.METHOD_POST, + uri: `${process.env.DATABUS_DATABASE_URL}/sparql?timeout=10000`, + body: `query=${encodeURIComponent(query)}`, + headers: headers, + }; + + request(options, function (error, response, body) { + if (error) { + res.status(500).send(error.message); + return; + } + + if (!body.endsWith('\n')) { + body += '\n'; + } + + res.set(response.headers); + res.send(body); + }); } \ No newline at end of file diff --git a/server/app/common/protect/middleware.js b/server/app/common/protect/middleware.js index 34159df0..0b29d791 100644 --- a/server/app/common/protect/middleware.js +++ b/server/app/common/protect/middleware.js @@ -4,17 +4,19 @@ const COOKIES = require('express-openid-connect/lib/cookies'); const COOKIE_NAME = 'skipSilentLogin'; +const jwt = require('jsonwebtoken'); var oidc = require('express-openid-connect'); var oidcConfig = require('./oidc.json'); const weakRef = require('express-openid-connect/lib/weakCache'); var getRandomValues = require('get-random-values'); +const { ProxyAgent } = require('proxy-agent'); var fs = require('fs'); const Constants = require('../constants'); const DatabusUserDatabase = require('../../../userdb'); -const DatabusMessage = require('../databus-message'); +const ServerUtils = require('../utils/server-utils'); function uuidv4() { return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => @@ -25,12 +27,18 @@ function uuidv4() { function forceLogin(request, response) { response.oidc.getRedirectUri = function () { - return getRequestUri(request) + Constants.DATABUS_OIDC_CALLBACK_ROUTE; + let redirect_uri = getRequestUri(request) + Constants.DATABUS_OIDC_CALLBACK_ROUTE; + console.log("Sending auth request with redirect_uri: " + redirect_uri); + return redirect_uri; } + if (request.query.redirectUrl == undefined) { response.oidc.login(); } else { + + console.log("Logging in with redirect: " + request.query.redirectUrl); + response.oidc.login({ returnTo: decodeURIComponent(request.query.redirectUrl), authorizationParams: { @@ -101,17 +109,17 @@ class DatabusProtect { return user != null; } - async getUser(sub) { - return await this.userdb.getUser(sub); + async getUser(id) { + return await this.userdb.getAccountsById(id); } - async addUser(sub, name, accountName) { - return await this.userdb.addUser(sub, name, accountName); + async addUser(id, name, accountName) { + return await this.userdb.addUser(id, name, accountName); } - async addApiKey(sub, name) { + async addApiKey(accountName, name) { var apikey = uuidv4(); - if (await this.userdb.addApiKey(sub, name, apikey)) { + if (await this.userdb.addApiKey(accountName, name, apikey)) { return { keyname: name, apikey: apikey }; } @@ -120,12 +128,11 @@ class DatabusProtect { /** * Removes one or more API keys by name - * @param {} sub * @param {*} name * @returns */ - async removeApiKey(sub, name) { - return await this.userdb.deleteApiKey(sub, name); + async removeApiKey(accountName, name) { + return await this.userdb.deleteApiKey(accountName, name); } async getApiKeyUser(req) { @@ -135,21 +142,50 @@ class DatabusProtect { return null; } - var entry = await this.userdb.getSub(apikey); + var apiKey = await this.userdb.getApiKey(apikey); - if (entry == null) { + if (apiKey == null) { return null; } - return await this.userdb.getUser(entry.sub); + let account = await this.userdb.getAccount(apiKey.accountName); + + if (account == null) { + return null; + } + + return { + userId: account.id, + accounts: [ account ] + }; + } + + sendError(req, res, code, message, description) { + if (this.isBrowserRequest(req)) { + var data = {} + data.auth = ServerUtils.getAuthInfoFromRequest(req); + data.code = code; + data.message = message; + data.description = description; + + return res.status(401).render('unauthorized', { + title: 'Unauthorized', + data: data, + }); + } else { + return res.status(401).send(); + } } - auth() { + auth() { + oidcConfig.issuerBaseURL = process.env.DATABUS_OIDC_ISSUER_BASE_URL; oidcConfig.clientID = process.env.DATABUS_OIDC_CLIENT_ID; + oidcConfig.clientSecret = process.env.DATABUS_OIDC_SECRET; oidcConfig.secret = process.env.DATABUS_OIDC_SECRET; - oidcConfig.baseURL = 'http://localhost:3000'; + oidcConfig.baseURL = process.env.DATABUS_RESOURCE_BASE_URL; + oidcConfig.httpAgent = { "http": new ProxyAgent(), "https": new ProxyAgent() }; oidcConfig.routes = { "login": false, @@ -165,6 +201,19 @@ class DatabusProtect { rollingDuration: 60 * 24, }; + oidcConfig.authorizationParams = { + prompt: 'login', + scope: "openid profile email roles" + }; + + if(process.env.DATABUS_OIDC_RESPONSE_TYPE != undefined) { + oidcConfig.authorizationParams.response_type = process.env.DATABUS_OIDC_RESPONSE_TYPE; + } + + if(oidcConfig.authorizationParams.response_type == 'code') { + oidcConfig.authorizationParams.code_challenge_method = "S256"; + } + return oidc.auth(oidcConfig); } @@ -172,12 +221,12 @@ class DatabusProtect { return async (req, res, next) => { - if (req.databus.authenticated == false || req.databus.accountName == null) { - response.status(401).send('Authentication failed.'); + if (req.databus.authenticated == false || req.databus.accounts == null) { + res.status(401).send('Authentication failed.'); return; } - if (req.params.account != req.databus.accountName) { + if(!req.databus.accounts.some(a => a.accountName == req.params.account)) { res.status(403).send(Constants.MESSAGE_WRONG_NAMESPACE); return; } @@ -186,12 +235,37 @@ class DatabusProtect { } } + getUserIdFromOIDCToken(oidc) { + var idProperty = process.env.DATABUS_OIDC_USER_ID_PROPERTY; + + if(idProperty == undefined) { + console.log("No id property specified. Falling back to 'sub'"); + idProperty = 'sub'; + } + + if(oidc[idProperty] == undefined) { + console.log(`OIDC token did not contain a property '${idProperty}'. Falling back to 'sub'`); + idProperty = 'sub'; + } + + return oidc[idProperty]; + } + fetchUser() { return async (req, res, next) => { + + // console.log("==== USER FETCH ===="); + // User already fetched and authenticated if (req.databus != undefined && req.databus.authenticated) { + + // console.log("DATABUS OBJECT:"); + // console.log(JSON.stringify(req.databus, null, 3)); + + // console.log("OIDC OBJECT:"); + // console.log(JSON.stringify(req.oidc, null, 3)); // console.log(`PROTECT Authenticated request by \x1b[32m${req.databus.accountName}\x1b[0m: \x1b[36m${req.url}\x1b[0m`); return next(); } @@ -200,15 +274,25 @@ class DatabusProtect { req.databus.authenticated = false; if (req.oidc == undefined) { + //console.log("OIDC INFO NOT FOUND"); return next(); } - + + if (!req.oidc.isAuthenticated()) { + // console.log("OIDC INFO SAYS: NOT AUTHENTICATED"); return next(); } + //console.log("OIDC INFO:"); + //console.log(JSON.stringify(req.oidc, null, 3)); + req.databus.authenticated = true; + if (req.path == Constants.DATABUS_OIDC_LOGOUT_ROUTE) { + return next(); + } + if (req.oidc.user == undefined) { return next(); } @@ -216,17 +300,48 @@ class DatabusProtect { // console.log(`user: ${JSON.stringify(req.oidc.user)}`); req.databus.oidc_name = req.oidc.user.name; req.databus.oidc_email = req.oidc.user.email; + req.databus.userId = this.getUserIdFromOIDCToken(req.oidc.user) + + if (req.oidc.accessToken) { + + req.databus.accessToken = req.oidc.accessToken; + + try { + // Decode the access token WITHOUT verifying the signature + const decodedToken = jwt.decode(req.oidc.accessToken["access_token"]); + + if (decodedToken && decodedToken.resource_access) { + let access = decodedToken.resource_access; + let clientAccess = access[oidcConfig.clientID]; + + // console.log("Access: " + JSON.stringify(access, null, 3)); + + if(clientAccess && clientAccess.roles) { + req.databus.roles = clientAccess.roles; + } + + } else { + console.warn("Failed to decode access token."); + } + } catch (error) { + console.error("Error decoding access token:", error); + } + } else { + console.warn("No access token received."); + } + + // Looking up the user... - var user = await this.userdb.getUser(req.oidc.user.sub); + let userId = this.getUserIdFromOIDCToken(req.oidc.user) + var accounts = await this.userdb.getAccountsById(userId); - if (user != undefined) { - req.databus.sub = user.sub; - req.databus.accountName = user.accountName; - req.databus.apiKeys = await this.userdb.getApiKeys(user.sub); + if (accounts != undefined) { + req.databus.userId = userId; + req.databus.accounts = accounts; } - console.log(`PROTECT Authenticated request by \x1b[32m${req.databus.accountName}\x1b[0m: \x1b[36m${req.url}\x1b[0m`); + console.log(`PROTECT Authenticated request by \x1b[32m${userId}\x1b[0m: \x1b[36m${req.url}\x1b[0m`); return next(); } } @@ -237,22 +352,25 @@ class DatabusProtect { return [async (req, res, next) => { + // console.log("CHECKING SSO"); + if (req.oidc == undefined || !self.isBrowserRequest(req)) { var apiTokenUser = await self.getApiKeyUser(req); + // console.log("TRYING API TOKEN"); if (apiTokenUser != null) { // Api token has been found req.databus = {}; - req.databus.sub = apiTokenUser.sub; + req.databus.userId = apiTokenUser.userId; req.databus.authenticated = true; - req.databus.accountName = apiTokenUser.accountName; - req.databus.apiKeys = await self.userdb.getApiKeys(apiTokenUser.sub); + req.databus.accounts = apiTokenUser.accounts; return next(); } + // console.log("NO SUCCESS..."); return next(); } @@ -270,6 +388,8 @@ class DatabusProtect { cancelSilentLogin(req, res); // console.log('attempting silent login'); try { + + // console.log("SILENT LOGIN..."); return res.oidc.silentLogin(); } catch (e) { return next(); @@ -281,18 +401,56 @@ class DatabusProtect { }, this.fetchUser()]; } + checkRequiredRole() { + return (req, res, next) => { + + if (process.env.DATABUS_PRIVATE_MODE != "true") { + next(); + return; + } + + if (req.path == Constants.DATABUS_OIDC_LOGOUT_ROUTE) { + next(); + return; + } + + let requiredRole = process.env.DATABUS_OIDC_REQUIRED_ROLE; + + if(requiredRole == undefined || requiredRole === "") { + next(); + return; + } + + if (!req.databus || !req.databus.authenticated) { + return this.sendError(req, res, 401, 'Authentication required.'); + } + + if (req.databus.roles == undefined || !req.databus.roles.includes(requiredRole)) { + return this.sendError(req, res, 403, 'Forbidden', 'You do not have the required roles to access this resource'); + } + + console.log(`Required role ${requiredRole} is present.`); + next(); + }; + } + + async authenticate(request, response, next, noRedirect, responseHandler) { + // Consider doing webid tls here var apiTokenUser = await this.getApiKeyUser(request); if (apiTokenUser != null) { + + let requiredRole = process.env.DATABUS_OIDC_REQUIRED_ROLE; + // Api token has been found request.databus = {}; - request.databus.sub = apiTokenUser.sub; + request.databus.userId = apiTokenUser.userId; request.databus.authenticated = true; - request.databus.accountName = apiTokenUser.accountName; - request.databus.apiKeys = await this.userdb.getApiKeys(apiTokenUser.sub); + request.databus.accounts = apiTokenUser.accounts; + request.databus.roles = [ requiredRole ]; return next(); } @@ -314,6 +472,7 @@ class DatabusProtect { // Html requests need a redirect if (!noRedirect && this.isBrowserRequest(request)) { + forceLogin(request, response); return; } @@ -336,7 +495,7 @@ class DatabusProtect { var self = this; return [async function (request, response, next) { await self.authenticate(request, response, next, noRedirect, responseHandler); - }, this.fetchUser()]; + }, this.fetchUser(), this.checkRequiredRole(), ]; } protectAccount(noRedirect) { @@ -348,7 +507,7 @@ class DatabusProtect { var self = this; return [async function (request, response, next) { await self.authenticate(request, response, next, noRedirect); - }, this.fetchUser(), this.checkAccount()]; + }, this.fetchUser(), this.checkRequiredRole(), this.checkAccount()]; } } diff --git a/server/app/common/queries/constructs/ld/construct-artifact.sparql b/server/app/common/queries/constructs/ld/construct-artifact.sparql index e8c2a0d8..cd4a85c0 100644 --- a/server/app/common/queries/constructs/ld/construct-artifact.sparql +++ b/server/app/common/queries/constructs/ld/construct-artifact.sparql @@ -1,20 +1,19 @@ PREFIX databus: -PREFIX dcv: -PREFIX dct: -PREFIX dcat: -PREFIX rdfs: -PREFIX rdf: - -CONSTRUCT -{ +CONSTRUCT { ?artifact ?p ?o . + ?artifact databus:hasVersion ?version . } -WHERE -{ - GRAPH ?g - { - VALUES ?artifact { <%RESOURCE_URI%> } +WHERE { + VALUES ?artifact { <%RESOURCE_URI%> } + + GRAPH ?gArtifact { ?artifact ?p ?o . } -} \ No newline at end of file + + OPTIONAL { + GRAPH ?gVersion { + ?version databus:artifact ?artifact . + } + } +} diff --git a/server/app/common/queries/constructs/ld/construct-group.sparql b/server/app/common/queries/constructs/ld/construct-group.sparql index b18e6e94..3999eee9 100644 --- a/server/app/common/queries/constructs/ld/construct-group.sparql +++ b/server/app/common/queries/constructs/ld/construct-group.sparql @@ -1,12 +1,19 @@ -CONSTRUCT -{ +PREFIX databus: + +CONSTRUCT { ?group ?p ?o . + ?group databus:hasArtifact ?artifact . } -WHERE -{ - GRAPH ?g - { - VALUES ?group { <%RESOURCE_URI%> } +WHERE { + VALUES ?group { <%RESOURCE_URI%> } + + GRAPH ?gGroup { ?group ?p ?o . } -} \ No newline at end of file + + OPTIONAL { + GRAPH ?gArtifact { + ?artifact databus:group ?group . + } + } +} diff --git a/server/app/common/queries/query-modules/accounts.js b/server/app/common/queries/query-modules/accounts.js index 57b752f7..d760e4d2 100644 --- a/server/app/common/queries/query-modules/accounts.js +++ b/server/app/common/queries/query-modules/accounts.js @@ -12,7 +12,7 @@ instance.getPublisherHasAccount = async function (publisherUri, accountUri) { var query = exec.formatQuery(require('../sparql/get-publisher-has-account.sparql'), queryOptions); - console.log(query); + // console.log(query); var result = await exec.executeAsk(query); diff --git a/server/app/common/queries/query-modules/collections.js b/server/app/common/queries/query-modules/collections.js index 99301f8f..2d80aa82 100644 --- a/server/app/common/queries/query-modules/collections.js +++ b/server/app/common/queries/query-modules/collections.js @@ -89,7 +89,7 @@ instance.hasCollectionContent = function (content) { instance.getCollectionsByAccount = async function (accountName, onlyIssued) { let queryOptions = { - ACCOUNT_URI: `${UriUtils.createResourceUri([accountName])}#this` + ACCOUNT_URI: `${UriUtils.createResourceUri([accountName])}` }; let query = exec.formatQuery(require('../sparql/get-collections-by-account.sparql'), queryOptions); @@ -111,6 +111,7 @@ instance.getCollectionsByAccount = async function (accountName, onlyIssued) { bindings[e].title = unescape(bindings[e].title); bindings[e].abstract = unescape(bindings[e].abstract); bindings[e].description = unescape(bindings[e].description); + bindings[e].accountName = accountName; bindings[e].content = DatabusUtils.tryParseJson(unescape(bindings[e].content)); result[bindings[e].uri] = bindings[e]; diff --git a/server/app/common/queries/query-modules/pages.js b/server/app/common/queries/query-modules/pages.js index 89bbf488..22472f46 100644 --- a/server/app/common/queries/query-modules/pages.js +++ b/server/app/common/queries/query-modules/pages.js @@ -199,7 +199,7 @@ instance.getGroupFacets = async function (resourceUri) { return null; } - return formatFacets(bindings, require('../../../pages/facet-metadata.json')); + return formatFacets(bindings, require('../../../webapp/facet-metadata.json')); } instance.getArtifactFacets = async function (resourceUri) { @@ -214,7 +214,7 @@ instance.getArtifactFacets = async function (resourceUri) { return null; } - return formatFacets(bindings, require('../../../pages/facet-metadata.json')); + return formatFacets(bindings, require('../../../webapp/facet-metadata.json')); } instance.getVersionFacets = async function (resourceUri) { @@ -228,7 +228,7 @@ instance.getVersionFacets = async function (resourceUri) { return null; } - return formatFacets(bindings, require('../../../pages/facet-metadata.json')); + return formatFacets(bindings, require('../../../webapp/facet-metadata.json')); } function formatFacets(facetData, facetMetadata) { diff --git a/server/app/common/queries/sparql/get-artifacts-by-account.sparql b/server/app/common/queries/sparql/get-artifacts-by-account.sparql index fed57cc2..e970404a 100644 --- a/server/app/common/queries/sparql/get-artifacts-by-account.sparql +++ b/server/app/common/queries/sparql/get-artifacts-by-account.sparql @@ -7,11 +7,9 @@ PREFIX rdfs: SELECT DISTINCT ?artifact as ?uri ?title ?abstract ?description WHERE { - GRAPH ?g { ?artifact a databus:Artifact . ?artifact databus:account <%ACCOUNT_URI%> . OPTIONAL { ?artifact dct:title ?title . } OPTIONAL { ?artifact dct:abstract ?abstract . } OPTIONAL { ?artifact dct:description ?description . } - } -} \ No newline at end of file +} diff --git a/server/app/common/queries/sparql/get-collection-content.sparql b/server/app/common/queries/sparql/get-collection-content.sparql new file mode 100644 index 00000000..db5b6d95 --- /dev/null +++ b/server/app/common/queries/sparql/get-collection-content.sparql @@ -0,0 +1,8 @@ +PREFIX databus: + +SELECT ?content WHERE +{ + GRAPH ?g { + <%COLLECTION_URI%> databus:collectionContent ?content . + } +} diff --git a/server/app/common/queries/sparql/get-collection.sparql b/server/app/common/queries/sparql/get-collection.sparql index 4217c3de..863a14f1 100644 --- a/server/app/common/queries/sparql/get-collection.sparql +++ b/server/app/common/queries/sparql/get-collection.sparql @@ -6,7 +6,7 @@ SELECT * WHERE GRAPH ?g { <%COLLECTION_URI%> a databus:Collection . <%COLLECTION_URI%> dct:title ?label . - <%COLLECTION_URI%> dct:publisher ?publisher . + <%COLLECTION_URI%> databus:account ?account . <%COLLECTION_URI%> dct:abstract ?abstract . <%COLLECTION_URI%> dct:description ?description . <%COLLECTION_URI%> databus:collectionContent ?content . diff --git a/server/app/common/queries/sparql/get-collections-by-account.sparql b/server/app/common/queries/sparql/get-collections-by-account.sparql index 8cdb0216..167bc8bd 100644 --- a/server/app/common/queries/sparql/get-collections-by-account.sparql +++ b/server/app/common/queries/sparql/get-collections-by-account.sparql @@ -5,7 +5,7 @@ PREFIX databus: SELECT * WHERE { ?uri a databus:Collection . - ?uri dct:publisher <%ACCOUNT_URI%> . + ?uri databus:account <%ACCOUNT_URI%> . ?uri dct:title ?title . ?uri dct:abstract ?abstract . ?uri dct:description ?description . diff --git a/server/app/common/queries/sparql/get-groups-by-account.sparql b/server/app/common/queries/sparql/get-groups-by-account.sparql index fb1571f4..1ddbfcb8 100644 --- a/server/app/common/queries/sparql/get-groups-by-account.sparql +++ b/server/app/common/queries/sparql/get-groups-by-account.sparql @@ -6,7 +6,6 @@ PREFIX rdfs: SELECT DISTINCT ?group as ?uri ?title ?abstract ?description WHERE { - ?group a databus:Group . ?group databus:account <%ACCOUNT_URI%> . OPTIONAL { ?group dct:title ?title. } diff --git a/server/app/common/queries/userdb/add-account.sql b/server/app/common/queries/userdb/add-account.sql new file mode 100644 index 00000000..ee2235ae --- /dev/null +++ b/server/app/common/queries/userdb/add-account.sql @@ -0,0 +1,2 @@ +INSERT INTO accounts(id, accountName) +VALUES ("%ID%", "%ACCOUNT_NAME%") \ No newline at end of file diff --git a/server/app/common/queries/userdb/add-api-key.sql b/server/app/common/queries/userdb/add-api-key.sql index e3d11987..fef8f7d9 100644 --- a/server/app/common/queries/userdb/add-api-key.sql +++ b/server/app/common/queries/userdb/add-api-key.sql @@ -1,2 +1,2 @@ -INSERT INTO apikeys(apikey, keyname, sub) -VALUES ("%APIKEY%", "%KEYNAME%", "%SUB%") \ No newline at end of file +INSERT INTO apikeys(apikey, keyname, accountName) +VALUES ("%APIKEY%", "%KEYNAME%", "%ACCOUNTNAME%") \ No newline at end of file diff --git a/server/app/common/queries/userdb/add-user.sql b/server/app/common/queries/userdb/add-user.sql index 12552214..892321fc 100644 --- a/server/app/common/queries/userdb/add-user.sql +++ b/server/app/common/queries/userdb/add-user.sql @@ -1,2 +1,2 @@ -INSERT INTO users(sub, displayName, accountName) -VALUES ("%SUB%", "%DISPLAYNAME%", "%ACCOUNT_NAME%") \ No newline at end of file +INSERT INTO users(id, email) +VALUES ("%ID%", "%EMAIL%") \ No newline at end of file diff --git a/server/app/common/queries/userdb/create-account-table.sql b/server/app/common/queries/userdb/create-account-table.sql new file mode 100644 index 00000000..8a8af451 --- /dev/null +++ b/server/app/common/queries/userdb/create-account-table.sql @@ -0,0 +1,6 @@ +CREATE TABLE IF NOT EXISTS accounts ( + accountName VARCHAR(255) NOT NULL, + id VARCHAR(255) NOT NULL, + PRIMARY KEY (accountName), + FOREIGN KEY(id) REFERENCES users(id) ON DELETE CASCADE +); \ No newline at end of file diff --git a/server/app/common/queries/userdb/create-api-key-table.sql b/server/app/common/queries/userdb/create-api-key-table.sql index 0ad49238..aea11af3 100644 --- a/server/app/common/queries/userdb/create-api-key-table.sql +++ b/server/app/common/queries/userdb/create-api-key-table.sql @@ -1,7 +1,7 @@ CREATE TABLE IF NOT EXISTS apikeys ( apikey VARCHAR(255) PRIMARY KEY, keyname VARCHAR(255) NOT NULL, - sub VARCHAR(255) NOT NULL, - UNIQUE(keyname, sub), - FOREIGN KEY(sub) REFERENCES users(sub) ON DELETE CASCADE + accountName VARCHAR(255) NOT NULL, + UNIQUE(keyname, accountName), + FOREIGN KEY(accountName) REFERENCES accounts(accountName) ON DELETE CASCADE ); \ No newline at end of file diff --git a/server/app/common/queries/userdb/create-user-table.sql b/server/app/common/queries/userdb/create-user-table.sql index fe8efc86..fb4b4b4a 100644 --- a/server/app/common/queries/userdb/create-user-table.sql +++ b/server/app/common/queries/userdb/create-user-table.sql @@ -1,5 +1,5 @@ CREATE TABLE IF NOT EXISTS users ( - sub VARCHAR(255) NOT NULL PRIMARY KEY, - displayName VARCHAR(255) NOT NULL, - accountName VARCHAR(255) NOT NULL + id VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL, + PRIMARY KEY (id) ); \ No newline at end of file diff --git a/server/app/common/queries/userdb/delete-account.sql b/server/app/common/queries/userdb/delete-account.sql new file mode 100644 index 00000000..096e1f51 --- /dev/null +++ b/server/app/common/queries/userdb/delete-account.sql @@ -0,0 +1 @@ +DELETE FROM accounts WHERE accountName="%ACCOUNT_NAME%" \ No newline at end of file diff --git a/server/app/common/queries/userdb/delete-api-key.sql b/server/app/common/queries/userdb/delete-api-key.sql index debe8672..9059fe9f 100644 --- a/server/app/common/queries/userdb/delete-api-key.sql +++ b/server/app/common/queries/userdb/delete-api-key.sql @@ -1 +1 @@ -DELETE FROM apikeys WHERE keyname="%NAME%" AND sub="%SUB%" \ No newline at end of file +DELETE FROM apikeys WHERE keyname="%NAME%" AND accountName="%ACCOUNTNAME%" \ No newline at end of file diff --git a/server/app/common/queries/userdb/delete-user.sql b/server/app/common/queries/userdb/delete-user.sql index 4866c4a1..8e368c43 100644 --- a/server/app/common/queries/userdb/delete-user.sql +++ b/server/app/common/queries/userdb/delete-user.sql @@ -1 +1 @@ -DELETE FROM users WHERE sub="%SUB%" \ No newline at end of file +DELETE FROM users WHERE id="%ID%" \ No newline at end of file diff --git a/server/app/common/queries/userdb/get-account-by-api-key.sql b/server/app/common/queries/userdb/get-account-by-api-key.sql new file mode 100644 index 00000000..c1f981ee --- /dev/null +++ b/server/app/common/queries/userdb/get-account-by-api-key.sql @@ -0,0 +1 @@ +SELECT * FROM apikeys WHERE apikey="%APIKEY%" \ No newline at end of file diff --git a/server/app/common/queries/userdb/get-account.sql b/server/app/common/queries/userdb/get-account.sql new file mode 100644 index 00000000..ce95d269 --- /dev/null +++ b/server/app/common/queries/userdb/get-account.sql @@ -0,0 +1 @@ +SELECT * FROM accounts WHERE accountName="%ACCOUNT_NAME%" \ No newline at end of file diff --git a/server/app/common/queries/userdb/get-accounts-by-id.sql b/server/app/common/queries/userdb/get-accounts-by-id.sql new file mode 100644 index 00000000..6349755b --- /dev/null +++ b/server/app/common/queries/userdb/get-accounts-by-id.sql @@ -0,0 +1 @@ +SELECT * FROM accounts WHERE id="%ID%" \ No newline at end of file diff --git a/server/app/common/queries/userdb/get-accounts.sql b/server/app/common/queries/userdb/get-accounts.sql new file mode 100644 index 00000000..ec309677 --- /dev/null +++ b/server/app/common/queries/userdb/get-accounts.sql @@ -0,0 +1 @@ +SELECT * FROM accounts \ No newline at end of file diff --git a/server/app/common/queries/userdb/get-api-keys.sql b/server/app/common/queries/userdb/get-api-keys.sql index fd95a437..ead8ebca 100644 --- a/server/app/common/queries/userdb/get-api-keys.sql +++ b/server/app/common/queries/userdb/get-api-keys.sql @@ -1 +1 @@ -SELECT * FROM apikeys WHERE sub="%SUB%" \ No newline at end of file +SELECT * FROM apikeys WHERE accountName="%ACCOUNTNAME%" \ No newline at end of file diff --git a/server/app/common/queries/userdb/get-sub.sql b/server/app/common/queries/userdb/get-sub.sql deleted file mode 100644 index 05b8e83d..00000000 --- a/server/app/common/queries/userdb/get-sub.sql +++ /dev/null @@ -1 +0,0 @@ -SELECT sub FROM apikeys WHERE apikey="%APIKEY%" \ No newline at end of file diff --git a/server/app/common/queries/userdb/get-user.sql b/server/app/common/queries/userdb/get-user.sql index b1bfd4f8..002b7b69 100644 --- a/server/app/common/queries/userdb/get-user.sql +++ b/server/app/common/queries/userdb/get-user.sql @@ -1 +1 @@ -SELECT * FROM users WHERE sub="%SUB%" \ No newline at end of file +SELECT * FROM users WHERE id="%ID%" \ No newline at end of file diff --git a/server/app/common/request-rdf.js b/server/app/common/request-rdf.js index 81ce7cf6..d04c08a9 100644 --- a/server/app/common/request-rdf.js +++ b/server/app/common/request-rdf.js @@ -7,67 +7,67 @@ const Constants = require("./constants"); var requestRDF = {}; -requestRDF.parseRdf = async function(contentType, data) { - var buffer = streamify(data); - var quads = []; - return new Promise(function (resolve, reject) { - rdfParser.parse(buffer, { contentType: contentType }) - .on('data', (quad) => { quads.push(quad); }) - .on('error', (error) => reject(error)) - .on('end', () => resolve(quads)); - }); +requestRDF.parseRdf = async function (contentType, data) { + var buffer = streamify(data); + var quads = []; + return new Promise(function (resolve, reject) { + rdfParser.parse(buffer, { contentType: contentType }) + .on('data', (quad) => { quads.push(quad); }) + .on('error', (error) => reject(error)) + .on('end', () => resolve(quads)); + }); } -requestRDF.requestQuads = async function(uri) { - - // Do a POST request with the passed query - var options = { - method: 'GET', - uri: uri, - headers: { - "Accept": Constants.HTTP_ACCEPT_RDF - }, - transform: function (body, response, resolveWithFullResponse) { - return { 'headers': response.headers, 'data': body }; +requestRDF.requestQuads = async function (uri) { + + // Do a POST request with the passed query + var options = { + method: 'GET', + uri: uri, + headers: { + "Accept": Constants.HTTP_ACCEPT_RDF + }, + transform: function (body, response, resolveWithFullResponse) { + return { 'headers': response.headers, 'data': body }; + } + }; + + // Await the response + var response = await rp(options); + var contentType = response.headers['content-type'].split(' ')[0].split(';')[0]; + + if (contentType.startsWith(Constants.HTTP_CONTENT_TYPE_TEXT)) { + contentType = Constants.HTTP_CONTENT_TYPE_TURTLE; } - }; - // Await the response - var response = await rp(options); - var contentType = response.headers['content-type'].split(' ')[0].split(';')[0]; - - if (contentType.startsWith(Constants.HTTP_CONTENT_TYPE_TEXT)) { - contentType = Constants.HTTP_CONTENT_TYPE_TURTLE; - } - - if(contentType.startsWith(Constants.HTTP_CONTENT_TYPE_JSON)) { - contentType = Constants.HTTP_CONTENT_TYPE_JSONLD; - } + if (contentType.startsWith(Constants.HTTP_CONTENT_TYPE_JSON)) { + contentType = Constants.HTTP_CONTENT_TYPE_JSONLD; + } - console.log(`Content type ${contentType} detected. Parsing...`); - var quads = await requestRDF.parseRdf(contentType, response.data); + console.log(`Content type ${contentType} detected. Parsing...`); + var quads = await requestRDF.parseRdf(contentType, response.data); - return quads; + return quads; } -requestRDF.requestN3 = async function(uri) { - var quads = await requestRDF.requestQuads(uri); - var triples = []; +requestRDF.requestN3 = async function (uri) { + var quads = await requestRDF.requestQuads(uri); + var triples = []; - for(var quad of quads) { + for (var quad of quads) { - var subjectValue = `<${quad.subject.value}>`; - var predicateValue = `<${quad.predicate.value}>`; - var objectValue = `<${quad.object.value}>`; + var subjectValue = `<${quad.subject.value}>`; + var predicateValue = `<${quad.predicate.value}>`; + var objectValue = `<${quad.object.value}>`; - if (quad.object.termType == 'Literal') { - objectValue = `${quad.object.id}` - } + if (quad.object.termType == 'Literal') { + objectValue = `${quad.object.id}` + } - triples.push(`${subjectValue} ${predicateValue} ${objectValue} .`); - } + triples.push(`${subjectValue} ${predicateValue} ${objectValue} .`); + } - return triples; + return triples; } diff --git a/server/app/common/res/context.jsonld b/server/app/common/res/context.jsonld index c8c0c7d2..d7915b68 100644 --- a/server/app/common/res/context.jsonld +++ b/server/app/common/res/context.jsonld @@ -25,12 +25,16 @@ "description": { "@id": "dct:description" }, + "name": { + "@id": "databus:name" + }, "Artifact": "databus:Artifact", "artifact": { "@id": "databus:artifact", "@type": "@id" }, "Version": "databus:Version", + "Dataset": "dcat:Dataset", "publisher": { "@id": "dct:publisher", "@type": "@id" @@ -123,7 +127,7 @@ "@id": "foaf:primaryTopic", "@type": "@id" }, - "name": { + "displayName": { "@id": "foaf:name" }, "account": { diff --git a/server/app/common/res/shacl/artifact.shacl b/server/app/common/res/shacl/artifact.shacl index 0ec70ca3..455f12c3 100644 --- a/server/app/common/res/shacl/artifact.shacl +++ b/server/app/common/res/shacl/artifact.shacl @@ -23,9 +23,9 @@ ] ; sh:property [ sh:path [ sh:inversePath rdf:type ] ; - sh:nodekind sh:IRI ; - sh:pattern "/[a-zA-Z0-9\\-_]{4,}/[a-zA-Z0-9\\-_\\.]{3,}/[a-zA-Z0-9\\-_\\.]{3,}$" ; - sh:message "IRI for databus:Artifact must match /[a-zA-Z0-9\\-_]{4,}/[a-zA-Z0-9\\-_\\.]{3,}/[a-zA-Z0-9\\-_\\.]{3,}$"@en ; + sh:nodekind sh:IRI ; + sh:pattern "^[\\w+.-]+:\\/\\/[\\w+.:-]+\\/[\\w+.-]{4,}(?:\\/[\\w+.-]{3,}){2,2}$" ; + sh:message "IRI for databus:Artifact must be a 3-segment URI and match ^[\\w+.-]+:\\/\\/[\\w+.:-]+\\/[\\w+.-]{4,}(?:\\/[\\w+.-]{3,}){2,2}$"@en ; ] . <#title-artifact> diff --git a/server/app/common/res/shacl/group.shacl b/server/app/common/res/shacl/group.shacl index f8ad9496..bb6293e4 100644 --- a/server/app/common/res/shacl/group.shacl +++ b/server/app/common/res/shacl/group.shacl @@ -24,8 +24,8 @@ sh:property [ sh:path [ sh:inversePath rdf:type ] ; sh:nodekind sh:IRI ; - sh:pattern "/[a-zA-Z0-9\\-_]{4,}/[a-zA-Z0-9\\-_\\.]{3,}$" ; - sh:message "IRI for databus:Group must match /[a-zA-Z0-9\\-_]{4,}/[a-zA-Z0-9\\-_\\.]{3,}$"@en ; + sh:pattern "^[\\w+.-]+:\\/\\/[\\w+.:-]+\\/[\\w+.-]{4,}\\/[\\w+.-]{3,}$" ; + sh:message "IRI for databus:Group must be a 2-segment URI and match ^[\\w+.-]+:\\/\\/[\\w+.:-]+\\/[\\w+.-]{4,}\\/[\\w+.-]{3,}$"@en ; ] . <#title-group> diff --git a/server/app/common/res/shacl/version.shacl b/server/app/common/res/shacl/version.shacl index 0360b6b2..7cc09e95 100644 --- a/server/app/common/res/shacl/version.shacl +++ b/server/app/common/res/shacl/version.shacl @@ -11,6 +11,8 @@ @prefix dbo: . @prefix foaf: . @prefix db: . +@prefix prov: . + <#version-exists> a sh:NodeShape ; @@ -24,8 +26,8 @@ sh:property [ sh:path [ sh:inversePath rdf:type ] ; sh:nodekind sh:IRI ; - sh:pattern "/[a-zA-Z0-9\\-_]{4,}/[a-zA-Z0-9\\-_\\.]{1,}/[a-zA-Z0-9\\-_\\.]{1,}/[a-zA-Z0-9\\-_\\.]{1,}$" ; - sh:message "IRI for databus:Version must match /USER/GROUP/ARTIFACT/VERSION , |USER|>3"@en ; + sh:pattern "^[\\w+.-]+:\\/\\/[\\w+.:-]+\\/[\\w+.-]{4,}(?:\\/[\\w+.-]{3,}){3,3}$" ; + sh:message "IRI for databus:Artifact must be a 4-segment URI and match ^[\\w+.-]+:\\/\\/[\\w+.:-]+\\/[\\w+.-]{4,}(?:\\/[\\w+.-]{3,}){3,3}$)"@en ; ] . <#title-version> @@ -103,7 +105,13 @@ sh:maxCount 1 ; sh:nodeKind sh:IRI . - +<#was-derived-from> + a sh:PropertyShape ; + sh:targetClass databus:Version ; + sh:severity sh:Violation ; + sh:message "Value of prov:wasDerivedFrom from must be a valid IRI."@en ; + sh:path prov:wasDerivedFrom ; + sh:nodeKind sh:IRI . diff --git a/server/app/common/shacl-tester.js b/server/app/common/shacl-tester.js index 8c345519..df1b1260 100644 --- a/server/app/common/shacl-tester.js +++ b/server/app/common/shacl-tester.js @@ -11,6 +11,7 @@ const path = require("path"); var jsonld = require('jsonld'); const JsonldUtils = require('../../../public/js/utils/jsonld-utils'); const DatabusUris = require('../../../public/js/utils/databus-uris'); +const DatabusUtils = require('../../../public/js/utils/databus-utils'); var databaseUri = process.env.DATABUS_DATABASE_URL || Constants.DEFAULT_DATABASE_URL; @@ -23,6 +24,7 @@ instance.validateJsonld = async function(rdf, shaclFile) { try { + var options = { formData: { graph: JSON.stringify(rdf), @@ -44,10 +46,10 @@ instance.validateJsonld = async function(rdf, shaclFile) { var conforms = validationReport[DatabusUris.SHACL_CONFORMS][0][DatabusUris.JSONLD_VALUE]; var messages = []; - if(!conforms) { + var conforms = DatabusUtils.resemblesTrue(conforms); + if(!conforms) { var validationResults = JsonldUtils.getTypedGraphs(expandedRes, DatabusUris.SHACL_VALIDATION_RESULT); - for(var result of validationResults) { var message = result[DatabusUris.SHACL_RESULT_MESSAGE][0][DatabusUris.JSONLD_VALUE]; messages.push(message); @@ -58,7 +60,6 @@ instance.validateJsonld = async function(rdf, shaclFile) { } catch (err) { - console.log(err); if (err.response == undefined) { return { isSuccess: false, message: err }; } @@ -67,6 +68,9 @@ instance.validateJsonld = async function(rdf, shaclFile) { } } +instance.validateVersionInputRDF = async function (rdf) { + return await instance.validateJsonld(rdf, './shacl/version-input.shacl'); +} instance.validateGroupRDF = async function (rdf) { return await instance.validateJsonld(rdf, './res/shacl/group.shacl'); diff --git a/server/app/common/shacl/version-input.shacl b/server/app/common/shacl/version-input.shacl new file mode 100644 index 00000000..c6a812ae --- /dev/null +++ b/server/app/common/shacl/version-input.shacl @@ -0,0 +1,34 @@ +@prefix dash: . +@prefix rdf: . +@prefix rdfs: . +@prefix schema: . +@prefix sh: . +@prefix xsd: . +@prefix databus: . +@prefix dct: . +@prefix dcat: . +@prefix dcv: . +@prefix dbo: . +@prefix foaf: . +@prefix db: . +@prefix prov: . + +# TODO: Auto-generate this and provide as resource instead of full SHACL + +<#version-exists> + a sh:NodeShape ; + sh:targetNode databus:Version ; + sh:property [ + sh:path [ sh:inversePath rdf:type ] ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:message "Exactly one subject with an rdf:type of databus:Version must occur."@en ; + ] . + +<#has-distribution> + a sh:PropertyShape ; + sh:targetClass databus:Version ; + sh:severity sh:Violation ; + sh:message "Required property dcat:distribution MUST occur at least once."@en ; + sh:path dcat:distribution; + sh:minCount 1 . diff --git a/server/app/common/utils/api-error.js b/server/app/common/utils/api-error.js new file mode 100644 index 00000000..68063e37 --- /dev/null +++ b/server/app/common/utils/api-error.js @@ -0,0 +1,11 @@ +class ApiError extends Error { + constructor(statusCode, resource, message, body) { + super(message); + this.name = "ApiError"; + this.statusCode = statusCode; + this.resource = resource; + this.body = body; + } +} + +module.exports = ApiError; \ No newline at end of file diff --git a/server/app/common/utils/gstore-helper.js b/server/app/common/utils/gstore-helper.js index 26849273..ef9333ce 100644 --- a/server/app/common/utils/gstore-helper.js +++ b/server/app/common/utils/gstore-helper.js @@ -1,72 +1,78 @@ -var rp = require('request-promise'); -const Constants = require('../constants'); +const axios = require('axios'); const DatabusMessage = require('../databus-message'); +const Constants = require('../constants'); -var prefix = encodeURIComponent(`${process.env.DATABUS_RESOURCE_BASE_URL}/`); +// Constants for URLs and headers +const prefix = encodeURIComponent(`${process.env.DATABUS_RESOURCE_BASE_URL}/`); +// asdf -class GstoreHelper { +// Helper functions for URL building +const buildReadUrl = (repo, path) => `${process.env.DATABUS_DATABASE_URL}/document/read?repo=${repo}&path=${path}`; +const buildSaveUrl = (repo, path) => `${process.env.DATABUS_DATABASE_URL}/document/save?repo=${repo}&prefix=${prefix}&path=${path}`; +const buildDeleteUrl = (repo, path) => `${process.env.DATABUS_DATABASE_URL}/document/delete?repo=${repo}&prefix=${prefix}&path=${path}`; - +class GstoreHelper { static async read(repo, path) { - - let options = { - url: `${process.env.DATABUS_DATABASE_URL}/graph/read?repo=${repo}&path=${path}`, - headers: { - 'Accept': 'application/ld+json' - }, - json: true - }; + const url = buildReadUrl(repo, path); try { - var res = await rp.get(options); - return res; - + const response = await axios.get(url, { + headers: { + 'Accept': Constants.HTTP_CONTENT_TYPE_JSONLD + } + }); + return response.data; } catch (err) { return null; } } static async save(repo, path, content) { + const url = buildSaveUrl(repo, path); - try { - var options = { - uri: `${process.env.DATABUS_DATABASE_URL}/graph/save?repo=${repo}&prefix=${prefix}&path=${path}`, - body: content, - json: true - }; - - await rp.post(options); - + await axios.post(url, content, { + headers: { + 'Content-Type': Constants.HTTP_CONTENT_TYPE_JSON + } + }); return { isSuccess: true }; } catch (err) { console.log(err); - return { isSuccess: false }; + return { isSuccess: false, message: err.message, statusCode: err.response?.status }; } - } static async delete(repo, path) { + const url = buildDeleteUrl(repo, path); + console.log(url); + try { - - var uri = `${process.env.DATABUS_DATABASE_URL}/graph/delete?repo=${repo}&prefix=${prefix}&path=${path}`; - var res = await rp.delete(uri); - - process.send({ - id: DatabusMessage.REQUEST_SEARCH_INDEX_REBUILD - }); - + await axios.delete(url); + + console.log("Resource deleted"); + + // TODO handle drop from index on delete! + /** + if (process.send != undefined) { + process.send({ + id: DatabusMessage.REQUEST_SEARCH_INDEX_REBUILD, + resource: versionGraphUri + }); + } */ + + return { isSuccess: true }; } catch (err) { + console.log(err); - return { isSuccess: false }; + return { error: err, isSuccess: false }; } - } } -module.exports = GstoreHelper; \ No newline at end of file +module.exports = GstoreHelper; diff --git a/server/app/common/utils/jsonld-loader.js b/server/app/common/utils/jsonld-loader.js new file mode 100644 index 00000000..bc69b855 --- /dev/null +++ b/server/app/common/utils/jsonld-loader.js @@ -0,0 +1,52 @@ + +const Constants = require('../constants'); +const axios = require('axios'); +const jsonld = require('jsonld'); +const defaultContext = require('../../common/res/context.jsonld'); + +class JsonldLoader { + + static DEFAULT_CONTEXT_URL; + + static initialize() { + + JsonldLoader.DEFAULT_CONTEXT_URL = `${process.env.DATABUS_RESOURCE_BASE_URL}${Constants.DATABUS_DEFAULT_CONTEXT_PATH}` + + // define a mapping of context URL => context doc + const CONTEXTS = {} + CONTEXTS[JsonldLoader.DEFAULT_CONTEXT_URL] = defaultContext; + + // change the default document loader + const customLoader = async (url, options) => { + + if (url in CONTEXTS) { + return { + contextUrl: null, + document: CONTEXTS[url], + documentUrl: url + }; + } + + try { + // Use axios to fetch the document + const response = await axios.get(url, { + headers: { Accept: [ Constants.HTTP_CONTENT_TYPE_JSONLD, Constants.HTTP_CONTENT_TYPE_JSON ]}, + responseType: 'json', + timeout: 5000, + }); + + return { + contextUrl: null, + document: response.data, + documentUrl: url + }; + } catch (error) { + throw new Error(`Failed to load document from ${url}: ${error.message}`); + } + }; + + jsonld.documentLoader = customLoader; + } +} + +module.exports = JsonldLoader; \ No newline at end of file diff --git a/server/app/common/utils/server-utils.js b/server/app/common/utils/server-utils.js index c8f3d0df..6ae31281 100644 --- a/server/app/common/utils/server-utils.js +++ b/server/app/common/utils/server-utils.js @@ -1,8 +1,44 @@ var ASN1 = require('asn1js'); +const fs = require('fs'); +const DatabusUris = require('../../../../public/js/utils/databus-uris'); +const DatabusConstants = require('../../../../public/js/utils/databus-constants'); class ServerUtils { - + + static setupRequireExtensions() { + // add a sparql file loading extension (simply read the file as a string) + require.extensions['.sparql'] = function (module, filename) { + module.exports = fs.readFileSync(filename, 'utf8'); + }; + + require.extensions['.shacl'] = function (module, filename) { + module.exports = fs.readFileSync(filename, 'utf8'); + }; + + + require.extensions['.jsonld'] = function (module, filename) { + module.exports = JSON.parse(fs.readFileSync(filename, 'utf8')); + }; + + require.extensions['.ejs'] = function (module, filename) { + module.exports = fs.readFileSync(filename, 'utf8'); + }; + + require.extensions['.sql'] = function (module, filename) { + module.exports = fs.readFileSync(filename, 'utf8'); + }; + + require.extensions['.md'] = function (module, filename) { + module.exports = fs.readFileSync(filename, 'utf8'); + }; + require.extensions['.ttl'] = function (module, filename) { + module.exports = fs.readFileSync(filename, 'utf8'); + }; + require.extensions['.html'] = function (module, filename) { + module.exports = fs.readFileSync(filename, 'utf8'); + }; + } static getRSAModulusAndExponent(pubkey) { var unarmor = /-----BEGIN PUBLIC KEY-----([A-Za-z0-9+\/=\s]+)-----END PUBLIC KEY-----/; @@ -98,7 +134,7 @@ class ServerUtils { var result = {}; result.authenticated = false; - if(req.databus != undefined) { + if (req.databus != undefined) { result.authenticated = req.databus.authenticated; //.isAuthenticated(); } @@ -109,7 +145,7 @@ class ServerUtils { // result.info.name = req.oidc.user.name; if (req.databus != undefined) { - result.info.accountName = req.databus.accountName; + result.info.accounts = req.databus.accounts; result.info.oidc_name = req.databus.oidc_name; result.info.oidc_email = req.databus.oidc_email; result.info.apiKeys = req.databus.apiKeys; @@ -147,11 +183,11 @@ class ServerUtils { static NOT_HTML_ACCEPTED(req, res, next) { var acceptHeader = req.get('Accept'); - if(acceptHeader == undefined) { + if (acceptHeader == undefined) { return next(); } - if(acceptHeader.includes('html')) { + if (acceptHeader.includes('html')) { return next("route"); } @@ -161,7 +197,7 @@ class ServerUtils { static HTML_ACCEPTED(req, res, next) { var acceptHeader = req.get('Accept'); - if(acceptHeader == undefined) { + if (acceptHeader == undefined) { return next("route"); } @@ -186,7 +222,137 @@ class ServerUtils { static WGET(req, res, next) { } -} + static async hasWriteAccess(req, accountName) { + + var accounts = req.databus.accounts; + + let accountUri = `${process.env.DATABUS_RESOURCE_BASE_URL}/${accountName}`; + + if (accounts != null && accounts.some(acc => acc.accountName == accountName)) { + return true; + } + + const onBehalfOf = req.headers['x-on-behalf-of']; + + if (onBehalfOf && onBehalfOf == accountUri) { + try { + const response = await axios.get(onBehalfOf, { + headers: { + 'Content-Type': 'application/ld+json', + 'Accept': 'application/ld+json' + } + }); + + const expanded = await jsonld.expand(response.data); + const secretaries = expanded.flatMap(e => + e[DatabusUris.DATABUS_SECRETARY_PROPERTY] || [] + ).map(a => a[DatabusUris.DATABUS_ACCOUNT_PROPERTY][0]); + + for (var secretary of secretaries) { + var accountResource = new DatabusResource(secretary[DatabusUris.JSONLD_ID]); + + if (!accountResource.isAccount()) { + continue; + } + + let secretaryName = accountResource.getAccount(); + + if (this.userData.accounts && this.userData.accounts.some(acc => acc.accountName == secretaryName)) { + return true; + } + } + + } catch (_) { + // fall through to error below + } + } + + return false; + } + + static async createAccountGraphs(uri, name, label, img, secretaries, status) { + + + const signer = require('../../api/lib/databus-tractate-suite'); + const JsonldLoader = require('./jsonld-loader'); + const jsonld = require('jsonld'); + const UriUtils = require('./uri-utils'); + const JsonldUtils = require('../../../../public/js/utils/jsonld-utils'); + + var name = UriUtils.uriToName(uri); + + var rsaKeyGraph = {}; + rsaKeyGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.CERT_RSA_PUBLIC_KEY; + rsaKeyGraph[DatabusUris.RDFS_LABEL] = DatabusConstants.WEBID_SHARED_PUBLIC_KEY_LABEL; + rsaKeyGraph[DatabusUris.CERT_MODULUS] = signer.getModulus(); + rsaKeyGraph[DatabusUris.CERT_EXPONENT] = 65537; + + var personUri = `${uri}${DatabusConstants.WEBID_THIS}`; + + var personGraph = {}; + personGraph[DatabusUris.JSONLD_ID] = personUri; + personGraph[DatabusUris.JSONLD_TYPE] = [DatabusUris.FOAF_PERSON, DatabusUris.DBP_DBPEDIAN]; + personGraph[DatabusUris.FOAF_ACCOUNT] = JsonldUtils.refTo(uri); + personGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY] = uri; + personGraph[DatabusUris.CERT_KEY] = [rsaKeyGraph]; + personGraph[DatabusUris.FOAF_NAME] = label; + + if (img != null) { + personGraph[DatabusUris.FOAF_IMG] = img; + } + + if (status != null) { + personGraph[DatabusUris.FOAF_STATUS] = status; + } + + var profileUri = `${uri}${DatabusConstants.WEBID_DOCUMENT}`; + + var profileDocumentGraph = {}; + profileDocumentGraph[DatabusUris.JSONLD_ID] = profileUri; + profileDocumentGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.FOAF_PERSONAL_PROFILE_DOCUMENT; + profileDocumentGraph[DatabusUris.FOAF_MAKER] = JsonldUtils.refTo(personUri); + profileDocumentGraph[DatabusUris.FOAF_PRIMARY_TOPIC] = JsonldUtils.refTo(personUri); + + var accountGraph = {} + accountGraph[DatabusUris.JSONLD_ID] = uri; + accountGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.DATABUS_ACCOUNT; + accountGraph[DatabusUris.FOAF_ACCOUNT_NAME] = name; + accountGraph[DatabusUris.DATABUS_NAME] = name; + + if (secretaries != null) { + + accountGraph[DatabusUris.DATABUS_SECRETARY_PROPERTY] = []; + + for (var secretary of secretaries) { + + let secretaryAccountUri = `${secretary.accountName}`; + + let secretaryGraph = {}; + secretaryGraph[DatabusUris.JSONLD_TYPE] = DatabusUris.DATABUS_SECRETARY; + secretaryGraph[DatabusUris.DATABUS_ACCOUNT_PROPERTY] = JsonldUtils.refTo(secretaryAccountUri); + + if (secretary.hasWriteAccessTo != undefined) { + secretaryGraph[DatabusUris.DATABUS_HAS_WRITE_ACCESS_TO] = []; + + for (var writeAccess of secretary.hasWriteAccessTo) { + secretaryGraph[DatabusUris.DATABUS_HAS_WRITE_ACCESS_TO].push(JsonldUtils.refTo(writeAccess)); + } + } + + accountGraph[DatabusUris.DATABUS_SECRETARY_PROPERTY].push(secretaryGraph); + } + } + + let expandedGraphs = [ + accountGraph, + personGraph, + profileDocumentGraph + ]; + + return await jsonld.compact(expandedGraphs, JsonldLoader.DEFAULT_CONTEXT_URL); + } +} + module.exports = ServerUtils diff --git a/server/app/common/utils/uri-utils.js b/server/app/common/utils/uri-utils.js index 66fc5700..c6c5e1cd 100644 --- a/server/app/common/utils/uri-utils.js +++ b/server/app/common/utils/uri-utils.js @@ -15,13 +15,28 @@ class UriUtils { var result = baseUrl; - for (var p in path) { - result += "/" + encodeURI(path[p]); + for (var segment of path) { + if(segment == undefined) { + continue; + } + + result += "/" + encodeURI(segment); } return sanitizeUrl(result); } + static fromRequest(req) { + + return UriUtils.createResourceUri([ + req.params.account, + req.params.group, + req.params.artifact, + req.params.version + ]); + + } + static createPath(path) { var result = ""; diff --git a/server/app/indexing/index-group.js b/server/app/indexing/index-group.js new file mode 100644 index 00000000..11e00b3b --- /dev/null +++ b/server/app/indexing/index-group.js @@ -0,0 +1,17 @@ +class IndexGroup { + + constructor(name, indexConfigurationPaths) { + this.indexConfigurationPaths = indexConfigurationPaths; + this.name = name; + } + + getName() { + return this.name; + } + + getIndexConfigurationPaths() { + return this.indexConfigurationPaths; + } +} + +module.exports = IndexGroup; \ No newline at end of file diff --git a/server/app/indexing/index-manager.js b/server/app/indexing/index-manager.js new file mode 100644 index 00000000..a56c9b8b --- /dev/null +++ b/server/app/indexing/index-manager.js @@ -0,0 +1,85 @@ + +const IndexingTask = require('./indexing-task'); + +/** + * Takes care of reindexing resources with index groups. + * An index group is a range of index configuration files that need to be run for a resource + */ +class IndexManager { + + constructor(groups, tickRate) { + + this.tasks = []; + this.groupMap = {}; + this.tickRate = tickRate; + this.isRunningTask = false; + this.groups = groups; + + for(var group of groups) { + this.groupMap[group.getName()] = group; + } + } + + async start() { + await this.buildIndex(); + this.iid = setInterval(this.tick.bind(this), this.tickRate); + } + + async buildIndex() { + + for(var group of this.groups) { + let indexingTask = new IndexingTask(null, group); + await indexingTask.run(); + } + } + + async runNextTask() { + this.isRunningTask = true; + var task = this.tasks.shift(); + await task.run(); + this.isRunningTask = false; + } + + tick() { + + if(this.isRunningTask) { + return; + } + + if(this.tasks.length == 0) { + return; + } + + this.runNextTask(); + } + + updateResource(resourceURI, indexGroupName) { + + if(resourceURI == null) { + return; + } + + if(indexGroupName != null && this.groupMap[indexGroupName] == undefined) { + return; + } + + let indexGroup = null; + + if(indexGroupName != null) { + indexGroup = this.groupMap[indexGroupName]; + } + + let indexingTask = new IndexingTask(resourceURI, indexGroup); + + // Leave, if a task like this already exists in the list + for(var existingTask of this.tasks) { + if(existingTask.equals(indexingTask)) { + return; + } + } + + this.tasks.push(indexingTask); + } +} + +module.exports = IndexManager; \ No newline at end of file diff --git a/server/app/indexing/indexing-task.js b/server/app/indexing/indexing-task.js new file mode 100644 index 00000000..1108f46f --- /dev/null +++ b/server/app/indexing/indexing-task.js @@ -0,0 +1,93 @@ +const axios = require('axios'); +const FormData = require('form-data'); +var fs = require('fs'); + +class IndexingTask { + + static PARAM_CONFIG = "config"; + static PARAM_VALUES = "values"; + static ROUTE_INDEX = "/api/index/run"; + static ROUTE_DELETE = "/api/index/delete"; + static ENCODING_UTF8 = [ 'utf8' ]; + + constructor(resourceURI, indexGroup) { + this.resourceURI = resourceURI; + this.indexGroup = indexGroup; + } + + async run() { + try { + + const deleteFormData = new FormData(); + + if(this.resourceURI != null) { + deleteFormData.append(IndexingTask.PARAM_VALUES, this.resourceURI); + // FIRST: Delete document from index + try { + var response = await axios.post(`${process.env.LOOKUP_BASE_URL}${IndexingTask.ROUTE_DELETE}`, deleteFormData, + { + headers: { + ...deleteFormData.getHeaders(), // Ensure the proper headers for multipart + }, + } + ); + console.log(`Resource <${this.resourceURI}> dropped from index: ${response.status}`); + } catch(err) { + console.log(`Failed to delete resource <${this.resourceURI}> from index: ${err}`); + } + } + + if(this.indexGroup == null) { + return; + } + + let resourceLabel = "all"; + + if(this.resourceURI != null) { + resourceLabel = this.resourceURI; + } + + // SECOND: Sequentially run all indexers of the specified group for the resource + // console.log(`Reindexing <${resourceLabel}> with index group <${this.indexGroup.getName()}>`); + + for(var configPath of this.indexGroup.getIndexConfigurationPaths()) { + + const indexFormData = new FormData(); + + if(this.resourceURI != null) { + indexFormData.append(IndexingTask.PARAM_VALUES, this.resourceURI); + } + + indexFormData.append(IndexingTask.PARAM_CONFIG, fs.createReadStream(configPath)); + + try { + let response = await axios.post(`${process.env.LOOKUP_BASE_URL}${IndexingTask.ROUTE_INDEX}`, indexFormData, + { + headers: { + ...indexFormData.getHeaders(), // Ensure the proper headers for multipart + }, + } + ); + console.log(`Resource <${resourceLabel}> indexed with config ${configPath}: ${response.status}`); + } catch (err) { + console.log(`Indexer ${configPath} failed: ${err}`); + } + } + + } catch (err) { + console.log(`Indexing task failed: ${err}`); + } + } + + equals(task) { + if (this == task) return true; + + if(task.resourceURI == this.resourceURI && task.indexGroup == this.indexGroup) { + return true; + } + + return false; + } +} + +module.exports = IndexingTask; \ No newline at end of file diff --git a/server/app/pages/modules/publish-wizard.js b/server/app/pages/modules/publish-wizard.js deleted file mode 100644 index cb2dac21..00000000 --- a/server/app/pages/modules/publish-wizard.js +++ /dev/null @@ -1,241 +0,0 @@ -var sanitizeUrl = require('@braintree/sanitize-url').sanitizeUrl; -var rp = require('request-promise'); -const cheerio = require('cheerio'); -var sparql = require("../../common/queries/sparql"); -const got = require('got'); - -const ServerUtils = require('../../common/utils/server-utils.js'); - -module.exports = function (router, protector) { - - require('../../common/file-analyzer').route(router, protector); - - - router.get('/app/publish-wizard/licenses', protector.protect(), async function(req, res, next) { - - var search = new URL(`${req.protocol}://${req.get('host')}${req.originalUrl}`).search; - - var options = { - method : 'GET', - headers : { - accept: 'application/json' - }, - }; - - try { - var daliccRes = await got(`https://api.dalicc.net/licenselibrary/list${search}`, options); - var dalicc = JSON.parse(daliccRes.body); - res.status(200).send(dalicc); - - } catch(err) { - console.log(err); - res.status(500).send("DALICC SERVICE APPEARS TO BE DOWN!"); - } - }); - - router.get('/app/publish-wizard', protector.checkSso(), async function (req, res, next) { - - try { - var auth = ServerUtils.getAuthInfoFromRequest(req); - var publishers = await sparql.accounts.getPublishersByAccount(auth.info.accountName); - var texts = require('../publish-wizard-texts.json'); - - res.render('publish-wizard', { - title: 'Publish Data', - data: { auth: auth, publisherData: publishers, texts: texts } - }); - } catch (err) { - console.log(err); - res.status(500).send(err); - } - }); - - - router.get('/app/publish-wizard/fetch-resource-page', async function (req, res, next) { - - try { - var result = await fetchLinksRecursive(req.query.url, '', 0); - res.status(200).send(result); - - } catch (err) { - console.log(err); - res.status(500).send(err); - } - }); - - router.get('/app/publish-wizard/fetch-file', async function (req, res, next) { - - try { - - var url = req.query.url; - var result = await fetchFile(url); - res.status(200).send(result); - - } catch (err) { - console.log(err); - - res.status(500).send(err); - } - }); - - async function fetchFile(url) { - - if (url.includes('://localhost')) { - return null; - } - - if (url.startsWith('https://databus.dbpedia.org')) { - return null; - } - - // Check if definitely no file - if (url.startsWith('http') - && url.lastIndexOf('/') < url.lastIndexOf('.') - && url.lastIndexOf('/') < url.indexOf('.')) { - return null; - } - - if (url.startsWith("https://github.com/")) { - url = transformGithubUrl(url); - } - - - // File should be reachable, do a head request! - var options = {}; - options.uri = url; - options.method = 'HEAD'; - - var header = await rp(options); - - var result = { - url: url - }; - - // Prefer content disposition - if (header['content-disposition'] != undefined) { - - console.log(header['content-disposition']); - - var filename = getFileNameFromDisposition(header['content-disposition']); - - console.log(filename); - parseFormatAndCompression(result, filename); - - return result; - } - - // Try to parse from url - if (url.lastIndexOf('/') < url.lastIndexOf('.')) { - parseFormatAndCompression(result, url); - - return result; - } - - return null; - } - - function getFileNameFromDisposition(disposition) { - - console.log(disposition); - var entries = disposition.split(' '); - - for(var entry of entries) { - if(entry.startsWith('filename=')) { - return entry.split('=')[1].replace(/(^")|("$)|(;$)|(";$)/g, ""); - } - } - - return undefined; - } - - - function transformGithubUrl(url) { - - url = url.replace("/blob/", "/"); - url = url.replace("https://github.com/", "https://raw.githubusercontent.com/") - - return url; - } - - function parseFormatAndCompression(result, value) { - - if(value == undefined) { - return; - } - - console.log(value); - - var nameComponents = value.split('/'); - - nameComponents = nameComponents[nameComponents.length - 1].split('.'); - - if (nameComponents[nameComponents.length - 1].includes('#')) { - nameComponents[nameComponents.length - 1] = nameComponents[nameComponents.length - 1].split('#')[0] - } - - if (nameComponents.length > 2) { - result.compression = nameComponents[nameComponents.length - 1]; - result.formatExtension = nameComponents[nameComponents.length - 2]; - } else if (nameComponents.length > 1) { - result.compression = 'none'; - result.formatExtension = nameComponents[nameComponents.length - 1]; - } else { - result.compression = 'none'; - result.formatExtension = 'none'; - } - - - - } - - async function fetchLinksRecursive(baseUrl, path, depth) { - - var result = []; - - try { - - // Check if the url is a file: - var url = baseUrl + path; - - // Get the HTML and pick up HREFs - var options = {} - options.uri = url; - options.method = 'GET'; - options.headers = { - Accept: 'text/html' - }; - options.transform = function (body) { return cheerio.load(body); }; - var $ = await rp(options); - - var hrefs = getFilteredHrefs(baseUrl + path, $); - - for (var h in hrefs) { - - var href = hrefs[h]; - result.push(href); - } - - } catch (err) { - console.log(err); - } - - return result; - } - - function getFilteredHrefs(baseUrl, $) { - - var result = []; - links = $('a'); - - - $(links).each(function (i, link) { - - var link = $(link).attr('href'); - var href = new URL(link, baseUrl).href; - - result.push(href); - }); - - return result; - } -} diff --git a/server/app/test.runner.js b/server/app/test.runner.js new file mode 100644 index 00000000..a2693f43 --- /dev/null +++ b/server/app/test.runner.js @@ -0,0 +1,69 @@ +const { setTimeout } = require('timers/promises'); +const { spawn } = require('child_process'); +const http = require('http'); + +let server; + +function waitForServerReady(port = 3000, timeoutMs = 5000) { + return new Promise((resolve, reject) => { + const deadline = Date.now() + timeoutMs; + + (function poll() { + const req = http.get(`http://localhost:${port}`, () => resolve()); + req.on('error', () => { + if (Date.now() < deadline) { + setTimeout(200).then(poll); + } else { + reject(new Error('Server not responding in time')); + } + }); + })(); + }); +} + +process.on('unhandledRejection', (reason) => { + console.error('Unhandled Rejection at:', reason); + process.exit(1); +}); + +process.on('uncaughtException', (error) => { + console.error('Uncaught Exception thrown:', error); + process.exit(1); +}); + +async function runUvu() { + return new Promise((resolve, reject) => { + const uvuProcess = spawn('npx', ['uvu', './app/tests/'], { + stdio: 'inherit', + shell: true, + }); + + uvuProcess.on('error', (err) => reject(err)); + uvuProcess.on('close', (code) => resolve(code)); + }); +} + +/** + * TEST ENTRY POINT + * Spawns Databus Server and then runs the test suite + */ +async function run() { + server = spawn('node', ['--trace-warnings', 'www'], { + cwd: '../server', + stdio: ['ignore', 'pipe', 'pipe'] + }); + + try { + await waitForServerReady(3000, 5000); + + const code = await runUvu(); + + } catch (err) { + console.error(err); + process.exit(1); + } finally { + if (server) server.kill(); + } +} + +run(); diff --git a/server/app/test/main.js b/server/app/test/main.js deleted file mode 100644 index fd4780a6..00000000 --- a/server/app/test/main.js +++ /dev/null @@ -1,486 +0,0 @@ -const userDatabaseTests = require('./methods/user-database-tests'); -const utilTests = require('./methods/util-tests'); -const webdavTests = require('./methods/webdav-tests'); -const cacheTests = require('./methods/cache-tests'); -const { accountTests, createTestAccount, deleteTestAccount } = require('./methods/api/account-tests'); -const tractateTests = require('./methods/api/tractate-tests'); -const { createTestUser, deleteTestUser } = require('./test-utils'); -const generalTests = require('./methods/api/general-tests'); -const { groupTests } = require('./methods/api/group-tests'); -const { artifactTests } = require('./methods/api/artifact-tests'); -const { versionTests } = require('./methods/api/version-tests'); - -const testUserParams = require('./test-user.json'); -const nerdUserParams = require('./test-user-nerd.json'); -const databus = require('../../www'); -const resourceTests = require('./methods/resource-tests'); - -module.exports = async function () { - - try { - - // Base - console.log("===== Base Tests =====") - await userDatabaseTests(); - await utilTests(); - await webdavTests(); - await cacheTests(); - await resourceTests(); - - - - // Account - console.log("===== Account Tests =====") - await accountTests(); - - // API - console.log("===== API Tests =====") - // Create user and account for API tests - await createTestUser(testUserParams); - await createTestAccount(testUserParams); - await createTestUser(nerdUserParams); - await createTestAccount(nerdUserParams); - - console.log("===== Tractate Tests =====") - await generalTests(); - await tractateTests(); - await groupTests(); - await artifactTests(); - await versionTests(); - - - await deleteTestAccount(testUserParams); - await deleteTestUser(testUserParams); - await deleteTestAccount(nerdUserParams); - await deleteTestUser(nerdUserParams); - - console.log(`================================================`); - console.log('Tests completed successfully.'); - console.log('Databus ready to start.'); - console.log('Use the command "make srv-start-auth0".'); - console.log(`================================================`); - - process.exit(0) - - } catch (err) { - console.log(err); - console.log(`================================================`); - console.log('Tests completed with errors.'); - console.log(`================================================`); - - process.exit(1) - - } -} - -/** - * Tests for API calls - -async function apiTests() { - - console.log(`Testing API calls.`); - - var user = await createTestUser(); - - await manifestTest(); - - await accountTests(user); - - await deleteTestUser(); -} - -/* - -async function accountTests(user) { - - const options = { - "headers": { "x-api-key": params.APIKEY }, - "resolveWithFullResponse": true - }; - - // ========= PUT Account =========== - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/${user.accountName}`; - options.method = "PUT"; - options.json = true; - - const template = JSON.stringify(require('../../../public/templates/json/account.json')); - - options.body = JSON.parse(ServerUtils.formatTemplate(template, { - DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, - ACCOUNT_NAME: user.accountName - })); - - - response = await rp(options); - assert(response.statusCode == 201, 'Account could not be updated.'); - - // ======= INVALID PUT Account ======= - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/janfo`; - - try { - response = await rp(options); - assert(response.statusCode != 200, 'Able to write to janfo account. This should be forbidden'); - } catch (err) { - assert(err.response.statusCode == 403, 'Trying to write to unowned account. 403 expected.'); - } - - // ======= GET Account ========== - delete options.body; - delete options.json; - - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/${user.accountName}`; - options.method = "GET"; - options.headers['Accept'] = "application/ld+json" - - response = await rp(options); - assert(response.statusCode == 200, 'Expected 200 when retrieving account.'); - - - // ========= EXECUTE OTHER TESTS ========== - await apiKeyTests() - await webidTests() - await tractateTests(user) - await publishTest(user) - await groupTests(user) - await artifactTests(user) - await dataidTests(user) - await collectionTests(user) - // ========= EXECUTE OTHER TESTS ========== - - - // ========= DELETE Account ========== - options.method = "DELETE"; - - response = await rp(options); - assert(response.statusCode == 200, 'Expected 200 when deleting existing account.'); - - response = await rp(options); - assert(response.statusCode == 204, 'Expected 204 when deleting deleted account.'); - - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/janfo`; - - try { - await rp(options); - assert(err.response.statusCode != 200, 'Deleting unowned account should not be possible'); - } catch (err) { - assert(err.response.statusCode == 403, 'Expected 403 when trying to delete unowned account.'); - } -} - -async function apiKeyTests() { - - const options = { - "headers": { "x-api-key": params.APIKEY }, - "resolveWithFullResponse": true - }; - - // ========= Create API Key =========== - let keyName = "testkey2" - - options.method = "POST"; - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}` + `/api/account/api-key/create?name=${keyName}`; - - console.log(options) - - response = await rp(options); - - assert(response.statusCode == 200, 'API key could not be created.'); - - // ========= Delete API Key =========== - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}` + `/api/account/api-key/delete?name=${keyName}`; - response = await rp(options); - assert(response.statusCode == 200, 'API key could not be deleted.'); -} - - -async function publishTest(user) { - console.log("PUBLISH TESTS") - const options = { - "headers": { "x-api-key": params.APIKEY }, - "resolveWithFullResponse": true - }; - - // ========= Publish =========== - let group = "cleaned" - let artifact = "geonames" - let version = "2022-02-09" - var template = JSON.stringify(require('../../../public/templates/json/dataid.json')); - - options.method = "POST"; - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}` + '/api/publish'; - options.json = true; - options.resolveWithFullResponse = true; - options.body = JSON.parse(ServerUtils.formatTemplate(template, { - DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, - ACCOUNT: user.accountName, - GROUP: group, - ARTIFACT: artifact, - VERSION: version - })); - - response = await rp(options); - assert(response.statusCode == 200, 'Metadata could not be published.'); -} - -async function tractateTests(user) { - const options = { - "headers": { "x-api-key": params.APIKEY }, - "resolveWithFullResponse": true - }; - - // ========= Generate Databus Tractate v1 =========== - var template = JSON.stringify(require('../../../public/templates/json/dataid.json')); - - var testMetadata = JSON.parse(ServerUtils.formatTemplate(template, { - DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, - ACCOUNT: user.accountName, - GROUP: "testgroup", - ARTIFACT: "testartifact", - VERSION: "1000" - })); - - options.method = "POST"; - options.headers = { 'Accept': 'text/plain' } - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/api/tractate/v1/canonicalize`; - options.json = true; - options.body = testMetadata; - - response = await rp(options); - assert(response.statusCode == 200, 'Could not generate Databus Tractate v1.'); - - // ========= Validate Databus Tractate v1 =========== - - // Expand graph, autocomplete, create proof - testMetadata = await jsonld.flatten(await jsonld.expand(testMetadata)); - autocomplete(testMetadata); - - options.body[DatabusUris.JSONLD_GRAPH][DatabusUris.SEC_PROOF] = suite.createProof(testMetadata); - - delete options.headers['Accept']; - - options.headers = { 'Accept': 'application/json' } - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/api/tractate/v1/verify`; - - response = await rp(options); - - assert(response.statusCode == 200 && response.body.success == true, response.body.message); - -} - -async function groupTests(user) { - console.log("GROUPTESTS") - - const options = { - "headers": { "x-api-key": params.APIKEY }, - "resolveWithFullResponse": true - }; - - const group = "testgroup" - - // ========= Create Group =========== - let template = JSON.stringify(require('../../../public/templates/json/group.json')); - - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}` + `/${user.accountName}/${group}`; - options.method = "PUT"; - options.json = true; - - options.body = JSON.parse(ServerUtils.formatTemplate(template, { - DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, - ACCOUNT: user.accountName, - GROUP: group - })); - - response = await rp(options); - assert(response.statusCode == 200, 'Group could not be created.'); - - // ========= Get Group =========== - delete options.headers; - delete options.body; - - options.method = "GET"; - options.headers = { 'Accept': 'application/ld+json' } - - response = await rp(options); - assert(response.statusCode == 200, 'Could not get group.'); - - // ========= Delete Group =========== - delete options.headers; - options.method = "DELETE"; - options.headers = { "x-api-key": params.APIKEY }; - - response = await rp(options); - assert(response.statusCode == 200, 'Could not delete group.'); - - // ========= Get Group =========== - delete options.headers; - delete options.body; - options.headers = { 'Accept': 'application/ld+json' } - options.method = "GET"; - - response = await rp(options); - assert(response.statusCode == 404, 'Group has not been deleted.'); -} - -async function artifactTests(user) { - console.log("ARTIFACTTESTS") - - const options = { - "headers": { "x-api-key": params.APIKEY }, - "resolveWithFullResponse": true - }; - - const group = "testgroup" - const artifact = "testartifact" - - // ========= Create Artifact =========== - let template = JSON.stringify(require('../../../public/templates/json/artifact.json')); - - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}` + `/${user.accountName}/${group}/${artifact}`; - options.method = "PUT"; - options.json = true; - - options.body = JSON.parse(ServerUtils.formatTemplate(template, { - DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, - ACCOUNT: user.accountName, - GROUP: group, - ARTIFACT: artifact - })); - - response = await rp(options); - assert(response.statusCode == 200, 'Artifact could not be created.'); - - // ========= Get Artifact =========== - delete options.headers; - delete options.body; - - options.method = "GET"; - options.headers = { 'Accept': 'application/ld+json' } - - response = await rp(options); - assert(response.statusCode == 200, 'Could not get artifact.'); - - // ========= Delete Artifact =========== - delete options.headers; - - options.method = "DELETE"; - - response = await rp(options); - assert(response.statusCode == 200, 'Could not delete artifact.'); -} - -async function dataidTests(user) { - console.log("DATAIdTESTS") - - const options = { - "headers": { "x-api-key": params.APIKEY }, - "resolveWithFullResponse": true - }; - - const group = "testgroup" - const artifact = "testartifact" - const version = "2022-02-09" - - // ========= Create DataId =========== - let template = JSON.stringify(require('../../../public/templates/json/artifact.json')); - - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}` + `/${user.accountName}/${group}/${artifact}/${version}`; - options.method = "PUT"; - options.json = true; - - options.body = JSON.parse(ServerUtils.formatTemplate(template, { - DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, - ACCOUNT: user.accountName, - GROUP: group, - ARTIFACT: artifact, - VERSION: version - })); - - response = await rp(options); - assert(response.statusCode == 200, 'Artifact could not be created.'); - - // ========= Get DataId =========== - delete options.headers; - delete options.body; - - options.method = "GET"; - options.headers = { 'Accept': 'application/ld+json' } - - response = await rp(options); - assert(response.statusCode == 200, 'Could not get artifact.'); - - // ========= Get File =========== - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}` + `/${user.accountName}/${group}/${artifact}/${version}/geonames.ttl`; - delete options.headers; - - options.method = "GET"; - - response = await rp(options); - assert(response.statusCode == 200, 'Could not get File.'); - - // ========= Delete DataId =========== - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}` + `/${user.accountName}/${group}/${artifact}/${version}`; - delete options.headers; - - options.method = "DELETE"; - - response = await rp(options); - assert(response.statusCode == 200, 'Could not delete artifact.'); -} - -async function collectionTests(user) { - console.log("CollectionTESTS") - - const options = { - "headers": { "x-api-key": params.APIKEY }, - "resolveWithFullResponse": true - }; - - const collection = "testCollection" - - // ========= Create Collection =========== - let template = JSON.stringify(require('../../../public/templates/json/collection.json')); - - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}` + `/${user.accountName}/collections/${collection}`; - options.method = "PUT"; - options.json = true; - - options.body = JSON.parse(ServerUtils.formatTemplate(template, { - DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, - ACCOUNT: user.accountName, - COLLECTION: collection - })); - - response = await rp(options); - assert(response.statusCode == 200, 'Collection could not be created.'); - - // ========= Get Collection =========== - delete options.headers; - delete options.body; - - options.method = "GET"; - options.headers = { 'Accept': 'application/ld+json' } - - response = await rp(options); - assert(response.statusCode == 200, 'Could not get Collection.'); - - // ========= Get MD5Hash =========== - let collectionURI = `${process.env.DATABUS_RESOURCE_BASE_URL}/${user.accountName}/collections/${collection}` - - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}` + `/api/collection/md5hash?uri=${encodeURIComponent(collectionURI)}`; - delete options.headers; - - options.method = "GET"; - - response = await rp(options); - assert(response.statusCode == 200, 'Could not get MD5 Hash of Collection.'); - - // ========= Delete Collection =========== - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}` + `/${user.accountName}/collections/${collection}`; - - options.method = "DELETE"; - - response = await rp(options); - assert(response.statusCode == 200, 'Could not delete Collection.'); -} -*/ - diff --git a/server/app/test/methods/api/account-tests.js b/server/app/test/methods/api/account-tests.js deleted file mode 100644 index 016c4c28..00000000 --- a/server/app/test/methods/api/account-tests.js +++ /dev/null @@ -1,219 +0,0 @@ -const params = require('../../test-user.json'); -const nerdParams = require('../../test-user-nerd.json'); -const rp = require('request-promise'); -const assert = require('assert'); -const ServerUtils = require('../../../common/utils/server-utils'); -const { createTestUser, deleteTestUser } = require('../../test-utils'); - -var accountTests = {}; - -/** - * Tests for Databus accounts - * @param {*} user - */ -accountTests.accountTests = async function() { - - await createTestUser(params); - await createTestUser(nerdParams); - - const options = {}; - options.headers = { "x-api-key": params.APIKEY }; - options.resolveWithFullResponse = true; - - var webid = ServerUtils.formatJsonTemplate(require('../../templates/account.json'), { - DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, - ACCOUNT_NAME: params.ACCOUNT_NAME - }); - - // delete account from preexisting tests - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/${params.ACCOUNT_NAME}`; - options.method = "DELETE"; - await rp(options); - - - options.headers = { "x-api-key": nerdParams.APIKEY }; - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/${nerdParams.ACCOUNT_NAME}`; - options.method = "DELETE"; - await rp(options); - - - options.headers = { "x-api-key": params.APIKEY }; - - // ======= GET non-existing Account ========== - - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/${params.ACCOUNT_NAME}`; - options.method = "GET"; - options.headers['Accept'] = "application/ld+json" - - // should return 404 because account doesn't exist yet - try { - await rp(options); - // assert(false, 'Account does not exist, request should return 404.'); - } catch (err) { - assert(err.response.statusCode == 404, 'Expected 404 when retrieving account.'); - } - - // ========= PUT Account - valid =========== - - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/${params.ACCOUNT_NAME}`; - options.method = "PUT"; - options.json = true; - options.body = webid; - - var response = await rp(options); - assert(response.statusCode == 201, 'Account could not be created. 201 expected.'); - - // ======= PUT Account - invalid ======= - - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/janfo`; - - try { - response = await rp(options); - assert(response.statusCode != 200, 'Able to write to janfo account. This should be forbidden'); - } catch (err) { - assert(err.response.statusCode == 403, 'Trying to write to unowned account. 403 expected.'); - } - - // ======= GET existing Account ========== - - delete options.body; - delete options.json; - - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/${params.ACCOUNT_NAME}`; - options.method = "GET"; - options.headers['Accept'] = "application/ld+json" - - response = await rp(options); - assert(response.statusCode == 200, 'Expected 200 when retrieving account.'); - - // ========= EXECUTE OTHER TESTS ========== - - await apiKeyTests(); - await webidTests(); - - // ========= DELETE Account ========== - - options.method = "DELETE"; - - response = await rp(options); - assert(response.statusCode == 200, 'Expected 200 when deleting existing account.'); - - response = await rp(options); - assert(response.statusCode == 204, 'Expected 204 when deleting deleted account.'); - - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/janfo`; - - try { - await rp(options); - assert(err.response.statusCode != 200, 'Deleting unowned account should not be possible'); - } catch (err) { - assert(err.response.statusCode == 403, 'Expected 403 when trying to delete unowned account.'); - } - - await deleteTestUser(params); -} - - -/** - * Tests for removing and adding extra API keys - */ -async function apiKeyTests() { - - const options = {}; - options.headers = { "x-api-key": params.APIKEY }; - options.resolveWithFullResponse = true; - - let keyName = "testkey2" - - // ========= Create API Key =========== - - options.method = "POST"; - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/api-key/create?name=${keyName}`; - - response = await rp(options); - assert(response.statusCode == 200, 'API key could not be created.'); - - // ======== Create already existing API Key ========= - try{ - response = await rp(options); - assert(false, 'creating already existing API key shouldnt be possible.') - } catch(err) { - assert(err.response.statusCode == 400, 'API key could be created. Shouldnt have been possible, because it already exists.'); - } - - // ========= Delete API Key =========== - - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/api-key/delete?name=${keyName}`; - - response = await rp(options); - assert(response.statusCode == 200, 'API key could not be deleted.'); - - // ========= Delete Non-Existing API Key =========== - - response = await rp(options); - assert(response.statusCode == 204, 'API key does not exist. Expected return code 204.'); -} - -/** - * Tests for connecting a WebId to an account - */ -async function webidTests() { - - const options = {}; - options.headers = { "x-api-key": params.APIKEY }; - options.resolveWithFullResponse = true; - options.method = "POST"; - - // ========= POST WebId =========== - - let webid = encodeURIComponent('https://holycrab13.github.io/webid.ttl') - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/webid/add?uri=${webid}`; - - try { - response = await rp(options); - assert(response.statusCode != 200, 'Unable to add webid to account'); - } catch (err) { - assert(err.response.statusCode == 403, '403 expected since there is no backlink.'); - } - - // ========= Remove WebId =========== - - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/webid/remove?uri=${webid}`; - - response = await rp(options); - assert(response.statusCode == 200, 'WebId could not be removed.'); -} - -accountTests.createTestAccount = async function(params) { - - var webid = ServerUtils.formatJsonTemplate(require('../../templates/account.json'), { - DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, - ACCOUNT_NAME: params.ACCOUNT_NAME - }); - - const options = {}; - options.headers = { "x-api-key": params.APIKEY }; - options.resolveWithFullResponse = true; - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/${params.ACCOUNT_NAME}`; - options.method = "PUT"; - options.json = true; - options.body = webid; - - var response = await rp(options); - assert(response.statusCode == 201, 'Account could not be created.'); -} - -accountTests.deleteTestAccount = async function(params) { - - const options = {}; - options.headers = { "x-api-key": params.APIKEY }; - options.resolveWithFullResponse = true; - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/${params.ACCOUNT_NAME}`; - options.method = "DELETE"; - - var response = await rp(options); - assert(response.statusCode == 200 || response.statusCode == 204, - 'Expected 200 or 204 when deleting account.'); -} - -module.exports = accountTests; \ No newline at end of file diff --git a/server/app/test/methods/api/artifact-tests.js b/server/app/test/methods/api/artifact-tests.js deleted file mode 100644 index e0dc68f9..00000000 --- a/server/app/test/methods/api/artifact-tests.js +++ /dev/null @@ -1,99 +0,0 @@ -const params = require('../../test-user.json'); -const rp = require('request-promise'); -const assert = require('assert'); -const ServerUtils = require('../../../common/utils/server-utils'); -const groupTests = require('./group-tests'); -const UriUtils = require('../../../common/utils/uri-utils'); - -var artifactTests = {}; - -artifactTests.artifactTests = async function() { - - // ========= Get Artifact =========== - await artifactTests.getTestArtifact(404); - - - // ========= Create Artifact =========== - - const options = {}; - - options.method = "PUT"; - options.json = true; - options.resolveWithFullResponse = true; - options.headers = { "x-api-key": params.APIKEY }; - options.uri = UriUtils.createResourceUri([ - params.ACCOUNT_NAME, - params.GROUP_NAME, - params.ARTIFACT_NAME - ]); - options.body = ServerUtils.formatJsonTemplate(require('../../templates/artifact.json'), { - DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, - ACCOUNT: params.ACCOUNT_NAME, - GROUP: params.GROUP_NAME, - ARTIFACT: params.ARTIFACT_NAME - }); - - response = await rp(options); - assert(response.statusCode == 200, 'Artifact could not be created.'); - - // ========= Get Artifact =========== - await artifactTests.getTestArtifact(200); - - // ========= Get Group =========== - await groupTests.getTestGroup(200); - - // ========= Delete Group ========= - options.method = "DELETE"; - options.headers = { "x-api-key": params.APIKEY }; - options.uri = UriUtils.createResourceUri([ - params.ACCOUNT_NAME, - params.GROUP_NAME - ]); - - var statusCode = 0; - try { await rp(options); } catch(err) { statusCode = err.statusCode; } - assert(statusCode == 409, 'Deleting non-empty group should cause a conflict.'); - - // ========= Delete Artifact =========== - delete options.body; - options.uri = UriUtils.createResourceUri([ - params.ACCOUNT_NAME, - params.GROUP_NAME, - params.ARTIFACT_NAME - ]); - options.method = "DELETE"; - - response = await rp(options); - assert(response.statusCode == 204, 'Could not delete artifact.'); - - // ========= Get Artifact ========= - await artifactTests.getTestArtifact(404); -} - -artifactTests.getTestArtifact = async function (expectedCode) { - const options = {}; - options.resolveWithFullResponse = true; - options.method = "GET"; - options.headers = { 'Accept': 'application/ld+json' } - options.uri = UriUtils.createResourceUri([ - params.ACCOUNT_NAME, - params.GROUP_NAME, - params.ARTIFACT_NAME - ]); - - var artifact = null; - var statusCode = 0; - - try { - response = await rp(options); - statusCode = response.statusCode; - artifact = response.body - } catch (err) { - statusCode = err.response.statusCode; - } - - assert(statusCode == expectedCode, `Unexpected statusCode ${statusCode} when retrieving test artifact (${expectedCode} expected).`); - return artifact; -} - -module.exports = artifactTests; \ No newline at end of file diff --git a/server/app/test/methods/api/general-tests.js b/server/app/test/methods/api/general-tests.js deleted file mode 100644 index 8d75eac3..00000000 --- a/server/app/test/methods/api/general-tests.js +++ /dev/null @@ -1,147 +0,0 @@ -const params = require('../../test-user.json'); -const params_nerd = require('../../test-user-nerd.json'); -const rp = require('request-promise'); -const assert = require('assert'); -const ServerUtils = require('../../../common/utils/server-utils'); -const { version } = require('../../../../config.json'); - -module.exports = async function generalTests() { - - await manifestTest(); - await publishTest(); -} - -async function manifestTest() { - - // ========= GET Manifest ========== - const options = {}; - options.headers = { "x-api-key": params.APIKEY }; - - options.method = "GET"; - options.uri = process.env.DATABUS_RESOURCE_BASE_URL; - options.headers['Accept'] = "text/turtle" - - var response = await rp(options); - - var manifest = ServerUtils.formatJsonTemplate(require('../../../../manifest-template.ttl'), { - DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, - DATABUS_VERSION: version - }); - - assert(response == manifest, "Manifest is not well-formed."); -} - -async function publishTest() { - - const options = {}; - - // // ======== Publish Dataid ======= - // options.method = "POST"; - // options.headers = { "x-api-key": params.APIKEY }; - // options.resolveWithFullResponse = true; - // options.json = true; - // options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/api/publish`; - // options.body = ServerUtils.formatJsonTemplate(require('../../templates/version.json'), { - // DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, - // ACCOUNT: params.ACCOUNT_NAME, - // GROUP: params.GROUP_NAME, - // ARTIFACT: params.ARTIFACT_NAME, - // VERSION: params.VERSION_NAME - // }); - - // response = await rp(options); - // assert(response.statusCode == 200, 'Metadata could not be published.'); - - - // // ========= Delete Version =========== - // delete options.headers; - // options.method = "DELETE"; - // options.headers = { "x-api-key": params.APIKEY }; - // options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/${params.ACCOUNT_NAME}/${params.GROUP_NAME}/${params.ARTIFACT_NAME}/${params.VERSION_NAME}`; - - // response = await rp(options); - // assert(response.statusCode == 204, `Could not delete version ${options.uri}.`); - - // ======== Publish Dataid ======= - options.method = "POST"; - options.headers = { "x-api-key": params_nerd.APIKEY }; - options.resolveWithFullResponse = true; - options.json = true; - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/api/publish`; - options.body = ServerUtils.formatJsonTemplate(require('../../templates/version.json'), { - DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, - ACCOUNT: params_nerd.ACCOUNT_NAME, - GROUP: params_nerd.GROUP_NAME, - ARTIFACT: params_nerd.ARTIFACT_NAME, - VERSION: params_nerd.VERSION_NAME - }); - - let response = await rp(options); - assert(response.statusCode == 200, 'Nerdy metadata could not be published.'); - - // ======== Publish invalid Dataid ======= - delete options.body; - try { - response = await rp(options); - assert(false, 'should not be possible to publish non existent metadata') - } catch (err) { - assert(err.response.statusCode == 400, 'empty metadata shoud not be publishibly.'); - } - - // search doesnt work - // Error: connect ECONNREFUSED 127.0.0.1:8082 - // at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1247:16) { - // errno: -111, - // code: 'ECONNREFUSED', - // syscall: 'connect', - // address: '127.0.0.1', - // port: 8082 - // } - // // ========= Search Tests =========== - // // ========= Search existing Data =========== - // options.method = "GET"; - // options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/api/search?query=${params_nerd.ARTIFACT_NAME}&typeName=Artifact&partRequired=true`; - - // response = await rp(options); - // assert(response.statusCode == 200, "couldnt find test artifact") - - // // ========= Search non existing Data ========= - - - // ========= Delete published Data =========== - delete options.headers; - options.method = "DELETE"; - options.headers = { "x-api-key": params_nerd.APIKEY }; - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/${params_nerd.ACCOUNT_NAME}/${params_nerd.GROUP_NAME}/${params_nerd.ARTIFACT_NAME}/${params_nerd.VERSION_NAME}`; - - response = await rp(options); - assert(response.statusCode == 204, `Could not delete version ${options.uri}.`); - - // ======== send sparql request POST =========== - options.method = "POST"; - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/sparql`; - options.body = { query: "select distinct * where {?a ?b ?c} LIMIT 2" } - - response = await rp(options); - assert(response.statusCode == 200, 'fail') - - // ======== send sparql request GET =========== - options.method = "GET"; - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/sparql?query=select%20distinct%20%2A%20where%20%7B%3Fa%20%3Fb%20%3Fc%7D%20LIMIT%202`; - - response = await rp(options); - - assert(response.statusCode == 200, 'fail') - - // ========= send invalid sparql request ========= - options.method = "POST"; - options.body = { query: "asd" } - - try { - await rp(options); - assert(false, 'invalid sparql query shouldnt work.') - } catch (err) { - assert(err.response.statusCode == 400, "invalid sparql query shouldnt work.") - } - -} diff --git a/server/app/test/methods/api/group-tests.js b/server/app/test/methods/api/group-tests.js deleted file mode 100644 index 202aee84..00000000 --- a/server/app/test/methods/api/group-tests.js +++ /dev/null @@ -1,87 +0,0 @@ -const params = require('../../test-user.json'); -const rp = require('request-promise'); -const assert = require('assert'); -const ServerUtils = require('../../../common/utils/server-utils'); -const UriUtils = require('../../../common/utils/uri-utils'); - -var groupTests = {}; - -groupTests.groupTests = async function() { - - // ========= Get Group =========== - await groupTests.getTestGroup(404); - - // ========= Create Group =========== - - const options = {}; - options.resolveWithFullResponse = true; - options.method = "PUT"; - options.headers = { "x-api-key": params.APIKEY }; - options.json = true; - options.uri = UriUtils.createResourceUri([ - params.ACCOUNT_NAME, - params.GROUP_NAME - ]); - options.body = ServerUtils.formatJsonTemplate(require('../../templates/group.json'), { - DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, - ACCOUNT: params.ACCOUNT_NAME, - GROUP: params.GROUP_NAME - }); - - response = await rp(options); - assert(response.statusCode == 200, 'Group could not be created.'); - - // ========= Get Group =========== - await groupTests.getTestGroup(200); - - // ========= Delete Group =========== - delete options.headers; - options.method = "DELETE"; - options.headers = { "x-api-key": params.APIKEY }; - - response = await rp(options); - assert(response.statusCode == 204, 'Could not delete group.'); - - // ========= Get Group =========== - await groupTests.getTestGroup(404); - - // ========= invalid create Group ======= - options.method = "PUT"; - options.uri = UriUtils.createResourceUri([ - params.ACCOUNT_NAME, - "wrongGroup" - ]); - try{ - response = await rp(options); - } catch(err){ - - assert(err.response.statusCode == 400, 'Group should not be possible to create.'); - } -} - -groupTests.getTestGroup = async function(expectedCode) { - const options = {}; - options.resolveWithFullResponse = true; - options.method = "GET"; - options.headers = { 'Accept': 'application/ld+json' } - options.uri = UriUtils.createResourceUri([ - params.ACCOUNT_NAME, - params.GROUP_NAME - ]); - - var group = null; - var statusCode = 0; - - try { - response = await rp(options); - statusCode = response.statusCode; - group = response.body - } catch (err) { - statusCode = err.response.statusCode; - } - - assert(statusCode == expectedCode, `Unexpected statusCode ${statusCode} when retrieving test group (${expectedCode} expected).`); - return group; -} - -module.exports = groupTests; \ No newline at end of file diff --git a/server/app/test/methods/api/tractate-tests.js b/server/app/test/methods/api/tractate-tests.js deleted file mode 100644 index 6e90c072..00000000 --- a/server/app/test/methods/api/tractate-tests.js +++ /dev/null @@ -1,52 +0,0 @@ -const params = require('../../test-user.json'); -const rp = require('request-promise'); -const suite = require('../../../api/lib/databus-tractate-suite'); -const assert = require('assert'); -const ServerUtils = require('../../../common/utils/server-utils'); -const { autocomplete } = require('../../../api/lib/dataid-autocomplete'); -const jsonld = require('jsonld'); -const DatabusUris = require('../../../../../public/js/utils/databus-uris'); - -/** - * Tests for generating tractates and verifying signatures - */ -module.exports = async function tractateTests() { - - const options = {}; - options.headers = { "x-api-key": params.APIKEY }; - options.resolveWithFullResponse = true; - - - // Format input data - var testMetadata = ServerUtils.formatJsonTemplate(require('../../templates/version.json'), { - DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, - ACCOUNT: params.ACCOUNT_NAME, - GROUP: params.GROUP_NAME, - ARTIFACT: params.ARTIFACT_NAME, - VERSION: params.VERSION_NAME - }); - - // ========= Generate Databus Tractate v1 =========== - options.method = "POST"; - options.headers = { 'Accept': 'text/plain' } - options.json = true; - options.body = testMetadata; - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/api/tractate/v1/canonicalize`; - - let response = await rp(options); - assert(response.statusCode == 200, 'Could not generate Databus Tractate v1.'); - let results = response.body.split('\n'); - assert(results[1] == `${process.env.DATABUS_RESOURCE_BASE_URL}/${params.ACCOUNT_NAME}/${params.GROUP_NAME}/${params.ARTIFACT_NAME}/${params.VERSION_NAME}`, 'Version in Tractate is wrong'); - - // Expand graph, autocomplete, create proof - var expandedData = autocomplete(await jsonld.flatten(await jsonld.expand(testMetadata))); - var proof = suite.createProof(expandedData); - testMetadata[DatabusUris.JSONLD_GRAPH][DatabusUris.SEC_PROOF] = proof; - - // ========= Validate Databus Tractate v1 =========== - options.headers = { 'Accept': 'application/json' } - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/api/tractate/v1/verify`; - - response = await rp(options); - assert(response.statusCode == 200 && response.body.success == true, response.body.message); -} \ No newline at end of file diff --git a/server/app/test/methods/api/version-tests.js b/server/app/test/methods/api/version-tests.js deleted file mode 100644 index b9ff8c23..00000000 --- a/server/app/test/methods/api/version-tests.js +++ /dev/null @@ -1,148 +0,0 @@ -const params = require('../../test-user.json'); -const rp = require('request-promise'); -const assert = require('assert'); -const ServerUtils = require('../../../common/utils/server-utils'); -const UriUtils = require('../../../common/utils/uri-utils'); -const artifactTests = require('./artifact-tests'); -const groupTests = require('./group-tests'); - -var versionTests = {}; - -versionTests.versionTests = async function () { - - // ========= Get Version =========== - await versionTests.getTestVersion(404); - - // ========= Create Version =========== - var options = {}; - - options.method = "PUT"; - options.json = true; - options.resolveWithFullResponse = true; - options.headers = { "x-api-key": params.APIKEY }; - options.uri = UriUtils.createResourceUri([ - params.ACCOUNT_NAME, - params.GROUP_NAME, - params.ARTIFACT_NAME, - params.VERSION_NAME - ]); - options.body = ServerUtils.formatJsonTemplate(require('../../templates/version.json'), { - DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, - ACCOUNT: params.ACCOUNT_NAME, - GROUP: params.GROUP_NAME, - ARTIFACT: params.ARTIFACT_NAME, - VERSION: params.VERSION_NAME - }); - - response = await rp(options); - assert(response.statusCode == 200, 'Version could not be created.'); - - // ========= Get Version =========== - await versionTests.getTestVersion(200); - - // ========= Get Artifact =========== - await artifactTests.getTestArtifact(200); - - // ========= Get Group =========== - await groupTests.getTestGroup(200); - - // ========= Delete Group ========= - options.method = "DELETE"; - options.headers = { "x-api-key": params.APIKEY }; - options.uri = UriUtils.createResourceUri([ - params.ACCOUNT_NAME, - params.GROUP_NAME - ]); - - var statusCode = 0; - try { await rp(options); } catch (err) { statusCode = err.statusCode; } - assert(statusCode == 409, 'Deleting non-empty group should cause a conflict.'); - - // ========= Delete Artifact =========== - options.method = "DELETE"; - options.uri = UriUtils.createResourceUri([ - params.ACCOUNT_NAME, - params.GROUP_NAME, - params.ARTIFACT_NAME - ]); - - try { await rp(options); } catch (err) { statusCode = err.statusCode; } - assert(statusCode == 409, 'Deleting non-empty artifact should cause a conflict.'); - - // ========= Delete Version =========== - options.method = "DELETE"; - options.headers = { "x-api-key": params.APIKEY }; - options.uri = UriUtils.createResourceUri([ - params.ACCOUNT_NAME, - params.GROUP_NAME, - params.ARTIFACT_NAME, - params.VERSION_NAME - ]); - - response = await rp(options); - assert(response.statusCode == 204, 'Could not delete version.'); - - // ========= Get Version =========== - await versionTests.getTestVersion(404); - - - // ========= Create Constructed Version ======== - options = {}; - options.method = "PUT"; - options.json = true; - options.resolveWithFullResponse = true; - options.headers = { "x-api-key": params.APIKEY }; - options.uri = UriUtils.createResourceUri([ - params.ACCOUNT_NAME, - params.GROUP_NAME, - "constructed", - params.VERSION_NAME - ]); - options.body = ServerUtils.formatJsonTemplate(require('../../templates/constructed-version.json'), { - DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, - ACCOUNT: params.ACCOUNT_NAME, - GROUP: params.GROUP_NAME, - ARTIFACT: "constructed", - VERSION: params.VERSION_NAME - }); - - response = await rp(options); - assert(response.statusCode == 200, 'Constructed version could not be created.'); - - options.method = "DELETE"; - options.headers = { "x-api-key": params.APIKEY }; - - response = await rp(options); - assert(response.statusCode == 204, 'Could not delete constructed version.'); - -} - - -versionTests.getTestVersion = async function (expectedCode) { - const options = {}; - options.resolveWithFullResponse = true; - options.method = "GET"; - options.headers = { 'Accept': 'application/ld+json' } - options.uri = UriUtils.createResourceUri([ - params.ACCOUNT_NAME, - params.GROUP_NAME, - params.ARTIFACT_NAME, - params.VERSION_NAME - ]); - - var version = null; - var statusCode = 0; - - try { - response = await rp(options); - statusCode = response.statusCode; - version = response.body - } catch (err) { - statusCode = err.response.statusCode; - } - - assert(statusCode == expectedCode, `Unexpected statusCode ${statusCode} when retrieving test artifact (${expectedCode} expected).`); - return version; -} - -module.exports = versionTests; \ No newline at end of file diff --git a/server/app/test/methods/cache-tests.js b/server/app/test/methods/cache-tests.js deleted file mode 100644 index cf064c43..00000000 --- a/server/app/test/methods/cache-tests.js +++ /dev/null @@ -1,27 +0,0 @@ -const DatabusCache = require("../../common/cache/databus-cache"); -const assert = require('assert'); -const sparql = require('../../common/queries/sparql'); - -/** - * Tests for the DatabusCache class - */ -module.exports = async function cacheTests() { - - console.log(`Testing caching.`); - var cache = new DatabusCache(60); - var result = {}; - - // normal call - result = await cache.get('ga', () => sparql.pages.getGlobalActivityChartData()); - assert(result.length > 0); - - // cached call - should be fast - var startDate = Date.now().valueOf(); - result = await cache.get('ga', () => sparql.pages.getGlobalActivityChartData()); - assert((Date.now().valueOf() - startDate) < 10); - assert(result.length > 0); - - // cached call without promise - should still return the correct result - result = await cache.get('ga', () => async function () { }); - assert(result.length > 0); -} diff --git a/server/app/test/methods/resource-tests.js b/server/app/test/methods/resource-tests.js deleted file mode 100644 index b4b7caaf..00000000 --- a/server/app/test/methods/resource-tests.js +++ /dev/null @@ -1,24 +0,0 @@ -const DatabusCache = require("../../common/cache/databus-cache"); -const assert = require('assert'); -const sparql = require('../../common/queries/sparql'); -const rp = require('request-promise'); -const Constants = require("../../common/constants"); - -/** - * Tests for the DatabusCache class - */ -module.exports = async function resourceTests() { - - console.log(`Testing resources.`); - - var options = {}; - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}${Constants.DATABUS_DEFAULT_CONTEXT_PATH}`; - options.method = "GET"; - options.headers = {}; - options.resolveWithFullResponse = true; - - options.headers['Accept'] = "application/ld+json" - - var res = await rp(options); - assert(res.statusCode == 200, 'Expected 200 when retrieving context.'); -} diff --git a/server/app/test/methods/user-database-tests.js b/server/app/test/methods/user-database-tests.js deleted file mode 100644 index 63eafab7..00000000 --- a/server/app/test/methods/user-database-tests.js +++ /dev/null @@ -1,48 +0,0 @@ -const DatabusUserDatabase = require("../../../userdb"); -const params = require('../test-user.json'); -const assert = require('assert'); - -/** - * Tests the user database - */ - module.exports = async function userDatabaseTests() { - - console.log(`Testing user database.`); - - const db = new DatabusUserDatabase(); - db.debug = false; - - var result = false; - - result = await db.connect(); - assert(result); - - result = await db.getUsers(); - - if (db.debug) { - console.log(result); - } - - await db.deleteUser(params.SUB); - - result = await db.addApiKey(params.SUB, params.KEYNAME, params.APIKEY); - assert(!result); - - result = await db.addUser(params.SUB, params.DISPLAYNAME, params.ACCOUNT_NAME); - assert(result); - - result = await db.addApiKey(params.SUB, params.KEYNAME, params.APIKEY); - assert(result); - - result = await db.getUser(params.SUB); - assert(result.sub == params.SUB); - - result = await db.deleteUser(params.SUB); - assert(result); - - result = await db.getSub(params.APIKEY); - assert(result == null); - - var injectUserAdded = await db.addUser("testerman_ones_sub_token;\"--".SUB, params.DISPLAYNAME, params.ACCOUNT_NAME); - assert(!injectUserAdded); - } \ No newline at end of file diff --git a/server/app/test/methods/util-tests.js b/server/app/test/methods/util-tests.js deleted file mode 100644 index 8d576db5..00000000 --- a/server/app/test/methods/util-tests.js +++ /dev/null @@ -1,32 +0,0 @@ -const DatabusUtils = require("../../../../public/js/utils/databus-utils"); -const UriUtils = require("../../common/utils/uri-utils"); -const assert = require('assert'); - -/** - * Tests for util functions - */ -module.exports = async function utilTests() { - - console.log(`Testing util functions.`); - - // objSize - var obj = { one: 1, two: 2 }; - assert(DatabusUtils.objSize(obj) == 2); - assert(DatabusUtils.objSize({}) == 0); - assert(DatabusUtils.objSize(null) == 0); - - // uniqueList - var list = [0, 1, 1, 2, 2]; - var uniqueList = DatabusUtils.uniqueList(list); - assert(uniqueList.length == 3); - assert(uniqueList[0] == 0); - assert(uniqueList[1] == 1); - assert(uniqueList[2] == 2); - - // uriToName - assert(UriUtils.uriToName('https://example.org/test/my-name') == 'my-name'); - assert(UriUtils.uriToName('https://example.org/test/my-name#tag') == 'tag'); - - assert(UriUtils.createResourceUri(["asdf", "qwer"]) == - `${process.env.DATABUS_RESOURCE_BASE_URL}/asdf/qwer`) -} \ No newline at end of file diff --git a/server/app/test/methods/webdav-tests.js b/server/app/test/methods/webdav-tests.js deleted file mode 100644 index 4573fb26..00000000 --- a/server/app/test/methods/webdav-tests.js +++ /dev/null @@ -1,75 +0,0 @@ -const DatabusWebDAV = require("../../webdav"); -const params = require('../test-user.json'); -const rp = require('request-promise'); -const fs = require('fs'); -const assert = require('assert'); -const { createTestUser, deleteTestUser } = require("../test-utils"); - -/** - * Tests for webDAV calls - */ -module.exports = async function webDAVTests() { - - console.log(`Testing webDAV functions.`); - var user = await createTestUser(params); - - assert(user.accountName == params.ACCOUNT_NAME); - var payload = JSON.stringify({ success: true }); - - var dav = new DatabusWebDAV(); - - const options = {}; - options.headers = { - "x-api-key": params.APIKEY - } - - var userDavDirectory = `${dav.directory}${user.accountName}`; - - if (fs.existsSync(userDavDirectory)) { - fs.rmSync(userDavDirectory, { recursive: true, force: true }); - } - - var isDirDeleted = !fs.existsSync(userDavDirectory); - - assert(isDirDeleted); - - try { - - options.method = "MKCOL" - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/dav/${user.accountName}/test/`; - - var response = await rp(options); - assert(response == ""); - - var davDirectoryExists = fs.existsSync(userDavDirectory); - assert(davDirectoryExists); - - options.method = "PUT"; - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/dav/${user.accountName}/test/upload.json`; - options.body = payload; - - response = await rp(options); - assert(response == ""); - - var fileExists = fs.existsSync(`${userDavDirectory}/test/upload.json`); - assert(fileExists); - - options.method = "DELETE"; - options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/dav/${user.accountName}/test/`; - - response = await rp(options); - assert(response == ""); - - await deleteTestUser(params); - fs.rmSync(userDavDirectory, { recursive: true, force: true }); - - } catch (err) { - console.log(err); - - // Cleanup - await deleteTestUser(params); - fs.rmSync(userDavDirectory, { recursive: true, force: true }); - - assert(err == null); - } - } \ No newline at end of file diff --git a/server/app/test/templates/account.json b/server/app/test/templates/account.json deleted file mode 100644 index 74798090..00000000 --- a/server/app/test/templates/account.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "@context": "https://downloads.dbpedia.org/databus/context.jsonld", - "@graph": [ - { - "@id": "%DATABUS_RESOURCE_BASE_URL%/%ACCOUNT_NAME%#doc", - "@type": "foaf:PersonalProfileDocument", - "maker": "%DATABUS_RESOURCE_BASE_URL%/%ACCOUNT_NAME%#this", - "primaryTopic": "%DATABUS_RESOURCE_BASE_URL%/%ACCOUNT_NAME%#this" - }, - { - "@id": "%DATABUS_RESOURCE_BASE_URL%/%ACCOUNT_NAME%#this", - "@type": [ - "dbo:DBpedian", - "foaf:Person" - ], - "name": "%ACCOUNT_NAME%", - "rdfs:comment": "Hello Databus!", - "foaf:account": { "@id": "%DATABUS_RESOURCE_BASE_URL%/%ACCOUNT_NAME%" } - } - ] -} \ No newline at end of file diff --git a/server/app/test/templates/group.json b/server/app/test/templates/group.json deleted file mode 100644 index 5427bd68..00000000 --- a/server/app/test/templates/group.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "@context": "https://downloads.dbpedia.org/databus/context.jsonld", - "@graph": { - "@id": "%DATABUS_RESOURCE_BASE_URL%/%ACCOUNT%/%GROUP%", - "@type": "Group", - "title": "Test Group", - "description": "A test group for API testing." - } - } \ No newline at end of file diff --git a/server/app/test/test-user-nerd.json b/server/app/test/test-user-nerd.json deleted file mode 100644 index a5cb1c52..00000000 --- a/server/app/test/test-user-nerd.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "SUB": "testerman_twos_sub_token", - "DISPLAYNAME": "Testerman Two", - "ACCOUNT_NAME": "tester_two", - "APIKEY": "000000000000001", - "KEYNAME": "testkey", - "GROUP_NAME": "testgroup_two", - "ARTIFACT_NAME": "artifact-with_underscore", - "VERSION_NAME": "2.1.0-5" -} \ No newline at end of file diff --git a/server/app/test/test-user.json b/server/app/test/test-user.json deleted file mode 100644 index adf477ee..00000000 --- a/server/app/test/test-user.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "SUB": "testerman_ones_sub_token", - "DISPLAYNAME": "Testerman One", - "ACCOUNT_NAME": "tester", - "APIKEY": "000000000000000", - "KEYNAME": "testkey", - "GROUP_NAME": "testgroup", - "ARTIFACT_NAME": "testartifact", - "VERSION_NAME": "2.1.0" -} \ No newline at end of file diff --git a/server/app/test/test-utils.js b/server/app/test/test-utils.js deleted file mode 100644 index 0225949e..00000000 --- a/server/app/test/test-utils.js +++ /dev/null @@ -1,50 +0,0 @@ -const DatabusUserDatabase = require("../../userdb"); -const assert = require('assert'); - -var utils = {}; - -/** - * Creates a user database connection, inserts a test user and returns the entry - * @returns - */ -utils.createTestUser = async function(params) { - - const db = new DatabusUserDatabase(); - db.debug = false; - - var isConnected = await db.connect(); - assert(isConnected); - - await db.deleteUser(params.SUB); - var userAdded = await db.addUser(params.SUB, params.DISPLAYNAME, params.ACCOUNT_NAME); - assert(userAdded); - - var apiKeyAdded = await db.addApiKey(params.SUB, params.KEYNAME, params.APIKEY); - assert(apiKeyAdded); - - var user = await db.getUser(params.SUB); - assert(user.sub == params.SUB); - - return user; -} - - -/** - * Deletes the test user from the user database - */ -utils.deleteTestUser = async function(params) { - - const db = new DatabusUserDatabase(); - db.debug = false; - - var isConnected = await db.connect(); - assert(isConnected); - - var isDeleted = await db.deleteUser(params.SUB); - assert(isDeleted); - - var user = await db.getUser(params.SUB); - assert(user == null); -} - -module.exports = utils; \ No newline at end of file diff --git a/server/app/test/templates/artifact.json b/server/app/tests/templates/artifact.json similarity index 74% rename from server/app/test/templates/artifact.json rename to server/app/tests/templates/artifact.json index 43613afb..21169d18 100644 --- a/server/app/test/templates/artifact.json +++ b/server/app/tests/templates/artifact.json @@ -1,5 +1,5 @@ { - "@context": "https://downloads.dbpedia.org/databus/context.jsonld", + "@context": "http://localhost:3000/res/context.jsonld", "@graph": { "@id": "%DATABUS_RESOURCE_BASE_URL%/%ACCOUNT%/%GROUP%/%ARTIFACT%", "@type": "Artifact", diff --git a/server/app/tests/templates/collection.json b/server/app/tests/templates/collection.json new file mode 100644 index 00000000..f6b800ef --- /dev/null +++ b/server/app/tests/templates/collection.json @@ -0,0 +1,10 @@ +{ + "@context": "http://localhost:3000/res/context.jsonld", + "@graph": { + "@id": "%DATABUS_RESOURCE_BASE_URL%/%ACCOUNT%/collections/%COLLECTION%", + "@type": "Collection", + "title": "Test Collection", + "description": "A test collection for API testing.", + "databus:collectionContent": "%7B%22root%22%3A%7B%22uri%22%3Anull%2C%22property%22%3Anull%2C%22childNodes%22%3A%5B%7B%22uri%22%3A%22https%3A%2F%2Fdatabus.dbpedia.org%22%2C%22property%22%3Anull%2C%22childNodes%22%3A%5B%7B%22uri%22%3A%22https%3A%2F%2Fdatabus.dbpedia.org%2Fdbpedia%2Fgeneric%22%2C%22property%22%3A%22databus%3Agroup%22%2C%22childNodes%22%3A%5B%7B%22uri%22%3A%22https%3A%2F%2Fdatabus.dbpedia.org%2Fdbpedia%2Fgeneric%2Fredirects%22%2C%22property%22%3A%22databus%3Aartifact%22%2C%22childNodes%22%3A%5B%5D%2C%22facetSettings%22%3A%7B%7D%7D%2C%7B%22uri%22%3A%22https%3A%2F%2Fdatabus.dbpedia.org%2Fdbpedia%2Fgeneric%2Flabels%22%2C%22property%22%3A%22databus%3Aartifact%22%2C%22childNodes%22%3A%5B%5D%2C%22facetSettings%22%3A%7B%22https%3A%2F%2Fdataid.dbpedia.org%2Fdatabus-cv%23lang%22%3A%5B%7B%22value%22%3A%22de%22%2C%22checked%22%3Atrue%7D%2C%7B%22value%22%3A%22fr%22%2C%22checked%22%3Atrue%7D%5D%7D%7D%2C%7B%22uri%22%3A%22https%3A%2F%2Fdatabus.dbpedia.org%2Fdbpedia%2Fgeneric%2Fcategories%22%2C%22property%22%3A%22databus%3Aartifact%22%2C%22childNodes%22%3A%5B%5D%2C%22facetSettings%22%3A%7B%22https%3A%2F%2Fdataid.dbpedia.org%2Fdatabus-cv%23tag%22%3A%5B%7B%22value%22%3A%22labels%22%2C%22checked%22%3Atrue%7D%2C%7B%22value%22%3A%22articles%22%2C%22checked%22%3Atrue%7D%5D%7D%7D%2C%7B%22uri%22%3A%22https%3A%2F%2Fdatabus.dbpedia.org%2Fdbpedia%2Fgeneric%2Fpersondata%22%2C%22property%22%3A%22databus%3Aartifact%22%2C%22childNodes%22%3A%5B%5D%2C%22facetSettings%22%3A%7B%22http%3A%2F%2Fpurl.org%2Fdc%2Fterms%2FhasVersion%22%3A%5B%7B%22value%22%3A%222018.09.12%22%2C%22checked%22%3Atrue%7D%5D%7D%7D%5D%2C%22facetSettings%22%3A%7B%22http%3A%2F%2Fpurl.org%2Fdc%2Fterms%2FhasVersion%22%3A%5B%7B%22value%22%3A%22%24latest%22%2C%22checked%22%3Atrue%7D%5D%2C%22https%3A%2F%2Fdataid.dbpedia.org%2Fdatabus-cv%23lang%22%3A%5B%7B%22value%22%3A%22en%22%2C%22checked%22%3Atrue%7D%5D%7D%7D%2C%7B%22uri%22%3A%22https%3A%2F%2Fdatabus.dbpedia.org%2Fdbpedia%2Fmappings%22%2C%22property%22%3A%22databus%3Agroup%22%2C%22childNodes%22%3A%5B%7B%22uri%22%3A%22https%3A%2F%2Fdatabus.dbpedia.org%2Fdbpedia%2Fmappings%2Finstance-types%22%2C%22property%22%3A%22databus%3Aartifact%22%2C%22childNodes%22%3A%5B%5D%2C%22facetSettings%22%3A%7B%7D%7D%2C%7B%22uri%22%3A%22https%3A%2F%2Fdatabus.dbpedia.org%2Fdbpedia%2Fmappings%2Fmappingbased-objects%22%2C%22property%22%3A%22databus%3Aartifact%22%2C%22childNodes%22%3A%5B%5D%2C%22facetSettings%22%3A%7B%7D%7D%5D%2C%22facetSettings%22%3A%7B%22http%3A%2F%2Fpurl.org%2Fdc%2Fterms%2FhasVersion%22%3A%5B%7B%22value%22%3A%222020.05.01%22%2C%22checked%22%3Atrue%7D%5D%2C%22https%3A%2F%2Fdataid.dbpedia.org%2Fdatabus-cv%23lang%22%3A%5B%7B%22value%22%3A%22en%22%2C%22checked%22%3Atrue%7D%5D%7D%7D%2C%7B%22uri%22%3A%22https%3A%2F%2Fdatabus.dbpedia.org%2Fdbpedia%2Ftext%22%2C%22property%22%3A%22databus%3Agroup%22%2C%22childNodes%22%3A%5B%7B%22uri%22%3A%22https%3A%2F%2Fdatabus.dbpedia.org%2Fdbpedia%2Ftext%2Fshort-abstracts%22%2C%22property%22%3A%22databus%3Aartifact%22%2C%22childNodes%22%3A%5B%5D%2C%22facetSettings%22%3A%7B%7D%7D%5D%2C%22facetSettings%22%3A%7B%22http%3A%2F%2Fpurl.org%2Fdc%2Fterms%2FhasVersion%22%3A%5B%7B%22value%22%3A%222020.02.01%22%2C%22checked%22%3Atrue%7D%5D%2C%22https%3A%2F%2Fdataid.dbpedia.org%2Fdatabus-cv%23lang%22%3A%5B%7B%22value%22%3A%22en%22%2C%22checked%22%3Atrue%7D%5D%7D%7D%5D%2C%22facetSettings%22%3A%7B%7D%7D%5D%2C%22facetSettings%22%3A%7B%7D%7D%7D" + } +} \ No newline at end of file diff --git a/server/app/test/templates/constructed-version.json b/server/app/tests/templates/constructed-version.json similarity index 100% rename from server/app/test/templates/constructed-version.json rename to server/app/tests/templates/constructed-version.json diff --git a/server/app/tests/templates/group.json b/server/app/tests/templates/group.json new file mode 100644 index 00000000..f7913d41 --- /dev/null +++ b/server/app/tests/templates/group.json @@ -0,0 +1,9 @@ +{ + "@context": "http://localhost:3000/res/context.jsonld", + "@graph": { + "@id": "%DATABUS_RESOURCE_BASE_URL%/%ACCOUNT%/%GROUP%", + "@type": "Group", + "title": "Test Group", + "description": "A test group for API testing." + } +} \ No newline at end of file diff --git a/server/app/tests/templates/master-account.json b/server/app/tests/templates/master-account.json new file mode 100644 index 00000000..e9da6797 --- /dev/null +++ b/server/app/tests/templates/master-account.json @@ -0,0 +1,10 @@ +{ + "ID": "testerman_sub_token", + "DISPLAYNAME": "Master", + "ACCOUNT_NAME": "master", + "APIKEY": "000000000000003", + "KEYNAME": "testkey", + "GROUP_NAME": "testgroup", + "ARTIFACT_NAME": "testartifact", + "VERSION_NAME": "2.1.0" +} \ No newline at end of file diff --git a/server/app/tests/templates/test-account.json b/server/app/tests/templates/test-account.json new file mode 100644 index 00000000..26a2b446 --- /dev/null +++ b/server/app/tests/templates/test-account.json @@ -0,0 +1,11 @@ +{ + "ID": "testerman_sub_token", + "DISPLAYNAME": "Testerman", + "ACCOUNT_NAME": "testerman", + "APIKEY": "000000000000000", + "KEYNAME": "testkey", + "GROUP_NAME": "testgroup", + "ARTIFACT_NAME": "testartifact", + "VERSION_NAME": "2.1.0", + "COLLECTION_NAME": "testcollection" +} \ No newline at end of file diff --git a/server/app/test/templates/version.json b/server/app/tests/templates/version.json similarity index 89% rename from server/app/test/templates/version.json rename to server/app/tests/templates/version.json index 319c6d16..00ab611f 100644 --- a/server/app/test/templates/version.json +++ b/server/app/tests/templates/version.json @@ -1,5 +1,5 @@ { - "@context": "https://downloads.dbpedia.org/databus/context.jsonld", + "@context": "http://localhost:3000/res/context.jsonld", "@graph": { "@type": "Version", "@id": "%DATABUS_RESOURCE_BASE_URL%/%ACCOUNT%/%GROUP%/%ARTIFACT%/%VERSION%", diff --git a/server/app/tests/test.accounts.js b/server/app/tests/test.accounts.js new file mode 100644 index 00000000..a9dc5c5b --- /dev/null +++ b/server/app/tests/test.accounts.js @@ -0,0 +1,232 @@ +const { suite } = require('uvu'); +const assert = require('uvu/assert'); +const rp = require('request-promise'); + +const ServerUtils = require('../common/utils/server-utils'); +const DatabusUserTestUtils = require('./utils/userdb-utils'); +const DatabusUserDatabase = require('../../userdb'); + +const test_account = require('./templates/test-account.json'); +const master_account = require('./templates/master-account.json'); + +const test = suite('account-tests'); + +/** @type {DatabusUserDatabase} */ +let db; + +test.before(async () => { + ServerUtils.setupRequireExtensions(); + + db = new DatabusUserDatabase(); + await db.connect(); + + await DatabusUserTestUtils.insertAccount(db, master_account); + + const options = { + headers: { 'x-api-key': master_account.APIKEY }, + resolveWithFullResponse: true, + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/delete`, + method: 'POST', + json: true, + body: { accountName: test_account.ACCOUNT_NAME }, + }; + + try { + await rp(options); + } catch (err) { + assert.is(err.response?.statusCode, 404); + } + +}); + +test('GET non-existing account returns 404', async () => { + const options = { + headers: { + 'x-api-key': master_account.APIKEY, + Accept: 'application/ld+json' + }, + resolveWithFullResponse: true, + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/${test_account.ACCOUNT_NAME}`, + method: 'GET', + }; + + try { + await rp(options); + assert.unreachable('Expected 404 for non-existing account.'); + } catch (err) { + assert.is(err.response?.statusCode, 404); + } +}); + +test('CREATE account returns 200', async () => { + const options = { + headers: { 'x-api-key': master_account.APIKEY }, + resolveWithFullResponse: true, + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/create`, + method: 'POST', + json: true, + body: { + name: test_account.ACCOUNT_NAME, + label: 'Test Label', + }, + }; + + const response = await rp(options); + assert.is(response.statusCode, 200); +}); + +test('SEARCH account by label returns 200', async () => { + await new Promise(resolve => setTimeout(resolve, 2000)); + + const options = { + headers: { 'x-api-key': master_account.APIKEY }, + resolveWithFullResponse: true, + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/search?query=Test Label&typeName=Account`, + method: 'GET', + json: true, + }; + + const response = await rp(options); + assert.is(response.statusCode, 200); + + const responseBody = response.body; + + const accountDoc = responseBody.docs.find( + doc => doc.id[0] === `${process.env.DATABUS_RESOURCE_BASE_URL}/${test_account.ACCOUNT_NAME}` + ); + + assert.ok(accountDoc, 'Account not found in search results'); +}); + +test('GET created account returns 200', async () => { + const options = { + headers: { 'x-api-key': master_account.APIKEY, Accept: 'application/ld+json' }, + resolveWithFullResponse: true, + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/${test_account.ACCOUNT_NAME}`, + method: 'GET', + }; + + const response = await rp(options); + assert.is(response.statusCode, 200); +}); + +test('UPDATE account returns 200', async () => { + const options = { + headers: { 'x-api-key': master_account.APIKEY }, + resolveWithFullResponse: true, + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/update`, + method: 'POST', + json: true, + body: { + accountName: test_account.ACCOUNT_NAME, + label: 'Updated Label', + status: 'active', + }, + }; + + const response = await rp(options); + assert.is(response.statusCode, 200); +}); + + +test('Cannot create API key for someone else', async () => { + + await DatabusUserTestUtils.insertApiKey(db, test_account); + + const options = { + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/api-key/create`, + headers: { 'x-api-key': test_account.APIKEY }, + resolveWithFullResponse: true, + method: 'POST', + json: true, + body: { + accountName: 'janfo', + keyname: 'testkey' + }, + }; + + try { + await rp(options); + } catch (err) { + assert.is(err.response?.statusCode, 403); + } +}); + +test('API key create and delete tests', async () => { + + await DatabusUserTestUtils.insertApiKey(db, test_account); + + const options = { + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/api-key/create`, + headers: { 'x-api-key': test_account.APIKEY }, + resolveWithFullResponse: true, + method: 'POST', + json: true, + body: { + accountName: test_account.ACCOUNT_NAME, + keyname: 'testkey2' + }, + }; + + let response = await rp(options,); + assert.is(response.statusCode, 200); + + try { + response = await rp(options); + assert.unreachable('Creating already existing API key should fail'); + } catch (err) { + assert.is(err.response?.statusCode, 400); + } + + options.uri = `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/api-key/delete`; + response = await rp(options); + assert.is(response.statusCode, 200); + + response = await rp(options); + assert.is(response.statusCode, 204); +}); + +test('DELETE account returns 200 and 404 when deleting again', async () => { + const options = { + headers: { 'x-api-key': master_account.APIKEY }, + resolveWithFullResponse: true, + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/delete`, + method: 'POST', + json: true, + body: { accountName: test_account.ACCOUNT_NAME }, + }; + + let response = await rp(options); + assert.is(response.statusCode, 200); + + try { + response = await rp(options); + assert.unreachable('Expected 404 when deleting already deleted account.'); + } catch (err) { + assert.is(err.response?.statusCode, 404); + } +}); + + + +test.after(async () => { + + const options = { + headers: { 'x-api-key': master_account.APIKEY }, + resolveWithFullResponse: true, + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/delete`, + method: 'POST', + json: true, + body: { accountName: test_account.ACCOUNT_NAME }, + }; + + try { + await rp(options); + } catch (err) { + + } + + await DatabusUserTestUtils.deleteUser(db, master_account); +}); + +test.run(); diff --git a/server/app/tests/test.artifacts.js b/server/app/tests/test.artifacts.js new file mode 100644 index 00000000..214cc516 --- /dev/null +++ b/server/app/tests/test.artifacts.js @@ -0,0 +1,221 @@ +const { suite } = require('uvu'); +const assert = require('uvu/assert'); +const rp = require('request-promise'); +const N3 = require('n3'); +const { Parser } = N3; + +const ServerUtils = require('../common/utils/server-utils'); +const UriUtils = require('../common/utils/uri-utils'); +const DatabusUserDatabase = require('../../userdb'); +const DatabusUserTestUtils = require('./utils/userdb-utils'); + +const test_account = require('./templates/test-account.json'); +const master_account = require('./templates/master-account.json'); +const DatabusUris = require('../../../public/js/utils/databus-uris'); + +const test = suite('artifact-crud'); + +/** @type {DatabusUserDatabase} */ +let db; + +const getArtifact = async (expectedCode) => { + const options = { + method: 'GET', + uri: UriUtils.createResourceUri([ + test_account.ACCOUNT_NAME, + test_account.GROUP_NAME, + test_account.ARTIFACT_NAME, + ]), + headers: { Accept: 'application/ld+json' }, + resolveWithFullResponse: true, + }; + + try { + const res = await rp(options); + assert.is(res.statusCode, expectedCode); + } catch (err) { + assert.is(err.response.statusCode, expectedCode); + } +}; + +test.before(async () => { + ServerUtils.setupRequireExtensions(); + + db = new DatabusUserDatabase(); + await db.connect(); + + await DatabusUserTestUtils.insertAccount(db, master_account); + + const createAccountOptions = { + method: 'POST', + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/create`, + headers: { 'x-api-key': master_account.APIKEY }, + json: true, + resolveWithFullResponse: true, + body: { + name: test_account.ACCOUNT_NAME, + label: 'Test Label', + }, + }; + + await rp(createAccountOptions); + + await DatabusUserTestUtils.insertApiKey(db, test_account); +}); + +test('READ: artifact should not exist initially', async () => { + await getArtifact(404); +}); + +test('CREATE: artifact can be created', async () => { + const options = { + method: 'POST', + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/register`, + headers: { 'x-api-key': test_account.APIKEY }, + json: true, + resolveWithFullResponse: true, + body: ServerUtils.formatJsonTemplate(require('./templates/artifact.json'), { + DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, + ACCOUNT: test_account.ACCOUNT_NAME, + GROUP: test_account.GROUP_NAME, + ARTIFACT: test_account.ARTIFACT_NAME, + }), + }; + + const res = await rp(options); + assert.is(res.statusCode, 200); +}); + +test('READ: artifact exists after creation', async () => { + await getArtifact(200); +}); + +test('READ GROUP TURTLE: group can be read as turtle and turtle contains link to artifact', async () => { + const options = { + method: 'GET', + uri: UriUtils.createResourceUri([ + test_account.ACCOUNT_NAME, + test_account.GROUP_NAME, + ]), + headers: { Accept: 'text/turtle' }, + resolveWithFullResponse: true, + simple: false, + }; + + const expectedCode = 200; + + try { + const res = await rp(options); + assert.is(res.statusCode, expectedCode); + + const parser = new Parser(); + let quads; + try { + quads = parser.parse(res.body); + } catch (parseError) { + assert.fail(`Turtle parsing failed: ${parseError.message}`); + } + + assert.ok(quads.length > 0, 'Turtle should contain at least one triple'); + + const hasArtifactLink = quads.some( + q => q.predicate.value === DatabusUris.DATABUS_HAS_ARTIFACT + ); + + assert.ok( + hasArtifactLink, + 'Turtle should contain at least one databus:hasArtifact link' + ); + } catch (err) { + assert.is(err.response.statusCode, expectedCode); + } +}); + + +test('DELETE: deleting non-empty group returns conflict', async () => { + const options = { + method: 'DELETE', + uri: UriUtils.createResourceUri([ + test_account.ACCOUNT_NAME, + test_account.GROUP_NAME, + ]), + headers: { 'x-api-key': test_account.APIKEY }, + resolveWithFullResponse: true, + }; + + let statusCode = 0; + try { + await rp(options); + } catch (err) { + statusCode = err.statusCode || err.response?.statusCode; + } + assert.is(statusCode, 409); +}); + +test('DELETE: artifact can be deleted', async () => { + const options = { + method: 'DELETE', + uri: UriUtils.createResourceUri([ + test_account.ACCOUNT_NAME, + test_account.GROUP_NAME, + test_account.ARTIFACT_NAME, + ]), + headers: { 'x-api-key': test_account.APIKEY }, + resolveWithFullResponse: true, + }; + + const res = await rp(options); + assert.is(res.statusCode, 204); +}); + +test('READ: artifact is gone after deletion', async () => { + await getArtifact(404); +}); + +test.after(async () => { + try { + await rp({ + method: 'DELETE', + uri: UriUtils.createResourceUri([ + test_account.ACCOUNT_NAME, + test_account.GROUP_NAME, + test_account.ARTIFACT_NAME, + ]), + headers: { 'x-api-key': test_account.APIKEY }, + resolveWithFullResponse: true, + }); + } catch (err) { + if (![204, 404].includes(err.response?.statusCode)) throw err; + } + + try { + await rp({ + method: 'DELETE', + uri: UriUtils.createResourceUri([ + test_account.ACCOUNT_NAME, + test_account.GROUP_NAME, + ]), + headers: { 'x-api-key': test_account.APIKEY }, + resolveWithFullResponse: true, + }); + } catch (err) { + if (![204, 404].includes(err.response?.statusCode)) throw err; + } + + try { + await rp({ + method: 'POST', + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/delete`, + headers: { 'x-api-key': master_account.APIKEY }, + json: true, + resolveWithFullResponse: true, + body: { accountName: test_account.ACCOUNT_NAME }, + }); + } catch (err) { + assert.is(err.response?.statusCode, 404); + } + + await DatabusUserTestUtils.deleteUser(db, master_account); +}); + +test.run(); diff --git a/server/app/tests/test.collections.js b/server/app/tests/test.collections.js new file mode 100644 index 00000000..c96e35ca --- /dev/null +++ b/server/app/tests/test.collections.js @@ -0,0 +1,180 @@ +const { suite } = require('uvu'); +const assert = require('uvu/assert'); +const rp = require('request-promise'); + +const ServerUtils = require('../common/utils/server-utils'); +const UriUtils = require('../common/utils/uri-utils'); +const DatabusUserDatabase = require('../../userdb'); +const DatabusUserTestUtils = require('./utils/userdb-utils'); + +const test_account = require('./templates/test-account.json'); +const master_account = require('./templates/master-account.json'); + +const test = suite('group-crud'); + +/** @type {DatabusUserDatabase} */ +let db; + +const getCollection = async (expectedCode) => { + const options = { + method: 'GET', + uri: UriUtils.createResourceUri([ + test_account.ACCOUNT_NAME, + "collections", + test_account.COLLECTION_NAME, + ]), + headers: { Accept: 'application/ld+json' }, + resolveWithFullResponse: true, + }; + + try { + const res = await rp(options); + assert.is(res.statusCode, expectedCode); + } catch (err) { + assert.is(err.response.statusCode, expectedCode); + } +}; + +test.before(async () => { + ServerUtils.setupRequireExtensions(); + + db = new DatabusUserDatabase(); + await db.connect(); + + await DatabusUserTestUtils.insertAccount(db, master_account); + + const createOptions = { + method: 'POST', + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/create`, + headers: { 'x-api-key': master_account.APIKEY }, + json: true, + resolveWithFullResponse: true, + body: { + name: test_account.ACCOUNT_NAME, + label: 'Test Label', + }, + }; + + await rp(createOptions); + + await DatabusUserTestUtils.insertApiKey(db, test_account); +}); + +test('READ: collection should not exist initially', async () => { + await getCollection(404); +}); + +test('CREATE: collection can be created', async () => { + const registerOptions = { + method: 'POST', + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/register`, + headers: { 'x-api-key': test_account.APIKEY }, + json: true, + resolveWithFullResponse: true, + body: ServerUtils.formatJsonTemplate(require('./templates/collection.json'), { + DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, + ACCOUNT: test_account.ACCOUNT_NAME, + COLLECTION: test_account.COLLECTION_NAME, + }), + }; + + const res = await rp(registerOptions); + assert.is(res.statusCode, 200); +}); + +test('READ: collection exists after creation', async () => { + await getCollection(200); +}); + +test('GET SPARQL: collection sparql can be read', async () => { + + const getCollectionSparqlRequest = { + method: 'GET', + uri: UriUtils.createResourceUri([ + test_account.ACCOUNT_NAME, + "collections", + test_account.COLLECTION_NAME, + ]), + headers: { Accept: 'text/sparql' }, + resolveWithFullResponse: true, + }; + + try { + const res = await rp(getCollectionSparqlRequest); + assert.is(res.statusCode, 200); + } catch (err) { + assert.is(err.response.statusCode, 200); + } +}); + +// TODO: Test SPARQL query for correctness + +// TODO: Test MD5 hash retrieval + +test('DELETE: collection can be deleted', async () => { + + var resourcerUri = UriUtils.createResourceUri([ + test_account.ACCOUNT_NAME, + "collections", + test_account.COLLECTION_NAME, + ]); + + const deleteOptions = { + method: 'DELETE', + uri: resourcerUri, + headers: { 'x-api-key': test_account.APIKEY }, + resolveWithFullResponse: true, + }; + + const res = await rp(deleteOptions); + assert.is(res.statusCode, 204); +}); + +test('READ: collection is gone after deletion', async () => { + await getCollection(404); +}); + + +test.after(async () => { + // Try deleting the group in case it still exists + var resourcerUri = UriUtils.createResourceUri([ + test_account.ACCOUNT_NAME, + "collections", + test_account.COLLECTION_NAME, + ]); + + + const cleanupCollectionRequest = { + method: 'DELETE', + uri: resourcerUri, + headers: { 'x-api-key': test_account.APIKEY }, + resolveWithFullResponse: true, + }; + + try { + await rp(cleanupCollectionRequest); + } catch (err) { + if (err.response?.statusCode !== 404 && err.response?.statusCode !== 204) { + throw err; + } + } + + const deleteAccountRequest = { + method: 'POST', + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/delete`, + headers: { 'x-api-key': master_account.APIKEY }, + json: true, + resolveWithFullResponse: true, + body: { accountName: test_account.ACCOUNT_NAME }, + }; + + try { + await rp(deleteAccountRequest); + } catch (err) { + assert.is(err.response?.statusCode, 404); + } + + await DatabusUserTestUtils.deleteUser(db, master_account); +}); + +test.run(); diff --git a/server/app/tests/test.groups.js b/server/app/tests/test.groups.js new file mode 100644 index 00000000..fedc9aee --- /dev/null +++ b/server/app/tests/test.groups.js @@ -0,0 +1,177 @@ +const { suite } = require('uvu'); +const assert = require('uvu/assert'); +const rp = require('request-promise'); +const N3 = require('n3'); +const { Parser } = N3; + +const ServerUtils = require('../common/utils/server-utils'); +const UriUtils = require('../common/utils/uri-utils'); +const DatabusUserDatabase = require('../../userdb'); +const DatabusUserTestUtils = require('./utils/userdb-utils'); + +const test_account = require('./templates/test-account.json'); +const master_account = require('./templates/master-account.json'); + +const test = suite('group-crud'); + +/** @type {DatabusUserDatabase} */ +let db; + +const getGroup = async (expectedCode) => { + const options = { + method: 'GET', + uri: UriUtils.createResourceUri([ + test_account.ACCOUNT_NAME, + test_account.GROUP_NAME, + ]), + headers: { Accept: 'application/ld+json' }, + resolveWithFullResponse: true, + }; + + try { + const res = await rp(options); + assert.is(res.statusCode, expectedCode); + } catch (err) { + assert.is(err.response.statusCode, expectedCode); + } +}; + +test.before(async () => { + ServerUtils.setupRequireExtensions(); + + db = new DatabusUserDatabase(); + await db.connect(); + + await DatabusUserTestUtils.insertAccount(db, master_account); + + const createOptions = { + method: 'POST', + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/create`, + headers: { 'x-api-key': master_account.APIKEY }, + json: true, + resolveWithFullResponse: true, + body: { + name: test_account.ACCOUNT_NAME, + label: 'Test Label', + }, + }; + + await rp(createOptions); + + await DatabusUserTestUtils.insertApiKey(db, test_account); +}); + +test('READ: group should not exist initially', async () => { + await getGroup(404); +}); + +test('CREATE: group can be created', async () => { + const registerOptions = { + method: 'POST', + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/register`, + headers: { 'x-api-key': test_account.APIKEY }, + json: true, + resolveWithFullResponse: true, + body: ServerUtils.formatJsonTemplate(require('./templates/group.json'), { + DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, + ACCOUNT: test_account.ACCOUNT_NAME, + GROUP: test_account.GROUP_NAME, + }), + }; + + const res = await rp(registerOptions); + assert.is(res.statusCode, 200); +}); + +test('READ: group exists after creation', async () => { + await getGroup(200); +}); + +test('READ AS TURTLE: group can be read as turtle and turtle ends with a newline character', async () => { + const options = { + method: 'GET', + uri: UriUtils.createResourceUri([ + test_account.ACCOUNT_NAME, + test_account.GROUP_NAME, + ]), + headers: { Accept: 'text/turtle' }, + resolveWithFullResponse: true, + simple: false, + }; + + const expectedCode = 200; + + try { + const res = await rp(options); + assert.is(res.statusCode, expectedCode); + assert.ok(res.body.endsWith('\n'), 'Turtle response should end with a newline'); + + const parser = new Parser(); + let quads; + try { + quads = parser.parse(res.body); + } catch (parseError) { + assert.fail(`Turtle parsing failed: ${parseError.message}`); + } + + assert.ok(quads.length > 0, 'Turtle should contain at least one triple'); + + } catch (err) { + assert.is(err.response.statusCode, expectedCode); + } +}); + +test('DELETE: group can be deleted', async () => { + + const deleteOptions = { + method: 'DELETE', + uri: UriUtils.createResourceUri([test_account.ACCOUNT_NAME, test_account.GROUP_NAME]), + headers: { 'x-api-key': test_account.APIKEY }, + resolveWithFullResponse: true, + }; + + const res = await rp(deleteOptions); + assert.is(res.statusCode, 204); +}); + +test('READ: group is gone after deletion', async () => { + await getGroup(404); +}); + + +test.after(async () => { + // Try deleting the group in case it still exists + const cleanupGroup = { + method: 'DELETE', + uri: UriUtils.createResourceUri([test_account.ACCOUNT_NAME, test_account.GROUP_NAME]), + headers: { 'x-api-key': test_account.APIKEY }, + resolveWithFullResponse: true, + }; + + try { + await rp(cleanupGroup); + } catch (err) { + if (err.response?.statusCode !== 404 && err.response?.statusCode !== 204) { + throw err; + } + } + + const deleteAccountOptions = { + method: 'POST', + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/delete`, + headers: { 'x-api-key': master_account.APIKEY }, + json: true, + resolveWithFullResponse: true, + body: { accountName: test_account.ACCOUNT_NAME }, + }; + + try { + await rp(deleteAccountOptions); + } catch (err) { + assert.is(err.response?.statusCode, 404); + } + + await DatabusUserTestUtils.deleteUser(db, master_account); +}); + +test.run(); diff --git a/server/app/tests/test.resources.js b/server/app/tests/test.resources.js new file mode 100644 index 00000000..09a1c370 --- /dev/null +++ b/server/app/tests/test.resources.js @@ -0,0 +1,23 @@ +const { suite } = require('uvu'); +const assert = require('uvu/assert'); +const rp = require('request-promise'); + +const Constants = require('../common/constants'); + +const test = suite('resource-tests'); + +test('GET default context returns 200', async () => { + const options = { + method: 'GET', + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}${Constants.DATABUS_DEFAULT_CONTEXT_PATH}`, + headers: { + Accept: 'application/ld+json', + }, + resolveWithFullResponse: true, + }; + + const response = await rp(options); + assert.is(response.statusCode, 200); +}); + +test.run(); diff --git a/server/app/tests/test.tractate.js b/server/app/tests/test.tractate.js new file mode 100644 index 00000000..3e4589d1 --- /dev/null +++ b/server/app/tests/test.tractate.js @@ -0,0 +1,115 @@ +const { suite } = require('uvu'); +const assert = require('uvu/assert'); +const rp = require('request-promise'); +const jsonld = require('jsonld'); + +const DatabusUserTestUtils = require('./utils/userdb-utils'); +const DatabusUserDatabase = require('../../userdb'); +const ServerUtils = require('../common/utils/server-utils'); +const suiteUtils = require('../api/lib/databus-tractate-suite'); +const { autocomplete } = require('../api/lib/dataid-autocomplete'); +const DatabusUris = require('../../../public/js/utils/databus-uris'); + +const test_account = require('./templates/test-account.json'); +const master_account = require('./templates/master-account.json'); + +const test = suite('tractate-tests'); + +let testMetadata; + +/** @type {DatabusUserDatabase} */ +let db; + + +test.before(async () => { + ServerUtils.setupRequireExtensions(); + + testMetadata = ServerUtils.formatJsonTemplate(require('./templates/version.json'), { + DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, + ACCOUNT: test_account.ACCOUNT_NAME, + GROUP: test_account.GROUP_NAME, + ARTIFACT: test_account.ARTIFACT_NAME, + VERSION: test_account.VERSION_NAME, + }); + + db = new DatabusUserDatabase(); + await db.connect(); + + await DatabusUserTestUtils.insertAccount(db, master_account); + + const options = { + headers: { 'x-api-key': master_account.APIKEY }, + resolveWithFullResponse: true, + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/create`, + method: 'POST', + json: true, + body: { + name: test_account.ACCOUNT_NAME, + label: 'Test Label', + }, + }; + + await rp(options); +}); + +test('generate Databus Tractate v1', async () => { + const options = { + method: 'POST', + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/tractate/v1/canonicalize`, + headers: { Accept: 'text/plain' }, + resolveWithFullResponse: true, + json: true, + body: testMetadata, + }; + + const response = await rp(options); + assert.is(response.statusCode, 200); + + const lines = response.body.split('\n'); + assert.is( + lines[1], + `${process.env.DATABUS_RESOURCE_BASE_URL}/${test_account.ACCOUNT_NAME}/${test_account.GROUP_NAME}/${test_account.ARTIFACT_NAME}/${test_account.VERSION_NAME}` + ); +}); + +test('verify Databus Tractate v1', async () => { + + const expandedData = autocomplete(await jsonld.flatten(await jsonld.expand(testMetadata))); + const proof = suiteUtils.createProof(expandedData); + testMetadata[DatabusUris.JSONLD_GRAPH][DatabusUris.SEC_PROOF] = proof; + + const options = { + method: 'POST', + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/tractate/v1/verify`, + headers: { Accept: 'application/json' }, + resolveWithFullResponse: true, + json: true, + body: testMetadata, + }; + + const response = await rp(options); + assert.is(response.statusCode, 200); + assert.ok(response.body.success, response.body.message); +}); + +test.after(async () => { + const options = { + headers: { 'x-api-key': master_account.APIKEY }, + resolveWithFullResponse: true, + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/delete`, + method: 'POST', + json: true, + body: { accountName: test_account.ACCOUNT_NAME }, + }; + + try { + await rp(options); + } catch(err) { + assert.is(err.response?.statusCode, 404); + } + + await DatabusUserTestUtils.deleteUser(db, master_account); +}); + + +test.run(); diff --git a/server/app/tests/test.userdb.js b/server/app/tests/test.userdb.js new file mode 100644 index 00000000..85887a58 --- /dev/null +++ b/server/app/tests/test.userdb.js @@ -0,0 +1,76 @@ +const { suite } = require('uvu'); +const assert = require('uvu/assert'); +const DatabusUserDatabase = require('../../userdb'); +const ServerUtils = require('../common/utils/server-utils'); +const masterAccount = require('./templates/master-account.json'); +const test = suite('user-db'); + +/** @type {DatabusUserDatabase} */ +let db; + +test.before(async () => { + ServerUtils.setupRequireExtensions(); + db = new DatabusUserDatabase(); + db.debug = false; + const connected = await db.connect(); + assert.ok(connected); +}); + +test('getAllUsers does not throw', async () => { + const users = await db.getAllUsers(); + assert.ok(Array.isArray(users)); +}); + +test('addApiKey fails if user does not exist', async () => { + + await db.deleteUser(masterAccount.ID); + + const user = await db.getUser(masterAccount.ID); + assert.ok(user == null); + + const added = await db.addApiKey(masterAccount.ACCOUNT_NAME, masterAccount.KEYNAME, masterAccount.APIKEY); + assert.not(added); +}); + +test('addUser succeeds', async () => { + const added = await db.addUser(masterAccount.ID); + assert.ok(added); +}); + +test('addAccount succeeds', async () => { + const added = await db.addAccount(masterAccount.ID, masterAccount.ACCOUNT_NAME); + assert.ok(added); +}); + +test('addApiKey succeeds after user exists', async () => { + const added = await db.addApiKey(masterAccount.ACCOUNT_NAME, masterAccount.KEYNAME, masterAccount.APIKEY); + assert.ok(added); +}); + +test('getUser returns correct user', async () => { + const user = await db.getUser(masterAccount.ID); + assert.is(user.id, masterAccount.ID); +}); + +test('deleteUser removes user and api keys', async () => { + const deleted = await db.deleteUser(masterAccount.ID); + assert.ok(deleted); + + const apikey = await db.getApiKey(masterAccount.APIKEY); + assert.ok(apikey == null); +}); + +test('addUser SQL injection test fails', async () => { + const injected = await db.addUser('testerman_ones_sub_token;"--'); + assert.not(injected); +}); + +test.after(async () => { + try { + await db.deleteUser(masterAccount.ID); + } catch (err) { + console.error(`Cleanup failed for user ${masterAccount.ID}:`, err); + } +}); + +test.run(); \ No newline at end of file diff --git a/server/app/tests/test.utils.js b/server/app/tests/test.utils.js new file mode 100644 index 00000000..a48623d5 --- /dev/null +++ b/server/app/tests/test.utils.js @@ -0,0 +1,33 @@ +const { suite } = require('uvu'); +const assert = require('uvu/assert'); +const DatabusUtils = require('../../../public/js/utils/databus-utils'); +const UriUtils = require('../common/utils/uri-utils'); + +const test = suite('util-functions'); + +test('objSize returns correct size', () => { + assert.is(DatabusUtils.objSize({ one: 1, two: 2 }), 2); + assert.is(DatabusUtils.objSize({}), 0); + assert.is(DatabusUtils.objSize(null), 0); +}); + +test('uniqueList returns unique elements', () => { + const list = [0, 1, 1, 2, 2]; + const uniqueList = DatabusUtils.uniqueList(list); + assert.is(uniqueList.length, 3); + assert.is(uniqueList[0], 0); + assert.is(uniqueList[1], 1); + assert.is(uniqueList[2], 2); +}); + +test('uriToName extracts correct name', () => { + assert.is(UriUtils.uriToName('https://example.org/test/my-name'), 'my-name'); + assert.is(UriUtils.uriToName('https://example.org/test/my-name#tag'), 'tag'); +}); + +test('createResourceUri creates correct URI', () => { + const expected = `${process.env.DATABUS_RESOURCE_BASE_URL}/asdf/qwer`; + assert.is(UriUtils.createResourceUri(['asdf', 'qwer']), expected); +}); + +test.run(); diff --git a/server/app/tests/test.versions.js b/server/app/tests/test.versions.js new file mode 100644 index 00000000..0047351d --- /dev/null +++ b/server/app/tests/test.versions.js @@ -0,0 +1,252 @@ +const { suite } = require('uvu'); +const assert = require('uvu/assert'); +const rp = require('request-promise'); +const N3 = require('n3'); +const { Parser } = N3; + +const ServerUtils = require('../common/utils/server-utils'); +const UriUtils = require('../common/utils/uri-utils'); +const DatabusUserDatabase = require('../../userdb'); +const DatabusUserTestUtils = require('./utils/userdb-utils'); + +const test_account = require('./templates/test-account.json'); +const master_account = require('./templates/master-account.json'); +const DatabusUris = require('../../../public/js/utils/databus-uris'); + +const test = suite('version-crud'); + +/** @type {DatabusUserDatabase} */ +let db; + +const getVersion = async (expectedCode) => { + const options = { + method: 'GET', + uri: UriUtils.createResourceUri([ + test_account.ACCOUNT_NAME, + test_account.GROUP_NAME, + test_account.ARTIFACT_NAME, + test_account.VERSION_NAME, + ]), + headers: { Accept: 'application/ld+json' }, + resolveWithFullResponse: true, + }; + + try { + const res = await rp(options); + assert.is(res.statusCode, expectedCode); + return res.body; + } catch (err) { + assert.is(err.response.statusCode, expectedCode); + } +}; + +test.before(async () => { + ServerUtils.setupRequireExtensions(); + + db = new DatabusUserDatabase(); + await db.connect(); + + await DatabusUserTestUtils.insertAccount(db, master_account); + + await rp({ + method: 'POST', + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/create`, + headers: { 'x-api-key': master_account.APIKEY }, + json: true, + resolveWithFullResponse: true, + body: { name: test_account.ACCOUNT_NAME, label: 'Test Label' }, + }); + + await DatabusUserTestUtils.insertApiKey(db, test_account); +}); + +test('READ: version should not exist initially', async () => { + await getVersion(404); +}); + +test('CREATE: version can be created', async () => { + const options = { + method: 'POST', + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/register`, + headers: { 'x-api-key': test_account.APIKEY }, + json: true, + resolveWithFullResponse: true, + body: ServerUtils.formatJsonTemplate(require('./templates/version.json'), { + DATABUS_RESOURCE_BASE_URL: process.env.DATABUS_RESOURCE_BASE_URL, + ACCOUNT: test_account.ACCOUNT_NAME, + GROUP: test_account.GROUP_NAME, + ARTIFACT: test_account.ARTIFACT_NAME, + VERSION: test_account.VERSION_NAME, + }), + }; + + const res = await rp(options); + assert.is(res.statusCode, 200); +}); + +test('READ: version exists after creation', async () => { + await getVersion(200); +}); + + +test('READ ARTIFACT TURTLE: artifact can be read as turtle and turtle contains link to version', async () => { + const options = { + method: 'GET', + uri: UriUtils.createResourceUri([ + test_account.ACCOUNT_NAME, + test_account.GROUP_NAME, + test_account.ARTIFACT_NAME + ]), + headers: { Accept: 'text/turtle' }, + resolveWithFullResponse: true, + simple: false, + }; + + const expectedCode = 200; + + try { + const res = await rp(options); + assert.is(res.statusCode, expectedCode); + + const parser = new Parser(); + let quads; + try { + quads = parser.parse(res.body); + } catch (parseError) { + assert.fail(`Turtle parsing failed: ${parseError.message}`); + } + + assert.ok(quads.length > 0, 'Turtle should contain at least one triple'); + + const hasVersionLink = quads.some( + q => q.predicate.value === DatabusUris.DATABUS_HAS_VERSION + ); + + assert.ok( + hasVersionLink, + 'Turtle should contain at least one databus:hasVersion link' + ); + } catch (err) { + assert.is(err.response.statusCode, expectedCode); + } +}); + +test('DELETE: deleting non-empty group returns conflict', async () => { + const options = { + method: 'DELETE', + uri: UriUtils.createResourceUri([test_account.ACCOUNT_NAME, test_account.GROUP_NAME]), + headers: { 'x-api-key': test_account.APIKEY }, + resolveWithFullResponse: true, + }; + + let statusCode = 0; + try { + await rp(options); + } catch (err) { + statusCode = err.statusCode || err.response?.statusCode; + } + assert.is(statusCode, 409); +}); + +test('DELETE: deleting non-empty artifact returns conflict', async () => { + const options = { + method: 'DELETE', + uri: UriUtils.createResourceUri([ + test_account.ACCOUNT_NAME, + test_account.GROUP_NAME, + test_account.ARTIFACT_NAME, + ]), + headers: { 'x-api-key': test_account.APIKEY }, + resolveWithFullResponse: true, + }; + + let statusCode = 0; + try { + await rp(options); + } catch (err) { + statusCode = err.statusCode || err.response?.statusCode; + } + assert.is(statusCode, 409); +}); + +test('DELETE: version can be deleted', async () => { + const options = { + method: 'DELETE', + uri: UriUtils.createResourceUri([ + test_account.ACCOUNT_NAME, + test_account.GROUP_NAME, + test_account.ARTIFACT_NAME, + test_account.VERSION_NAME, + ]), + headers: { 'x-api-key': test_account.APIKEY }, + resolveWithFullResponse: true, + }; + + const res = await rp(options); + assert.is(res.statusCode, 204); +}); + +test('READ: version is gone after deletion', async () => { + await getVersion(404); +}); + +test.after(async () => { + try { + await rp({ + method: 'DELETE', + uri: UriUtils.createResourceUri([ + test_account.ACCOUNT_NAME, + test_account.GROUP_NAME, + test_account.ARTIFACT_NAME, + test_account.VERSION_NAME, + ]), + headers: { 'x-api-key': test_account.APIKEY }, + resolveWithFullResponse: true, + }); + } catch (err) { + if (![204, 404].includes(err.response?.statusCode)) throw err; + } + + try { + await rp({ + method: 'DELETE', + uri: UriUtils.createResourceUri([ + test_account.ACCOUNT_NAME, + test_account.GROUP_NAME, + test_account.ARTIFACT_NAME, + ]), + headers: { 'x-api-key': test_account.APIKEY }, + resolveWithFullResponse: true, + }); + } catch (err) { + if (![204, 404].includes(err.response?.statusCode)) throw err; + } + + try { + await rp({ + method: 'DELETE', + uri: UriUtils.createResourceUri([test_account.ACCOUNT_NAME, test_account.GROUP_NAME]), + headers: { 'x-api-key': test_account.APIKEY }, + resolveWithFullResponse: true, + }); + } catch (err) { + if (![204, 404].includes(err.response?.statusCode)) throw err; + } + + try { + await rp({ + method: 'POST', + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/api/account/delete`, + headers: { 'x-api-key': master_account.APIKEY }, + json: true, + resolveWithFullResponse: true, + body: { accountName: test_account.ACCOUNT_NAME }, + }); + } catch (err) { + assert.is(err.response?.statusCode, 404); + } + + await DatabusUserTestUtils.deleteUser(db, master_account); +}); + +test.run(); diff --git a/server/app/tests/test.webdav.js b/server/app/tests/test.webdav.js new file mode 100644 index 00000000..6fa5915b --- /dev/null +++ b/server/app/tests/test.webdav.js @@ -0,0 +1,95 @@ +const { suite } = require('uvu'); +const assert = require('uvu/assert'); +const rp = require('request-promise'); +const fs = require('fs'); + +const test = suite('webDAV'); + +const DatabusUserTestUtils = require('./utils/userdb-utils'); +const DatabusUserDatabase = require('../../userdb'); +const DatabusWebDAV = require('../api/webdav'); +const test_account = require('./templates/test-account.json'); +const ServerUtils = require('../common/utils/server-utils'); + +/** @type {DatabusUserDatabase} */ +let db; + +/** @type {import('../api/webdav')} */ +let dav; + +/** @type {string} */ +let userDavDirectory; + +test.before(async () => { + + ServerUtils.setupRequireExtensions(); + + db = new DatabusUserDatabase(); + await db.connect(); + + await DatabusUserTestUtils.insertAccount(db, test_account); + + dav = new DatabusWebDAV(); + userDavDirectory = `${dav.directory}${test_account.ACCOUNT_NAME}`; + + if (fs.existsSync(userDavDirectory)) { + fs.rmSync(userDavDirectory, { recursive: true, force: true }); + } +}); + +test('MKCOL creates directory', async () => { + const options = { + method: 'MKCOL', + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/dav/${test_account.ACCOUNT_NAME}/test/`, + headers: { + 'x-api-key': test_account.APIKEY, + }, + }; + + const response = await rp(options); + assert.is(response, ''); + assert.ok(fs.existsSync(userDavDirectory)); +}); + +test('PUT uploads a file', async () => { + const payload = JSON.stringify({ success: true }); + + const options = { + method: 'PUT', + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/dav/${test_account.ACCOUNT_NAME}/test/upload.json`, + headers: { + 'x-api-key': test_account.APIKEY, + }, + body: payload, + }; + + const response = await rp(options); + assert.is(response, ''); + + assert.ok(fs.existsSync(`${userDavDirectory}/test/upload.json`)); +}); + +test('DELETE empties directory', async () => { + const options = { + method: 'DELETE', + uri: `${process.env.DATABUS_RESOURCE_BASE_URL}/dav/${test_account.ACCOUNT_NAME}/test/`, + headers: { + 'x-api-key': test_account.APIKEY, + }, + }; + + const response = await rp(options); + assert.is(response, ''); + + const files = fs.existsSync(userDavDirectory) ? fs.readdirSync(userDavDirectory) : []; + assert.is(files.length, 0); +}); + +test.after(async () => { + await DatabusUserTestUtils.deleteUser(db, test_account); + if (fs.existsSync(userDavDirectory)) { + fs.rmSync(userDavDirectory, { recursive: true, force: true }); + } +}); + +test.run(); diff --git a/server/app/tests/utils/request-utils.js b/server/app/tests/utils/request-utils.js new file mode 100644 index 00000000..9f00befa --- /dev/null +++ b/server/app/tests/utils/request-utils.js @@ -0,0 +1,52 @@ +const rp = require('request-promise'); + +class TestRequestUtils { + + static deleteOptions(uri, apikey) { + return { + method: 'DELETE', + uri: uri, + headers: { 'x-api-key': apikey }, + resolveWithFullResponse: true, + }; + } + + static postOptions(uri, apikey) { + return { + method: 'DELETE', + uri: uri, + headers: { 'x-api-key': apikey }, + resolveWithFullResponse: true, + }; + } + + static createOptions( + uri, + method = 'GET', + apiKey = null, + json = true, + body = null, + accept = 'application/json', + extraHeaders = {} + ) { + const headers = { + Accept: accept, + ...extraHeaders, + }; + + if (apiKey) { + headers['x-api-key'] = apiKey; + } + + return { + "method": method, + "uri": uri, + "headers": headers, + "json": true, + "body": body, + "resolveWithFullResponse": true, + }; + } +} + +module.exports = TestRequestUtils; diff --git a/server/app/tests/utils/userdb-utils.js b/server/app/tests/utils/userdb-utils.js new file mode 100644 index 00000000..3d113bcf --- /dev/null +++ b/server/app/tests/utils/userdb-utils.js @@ -0,0 +1,39 @@ +const DatabusUserDatabase = require("../../../userdb"); +const assert = require('assert'); + +/** + * @class DatabusUserTestUtils + */ +class DatabusUserTestUtils { + + /** + * @param {DatabusUserDatabase} db + * @param {{ID: string, ACCOUNT_NAME: string, KEYNAME: string, APIKEY: string}} params + * @returns {Promise} + */ + static async insertAccount(db, params) { + await db.addUser(params.ID); + await db.addAccount(params.ID, params.ACCOUNT_NAME); + await db.addApiKey(params.ACCOUNT_NAME, params.KEYNAME, params.APIKEY); + } + + /** + * @param {DatabusUserDatabase} db + * @param {{ID: string, ACCOUNT_NAME: string, KEYNAME: string, APIKEY: string}} params + * @returns {Promise} + */ + static async insertApiKey(db, params) { + await db.addApiKey(params.ACCOUNT_NAME, params.KEYNAME, params.APIKEY); + } + + /** + * @param {DatabusUserDatabase} db + * @param {{ID: string, ACCOUNT_NAME: string, KEYNAME: string, APIKEY: string}} params + * @returns {Promise} + */ + static async deleteUser(db, params) { + await db.deleteUser(params.ID); + } +} + +module.exports = DatabusUserTestUtils; diff --git a/server/app/pages/facet-metadata.json b/server/app/webapp/facet-metadata.json similarity index 100% rename from server/app/pages/facet-metadata.json rename to server/app/webapp/facet-metadata.json diff --git a/server/app/pages/module.js b/server/app/webapp/module.js similarity index 100% rename from server/app/pages/module.js rename to server/app/webapp/module.js diff --git a/server/app/pages/modules/account-page.js b/server/app/webapp/modules/account-page.js similarity index 87% rename from server/app/pages/modules/account-page.js rename to server/app/webapp/modules/account-page.js index 28511467..85a2637f 100644 --- a/server/app/pages/modules/account-page.js +++ b/server/app/webapp/modules/account-page.js @@ -6,30 +6,35 @@ var DatabusCache = require('../../common/cache/databus-cache'); const { executeAsk } = require('../../common/execute-query'); const DatabusConstants = require('../../../../public/js/utils/databus-constants'); const UriUtils = require('../../common/utils/uri-utils'); +const DatabusProtect = require('../../common/protect/middleware.js'); var cache = new DatabusCache(15); +/** + * + * @param {*} router + * @param {DatabusProtect} protector + */ module.exports = function (router, protector) { - router.get('/app/account', protector.protect(), async function (req, res, next) { + router.get('/app/user', protector.protect(), async function (req, res, next) { + + let userdb = protector.userdb; + let userId = req.databus.userId; var auth = ServerUtils.getAuthInfoFromRequest(req); - console.log(auth); + let accounts = await userdb.getAccountsById(userId); - if (auth.info.accountName == undefined) { - // Let the user create an account here - res.render('account', { - title: 'Create Profile', - data: { - auth: auth - } - }); - return; + console.log(accounts); - } else { - res.redirect('/' + auth.info.accountName); - } + res.render('user-settings', { + title: 'User Settings', + data: { + auth: auth, + accounts: accounts, + } + }); }); router.get('/app/account/stats', async function (req, res, next) { diff --git a/server/app/pages/modules/collection-editor.js b/server/app/webapp/modules/collection-editor.js similarity index 100% rename from server/app/pages/modules/collection-editor.js rename to server/app/webapp/modules/collection-editor.js diff --git a/server/app/webapp/modules/publish-wizard.js b/server/app/webapp/modules/publish-wizard.js new file mode 100644 index 00000000..b1798c0e --- /dev/null +++ b/server/app/webapp/modules/publish-wizard.js @@ -0,0 +1,162 @@ +const axios = require('axios'); +const cheerio = require('cheerio'); +const ServerUtils = require('../../common/utils/server-utils.js'); + +module.exports = function (router, protector) { + require('../../common/file-analyzer').route(router, protector); + + router.get('/app/publish-wizard/licenses', protector.protect(), async function(req, res, next) { + const search = new URL(`${req.protocol}://${req.get('host')}${req.originalUrl}`).search; + + try { + const daliccRes = await axios.get(`https://api.dalicc.net/licenselibrary/list${search}`, { + headers: { accept: 'application/json' } + }); + res.status(200).send(daliccRes.data); + } catch (err) { + console.log(err); + res.status(500).send("DALICC SERVICE APPEARS TO BE DOWN!"); + } + }); + + router.get('/app/publish-wizard', protector.protect(), async function (req, res, next) { + try { + const auth = ServerUtils.getAuthInfoFromRequest(req); + const texts = require('../publish-wizard-texts.json'); + + res.render('publish-wizard', { + title: 'Publish Data', + data: { auth: auth, texts: texts } + }); + } catch (err) { + console.log(err); + res.status(500).send(err); + } + }); + + router.get('/app/publish-wizard/fetch-resource-page', async function (req, res, next) { + try { + const result = await fetchLinksRecursive(req.query.url, '', 0); + res.status(200).send(result); + } catch (err) { + console.log(err); + res.status(500).send(err); + } + }); + + router.get('/app/publish-wizard/fetch-file', async function (req, res, next) { + try { + const url = req.query.url; + const result = await fetchFile(url); + res.status(200).send(result); + } catch (err) { + console.log(err); + res.status(500).send(err); + } + }); + + async function fetchFile(url) { + if (url.includes('://localhost') || url.startsWith('https://databus.dbpedia.org')) { + return null; + } + + if (url.startsWith('http') + && url.lastIndexOf('/') < url.lastIndexOf('.') + && url.lastIndexOf('/') < url.indexOf('.')) { + return null; + } + + if (url.startsWith("https://github.com/")) { + url = transformGithubUrl(url); + } + + try { + const response = await axios.head(url); + const headers = response.headers; + + const result = { url: url }; + + if (headers['content-disposition']) { + const filename = getFileNameFromDisposition(headers['content-disposition']); + parseFormatAndCompression(result, filename); + return result; + } + + if (url.lastIndexOf('/') < url.lastIndexOf('.')) { + parseFormatAndCompression(result, url); + return result; + } + + return null; + } catch (err) { + console.log(err); + return null; + } + } + + function getFileNameFromDisposition(disposition) { + const entries = disposition.split(' '); + for (let entry of entries) { + if (entry.startsWith('filename=')) { + return entry.split('=')[1].replace(/(^")|("$)|(;$)|(";$)/g, ""); + } + } + return undefined; + } + + function transformGithubUrl(url) { + url = url.replace("/blob/", "/"); + url = url.replace("https://github.com/", "https://raw.githubusercontent.com/"); + return url; + } + + function parseFormatAndCompression(result, value) { + if (!value) return; + + let nameComponents = value.split('/'); + nameComponents = nameComponents[nameComponents.length - 1].split('.'); + + if (nameComponents[nameComponents.length - 1].includes('#')) { + nameComponents[nameComponents.length - 1] = nameComponents[nameComponents.length - 1].split('#')[0]; + } + + if (nameComponents.length > 2) { + result.compression = nameComponents[nameComponents.length - 1]; + result.formatExtension = nameComponents[nameComponents.length - 2]; + } else if (nameComponents.length > 1) { + result.compression = 'none'; + result.formatExtension = nameComponents[nameComponents.length - 1]; + } else { + result.compression = 'none'; + result.formatExtension = 'none'; + } + } + + async function fetchLinksRecursive(baseUrl, path, depth) { + const result = []; + try { + const url = baseUrl + path; + const response = await axios.get(url, { + headers: { Accept: 'text/html' } + }); + const $ = cheerio.load(response.data); + const hrefs = getFilteredHrefs(baseUrl + path, $); + for (const href of hrefs) { + result.push(href); + } + } catch (err) { + console.log(err); + } + return result; + } + + function getFilteredHrefs(baseUrl, $) { + const result = []; + const links = $('a'); + $(links).each(function (i, link) { + const href = new URL($(link).attr('href'), baseUrl).href; + result.push(href); + }); + return result; + } +} diff --git a/server/app/pages/modules/resource-pages.js b/server/app/webapp/modules/resource-pages.js similarity index 94% rename from server/app/pages/modules/resource-pages.js rename to server/app/webapp/modules/resource-pages.js index 5b080cd9..cb542420 100644 --- a/server/app/pages/modules/resource-pages.js +++ b/server/app/webapp/modules/resource-pages.js @@ -5,7 +5,6 @@ const DatabusCache = require('../../common/cache/databus-cache'); const ServerUtils = require('../../common/utils/server-utils.js'); const Constants = require('../../common/constants'); const UriUtils = require('../../common/utils/uri-utils'); -const rp = require('request-promise'); const JsonldUtils = require('../../../../public/js/utils/jsonld-utils'); const DatabusUris = require('../../../../public/js/utils/databus-uris'); const { dataid } = require('../../common/queries/sparql'); @@ -18,7 +17,13 @@ const versionQueryTemplate = require("../../common/queries/constructs/ld/constru const AppJsonFormatter = require('../../../../public/js/utils/app-json-formatter'); const DatabusConstants = require('../../../../public/js/utils/databus-constants'); +const DatabusProtect = require('../../common/protect/middleware.js'); +/** + * + * @param {*} router + * @param {DatabusProtect} protector + */ module.exports = function (router, protector) { var cache = new DatabusCache(10); @@ -37,13 +42,24 @@ module.exports = function (router, protector) { } var accountData = AppJsonFormatter.formatAccountData(accountJsonLd); + let ownerData = null; + + try { + let userId = req.databus.userId; + let accounts = await protector.userdb.getAccountsById(userId); + ownerData = accounts.find(a => a.accountName === req.params.account); + } catch(err) { + console.log(err); + + } res.render('account', { title: accountData.label, data: { auth: auth, username: req.params.account, - profile: accountData, + account: accountData, + owner: ownerData } }); diff --git a/server/app/pages/modules/sparql-editor.js b/server/app/webapp/modules/sparql-editor.js similarity index 100% rename from server/app/pages/modules/sparql-editor.js rename to server/app/webapp/modules/sparql-editor.js diff --git a/server/app/pages/publish-wizard-texts.json b/server/app/webapp/publish-wizard-texts.json similarity index 83% rename from server/app/pages/publish-wizard-texts.json rename to server/app/webapp/publish-wizard-texts.json index 18a910b3..7c64baf0 100644 --- a/server/app/pages/publish-wizard-texts.json +++ b/server/app/webapp/publish-wizard-texts.json @@ -21,11 +21,8 @@ ] }, "errors": { - "err_invalid_group_description": "The group description is invalid. Please enter at least 25 characters.", - "err_invalid_group_label": "The group label is invalid. Please enter at least 3 characters.", - "err_invalid_group_title": "The group name is invalid. Please enter between 3 to 50 characters. \nRegex: [a-zA-Z0-9_\\-\\.]{3,50}$", - "err_invalid_artifact_title": "The artifact name is invalid. Please enter between 3 to 50 characters. \nRegex: [a-zA-Z0-9_\\-\\.]{3,50}$", - "err_invalid_artifact_label": "The artifact label is invalid. Please enter at least 3 characters.", + "err_invalid_group_name": "The group name is invalid. Please enter between 3 to 50 characters. \nRegex: [a-zA-Z0-9_\\-\\.]{3,50}$", + "err_invalid_artifact_name": "The artifact name is invalid. Please enter between 3 to 50 characters. \nRegex: [a-zA-Z0-9_\\-\\.]{3,50}$", "err_invalid_version_name": "The version name is invalid. Please enter at least 3 characters.", "err_invalid_version_title": "The version title is missing.", "err_invalid_version_abstract": "The version abstract is missing.", diff --git a/server/config.json b/server/config.json index 24523c0b..e9b46d8f 100644 --- a/server/config.json +++ b/server/config.json @@ -1,5 +1,5 @@ { - "version" : "2.1.0-rc.10", + "version" : "2.1.1", "defaultColors": { "banner": "#81b8b2" } diff --git a/server/init.js b/server/init.js index aa076c8a..3453df1f 100644 --- a/server/init.js +++ b/server/init.js @@ -1,16 +1,11 @@ // External includes var fs = require('fs'); -var rp = require('request-promise'); const crypto = require("crypto"); const Constants = require('./app/common/constants.js'); var config = require('./config.json'); const DatabusUserDatabase = require('./userdb.js'); -const DatabusConstants = require('../public/js/utils/databus-constants.js'); -const UriUtils = require('./app/common/utils/uri-utils.js'); -const { executeAsk } = require('./app/common/execute-query.js'); -const publishAccount = require('./app/api/lib/publish-account.js'); -const AppJsonFormatter = require('../public/js/utils/app-json-formatter.js'); - +const MetricsManager = require('./app/api/statistics/metrics-manager.js'); +const JsonldLoader = require('./app/common/utils/jsonld-loader.js'); function writeManifest() { @@ -84,17 +79,24 @@ async function initializeShacl() { 'account', 'artifact', 'collection', 'version', 'group' ]; - for(var file of shaclFiles) { + for (var file of shaclFiles) { var shacl = require(`../model/generated/shacl/${file}.shacl`); var shaclFile = `${__dirname}/app/common/res/shacl/${file}.shacl`; - console.log(`Creating SHACL resource }/app/common/res/shacl/${file}.shacl`); + console.log(`Creating SHACL resource /app/common/res/shacl/${file}.shacl`); fs.writeFileSync(shaclFile, shacl, "utf8"); } } async function initializeContext() { + var defaultContextUrl = `${process.env.DATABUS_RESOURCE_BASE_URL}${Constants.DATABUS_DEFAULT_CONTEXT_PATH}` + + console.log(`Using self-hosted jsonld context at ${defaultContextUrl}...`); + process.env.DATABUS_CONTEXT_URL = defaultContextUrl; + + + /* // Load the internal default context var context = require('../model/generated/context.json'); var hasContext = false; @@ -147,49 +149,68 @@ async function initializeContext() { console.log(``); fs.writeFileSync(contextFile, contextString, "utf8"); console.log(`Successfully saved context to ${contextFile}:`); + */ } -async function initializeUserDatabase() { - console.log(`Connecting to User Databse...`); - var userDatabase = new DatabusUserDatabase(); - await userDatabase.connect(); - - console.log(`Verifying user account integrity`); +async function waitForService(url, maxAttempts = 10, delayMs = 1000) { + const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); - for(var user of await userDatabase.getUsers()) { - var profileUri = `${UriUtils.createResourceUri([user.accountName])}${DatabusConstants.WEBID_DOCUMENT}`; - var exists = await executeAsk(`ASK { <${profileUri}> ?p ?o }`); + for (let attempt = 1; attempt <= maxAttempts; attempt++) { + try { + const response = await fetch(url, { method: 'HEAD' }); + if (response.ok) { + console.log(`Service is online at ${url} (attempt ${attempt})`); + return true; + } + } catch (err) { + // Could log or ignore depending on use case + } - if (!exists) { - // Redirect to the specific account page - console.log(`No profile found for user ${user.accountName}. Creating profile...`); - - var accountJsonLd = AppJsonFormatter.createAccountData( - process.env.DATABUS_RESOURCE_BASE_URL, - user.accountName, - user.accountName, - null, - null); + console.log(`Attempt ${attempt} failed. Retrying in ${delayMs}ms...`); + await delay(delayMs); + } - await publishAccount(user.accountName, accountJsonLd); + console.error(`Service at ${url} did not come online after ${maxAttempts} attempts.`); + return false; +} - console.log(`Created new default profile for user ${user.accountName}`); - } - } +async function initializeUserDatabase(indexer) { + console.log(`Connecting to User Database...`); + var userDatabase = new DatabusUserDatabase(); + await userDatabase.connect(); } -module.exports = async function () { +module.exports = async function (indexer) { console.log(`Initializing...`); console.log(config); - await initializeUserDatabase(); - await initializeShacl(); + console.log(`Waiting for gstore service...`); + + await initializeContext(); + + // Ping process.env.DATABUS_DATABASE_URL + // await waitForService(process.env.DATABUS_DATABASE_URL, 50, 1000); + + if (process.env.METRICS_PORT != undefined) { + console.log(`Settings up Prometheus metrics...`); + MetricsManager.initialize(); + } + + + JsonldLoader.initialize(); + + await initializeUserDatabase(indexer); + + // await initializeShacl(); + + // initializeJsonLd(); + writeManifest(); tryCreateKeyPair(); diff --git a/server/package.json b/server/package.json index bf65995d..5777be8d 100644 --- a/server/package.json +++ b/server/package.json @@ -4,43 +4,46 @@ "private": true, "scripts": { "start": "node ./www", - "test": "DATABUS_TEST=true node ./www" + "test": "node ./app/test.runner.js" }, "dependencies": { "@braintree/sanitize-url": "^6.0.0", "@rdfjs/parser-jsonld": "^1.2.1", "@rdfjs/parser-n3": "^1.1.4", "asn1js": "^2.1.1", - "axios": "^0.21.1", - "body-parser": "^1.20.2", + "axios": "^1.7.9", + "body-parser": "^2.2.0", "cheerio": "^1.0.0-rc.9", "child-process": "^1.0.2", "cookie-parser": "~1.4.4", "cookies": "^0.8.0", "cors": "^2.8.5", "debug": "~2.6.9", - "ejs": ">=3.1.7", - "express": "^4.16.4", - "express-openid-connect": "^2.4.0", + "ejs": ">=3.1.9", + "express": "^5.1.0", + "express-openid-connect": "^2.17.0", "express-session": "^1.17.2", + "form-data": "^4.0.1", "get-random-values": "^1.2.2", "glob": "^7.1.7", "glob-promise": "^4.2.0", "http-errors": "~1.6.3", "js-sha256": "^0.9.0", - "jsonld": "^5.2.0", + "jsonld": "^8.3.3", "jsonld-signatures": "^9.0.2", + "jsonwebtoken": "^9.0.2", "memorystore": "^1.6.6", "morgan": "~1.9.1", "node-cache": "^4.2.0", "node-rsa": "^1.1.1", - "openid-client": "^3.10.0", + "openid-client": "^5.6.5", "pem-jwk": "^2.0.0", + "prom-client": "^15.1.3", + "proxy-agent": "^6.4.0", "rdf-ext": "^1.3.1", "rdf-parse": "^1.8.0", "rdf-validate-shacl": "^0.3.2", "rdfstore": "^0.9.17", - "request": "^2.88.0", "request-digest": "^1.0.13", "request-promise": "^4.2.4", "serve-favicon": "^2.5.0", @@ -51,11 +54,15 @@ "streamify-string": "^1.0.1", "swagger-ui-express": "^4.3.0", "terser": "^5.7.0", + "vitest": "^3.2.4", "webdav-server": "^2.6.2", "yamljs": "^0.3.0" }, "main": "app.js", "author": "", "license": "ISC", - "description": "" + "description": "", + "devDependencies": { + "uvu": "^0.5.6" + } } diff --git a/server/search-indexer.js b/server/search-indexer.js deleted file mode 100644 index 103fa92b..00000000 --- a/server/search-indexer.js +++ /dev/null @@ -1,138 +0,0 @@ -const { DateTime } = require('asn1js'); -var { spawn } = require('child_process'); -var fs = require('fs'); - -class LookupSearchIndexer { - - constructor(tickRate) { - this.tickRate = tickRate; - this.rebuildRequested = true; - this.configPath = `../search/index-config.yml`; - - var content = fs.readFileSync(this.configPath, ['utf8']).toString(); - var regex = new RegExp(`sparqlEndpoint:\\s(.*)`, `g`); - content = content.replace(regex, `sparqlEndpoint: ${process.env.DATABUS_DATABASE_URL}/sparql`); - fs.writeFileSync(this.configPath, content, ['utf8']); - - this.resources = []; - } - - async start() { - - await this.buildIndex(); - - this.iid = setInterval(this.tick.bind(this), this.tickRate); - var self = this; - - this.servletProcess = spawn('java', [ - '-jar', '../search/lookup-server.jar', - '-c', `../search/servlet-config.yml` - ]); - - this.servletProcess.stdout.on('data', (data) => { - self.log(`[SERVER] - ${data}`); - }); - - this.servletProcess.stderr.on('data', (data) => { - self.log(`[SERVER ERROR] - ${data}`); - }); - } - - - tick() { - if (this.rebuildRequested) { - - if (!this.isBuilding) { - this.rebuildRequested = false; - - this.log(`Rebuilding with ${this.resources.length} resource(s)`); - this.buildIndex(this.resources); - this.resources = []; - } - } - } - - log(msg) { - console.log('\x1b[90m%s\x1b[0m', `[SEARCH] ${msg}`); - } - - requestRebuild(resource) { - this.rebuildRequested = true; - - if(resource != undefined && !this.resources.includes(resource)) { - this.resources.push(resource); - } - } - - async buildIndex(resources) { - this.isBuilding = true; - await this.buildIndexPromise(resources); - this.isBuilding = false; - } - - buildIndexPromise(resources) { - - var self = this; - - return new Promise((resolve, reject) => { - - var processArgs = [ - '-jar', '../search/lookup-indexer.jar', - '-conf', this.configPath, - ]; - - if(resources != null && resources.length > 0) { - processArgs.push('-r'); - processArgs.push(resources.join(' ')); - } - - - var indexingProcess = spawn('java', processArgs); - - // Uncomment to debug search indexer - //indexingProcess.stdout.on('data', (data) => { - // self.log(`[INDEXER] - ${data}`); - //}); - - indexingProcess.stderr.on('data', (data) => { - self.log(`[INDEXER ERROR] - ${data}`); - }); - - indexingProcess.on('close', (code) => { - if (code == 0) { - resolve(code); - } else { - reject(code); - } - }); - }); - } - - promisedRedeploy() { - var self = this; - return new Promise((resolve, reject) => { - var touchProcess = spawn('touch', ['/usr/local/tomcat/webapps/lookup-application.war']); - touchProcess.on('close', (code) => { - - self.lastRebuildTime = new Date(); - if (code == 0) { - resolve(code); - } else { - reject(code); - } - }); - }); - } - - async rebuildAndRedeploy() { - try { - var result = await this.promisedRebuild(); - this.log('Search Index creation finished with code ' + result); - } catch (err) { - this.log('Search Index creation failed: ' + err); - } - } - -} - -module.exports = LookupSearchIndexer; diff --git a/server/setup.sh b/server/setup.sh new file mode 100644 index 00000000..7361ffc2 --- /dev/null +++ b/server/setup.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Wait for the database container to be available +echo "Waiting for database at ${DATABUS_DATABASE_URL}..." +while ! curl -s -o /dev/null -w '%{http_code}' ${DATABUS_DATABASE_URL}/sparql | grep -q "200"; do + sleep 5 +done +echo "Database is up and running." + +# Start the app +cd /databus/server +npm start diff --git a/server/userdb.js b/server/userdb.js index c04ae816..50517986 100644 --- a/server/userdb.js +++ b/server/userdb.js @@ -3,6 +3,7 @@ const sqlite3 = require('sqlite3'); const { open } = require('sqlite'); const ServerUtils = require('./app/common/utils/server-utils.js'); const Constants = require('./app/common/constants'); +const { log } = require('console'); class DatabusUserDatabase { @@ -11,20 +12,55 @@ class DatabusUserDatabase { this.addUserQuery = require('./app/common/queries/userdb/add-user.sql'); this.addUserQueryPrefix = this.addUserQuery.substring(0, 10); this.addApiKeyQuery = require('./app/common/queries/userdb/add-api-key.sql'); - this.getUserQuery = require('./app/common/queries/userdb/get-user.sql'); + this.getAccountsByIdQuery = require('./app/common/queries/userdb/get-accounts-by-id.sql'); + this.addAccountQuery = require('./app/common/queries/userdb/add-account.sql'); + this.getAccountsQuery = require('./app/common/queries/userdb/get-accounts.sql'); this.getUsersQuery = require('./app/common/queries/userdb/get-users.sql'); this.getUserByAccountNameQuery = require('./app/common/queries/userdb/get-user-by-account-name.sql'); - this.getSubQuery = require('./app/common/queries/userdb/get-sub.sql'); + this.getAccountQuery = require('./app/common/queries/userdb/get-account.sql'); + this.getApiKeyQuery = require('./app/common/queries/userdb/get-account-by-api-key.sql'); + this.deleteAccountQuery = require('./app/common/queries/userdb/delete-account.sql'); + this.getUserQuery = require('./app/common/queries/userdb/get-user.sql'); this.deleteUserQuery = require('./app/common/queries/userdb/delete-user.sql'); this.deleteApiKeyQuery = require('./app/common/queries/userdb/delete-api-key.sql'); this.getApiKeysQuery = require('./app/common/queries/userdb/get-api-keys.sql'); + + this.debug = false; } onTrace(query) { - if(query.startsWith(this.addUserQueryPrefix)) { + if (query.startsWith(this.addUserQueryPrefix)) { console.log(query); } } + async migrateSubToId() { + try { + + const columns = await this.db.all(`PRAGMA table_info(users)`); + const usersHasSub = columns.some(col => col.name === 'sub'); + const usersHasId = columns.some(col => col.name === 'id'); + + + if (usersHasSub && !usersHasId) { + await this.db.run(`ALTER TABLE users RENAME COLUMN sub TO id`); + await this.db.run(`ALTER TABLE users ADD COLUMN email VARCHAR(255) NOT NULL DEFAULT ''`); + console.log("Migrated 'users.sub' column to 'id' and added 'email' column."); + } + + const accountColumns = await this.db.all(`PRAGMA table_info(accounts)`); + const accountHasSub = accountColumns.some(col => col.name === 'sub'); + const accountHasId = accountColumns.some(col => col.name === 'id'); + + if (accountHasSub && !accountHasId) { + await this.db.run(`ALTER TABLE accounts RENAME COLUMN sub TO id`); + console.log("Migrated 'account.sub' to 'id'."); + } + + } catch (err) { + console.log("Migration error:", err); + } + } + async connect() { @@ -38,13 +74,20 @@ class DatabusUserDatabase { driver: sqlite3.Database }); - if(this.userAddedCallback != undefined) { + if (this.userAddedCallback != undefined) { this.db.on('trace', this.onTrace); } + if (this.debug) { + console.log("Creating tables"); + } + + await this.migrateSubToId(); + await this.db.get("PRAGMA foreign_keys = ON"); await this.db.run(require('./app/common/queries/userdb/create-user-table.sql')); await this.db.run(require('./app/common/queries/userdb/create-api-key-table.sql')); + await this.db.run(require('./app/common/queries/userdb/create-account-table.sql')); console.log(`Connected to user database at ${__dirname + Constants.DATABUS_SQLITE_USER_DATABASE_PATH}.`); return true; @@ -58,27 +101,40 @@ class DatabusUserDatabase { /** * Retrieve a user - * @param {*} sub + * @param {*} id * @returns */ - async getUser(sub) { - return await this.get(this.getUserQuery, { - SUB: sub, + async getAccountsById(id) { + let result = await this.all(this.getAccountsByIdQuery, { + ID: id, }); + + if (result == undefined) { + return []; + } + + for (let account of result) { + account.apiKeys = await this.getApiKeys(account.accountName); + } + + return result; } /** * Retrieve all users - * @param {*} sub + * @param {*} id * @returns */ - async getUsers() { + async getAllUsers() { return await this.all(this.getUsersQuery, null); } + async getAllAccounts() { + return await this.all(this.getAccountsQuery, null); + } /** * Retrieve a user ny account name - * @param {*} sub + * @param {*} id * @returns */ async getUserByAccountName(accountName) { @@ -87,27 +143,54 @@ class DatabusUserDatabase { }); } - /** - * Retrieve a sub string by apikey - * @param {*} apikey - * @returns - */ - async getSub(apikey) { - return await this.get(this.getSubQuery, { + async hasUser(accountName) { + var user = await this.getUserByAccountName(accountName); + return user != null; + } + + async hasAccount(accountName) { + var account = await this.getAccount(accountName); + return account != null; + } + + async getAccount(accountName) { + return await this.get(this.getAccountQuery, { + ACCOUNT_NAME: accountName + }); + } + + async getApiKey(apikey) { + return await this.get(this.getApiKeyQuery, { APIKEY: apikey }); } + + async getUser(id) { + return await this.get(this.getUserQuery, { + ID: id + }); + } + /** * Adds an API key to a user - * @param {} sub + * @param {} id * @param {*} apikey * @param {*} debugLog * @returns */ - async addApiKey(sub, name, apikey) { + async addApiKey(accountName, name, apikey) { + + let account = await this.getAccount(accountName); + if (account == undefined) { + return false; + //if(!await this.addUser(id)) { + // return false; + //} + } + var result = await this.run(this.addApiKeyQuery, { - SUB: sub, + ACCOUNTNAME: accountName, KEYNAME: name, APIKEY: apikey }); @@ -115,20 +198,20 @@ class DatabusUserDatabase { return result != null && result.changes != 0; } - async getApiKeys(sub) { + async getApiKeys(accountName) { return await this.all(this.getApiKeysQuery, { - SUB: sub, + ACCOUNTNAME: accountName, }); } /** * Delete api key - * @param {*} sub + * @param {*} id * @returns */ - async deleteApiKey(sub, name) { + async deleteApiKey(accountName, name) { var result = await this.run(this.deleteApiKeyQuery, { - SUB: sub, + ACCOUNTNAME: accountName, NAME: name, }); @@ -137,29 +220,67 @@ class DatabusUserDatabase { /** * Adds a user - * @param {*} sub - * @param {*} email - * @param {*} accountName + * @param {*} id * @returns */ - async addUser(sub, displayName, accountName) { + async addUser(id, email) { + var result = await this.run(this.addUserQuery, { - SUB: sub, - DISPLAYNAME: displayName, + ID: id, + EMAIL: email + }); + + return result != null && result.changes != 0; + } + + /** + * Adds an account + * @param {*} id + * @param {*} label + * @param {*} accountName + * @returns + */ + async addAccount(id, accountName) { + + if (this.debug) { + console.log(`ADD USER id:${id}, accountName:${accountName}`); + } + + let user = await this.getUser(id); + + if (user == undefined) { + if (!await this.addUser(id)) { + return false; + } + } + + var result = await this.run(this.addAccountQuery, { + ID: id, + ACCOUNT_NAME: accountName + }); + + return result != null && result.changes != 0; + } + + async deleteAccount(accountName) { + + var result = await this.run(this.deleteAccountQuery, { ACCOUNT_NAME: accountName }); + return result != null && result.changes != 0; + } /** * Delete user - * @param {*} sub + * @param {*} id * @returns */ - async deleteUser(sub) { + async deleteUser(id) { var result = await this.run(this.deleteUserQuery, { - SUB: sub, + ID: id }); return result != null && result.changes != 0; @@ -170,7 +291,8 @@ class DatabusUserDatabase { for (var key in params) { var value = params[key]; - if(value.includes("\"") || value.includes(";")) { + + if (value != null && (value.includes("\"") || value.includes(";"))) { return true; } } @@ -187,7 +309,11 @@ class DatabusUserDatabase { async run(query, params) { try { - if(this.isInputDangerous(params)) { + if (this.debug) { + console.log(JSON.stringify(params, null, 3)); + } + + if (this.isInputDangerous(params)) { if (this.debug) { console.log(`USERDB: Dangerous database input detected: ${JSON.stringify(params)}`); @@ -223,7 +349,7 @@ class DatabusUserDatabase { try { - if(this.isInputDangerous(params)) { + if (this.isInputDangerous(params)) { if (this.debug) { console.log(`USERDB: Dangerous database input detected: ${JSON.stringify(params)}`); @@ -239,6 +365,7 @@ class DatabusUserDatabase { } return await this.db.get(formattedQuery); + } catch (err) { if (this.debug) { @@ -258,7 +385,7 @@ class DatabusUserDatabase { async all(query, params) { try { - if(this.isInputDangerous(params)) { + if (this.isInputDangerous(params)) { if (this.debug) { console.log(`USERDB: Dangerous database input detected: ${JSON.stringify(params)}`); diff --git a/server/validate.js b/server/validate.js new file mode 100644 index 00000000..c0e74f2c --- /dev/null +++ b/server/validate.js @@ -0,0 +1,114 @@ +// External includes +const Constants = require('./app/common/constants.js'); +const DatabusUserDatabase = require('./userdb.js'); +const DatabusConstants = require('../public/js/utils/databus-constants.js'); +const UriUtils = require('./app/common/utils/uri-utils.js'); +const { executeAsk, executeSelect } = require('./app/common/execute-query.js'); +const AppJsonFormatter = require('../public/js/utils/app-json-formatter.js'); +const AccountWriter = require('./app/api/lib/account-writer.js'); +const DatabusLogger = require('./app/common/databus-logger.js'); +const DatabusLogLevel = require('./app/common/databus-log-level.js'); +const DatabusUris = require('../public/js/utils/databus-uris.js'); +const GstoreResource = require('./app/api/lib/gstore-resource.js'); +const ServerUtils = require('./app/common/utils/server-utils.js'); + + +async function verifyAccountIntegrity(indexer) { + + console.log(`Verifying user account integrity`); + var userDatabase = new DatabusUserDatabase(); + await userDatabase.connect(); + + for (var account of await userDatabase.getAllAccounts()) { + + try { + var accountUri = `${UriUtils.createResourceUri([account.accountName])}`; + var exists = await executeAsk(`ASK { <${accountUri}> ?p ?o }`); + + if (!exists) { + // Redirect to the specific account page + console.log(`No account found for user ${account.accountName}. Creating account...`); + var accountUri = `${process.env.DATABUS_RESOURCE_BASE_URL}/${account.accountName}`; + + var personUri = `${process.env.DATABUS_RESOURCE_BASE_URL}/${account.accountName}${DatabusConstants.WEBID_THIS}`; + + var hasPerson = await executeAsk(`ASK { <${personUri}> ?p ?o }`); + + let accountLabel = account.accountName; + let accountImg = null; + + if(hasPerson) { + let personInfo = await executeSelect(`SELECT DISTINCT * WHERE { <${personUri}> ?p ?o . }`); + + for(let info of personInfo) { + + if(info.p == DatabusUris.FOAF_NAME) { + accountLabel = info.o; + } + + if(info.p == DatabusUris.FOAF_IMG) { + accountImg = info.o; + } + } + } + + console.log(`Creating account { name: ${account.accountName}, label: ${accountLabel}, img: ${accountImg} }`); + var accountJsonLd = await ServerUtils.createAccountGraphs( + accountUri, account.accountName, accountLabel, accountImg, null, null); + + + let gstoreResource = new GstoreResource(accountUri, accountJsonLd); + await gstoreResource.save(); + + indexer.updateResource(accountUri, 'Account'); + console.log(`Created new default account for user ${account.accountName}`); + } + } catch(err) { + console.log(`Failed to create new default account for user ${account.accountName}`); + } + } +} + +async function waitForService(url, maxAttempts = 10, delayMs = 1000) { + const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + + for (let attempt = 1; attempt <= maxAttempts; attempt++) { + try { + const response = await fetch(url, { method: 'HEAD' }); + if (response.ok) { + return true; + } + } catch (err) { + // Could log or ignore depending on use case + } + + console.log(`Attempt ${attempt} failed. Retrying in ${delayMs}ms...`); + await delay(delayMs); + } + + console.error(`Service at ${url} did not come online after ${maxAttempts} attempts.`); + return false; +} + +module.exports = async function (indexer) { + + try { + + console.log("================================================"); + + console.log(`Waiting for context...`); + var defaultContextUrl = `${process.env.DATABUS_RESOURCE_BASE_URL}${Constants.DATABUS_DEFAULT_CONTEXT_PATH}` + await waitForService(defaultContextUrl, 10, 1000); + + // console.log(`Context available at ${defaultContextUrl}`); + await verifyAccountIntegrity(indexer); + + // TODO: Check availability of manifest + + console.log(`Databus is running!...`); + } + catch(error) { + console.log(`There was an issue during Databus startup!`); + console.log(error); + } +} \ No newline at end of file diff --git a/server/www b/server/www index fb50a8a9..41637b29 100755 --- a/server/www +++ b/server/www @@ -4,20 +4,23 @@ * Module dependencies. */ - initializeExtensions(); -const numCPUs = require('os').cpus().length; + +initializeExtensions(); + +let numCPUs = require('os').cpus().length; const fs = require('fs'); const cluster = require('cluster'); const http = require('http'); -const LookupSearchIndexer = require('./search-indexer'); const DatabusMessage = require('./app/common/databus-message'); var initialize = require('./init'); -var searchIndexer = null; -var config = require('./config.json') -var runTests = require('./app/test/main.js'); -const { log } = require('console'); +var validate = require('./validate'); +var searchIndexer = null; +const IndexManager = require('./app/indexing/index-manager.js'); +const IndexGroup = require('./app/indexing/index-group.js'); +const DatabusResource = require('./app/common/databus-resource.js'); +const MetricsManager = require('./app/api/statistics/metrics-manager.js'); if (cluster.isMaster) { masterProcess(); @@ -48,8 +51,18 @@ function createFork(onListening) { }); } + if (message.id == DatabusMessage.FILE_DOWNLOADED) { + MetricsManager.registerRequest(message.body); + } + if (message.id == DatabusMessage.REQUEST_SEARCH_INDEX_REBUILD) { - searchIndexer.requestRebuild(message.resource); + try { + let databusResource = new DatabusResource(message.resource); + searchIndexer.updateResource(message.resource, databusResource.getTypeName()); + } catch(e) { + console.log("Error rebuilding the search index:"); + console.log(e); + } } }); } @@ -65,47 +78,75 @@ async function masterProcess() { console.log(`###### # # # # # ##### #### ####`); console.log(``) + console.log(`==== 2 ====`) console.log(`================================================`); console.log(``); console.log(`Environment Variables:`); console.log(`------------------------------------------------`); console.log(`DATABUS_RESOURCE_BASE_URL\t${process.env.DATABUS_RESOURCE_BASE_URL}`); + console.log(`DATABUS_DATABASE_URL\t\t${process.env.DATABUS_DATABASE_URL}`); console.log(`DATABUS_NAME\t\t\t${process.env.DATABUS_NAME}`); console.log(`DATABUS_ORG_ICON\t\t${process.env.DATABUS_ORG_ICON}`); console.log(`DATABUS_BANNER_COLOR\t\t${process.env.DATABUS_BANNER_COLOR}`); + console.log(`METRICS_PORT\t\t\t${process.env.METRICS_PORT}`); console.log(``); console.log(`================================================`); console.log(``); + if(process.env.DATABUS_RESOURCE_BASE_URL == null) { + console.log(`Environment variable DATABUS_RESOURCE_BASE_URL not set. Exiting.`); + } + + if(process.env.DATABUS_DATABASE_URL == null) { + console.log(`Environment variable DATABUS_DATABASE_URL not set. Exiting.`); + } + try { console.log(`Starting search service...`) - searchIndexer = new LookupSearchIndexer(5000); + + + let indexGroups = []; + indexGroups.push(new IndexGroup("version", ["../search/indexer/version.yml" ])); + indexGroups.push(new IndexGroup("artifact", ["../search/indexer/artifact.yml" ])); + indexGroups.push(new IndexGroup("group", ["../search/indexer/group.yml" ])); + indexGroups.push(new IndexGroup("account", ["../search/indexer/account.yml" ])); + indexGroups.push(new IndexGroup("collection", ["../search/indexer/collection.yml" ])); + + searchIndexer = new IndexManager(indexGroups, 1000); await searchIndexer.start(); console.log(``); console.log(`================================================`); console.log(``); - await initialize(); + await initialize(searchIndexer); // TODO: Validate startup environment variables console.log(`Number of CPUs is ${numCPUs}`); console.log(`Master ${process.pid} is running`); + if(process.env.MAX_WORKERS != undefined) { + var maxWorkers = parseInt(process.env.MAX_WORKERS); + + if(numCPUs > maxWorkers) { + console.log(`Number of workers restricted to ${maxWorkers} by ENV:MAX_WORKERS.`); + numCPUs = maxWorkers; + } + } + + console.log(`================================================`); var numStartedWorkers = 0; for (let i = 0; i < numCPUs; i++) { console.log(`Forking process number ${i}...`); - createFork(function () { + createFork(async function () { numStartedWorkers++; if (numCPUs == numStartedWorkers) { console.log(`================================================`); console.log(`All workers started.`); - if ( process.env.DATABUS_TEST ){ - console.log('Running tests...'); - runTests(); - } + + await validate(searchIndexer); console.log(`================================================`); } }); @@ -118,6 +159,11 @@ async function masterProcess() { createFork(); }); + if(process.env.METRICS_PORT != null) { + var port = normalizePort(process.env.METRICS_PORT || '3001'); + console.log(`Starting Prometheus metrics server (port ${port})...`); + MetricsManager.startServer(port); + } } catch (err) { console.log(`Initialization error: \n${err}`); diff --git a/setup.sh b/setup.sh deleted file mode 100644 index 25cbef5b..00000000 --- a/setup.sh +++ /dev/null @@ -1,50 +0,0 @@ -echo "Waiting for database at ${DATABUS_DATABASE_URL}..." -bash -c 'while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' ${DATABUS_DATABASE_URL}/sparql)" != "200" ]]; do sleep 5; done' - -# Is the proxy server enabled? -if [ "$DATABUS_PROXY_SERVER_ENABLE" = "true" ]; then - - # Remove the previous Caddyfile: - rm -f /etc/caddy/Caddyfile - - # Should we use ACME? - if [ "$DATABUS_PROXY_SERVER_USE_ACME" = "true" ]; then - - # Re-create the Caddyfile using the user's configuration: - cat < /etc/caddy/Caddyfile -$DATABUS_PROXY_SERVER_HOSTNAME:4000 { - reverse_proxy 127.0.0.1:3000 -} - -$DATABUS_PROXY_SERVER_HOSTNAME:4001 { - reverse_proxy 127.0.0.1:8080 -} -EOF - - # No, instead we should use the user's certificate: - else - - # Re-create the Caddyfile using the user's configuration: - cat < /etc/caddy/Caddyfile -$DATABUS_PROXY_SERVER_HOSTNAME:4000 { - tls /tls/$DATABUS_PROXY_SERVER_OWN_CERT /tls/$DATABUS_PROXY_SERVER_OWN_CERT_KEY - reverse_proxy 127.0.0.1:3000 -} - -$DATABUS_PROXY_SERVER_HOSTNAME:4001 { - tls /tls/$DATABUS_PROXY_SERVER_OWN_CERT /tls/$DATABUS_PROXY_SERVER_OWN_CERT_KEY - reverse_proxy 127.0.0.1:8080 -} -EOF - - fi - caddy start --config /etc/caddy/Caddyfile - echo "The proxy server is enabled." - -# The proxy server is disabled: -else - echo "The proxy server is disabled." -fi - -cd /databus/server -npm start \ No newline at end of file