1
0
mirror of https://github.com/octoleo/plantuml-server.git synced 2024-11-12 23:46:30 +00:00

Compare commits

..

No commits in common. "master" and "v1.2021.14" have entirely different histories.

173 changed files with 2237 additions and 7009 deletions

View File

@ -1,25 +0,0 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
[*.java]
indent_size = 4
[*.md]
max_line_length = off
trim_trailing_whitespace = false
[*.puml]
insert_final_newline = false
[{Dockerfile,Dockerfile.*}]
indent_size = 4
[.vscode/*.json]
indent_size = 4

1
.gitattributes vendored
View File

@ -1 +0,0 @@
* text=auto eol=lf

5
.github/FUNDING.yml vendored
View File

@ -1,5 +0,0 @@
# These are supported funding model platforms
github: plantuml
patreon: plantuml
liberapay: plantuml

View File

@ -1,38 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@ -1,27 +0,0 @@
---
name: Documentation request
about: Identify an area for improvement in documentation
---
**What is the URL of the documentation?**
- **Example:** https://github.com/evantill/docker-cheerpj/blob/main/README.md#example-of-using-cheerpj-on-your-computer
- *Note:* This URL includes the web page and the section of the documentation.
**What can be improved?**
A clear and concise description of what can be improved.
Examples:
- "I don't understand where the ${XYZ} variable is set."
- "There seems to be a step missing between 'X' and 'Z'. I don't know how to get to 'Z'."
- "When I run `command sub-command ...` I get the following error:"
- "I don't know what is meant by 'gerble barb gazoink` in the instructions".
**Additional context**
Add any other context or screenshots to help describe the documentation improvement.
If you think the documentation improvement is operating system specific,
please indicate which operating system is being used.

View File

@ -1,17 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -9,11 +9,11 @@ jobs:
build-jdk11:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-java@v3
- uses: actions/setup-java@v2
with:
distribution: "zulu"
java-version: 11
@ -22,7 +22,7 @@ jobs:
- name: get tag name
id: version
run: echo "VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
run: echo ::set-output name=VERSION::${GITHUB_REF#refs/*/}
- name: build with maven
run: mvn --batch-mode --define java.net.useSystemProxies=true package
@ -37,7 +37,7 @@ jobs:
run: cp target/plantuml.war target/plantuml-jsp-${{ steps.version.outputs.VERSION }}.war
- name: temporarily save generated war files
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v2
with:
name: war-jre11
path: target/plantuml*-${{ steps.version.outputs.VERSION }}.war
@ -45,11 +45,11 @@ jobs:
build-jdk8:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-java@v3
- uses: actions/setup-java@v2
with:
distribution: "zulu"
java-version: 8
@ -58,7 +58,7 @@ jobs:
- name: get tag name
id: version
run: echo "VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
run: echo ::set-output name=VERSION::${GITHUB_REF#refs/*/}
- name: Remove test code (not java 8 compatible)
run: rm -rf src/test
@ -76,7 +76,7 @@ jobs:
run: cp target/plantuml.war target/plantuml-jre8-jsp-${{ steps.version.outputs.VERSION }}.war
- name: temporarily save generated war files
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v2
with:
name: war-jre8
path: target/plantuml*-${{ steps.version.outputs.VERSION }}.war
@ -88,13 +88,13 @@ jobs:
- build-jdk8
steps:
- name: retrieve generated war files (jre8)
uses: actions/download-artifact@v3
uses: actions/download-artifact@v2
with:
name: war-jre8
path: artifacts
- name: retrieve generated war files (jre11)
uses: actions/download-artifact@v3
uses: actions/download-artifact@v2
with:
name: war-jre11
path: artifacts
@ -116,7 +116,7 @@ jobs:
needs:
- publish-releases
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 0
@ -165,7 +165,7 @@ jobs:
with:
context: .
file: ./Dockerfile.tomcat
platforms: linux/amd64, linux/arm64
platforms: linux/amd64
push: true
tags: ${{ steps.docker_meta_tomcat.outputs.tags }}
labels: ${{ steps.docker_meta_tomcat.outputs.labels }}
@ -176,7 +176,7 @@ jobs:
with:
context: .
file: ./Dockerfile.jetty
platforms: linux/amd64, linux/arm64
platforms: linux/amd64
push: true
tags: ${{ steps.docker_meta_jetty.outputs.tags }}
labels: ${{ steps.docker_meta_jetty.outputs.labels }}
@ -186,11 +186,11 @@ jobs:
needs:
- publish-releases
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-java@v3
- uses: actions/setup-java@v2
with:
distribution: "zulu"
java-version: 11

View File

@ -8,11 +8,11 @@ jobs:
test-java-8-war-generation:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-java@v3
- uses: actions/setup-java@v2
with:
distribution: "zulu"
java-version: 8
@ -29,7 +29,7 @@ jobs:
run: mvn --batch-mode -f pom.jdk8.xml -D java.net.useSystemProxies=true clean package
- name: temporarily save generated files
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v2
with:
name: war-jre8
path: target/plantuml.war
@ -42,17 +42,17 @@ jobs:
matrix:
java-version: [ 11, 17 ]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: retrieve generated files (jre8)
uses: actions/download-artifact@v3
uses: actions/download-artifact@v2
with:
name: war-jre8
path: artifacts
- uses: actions/setup-java@v3
- uses: actions/setup-java@v2
with:
distribution: "zulu"
java-version: ${{ matrix.java-version }}
@ -69,7 +69,7 @@ jobs:
run: sleep 5s
- name: Run tests against "mvn jetty:run" server
run: mvn --batch-mode test -DskipTests=false -Dgroups=\!graphviz-test -DargLine="-Dsystem.test.server=http://localhost:8080/plantuml"
run: mvn --batch-mode test -DskipTests=false -DargLine="-Dsystem.test.server=http://localhost:8080/plantuml"
test-mvn-livecycle:
runs-on: ubuntu-latest
@ -77,11 +77,11 @@ jobs:
matrix:
java-version: [ 11, 17 ]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-java@v3
- uses: actions/setup-java@v2
with:
distribution: "zulu"
java-version: ${{ matrix.java-version }}
@ -93,7 +93,7 @@ jobs:
- name: Lifecycle - Step 2/8 - mvn validate
run: mvn --batch-mode validate
- name: Lifecycle - Step 3/8 - mvn compile
run: mvn --batch-mode compile
@ -111,7 +111,7 @@ jobs:
- name: Lifecycle - Step 8/8 - mvn site
run: mvn --batch-mode site
test-embedded:
runs-on: ubuntu-latest
needs: test-mvn-livecycle
@ -119,11 +119,11 @@ jobs:
matrix:
java-version: [ 11, 17 ]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-java@v3
- uses: actions/setup-java@v2
with:
distribution: "zulu"
java-version: ${{ matrix.java-version }}
@ -131,7 +131,7 @@ jobs:
cache: "maven"
- name: Run tests against jetty embedded server
run: mvn --batch-mode clean test -DskipTests=false -Dgroups=\!graphviz-test
run: mvn --batch-mode clean test -DskipTests=false
test-mvn-jetty-run:
runs-on: ubuntu-latest
@ -140,11 +140,11 @@ jobs:
matrix:
java-version: [ 11, 17 ]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-java@v3
- uses: actions/setup-java@v2
with:
distribution: "zulu"
java-version: ${{ matrix.java-version }}
@ -161,7 +161,7 @@ jobs:
run: sleep 20s
- name: Run tests against "mvn jetty:run" server
run: mvn --batch-mode test -DskipTests=false -Dgroups=\!graphviz-test -DargLine="-Dsystem.test.server=http://localhost:8080/plantuml"
run: mvn --batch-mode test -DskipTests=false -DargLine="-Dsystem.test.server=http://localhost:8080/plantuml"
test-jetty-runner:
runs-on: ubuntu-latest
@ -170,11 +170,11 @@ jobs:
matrix:
java-version: [ 11, 17 ]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-java@v3
- uses: actions/setup-java@v2
with:
distribution: "zulu"
java-version: ${{ matrix.java-version }}
@ -191,7 +191,7 @@ jobs:
run: sleep 5s
- name: Run tests against "mvn jetty:run" server
run: mvn --batch-mode test -DskipTests=false -Dgroups=\!graphviz-test -DargLine="-Dsystem.test.server=http://localhost:8080/plantuml"
run: mvn --batch-mode test -DskipTests=false -DargLine="-Dsystem.test.server=http://localhost:8080/plantuml"
test-jetty:
runs-on: ubuntu-latest
@ -200,11 +200,11 @@ jobs:
matrix:
java-version: [ 11, 17 ]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-java@v3
- uses: actions/setup-java@v2
with:
distribution: "zulu"
java-version: ${{ matrix.java-version }}
@ -217,7 +217,7 @@ jobs:
- name: Build the jetty docker stack
run: |
docker image build -f Dockerfile.jetty -t plantuml-server:local .
docker run -d --hostname=test.localhost -p 8080:8080 -e BASE_URL=plantuml plantuml-server:local
docker run -d -p 8080:8080 -e BASE_URL=plantuml plantuml-server:local
- name: Check running containers
run: docker ps
@ -232,11 +232,11 @@ jobs:
matrix:
java-version: [ 11, 17 ]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-java@v3
- uses: actions/setup-java@v2
with:
distribution: "zulu"
java-version: ${{ matrix.java-version }}
@ -249,7 +249,7 @@ jobs:
- name: Build the tomcat docker stack
run: |
docker image build -f Dockerfile.tomcat -t plantuml-server:local .
docker run -d --hostname=test.localhost -p 8080:8080 -e BASE_URL=plantuml plantuml-server:local
docker run -d -p 8080:8080 -e BASE_URL=plantuml plantuml-server:local
- name: Check running containers
run: docker ps

8
.gitignore vendored
View File

@ -1,13 +1,5 @@
# Eclipse Ignores
target
.settings
.classpath
.project
.checkstyle
# IntelliJ ignores
.idea/
out/
*.iml
*.ipr
*.iws

18
.vscode/settings.json vendored
View File

@ -2,29 +2,21 @@
"java.configuration.updateBuildConfiguration": "automatic",
"cSpell.words": [
"Arnaud",
"buildx",
"ditaa",
"endditaa",
"enduml",
"epstext",
"etag",
"ghaction",
"inmemory",
"Lalloni",
"monaco",
"plantuml",
"puml",
"Roques",
"servlet",
"servlets",
"startditaa",
"startuml",
"undock",
"utxt"
"utxt",
"ghaction",
"buildx"
],
"cSpell.allowCompoundWords": true,
"svg.preview.background": "dark-transparent",
"files.associations": {
"*.jspf": "html"
}
}
"cSpell.allowCompoundWords": true
}

View File

