Integration of Vue.js now working

Former-commit-id: 982f4f1fd5
restapi
Jens Pelzetter 2020-08-27 20:48:00 +02:00
parent dfe36e2614
commit 46ea2de206
14 changed files with 172 additions and 49 deletions

View File

@ -245,6 +245,9 @@
<configuration> <configuration>
<skip>false</skip> <skip>false</skip>
<propertiesFile>${project.basedir}/wildfly.properties</propertiesFile> <propertiesFile>${project.basedir}/wildfly.properties</propertiesFile>
<java-opts>
<java-opt>-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8787</java-opt>
</java-opts>
</configuration> </configuration>
</plugin> </plugin>

View File

@ -101,5 +101,10 @@
<Logger name="com.arsdigita.web.DefaultApplicationFileResolver" <Logger name="com.arsdigita.web.DefaultApplicationFileResolver"
level="debug"> level="debug">
</Logger> </Logger>
<Logger name="org.libreccm.ui.admin.AdminUi"
level="debug">
</Logger>
</Loggers> </Loggers>
</Configuration> </Configuration>

View File

@ -1142,6 +1142,16 @@
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==",
"dev": true "dev": true
}, },
"@nuxt/opencollective": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@nuxt/opencollective/-/opencollective-0.3.0.tgz",
"integrity": "sha512-Vf09BxCdj1iT2IRqVwX5snaY2WCTkvM0O4cWWSO1ThCFuc4if0Q/nNwAgCxRU0FeYHJ7DdyMUNSdswCLKlVqeg==",
"requires": {
"chalk": "^2.4.2",
"consola": "^2.10.1",
"node-fetch": "^2.6.0"
}
},
"@soda/friendly-errors-webpack-plugin": { "@soda/friendly-errors-webpack-plugin": {
"version": "1.7.1", "version": "1.7.1",
"resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.1.tgz", "resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.1.tgz",
@ -2446,7 +2456,6 @@
"version": "3.2.1", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": { "requires": {
"color-convert": "^1.9.0" "color-convert": "^1.9.0"
} }
@ -2973,6 +2982,23 @@
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
"dev": true "dev": true
}, },
"bootstrap": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.5.2.tgz",
"integrity": "sha512-vlGn0bcySYl/iV+BGA544JkkZP5LB3jsmkeKLFQakCOwCM3AOk7VkldBz4jrzSe+Z0Ezn99NVXa1o45cQY4R6A=="
},
"bootstrap-vue": {
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/bootstrap-vue/-/bootstrap-vue-2.16.0.tgz",
"integrity": "sha512-gLETwPmeRHCe5WHmhGxzb5PtTEuKqQPGl0TFvZ2Odbkg/7UuIHdqIexrJRerpnomP4ZzDQ+qYGL91Ls9lcQsJQ==",
"requires": {
"@nuxt/opencollective": "^0.3.0",
"bootstrap": ">=4.5.0 <5.0.0",
"popper.js": "^1.16.1",
"portal-vue": "^2.1.7",
"vue-functional-data-merge": "^3.1.0"
}
},
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -3414,7 +3440,6 @@
"version": "2.4.2", "version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": { "requires": {
"ansi-styles": "^3.2.1", "ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5", "escape-string-regexp": "^1.0.5",
@ -3739,7 +3764,6 @@
"version": "1.9.3", "version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": { "requires": {
"color-name": "1.1.3" "color-name": "1.1.3"
} }
@ -3747,8 +3771,7 @@
"color-name": { "color-name": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
"dev": true
}, },
"color-string": { "color-string": {
"version": "1.5.3", "version": "1.5.3",
@ -3864,6 +3887,11 @@
"integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==",
"dev": true "dev": true
}, },
"consola": {
"version": "2.15.0",
"resolved": "https://registry.npmjs.org/consola/-/consola-2.15.0.tgz",
"integrity": "sha512-vlcSGgdYS26mPf7qNi+dCisbhiyDnrN1zaRbw3CSuc2wGOMEGGPsp46PdRG5gqXwgtJfjxDkxRNAgRPr1B77vQ=="
},
"console-browserify": { "console-browserify": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
@ -5103,8 +5131,7 @@
"escape-string-regexp": { "escape-string-regexp": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
"dev": true
}, },
"eslint": { "eslint": {
"version": "6.8.0", "version": "6.8.0",
@ -6311,8 +6338,7 @@
"has-flag": { "has-flag": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
"dev": true
}, },
"has-symbols": { "has-symbols": {
"version": "1.0.1", "version": "1.0.1",
@ -8175,6 +8201,11 @@
"lower-case": "^1.1.1" "lower-case": "^1.1.1"
} }
}, },
"node-fetch": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
},
"node-forge": { "node-forge": {
"version": "0.9.0", "version": "0.9.0",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz",
@ -8971,6 +9002,16 @@
"ts-pnp": "^1.1.6" "ts-pnp": "^1.1.6"
} }
}, },
"popper.js": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ=="
},
"portal-vue": {
"version": "2.1.7",
"resolved": "https://registry.npmjs.org/portal-vue/-/portal-vue-2.1.7.tgz",
"integrity": "sha512-+yCno2oB3xA7irTt0EU5Ezw22L2J51uKAacE/6hMPMoO/mx3h4rXFkkBkT4GFsMDv/vEe8TNKC3ujJJ0PTwb6g=="
},
"portfinder": { "portfinder": {
"version": "1.0.28", "version": "1.0.28",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
@ -11336,7 +11377,6 @@
"version": "5.5.0", "version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": { "requires": {
"has-flag": "^3.0.0" "has-flag": "^3.0.0"
} }
@ -12187,6 +12227,11 @@
} }
} }
}, },
"vue-functional-data-merge": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/vue-functional-data-merge/-/vue-functional-data-merge-3.1.0.tgz",
"integrity": "sha512-leT4kdJVQyeZNY1kmnS1xiUlQ9z1B/kdBFCILIjYYQDqZgLqCLa0UhjSSeRX6c3mUe6U5qYeM8LrEqkHJ1B4LA=="
},
"vue-hot-reload-api": { "vue-hot-reload-api": {
"version": "2.3.4", "version": "2.3.4",
"resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz",

View File

@ -8,6 +8,8 @@
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"bootstrap": "^4.5.2",
"bootstrap-vue": "^2.16.0",
"core-js": "^3.6.5", "core-js": "^3.6.5",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-class-component": "^7.2.3", "vue-class-component": "^7.2.3",
@ -36,7 +38,8 @@
"vue-template-compiler": "^2.6.11" "vue-template-compiler": "^2.6.11"
}, },
"vue": { "vue": {
"filenameHashing": false "filenameHashing": false,
"publicPath": "./"
}, },
"eslintConfig": { "eslintConfig": {
"root": true, "root": true,

View File

@ -8,7 +8,7 @@
>vue-cli documentation</a >vue-cli documentation</a
>. >.
</p> </p>
<h3>Installed CLI Plugins</h3> <h3>Installed CLI Plugins</h3>
<ul> <ul>
<li> <li>
<a <a

View File

@ -3,10 +3,18 @@ import App from "./App.vue";
import router from "./router"; import router from "./router";
import store from "./store"; import store from "./store";
import { BootstrapVue, IconsPlugin } from "bootstrap-vue";
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
Vue.config.productionTip = false; Vue.config.productionTip = false;
Vue.use(BootstrapVue);
Vue.use(IconsPlugin);
new Vue({ new Vue({
router, router,
store, store,
render: h => h(App) render: h => h(App),
}).$mount("#app"); }).$mount("#app");

View File

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

View File

@ -1,8 +1,20 @@
<template> <template>
<div class="home"> <div class="home">
<img alt="Vue logo" src="../assets/logo.png" /> <b-navbar toggleable="lg" type="dark" variant="info">
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App" /> <b-navbar-brand href="#">LibreCCM</b-navbar-brand>
</div>
<b-navbar-toggle target="nav-collapse"></b-navbar-toggle>
<b-collapse id="nav-collapse" is-nav>
<b-navbar-nav>
<b-nav-item href="#">Link 1</b-nav-item>
<b-nav-item href="#">Link 2</b-nav-item>
</b-navbar-nav>
</b-collapse>
</b-navbar>
<img alt="Vue logo" src="../assets/logo.png" />
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App" />
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -10,9 +22,9 @@ import { Component, Vue } from "vue-property-decorator";
import HelloWorld from "@/components/HelloWorld.vue"; // @ is an alias to /src import HelloWorld from "@/components/HelloWorld.vue"; // @ is an alias to /src
@Component({ @Component({
components: { components: {
HelloWorld HelloWorld,
} },
}) })
export default class Home extends Vue {} export default class Home extends Vue {}
</script> </script>

View File

@ -26,10 +26,13 @@ import freemarker.template.Configuration;
import freemarker.template.Template; import freemarker.template.Template;
import freemarker.template.TemplateException; import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler; import freemarker.template.TemplateExceptionHandler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.libreccm.security.Shiro; import org.libreccm.security.Shiro;
import java.io.IOException; import java.io.IOException;
import java.io.StringWriter; import java.io.StringWriter;
import java.net.URI;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -42,11 +45,12 @@ import javax.ws.rs.NotFoundException;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.RedirectionException;
import javax.ws.rs.WebApplicationException; import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
/** /**
* *
@ -56,6 +60,8 @@ import javax.ws.rs.core.UriBuilder;
@Path("/") @Path("/")
public class AdminUi { public class AdminUi {
private static final Logger LOGGER = LogManager.getLogger(AdminUi.class);
@Inject @Inject
private AdminUiApps adminUiApps; private AdminUiApps adminUiApps;
@ -94,8 +100,8 @@ public class AdminUi {
@Produces(MediaType.TEXT_HTML) @Produces(MediaType.TEXT_HTML)
// @AuthorizationRequired // @AuthorizationRequired
// @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) // @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public String getDashboard() { public Response getDashboard(@Context final UriInfo uriInfo) {
return getApp("dashboard"); return getApp("dashboard", uriInfo);
} }
@GET @GET
@ -103,17 +109,32 @@ public class AdminUi {
@Produces(MediaType.TEXT_HTML) @Produces(MediaType.TEXT_HTML)
// @AuthorizationRequired // @AuthorizationRequired
// @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) // @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public String getApp(@PathParam("appName") final String appName) { public Response getApp(
@PathParam("appName") final String appName,
@Context UriInfo uriInfo
) {
if (!shiro.getUser().isPresent() if (!shiro.getUser().isPresent()
|| !shiro.getSubject().isAuthenticated()) { || !shiro.getSubject().isAuthenticated()) {
final UriBuilder uriBuilder = UriBuilder.fromUri("/ccm/register"); final URI uri = UriBuilder
uriBuilder.queryParam( .fromUri(
"return_url", String.format(
String.format("%s/@admin", servletContext.getContextPath()) "%s://%s:%d%s/ccm/register",
); uriInfo.getBaseUri().getScheme(),
throw new RedirectionException( uriInfo.getBaseUri().getHost(),
Response.Status.TEMPORARY_REDIRECT, uriBuilder.build() uriInfo.getBaseUri().getPort(),
); servletContext.getContextPath()
)
)
.queryParam(
"return_url",
String.format(
"%s/@admin/%s",
servletContext.getContextPath(),
appName
)
).build();
LOGGER.debug("Redirecting to {}...", uri.toString());
return Response.temporaryRedirect(uri).build();
} }
final AdminUiApp app = adminUiApps final AdminUiApp app = adminUiApps
@ -141,6 +162,7 @@ public class AdminUi {
data.put("adminUiApps", adminUiApps.getAdminUiApps()); data.put("adminUiApps", adminUiApps.getAdminUiApps());
data.put("activeApp", app); data.put("activeApp", app);
data.put("currentUser", shiro.getUser().get()); data.put("currentUser", shiro.getUser().get());
data.put("contextPath", servletContext.getContextPath());
final StringWriter writer = new StringWriter(); final StringWriter writer = new StringWriter();
try { try {
@ -153,7 +175,7 @@ public class AdminUi {
); );
} }
return writer.toString(); return Response.ok(writer.toString()).build();
} }
} }

View File

@ -31,7 +31,7 @@ public interface AdminUiApp {
* *
* @return * @return
*/ */
String getName(); String getSlug();
/** /**
* Localized label for the application * Localized label for the application

View File

@ -39,7 +39,7 @@ public class AdminUiApps {
public Optional<AdminUiApp> getAdminUiApp(final String name) { public Optional<AdminUiApp> getAdminUiApp(final String name) {
return adminUiApps return adminUiApps
.stream() .stream()
.filter(app -> app.getName().equals(name)) .filter(app -> app.getSlug().equals(name))
.findAny(); .findAny();
} }

View File

@ -42,15 +42,16 @@ public class DashboardApp implements AdminUiApp {
@PostConstruct @PostConstruct
private void init() { private void init() {
adminBundle = ResourceBundle.getBundle( adminBundle = ResourceBundle.getBundle(
"org.libreccm.ui.admin", globalizationHelper.getNegotiatedLocale() "org.libreccm.ui.admin",
globalizationHelper.getNegotiatedLocale()
); );
} }
@Override @Override
public String getName() { public String getSlug() {
return "dashboard"; return "dashboard";
} }
@Override @Override
public String getLabel() { public String getLabel() {
return adminBundle.getString("dashboard.label"); return adminBundle.getString("dashboard.label");

View File

@ -47,7 +47,7 @@ public class SystemInformationApp implements AdminUiApp {
} }
@Override @Override
public String getName() { public String getSlug() {
return "systeminformation"; return "systeminformation";
} }
@ -69,14 +69,17 @@ public class SystemInformationApp implements AdminUiApp {
@Override @Override
public String[] getJsFilesUrls() { public String[] getJsFilesUrls() {
return new String[]{ return new String[]{
"/@admin/systeminformation/js/app.js", "/_admin/systeminformation/js/chunk-vendors.js",
"/@admin/systeminformation/js/chunk-vendors.js",}; "/_admin/systeminformation/js/app.js"
};
} }
@Override @Override
public String[] getCssFilesUrls() { public String[] getCssFilesUrls() {
return new String[]{ return new String[]{
"/@admin/systeminformation/css/app.css",}; "/_admin/systeminformation/css/chunk-vendors.css",
"/_admin/systeminformation/css/app.css"
};
} }
@Override @Override

View File

@ -1,19 +1,39 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>Admin UI</title> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<!-- ToDo: Favicon link rel="icon" href="<%= BASE_URL %>favicon.ico"> -->
<#list activeApp.cssFilesUrls as cssFile>
<link href="${contextPath}${cssFile}" rel="preload" as="style">
</#list>
<#list activeApp.jsFilesUrls as jsFile>
<link href="${contextPath}${jsFile}" rel="preload" as="script">
</#list>
<#list activeApp.cssFilesUrls as cssFile>
<link rel="stylesheet" href="${contextPath}${cssFile}">
</#list>
<title>LibreCCM - ${activeApp.label}</title>
</head> </head>
<body> <body>
<div id="app"></div>
<#list activeApp.jsFilesUrls as jsFile>
<script src="${contextPath}${jsFile}"></script>
</#list>
<!--
<h1>Admin UI</h1> <h1>Admin UI</h1>
<p> <p>
Current user ${currentUser.name} Current user ${currentUser.name}
</p> </p>
<ul> <ul>
<#list adminUiApps as app> <#list adminUiApps as app>
<li> <li>
<dl> <dl>
<dt>Name</dt> <dt>Name</dt>
<dd>${app.name}</dd> <dd>${app.slug}</dd>
<dt>Label</dt> <dt>Label</dt>
<dd>${app.label}</dd> <dd>${app.label}</dd>
<dt>Description</dt> <dt>Description</dt>
@ -56,5 +76,6 @@
</li> </li>
</#list> </#list>
</ul> </ul>
-->
</body> </body>
</html> </html>