Java part of the Admin UI

Jens Pelzetter 2020-08-24 21:27:40 +02:00
parent c8138350b6
commit a0ae1d1c64
32 changed files with 14097 additions and 8 deletions

View File

@ -59,7 +59,7 @@
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<nodeVersion>v12.18.2</nodeVersion>
<nodeVersion>${nodeVersion}</nodeVersion>
</configuration>
</execution>
<execution>

View File

@ -107,7 +107,7 @@
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<nodeVersion>v8.11.4</nodeVersion>
<nodeVersion>${nodeVersion}</nodeVersion>
</configuration>
</execution>
<!-- <execution>

View File

@ -75,7 +75,7 @@
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<nodeVersion>v12.18.2</nodeVersion>
<nodeVersion>${nodeVersion}</nodeVersion>
</configuration>
</execution>
<execution>

View File

@ -72,7 +72,7 @@
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<nodeVersion>v12.18.2</nodeVersion>
<nodeVersion>${nodeVersion}</nodeVersion>
</configuration>
</execution>
<execution>

View File

@ -58,7 +58,7 @@
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<nodeVersion>v12.18.2</nodeVersion>
<nodeVersion>${nodeVersion}</nodeVersion>
</configuration>
</execution>
<execution>

View File

@ -307,6 +307,9 @@
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>target/generated-resources/@admin</directory>
</resource>
</resources>
<testResources>
@ -456,6 +459,46 @@
</plugin>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<configuration>
<installDirectory>../node</installDirectory>
</configuration>
<executions>
<execution>
<goals>
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<nodeVersion>${nodeVersion}</nodeVersion>
</configuration>
</execution>
<execution>
<id>npm install ccm-admin-systeminformation</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>install</arguments>
<workingDirectory>src/ccm-admin-systeminformation</workingDirectory>
</configuration>
</execution>
<execution>
<id>build ccm-admin-systeminformation</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>run build</arguments>
<workingDirectory>src/ccm-admin-systeminformation</workingDirectory>
</configuration>
</execution>
</executions>
</plugin>
<!-- <plugin>
<groupId>io.swagger</groupId>
<artifactId>swagger-codegen-maven-plugin</artifactId>
<executions>
@ -471,7 +514,7 @@
</configuration>
</execution>
</executions>
</plugin>
</plugin>-->
</plugins>
</build>

View File

@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@ -0,0 +1,24 @@
# ccm-admin-systeminformation
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

View File