@ -1,6 +1,6 @@
FROM maven:3-eclipse-temurin-17 AS builder
FROM maven:3-jdk-11-slim AS builder
COPY pom.xml pom.parent.xml /app/
COPY pom.xml /app/
COPY src/main /app/src/main/
WORKDIR /app
@ -8,7 +8,7 @@ RUN mvn --batch-mode --define java.net.useSystemProxies=true package
########################################################################################
FROM jetty:11.0.18-jre17-eclipse-temurin
FROM jetty:11.0.7-jre11-slim
# Proxy and OldProxy need empty path segments support in URIs
# Hence: allow AMBIGUOUS_EMPTY_SEGMENT
@ -16,64 +16,23 @@ FROM jetty:11.0.18-jre17-eclipse-temurin
RUN sed -i 's/# jetty\.httpConfig\.uriCompliance=DEFAULT/jetty.httpConfig.uriCompliance=DEFAULT,AMBIGUOUS_EMPTY_SEGMENT/g' /var/lib/jetty/start.d/server.ini
USER root
RUN apt-get update && \
apt-get install -y --no-install-recommends \
curl \
fonts-noto-cjk \
libgd3 \
graphviz \
&& \
rm -rf /var/lib/apt/lists/* && \
/generate-jetty-start.sh
# Build Graphviz from source because there are no binary distributions for recent versions
ARG GRAPHVIZ_VERSION
ARG GRAPHVIZ_BUILD_DIR=/tmp/graphiz-build
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
jq \
libexpat1-dev \
libgd-dev \
zlib1g-dev \
&& \
mkdir -p $GRAPHVIZ_BUILD_DIR && \
cd $GRAPHVIZ_BUILD_DIR && \
GRAPHVIZ_VERSION=${GRAPHVIZ_VERSION:-$(curl -s https://gitlab.com/api/v4/projects/4207231/releases/ | jq -r '.[] | .name' | sort -V -r | head -1)} && \
curl -o graphviz.tar.gz https://gitlab.com/api/v4/projects/4207231/packages/generic/graphviz-releases/${GRAPHVIZ_VERSION}/graphviz-${GRAPHVIZ_VERSION}.tar.gz && \
tar -xzf graphviz.tar.gz && \
cd graphviz-$GRAPHVIZ_VERSION && \
./configure && \
make && \
make install && \
apt-get remove -y \
build-essential \
jq \
libexpat1-dev \
libgd-dev \
zlib1g-dev \
&& \
apt-get autoremove -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* && \
rm -rf $GRAPHVIZ_BUILD_DIR
COPY docker-entrypoint.jetty.sh /entrypoint.sh
COPY docker-entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
USER jetty
ENV WEBAPP_PATH=$JETTY_BASE/webapps
ENV BASE_URL=ROOT \
WEBAPP_PATH=$JETTY_BASE/webapps
RUN rm -rf $WEBAPP_PATH && \
mkdir -p $WEBAPP_PATH
COPY --from=builder /app/target/plantuml.war /plantuml.war
COPY ROOT.jetty.xml $WEBAPP_PATH/ROOT.xml
# Openshift https://docs.openshift.com/container-platform/4.9/openshift_images/create-images.html#images-create-guide-openshift_create-images
USER root
RUN chgrp -R 0 $JETTY_BASE && chmod -R g=u $JETTY_BASE
RUN chgrp -R 0 /tmp && chmod -R g=u /tmp
USER jetty
COPY --from=builder /app/target/plantuml.war $WEBAPP_PATH/ROOT.war
ENTRYPOINT ["/entrypoint.sh"]
VOLUME ["/tmp/jetty"]

View File

@ -1,76 +0,0 @@
FROM maven:3-eclipse-temurin-17-alpine AS builder
COPY pom.xml pom.parent.xml /app/
COPY src/main /app/src/main/
WORKDIR /app
RUN mvn --batch-mode --define java.net.useSystemProxies=true package
########################################################################################
FROM jetty:11.0.18-jre17-alpine-eclipse-temurin
# Proxy and OldProxy need empty path segments support in URIs
# Hence: allow AMBIGUOUS_EMPTY_SEGMENT
# Changes are only active if `/generate-jetty-start.sh` is called!
RUN sed -i 's/# jetty\.httpConfig\.uriCompliance=DEFAULT/jetty.httpConfig.uriCompliance=DEFAULT,AMBIGUOUS_EMPTY_SEGMENT/g' /var/lib/jetty/start.d/server.ini
USER root
RUN apk add --no-cache \
curl \
font-noto-cjk \
libgd \
&& \
/generate-jetty-start.sh
#RUN apk add --no-cache graphviz
ARG GRAPHVIZ_VERSION
ARG GRAPHVIZ_BUILD_DIR=/tmp/graphiz-build
RUN apk add --no-cache \
g++ \
jq \
expat-dev \
make \
zlib \
pkgconf \
&& \
mkdir -p $GRAPHVIZ_BUILD_DIR && \
cd $GRAPHVIZ_BUILD_DIR && \
GRAPHVIZ_VERSION=${GRAPHVIZ_VERSION:-$(curl -s https://gitlab.com/api/v4/projects/4207231/releases/ | jq -r '.[] | .name' | sort -V -r | head -1)} && \
curl -o graphviz.tar.gz https://gitlab.com/api/v4/projects/4207231/packages/generic/graphviz-releases/${GRAPHVIZ_VERSION}/graphviz-${GRAPHVIZ_VERSION}.tar.gz && \
tar -xzf graphviz.tar.gz && \
cd graphviz-$GRAPHVIZ_VERSION && \
./configure && \
make && \
make install && \
apk del --no-cache \
g++ \
jq \
expat-dev \
make \
zlib \
&& \
rm -rf $GRAPHVIZ_BUILD_DIR
COPY docker-entrypoint.jetty.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
USER jetty
ENV WEBAPP_PATH=$JETTY_BASE/webapps
RUN rm -rf $WEBAPP_PATH && \
mkdir -p $WEBAPP_PATH
COPY --from=builder /app/target/plantuml.war /plantuml.war
COPY ROOT.jetty.xml $WEBAPP_PATH/ROOT.xml
# Openshift https://docs.openshift.com/container-platform/4.9/openshift_images/create-images.html#images-create-guide-openshift_create-images
USER root
RUN chgrp -R 0 $JETTY_BASE && \
chmod -R g=u $JETTY_BASE
RUN chgrp -R 0 /tmp && \
chmod -R g=u /tmp
USER jetty
ENTRYPOINT ["/entrypoint.sh"]
VOLUME ["/tmp/jetty"]

View File

@ -1,6 +1,6 @@
FROM maven:3-eclipse-temurin-11 AS builder
FROM maven:3-jdk-11-slim AS builder
COPY pom.xml pom.parent.xml /app/
COPY pom.xml /app/
COPY src/main /app/src/main/
WORKDIR /app
@ -8,58 +8,23 @@ RUN mvn --batch-mode --define java.net.useSystemProxies=true -Dapache-jsp.scope=
########################################################################################
FROM tomcat:10-jdk11
FROM tomcat:10-jdk11-openjdk-slim
RUN apt-get update && \
apt-get install -y --no-install-recommends \
curl \
fonts-noto-cjk \
libgd3 \
graphviz \
&& \
rm -rf /var/lib/apt/lists/*
# Build Graphviz from source because there are no binary distributions for recent versions
ARG GRAPHVIZ_VERSION
ARG GRAPHVIZ_BUILD_DIR=/tmp/graphiz-build
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
jq \
libexpat1-dev \
libgd-dev \
zlib1g-dev \
&& \
mkdir -p $GRAPHVIZ_BUILD_DIR && \
cd $GRAPHVIZ_BUILD_DIR && \
GRAPHVIZ_VERSION=${GRAPHVIZ_VERSION:-$(curl -s https://gitlab.com/api/v4/projects/4207231/releases/ | jq -r '.[] | .name' | sort -V -r | head -1)} && \
curl -o graphviz.tar.gz https://gitlab.com/api/v4/projects/4207231/packages/generic/graphviz-releases/${GRAPHVIZ_VERSION}/graphviz-${GRAPHVIZ_VERSION}.tar.gz && \
tar -xzf graphviz.tar.gz && \
cd graphviz-$GRAPHVIZ_VERSION && \
./configure && \
make && \
make install && \
apt-get remove -y \
build-essential \
jq \
libexpat1-dev \
libgd-dev \
zlib1g-dev \
&& \
apt-get autoremove -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* && \
rm -rf $GRAPHVIZ_BUILD_DIR
COPY docker-entrypoint.tomcat.sh /entrypoint.sh
COPY docker-entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENV WEBAPP_PATH=$CATALINA_HOME/webapps
ENV BASE_URL=ROOT \
WEBAPP_PATH=$CATALINA_HOME/webapps
RUN rm -rf $WEBAPP_PATH && \
mkdir -p $WEBAPP_PATH
COPY --from=builder /app/target/plantuml.war /plantuml.war
# Openshift https://docs.openshift.com/container-platform/4.9/openshift_images/create-images.html#images-create-guide-openshift_create-images
RUN chgrp -R 0 $CATALINA_HOME && chmod -R g=u $CATALINA_HOME
COPY --from=builder /app/target/plantuml.war $WEBAPP_PATH/ROOT.war
ENTRYPOINT ["/entrypoint.sh"]
CMD ["catalina.sh", "run"]

View File

@ -4,33 +4,14 @@
[![latest tag](https://img.shields.io/github/v/tag/plantuml/plantuml-server)](https://github.com/plantuml/plantuml-server/tags)
![workflow status (Main)](https://github.com/plantuml/plantuml-server/actions/workflows/main.yml/badge.svg)
![workflow status (Tests)](https://github.com/plantuml/plantuml-server/actions/workflows/tests.yml/badge.svg)
[![online](https://img.shields.io/endpoint?url=https://www.plantuml.com/plantuml/badge)](https://www.plantuml.com/plantuml)
[![rate](https://img.shields.io/endpoint?url=https://www.plantuml.com/plantuml/rate)](https://www.plantuml.com/plantuml)
[![peak](https://img.shields.io/endpoint?url=https://www.plantuml.com/plantuml/rate?peak)](https://www.plantuml.com/plantuml)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/plantuml?logo=github)](https://github.com/sponsors/plantuml/)
![workflow status (Pages)](https://github.com/plantuml/plantuml-server/actions/workflows/pages.yml/badge.svg)
[![docker pulls](https://img.shields.io/docker/pulls/plantuml/plantuml-server.svg?color=blue)](https://hub.docker.com/r/plantuml/plantuml-server)
![Docker Image Size (Jetty)](https://img.shields.io/docker/image-size/plantuml/plantuml-server/jetty?label=jetty%20image%20size)
![Docker Image Size (Tomcat)](https://img.shields.io/docker/image-size/plantuml/plantuml-server/tomcat?label=tomcat%20image%20size)
PlantUML Server is a web application to generate UML diagrams on-the-fly.
> [PlantUML is **not** affected by the log4j vulnerability.](https://github.com/plantuml/plantuml/issues/826)
> **Breaking changes**:
> The PlantUML core removed the deprecated `ALLOW_PLANTUML_INCLUDE` environment property feature and switch to the
> `PLANTUML_SECURITY_PROFILE` concept with version `v1.2023.9`.
> All details about PlantUML's security can be found on <https://plantuml.com/security>.
>
> By default PlantUML server sets the `PLANTUML_SECURITY_PROFILE` to `INTERNET`.
> If you need more access to e.g. other ports than 80 (http) and 443 (https) or even access to local files, please
> consider using one of the allowlist features.
> It is strongly advised **not** to set the `PLANTUML_SECURITY_PROFILE` below `INTERNET`!
![PlantUML Server](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/screenshot.png)
More examples and features about the Web UI can be found in [docs/WebUI](https://github.com/plantuml/plantuml-server/tree/master/docs/WebUI).
![PlantUML Server](https://raw.githubusercontent.com/plantuml/plantuml-server/master/screenshots/screenshot.png)
To know more about PlantUML, please visit https://plantuml.com.
@ -40,11 +21,6 @@ To know more about PlantUML, please visit https://plantuml.com.
- jre/jdk 11 or above
- apache maven 3.0.2 or above
## Recommendations
- Jetty 11 or above
- Tomcat 10 or above
## How to run the server
@ -126,22 +102,6 @@ You can set all the following variables:
* `BASE_URL`
* PlantUML Base URL path
* Default value: `ROOT`
* `PLANTUML_SECURITY_PROFILE`
* Set PlantUML security profile. See [PlantUML security](https://plantuml.com/security).
* If you need more access to e.g. other ports than 80 (http) and 443 (https) or even access to local files, please consider using one of the allowlist features:
* `plantuml.allowlist.path`
* `plantuml.include.path`
* `plantuml.allowlist.url`
* It is strongly advised **not** to set the `PLANTUML_SECURITY_PROFILE` below `INTERNET`!
* Default value: `INTERNET`
* `PLANTUML_PROPERTY_FILE`
* Set PlantUML system properties (like over the Java command line using the `-Dpropertyname=value` syntax).
* To see what kind of file content is supported, see the documentation of [`java.util.Properties.load`](https://docs.oracle.com/javase/8/docs/api/java/util/Properties.html#load-java.io.Reader-).
* Default value: `null`
* `PLANTUML_CONFIG_FILE`
* Local path to a PlantUML configuration file (identical to the `-config` flag on the CLI)
* File content will be added before each PlantUML diagram code.
* Default value: `null`
* `PLANTUML_LIMIT_SIZE`
* Limits image width and height
* Default value: `4096`
@ -151,15 +111,17 @@ You can set all the following variables:
* `HTTP_AUTHORIZATION`
* when calling the `proxy` endpoint, the value of `HTTP_AUTHORIZATION` will be used to set the HTTP Authorization header
* Default value: `null`
* `HTTP_PROXY_READ_TIMEOUT`
* when calling the `proxy` endpoint, the value of `HTTP_PROXY_READ_TIMEOUT` will be the connection read timeout in milliseconds
* Default value: `10000` (10 seconds)
* `ALLOW_PLANTUML_INCLUDE`
* Enables `!include` processing which can read files from the server into diagrams. Files are read relative to the current working directory.
* Default value: `false`
## Alternate: How to build your docker image
This method uses maven to run the application. That requires internet connectivity.
So, you can use following command to create a self-contained docker image that will "just work".
So, you can use following command to create a self-contained docker image that will "just-work".
*Note: Generate the WAR (instructions further below) prior to running "docker build"*
```sh
docker image build -f Dockerfile.jetty -t plantuml-server:local .
@ -188,9 +150,3 @@ If you want to generate the war with java 8 as target just remove the src/test d
rm -rf src/test
mvn package -f pom.jdk8.xml [-Dapache-jsp.scope=compile]
```
## Use with reverse-proxy
It is possible to use PlantUML with a reverse proxy.
You can find this and other examples [here](https://github.com/plantuml/plantuml-server/tree/master/examples).

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Set name="contextPath">
<Env name="CONTEXT_PATH" />
</Set>
<Set name="war">/plantuml.war</Set>
</Configure>

View File

@ -1,12 +0,0 @@
# Security Policy
## Reporting a Vulnerability
If you find any security concern, please send a mail to plantuml@gmail.com
with title **Security concern**.
We will then study the concern and will answer back by email.
Thanks!

View File

@ -1,9 +1,11 @@
version: '3.3'
services:
plantuml-server:
build:
context: .
dockerfile: Dockerfile.jetty
image: plantuml/plantuml-server:jetty
image: plantuml/plantuml-server:jetty-local
container_name: plantuml-server
ports:
- 8080:8080

View File

@ -3,8 +3,11 @@
# cspell:enableCompoundWords
###########################################################
# ensure context path starts with a slash
export CONTEXT_PATH="/${BASE_URL#'/'}"
# use environment variables
if [ "$BASE_URL" != "ROOT" ]; then
mkdir -p "$(dirname "$WEBAPP_PATH/$BASE_URL")"
mv "$WEBAPP_PATH/ROOT.war" "$WEBAPP_PATH/$BASE_URL.war"
fi
# base image entrypoint
if [ -x /docker-entrypoint.sh ]; then

View File

@ -1,18 +0,0 @@
#!/bin/sh
# cspell:words mkdir
# cspell:enableCompoundWords
###########################################################
# choose war file name so that context path is correctly set based on BASE_URL,
# following the rules from https://tomcat.apache.org/tomcat-9.0-doc/config/context.html#Naming,
# specifically remove leading and trailing slashes and replace the remaining ones by hashes.
export FILE_NAME="$(echo "$BASE_URL" | sed -e 's:^/::' -e 's:/$::' -e 's:/:#:g')"
export FILE_PATH="$WEBAPP_PATH/$FILE_NAME.war"
mv /plantuml.war "$FILE_PATH"
# base image entrypoint
if [ -x /docker-entrypoint.sh ]; then
/docker-entrypoint.sh "$@"
else
exec "$@"
fi

View File

@ -1,25 +0,0 @@
# PlantUML Server Web UI
## First example: "Alice and Bob"
![alice-bob](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/alice-bob.gif)
## Multipaging
Just see what you want.
And if its the second diagram page, so be it.
![multipaging](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/multipaging.gif)
## Split Screen
You have multiple monitors? You want to share your Window but only show the diagram and not the code? Than do it!
![multipaging](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/split-screen.gif)
## More
- [Settings](https://github.com/plantuml/plantuml-server/blob/master/docs/WebUI/settings.md)
- [PlantUML Language Features](https://github.com/plantuml/plantuml-server/blob/master/docs/WebUI/language-features.md)
- [Import/Export](https://github.com/plantuml/plantuml-server/blob/master/docs/WebUI/import-export.md)
- [Mobile Version](https://github.com/plantuml/plantuml-server/blob/master/docs/WebUI/mobile.md)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 KiB

View File

@ -1,21 +0,0 @@
# Import and Export editable PlantUML Diagrams
Similar to [draw.io](https://app.diagrams.net) it is possible to load and continue editing PlantUML diagram images.
## Export a diagram
Via the editor menu or <kbd>Ctrl</kbd> + <kbd>S</kbd> (or <kbd>Meta</kbd> + <kbd>S</kbd> in the case of a Mac) you can open the file save dialog.
Here you can edit the file name, choose a file/diagram type and download the diagram.
The default is to download the PlantUML code.
![export](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/diagram-export.gif)
## Import a diagram
This feature is based on the PlantUML meta data which currently **support only PNG and SVG** diagrams.
Besides a diagram image, you can of course also load a diagram code file.
Moreover, because it is so nice and convenient, we also added a drag-and-drop feature.
![import](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/diagram-import.gif)

View File

@ -1,41 +0,0 @@
# PlantUML Language Features
## Auto Completion
### Icons
- type `<&` to get a list of PlantUML available icons
- see a preview of the suggested icon in its description
- [PlantUML documentation](https://plantuml.com/openiconic)
![icons](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/auto-completion-icons.gif)
### Emojis
- type `<:` to get a list of PlantUML available icons
- see a preview of the suggested icon in its description
- [PlantUML documentation](https://plantuml.com/creole#68305e25f5788db0)
![emojis](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/auto-completion-emojis.gif)
### Themes
- type `!t` to get the suggestion `theme`
- type `!theme ` to get a list of (local) available PlantUML themes.
- [PlantUML documentation](https://plantuml.com/theme)
![themes](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/auto-completion-themes.gif)
## Validation
### `@start...` and `@end...`
- `@start...` should always be the first command
- `@end...` should alway be the last command
- `@start...` should only exists once
- `@end...` should only exists once
- `@end...` should have the same type as `@start...`
e.g.: `@startjson ... @endjson`
![start-end](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/validation-start-end.gif)

View File

@ -1,12 +0,0 @@
# Mobile Version
PlantUML Server is mobile ready.
## First example: "Alice and Bob"
![alice-bob](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/mobile-alice-bob.gif)
## Settings
![settings](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/mobile-settings.gif)

View File

@ -1,38 +0,0 @@
# Settings
Via the menu or <kbd>Ctrl</kbd> + <kbd>,</kbd> (or <kbd>Meta</kbd> + <kbd>,</kbd> in the case of a Mac) you can open the Setting dialog.
## Theme
_The sun is too bright? You live on the dark side or only in the basement?_
Choose between the `dark` and `light` theme.
![theme](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/settings-theme.gif)
## Rendering Type
You want always to work and see only the SVG version? Not Problem.
Choose the rendering type you want to see.
![rendering-type](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/settings-rendering-type.gif)
## Editor Watcher Timeout
You can change the Editor Watcher Timeout, by default it is `500 ms`.
## Editor Options
You can change the options of the editor:
```yaml
{
automaticLayout: true,
fixedOverflowWidgets: true,
minimap: { enabled: false },
scrollbar: { alwaysConsumeMouseWheel: false },
scrollBeyondLastLine: false,
tabSize: 2,
theme: "vs", // "vs-dark"
}
```

View File

@ -1,83 +0,0 @@
# Front-end Contribution
## Web UI
The Web UI uses vanilla javascript.
As online editor Microsoft's [Monaco Editor](https://github.com/microsoft/monaco-editor).
The documentation can be found [here](https://microsoft.github.io/monaco-editor/docs.html).
You may recognize the editor since it's the code editor from [VS Code](https://github.com/microsoft/vscode).
The main entry file are `index.jsp`, `previewer.jsp` and `error.jsp`.
The code structure is mainly divided into `components` and `js`:
- `components` are for example a modal or dialog.
Anything that include things directly seen and rendered on the page.
- `js` contains more the things that do not have a direct influence on the UI. For example the PlantUML language features or the methods for cross-browser/cross-tab communication.
## PlantUML Language Features
At the moment there is no defined PlantUML language.
Feel free to create one!
But until then the syntax highlighting form `apex` is used.
IMHO it works quite well.
All PlantUML language features are bundled into a seperate file `plantuml-language.min.js`.
Therefore anything under `js/language` should be independent!
### Code Completion
What do you need to do to create a new code completion feature:
1. create a new JS file under `js/language/completion` - let's say `xxx.js`
2. create a new `registerXxxCompletion` method
_It may help you if you look into the [documentation](https://microsoft.github.io/monaco-editor/docs.html#functions/languages.registerCompletionItemProvider.html) or at the provided [sample code](https://microsoft.github.io/monaco-editor/playground.html?source=v0.38.0#example-extending-language-services-completion-provider-example) to understand more about `monaco.languages.registerCompletionItemProvider`._
```js
PlantUmlLanguageFeatures.prototype.registerEmojiCompletion = function() {
monaco.languages.registerCompletionItemProvider(PlantUmlLanguageFeatures.languageSelector, {
provideCompletionItems: async (model, position) => {
// ...
return { suggestions };
}
});
};
```
4. add your new method inside the language initialization inside `js/language/language.js`
```diff
const PlantUmlLanguageFeatures = function(initialize = true) {
if (initialize) {
// initialize all validation and code completion methods
this.addStartEndValidationListeners();
this.registerThemeCompletion();
this.registerIconCompletion();
this.registerEmojiCompletion();
+ this.registerXxxCompletion();
}
};
```
### Code Validation
What do you need to do to create a new code validation feature:
1. create a new JS file under `js/language/validation/listeners` - let's say `zzz-validation.js`
2. register your validation methods to the designated event listener
The validation event order is: `before` &#8594; `code` &#8594; `line` &#8594; `after`
You may look at `js/language/validation/listeners/start-end-validation.js` to get an idea how to register a new listener.
3. add your new method inside the language initialization inside `js/language/language.js`
```diff
const PlantUmlLanguageFeatures = function(initialize = true) {
if (initialize) {
// initialize all validation and code completion methods
this.addStartEndValidationListeners();
+ this.addZzzValidationListeners();
this.registerThemeCompletion();
this.registerIconCompletion();
this.registerEmojiCompletion();
}
};
```
### Tipps
- `pom.xml`: set `withoutCSSJSCompress` to `true` to deactivate the minification
- use `mvn fizzed-watcher:run` to watch changes and automatically update the bundled `plantuml.min.{css,js}` and `plantuml-language.min.js` files
- if the browser get the error `ReferenceError: require is not defined` or something similar related to the webjars, try `mvn clean install` to get things straight

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

View File

@ -1,6 +0,0 @@
# Examples
- [Additional fonts inside the PlantUML docker container](./additional-fonts)
- [Nginx simple reverse proxy example](./nginx-simple)
- [Nginx reverse proxy example with defined location directive (different context path)](./nginx-contextpath)
- [Kubernetes simple deployment](./kubernetes-simple)

View File

@ -1,73 +0,0 @@
# Additional fonts inside the PlantUML docker container
It is possible to make additional fonts available to PlantUML by mapping them via a volume within the docker container.
Since the base image from the docker container is using Ubuntu, fonts can easily be provided by just adding them somewhere inside the `~/.local/share/fonts` directory.
**Tipp**: to not overwrite the container fonts add the additional files inside an own sub-folder, e.g., `custom` or `host`.
In the following you can find an example how to provide all fonts of your host machine in the PlantUML docker container for Jetty and Tomcat.
## Jetty
In the case of the Jetty docker container the home directory is `/var/lib/jetty`.
```yaml
services:
plantuml-server:
image: plantuml/plantuml-server:jetty
container_name: plantuml-server
ports:
- "80:8080"
environment:
- TZ=Europe/Berlin
- BASE_URL=plantuml
volumes:
- /usr/share/fonts:/var/lib/jetty/.local/share/fonts/host:ro
```
## Tomcat
In the case of the Tomcat docker container the home directory is `/root`.
_Yes, the tomcat container is running as `root` which is basically a really bad idea w.r.t. security. Create a pull request and maintain it if you want to change that._
```yaml
services:
plantuml-server:
image: plantuml/plantuml-server:tomcat
container_name: plantuml-server
ports:
- "80:8080"
environment:
- TZ=Europe/Berlin
- BASE_URL=plantuml
volumes:
- /usr/share/fonts:/root/.local/share/fonts/host:ro
```
## Verification
The following command will print a list of all available fonts inside the docker container:
```bash
docker exec -it plantuml-server fc-list
```
To find a special font add a grep filter to the command:
```bash
docker exec -it plantuml-server fc-list | grep "<name-of-font>"
```
Naturally, it is also possible to check this via PlantUML itself by rendering the following diagram:
```plantuml
@startuml
listfonts
@enduml
```
**Note**: If you have added a lot of fonts: (a) this diagram may take a few seconds to generate, and (b) eventually the PNG image may be clipped. To avoid the latter, render the diagram as an SVG image.

View File

@ -1,13 +0,0 @@
version: "3"
services:
plantuml-server:
image: plantuml/plantuml-server:jetty
container_name: plantuml-server
ports:
- "80:8080"
environment:
- TZ=Europe/Berlin
- BASE_URL=plantuml
volumes:
- /usr/share/fonts:/var/lib/jetty/.local/share/fonts/host:ro

View File

@ -1,51 +0,0 @@
# PlantUML Kubernetes Deployment
In this example, PlantUML is deployed on an Kubernetes cluster using a `Deployment`, a `Service` and an `Ingress`.
## Quick start
Install:
```bash
# Hint: Adjust the Ingress host to your URL
kubectl create ns plantuml
kubectl -n plantuml apply -f deployment.yaml
```
Uninstall:
```bash
kubectl -n plantuml delete -f deployment.yaml
kubectl delete ns plantuml
```
## TLS configuration
Create a TLS `Secret` and extend the `Ingress` spec with a TLS configuration:
```bash
[...]
tls:
- hosts:
- plantuml-example.localhost
secretName: plantuml-tls
```
Since the `Ingress Controller` terminates the TLS and routes `http` to the application, we might need to tell the application explicitly that it got a forwarded request.
This configuration changes depending on the `Ingress Controller`. Here an nginx example:
```
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "X-Forwarded-Proto: https";
```
## Useful commands
```bash
# see whats going on inside your Deployment
kubectl -n plantuml logs -l "app.kubernetes.io/name=plantuml"
```

View File

@ -1,84 +0,0 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: plantuml
labels:
app.kubernetes.io/name: plantuml
app.kubernetes.io/instance: plantuml
spec:
replicas: 3 # Can be adjusted
selector:
matchLabels:
app.kubernetes.io/name: plantuml
app.kubernetes.io/instance: plantuml
template:
metadata:
labels:
app.kubernetes.io/name: plantuml
app.kubernetes.io/instance: plantuml
spec:
containers:
- name: plantuml
securityContext:
allowPrivilegeEscalation: false
image: plantuml/plantuml-server:jetty-v1.2022.6
imagePullPolicy: Always
# env: # In case of different base URL
# - name: BASE_URL
# value: plantuml
ports:
- name: http
containerPort: 8080
protocol: TCP
livenessProbe:
tcpSocket:
port: http
readinessProbe:
tcpSocket:
port: http
resources:
limits:
cpu: 500m
memory: 2048Mi
requests:
cpu: 250m
memory: 1024Mi
---
apiVersion: v1
kind: Service
metadata:
name: plantuml
labels:
app.kubernetes.io/name: plantuml
app.kubernetes.io/instance: plantuml
spec:
type: ClusterIP
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app.kubernetes.io/name: plantuml
app.kubernetes.io/instance: plantuml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: plantuml
labels:
app.kubernetes.io/name: plantuml
app.kubernetes.io/instance: plantuml
spec:
rules:
- host: plantuml-example.localhost
http:
paths:
- backend:
service:
name: plantuml
port:
number: 80
path: /
pathType: Prefix

View File

@ -1,119 +0,0 @@
# Nginx reverse proxy example with defined location directive
In this example, the reverse proxy is defined only under the `/plantuml` context path.
All other context paths (locations) are not affected and are freely available.
This allows the server to be used for more than "just" PlantUML.
References:
- [Nginx documentation](https://nginx.org/en/docs/)
- [Nginx beginner's guide](https://nginx.org/en/docs/beginners_guide.html)
## Quick start
Be sure to have [`docker-compose.yml`](./docker-compose.yml) and [`nginx.conf`](./nginx.conf) inside your current working directory.
```bash
# start nginx and plantuml server
docker-compose up -d
# stop nginx and plantuml server
docker-compose down
```
Check with `docker ps` if both container are up and running:
```
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
217e753a0dcf plantuml/plantuml-server:jetty "/entrypoint.sh" 4 seconds ago Up 3 seconds 8080/tcp plantuml-server
9b1290c100f5 nginx:alpine "/docker-entrypoint.…" 4 seconds ago Up 3 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp nginx
```
Open [http://localhost/plantuml](http://localhost/plantuml) inside your browser.
YEAH! You are now using PlantUML behind a simple Nginx reverse proxy.
## Nginx configuration
```nginx
...
# PlantUML
location /plantuml/ {
proxy_pass http://plantuml-server:8080/plantuml/;
}
...
```
- `location /plantuml/` to reverse only the context path `/plantuml`
- `proxy_pass http://plantuml-server:8080/plantuml/` to set reverse proxy path to plantuml server.
Use the docker container name `plantuml-server` instead of ip addresses.
Also, use the same context path (`BASE_URL`) as PlantUML, which is configurable as an environment variable in the docker-compose file.
NOTE: `BASE_URL`, `location` and therefore the `proxy_pass` should have the some context path!
If that is not possible it may be possible to solve the problem by using NGINX `sub_filter`:
```nginx
# PlantUML
location /plantuml/ {
sub_filter '<base href="/" />' '<base href="/plantuml/" />';
sub_filter_types text/html;
proxy_pass http://plantuml-server:8080/;
}
```
NOTE: Since [PR#256](https://github.com/plantuml/plantuml-server/pull/256) it is possible to use deep base URLs.
So with e.g. `BASE_URL=foo/bar` the following is possible:
```nginx
# PlantUML
location /foo/bar/ {
proxy_pass http://plantuml-server:8080/foo/bar/;
}
```
## Nginx and PlantUML server
```yaml
version: "3"
services:
plantuml-server:
image: plantuml/plantuml-server:jetty
container_name: plantuml-server
environment:
- TZ=Europe/Berlin
- BASE_URL=plantuml
nginx:
image: nginx:alpine
container_name: nginx
ports:
- "80:80"
environment:
- TZ=Europe/Berlin
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
```
- Set `container_name` to use them instead of e.g. ip addresses
- Set the environment `TZ` the ensure the same timezone.
For example to server timezone (`cat /etc/timezone`)?
- plantuml-server
* plantuml-server already exposes port `8080` to it's own local network (but not outside).
Since plantuml-server and nginx are sharing a network, nginx can reach plantuml-server without further settings.
* Set the environment `BASE_URL` to the preferred context path
- nginx
* open/link port `80` to the outside
* `./nginx.conf:/etc/nginx/nginx.conf:ro` to use your own Nginx configuration (readonly)
## Useful commands
```bash
# see whats going on inside your docker containers
docker logs --tail 50 --follow --timestamps nginx
docker logs --tail 50 --follow --timestamps plantuml-server
```

View File

@ -1,19 +0,0 @@
version: "3"
services:
plantuml-server:
image: plantuml/plantuml-server:jetty
container_name: plantuml-server
environment:
- TZ=Europe/Berlin
- BASE_URL=plantuml
nginx:
image: nginx:alpine
container_name: nginx
ports:
- "80:80"
environment:
- TZ=Europe/Berlin
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro

View File

@ -1,44 +0,0 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name localhost;
# PlantUML
location /plantuml/ {
proxy_pass http://plantuml-server:8080/plantuml/;
}
}
client_max_body_size 0;
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}

View File

@ -1,97 +0,0 @@
# Nginx simple reverse proxy example
References:
- [Nginx documentation](https://nginx.org/en/docs/)
- [Nginx beginner's guide](https://nginx.org/en/docs/beginners_guide.html)
## Quick start
Be sure to have [`docker-compose.yml`](./docker-compose.yml) and [`nginx.conf`](./nginx.conf) inside your current working directory.
```bash
# start nginx and plantuml server
docker-compose up -d
# stop nginx and plantuml server
docker-compose down
```
Check with `docker ps` if both container are up and running:
```
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
217e753a0dcf plantuml/plantuml-server:jetty "/entrypoint.sh" 4 seconds ago Up 3 seconds 8080/tcp plantuml-server
9b1290c100f5 nginx:alpine "/docker-entrypoint.…" 4 seconds ago Up 3 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp nginx
```
Open [http://localhost](http://localhost) inside your browser.
YEAH! You are now using PlantUML behind a simple Nginx reverse proxy.
## Nginx configuration
```nginx
...
# PlantUML
location / {
proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://plantuml-server:8080/;
}
...
```
- `location /` to reverse complete server
- `proxy_set_header HOST $host` and `proxy_set_header X-Forwarded-Host $host` to replaces local plantuml server ip with FQDN
- `proxy_set_header X-Forwarded-Proto $scheme` to use reverse proxy protocol schema instead of communication schema between reverse proxy and plantuml server
- `proxy_pass http://plantuml-server:8080/` to set reverse proxy path to plantuml server.
Use the docker container name `plantuml-server` instead of ip addresses.
## Nginx and PlantUML server
```yaml
version: "3"
services:
plantuml-server:
image: plantuml/plantuml-server:jetty
container_name: plantuml-server
environment:
- TZ=Europe/Berlin
nginx:
image: nginx:alpine
container_name: nginx
ports:
- "80:80"
environment:
- TZ=Europe/Berlin
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
```
- Set `container_name` to use them instead of e.g. ip addresses
- Set the environment `TZ` the ensure the same timezone.
For example to server timezone (`cat /etc/timezone`)?
- plantuml-server
* plantuml-server already exposes port `8080` to it's own local network (but not outside).
Since plantuml-server and nginx are sharing a network, nginx can reach plantuml-server without further settings.
- nginx
* open/link port `80` to the outside
* `./nginx.conf:/etc/nginx/nginx.conf:ro` to use your own Nginx configuration (readonly)
## Useful commands
```bash
# see whats going on inside your docker containers
docker logs --tail 50 --follow --timestamps nginx
docker logs --tail 50 --follow --timestamps plantuml-server
```

View File

@ -1,18 +0,0 @@
version: "3"
services:
plantuml-server:
image: plantuml/plantuml-server:jetty
container_name: plantuml-server
environment:
- TZ=Europe/Berlin
nginx:
image: nginx:alpine
container_name: nginx
ports:
- "80:80"
environment:
- TZ=Europe/Berlin
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro

View File

@ -1,48 +0,0 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name localhost;
# PlantUML
location / {
proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://plantuml-server:8080/;
}
}
client_max_body_size 0;
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}

View File

@ -6,30 +6,112 @@
>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.sourceforge.plantuml</groupId>
<artifactId>plantumlservlet-parent</artifactId>
<version>1-SNAPSHOT</version>
<relativePath>pom.parent.xml</relativePath>
</parent>
<groupId>org.sourceforge.plantuml</groupId>
<artifactId>plantumlservlet</artifactId>
<version>1-SNAPSHOT</version>
<packaging>war</packaging>
<name>PlantUML Servlet</name>
<url>https://plantuml.github.io/plantuml-server/index.html</url>
<properties>
<java.version>8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!--
Skip tests by default.
Run tests manually:
- mvn test -DskipTests=false
- mvn test -DskipTests=false -DargLine="-Dsystem.test.server=http://localhost:8080/plantuml"
-->
<skipTests>true</skipTests>
<!--
This artifact is required for:
1. EmbeddedJettyServer -> scope: test
2. Tomcat docker image -> scope: compile
BUT: Jetty docker image as well as jetty-runner will crash on runtime if
this artifact is included because it's already provided so that the
artifact would apear multiple times on the classpath.
You can test it via: `mvn jetty:run [-Dapache-jsp.scope=compile]`
Error: java.util.ServiceConfigurationError: org.apache.juli.logging.Log: org.eclipse.jetty.apache.jsp.JuliLog not a subtype
HENCE: Default is the "test" scope and for Tomcat docker image building add:
-Dapache-jsp.scope=compile
-->
<apache-jsp.scope>test</apache-jsp.scope>
<maven.build.timestamp.format>yyyyMMdd-HHmm</maven.build.timestamp.format>
<timestamp>${maven.build.timestamp}</timestamp>
<wtp.version>1.5</wtp.version>
<wtp.contextName>plantuml</wtp.contextName>
<jetty.http.port>8080</jetty.http.port>
<jetty.contextpath>/${wtp.contextName}</jetty.contextpath>
<!-- main versions -->
<plantuml.version>1.2021.14</plantuml.version>
<!-- Please keep the jetty version identical with the docker image -->
<jetty.version>11.0.7</jetty.version>
<codemirror.version>5.63.0</codemirror.version>
<slf4j.version>1.7.32</slf4j.version>
<!-- dependencies -->
<jstl.version>1.2</jstl.version>
<apache-jsp.version>${jetty.version}</apache-jsp.version>
<jetty-annotations.version>${jetty.version}</jetty-annotations.version>
<glassfish-jstl.version>${jetty.version}</glassfish-jstl.version>
<batik-all.version>1.14</batik-all.version>
<!-- jlatexmath -->
<jlatexmath.version>1.0.7</jlatexmath.version>
<jlatexmath-font-greek.version>${jlatexmath.version}</jlatexmath-font-greek.version>
<jlatexmath-font-cyrillic.version>${jlatexmath.version}</jlatexmath-font-cyrillic.version>
<!-- Logging -->
<slf4j-log4j12.version>${slf4j.version}</slf4j-log4j12.version>
<slf4j-api.version>${slf4j.version}</slf4j-api.version>
<!-- Testing -->
<junit.version>4.13.2</junit.version>
<htmlunit.version>2.53.0</htmlunit.version>
<jetty-server.version>${jetty.version}</jetty-server.version>
<!-- build plugin management -->
<!-- no JDK8 support starting version 10.0.0 -->
<checkstyle.version>9.3</checkstyle.version>
<!-- lock down plugins versions to avoid using Maven defaults -->
<maven-clean-plugin.version>3.1.0</maven-clean-plugin.version>
<maven-dependency-plugin.version>3.2.0</maven-dependency-plugin.version>
<maven-resources-plugin.version>3.2.0</maven-resources-plugin.version>
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
<versions-maven-plugin.version>2.8.1</versions-maven-plugin.version>
<maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
<maven-war-plugin.version>3.3.2</maven-war-plugin.version>
<maven-install-plugin.version>2.5.2</maven-install-plugin.version>
<maven-deploy-plugin.version>2.8.2</maven-deploy-plugin.version>
<maven-site-plugin.version>3.9.1</maven-site-plugin.version>
<maven-project-info-reports-plugin.version>3.1.2</maven-project-info-reports-plugin.version>
<maven-checkstyle-plugin.version>3.1.2</maven-checkstyle-plugin.version>
<checkstyle.version>9.0.1</checkstyle.version>
<!-- plugins -->
<!-- no JDK8 support starting version 2.5.0 -->
<resources-optimizer-maven-plugin.version>2.4.4</resources-optimizer-maven-plugin.version>
<maven-eclipse-plugin.version>2.10</maven-eclipse-plugin.version>
<jetty-runner.version>${jetty.version}</jetty-runner.version>
<jetty-maven-plugin.version>${jetty.version}</jetty-maven-plugin.version>
<duplicate-finder-maven-plugin.version>1.5.0</duplicate-finder-maven-plugin.version>
<maven-javadoc-plugin.version>3.3.1</maven-javadoc-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>net.sourceforge.plantuml</groupId>
<artifactId>plantuml</artifactId>
<version>${plantuml.version}</version>
</dependency>
<dependency>
<groupId>org.webjars.npm</groupId>
<artifactId>codemirror</artifactId>
<version>${codemirror.version}</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
@ -64,8 +146,53 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<!-- batik-all generally tends to provide duplicate resources on the classpath -->
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-all</artifactId>
<version>${batik-all.version}</version>
<type>pom</type>
</dependency>
<!-- jlatexmath -->
<dependency>
<groupId>org.scilab.forge</groupId>
<artifactId>jlatexmath</artifactId>
<version>${jlatexmath.version}</version>
</dependency>
<dependency>
<groupId>org.scilab.forge</groupId>
<artifactId>jlatexmath-font-greek</artifactId>
<version>${jlatexmath-font-greek.version}</version>
</dependency>
<dependency>
<groupId>org.scilab.forge</groupId>
<artifactId>jlatexmath-font-cyrillic</artifactId>
<version>${jlatexmath-font-cyrillic.version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-log4j12.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>${htmlunit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
@ -79,4 +206,304 @@
</exclusions>
</dependency>
</dependencies>
<build>
<finalName>plantuml</finalName>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>${maven-clean-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>${maven-dependency-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>${maven-resources-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>${maven-war-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>${maven-install-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>${maven-deploy-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>${maven-site-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>${maven-project-info-reports-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${maven-checkstyle-plugin.version}</version>
<dependencies>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>${checkstyle.version}</version>
</dependency>
</dependencies>
<configuration>
<configLocation>${basedir}/src/main/config/checkstyle.xml</configLocation>
<linkXRef>false</linkXRef>
<consoleOutput>true</consoleOutput>
<failsOnError>true</failsOnError>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>${versions-maven-plugin.version}</version>
<configuration>
<outputFile>${project.build.directory}/outdated-dependencies.txt</outputFile>
<rulesUri>file:///${basedir}/src/main/config/rules.xml</rulesUri>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<skipTests>${skipTests}</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>${maven-site-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${maven-checkstyle-plugin.version}</version>
<executions>
<execution>
<?m2e execute onConfiguration,onIncremental?>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>${versions-maven-plugin.version}</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>display-property-updates</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.basepom.maven</groupId>
<artifactId>duplicate-finder-maven-plugin</artifactId>
<version>${duplicate-finder-maven-plugin.version}</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<configuration>
<ignoredResourcePatterns>
<ignoredResourcePattern>^about\.html$</ignoredResourcePattern>
<ignoredResourcePattern>^license/LICENSE\.dom-software\.txt$</ignoredResourcePattern>
<ignoredResourcePattern>^org/apache/batik/apps/rasterizer/resources/rasterizer\.policy$</ignoredResourcePattern>
</ignoredResourcePatterns>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven-javadoc-plugin.version}</version>
<configuration>
<show>private</show>
<nohelp>true</nohelp>
<source>${java.version}</source>
<failOnWarnings>true</failOnWarnings>
</configuration>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>${maven-dependency-plugin.version}</version>
<executions>
<execution>
<?m2e execute onConfiguration,onIncremental?>
<!-- To provide webjars for the embedded jetty server for junit tests -->
<id>unpack-resources</id>
<phase>generate-test-sources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.webjars.npm</groupId>
<artifactId>codemirror</artifactId>
<version>${codemirror.version}</version>
<includes>**/lib/*.js,**/lib/*.css</includes>
<outputDirectory>${project.build.outputDirectory}</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
<execution>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-runner</artifactId>
<version>${jetty-runner.version}</version>
<destFileName>jetty-runner.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>${maven-eclipse-plugin.version}</version>
<configuration>
<wtpversion>${wtp.version}</wtpversion>
<wtpContextName>${wtp.contextName}</wtpContextName>
</configuration>
</plugin>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>${jetty-maven-plugin.version}</version>
<configuration>
<!-- jetty.xml
Only necessary to support old proxy.
The old proxy needs empty path segments support in URIs.
Hence: allow AMBIGUOUS_EMPTY_SEGMENT
-->
<jettyXmls>${basedir}/src/main/config/jetty.xml</jettyXmls>
<scanIntervalSeconds>5</scanIntervalSeconds>
<webApp>
<contextPath>${jetty.contextpath}</contextPath>
</webApp>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${maven-war-plugin.version}</version>
<configuration>
<webResources>
<resource>
<directory>${basedir}/src/main/webapp</directory>
<includes>
<include>*.jspf</include>
</includes>
<filtering>true</filtering>
</resource>
</webResources>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>${maven-project-info-reports-plugin.version}</version>
<reportSets>
<reportSet>
<reports>
<report>index</report>
<report>dependencies</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>${versions-maven-plugin.version}</version>
<reportSets>
<reportSet>
<reports>
<!-- <report>dependency-updates-report</report> -->
<!-- <report>plugin-updates-report</report> -->
<report>property-updates-report</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven-javadoc-plugin.version}</version>
<reportSets>
<reportSet>
<id>html</id>
<reports>
<report>javadoc</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${maven-checkstyle-plugin.version}</version>
<reportSets>
<reportSet>
<reports>
<report>checkstyle</report>
<report>checkstyle-aggregate</report>
</reports>
</reportSet>
</reportSets>
</plugin>
</plugins>
</reporting>
</project>

View File

@ -1,688 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>4.0.0</modelVersion>
<groupId>org.sourceforge.plantuml</groupId>
<artifactId>plantumlservlet-parent</artifactId>
<version>1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>PlantUML Servlet</name>
<url>https://plantuml.github.io/plantuml-server/index.html</url>
<properties>
<!-- NOTE: property `java.version` has to be set inside the child pom. -->
<!-- <java.version>XX</java.version> -->
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!--
Skip tests by default.
Run tests manually:
- mvn test -DskipTests=false
- mvn test -DskipTests=false -DargLine="-Dsystem.test.server=http://localhost:8080/plantuml"
-->
<skipTests>true</skipTests>
<!--
JS and CSS compression / minify
If false minify is enabled.
Dev Tipp: set to `true` and run `mvn fizzed-watcher:run` while developing the frontend
-->
<withoutCSSJSCompress>false</withoutCSSJSCompress>
<!--
This artifact is required for:
1. EmbeddedJettyServer -> scope: test
2. Tomcat docker image -> scope: compile
BUT: Jetty docker image as well as jetty-runner will crash on runtime if
this artifact is included because it's already provided so that the
artifact would apear multiple times on the classpath.
You can test it via: `mvn jetty:run [-Dapache-jsp.scope=compile]`
Error: java.util.ServiceConfigurationError: org.apache.juli.logging.Log: org.eclipse.jetty.apache.jsp.JuliLog not a subtype
HENCE: Default is the "test" scope and for Tomcat docker image building add:
-Dapache-jsp.scope=compile
-->
<apache-jsp.scope>test</apache-jsp.scope>
<maven.build.timestamp.format>yyyyMMdd-HHmm</maven.build.timestamp.format>
<timestamp>${maven.build.timestamp}</timestamp>
<wtp.version>1.5</wtp.version>
<wtp.contextName>plantuml</wtp.contextName>
<jetty.http.port>8080</jetty.http.port>
<jetty.contextpath>/${wtp.contextName}</jetty.contextpath>
<!-- main versions -->
<plantuml.version>1.2024.7</plantuml.version>
<!-- Please keep the jetty version identical with the docker image -->
<jetty.version>11.0.18</jetty.version>
<!--
While changing the version, please update the versions in the following files as well:
- src/main/webapp/components/app-head.jsp (script import)
- src/main/webapp/components/editor/editor.js : loadMonacoCodeEditorAsync (require.config)
- src/test/java/net/sourceforge/plantuml/servlet/TestDependencies.java : testMonacoEditorWebJar (JUnit Test)
-->
<monaco-editor.version>0.36.1</monaco-editor.version>
<!-- dependencies -->
<jstl.version>1.2</jstl.version>
<apache-jsp.version>${jetty.version}</apache-jsp.version>
<jetty-annotations.version>${jetty.version}</jetty-annotations.version>
<glassfish-jstl.version>${jetty.version}</glassfish-jstl.version>
<!-- jlatexmath -->
<jlatexmath.version>1.0.7</jlatexmath.version>
<jlatexmath-font-greek.version>${jlatexmath.version}</jlatexmath-font-greek.version>
<jlatexmath-font-cyrillic.version>${jlatexmath.version}</jlatexmath-font-cyrillic.version>
<!-- PDF -->
<batik.version>1.16</batik.version>
<fop.version>2.8</fop.version>
<!-- Testing -->
<junit.version>5.9.3</junit.version>
<junit-suite.version>1.9.3</junit-suite.version>
<selenium.version>4.10.0</selenium.version>
<selenium-webdrivermanager.version>5.3.3</selenium-webdrivermanager.version>
<commons-io.version>2.11.0</commons-io.version>
<jetty-server.version>${jetty.version}</jetty-server.version>
<!-- build plugin management -->
<!-- lock down plugins versions to avoid using Maven defaults -->
<maven-clean-plugin.version>3.2.0</maven-clean-plugin.version>
<maven-dependency-plugin.version>3.5.0</maven-dependency-plugin.version>
<maven-resources-plugin.version>3.3.1</maven-resources-plugin.version>
<maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
<versions-maven-plugin.version>2.15.0</versions-maven-plugin.version>
<maven-surefire-plugin.version>3.1.0</maven-surefire-plugin.version>
<maven-war-plugin.version>3.3.2</maven-war-plugin.version>
<maven-install-plugin.version>3.1.1</maven-install-plugin.version>
<maven-deploy-plugin.version>3.1.1</maven-deploy-plugin.version>
<maven-site-plugin.version>3.12.1</maven-site-plugin.version>
<maven-project-info-reports-plugin.version>3.4.3</maven-project-info-reports-plugin.version>
<maven-checkstyle-plugin.version>3.2.2</maven-checkstyle-plugin.version>
<checkstyle.version>10.12.0</checkstyle.version>
<!-- plugins -->
<maven-eclipse-plugin.version>2.10</maven-eclipse-plugin.version>
<jetty-runner.version>${jetty.version}</jetty-runner.version>
<jetty-maven-plugin.version>${jetty.version}</jetty-maven-plugin.version>
<duplicate-finder-maven-plugin.version>1.5.1</duplicate-finder-maven-plugin.version>
<maven-javadoc-plugin.version>3.5.0</maven-javadoc-plugin.version>
<resources-optimizer-maven-plugin.version>2.5.6</resources-optimizer-maven-plugin.version>
<fizzed-watcher-maven-plugin.verson>1.0.6</fizzed-watcher-maven-plugin.verson>
</properties>
<dependencies>
<dependency>
<groupId>net.sourceforge.plantuml</groupId>
<artifactId>plantuml</artifactId>
<version>${plantuml.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.webjars.npm</groupId>
<artifactId>monaco-editor</artifactId>
<version>${monaco-editor.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>apache-jsp</artifactId>
<version>${apache-jsp.version}</version>
<scope>${apache-jsp.scope}</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-annotations</artifactId>
<version>${jetty-annotations.version}</version>
<scope>provided</scope>
</dependency>
<!-- jlatexmath -->
<dependency>
<groupId>org.scilab.forge</groupId>
<artifactId>jlatexmath</artifactId>
<version>${jlatexmath.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.scilab.forge</groupId>
<artifactId>jlatexmath-font-greek</artifactId>
<version>${jlatexmath-font-greek.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.scilab.forge</groupId>
<artifactId>jlatexmath-font-cyrillic</artifactId>
<version>${jlatexmath-font-cyrillic.version}</version>
<scope>runtime</scope>
</dependency>
<!-- PDF
PlantUMLs PDF generation requires:
- batik-dom
- batik-svgrasterizer (includes batik-dom)
- batik-svggen
- fop-core
-->
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-svgrasterizer</artifactId>
<version>${batik.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-svggen</artifactId>
<version>${batik.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>fop-core</artifactId>
<version>${fop.version}</version>
<scope>runtime</scope>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite-api</artifactId>
<version>${junit-suite.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>${selenium-webdrivermanager.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty-server.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.elk</groupId>
<artifactId>org.eclipse.elk.core</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.elk</groupId>
<artifactId>org.eclipse.elk.alg.layered</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.elk</groupId>
<artifactId>org.eclipse.elk.alg.mrtree</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
<build>
<finalName>plantuml</finalName>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>${maven-clean-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>${maven-dependency-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>${maven-resources-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>${maven-war-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>${maven-install-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>${maven-deploy-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>${maven-site-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>${maven-project-info-reports-plugin.version}</version>
</plugin>
<!-- set up java style rules -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${maven-checkstyle-plugin.version}</version>
<dependencies>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>${checkstyle.version}</version>
</dependency>
</dependencies>
<configuration>
<configLocation>${basedir}/src/main/config/checkstyle.xml</configLocation>
<linkXRef>false</linkXRef>
<consoleOutput>true</consoleOutput>
<failsOnError>true</failsOnError>
</configuration>
</plugin>
<!-- set up version validation rules -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>${versions-maven-plugin.version}</version>
<configuration>
<outputFile>${project.build.directory}/outdated-dependencies.txt</outputFile>
<rulesUri>file:///${basedir}/src/main/config/rules.xml</rulesUri>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<!-- set java compile version -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<!-- configure surefire to skip unit tests if skipTests is set -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<skipTests>${skipTests}</skipTests>
</configuration>
</plugin>
<!-- configure plugin for project's reports -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>${maven-site-plugin.version}</version>
</plugin>
<!-- setup java style checks -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${maven-checkstyle-plugin.version}</version>
<executions>
<execution>
<?m2e execute onConfiguration,onIncremental?>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- check and display possible version updates during validation
manual execution:
- mvn versions:display-property-updates
- mvn versions:display-dependency-updates
-->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>${versions-maven-plugin.version}</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>display-property-updates</goal>
<goal>display-dependency-updates</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- check for duplicate classes/resources
also see:
- mvn dependency:analyze
- mvn dependency:tree
-->
<plugin>
<groupId>org.basepom.maven</groupId>
<artifactId>duplicate-finder-maven-plugin</artifactId>
<version>${duplicate-finder-maven-plugin.version}</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<configuration>
<ignoredResourcePatterns>
<ignoredResourcePattern>^about\.html$</ignoredResourcePattern>
<ignoredResourcePattern>^license/LICENSE\.dom-software\.txt$</ignoredResourcePattern>
<!-- <ignoredResourcePattern>^org/apache/batik/apps/rasterizer/resources/rasterizer\.policy$</ignoredResourcePattern> -->
</ignoredResourcePatterns>
</configuration>
</plugin>
<!-- check for missing java documentation -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven-javadoc-plugin.version}</version>
<configuration>
<show>private</show>
<nohelp>true</nohelp>
<source>${java.version}</source>
<failOnWarnings>true</failOnWarnings>
</configuration>
</plugin>
<!-- provide dependencies -->
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>${maven-dependency-plugin.version}</version>
<executions>
<!-- provide webjars for the embedded jetty server for junit tests -->
<execution>
<?m2e execute onConfiguration,onIncremental?>
<id>unpack-resources</id>
<phase>generate-test-sources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.webjars.npm</groupId>
<artifactId>monaco-editor</artifactId>
<version>${monaco-editor.version}</version>
<includes>**/min/vs/loader.js,**/min/vs/**/*,**/min-maps/vs/**/*</includes>
<outputDirectory>${project.build.outputDirectory}</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
<!-- provide jetty-runner -->
<execution>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-runner</artifactId>
<version>${jetty-runner.version}</version>
<destFileName>jetty-runner.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<!-- configure eclipse web tools platform (WTP) -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>${maven-eclipse-plugin.version}</version>
<configuration>
<wtpversion>${wtp.version}</wtpversion>
<wtpContextName>${wtp.contextName}</wtpContextName>
</configuration>
</plugin>
<!-- configure jetty -->
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>${jetty-maven-plugin.version}</version>
<configuration>
<!-- jetty.xml
Only necessary to support old proxy.
The old proxy needs empty path segments support in URIs.
Hence: allow AMBIGUOUS_EMPTY_SEGMENT
-->
<jettyXmls>${basedir}/src/main/config/jetty.xml</jettyXmls>
<scanIntervalSeconds>5</scanIntervalSeconds>
<webApp>
<contextPath>${jetty.contextpath}</contextPath>
</webApp>
</configuration>
</plugin>
<!-- configure java server pages (JSP) web resources -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${maven-war-plugin.version}</version>
<configuration>
<webResources>
<resource>
<directory>${basedir}/src/main/webapp</directory>
<includes>
<include>*.jspf</include>
</includes>
<filtering>true</filtering>
</resource>
</webResources>
</configuration>
</plugin>
<!-- remove minified web resources (css, js) before regeneration -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<executions>
<execution>
<id>clean-minified-resources</id>
<phase>initialize</phase>
<goals>
<goal>clean</goal>
</goals>
<configuration>
<excludeDefaultDirectories>true</excludeDefaultDirectories>
<filesets>
<fileset>
<directory>${basedir}/src/main/webapp/min</directory>
</fileset>
</filesets>
</configuration>
</execution>
</executions>
</plugin>
<!-- optimize/minimize web resources (css, js) -->
<plugin>
<groupId>org.primefaces.extensions</groupId>
<artifactId>resources-optimizer-maven-plugin</artifactId>
<version>${resources-optimizer-maven-plugin.version}</version>
<executions>
<execution>
<id>optimize</id>
<phase>generate-resources</phase>
<goals>
<goal>optimize</goal>
</goals>
</execution>
</executions>
<configuration>
<warningLevel>DEFAULT</warningLevel>
<failOnWarning>true</failOnWarning>
<suffix>.min</suffix>
<languageIn>ECMASCRIPT_2020</languageIn>
<languageOut>ECMASCRIPT5_STRICT</languageOut>
<emitUseStrict>true</emitUseStrict>
<resourcesSets>
<!-- combine and optimize all JS files for the web server except the PlantUML JS language features -->
<resourcesSet>
<inputDir>${basedir}/src/main/webapp</inputDir>
<includes>
<include>components/**/*.js</include>
<include>js/**/*.js</include>
</includes>
<excludes>
<exclude>js/language/**</exclude>
</excludes>
<aggregations>
<aggregation>
<withoutCompress>${withoutCSSJSCompress}</withoutCompress>
<removeIncluded>false</removeIncluded>
<outputFile>${basedir}/src/main/webapp/min/plantuml.min.js</outputFile>
</aggregation>
</aggregations>
</resourcesSet>
<!-- combine and optimize all PlantUML JS language features -->
<resourcesSet>
<inputDir>${basedir}/src/main/webapp/js/language</inputDir>
<includes>
<include>language.js</include>
<include>validation/validation.js</include>
<include>**/*.js</include>
</includes>
<aggregations>
<aggregation>
<withoutCompress>${withoutCSSJSCompress}</withoutCompress>
<removeIncluded>false</removeIncluded>
<outputFile>${basedir}/src/main/webapp/min/plantuml-language.min.js</outputFile>
</aggregation>
</aggregations>
</resourcesSet>
<!-- combine and optimize all web server style files -->
<resourcesSet>
<inputDir>${basedir}/src/main/webapp/components</inputDir>
<includes>
<include>**/*.css</include>
</includes>
<aggregations>
<aggregation>
<withoutCompress>${withoutCSSJSCompress}</withoutCompress>
<removeIncluded>false</removeIncluded>
<outputFile>${basedir}/src/main/webapp/min/plantuml.min.css</outputFile>
</aggregation>
</aggregations>
</resourcesSet>
</resourcesSets>
</configuration>
</plugin>
<!-- watch for changes in web resources (css, js) and regenerate minified resources (only for development) -->
<plugin>
<groupId>com.fizzed</groupId>
<artifactId>fizzed-watcher-maven-plugin</artifactId>
<version>${fizzed-watcher-maven-plugin.verson}</version>
<configuration>
<watches>
<watch>
<directory>${basedir}/src/main/webapp/components</directory>
<recursive>true</recursive>
<includes>
<include>*.js</include>
<include>*.css</include>
</includes>
<excludes>
<exclude>*.min.js</exclude>
<exclude>*.min.css</exclude>
</excludes>
</watch>
</watches>
<goals>
<goal>clean:clean@clean-minified-resources</goal>
<goal>org.primefaces.extensions:resources-optimizer-maven-plugin:optimize</goal>
</goals>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>${maven-project-info-reports-plugin.version}</version>
<reportSets>
<reportSet>
<reports>
<report>index</report>
<report>dependencies</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>${versions-maven-plugin.version}</version>
<reportSets>
<reportSet>
<reports>
<!-- <report>dependency-updates-report</report> -->
<!-- <report>plugin-updates-report</report> -->
<report>property-updates-report</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven-javadoc-plugin.version}</version>
<reportSets>
<reportSet>
<id>html</id>
<reports>
<report>javadoc</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${maven-checkstyle-plugin.version}</version>
<reportSets>
<reportSet>
<reports>
<report>checkstyle</report>
<report>checkstyle-aggregate</report>
</reports>
</reportSet>
</reportSets>
</plugin>
</plugins>
</reporting>
</project>

473
pom.xml
View File

@ -6,17 +6,476 @@
>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.sourceforge.plantuml</groupId>
<artifactId>plantumlservlet-parent</artifactId>
<version>1-SNAPSHOT</version>
<relativePath>pom.parent.xml</relativePath>
</parent>
<groupId>org.sourceforge.plantuml</groupId>
<artifactId>plantumlservlet</artifactId>
<version>1-SNAPSHOT</version>
<packaging>war</packaging>
<name>PlantUML Servlet</name>
<url>https://plantuml.github.io/plantuml-server/index.html</url>
<properties>
<java.version>11</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!--
Skip tests by default.
Run tests manually:
- mvn test -DskipTests=false
- mvn test -DskipTests=false -DargLine="-Dsystem.test.server=http://localhost:8080/plantuml"
-->
<skipTests>true</skipTests>
<!--
This artifact is required for:
1. EmbeddedJettyServer -> scope: test
2. Tomcat docker image -> scope: compile
BUT: Jetty docker image as well as jetty-runner will crash on runtime if
this artifact is included because it's already provided so that the
artifact would apear multiple times on the classpath.
You can test it via: `mvn jetty:run [-Dapache-jsp.scope=compile]`
Error: java.util.ServiceConfigurationError: org.apache.juli.logging.Log: org.eclipse.jetty.apache.jsp.JuliLog not a subtype
HENCE: Default is the "test" scope and for Tomcat docker image building add:
-Dapache-jsp.scope=compile
-->
<apache-jsp.scope>test</apache-jsp.scope>
<maven.build.timestamp.format>yyyyMMdd-HHmm</maven.build.timestamp.format>
<timestamp>${maven.build.timestamp}</timestamp>
<wtp.version>1.5</wtp.version>
<wtp.contextName>plantuml</wtp.contextName>
<jetty.http.port>8080</jetty.http.port>
<jetty.contextpath>/${wtp.contextName}</jetty.contextpath>
<!-- main versions -->
<plantuml.version>1.2021.14</plantuml.version>
<!-- Please keep the jetty version identical with the docker image -->
<jetty.version>11.0.7</jetty.version>
<codemirror.version>5.63.0</codemirror.version>
<slf4j.version>1.7.32</slf4j.version>
<!-- dependencies -->
<jstl.version>1.2</jstl.version>
<apache-jsp.version>${jetty.version}</apache-jsp.version>
<jetty-annotations.version>${jetty.version}</jetty-annotations.version>
<glassfish-jstl.version>${jetty.version}</glassfish-jstl.version>
<batik-all.version>1.14</batik-all.version>
<!-- jlatexmath -->
<jlatexmath.version>1.0.7</jlatexmath.version>
<jlatexmath-font-greek.version>${jlatexmath.version}</jlatexmath-font-greek.version>
<jlatexmath-font-cyrillic.version>${jlatexmath.version}</jlatexmath-font-cyrillic.version>
<!-- Logging -->
<slf4j-log4j12.version>${slf4j.version}</slf4j-log4j12.version>
<slf4j-api.version>${slf4j.version}</slf4j-api.version>
<!-- Testing -->
<junit.version>4.13.2</junit.version>
<htmlunit.version>2.53.0</htmlunit.version>
<jetty-server.version>${jetty.version}</jetty-server.version>
<!-- build plugin management -->
<!-- lock down plugins versions to avoid using Maven defaults -->
<maven-clean-plugin.version>3.1.0</maven-clean-plugin.version>
<maven-dependency-plugin.version>3.2.0</maven-dependency-plugin.version>
<maven-resources-plugin.version>3.2.0</maven-resources-plugin.version>
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
<versions-maven-plugin.version>2.8.1</versions-maven-plugin.version>
<maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
<maven-war-plugin.version>3.3.2</maven-war-plugin.version>
<maven-install-plugin.version>2.5.2</maven-install-plugin.version>
<maven-deploy-plugin.version>2.8.2</maven-deploy-plugin.version>
<maven-site-plugin.version>3.9.1</maven-site-plugin.version>
<maven-project-info-reports-plugin.version>3.1.2</maven-project-info-reports-plugin.version>
<maven-checkstyle-plugin.version>3.1.2</maven-checkstyle-plugin.version>
<checkstyle.version>9.0.1</checkstyle.version>
<!-- plugins -->
<maven-eclipse-plugin.version>2.10</maven-eclipse-plugin.version>
<jetty-runner.version>${jetty.version}</jetty-runner.version>
<jetty-maven-plugin.version>${jetty.version}</jetty-maven-plugin.version>
<duplicate-finder-maven-plugin.version>1.5.0</duplicate-finder-maven-plugin.version>
<maven-javadoc-plugin.version>3.3.1</maven-javadoc-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>net.sourceforge.plantuml</groupId>
<artifactId>plantuml</artifactId>
<version>${plantuml.version}</version>
</dependency>
<dependency>
<groupId>org.webjars.npm</groupId>
<artifactId>codemirror</artifactId>
<version>${codemirror.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>apache-jsp</artifactId>
<version>${apache-jsp.version}</version>
<scope>${apache-jsp.scope}</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-annotations</artifactId>
<version>${jetty-annotations.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<!-- batik-all generally tends to provide duplicate resources on the classpath -->
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-all</artifactId>
<version>${batik-all.version}</version>
<type>pom</type>
</dependency>
<!-- jlatexmath -->
<dependency>
<groupId>org.scilab.forge</groupId>
<artifactId>jlatexmath</artifactId>
<version>${jlatexmath.version}</version>
</dependency>
<dependency>
<groupId>org.scilab.forge</groupId>
<artifactId>jlatexmath-font-greek</artifactId>
<version>${jlatexmath-font-greek.version}</version>
</dependency>
<dependency>
<groupId>org.scilab.forge</groupId>
<artifactId>jlatexmath-font-cyrillic</artifactId>
<version>${jlatexmath-font-cyrillic.version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-log4j12.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>${htmlunit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty-server.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>plantuml</finalName>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>${maven-clean-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>${maven-dependency-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>${maven-resources-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>${maven-war-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>${maven-install-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>${maven-deploy-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>${maven-site-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>${maven-project-info-reports-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${maven-checkstyle-plugin.version}</version>
<dependencies>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>${checkstyle.version}</version>
</dependency>
</dependencies>
<configuration>
<configLocation>${basedir}/src/main/config/checkstyle.xml</configLocation>
<linkXRef>false</linkXRef>
<consoleOutput>true</consoleOutput>
<failsOnError>true</failsOnError>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>${versions-maven-plugin.version}</version>
<configuration>
<outputFile>${project.build.directory}/outdated-dependencies.txt</outputFile>
<rulesUri>file:///${basedir}/src/main/config/rules.xml</rulesUri>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<skipTests>${skipTests}</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>${maven-site-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${maven-checkstyle-plugin.version}</version>
<executions>
<execution>
<?m2e execute onConfiguration,onIncremental?>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>${versions-maven-plugin.version}</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>display-property-updates</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.basepom.maven</groupId>
<artifactId>duplicate-finder-maven-plugin</artifactId>
<version>${duplicate-finder-maven-plugin.version}</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<configuration>
<ignoredResourcePatterns>
<ignoredResourcePattern>^about\.html$</ignoredResourcePattern>
<ignoredResourcePattern>^license/LICENSE\.dom-software\.txt$</ignoredResourcePattern>
<ignoredResourcePattern>^org/apache/batik/apps/rasterizer/resources/rasterizer\.policy$</ignoredResourcePattern>
</ignoredResourcePatterns>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven-javadoc-plugin.version}</version>
<configuration>
<show>private</show>
<nohelp>true</nohelp>
<source>${java.version}</source>
<failOnWarnings>true</failOnWarnings>
</configuration>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>${maven-dependency-plugin.version}</version>
<executions>
<execution>
<?m2e execute onConfiguration,onIncremental?>
<!-- To provide webjars for the embedded jetty server for junit tests -->
<id>unpack-resources</id>
<phase>generate-test-sources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.webjars.npm</groupId>
<artifactId>codemirror</artifactId>
<version>${codemirror.version}</version>
<includes>**/lib/*.js,**/lib/*.css</includes>
<outputDirectory>${project.build.outputDirectory}</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
<execution>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-runner</artifactId>
<version>${jetty-runner.version}</version>
<destFileName>jetty-runner.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>${maven-eclipse-plugin.version}</version>
<configuration>
<wtpversion>${wtp.version}</wtpversion>
<wtpContextName>${wtp.contextName}</wtpContextName>
</configuration>
</plugin>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>${jetty-maven-plugin.version}</version>
<configuration>
<!-- jetty.xml
Only necessary to support old proxy.
The old proxy needs empty path segments support in URIs.
Hence: allow AMBIGUOUS_EMPTY_SEGMENT
-->
<jettyXmls>${basedir}/src/main/config/jetty.xml</jettyXmls>
<scanIntervalSeconds>5</scanIntervalSeconds>
<webApp>
<contextPath>${jetty.contextpath}</contextPath>
</webApp>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${maven-war-plugin.version}</version>
<configuration>
<webResources>
<resource>
<directory>${basedir}/src/main/webapp</directory>
<includes>
<include>*.jspf</include>
</includes>
<filtering>true</filtering>
</resource>
</webResources>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>${maven-project-info-reports-plugin.version}</version>
<reportSets>
<reportSet>
<reports>
<report>index</report>
<report>dependencies</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>${versions-maven-plugin.version}</version>
<reportSets>
<reportSet>
<reports>
<!-- <report>dependency-updates-report</report> -->
<!-- <report>plugin-updates-report</report> -->
<report>property-updates-report</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven-javadoc-plugin.version}</version>
<reportSets>
<reportSet>
<id>html</id>
<reports>
<report>javadoc</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${maven-checkstyle-plugin.version}</version>
<reportSets>
<reportSet>
<reports>
<report>checkstyle</report>
<report>checkstyle-aggregate</report>
</reports>
</reportSet>
</reportSets>
</plugin>
</plugins>
</reporting>
</project>

BIN
screenshots/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -15,8 +15,6 @@
<module name="LineLength">
<property name="max" value="120" />
<property name="tabWidth" value="4" />
<!-- ignore java doc including links, e.g.: `* @see <a href="https://...">PlantUML Code</a>` -->
<property name="ignorePattern" value="^\s*(\*|//).*@see\s.*?\shref=.*$"/>
</module>
<module name="NewlineAtEndOfFile">
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit" />
@ -27,7 +25,6 @@
<property name="message" value="Line has trailing spaces." />
</module>
<module name="Translation" />
<module name="SuppressWarningsFilter" />
<module name="TreeWalker">
<module name="ArrayTypeStyle" />
<module name="AvoidInlineConditionals">
@ -116,6 +113,5 @@
<module name="VisibilityModifier" />
<module name="WhitespaceAfter" />
<module name="WhitespaceAround" />
<module name="SuppressWarningsHolder" />
</module>
</module>

View File

@ -64,8 +64,7 @@
</Array>
</Arg>
<!-- Change host and port according to properties. Default is 0.0.0.0 and 8080. -->
<Set name="host"><Property name="jetty.http.host" deprecated="jetty.host" default="0.0.0.0" /></Set>
<!-- Change port according to property. Default is 8080 -->
<Set name="port"><Property name="jetty.http.port" deprecated="jetty.port" default="8080" /></Set>
</New>

View File

@ -14,7 +14,6 @@
<ignoreVersion type="regex">(?i).*RC(?:-?\d+)?</ignoreVersion>
<ignoreVersion type="regex">(?i).*CR(?:-?\d+)?</ignoreVersion>
<ignoreVersion type="regex">(?i).*M(?:-?\d+)?</ignoreVersion>
<ignoreVersion type="regex">(?i).*-dev((?:-?\d+)|(?:\.20\d{6}))?</ignoreVersion>
</ignoreVersions>
<rules>
<rule groupId="net.sourceforge.plantuml" artifactId="plantuml" comparisonMethod="maven">

View File

@ -1,151 +0,0 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package net.sourceforge.plantuml.servlet;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.sourceforge.plantuml.code.Transcoder;
import net.sourceforge.plantuml.code.TranscoderUtil;
import net.sourceforge.plantuml.servlet.utility.UrlDataExtractor;
/**
* ASCII encoder and decoder servlet for the webapp.
* This servlet encodes the diagram in text format or decodes the compressed diagram string.
*/
@SuppressWarnings("SERIAL")
public class AsciiCoderServlet extends HttpServlet {
/**
* Regex pattern to fetch last part of the URL.
*/
private static final Pattern URL_PATTERN = Pattern.compile("^.*[^a-zA-Z0-9\\-\\_]([a-zA-Z0-9\\-\\_]+)");
/**
* Context path from the servlet mapping URL pattern.
*
* @return servlet context path without leading or tailing slash
*/
protected String getServletContextPath() {
return "coder";
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
final String encodedText = getEncodedTextFromUrl(request);
String text = "";
try {
text = getTranscoder().decode(encodedText);
} catch (Exception e) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
e.printStackTrace();
}
response.addHeader("Access-Control-Allow-Origin", "*");
response.setContentType("text/plain;charset=UTF-8");
response.getWriter().write(text);
}
@Override
protected void doPost(
HttpServletRequest request,
HttpServletResponse response
) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
// read textual diagram source from request body
final StringBuilder uml = new StringBuilder();
try (BufferedReader in = request.getReader()) {
String line;
while ((line = in.readLine()) != null) {
uml.append(line).append('\n');
}
}
// encode textual diagram source
String encoded = "";
try {
encoded = getTranscoder().encode(uml.toString());
} catch (Exception e) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
e.printStackTrace();
}
response.addHeader("Access-Control-Allow-Origin", "*");
response.setContentType("text/plain;charset=UTF-8");
response.getWriter().write(encoded);
}
/**
* Get PlantUML transcoder.
*
* @return transcoder instance
*/
protected Transcoder getTranscoder() {
return TranscoderUtil.getDefaultTranscoder();
}
/**
* Get encoded textual diagram source from URL.
*
* @param request http request which contains the source URL
*
* @return if successful encoded textual diagram source from URL; otherwise empty string
*
* @throws IOException if an input or output exception occurred
*/
protected String getEncodedTextFromUrl(HttpServletRequest request) throws IOException {
// textual diagram source from request URI
String url = request.getRequestURI();
final String contextpath = "/" + getServletContextPath() + "/";
if (url.contains(contextpath) && !url.endsWith(contextpath)) {
final String encoded = UrlDataExtractor.getEncodedDiagram(request.getRequestURI(), "");
if (!encoded.isEmpty()) {
return encoded;
}
}
// textual diagram source from "url" parameter
url = request.getParameter("url");
if (url != null && !url.trim().isEmpty()) {
// Catch the last part of the URL if necessary
final Matcher matcher = URL_PATTERN.matcher(url);
if (matcher.find()) {
url = matcher.group(1);
}
return url;
}
// nothing found
return "";
}
}

