Admin UI for Applications

Jens Pelzetter 2020-11-26 21:42:35 +01:00
parent a0e9183596
commit 21633451bc
15 changed files with 397 additions and 23 deletions

View File

@ -24,4 +24,6 @@ package org.libreccm.ui.admin.applications;
*/
public interface ApplicationController {
String getApplication();
}

View File

@ -37,6 +37,8 @@ public class ApplicationTypeInfoItem implements
private long numberOfInstances;
private String controllerLink;
protected ApplicationTypeInfoItem() {
// Nothing
}
@ -81,6 +83,14 @@ public class ApplicationTypeInfoItem implements
this.numberOfInstances = numberOfInstances;
}
public String getControllerLink() {
return controllerLink;
}
protected void setControllerLink(final String controllerLink) {
this.controllerLink = controllerLink;
}
@Override
public int compareTo(final ApplicationTypeInfoItem other) {
if (other == null) {

View File

@ -32,12 +32,15 @@ import java.util.Map;
import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Instance;
import javax.enterprise.util.AnnotationLiteral;
import javax.inject.Inject;
import javax.mvc.Controller;
import javax.mvc.Models;
import javax.mvc.MvcContext;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
/**
*
@ -60,6 +63,13 @@ public class ApplicationsController {
@Inject
private Models models;
@Inject
private MvcContext mvc;
@Inject
@Any
private Instance<ApplicationController> applicationControllers;
@GET
@Path("/")
@AuthorizationRequired
@ -79,16 +89,6 @@ public class ApplicationsController {
return "org/libreccm/ui/admin/applications/applicationtypes.xhtml";
}
@GET
@Path("/{type}")
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public String getApplicationInstances(
@PathParam("type") final String type
) {
throw new UnsupportedOperationException();
}
private ApplicationTypeInfoItem buildTypeInfoItem(
final ApplicationType applicationType
) {
@ -104,7 +104,46 @@ public class ApplicationsController {
appRepository.findByType(applicationType.name()).size()
);
final IsApplicationControllerForLiteral literal
= new IsApplicationControllerForLiteral(applicationType.name());
final Instance<ApplicationController> instance = applicationControllers
.select(literal);
if (instance.isResolvable()) {
final ApplicationController controller = instance.get();
item.setControllerLink(
mvc.uri(
String.format(
"%s#getApplication",
controller.getClass().getSimpleName()
)
).toString()
);
}
return item;
}
private class IsApplicationControllerForLiteral
extends AnnotationLiteral<IsApplicationControllerFor>
implements IsApplicationControllerFor {
private static final long serialVersionUID = 1L;
private final String value;
public IsApplicationControllerForLiteral(
final String value
) {
this.value = value;
}
@Override
public String value() {
return value;
}
}
}

View File

@ -25,6 +25,9 @@ import java.util.HashSet;
import java.util.Set;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
/**
*
@ -33,17 +36,28 @@ import javax.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class ApplicationsPage implements AdminPage {
@Inject
@Any
private Instance<ApplicationController> applicationControllers;
@Override
public Set<Class<?>> getControllerClasses() {
final Set<Class<?>> classes = new HashSet<>();
classes.add(ApplicationsController.class);
applicationControllers
.stream()
.map(controller -> controller.getClass())
.forEach(classes::add);
return classes;
}
@Override
public String getUriIdentifier() {
return String.format(
"%s#getApplicationTypes", ApplicationsController.class.getSimpleName()
"%s#getApplicationTypes", ApplicationsController.class
.getSimpleName()
);
}

View File

@ -18,10 +18,31 @@
*/
package org.libreccm.ui.admin.applications;
import org.libreccm.web.CcmApplication;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class DefaultApplicationController implements ApplicationController {
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target(
{
ElementType.METHOD,
ElementType.FIELD,
ElementType.PARAMETER,
ElementType.TYPE
}
)
public @interface IsApplicationControllerFor {
String value();
}

View File

@ -23,9 +23,6 @@ import com.arsdigita.ui.admin.applications.AbstractAppSettingsPane;
import com.arsdigita.ui.admin.applications.DefaultApplicationInstanceForm;
import com.arsdigita.ui.admin.applications.DefaultApplicationSettingsPane;
import org.libreccm.ui.admin.applications.ApplicationController;
import org.libreccm.ui.admin.applications.DefaultApplicationController;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
@ -120,8 +117,6 @@ public @interface ApplicationType {
Class<? extends AbstractAppInstanceForm> instanceForm() default DefaultApplicationInstanceForm.class;
Class<? extends ApplicationController> adminController() default DefaultApplicationController.class;
Class<? extends AbstractAppSettingsPane> settingsPane() default DefaultApplicationSettingsPane.class;
}

View File

@ -23,13 +23,21 @@
<li class="list-group-item">
<div class="d-flex w-100 justify-content-between align-items-center">
<h2>
<a href="#{mvc.uri('ApplicationsController#getApplicationInstances', {'type': type.name})}">${type.title}</a>
<c:when test="#{type.controllerLink != null}">
<a href="#{type.controllerLink}">#{type.title}</a>
</c:when>
<c:otherwise>
#{type.title}
</c:otherwise>
</h2>
<small class="badge badge-info badge-pill">
<c:choose>
<c:when test="#{type.singleton}">
#{AdminMessages['applications.types.singleton']}
</c:when>
<c:when test="#{type.numberOfInstances == 1}">
#{AdminMessages.getMessage('applications.number_of_instances_one', [type.numberOfInstances])}
</c:when>
<c:otherwise>
#{AdminMessages.getMessage('applications.number_of_instances', [type.numberOfInstances])}
</c:otherwise>

View File

@ -492,3 +492,4 @@ sites.form.buttons.create=Create Site
sites.form.buttons.save=Save
applications.types.singleton=Singleton
applications.number_of_instances={0} instances
applications.number_of_instances_one={0} instance

View File

@ -492,3 +492,4 @@ sites.form.buttons.create=Site anlegen
sites.form.buttons.save=Speichern
applications.types.singleton=Singleton
applications.number_of_instances={0} Instanzen
applications.number_of_instances_one={0} instance

View File

@ -73,6 +73,21 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.mvc</groupId>
<artifactId>javax.mvc-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.krazo</groupId>
<artifactId>krazo-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.krazo.ext</groupId>
<artifactId>krazo-freemarker</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>

View File

@ -0,0 +1,141 @@
/*
* 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.applications.shortcuts;
import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.shortcuts.ShortcutsConstants;
import org.libreccm.ui.admin.AdminConstants;
import java.text.MessageFormat;
import java.util.AbstractMap;
import java.util.List;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Named("ShortcutAdminMessages")
public class ShortcutAdminMessages extends AbstractMap<String, String> {
/**
* Provides access to the locale negoiated by LibreCCM.
*/
@Inject
private GlobalizationHelper globalizationHelper;
/**
* The {@link ResourceBundle} to use.
*/
private ResourceBundle messages;
/**
* Loads the resource bundle.
*/
@PostConstruct
private void init() {
messages = ResourceBundle.getBundle(
ShortcutsConstants.SHORTCUTS_BUNDLE,
globalizationHelper.getNegotiatedLocale()
);
}
/**
* Retrieves a message from the resource bundle.
*
* @param key The key of the message.
*
* @return The translated message or {@code ???message???} if the the key is
* not found in the resource bundle (message is replaced with the
* key).
*/
public String getMessage(final String key) {
if (messages.containsKey(key)) {
return messages.getString(key);
} else {
return String.format("???%s???", key);
}
}
/**
* Retrieves a message with placeholders.
*
* @param key The key of the message.
* @param parameters The parameters for the placeholders.
*
* @return The translated message or {@code ???message???} if the the key is
* not found in the resource bundle (message is replaced with the
* key).
*/
public String getMessage(
final String key, final List<Object> parameters
) {
return getMessage(key, parameters.toArray());
}
/**
* The translated message or {@code ???message???} if the the key is not
* found in the resource bundle (message is replaced with the key).
*
* @param key The key of the message.
* @param parameters The parameters for the placeholders.
*
* @return The translated message or {@code ???message???} if the the key is
* not found in the resource bundle (message is replaced with the
* key).
*/
public String getMessage(
final String key, final Object[] parameters
) {
if (messages.containsKey(key)) {
return MessageFormat.format(messages.getString(key), parameters);
} else {
return String.format("???%s???", key);
}
}
@Override
public String get(final Object key) {
return get((String) key);
}
public String get(final String key) {
return getMessage(key);
}
@Override
public Set<Entry<String, String>> entrySet() {
return messages
.keySet()
.stream()
.collect(
Collectors.toMap(key -> key, key -> messages.getString(key))
)
.entrySet();
}
}

View File

@ -0,0 +1,58 @@
/*
* 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.applications.shortcuts;
import org.libreccm.shortcuts.ShortcutRepository;
import org.libreccm.shortcuts.ShortcutsConstants;
import org.libreccm.ui.admin.applications.ApplicationController;
import org.libreccm.ui.admin.applications.IsApplicationControllerFor;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.mvc.Controller;
import javax.mvc.Models;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Controller
//@IsApplicationControllerFor(ShortcutsConstants.SHORTCUTS_APP_TYPE)
@Path("/application")
public class ShortcutsApplicationController implements ApplicationController {
@Inject
private Models models;
@Inject
private ShortcutRepository shortcutRepository;
@GET
@Path("/")
@Override
public String getApplication() {
models.put("shortcuts", shortcutRepository.findAll());
return "org/libreccm/ui/admin/applications/shortcuts/shortcuts.xhtml";
}
}

View File

@ -0,0 +1,67 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/WEB-INF/views/org/libreccm/ui/admin/ccm-admin.xhtml">
<ui:param name="activePage" value="applications" />
<ui:param name="title" value="#{AdminMessages['applications.label']}" />
<ui:define name="breadcrumb">
<li class="breadcrumb-item">
#{AdminMessages['applications.label']}
</li>
<li class="breadcrumb-item active">
#{ShortcutAdminMessages['application_title']}
</li>
</ui:define>
<ui:define name="main">
<div class="container">
<h1>#{ShortcutAdminMessages['application_title']}</h1>
<div class="text-right mb-2">
<a class="btn btn-secondary"
href="#">
<bootstrap:svgIcon icon="plus-circle" />
<span>#{ShortcutAdminMessages['shortcuts.ui.admin.add_shortcut']}</span>
</a>
</div>
<table class="table table-hover">
<thead>
<tr>
<th>#{ShortcutAdminMessages['shortcuts.ui.admin.shortcuts_table.col_url_key.header']}</th>
<th>#{ShortcutAdminMessages['shortcuts.ui.admin.shortcuts_table.col_redirect.header']}</th>
<th class="text-center" colspan="2"><th>#{ShortcutAdminMessages['shortcuts.ui.admin.shortcuts_table.col_actions.header']}</th></th>
</tr>
</thead>
<tbody>
<c:forEach items="#{shortcuts}" var="shortcut">
<tr>
<td>#{shortcut.urlKey}</td>
<td>#{shortcut.redirect}</td>
<td>
<a class="btn btn-info"
href="#">
<bootstrap:svgIcon icon="pen"/>
#{ShortcutAdminMessages['shortcuts.ui.admin.shortcuts_table.edit']}
</a>
</td>
<td>
<a class="btn btn-danger"
href="#">
<bootstrap:svgIcon icon="x-circle"/>
#{ShortcutAdminMessages['shortcuts.ui.admin.shortcuts_table.delete']}
</a>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</ui:define>
</ui:composition>
</html>

View File

@ -19,6 +19,7 @@ application_title=Shortcuts
application_desc=Manage short URLs for internal and external redirects.
shortcuts.ui.admin.shortcuts_table.col_url_key.header=URL
shortcuts.ui.admin.shortcuts_table.col_redirect.header=Redirect
shortcuts.ui.admin.shortcuts_table.col_actions.header=Actions
shortcuts.ui.admin.shortcuts_table.col_edit.header=Edit
shortcuts.ui.admin.shortcuts_table.col_delete.header=Delete
shortcuts.ui.admin.shortcuts_table.edit=Edit

View File

@ -32,3 +32,4 @@ shortcuts.ui.admin.redirect.error.invalid=Das Ziel der Weiterleitung muss eine a
shortcuts.ui.admin.heading=Shortcuts verwalten
shortcuts.ui.admin.table.empty=Es wurden noch keine Shortcuts angelegt.
shortcuts.ui.admin.add_shortcut=Shortcut hinzuf\u00fcgen
shortcuts.ui.admin.shortcuts_table.col_actions.header=Aktionen