@ -0,0 +1,3 @@
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"]
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,66 @@
{
"name": "ccm-admin-systeminformation",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build --dest ../../target/generated-resources/@admin/systeminformation",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.6.5",
"vue": "^2.6.11",
"vue-class-component": "^7.2.3",
"vue-property-decorator": "^8.4.2",
"vue-router": "^3.2.0",
"vuex": "^3.4.0"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^2.33.0",
"@typescript-eslint/parser": "^2.33.0",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-plugin-typescript": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/eslint-config-typescript": "^5.0.2",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-vue": "^6.2.2",
"node-sass": "^4.12.0",
"prettier": "^1.19.1",
"sass-loader": "^8.0.2",
"typescript": "~3.9.3",
"vue-template-compiler": "^2.6.11"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended",
"@vue/typescript/recommended",
"@vue/prettier",
"@vue/prettier/@typescript-eslint"
],
"parserOptions": {
"ecmaVersion": 2020
},
"rules": {}
},
"prettier": {
"trailingComma": "es5",
"tabWidth": 4,
"semi": true,
"singleQuote": false
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@ -0,0 +1,32 @@
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<router-view />
</div>
</template>
<style lang="scss">
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
padding: 30px;
a {
font-weight: bold;
color: #2c3e50;
&.router-link-exact-active {
color: #42b983;
}
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -0,0 +1,138 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br />
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener"
>vue-cli documentation</a
>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel"
target="_blank"
rel="noopener"
>babel</a
>
</li>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router"
target="_blank"
rel="noopener"
>router</a
>
</li>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex"
target="_blank"
rel="noopener"
>vuex</a
>
</li>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint"
target="_blank"
rel="noopener"
>eslint</a
>
</li>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-typescript"
target="_blank"
rel="noopener"
>typescript</a
>
</li>
</ul>
<h3>Essential Links</h3>
<ul>
<li>
<a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a>
</li>
<li>
<a href="https://forum.vuejs.org" target="_blank" rel="noopener"
>Forum</a
>
</li>
<li>
<a href="https://chat.vuejs.org" target="_blank" rel="noopener"
>Community Chat</a
>
</li>
<li>
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener"
>Twitter</a
>
</li>
<li>
<a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a>
</li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li>
<a href="https://router.vuejs.org" target="_blank" rel="noopener"
>vue-router</a
>
</li>
<li>
<a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a>
</li>
<li>
<a
href="https://github.com/vuejs/vue-devtools#vue-devtools"
target="_blank"
rel="noopener"
>vue-devtools</a
>
</li>
<li>
<a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener"
>vue-loader</a
>
</li>
<li>
<a
href="https://github.com/vuejs/awesome-vue"
target="_blank"
rel="noopener"
>awesome-vue</a
>
</li>
</ul>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
@Component
export default class HelloWorld extends Vue {
@Prop() private msg!: string;
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

View File

@ -0,0 +1,12 @@
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
Vue.config.productionTip = false;
new Vue({
router,
store,
render: h => h(App)
}).$mount("#app");

View File

@ -0,0 +1,30 @@
import Vue from "vue";
import VueRouter, { RouteConfig } from "vue-router";
import Home from "../views/Home.vue";
Vue.use(VueRouter);
const routes: Array<RouteConfig> = [
{
path: "/",
name: "Home",
component: Home
},
{
path: "/about",
name: "About",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "about" */ "../views/About.vue")
}
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes
});
export default router;

View File

@ -0,0 +1,13 @@
import Vue, { VNode } from "vue";
declare global {
namespace JSX {
// tslint:disable no-empty-interface
interface Element extends VNode {}
// tslint:disable no-empty-interface
interface ElementClass extends Vue {}
interface IntrinsicElements {
[elem: string]: any;
}
}
}

View File

@ -0,0 +1,4 @@
declare module "*.vue" {
import Vue from "vue";
export default Vue;
}

View File

@ -0,0 +1,11 @@
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {},
mutations: {},
actions: {},
modules: {}
});

View File

@ -0,0 +1,5 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>

View File

@ -0,0 +1,18 @@
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png" />
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App" />
</div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import HelloWorld from "@/components/HelloWorld.vue"; // @ is an alias to /src
@Component({
components: {
HelloWorld
}
})
export default class Home extends Vue {}
</script>

View File

@ -0,0 +1,40 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}

View File