View File

@ -23,14 +23,12 @@
*/
package net.sourceforge.plantuml.servlet;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@ -40,16 +38,14 @@ import net.sourceforge.plantuml.ErrorUml;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.NullOutputStream;
import net.sourceforge.plantuml.OptionFlags;
import net.sourceforge.plantuml.SourceStringReader;
import net.sourceforge.plantuml.StringUtils;
import net.sourceforge.plantuml.utils.Base64Coder;
import net.sourceforge.plantuml.code.Base64Coder;
import net.sourceforge.plantuml.core.Diagram;
import net.sourceforge.plantuml.core.DiagramDescription;
import net.sourceforge.plantuml.core.ImageData;
import net.sourceforge.plantuml.error.PSystemError;
import net.sourceforge.plantuml.preproc.Defines;
import net.sourceforge.plantuml.security.SecurityProfile;
import net.sourceforge.plantuml.security.SecurityUtils;
import net.sourceforge.plantuml.version.Version;
/**
@ -58,33 +54,27 @@ import net.sourceforge.plantuml.version.Version;
*/
public class DiagramResponse {
private static class BlockSelection {
private final BlockUml block;
private final int systemIdx;
BlockSelection(BlockUml blk, int idx) {
block = blk;
systemIdx = idx;
}
}
/**
* {@link FileFormat} to http content type mapping.
*/
private static final Map<FileFormat, String> CONTENT_TYPE;
/**
* X-Powered-By http header value included in every response by default.
*/
private static final String POWERED_BY = "PlantUML Version " + Version.versionString();
/**
* PLANTUML_CONFIG_FILE content.
*/
private static final List<String> CONFIG = new ArrayList<>();
/**
* Cache/flag to ensure that the `init()` method is called only once.
*/
private static boolean initialized = false;
static {
init();
OptionFlags.ALLOW_INCLUDE = false;
if ("true".equalsIgnoreCase(System.getenv("ALLOW_PLANTUML_INCLUDE"))) {
OptionFlags.ALLOW_INCLUDE = true;
}
CONTENT_TYPE = Collections.unmodifiableMap(new HashMap<FileFormat, String>() {{
put(FileFormat.PNG, "image/png");
put(FileFormat.SVG, "image/svg+xml");
put(FileFormat.EPS, "application/postscript");
put(FileFormat.UTXT, "text/plain;charset=UTF-8");
put(FileFormat.BASE64, "text/plain; charset=x-user-defined");
}});
}
/**
@ -113,42 +103,6 @@ public class DiagramResponse {
request = req;
}
/**
* Initialize PlantUML configurations and properties as well as loading the PlantUML config file.
*/
public static void init() {
if (initialized) {
return;
}
initialized = true;
// set headless mode manually since otherwise Windows 11 seems to have some issues with it
// see Issue#311 :: https://github.com/plantuml/plantuml-server/issues/311
// NOTE: This can only be set before any awt/X11/... related stuff is loaded
System.setProperty("java.awt.headless", System.getProperty("java.awt.headless", "true"));
// set security profile to INTERNET by default
// NOTE: this property is cached inside PlantUML and cannot be changed after the first call of PlantUML
System.setProperty("PLANTUML_SECURITY_PROFILE", SecurityProfile.INTERNET.toString());
if (System.getenv("PLANTUML_SECURITY_PROFILE") != null) {
System.setProperty("PLANTUML_SECURITY_PROFILE", System.getenv("PLANTUML_SECURITY_PROFILE"));
}
// load properties from file
if (System.getenv("PLANTUML_PROPERTY_FILE") != null) {
try (FileReader propertyFileReader = new FileReader(System.getenv("PLANTUML_PROPERTY_FILE"))) {
System.getProperties().load(propertyFileReader);
} catch (IOException e) {
e.printStackTrace();
}
}
// load PlantUML config file
if (System.getenv("PLANTUML_CONFIG_FILE") != null) {
try (BufferedReader br = new BufferedReader(new FileReader(System.getenv("PLANTUML_CONFIG_FILE")))) {
br.lines().forEach(CONFIG::add);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* Render and send a specific uml diagram.
*
@ -160,17 +114,7 @@ public class DiagramResponse {
public void sendDiagram(String uml, int idx) throws IOException {
response.addHeader("Access-Control-Allow-Origin", "*");
response.setContentType(getContentType());
if (idx < 0) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, String.format("Invalid diagram index: {0}", idx));
return;
}
final SourceStringReader reader = getSourceStringReader(uml);
if (reader == null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "No UML diagram found");
return;
}
SourceStringReader reader = new SourceStringReader(uml);
if (format == FileFormat.BASE64) {
byte[] imageBytes;
try (ByteArrayOutputStream outstream = new ByteArrayOutputStream()) {
@ -182,86 +126,20 @@ public class DiagramResponse {
response.getOutputStream().write(encodedBytes.getBytes());
return;
}
final BlockSelection blockSelection = getOutputBlockSelection(reader, idx);
if (blockSelection == null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
if (notModified(blockSelection.block)) {
addHeaderForCache(blockSelection.block);
final BlockUml blockUml = reader.getBlocks().get(0);
if (notModified(blockUml)) {
addHeaderForCache(blockUml);
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
if (StringUtils.isDiagramCacheable(uml)) {
addHeaderForCache(blockSelection.block);
addHeaderForCache(blockUml);
}
final Diagram diagram = blockSelection.block.getDiagram();
final Diagram diagram = blockUml.getDiagram();
if (diagram instanceof PSystemError) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
diagram.exportDiagram(response.getOutputStream(), blockSelection.systemIdx, new FileFormatOption(format));
}
private BlockSelection getOutputBlockSelection(SourceStringReader reader, int numImage) {
if (numImage < 0) {
return null;
}
Collection<BlockUml> blocks = reader.getBlocks();
if (blocks.isEmpty()) {
return null;
}
for (BlockUml b : blocks) {
final Diagram system = b.getDiagram();
final int nbInSystem = system.getNbImages();
if (numImage < nbInSystem) {
return new BlockSelection(b, numImage);
}
numImage -= nbInSystem;
}
return null;
}
private SourceStringReader getSourceStringReader(String uml) {
SourceStringReader reader = getSourceStringReaderWithConfig(uml);
if (reader.getBlocks().isEmpty()) {
uml = "@startuml\n" + uml + "\n@enduml";
reader = getSourceStringReaderWithConfig(uml);
if (reader.getBlocks().isEmpty()) {
return null;
}
}
return reader;
}
private SourceStringReader getSourceStringReaderWithConfig(String uml) {
final Defines defines = getPreProcDefines();
SourceStringReader reader = new SourceStringReader(defines, uml, CONFIG);
if (!CONFIG.isEmpty() && reader.getBlocks().get(0).getDiagram().getWarningOrError() != null) {
reader = new SourceStringReader(defines, uml);
}
return reader;
}
/**
* Get PlantUML preprocessor defines.
*
* @return preprocessor defines
*/
private Defines getPreProcDefines() {
final Defines defines;
if (SecurityUtils.getSecurityProfile() == SecurityProfile.UNSECURE) {
// set dirpath to current dir but keep filename and filenameNoExtension undefined
defines = Defines.createWithFileName(new java.io.File("dummy.puml"));
defines.overrideFilename("");
} else {
defines = Defines.createEmpty();
}
return defines;
diagram.exportDiagram(response.getOutputStream(), idx, new FileFormatOption(format));
}
/**
@ -293,36 +171,18 @@ public class DiagramResponse {
* @throws IOException if an input or output exception occurred
*/
public void sendMap(String uml, int idx) throws IOException {
response.addHeader("Access-Control-Allow-Origin", "*");
response.setContentType(getContentType());
if (idx < 0) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, String.format("Invalid diagram index: {0}", idx));
return;
idx = 0;
}
final SourceStringReader reader = getSourceStringReader(uml);
if (reader == null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "No UML diagram found");
return;
}
final BlockSelection blockSelection = getOutputBlockSelection(reader, idx);
if (blockSelection == null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
response.setContentType(getContentType());
SourceStringReader reader = new SourceStringReader(uml);
final BlockUml blockUml = reader.getBlocks().get(0);
if (StringUtils.isDiagramCacheable(uml)) {
addHeaderForCache(blockSelection.block);
addHeaderForCache(blockUml);
}
final Diagram diagram = blockSelection.block.getDiagram();
if (diagram instanceof PSystemError) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
ImageData map = diagram.exportDiagram(
new NullOutputStream(),
blockSelection.systemIdx,
new FileFormatOption(FileFormat.PNG, false)
);
final Diagram diagram = blockUml.getDiagram();
ImageData map = diagram.exportDiagram(new NullOutputStream(), idx,
new FileFormatOption(FileFormat.PNG, false));
if (map.containsCMapData()) {
PrintWriter httpOut = response.getWriter();
final String cmap = map.getCMapData("plantuml");
@ -393,7 +253,7 @@ public class DiagramResponse {
* @return response content type
*/
private String getContentType() {
return format.getMimeType();
return CONTENT_TYPE.get(format);
}
}

View File

@ -1,376 +0,0 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package net.sourceforge.plantuml.servlet;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Paths;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.MultipartConfig;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Part;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.code.NoPlantumlCompressionException;
import net.sourceforge.plantuml.code.TranscoderUtil;
import net.sourceforge.plantuml.json.JsonObject;
import net.sourceforge.plantuml.klimt.drawing.svg.SvgGraphics;
import net.sourceforge.plantuml.png.MetadataTag;
/**
* Meta data servlet for the webapp.
* This servlet responses with the meta data of a specific file as text report or JSON object.
*/
@SuppressWarnings("SERIAL")
@MultipartConfig
public class MetadataServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
final String urlString = request.getParameter("src");
// validate URL
final URL url = ProxyServlet.validateURL(urlString, response);
if (url == null) {
return; // error is already set/handled inside `validateURL`
}
// fetch image via URL and extract meta data from it
final HttpURLConnection conn = ProxyServlet.getConnection(url);
try (InputStream is = conn.getInputStream()) {
handleRequest(request, response, is, conn.getContentType(), null);
}
}
@Override
protected void doPost(
HttpServletRequest request,
HttpServletResponse response
) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
// get image via file upload
final Part filePart = request.getPart("diagram");
final String filename = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MS IE fix
try (InputStream is = filePart.getInputStream()) {
handleRequest(request, response, is, null, filename);
}
}
/**
* Handle request no matter whether GET or POST and
* response with the PlantUML diagram image in the in the desired format if possible.
*
* @param request an HttpServletRequest object that contains the request the client has made of the servlet
* @param response an HttpServletResponse object that contains the response the servlet sends to the client
* @param is PlantUML diagram image as input stream
* @param contentType the PlantUML diagram image content type [optional]
* @param filename the PlantUML diagram image filename [optional
*
* @throws IOException if an input or output error is detected when the servlet handles the request
*/
private void handleRequest(
HttpServletRequest request,
HttpServletResponse response,
InputStream is,
String contentType,
String filename
) throws IOException {
final String formString = request.getParameter("format");
final String accept = request.getHeader("Accept");
final boolean isJsonResponse = accept != null && accept.toLowerCase().contains("json");
// extract meta data
// @see <a href="https://github.com/plantuml/plantuml/blob/26874fe610617738f958b7e8d012128fe621cff6/src/net/sourceforge/plantuml/Run.java#L570-L592">PlantUML Code</a>
final FileFormat format = getImageFileFormat(formString, contentType, filename, response);
if (format == null) {
return; // error is already set/handled inside `getImageFileFormat`
}
final Metadata metadata = getMetadata(is, format, response);
if (metadata == null) {
return; // error is already set/handled inside `getMetadata`
}
response.addHeader("Access-Control-Allow-Origin", "*");
if (isJsonResponse) {
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(metadata.toJson().toString());
} else {
response.setContentType(FileFormat.UTXT.getMimeType());
response.getWriter().write(metadata.toString());
}
}
/**
* Get the file format from the PlantUML diagram image.
*
* @param format image format passed by the user via the request param `format`
* @param contentType response content type where the PlantUML diagram image is from
* @param filename diagram image file name
* @param response response object to `sendError` including error message
*
* @return PlantUML diagram image format; if unknown format return `null`
*
* @throws IOException `response.sendError` can result in a `IOException`
*/
private FileFormat getImageFileFormat(
String format, String contentType, String filename, HttpServletResponse response
) throws IOException {
if (format != null && !format.isEmpty()) {
return getImageFileFormatFromFormatString(format, response);
}
if (filename != null && !filename.isEmpty()) {
final FileFormat fileFormat = getImageFileFormatFromFilenameExtension(filename);
if (fileFormat != null) {
return fileFormat;
}
}
if (contentType != null && !contentType.isEmpty()) {
final FileFormat fileFormat = getImageFileFormatFromContentType(contentType);
if (fileFormat != null) {
return fileFormat;
}
}
response.sendError(
HttpServletResponse.SC_BAD_REQUEST,
"PlantUML image format detection failed. Please set \"format\" (format) manually."
);
return null;
}
/**
* Get the file format from the PlantUML diagram image based on a format string.
*
* @param format image format passed by the user via the request param `format`
* @param response response object to `sendError` including error message; if `null` no error will be send
*
* @return PlantUML diagram image format; if unknown format return `null`
*
* @throws IOException `response.sendError` can result in a `IOException`
*/
private FileFormat getImageFileFormatFromFormatString(
String format, HttpServletResponse response
) throws IOException {
switch (format.toLowerCase()) {
case "png": return FileFormat.PNG;
case "svg": return FileFormat.SVG;
default:
if (response != null) {
response.sendError(
HttpServletResponse.SC_BAD_REQUEST,
"The format \"" + format + "\" is not supported for meta data extraction."
);
}
return null;
}
}
/**
* Get the file format from the PlantUML diagram image based on the filenames extension.
*
* @param filename PlantUML image file name
*
* @return PlantUML diagram image format; if unknown format return `null`
*
* @throws IOException Can not happend! Will not occur.
*/
private FileFormat getImageFileFormatFromFilenameExtension(String filename) throws IOException {
int extensionPosition = filename.lastIndexOf(".");
if (extensionPosition != -1) {
String extension = filename.substring(extensionPosition + 1);
return getImageFileFormatFromFormatString(extension, null);
}
Logger logger = Logger.getLogger("com.plantuml");
logger.log(Level.WARNING, "File name \"{0}\" is malformed. Should be: name.extension", filename);
return null;
}
/**
* Get the file format from the PlantUML diagram image based on the response content type.
*
* @param contentType response content type where the PlantUML diagram image is from
*
* @return PlantUML diagram image format; if unknown content type return `null`
*/
private FileFormat getImageFileFormatFromContentType(String contentType) {
final String ct = contentType.toLowerCase();
if (ct.contains("png")) {
return FileFormat.PNG;
}
if (ct.contains("svg") || ct.contains("xml")) {
return FileFormat.SVG;
}
Logger logger = Logger.getLogger("com.plantuml");
logger.log(Level.SEVERE, "Unknown content type \"{0}\" for meta data extraction", contentType);
return null;
}
/**
* Get meta data from PlantUML diagram image.
*
* @param is PlantUML diagram image input stream
* @param format PlantUML diagram image file format
* @param response response object to `sendError` including error message
*
* @return parsed meta data; on error return `null`
*
* @throws IOException `response.sendError` can result in a `IOException`
*/
private Metadata getMetadata(
InputStream is, FileFormat format, HttpServletResponse response
) throws IOException {
switch (format) {
case PNG:
return getMetadataFromPNG(is, response);
case SVG:
final String svg;
try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
svg = br.lines().collect(Collectors.joining("\n"));
}
return getMetadataFromSVG(svg, response);
default:
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unsupported image format.");
return null;
}
}
/**
* Get meta data from PNG PlantUML diagram image.
*
* Challenge: PNG meta data is only a single String and contains more than the PlantUML diagram.
* PNG meta data contains:
* 1. decoded PlantUML code
* 2. empty line
* 3. version information
* Notes:
* - in theory the meta data could contain the PlantUML `RawString` as well as the `PlainString`
* but since both are ALWAYS identical (methods to get them are identical), one will ALWAYS dropped.
* @see <a href="https://github.com/plantuml/plantuml/blob/26874fe610617738f958b7e8d012128fe621cff6/src/net/sourceforge/plantuml/core/UmlSource.java#L173-L189">PlantUML Code</a>
* - version information do not contain any empty lines
* Solution: split meta data at the last occurring empty line the result in
* a. decoded PlantUML diagram
* b. version information
*
* @param is PNG image input stream
* @param response response object to `sendError` including error message
*
* @return parsed meta data; on error return `null`
*
* @throws IOException `response.sendError` can result in a `IOException`
*/
private Metadata getMetadataFromPNG(InputStream is, HttpServletResponse response) throws IOException {
final String rawMetadata = new MetadataTag(is, "plantuml").getData();
if (rawMetadata == null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "No meta data found.");
return null;
}
// parse meta data
final Metadata metadata = new Metadata(rawMetadata.trim());
metadata.decoded = metadata.rawContent.substring(0, metadata.rawContent.lastIndexOf("\n\n"));
metadata.encoded = TranscoderUtil.getDefaultTranscoder().encode(metadata.decoded);
metadata.version = metadata.rawContent.substring(rawMetadata.lastIndexOf("\n\n")).trim();
// add additionally the encoded plantuml string to raw meta data since it's missing by default
metadata.rawContent = metadata.encoded + "\n\n" + metadata.rawContent;
return metadata;
}
/**
* Get meta data from SVG PlantUML diagram image.
* @see <a href="https://github.com/plantuml/plantuml/blob/26874fe610617738f958b7e8d012128fe621cff6/src/net/sourceforge/plantuml/Run.java#L574-L587">PlantUML Code</a>
*
* @param svg PlantUML digram in SVG format
* @param response response object to `sendError` including error message
*
* @return parsed meta data; on error return `null`
*
* @throws IOException `response.sendError` can result in a `IOException`
*/
private Metadata getMetadataFromSVG(String svg, HttpServletResponse response) throws IOException {
final Metadata metadata = new Metadata();
// search for meta data start token
final int idx = svg.lastIndexOf(SvgGraphics.META_HEADER);
if (idx == -1) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "No meta data found.");
return null;
}
// search for meta data end token
final String part = svg.substring(idx + SvgGraphics.META_HEADER.length());
final int idxEnd = part.indexOf("]");
if (idxEnd == -1) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid meta data: No end token found.");
return null;
}
// parse meta data
metadata.encoded = part.substring(0, idxEnd);
try {
metadata.decoded = TranscoderUtil.getDefaultTranscoderProtected().decode(metadata.encoded);
} catch (NoPlantumlCompressionException ex) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid meta data: PlantUML diagram is corrupted.");
return null;
}
return metadata;
}
/**
* Helper class to store meta data.
*/
@SuppressWarnings("checkstyle:VisibilityModifier")
private class Metadata {
public String rawContent;
public String decoded;
public String encoded;
public String version;
Metadata() { }
Metadata(String rawMetadataContent) {
rawContent = rawMetadataContent;
}
public JsonObject toJson() {
JsonObject metadata = new JsonObject();
metadata.add("encoded", encoded);
metadata.add("decoded", decoded);
if (version != null && !version.isEmpty()) {
metadata.add("version", version);
}
return metadata;
}
@Override
public String toString() {
if (rawContent != null && !rawContent.isEmpty()) {
return rawContent;
}
if (version == null || version.isEmpty()) {
return encoded + "\n\n" + decoded;
}
return encoded + "\n\n" + decoded + "\n\n" + version;
}
}
}

View File

@ -51,7 +51,7 @@ public class OldProxyServlet extends HttpServlet {
/**
* Proxy request URI regex pattern.
*/
private static final Pattern PROXY_PATTERN = Pattern.compile("/\\w+/proxy/((\\d+)/)?((\\w+)/)?(https?://[^@]*)");
private static final Pattern PROXY_PATTERN = Pattern.compile("/\\w+/proxy/((\\d+)/)?((\\w+)/)?(https?://.*)");
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
@ -61,18 +61,13 @@ public class OldProxyServlet extends HttpServlet {
Matcher proxyMatcher = PROXY_PATTERN.matcher(uri);
if (!proxyMatcher.matches()) {
// Bad URI format.
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "URL malformed.");
response.setStatus(400);
return;
}
String num = proxyMatcher.group(2); // Optional number of the diagram source
String format = proxyMatcher.group(4); // Expected format of the generated diagram
String sourceURL = proxyMatcher.group(5);
if (ProxyServlet.forbiddenURL(sourceURL)) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Forbidden URL format.");
return;
}
handleImageProxy(response, num, format, sourceURL);
}

View File

@ -1,46 +0,0 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package net.sourceforge.plantuml.servlet;
import net.sourceforge.plantuml.FileFormat;
/**
* PDF servlet of the webapp.
* This servlet produces the UML diagram in PDF format.
*/
@SuppressWarnings("SERIAL")
public class PdfServlet extends UmlDiagramService {
/**
* Gives the wished output format of the diagram.
* This value is used by the DiagramResponse class.
*
* @return the format for pdf responses
*/
@Override
public FileFormat getOutputFormat() {
return FileFormat.PDF;
}
}