@ -0,0 +1,132 @@
/*
* Copyright (C) 2020 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is 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 Lesser 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 org.libreccm.ui.admin;
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.cache.WebappTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import org.libreccm.core.CoreConstants;
import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.RequiresPrivilege;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.servlet.ServletContext;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Path("/")
public class AdminUi {
@Inject
private AdminUiApps adminUiApps;
@Inject
private ServletContext servletContext;
private final Configuration configuration;
public AdminUi() {
configuration = new Configuration(Configuration.VERSION_2_3_30);
configuration.setDefaultEncoding("UTF-8");
configuration
.setTemplateExceptionHandler(
TemplateExceptionHandler.RETHROW_HANDLER);
configuration.setLogTemplateExceptions(false);
configuration.setWrapUncheckedExceptions(false);
configuration.setLocalizedLookup(false);
configuration.setTemplateLoader(
new MultiTemplateLoader(
new TemplateLoader[]{
new WebappTemplateLoader(
servletContext, "/templates/admin-ui"
),
new ClassTemplateLoader(getClass(), "/templates/admin-ui")
}
)
);
}
@GET
@Path("/{appName}")
@Produces(MediaType.TEXT_HTML)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public String getApp(@PathParam("appName") final String appName) {
final AdminUiApp app = adminUiApps
.getAdminUiApp(appName)
.orElseThrow(
() -> new NotFoundException(
String.format(
"No AdminUiApp with name \"%s\" available.", appName
)
)
);
final Template template;
try {
template = configuration.getTemplate("admin-ui.html.ftl");
} catch (IOException ex) {
throw new WebApplicationException(
"Error reading template for AdminUI",
ex,
Response.Status.INTERNAL_SERVER_ERROR
);
}
final Map<String, Object> data = new HashMap<>();
data.put("adminUiApps", adminUiApps.getAdminUiApps());
data.put("activeApp", appName);
final StringWriter writer = new StringWriter();
try {
template.process(data, writer);
} catch (TemplateException | IOException ex) {
throw new WebApplicationException(
"Error processing template for AdminUI",
ex,
Response.Status.INTERNAL_SERVER_ERROR
);
}
return writer.toString();
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (C) 2020 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is 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 Lesser 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 org.libreccm.ui.admin;
import java.util.Optional;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public interface AdminUiApp {
/**
* The name of the Admin UI App. Used as slug for the app.
*
* @return
*/
String getName();
/**
* Localized label for the application
*
* @return
*/
String getLabel();
/**
* Localized short description of the application.
*
* @return
*/
String getDescription();
/**
* A number which is used to order the app in the Admin UI Navbar
*
* @return
*/
int getOrder();
/**
* Path portion of the URL of the JavaScript file providing the Admin UI
* App.
*
* @return
*/
String getJsFileUrl();
/**
* Name of the icon from the Bootstrap Icons set
*
* @return The name of the icon of an empty {@link Optional} if no icon
* should be used for the app.
*
* @see https://icons.getbootstrap.com/
*/
Optional<String> getIconName();
/**
* An alternative large symbol for the Admin UI app to use in the dashboard.
* If none is provided the icon will be used.
*
* @return The URL path of the symbol to use for the app or any empty
* {@link Optional} if the icon should be used.
*/
Optional<String> getSymbolUrl();
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2020 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is 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 Lesser 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 org.libreccm.ui.admin;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@ApplicationPath("/@admin")
public class AdminUiApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
final HashSet<Class<?>> classes = new HashSet<>();
classes.add(AdminUi.class);
return classes;
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright (C) 2020 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is 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 Lesser 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 org.libreccm.ui.admin;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@ApplicationScoped
public class AdminUiApps {
@Inject
private Instance<AdminUiApp> adminUiApps;
public Optional<AdminUiApp> getAdminUiApp(final String name) {
return adminUiApps
.stream()
.filter(app -> app.getName().equals(name))
.findAny();
}
public List<AdminUiApp> getAdminUiApps() {
return adminUiApps
.stream()
.sorted(
(app1, app2) -> Integer.compare(
app1.getOrder(), app2.getOrder()
)
)
.collect(Collectors.toList());
}
}

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<title>Admin UI</title>
</head>
<body>
<h1>Admin UI</h1>
<ul>
<#list adminUis as adminUi>
<li>{{adminUi.name}}</li>
</#list>
</ul>
</body>
</html>

View File

@ -53,7 +53,7 @@
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<nodeVersion>v12.18.2</nodeVersion>
<nodeVersion>${nodeVersion}</nodeVersion>
</configuration>
</execution>
<execution>

View File

@ -61,7 +61,7 @@
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<nodeVersion>v12.18.2</nodeVersion>
<nodeVersion>${nodeVersion}</nodeVersion>
</configuration>
</execution>
<execution>

View File

@ -6,6 +6,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!--<ccm.version>7.0.0-SNAPSHOT</ccm.version>-->
<nodeVersion>v12.18.3</nodeVersion>
</properties>
<prerequisites>