View File

@ -27,15 +27,21 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.net.ssl.HttpsURLConnection;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.sourceforge.plantuml.OptionFlags;
import net.sourceforge.plantuml.api.PlantumlUtils;
import net.sourceforge.plantuml.code.NoPlantumlCompressionException;
import net.sourceforge.plantuml.code.Transcoder;
import net.sourceforge.plantuml.code.TranscoderUtil;
import net.sourceforge.plantuml.png.MetadataTag;
import net.sourceforge.plantuml.servlet.utility.Configuration;
import net.sourceforge.plantuml.servlet.utility.UmlExtractor;
@ -52,13 +58,7 @@ import net.sourceforge.plantuml.servlet.utility.UrlDataExtractor;
* Modified by Maxime Sinclair
*/
@SuppressWarnings("SERIAL")
public class PlantUmlServlet extends AsciiCoderServlet {
static {
// Initialize the PlantUML server.
// You could say that this is like the `static void main(String[] args)` of the PlantUML server.
DiagramResponse.init();
}
public class PlantUmlServlet extends HttpServlet {
/**
* Default encoded uml text.
@ -66,54 +66,16 @@ public class PlantUmlServlet extends AsciiCoderServlet {
*/
private static final String DEFAULT_ENCODED_TEXT = "SyfFKj2rKt3CoKnELR1Io4ZDoSa70000";
@Override
protected String getServletContextPath() {
return "uml";
}
/**
* Encode arbitrary string to HTML string.
*
* @param string arbitrary string
*
* @return html encoded string
* Regex pattern to fetch last part of the URL.
*/
public static String stringToHTMLString(String string) {
final StringBuilder sb = new StringBuilder(string.length());
// true if last char was blank
final int length = string.length();
int offset = 0;
while (offset < length) {
final int c = string.codePointAt(offset);
if (c == ' ') {
sb.append(' ');
} else if (c == '"') {
sb.append("&quot;");
} else if (c == '&') {
sb.append("&amp;");
} else if (c == '<') {
sb.append("&lt;");
} else if (c == '>') {
sb.append("&gt;");
} else if (c == '\r') {
sb.append("\r");
} else if (c == '\n') {
sb.append("\n");
} else {
int ci = 0xffffff & c;
if (ci < 160) {
// nothing special only 7 Bit
sb.append((char) c);
} else {
// Not 7 Bit use the unicode system
sb.append("&#");
sb.append(ci);
sb.append(';');
}
}
offset += Character.charCount(c);
private static final Pattern URL_PATTERN = Pattern.compile("^.*[^a-zA-Z0-9\\-\\_]([a-zA-Z0-9\\-\\_]+)");
static {
OptionFlags.ALLOW_INCLUDE = false;
if ("true".equalsIgnoreCase(System.getenv("ALLOW_PLANTUML_INCLUDE"))) {
OptionFlags.ALLOW_INCLUDE = true;
}
return sb.toString();
}
@Override
@ -133,15 +95,8 @@ public class PlantUmlServlet extends AsciiCoderServlet {
final int idx = UrlDataExtractor.getIndex(request.getRequestURI());
// forward to index.jsp
final String path;
final String view = request.getParameter("view");
if (view != null && view.equalsIgnoreCase("previewer")) {
path = "/previewer.jsp";
} else {
path = "/index.jsp";
}
prepareRequestForDispatch(request, text, idx);
final RequestDispatcher dispatcher = request.getRequestDispatcher(path);
final RequestDispatcher dispatcher = request.getRequestDispatcher("/index.jsp");
dispatcher.forward(request, response);
}
@ -189,10 +144,6 @@ public class PlantUmlServlet extends AsciiCoderServlet {
if (text != null && !text.isEmpty()) {
return text;
}
} catch (NoPlantumlCompressionException e) {
// no textual diagram source available from Url
// ignore and try 2. method (metadata) below
// do not spam output console
} catch (Exception e) {
e.printStackTrace();
}
@ -226,7 +177,26 @@ public class PlantUmlServlet extends AsciiCoderServlet {
* @throws IOException if an input or output exception occurred
*/
private String getTextFromUrl(HttpServletRequest request) throws IOException {
return getTranscoder().decode(getEncodedTextFromUrl(request));
// textual diagram source from request URI
String url = request.getRequestURI();
if (url.contains("/uml/") && !url.endsWith("/uml/")) {
final String encoded = UrlDataExtractor.getEncodedDiagram(request.getRequestURI(), "");
if (!encoded.isEmpty()) {
return getTranscoder().decode(encoded);
}
}
// textual diagram source from "url" parameter
url = request.getParameter("url");
if (url != null && !url.trim().isEmpty()) {
// Catch the last part of the URL if necessary
final Matcher matcher = URL_PATTERN.matcher(url);
if (matcher.find()) {
url = matcher.group(1);
}
return getTranscoder().decode(url);
}
// nothing found
return "";
}
/**
@ -239,16 +209,28 @@ public class PlantUmlServlet extends AsciiCoderServlet {
*/
private void prepareRequestForDispatch(HttpServletRequest request, String text, int idx) throws IOException {
final String encoded = getTranscoder().encode(text);
final String index = (idx < 0) ? "" : idx + "/";
// diagram sources
request.setAttribute("encoded", encoded);
request.setAttribute("decoded", text);
request.setAttribute("index", (idx < 0) ? "" : idx);
request.setAttribute("index", idx);
// properties
request.setAttribute("showSocialButtons", Configuration.get("SHOW_SOCIAL_BUTTONS"));
request.setAttribute("showGithubRibbon", Configuration.get("SHOW_GITHUB_RIBBON"));
// URL base
final String hostpath = getHostpath(request);
request.setAttribute("hostpath", hostpath);
// image URLs
final boolean hasImg = !text.isEmpty();
request.setAttribute("hasImg", hasImg);
request.setAttribute("imgurl", hostpath + "/png/" + index + encoded);
request.setAttribute("svgurl", hostpath + "/svg/" + index + encoded);
request.setAttribute("txturl", hostpath + "/txt/" + index + encoded);
request.setAttribute("mapurl", hostpath + "/map/" + index + encoded);
// map for diagram source if necessary
final boolean hasMap = PlantumlUtils.hasCMapData(text);
request.setAttribute("hasMap", hasMap);
String map = "";
if (PlantumlUtils.hasCMapData(text)) {
if (hasMap) {
try {
map = UmlExtractor.extractMap(text);
} catch (Exception e) {
@ -258,6 +240,33 @@ public class PlantUmlServlet extends AsciiCoderServlet {
request.setAttribute("map", map);
}
/**
* Get hostpath (URL base) from request.
*
* @param request http request
*
* @return hostpath
*/
private String getHostpath(final HttpServletRequest request) {
// port
String port = "";
if (
(request.getScheme() == "http" && request.getServerPort() != 80)
||
(request.getScheme() == "https" && request.getServerPort() != 443)
) {
port = ":" + request.getServerPort();
}
// scheme
String scheme = request.getScheme();
final String forwardedProto = request.getHeader("x-forwarded-proto");
if (forwardedProto != null && !forwardedProto.isEmpty()) {
scheme = forwardedProto;
}
// hostpath
return scheme + "://" + request.getServerName() + port + request.getContextPath();
}
/**
* Send redirect response to encoded uml text.
*
@ -291,13 +300,23 @@ public class PlantUmlServlet extends AsciiCoderServlet {
String encoded,
Integer index
) throws IOException {
final String path;
final String result;
if (index == null || index < 0) {
path = request.getContextPath() + "/uml/" + encoded;
result = request.getContextPath() + "/uml/" + encoded;
} else {
path = request.getContextPath() + "/uml/" + index + "/" + encoded;
result = request.getContextPath() + "/uml/" + index + "/" + encoded;
}
response.sendRedirect(path);
response.sendRedirect(result);
}
/**
* Get PlantUML transcoder.
*
* @return transcoder instance
*/
private Transcoder getTranscoder() {
return TranscoderUtil.getDefaultTranscoder();
}
/**
@ -314,6 +333,7 @@ public class PlantUmlServlet extends AsciiCoderServlet {
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setReadTimeout(10000); // 10 seconds
// printHttpsCert(con);
con.connect();
return con;
} else {

View File

@ -1,202 +0,0 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package net.sourceforge.plantuml.servlet;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.emoji.data.Dummy;
import net.sourceforge.plantuml.json.Json;
import net.sourceforge.plantuml.json.JsonArray;
import net.sourceforge.plantuml.theme.ThemeUtils;
import net.sourceforge.plantuml.openiconic.data.DummyIcon;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* Small PlantUML frontend or UI helper.
*/
@SuppressWarnings("SERIAL")
public class PlantUmlUIHelperServlet extends HttpServlet {
private interface HelperConsumer {
void accept(HttpServletRequest request, HttpServletResponse response) throws IOException;
}
private final Map<String, HelperConsumer> helpers = new HashMap<>();
private String svgIconsSpriteCache = null;
public PlantUmlUIHelperServlet() {
// add all supported request items/helper methods
helpers.put("emojis", this::sendEmojis);
helpers.put("icons.svg", this::sendIconsSprite);
helpers.put("icons", this::sendIcons);
helpers.put("themes", this::sendThemes);
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
final String requestItem = request.getParameter("request");
final HelperConsumer requestHelper = this.helpers.get(requestItem);
String errorMsg = null;
if (requestItem == null) {
errorMsg = "Request item not set.";
} else if (requestHelper == null) {
errorMsg = "Unknown requested item: " + requestItem;
}
if (errorMsg != null) {
setDefaultHeader(response, FileFormat.UTXT);
response.getWriter().write(errorMsg);
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}
requestHelper.accept(request, response);
}
private void setDefaultHeader(HttpServletResponse response, FileFormat fileFormat) {
setDefaultHeader(response, fileFormat.getMimeType());
}
private HttpServletResponse setDefaultHeader(HttpServletResponse response, String contentType) {
response.addHeader("Access-Control-Allow-Origin", "*");
response.setContentType(contentType);
return response;
}
private void sendJson(HttpServletResponse response, String json) throws IOException {
setDefaultHeader(response, "application/json;charset=UTF-8");
response.getWriter().write(json);
}
private String[] getIcons() throws IOException {
InputStream in = DummyIcon.class.getResourceAsStream("all.txt");
try (BufferedReader br = new BufferedReader(new InputStreamReader(in))) {
return br.lines().toArray(String[]::new);
}
}
private void sendIcons(HttpServletRequest request, HttpServletResponse response) throws IOException {
sendJson(response, Json.array(getIcons()).toString());
}
private void sendIconsSprite(HttpServletRequest request, HttpServletResponse response) throws IOException {
if (svgIconsSpriteCache == null) {
// NOTE: all icons has the following svg tag attributes: width="8" height="8" viewBox="0 0 8 8"
String[] iconNames = getIcons();
StringBuilder sprite = new StringBuilder();
sprite.append("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"8\" height=\"8\" viewBox=\"0 0 8 8\">\n");
sprite.append("<defs>\n");
sprite.append(" <style><![CDATA[\n");
sprite.append(" .sprite { display: none; }\n");
sprite.append(" .sprite:target { display: inline; }\n");
sprite.append(" ]]></style>\n");
sprite.append("</defs>\n");
for (String name : iconNames) {
try (InputStream in = DummyIcon.class.getResourceAsStream(name + ".svg")) {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
DocumentBuilder db = docFactory.newDocumentBuilder();
Document doc = db.parse(in);
Writer out = new StringWriter();
out.write("<g class=\"sprite\" id=\"" + name + "\">");
TransformerFactory tfFactory = TransformerFactory.newInstance();
tfFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
tfFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
Transformer tf = tfFactory.newTransformer();
tf.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
tf.setOutputProperty(OutputKeys.INDENT, "no");
NodeList svgInnerNodes = doc.getElementsByTagName("svg").item(0).getChildNodes();
for (int index = 0; index < svgInnerNodes.getLength(); index++) {
tf.transform(new DOMSource(svgInnerNodes.item(index)), new StreamResult(out));
}
out.write("</g>");
sprite.append(out.toString() + "\n");
} catch (ParserConfigurationException | SAXException | TransformerException ex) {
// skip icons which can not be parsed/read
Logger logger = Logger.getLogger("com.plantuml");
logger.log(Level.WARNING, "SVG icon \"{0}\" could not be parsed. Skip!", name);
}
}
sprite.append("</svg>\n");
svgIconsSpriteCache = sprite.toString();
}
setDefaultHeader(response, FileFormat.SVG);
response.getWriter().write(svgIconsSpriteCache);
}
private String[][] getEmojis() throws IOException {
InputStream in = Dummy.class.getResourceAsStream("emoji.txt");
try (BufferedReader br = new BufferedReader(new InputStreamReader(in))) {
return br.lines().map(line -> line.split(";")).toArray(String[][]::new);
}
}
private void sendEmojis(HttpServletRequest request, HttpServletResponse response) throws IOException {
String[][] emojis = getEmojis();
JsonArray json = new JsonArray();
for (String[] emojiUnicodeNamePair : emojis) {
json.add(Json.array(emojiUnicodeNamePair));
}
sendJson(response, json.toString());
}
private void sendThemes(HttpServletRequest request, HttpServletResponse response) throws IOException {
String[] themes = ThemeUtils.getAllThemeNames().toArray(new String[0]);
sendJson(response, Json.array(themes).toString());
}
}

View File

@ -29,16 +29,24 @@ import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.stream.Collectors;
import java.security.cert.Certificate;
import java.util.List;
import javax.imageio.IIOException;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLPeerUnverifiedException;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.sourceforge.plantuml.BlockUml;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.OptionFlags;
import net.sourceforge.plantuml.SourceStringReader;
import net.sourceforge.plantuml.core.Diagram;
import net.sourceforge.plantuml.core.UmlSource;
/**
* Proxy servlet of the webapp.
@ -48,86 +56,49 @@ import net.sourceforge.plantuml.FileFormat;
@SuppressWarnings("SERIAL")
public class ProxyServlet extends HttpServlet {
public static boolean forbiddenURL(String full) {
if (full == null) {
return true;
static {
OptionFlags.ALLOW_INCLUDE = false;
if ("true".equalsIgnoreCase(System.getenv("ALLOW_PLANTUML_INCLUDE"))) {
OptionFlags.ALLOW_INCLUDE = true;
}
if (full.contains("@")) {
return true;
}
if (full.startsWith("https://") == false && full.startsWith("http://") == false) {
return true;
}
if (full.matches("^https?://[-#.0-9:\\[\\]+]+/.*")) {
return true;
}
if (full.matches("^https?://[^.]+/.*")) {
return true;
}
if (full.matches("^https?://[^.]+$")) {
return true;
}
return false;
}
/**
* Validate external URL.
*
* @param url URL to validate
* @param response response object to `sendError` including error message; if `null` no error will be send
*
* @return valid URL; otherwise `null`
*
* @throws IOException `response.sendError` can result in a `IOException`
*/
public static URL validateURL(String url, HttpServletResponse response) throws IOException {
final URL parsedUrl;
try {
parsedUrl = new URL(url);
} catch (MalformedURLException mue) {
if (response != null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "URL malformed.");
}
return null;
}
// Check if URL is in a forbidden format (e.g. IP-Address)
if (forbiddenURL(url)) {
if (response != null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Forbidden URL format.");
}
return null;
}
return parsedUrl;
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
final String fmt = request.getParameter("fmt");
final String source = request.getParameter("src");
final String index = request.getParameter("idx");
final int idx = index == null ? 0 : Integer.parseInt(index);
final URL srcUrl = validateURL(source, response);
if (srcUrl == null) {
return; // error is already set/handled inside `validateURL`
final URL srcUrl;
// Check if the src URL is valid
try {
srcUrl = new URL(source);
} catch (MalformedURLException mue) {
mue.printStackTrace();
response.setStatus(400);
return;
}
// fetch diagram from URL
final String uml = getSource(srcUrl);
// generate the response
String diagmarkup = getSource(srcUrl);
SourceStringReader reader = new SourceStringReader(diagmarkup);
int n = index == null ? 0 : Integer.parseInt(index);
List<BlockUml> blocks = reader.getBlocks();
BlockUml block = blocks.get(n);
Diagram diagram = block.getDiagram();
UmlSource umlSrc = diagram.getSource();
String uml = umlSrc.getPlainString();
//System.out.println("uml=" + uml);
// generate the response
DiagramResponse dr = new DiagramResponse(response, getOutputFormat(fmt), request);
try {
// special handling for the MAP since it's not using "#sendDiagram()" like the other types
if ("map".equals(fmt)) {
dr.sendMap(uml, idx);
} else {
dr.sendDiagram(uml, idx);
}
dr.sendDiagram(uml, 0);
} catch (IIOException e) {
// Browser has closed the connection, so the HTTP OutputStream is closed
// Silently catch the exception to avoid annoying log
}
dr = null;
}
/**
@ -140,10 +111,25 @@ public class ProxyServlet extends HttpServlet {
* @throws IOException if an input or output exception occurred
*/
private String getSource(final URL url) throws IOException {
HttpURLConnection conn = getConnection(url);
try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
return br.lines().collect(Collectors.joining("\n"));
String line;
BufferedReader rd;
StringBuilder sb;
try {
HttpURLConnection con = getConnection(url);
rd = new BufferedReader(new InputStreamReader(con.getInputStream()));
sb = new StringBuilder();
while ((line = rd.readLine()) != null) {
sb.append(line + '\n');
}
rd.close();
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
rd = null;
}
return "";
}
/**
@ -158,16 +144,19 @@ public class ProxyServlet extends HttpServlet {
if (format == null) {
return FileFormat.PNG;
}
switch (format.toLowerCase()) {
case "png": return FileFormat.PNG;
case "svg": return FileFormat.SVG;
case "eps": return FileFormat.EPS;
case "epstext": return FileFormat.EPS_TEXT;
case "txt": return FileFormat.UTXT;
case "map": return FileFormat.UTXT;
case "pdf": return FileFormat.PDF;
default: return FileFormat.PNG;
if (format.equals("svg")) {
return FileFormat.SVG;
}
if (format.equals("eps")) {
return FileFormat.EPS;
}
if (format.equals("epstext")) {
return FileFormat.EPS_TEXT;
}
if (format.equals("txt")) {
return FileFormat.UTXT;
}
return FileFormat.PNG;
}
/**
@ -179,20 +168,48 @@ public class ProxyServlet extends HttpServlet {
*
* @throws IOException if an input or output exception occurred
*/
public static HttpURLConnection getConnection(final URL url) throws IOException {
final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
private HttpURLConnection getConnection(final URL url) throws IOException {
final HttpURLConnection con = (HttpURLConnection) url.openConnection();
//if (con instanceof HttpsURLConnection) {
// printHttpsCert((HttpsURLConnection) con);
//}
con.setRequestMethod("GET");
String token = System.getenv("HTTP_AUTHORIZATION");
if (token != null) {
conn.setRequestProperty("Authorization", token);
con.setRequestProperty("Authorization", token);
}
final String timeoutString = System.getenv("HTTP_PROXY_READ_TIMEOUT");
int timeout = 10000; // 10 seconds as default
if (timeoutString != null && timeoutString.matches("^\\d+$")) {
timeout = Integer.parseInt(timeoutString);
con.setReadTimeout(10000); // 10 seconds
con.connect();
return con;
}
/**
* Debug method used to dump the certificate info.
*
* @param con the https connection
*/
@SuppressWarnings("unused")
private void printHttpsCert(final HttpsURLConnection con) {
if (con != null) {
try {
System.out.println("Response Code : " + con.getResponseCode());
System.out.println("Cipher Suite : " + con.getCipherSuite());
System.out.println("\n");
Certificate[] certs = con.getServerCertificates();
for (Certificate cert : certs) {
System.out.println("Cert Type : " + cert.getType());
System.out.println("Cert Hash Code : " + cert.hashCode());
System.out.println("Cert Public Key Algorithm : " + cert.getPublicKey().getAlgorithm());
System.out.println("Cert Public Key Format : " + cert.getPublicKey().getFormat());
System.out.println("\n");
}
} catch (SSLPeerUnverifiedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
conn.setReadTimeout(timeout);
conn.connect();
return conn;
}
}

View File

@ -34,6 +34,7 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.OptionFlags;
import net.sourceforge.plantuml.servlet.utility.UmlExtractor;
import net.sourceforge.plantuml.servlet.utility.UrlDataExtractor;
@ -43,6 +44,13 @@ import net.sourceforge.plantuml.servlet.utility.UrlDataExtractor;
@SuppressWarnings("SERIAL")
public abstract class UmlDiagramService extends HttpServlet {
static {
OptionFlags.ALLOW_INCLUDE = false;
if ("true".equalsIgnoreCase(System.getenv("ALLOW_PLANTUML_INCLUDE"))) {
OptionFlags.ALLOW_INCLUDE = true;
}
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
final String url = request.getRequestURI();

View File

@ -29,6 +29,7 @@ import java.net.URLDecoder;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.OptionFlags;
import net.sourceforge.plantuml.SourceStringReader;
import net.sourceforge.plantuml.code.Transcoder;
import net.sourceforge.plantuml.code.TranscoderUtil;
@ -41,6 +42,13 @@ import net.sourceforge.plantuml.core.ImageData;
*/
public abstract class UmlExtractor {
static {
OptionFlags.ALLOW_INCLUDE = false;
if ("true".equalsIgnoreCase(System.getenv("ALLOW_PLANTUML_INCLUDE"))) {
OptionFlags.ALLOW_INCLUDE = true;
}
}
/**
* Build the complete UML source from the compressed source extracted from the
* HTTP URI.

View File

@ -1,255 +1,219 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:web="https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0"
>
<!-- ========================================================== -->
<!-- General -->
<!-- ========================================================== -->
<!-- Name the application -->
<display-name>PlantUML</display-name>
<description>PlantUML Online Server</description>
<!-- This app is cluster-ready -->
<distributable />
<!-- Set timeout to 120 minutes -->
<session-config>
<session-timeout>120</session-timeout>
</session-config>
<!-- ========================================================== -->
<!-- Custom Tag Libraries -->
<!-- ========================================================== -->
<!-- Taglib declarations are no longer required since JSP 2.0, see Removing taglib from web.xml -->
<!-- The <taglib> did not need to be a child of <jsp-config> in earlier versions but is required as of Tomcat 7 -->
<!-- Note that you can only have one <jsp-config> element per web.xml -->
<!--
<jsp-config>
<taglib>
<taglib-uri>http://java.sun.com/jsp/jstl/core</taglib-uri>
<taglib-location>/WEB-INF/lib/c.tld</taglib-location>
</taglib>
</jsp-config>
-->
<!-- ========================================================== -->
<!-- Context Parameters -->
<!-- ========================================================== -->
<context-param>
<param-name>org.eclipse.jetty.servlet.Default.welcomeServlets</param-name>
<param-value>exact</param-value>
</context-param>
<!-- ========================================================== -->
<!-- Servlets -->
<!-- ========================================================== -->
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.eclipse.jetty.jsp.JettyJspServlet</servlet-class>
<init-param>
<param-name>compilerSourceVM</param-name>
<param-value>1.8</param-value>
</init-param>
<init-param>
<param-name>compilerTargetVM</param-name>
<param-value>1.8</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>plantumlservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.PlantUmlServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>plantumlservlet</servlet-name>
<url-pattern>/welcome</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>plantumlservlet</servlet-name>
<url-pattern>/uml/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>plantumlservlet</servlet-name>
<url-pattern>/form</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>plantumlservlet</servlet-name>
<url-pattern>/start/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>imgservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.ImgServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>imgservlet</servlet-name>
<url-pattern>/png/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>imgservlet</servlet-name>
<url-pattern>/img/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>svgservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.SvgServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>svgservlet</servlet-name>
<url-pattern>/svg/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>pdfservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.PdfServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>pdfservlet</servlet-name>
<url-pattern>/pdf/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>epsservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.EpsServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>epsservlet</servlet-name>
<url-pattern>/eps/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>epstextservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.EpsTextServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>epstextservlet</servlet-name>
<url-pattern>/epstext/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>base64servlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.Base64Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>base64servlet</servlet-name>
<url-pattern>/base64/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>asciiservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.AsciiServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>asciiservlet</servlet-name>
<url-pattern>/txt/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>proxyservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.ProxyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>proxyservlet</servlet-name>
<url-pattern>/proxy</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>oldproxyservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.OldProxyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>oldproxyservlet</servlet-name>
<url-pattern>/proxy/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>mapservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.MapServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>mapservlet</servlet-name>
<url-pattern>/map/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>checkservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.CheckSyntaxServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>checkservlet</servlet-name>
<url-pattern>/check/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>languageservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.LanguageServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>languageservlet</servlet-name>
<url-pattern>/language</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>asciicoderservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.AsciiCoderServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>asciicoderservlet</servlet-name>
<url-pattern>/coder/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>plantumluihelperservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.PlantUmlUIHelperServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>plantumluihelperservlet</servlet-name>
<url-pattern>/ui-helper/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>metadataservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.MetadataServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>metadataservlet</servlet-name>
<url-pattern>/metadata/*</url-pattern>
</servlet-mapping>
<!-- ========================================================== -->
<!-- Error Handler -->
<!-- ========================================================== -->
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/error.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error.jsp</location>
</error-page>
<!-- ========================================================== -->
<!-- Welcome Files -->
<!-- ========================================================== -->
<welcome-file-list>
<welcome-file>welcome</welcome-file>
</welcome-file-list>
</web-app>
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:web="https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0"
>
<!-- ========================================================== -->
<!-- General -->
<!-- ========================================================== -->
<!-- Name the application -->
<display-name>PlantUML</display-name>
<description>PlantUML Online Server</description>
<!-- This app is cluster-ready -->
<distributable />
<!-- Set timeout to 120 minutes -->
<session-config>
<session-timeout>120</session-timeout>
</session-config>
<!-- ========================================================== -->
<!-- Custom Tag Libraries -->
<!-- ========================================================== -->
<!-- Taglib declarations are no longer required since JSP 2.0, see Removing taglib from web.xml -->
<!-- The <taglib> did not need to be a child of <jsp-config> in earlier versions but is required as of Tomcat 7 -->
<!-- Note that you can only have one <jsp-config> element per web.xml -->
<!--
<jsp-config>
<taglib>
<taglib-uri>http://java.sun.com/jsp/jstl/core</taglib-uri>
<taglib-location>/WEB-INF/lib/c.tld</taglib-location>
</taglib>
</jsp-config>
-->
<!-- ========================================================== -->
<!-- Context Parameters -->
<!-- ========================================================== -->
<context-param>
<param-name>org.eclipse.jetty.servlet.Default.welcomeServlets</param-name>
<param-value>exact</param-value>
</context-param>
<!-- ========================================================== -->
<!-- Servlets -->
<!-- ========================================================== -->
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.eclipse.jetty.jsp.JettyJspServlet</servlet-class>
<init-param>
<param-name>compilerSourceVM</param-name>
<param-value>1.7</param-value>
</init-param>
<init-param>
<param-name>compilerTargetVM</param-name>
<param-value>1.7</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>plantumlservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.PlantUmlServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>plantumlservlet</servlet-name>
<url-pattern>/welcome</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>plantumlservlet</servlet-name>
<url-pattern>/uml/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>plantumlservlet</servlet-name>
<url-pattern>/form</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>plantumlservlet</servlet-name>
<url-pattern>/start/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>imgservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.ImgServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>imgservlet</servlet-name>
<url-pattern>/png/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>imgservlet</servlet-name>
<url-pattern>/img/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>svgservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.SvgServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>svgservlet</servlet-name>
<url-pattern>/svg/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>epsservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.EpsServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>epsservlet</servlet-name>
<url-pattern>/eps/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>epstextservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.EpsTextServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>epstextservlet</servlet-name>
<url-pattern>/epstext/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>base64servlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.Base64Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>base64servlet</servlet-name>
<url-pattern>/base64/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>asciiservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.AsciiServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>asciiservlet</servlet-name>
<url-pattern>/txt/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>proxyservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.ProxyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>proxyservlet</servlet-name>
<url-pattern>/proxy</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>oldproxyservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.OldProxyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>oldproxyservlet</servlet-name>
<url-pattern>/proxy/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>mapservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.MapServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>mapservlet</servlet-name>
<url-pattern>/map/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>checkservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.CheckSyntaxServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>checkservlet</servlet-name>
<url-pattern>/check/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>languageservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.LanguageServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>languageservlet</servlet-name>
<url-pattern>/language</url-pattern>
</servlet-mapping>
<!-- ========================================================== -->
<!-- Error Handler -->
<!-- ========================================================== -->
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/error.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error.jsp</location>
</error-page>
<!-- ========================================================== -->
<!-- Welcome Files -->
<!-- ========================================================== -->
<welcome-file-list>
<welcome-file>welcome</welcome-file>
</welcome-file-list>
</web-app>

View File

@ -1 +0,0 @@
<svg height="24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M16 16v4c0 1.152-.848 2-2 2H4c-1.152 0-2-.848-2-2V10c0-1.152.848-2 2-2h4V4c0-1.152.848-2 2-2h10c1.152 0 2 .848 2 2v10c0 1.152-.848 2-2 2h-4zm-2 0h-4c-1.152 0-2-.848-2-2v-4H4v10h10v-4zM10 4v10h10V4H10z" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 301 B

View File

@ -1 +0,0 @@
<svg height="512" width="512" xmlns="http://www.w3.org/2000/svg"><path d="M384 224v184a40 40 0 01-40 40H104a40 40 0 01-40-40V168a40 40 0 0140-40h167.48" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"/><path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="32" d="M216 184v112h112m-104-8L440 72"/></svg>

Before

Width:  |  Height:  |  Size: 384 B

View File

@ -1 +0,0 @@
<svg fill="none" height="24" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>

Before

Width:  |  Height:  |  Size: 240 B

View File

@ -1 +0,0 @@
<svg height="512" width="512" xmlns="http://www.w3.org/2000/svg"><path d="M262.29 192.31a64 64 0 1057.4 57.4 64.13 64.13 0 00-57.4-57.4zM416.39 256a154.34 154.34 0 01-1.53 20.79l45.21 35.46a10.81 10.81 0 012.45 13.75l-42.77 74a10.81 10.81 0 01-13.14 4.59l-44.9-18.08a16.11 16.11 0 00-15.17 1.75A164.48 164.48 0 01325 400.8a15.94 15.94 0 00-8.82 12.14l-6.73 47.89a11.08 11.08 0 01-10.68 9.17h-85.54a11.11 11.11 0 01-10.69-8.87l-6.72-47.82a16.07 16.07 0 00-9-12.22 155.3 155.3 0 01-21.46-12.57 16 16 0 00-15.11-1.71l-44.89 18.07a10.81 10.81 0 01-13.14-4.58l-42.77-74a10.8 10.8 0 012.45-13.75l38.21-30a16.05 16.05 0 006-14.08c-.36-4.17-.58-8.33-.58-12.5s.21-8.27.58-12.35a16 16 0 00-6.07-13.94l-38.19-30A10.81 10.81 0 0149.48 186l42.77-74a10.81 10.81 0 0113.14-4.59l44.9 18.08a16.11 16.11 0 0015.17-1.75A164.48 164.48 0 01187 111.2a15.94 15.94 0 008.82-12.14l6.73-47.89A11.08 11.08 0 01213.23 42h85.54a11.11 11.11 0 0110.69 8.87l6.72 47.82a16.07 16.07 0 009 12.22 155.3 155.3 0 0121.46 12.57 16 16 0 0015.11 1.71l44.89-18.07a10.81 10.81 0 0113.14 4.58l42.77 74a10.8 10.8 0 01-2.45 13.75l-38.21 30a16.05 16.05 0 00-6.05 14.08c.33 4.14.55 8.3.55 12.47z" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"/></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1 +0,0 @@
<svg height="512" width="512" xmlns="http://www.w3.org/2000/svg"><path d="M384 224v184a40 40 0 01-40 40H104a40 40 0 01-40-40V168a40 40 0 0140-40h167.48M336 64h112v112M224 288L440 72" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"/></svg>

Before

Width:  |  Height:  |  Size: 281 B

View File

@ -1 +0,0 @@
<svg fill="none" height="24" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12"/></svg>

Before

Width:  |  Height:  |  Size: 241 B

View File

@ -1 +0,0 @@
<svg class="bi bi-filetype-ascii" fill="currentColor" height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M14 4.5V11h-1V4.5h-2A1.5 1.5 0 019.5 3V1H4a1 1 0 00-1 1v9H2V2a2 2 0 012-2h5.5zM2.404 14.903l-.313 1.028h-.8l1.342-3.999h.926l1.335 4h-.84l-.314-1.03H2.404zm1.178-.59l-.49-1.616h-.034l-.49 1.617h1.014zm1.782.977a1.178 1.178 0 01-.111-.449h.764a.58.58 0 00.255.384c.07.049.154.087.25.114.095.028.2.041.319.041.164 0 .3-.023.413-.07a.558.558 0 00.255-.193.507.507 0 00.085-.29.387.387 0 00-.153-.326c-.101-.08-.256-.144-.463-.193l-.618-.143a1.72 1.72 0 01-.54-.214 1.002 1.002 0 01-.35-.367 1.068 1.068 0 01-.123-.524c0-.244.063-.457.19-.639.127-.181.303-.322.527-.422.225-.1.484-.149.777-.149.304 0 .564.05.779.152.217.102.384.239.5.41.12.17.186.359.2.566h-.75a.56.56 0 00-.12-.258.623.623 0 00-.246-.181.923.923 0 00-.37-.068c-.216 0-.387.05-.512.152a.472.472 0 00-.184.384c0 .121.047.22.143.3a.97.97 0 00.404.175l.62.143c.218.05.407.12.567.211.16.09.285.21.375.358.09.148.135.335.135.56 0 .247-.063.466-.188.656-.133.196-.32.348-.54.439-.233.105-.52.158-.857.158a2.191 2.191 0 01-.665-.09 1.404 1.404 0 01-.478-.252 1.131 1.131 0 01-.29-.375zm4.383-2.246a1.732 1.732 0 00-.103.633v.495c0 .246.035.455.103.627a.834.834 0 00.299.393c.142.09.308.136.477.13a.872.872 0 00.402-.087.699.699 0 00.272-.248.8.8 0 00.117-.364h.765v.076c-.01.241-.088.475-.226.674-.136.194-.32.345-.55.454a1.81 1.81 0 01-.785.164c-.36 0-.665-.072-.915-.216a1.424 1.424 0 01-.57-.627c-.13-.272-.194-.597-.194-.976v-.498c0-.38.065-.705.196-.978.13-.274.32-.485.57-.633.253-.15.557-.223.913-.223.218 0 .42.032.606.097.187.062.35.153.49.272.283.241.452.591.465.964v.073h-.765a.85.85 0 00-.12-.38.7.7 0 00-.272-.261.802.802 0 00-.4-.097.814.814 0 00-.473.138.868.868 0 00-.302.398zm3.628-1.106v4h-.79v-4h.79zm1.337.005v3.999h-.791v-4h.79z" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1 +0,0 @@
<svg class="bi bi-filetype-map" fill="currentColor" height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M14 4.5V14a2 2 0 01-2 2v-1a1 1 0 001-1V4.5h-2A1.5 1.5 0 019.5 3V1H4a1 1 0 00-1 1v9H2V2a2 2 0 012-2h5.5zM.706 15.849v-2.66h.038l.952 2.16h.516l.946-2.16h.038v2.66h.715V11.85h-.8l-1.14 2.596h-.026L.805 11.85H0v3.999zm7.31-3.999h1.6c.289 0 .533.06.732.179.201.117.355.276.46.477.106.201.158.427.158.677 0 .25-.053.476-.16.677-.106.199-.26.357-.464.474a1.46 1.46 0 01-.732.173h-.803v1.342h-.79V11.85zm2.06 1.714a.795.795 0 00.085-.381c0-.226-.062-.4-.185-.521-.123-.122-.294-.182-.513-.182h-.659v1.406h.66a.794.794 0 00.374-.082.574.574 0 00.238-.24zm-5.12 2.306l.313-1.028h1.336l.314 1.028h.84l-1.336-3.999h-.925l-1.329 3.96m1.79-3.195l.488 1.617H5.433l.49-1.617z" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 808 B

View File

@ -1 +0,0 @@
<svg class="bi bi-filetype-pdf" fill="currentColor" height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M14 4.5V14a2 2 0 01-2 2h-1v-1h1a1 1 0 001-1V4.5h-2A1.5 1.5 0 019.5 3V1H4a1 1 0 00-1 1v9H2V2a2 2 0 012-2h5.5L14 4.5zM1.6 11.85H0v3.999h.791v-1.342h.803c.287 0 .531-.057.732-.173.203-.117.358-.275.463-.474a1.42 1.42 0 00.161-.677c0-.25-.053-.476-.158-.677a1.176 1.176 0 00-.46-.477c-.2-.12-.443-.179-.732-.179zm.545 1.333a.795.795 0 01-.085.38.574.574 0 01-.238.241.794.794 0 01-.375.082H.788V12.48h.66c.218 0 .389.06.512.181.123.122.185.296.185.522zm1.217-1.333v3.999h1.46c.401 0 .734-.08.998-.237a1.45 1.45 0 00.595-.689c.13-.3.196-.662.196-1.084 0-.42-.065-.778-.196-1.075a1.426 1.426 0 00-.589-.68c-.264-.156-.599-.234-1.005-.234H3.362zm.791.645h.563c.248 0 .45.05.609.152a.89.89 0 01.354.454c.079.201.118.452.118.753a2.3 2.3 0 01-.068.592 1.14 1.14 0 01-.196.422.8.8 0 01-.334.252 1.298 1.298 0 01-.483.082h-.563v-2.707zm3.743 1.763v1.591h-.79V11.85h2.548v.653H7.896v1.117h1.606v.638H7.896z" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1 +0,0 @@
<svg class="bi bi-filetype-png" fill="currentColor" height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M14 4.5V14a2 2 0 01-2 2v-1a1 1 0 001-1V4.5h-2A1.5 1.5 0 019.5 3V1H4a1 1 0 00-1 1v9H2V2a2 2 0 012-2h5.5L14 4.5zm-3.76 8.132c.076.153.123.317.14.492h-.776a.797.797 0 00-.097-.249.689.689 0 00-.17-.19.707.707 0 00-.237-.126.96.96 0 00-.299-.044c-.285 0-.506.1-.665.302-.156.201-.234.484-.234.85v.498c0 .234.032.439.097.615a.881.881 0 00.304.413.87.87 0 00.519.146.967.967 0 00.457-.096.67.67 0 00.272-.264c.06-.11.091-.23.091-.363v-.255H8.82v-.59h1.576v.798c0 .193-.032.377-.097.55a1.29 1.29 0 01-.293.458 1.37 1.37 0 01-.495.313c-.197.074-.43.111-.697.111a1.98 1.98 0 01-.753-.132 1.447 1.447 0 01-.533-.377 1.58 1.58 0 01-.32-.58 2.482 2.482 0 01-.105-.745v-.506c0-.362.067-.678.2-.95.134-.271.328-.482.582-.633.256-.152.565-.228.926-.228.238 0 .45.033.636.1.187.066.348.158.48.275.133.117.238.253.314.407zm-8.64-.706H0v4h.791v-1.343h.803c.287 0 .531-.057.732-.172.203-.118.358-.276.463-.475a1.42 1.42 0 00.161-.677c0-.25-.053-.475-.158-.677a1.176 1.176 0 00-.46-.477c-.2-.12-.443-.179-.732-.179zm.545 1.333a.795.795 0 01-.085.381.574.574 0 01-.238.24.794.794 0 01-.375.082H.788v-1.406h.66c.218 0 .389.06.512.182.123.12.185.295.185.521zm1.964 2.666V13.25h.032l1.761 2.675h.656v-3.999h-.75v2.66h-.032l-1.752-2.66h-.662v4h.747z" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1 +0,0 @@
<svg class="bi bi-filetype-svg" fill="currentColor" height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M14 4.5V14a2 2 0 01-2 2v-1a1 1 0 001-1V4.5h-2A1.5 1.5 0 019.5 3V1H4a1 1 0 00-1 1v9H2V2a2 2 0 012-2h5.5L14 4.5zM0 14.841a1.13 1.13 0 00.401.823c.13.108.288.192.478.252.19.061.411.091.665.091.338 0 .624-.053.858-.158.237-.105.416-.252.54-.44a1.17 1.17 0 00.187-.656c0-.224-.045-.41-.135-.56a1 1 0 00-.375-.357 2.027 2.027 0 00-.565-.21l-.621-.144a.97.97 0 01-.405-.176.37.37 0 01-.143-.299c0-.156.061-.284.184-.384.125-.101.296-.152.513-.152.143 0 .266.023.37.068a.625.625 0 01.245.181.56.56 0 01.12.258h.75a1.092 1.092 0 00-.199-.566 1.21 1.21 0 00-.5-.41 1.813 1.813 0 00-.78-.152c-.293 0-.552.05-.776.15-.225.099-.4.24-.528.421-.127.182-.19.395-.19.639 0 .201.04.376.123.524.082.149.199.27.351.367.153.095.332.167.54.213l.618.144c.207.049.36.113.462.193a.387.387 0 01.153.326.512.512 0 01-.085.29.559.559 0 01-.256.193c-.111.047-.249.07-.413.07-.117 0-.224-.013-.32-.04a.837.837 0 01-.248-.115.578.578 0 01-.255-.384H0zm4.575 1.09h.952l1.327-3.999h-.879l-.887 3.138H5.05l-.897-3.138h-.917l1.339 4zm5.483-3.293c.076.152.123.316.14.492h-.776a.797.797 0 00-.096-.249.689.689 0 00-.17-.19.707.707 0 00-.237-.126.963.963 0 00-.3-.044c-.284 0-.506.1-.664.302-.157.2-.235.484-.235.85v.497c0 .235.033.44.097.616a.881.881 0 00.305.413.87.87 0 00.518.146.965.965 0 00.457-.097.67.67 0 00.273-.263c.06-.11.09-.23.09-.364v-.254h-.823v-.59h1.576v.798c0 .193-.032.377-.096.55a1.29 1.29 0 01-.293.457 1.37 1.37 0 01-.495.314c-.198.074-.43.111-.698.111a1.98 1.98 0 01-.752-.132 1.447 1.447 0 01-.534-.377 1.58 1.58 0 01-.319-.58 2.482 2.482 0 01-.105-.745v-.507c0-.36.066-.677.199-.949.134-.271.329-.482.583-.633.256-.152.564-.228.926-.228.238 0 .45.033.635.1.188.066.348.158.48.275.134.117.238.253.314.407z" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1 +0,0 @@
<svg class="bi bi-filetype-txt" fill="currentColor" height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M14 4.5V14a2 2 0 01-2 2h-2v-1h2a1 1 0 001-1V4.5h-2A1.5 1.5 0 019.5 3V1H4a1 1 0 00-1 1v9H2V2a2 2 0 012-2h5.5L14 4.5zM1.928 15.849v-3.337h1.136v-.662H0v.662h1.134v3.337h.794zm4.689-3.999h-.894L4.9 13.289h-.035l-.832-1.439h-.932l1.228 1.983-1.24 2.016h.862l.853-1.415h.035l.85 1.415h.907l-1.253-1.992 1.274-2.007zm1.93.662v3.337h-.794v-3.337H6.619v-.662h3.064v.662H8.546z" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 516 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -1,14 +0,0 @@
<base href="<%= request.getContextPath() %>/" />
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="pragma" content="no-cache" />
<meta http-equiv="cache-control" content="no-cache, must-revalidate" />
<meta name="viewport" content="initial-scale=1.0, user-scalable=1" />
<meta name="color-scheme" content="light dark" />
<link rel="icon" href="favicon.ico" type="image/x-icon"/>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon"/>
<link rel="stylesheet" href="min/plantuml.min.css" />
<script src="min/plantuml.min.js"></script>
<script src="min/plantuml-language.min.js"></script>
<script src="webjars/monaco-editor/0.36.1/min/vs/loader.js"></script>

View File

@ -1,165 +0,0 @@
/**********************************
* PlantUML Server Application CSS *
***********************************/
/************* variables *************/
:root {
color-scheme: light dark;
--font-color: black;
--font-color-disabled: #888;
--bg-color: white;
--border-color: #ccc;
--border-color-2: #aaa;
--footer-font-color: #666;
--footer-bg-color: #eee;
--modal-bg-color: #fefefe;
--file-drop-color: #eee;
}
[data-theme="dark"] {
--font-color: #ccc;
--font-color-disabled: #777;
--bg-color: #212121;
--border-color: #848484;
--border-color-2: #aaa;
--footer-font-color: #ccc;
--footer-bg-color: black;
--modal-bg-color: #424242;
--file-drop-color: #212121;
}
/************* default settings *************/
html, body {
margin: 0;
padding: 0;
}
html {
font-family: arial,helvetica,sans-serif;
}
body {
background-color: var(--bg-color);
color: var(--font-color);
overflow: auto;
}
@media screen and (min-width: 900px) {
body {
height: 100vh;
overflow: hidden;
}
.app {
height: 100%;
}
}
input:not([type="image"]) {
background-color: var(--bg-color);
color: var(--font-color);
}
input[type="file"]::file-selector-button {
background-color: var(--bg-color);
color: var(--font-color);
}
select {
background-color: var(--bg-color);
color: var(--font-color);
}
/************* ruler *************/
.hr {
padding: 1rem 0;
width: 100%;
}
.flex-columns > .hr {
padding: 0 1rem;
width: initial;
height: 100%;
}
.hr:after {
content: "";
display: block;
background-color: var(--border-color);
height: 100%;
width: 100%;
min-height: 3px;
min-width: 3px;
}
/************* wait cursor *************/
.wait {
cursor: wait;
}
.wait > * {
pointer-events: none;
}
/************* flex rows and columns *************/
.flex-columns {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.flex-rows {
display: flex;
flex-direction: column;
}
.flex-main {
flex: 1 1 1px;
overflow: auto;
}
.flex-columns > *, .flex-rows > * {
flex-shrink: 0;
}
/*******************************************************************/
/************* header, main, footer *************/
.header {
margin-left: auto;
margin-right: auto;
text-align: center;
}
.main {
margin: 1% 5%;
z-index: 1;
}
.main > div {
margin: 0 1.75%;
}
.main > div:first-child {
margin-left: 0;
}
.main > div:last-child {
margin-right: 0;
}
@media screen and (max-width: 900px) {
.main {
display: block;
overflow: inherit;
}
.main > div {
margin: 1.75% 0;
}
.main > div:first-child {
margin-top: 0;
}
.main > div:last-child {
margin-bottom: 0;
}
}
.footer p {
background-color: var(--footer-bg-color);
color: var(--footer-font-color);
font-size: 0.7em;
margin: 0;
padding: 0.5em;
text-align: center;
}
/*******************************************************************/
/************* color themes *************/
[data-theme="dark"] img:not(#diagram-png):not(.no-filter) {
filter: invert() contrast(30%);
}
[data-theme="dark"] input[type="image"] {
filter: invert() contrast(30%);
}
[data-theme="dark"] a {
color: white;
}

View File

@ -1,46 +0,0 @@
/*********************************
* PlantUML Server Application JS *
**********************************/
"use strict";
async function initApp() {
const view = new URL(window.location.href).searchParams.get("view")?.toLowerCase();
function initializeAppData() {
const analysedUrl = analyseUrl(window.location.href);
const code = document.editor?.getValue();
document.appData = Object.assign({}, window.opener?.document.appData);
if (Object.keys(document.appData).length === 0) {
document.appData = {
encodedDiagram: analysedUrl.encodedDiagram,
index: analysedUrl.index,
numberOfDiagramPages: (code) ? getNumberOfDiagramPagesFromCode(code) : 1,
};
}
}
await initEditor(view);
initializeAppData();
initTheme();
initAppCommunication();
await initPreview(view);
initModals(view);
if (document.editor) {
document.editor.focus();
if (document.appData.encodedDiagram == "SyfFKj2rKt3CoKnELR1Io4ZDoSa70000") {
// if default `Bob -> Alice : hello` example mark example code for faster editing
document.editor.setSelection({
startLineNumber: 2,
endLineNumber: 2,
startColumn: 1,
endColumn: 21,
});
}
}
document.appConfig.autoRefreshState = "complete";
}
// main entry
window.onload = initApp;

View File

@ -1,28 +0,0 @@
/*************
* Editor CSS *
**************/
.editor {
border: 3px solid var(--border-color);
box-sizing: border-box;
overflow: hidden;
}
@media screen and (max-width: 900px) {
.editor {
height: 20em;
}
}
.editor .monaco-editor-container {
overflow: hidden;
position: relative;
}
#monaco-editor {
height: 100%;
}
/* Hack to display the icons and emojis in the auto completion documentation in a visible size.
* (see PlantUmlLanguageFeatures.register{Icon,Emoji}Completion) */
#monaco-editor .overlayWidgets .suggest-details p img[alt="icon"],
#monaco-editor .overlayWidgets .suggest-details p img[alt="emoji"] {
height: 1.2rem;
}

View File

@ -1,112 +0,0 @@
/************
* Editor JS *
*************/
const { setEditorValue, initEditor } = (function() {
function setEditorValue(
editor,
text,
{ suppressEditorChangedMessage=false, forceMoveMarkers=undefined } = {}
) {
if (suppressEditorChangedMessage && editor === document.editor) {
suppressNextMessage("editor");
}
// replace editor value but preserve undo stack
editor.executeEdits("", [{ range: editor.getModel().getFullModelRange(), text, forceMoveMarkers }]);
}
async function initEditor(view) {
function loadMonacoCodeEditorAsync() {
return new Promise((resolve, _reject) => {
require.config({ paths: { vs: "webjars/monaco-editor/0.36.1/min/vs" } });
require(["vs/editor/editor.main"], resolve);
});
}
function createEditorModel() {
let plantumlFeatures;
function onPlantumlEditorContentChanged(code, sender=undefined, broadcastChanges=true) {
function broadcastCodeEditorChanges() {
document.appConfig.autoRefreshState = "started";
const numberOfDiagramPages = getNumberOfDiagramPagesFromCode(code);
let index = document.appData.index;
if (index === undefined || numberOfDiagramPages === 1) {
index = undefined;
} else if (index >= numberOfDiagramPages) {
index = numberOfDiagramPages - 1;
}
makeRequest("POST", "coder", { data: code }).then((encodedDiagram) => {
sendMessage({
sender,
data: { encodedDiagram, numberOfDiagramPages, index },
synchronize: true,
});
});
}
const updatePlantumlLanguageMarkers = (function() {
return function() {
const model = document.editor.getModel();
plantumlFeatures = plantumlFeatures || new PlantUmlLanguageFeatures();
plantumlFeatures.validateCode(model)
.then(markers => monaco.editor.setModelMarkers(model, "plantuml", markers));
}
})();
if (sender && broadcastChanges) broadcastCodeEditorChanges();
updatePlantumlLanguageMarkers();
}
function getInitPlantumlCodeAndRemoveElement() {
const initCodeEl = document.getElementById("initCode");
const initCode = initCodeEl.value;
initCodeEl.remove();
return initCode;
}
// create editor model
const model = monaco.editor.createModel(
getInitPlantumlCodeAndRemoveElement(),
"apex",
monaco.Uri.parse("inmemory://plantuml")
);
// create editor model watcher
let timer = 0;
model.onDidChangeContent(() => {
clearTimeout(timer);
document.appConfig.autoRefreshState = "waiting";
timer = setTimeout(
() => onPlantumlEditorContentChanged(model.getValue(), "editor"),
document.appConfig.editorWatcherTimeout
);
});
return model;
}
function getDefaultStorageService() {
// create own storage service to expand suggestion documentation by default
return {
get() {},
getBoolean(key) { return key === "expandSuggestionDocs"; },
getNumber() { return 0; },
remove() {},
store() {},
onWillSaveState() {},
onDidChangeStorage() {},
onDidChangeValue() {},
};
}
// load monaco editor requirements
await loadMonacoCodeEditorAsync();
if (view !== "previewer") {
// create editor
const model = createEditorModel();
const storageService = getDefaultStorageService();
document.editor = monaco.editor.create(document.getElementById("monaco-editor"), {
model, ...document.appConfig.editorCreateOptions
}, { storageService });
// sometimes the monaco editor has resize problems
document.addEventListener("resize", () => document.editor.layout());
// init editor components
initEditorUrlInput();
initEditorMenu();
}
}
return { setEditorValue, initEditor };
})();

View File

@ -1,10 +0,0 @@
<div id="editor-main-container" class="editor flex-main flex-rows">
<div>
<%@ include file="/components/editor/url-input/editor-url-input.jsp" %>
</div>
<div class="flex-main monaco-editor-container">
<textarea id="initCode" name="initCode" style="display: none;"><%= net.sourceforge.plantuml.servlet.PlantUmlServlet.stringToHTMLString(decoded) %></textarea>
<div id="monaco-editor"></div>
<%@ include file="/components/editor/menu/editor-menu.jsp" %>
</div>
</div>

View File

@ -1,92 +0,0 @@
/******************
* Editor Menu CSS *
*******************/
.monaco-editor-container .editor-menu {
position: absolute;
right: 0;
top: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex: 1;
}
.monaco-editor-container .editor-menu > div.menu-kebab {
width: 60px;
height: 60px;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
cursor: pointer;
scale: 0.5;
}
.monaco-editor-container .editor-menu:hover > div.menu-kebab,
.monaco-editor-container .editor-menu:focus > div.menu-kebab {
outline: none;
scale: 0.65;
}
.monaco-editor-container .menu-kebab .kebab-circle {
width: 12px;
height: 12px;
margin: 3px;
background: var(--font-color);
border-radius: 50%;
display: block;
opacity: 0.8;
}
.monaco-editor-container .menu-kebab {
flex-direction: column;
position: relative;
transition: all 300ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.monaco-editor-container .menu-kebab .kebab-circle:nth-child(4),
.monaco-editor-container .menu-kebab .kebab-circle:nth-child(5) {
position: absolute;
opacity: 0;
top: 50%;
margin-top: -6px;
left: 50%;
}
.monaco-editor-container .menu-kebab .kebab-circle:nth-child(4) {
margin-left: -25px;
}
.monaco-editor-container .menu-kebab .kebab-circle:nth-child(5) {
margin-left: 13px;
}
.monaco-editor-container .editor-menu:hover .menu-kebab,
.monaco-editor-container .editor-menu:focus .menu-kebab {
transform: rotate(45deg);
}
.monaco-editor-container .editor-menu:hover .menu-kebab .kebab-circle,
.monaco-editor-container .editor-menu:focus .menu-kebab .kebab-circle {
opacity: 1;
}
.monaco-editor-container .editor-menu .menu-item {
display: none;
margin: 1rem 0;
height: 1.75rem;
opacity: 0.5;
position: relative;
-webkit-animation-name: editor-menu-animateitem;
-webkit-animation-duration: 0.4s;
animation-name: editor-menu-animateitem;
animation-duration: 0.4s;
}
@-webkit-keyframes editor-menu-animateitem {
from { top: -50%; opacity: 0; }
to { top: 0; opacity: 0.5; }
}
@keyframes editor-menu-animateitem {
from { top: -50%; opacity: 0; }
to { top: 0; opacity: 0.5; }
}
.monaco-editor-container .editor-menu .menu-item:hover {
opacity: 1;
}
.monaco-editor-container .editor-menu:hover .menu-item,
.monaco-editor-container .editor-menu:focus .menu-item {
display: block;
}

View File

@ -1,15 +0,0 @@
/*****************
* Editor Menu JS *
******************/
function initEditorMenu() {
function copyCodeToClipboard() {
const range = document.editor.getModel().getFullModelRange();
document.editor.focus();
document.editor.setSelection(range);
const code = document.editor.getValue();
navigator.clipboard?.writeText(code).catch(() => {});
}
// add listener
document.getElementById("menu-item-editor-code-copy").addEventListener("click", copyCodeToClipboard);
}

View File

@ -1,35 +0,0 @@
<div class="editor-menu" tabindex="-1">
<div class="menu-kebab">
<div class="kebab-circle"></div>
<div class="kebab-circle"></div>
<div class="kebab-circle"></div>
<div class="kebab-circle"></div>
<div class="kebab-circle"></div>
</div>
<div class="menu-items">
<input
id="menu-item-editor-code-copy"
class="menu-item"
type="image"
alt="copy"
title="Copy code"
src="assets/actions/copy.svg"
/>
<input
class="menu-item"
type="image"
alt="import"
title="Import diagram"
src="assets/actions/upload.svg"
onclick="openModal('diagram-import')"
/>
<input
class="menu-item"
type="image"
alt="export"
title="Export diagram"
src="assets/actions/download.svg"
onclick="openModal('diagram-export')"
/>
</div>
</div>

View File

@ -1,29 +0,0 @@
/***********************
* Editor URL Input CSS *
************************/
.editor .btn-input {
align-items: center;
border-bottom: 3px solid var(--border-color);
box-sizing: border-box;
display: flex;
justify-content: center;
}
.editor .btn-input input[type=text] {
border: 0;
flex: 1 1 1px;
font-family: monospace;
font-size: medium;
padding: 0.2em;
text-overflow: ellipsis;
}
.editor .btn-input input[type=text]:focus {
border: 0;
box-shadow: none;
outline: none;
}
.editor .btn-input input[type="image"] {
height: 1rem;
margin-left: 0.7em;
padding: 0 0.3em;
}

View File

@ -1,53 +0,0 @@
/**********************
* Editor URL Input JS *
***********************/
const { setUrlValue, initEditorUrlInput } = (function() {
function setUrlValue(
url=undefined,
{ encodedDiagram=undefined, index=undefined } = {},
{ suppressEditorChangedMessage=false } = {}
) {
if (!url && !encodedDiagram) return;
if (suppressEditorChangedMessage) {
suppressNextMessage("url");
}
document.getElementById("url").value = url ? url : resolvePath(buildUrl("png", encodedDiagram, index));
}
function initEditorUrlInput() {
const input = document.getElementById("url");
function copyUrlToClipboard() {
input.focus();
input.select();
navigator.clipboard?.writeText(input.value).catch(() => {});
}
async function onInputChanged(event) {
document.appConfig.autoRefreshState = "started";
event.target.title = event.target.value;
const analysedUrl = analyseUrl(event.target.value);
// decode diagram (server request)
const code = await makeRequest("GET", "coder/" + analysedUrl.encodedDiagram);
// change editor content without sending the editor change message
setEditorValue(document.editor, code, { suppressEditorChangedMessage: true });
sendMessage({
sender: "url",
data: {
encodedDiagram: analysedUrl.encodedDiagram,
index: analysedUrl.index,
},
synchronize: true,
});
}
// resolve relative path inside url input once
setUrlValue(resolvePath(input.value));
// update editor and everything else if the URL input is changed
input.addEventListener("change", onInputChanged);
// add listener
document.getElementById("url-copy-btn").addEventListener("click", copyUrlToClipboard);
}
return { setUrlValue, initEditorUrlInput };
})();

View File

@ -1,4 +0,0 @@
<div class="btn-input">
<input id="url" type="text" name="url" value="png/<%= diagramUrl %>" />
<input id="url-copy-btn" type="image" alt="copy" src="assets/actions/copy.svg" tabindex="-1" />
</div>

View File

@ -1 +0,0 @@
<p><%= net.sourceforge.plantuml.version.Version.fullDescription() %></p>

View File

@ -1,17 +0,0 @@
<div>
<img
style="display: inline; position: absolute; top: 0; right: 0; border: 0; max-width: 25%;"
class="no-filter"
src="assets/github-fork-me.svg"
alt="Fork me on GitHub"
usemap="#github-banner"
/>
<map id="github-banner" name="github-banner" style="cursor: pointer;">
<area
shape="poly"
coords="10,0 50,0 149,100 149,140"
href="https://github.com/plantuml/plantuml-server"
alt="Fork me on GitHub"
/>
</map>
</div>

View File

@ -1,8 +0,0 @@
<h1>PlantUML Server</h1>
<% if (showSocialButtons) { %>
<%@ include file="/components/header/social-buttons.jsp" %>
<% } %>
<% if (showGithubRibbon) { %>
<%@ include file="/components/header/github-ribbon.jsp" %>
<% } %>
<p>Create your <a href="https://plantuml.com">PlantUML</a> diagrams directly in your browser!</p>

View File

@ -1 +0,0 @@
<!-- Insert here the html code of your social buttons -->

View File

@ -1,7 +0,0 @@
/*********************
* Diagram Export CSS *
**********************/
#diagram-export.modal .label-input-pair label {
min-width: 8rem;
}

View File

@ -1,97 +0,0 @@
/********************
* Diagram Export JS *
*********************/
function initDiagramExport() {
const filenameInput = document.getElementById("download-name");
const fileTypeSelect = document.getElementById("download-type");
function openDiagramExportDialog() {
setVisibility(document.getElementById("diagram-export"), true, true);
const code = document.editor.getValue();
const name = Array.from(
code.matchAll(/^\s*@start[a-zA-Z]*\s+([a-zA-Z-_äöüÄÖÜß ]+)\s*$/gm),
m => m[1]
)[0] || "diagram";
filenameInput.value = name + ".puml";
fileTypeSelect.value = "code";
filenameInput.focus();
}
function splitFilename(filename) {
const idx = filename.lastIndexOf(".");
if (idx < 1) {
return { name: filename, ext: null };
}
if (idx === filename.length - 1) {
return { name: filename.slice(0, -1), ext: null };
}
return {
name: filename.substring(0, idx),
ext: filename.substring(idx + 1),
};
}
function getExtensionByType(type) {
switch (type) {
case "epstext": return "eps";
case "code": return "puml";
default: return type;
}
}
function getTypeByExtension(ext) {
if (!ext) return ext;
ext = ext.toLowerCase();
switch (ext) {
case "puml":
case "plantuml":
case "code":
return "code";
case "ascii": return "txt"
default: return ext;
}
}
function onTypeChanged(event) {
const type = event.target.value;
const ext = getExtensionByType(type);
const { name } = splitFilename(filenameInput.value);
filenameInput.value = name + "." + ext;
}
function onFilenameChanged(event) {
const { ext } = splitFilename(event.target.value);
const type = getTypeByExtension(ext);
if (!type) return;
fileTypeSelect.value = type;
}
function downloadFile() {
const filename = filenameInput.value;
const type = fileTypeSelect.value;
const link = document.createElement("a");
link.download = filename;
if (type === "code") {
const code = document.editor.getValue();
link.href = "data:," + encodeURIComponent(code);
} else {
if (document.appData.index !== undefined) {
link.href = type + "/" + document.appData.index + "/" + document.appData.encodedDiagram;
} else {
link.href = type + "/" + document.appData.encodedDiagram;
}
}
link.click();
}
// register modal
registerModalListener("diagram-export", openDiagramExportDialog);
// add listener
filenameInput.addEventListener("change", onFilenameChanged);
fileTypeSelect.addEventListener("change", onTypeChanged);
document.getElementById("diagram-export-ok-btn").addEventListener("click", downloadFile);
// add Ctrl+S or Meta+S (Mac) key shortcut to open export dialog
window.addEventListener("keydown", event => {
if (event.key === "s" && (isMac ? event.metaKey : event.ctrlKey)) {
event.preventDefault();
if (!isModalOpen("diagram-export")) {
openDiagramExportDialog();
}
}
}, false);
}

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