RESTful API for PageModels

Former-commit-id: e7e5afab5c
restapi
Jens Pelzetter 2020-06-28 12:50:43 +02:00
parent f7721dd41b
commit 4cc48686d5
13 changed files with 1358 additions and 3200 deletions

View File

@ -0,0 +1,531 @@
/*
* Copyright (C) 2018 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.api.pagemodels;
import org.libreccm.core.CoreConstants;
import org.libreccm.pagemodel.ComponentModel;
import org.libreccm.pagemodel.ComponentModelJsonConverter;
import org.libreccm.pagemodel.ComponentModelRepository;
import org.libreccm.pagemodel.ContainerModel;
import org.libreccm.pagemodel.ContainerModelManager;
import org.libreccm.pagemodel.ConvertsComponentModel;
import org.libreccm.pagemodel.PageModel;
import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.RequiresPrivilege;
import org.libreccm.web.CcmApplication;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.util.Iterator;
import java.util.Objects;
import java.util.Optional;
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.json.Json;
import javax.json.JsonArray;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObject;
import javax.transaction.Transactional;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
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;
/**
* Provides RESTful endpoints for retrieving, creating, updating and deleting
* {@link ComponentModel}s of a {@link ContainerModel}.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Path("/{appName}/{pageModelName}/containers/{containerKey}/components")
public class Components {
@Inject
private ComponentModelRepository componentRepo;
@Inject
private ContainerModelManager containerManager;
@Inject
private PageModelsController controller;
@Inject
@Any
private Instance<ComponentModelJsonConverter> jsonConverters;
/**
* Retrieve all {@link ComponentModel} of a {@link ContainerModel}.
*
* @param Name The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
*
* @return A JSON array containing the data of all {@link ComponentModel} of
* the {@link ContainerModel}.
*/
@GET
@Path("/")
@Produces(MediaType.APPLICATION_JSON)
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonArray getComponents(
@PathParam("appName") final String Name,
@PathParam("pageModelName") final String pageModelName,
@PathParam("containerKey") final String containerKey
) {
Objects.requireNonNull(Name);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", Name)
);
final PageModel pageModel = controller.findPageModel(
app, pageModelName
);
final ContainerModel container = controller.findContainer(
app, pageModel, containerKey
);
final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
container
.getComponents()
.stream()
.map(component -> mapComponentModelToJson(component))
.forEach(arrayBuilder::add);
return arrayBuilder.build();
}
/**
* Retrieves a specific {@link ComponentModel}.
*
* @param appName The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
* @param componentKey The key of the {@link ComponentModel}.
*
* @return A JSON object containing the data of the {@link ComponentModel}.
*/
@GET
@Path("/{componentKey}")
@Produces(MediaType.APPLICATION_JSON)
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject getComponent(
@PathParam("appName") final String appName,
@PathParam("pageModelName") final String pageModelName,
@PathParam("containerKey") final String containerKey,
@PathParam("componentKey") final String componentKey
) {
Objects.requireNonNull(appName);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(componentKey);
final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appName)
);
final PageModel pageModel = controller.findPageModel(
app, pageModelName
);
final ContainerModel container = controller.findContainer(
app, pageModel, containerKey
);
final ComponentModel component = controller.findComponentModel(
app, pageModel, container, componentKey
);
return mapComponentModelToJson(component);
}
/**
* Creates or updates a {@link ComponentModel}.If a {@link ComponentModel}
* with provided {@code componentKey} already exists in the container
* identified by {@code appName}, {@code pageModelName} and
* {@code containerKey} the {@link ComponentModel} is updated with the data
* from {@code componentModelData}.
*
* Otherwise a new {@link ComponentModel} is created using the data from
* {@code componentModelData}.
*
* @param appName The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
* @param componentKey The key of the {@link ComponentModel} to create
* or update.
* @param componentModelData The data for creating or updating the
* {@link ComponentModel}.
*
* @return A HTTP response with the status code {@code 200} if the component
* model was updated or {@code 201} if the component model was
* created. If the component model was created the response also
* contains the URI of the new component model.
*
*
*/
@PUT
@Path("/{componentKey}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public Response putComponent(
@PathParam("appName") final String appName,
@PathParam("pageModelName") final String pageModelName,
@PathParam("containerKey") final String containerKey,
@PathParam("componentKey") final String componentKey,
final JsonObject componentModelData
) {
Objects.requireNonNull(appName);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(componentKey);
Objects.requireNonNull(componentModelData);
final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appName)
);
final PageModel pageModel = controller.findPageModel(
app, pageModelName
);
final ContainerModel container = controller.findContainer(
app, pageModel, containerKey
);
final Optional<ComponentModel> result = container
.getComponents()
.stream()
.filter(c -> c.getKey().equals(componentKey))
.findAny();
final ComponentModel componentModel;
final boolean created;
if (result.isPresent()) {
componentModel = result.get();
created = false;
} else {
componentModel = createComponentModel(componentModelData);
componentModel.setKey(componentKey);
containerManager.addComponentModel(container, componentModel);
created = true;
}
setComponentPropertiesFromJson(componentModelData, componentModel);
componentRepo.save(componentModel);
if (created) {
return Response.created(
URI.create(
String.format(
"/page-models/%s/%s/%s/%s",
appName,
pageModelName,
containerKey,
componentKey
)
)
).build();
} else {
return Response.ok(
String.format(
"Component %s of container %s of page model %s of "
+ "application %s updated successfully.",
componentKey,
containerKey,
pageModelName,
appName
)
).build();
}
}
/**
* Deletes a {@link ComponentModel}.
*
* @param appName The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
* @param componentKey The key of the {@link ComponentModel} to delete.
*
* @return HTTP response for successful delete.
*
*/
@DELETE
@Path("/{componentKey}")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public Response deleteComponent(
@PathParam("appName") final String appName,
@PathParam("pageModelName") final String pageModelName,
@PathParam("containerKey") final String containerKey,
@PathParam("componentKey") final String componentKey
) {
Objects.requireNonNull(appName);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(componentKey);
final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appName)
);
final PageModel pageModel = controller.findPageModel(
app, pageModelName
);
final ContainerModel container = controller.findContainer(
app, pageModel, containerKey
);
final ComponentModel component = controller.findComponentModel(
app, pageModel, container, componentKey
);
containerManager.removeComponentModel(container, component);
return Response.ok(
String.format(
"Component %s successfully deleted from container %s "
+ "of page model %s of application %s.",
componentKey,
containerKey,
pageModelName,
appName
)
).build();
}
/**
* Helper method for mapping a {@link ComponentModel} to JSON.
*
* @param componentModel The {@link ComponentModel} to map.
*
* @return The JSON representation of the
* {@link ComponentModel} {@code componentModel}.
*/
private JsonObject mapComponentModelToJson(
final ComponentModel componentModel) {
final Class<? extends ComponentModel> clazz = Objects
.requireNonNull(componentModel)
.getClass();
final ComponentModelJsonConverter jsonConverter
= findJsonConverter(clazz)
.orElseThrow(
() -> new WebApplicationException(
String.format(
"No JSON converter available for component "
+ "model \"%s\".",
clazz.getName()
),
Response.Status.INTERNAL_SERVER_ERROR
)
);
return jsonConverter.toJson(componentModel);
}
/**
* Creates a new {@link ComponentModel} instance.
*
* Uses reflection and the value of {@code type} property from the JSON
* {@code data} to determine the correct class.
*
* @param data The data from which the new {@link ComponentModel} is
* created.
*
* @return The new {@link ComponentModel}.
*/
private ComponentModel createComponentModel(final JsonObject data) {
Objects.requireNonNull(data);
if (!data.containsKey("type")) {
throw new WebApplicationException(
"The JSON data for creating the "
+ "component has no value for the type of the component to "
+ "create.",
Response.Status.BAD_REQUEST
);
}
final String type = data.getString("type");
final Class<? extends ComponentModel> clazz = findComponentModelClass(
type
);
final ComponentModel componentModel;
try {
componentModel = clazz.getConstructor().newInstance();
} catch (IllegalAccessException
| InstantiationException
| InvocationTargetException
| NoSuchMethodException ex) {
throw new WebApplicationException(ex);
}
return componentModel;
}
/**
* Helper method for finding the correct subclass of {@link ComponentModel}
* using the fully qualified name the class.
*
* @param type The fully qualified name of the subclass of
* {@link ComponentModel}.
*
* @return The subclass of {@link ComponentModel}.
*
* @throws BadRequestException If there is no subclass of
* {@link ComponentModel} with the fully
* qualified name provided by the {@code type}
* parameter.
*/
@SuppressWarnings("unchecked")
private Class<? extends ComponentModel> findComponentModelClass(
final String type
) {
Objects.requireNonNull(type);
try {
final Class<?> clazz = Class.forName(type);
if (ComponentModel.class.isAssignableFrom(clazz)) {
return (Class<? extends ComponentModel>) clazz;
} else {
throw new WebApplicationException(
String.format(
"The type \"%s\" is not a subclass of \"%s\".",
type,
ComponentModel.class.getName()
),
Response.Status.BAD_REQUEST
);
}
} catch (ClassNotFoundException ex) {
throw new WebApplicationException(
String.format(
"The component model type \"%s\" "
+ "does not exist.",
type
),
Response.Status.BAD_REQUEST
);
}
}
/**
* Helper method for setting the properties of a {@link ComponentModel} from
* the JSON data.
*
* @param data The JSON data.
* @param componentModel The {@link ComponentModel}.
*/
private void setComponentPropertiesFromJson(
final JsonObject data,
final ComponentModel componentModel
) {
final Class<? extends ComponentModel> clazz = Objects
.requireNonNull(componentModel)
.getClass();
final ComponentModelJsonConverter jsonConverter
= findJsonConverter(clazz)
.orElseThrow(
() -> new WebApplicationException(
String.format(
"No JSON converter available for component "
+ "model \"%s\".",
clazz.getName()
),
Response.Status.INTERNAL_SERVER_ERROR
)
);
jsonConverter.fromJson(data, componentModel);
}
private Optional<ComponentModelJsonConverter>
findJsonConverter(
final Class<? extends ComponentModel> componentModelClass
) {
final ConvertsComponentModelLiteral literal
= new ConvertsComponentModelLiteral(
componentModelClass
);
final Instance<ComponentModelJsonConverter> instance = jsonConverters
.select(literal);
if (instance.isUnsatisfied()) {
return Optional.empty();
} else if (instance.isAmbiguous()) {
throw new WebApplicationException(
String.format(
"Multiple JSONConverter for \"%s\".",
componentModelClass.getName()
),
Response.Status.INTERNAL_SERVER_ERROR
);
} else {
final Iterator<ComponentModelJsonConverter> iterator = instance
.iterator();
@SuppressWarnings("unchecked")
final ComponentModelJsonConverter converter = iterator.next();
return Optional.of(converter);
}
}
private static class ConvertsComponentModelLiteral
extends AnnotationLiteral<ConvertsComponentModel>
implements ConvertsComponentModel {
private static final long serialVersionUID = 1L;
private final Class<? extends ComponentModel> componentModel;
public ConvertsComponentModelLiteral(
final Class<? extends ComponentModel> componentModel
) {
this.componentModel = componentModel;
}
@Override
public Class<? extends ComponentModel> componentModel() {
return componentModel;
}
}
}

View File

@ -16,7 +16,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA * MA 02110-1301 USA
*/ */
package org.libreccm.pagemodel.rs; package org.libreccm.api.pagemodels;
import org.libreccm.core.CoreConstants; import org.libreccm.core.CoreConstants;
import org.libreccm.pagemodel.ContainerModel; import org.libreccm.pagemodel.ContainerModel;
@ -27,6 +27,7 @@ import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.RequiresPrivilege; import org.libreccm.security.RequiresPrivilege;
import org.libreccm.web.CcmApplication; import org.libreccm.web.CcmApplication;
import java.net.URI;
import java.util.Optional; import java.util.Optional;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
@ -43,6 +44,8 @@ import javax.ws.rs.PUT;
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.core.MediaType;
import javax.ws.rs.core.Response;
/** /**
* Provides RESTful endpoints for managing the {@link ContainerModel}s of a * Provides RESTful endpoints for managing the {@link ContainerModel}s of a
@ -51,7 +54,7 @@ import javax.ws.rs.Produces;
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@RequestScoped @RequestScoped
@Path("/") @Path("/{appName}/{pageModelName}/containers")
public class Containers { public class Containers {
@Inject @Inject
@ -66,7 +69,7 @@ public class Containers {
/** /**
* Retrieves all {@link ContainerModel}s of a {@link PageModel}. * Retrieves all {@link ContainerModel}s of a {@link PageModel}.
* *
* @param appPath The primary URL of the {@link CcmApplication} to * @param appName The primary URL of the {@link CcmApplication} to
* which the {@link PageModel} belongs. * which the {@link PageModel} belongs.
* @param pageModelName The name of the {@link PageModel} of which the * @param pageModelName The name of the {@link PageModel} of which the
* containers are retrieved. * containers are retrieved.
@ -76,20 +79,22 @@ public class Containers {
* the {@link CcmApplication} with the primary URL {@code appPath}. * the {@link CcmApplication} with the primary URL {@code appPath}.
*/ */
@GET @GET
@Path(PageModelsApp.CONTAINERS_PATH) @Path("/")
@Produces("application/json; charset=utf-8") @Produces(MediaType.APPLICATION_JSON)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonArray getContainers( public JsonArray getContainers(
@PathParam(PageModelsApp.APP_NAME) final String appPath, @PathParam("appName") final String appName,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName) { @PathParam("pageModelName") final String pageModelName
) {
final CcmApplication app = controller.findCcmApplication( final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appPath)); String.format("/%s/", appName)
);
final PageModel pageModel = controller.findPageModel(app, final PageModel pageModel = controller.findPageModel(
pageModelName); app, pageModelName
);
final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder(); final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
pageModel pageModel
@ -104,7 +109,7 @@ public class Containers {
/** /**
* Retrieve a specific {@link ContainerModel}. * Retrieve a specific {@link ContainerModel}.
* *
* @param appPath The primary URL of the {@link CcmApplication} to * @param appName The primary URL of the {@link CcmApplication} to
* which the {@link PageModel} belongs. * which the {@link PageModel} belongs.
* @param pageModelName The name of the {@link PageModel} to which the * @param pageModelName The name of the {@link PageModel} to which the
* {@link ContainerModel} belongs. * {@link ContainerModel} belongs.
@ -114,25 +119,27 @@ public class Containers {
* @return A JSON object containing the data of the {@link PageModel}. * @return A JSON object containing the data of the {@link PageModel}.
*/ */
@GET @GET
@Path(PageModelsApp.CONTAINER_PATH) @Path("/{containerKey}")
@Produces("application/json; charset=utf-8") @Produces(MediaType.APPLICATION_JSON)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject getContainer( public JsonObject getContainer(
@PathParam(PageModelsApp.APP_NAME) final String appPath, @PathParam("appName") final String appName,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName, @PathParam("pageModelName") final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey) { @PathParam("containerKey") final String containerKey
) {
final CcmApplication app = controller.findCcmApplication( final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appPath)); String.format("/%s/", appName)
);
final PageModel pageModel = controller.findPageModel(app, final PageModel pageModel = controller.findPageModel(
pageModelName); app, pageModelName
);
final ContainerModel container = controller.findContainer(app, final ContainerModel container = controller.findContainer(
pageModel, app, pageModel, containerKey
containerKey); );
return mapContainerModelToJson(container); return mapContainerModelToJson(container);
} }
@ -147,32 +154,36 @@ public class Containers {
* If there is no such {@link ContainerModel} a new {@link ContainerModel} * If there is no such {@link ContainerModel} a new {@link ContainerModel}
* is created using the data provided by {@code containerModelData}. * is created using the data provided by {@code containerModelData}.
* *
* @param appPath The path of the {@link CcmApplication}. * @param appName The path of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}. * @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key identifying the {@link ContainerModel}. * @param containerKey The key identifying the {@link ContainerModel}.
* @param containerModelData The data for updating or creating the * @param containerModelData The data for updating or creating the
* {@link ContainerModel}. * {@link ContainerModel}.
* *
* @return The new or updated {@link ContainerModel}. * @return A HTTP response with the status code {@code 200} if the container
* model was updated or {@code 201} if the container model was
* created. If the container model was created the response also
* contains the URI of the new container model.
*/ */
@PUT @PUT
@Path(PageModelsApp.CONTAINER_PATH) @Path("/{containerKey}")
@Consumes("application/json; charset=utf-8") @Consumes(MediaType.APPLICATION_JSON)
@Produces("application/json; charset=utf-8") @Produces(MediaType.APPLICATION_JSON)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject putContainer( public Response putContainer(
@PathParam(PageModelsApp.APP_NAME) final String appPath, @PathParam("appName") final String appName,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName, @PathParam("pageModelName") final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey, @PathParam("containerKey") final String containerKey,
final JsonObject containerModelData) { final JsonObject containerModelData
) {
final CcmApplication app = controller.findCcmApplication( final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appPath)); String.format("/%s/", appName)
);
final PageModel pageModel = controller.findPageModel(app, final PageModel pageModel = controller.findPageModel(
pageModelName); app, pageModelName
);
final Optional<ContainerModel> result = pageModel final Optional<ContainerModel> result = pageModel
.getContainers() .getContainers()
@ -181,52 +192,90 @@ public class Containers {
.findAny(); .findAny();
final ContainerModel containerModel; final ContainerModel containerModel;
final boolean created;
if (result.isPresent()) { if (result.isPresent()) {
containerModel = result.get(); containerModel = result.get();
result.get().setKey(containerKey); containerModel.setKey(containerKey);
containerModelRepo.save(result.get()); containerModelRepo.save(containerModel);
created = false;
} else { } else {
containerModel = new ContainerModel(); containerModel = new ContainerModel();
containerModel.setKey(containerKey); containerModel.setKey(containerKey);
pageModelManager.addContainerModel(pageModel, containerModel); pageModelManager.addContainerModel(pageModel, containerModel);
created = true;
} }
return mapContainerModelToJson(containerModel); if (created) {
return Response.created(
URI.create(
String.format(
"/page-models/%s/%s/containers/%s",
appName,
pageModelName,
containerKey
)
)
).build();
} else {
return Response.ok(
String.format(
"Container model %s of page model %s of application %s "
+ "updated successfully.",
containerKey,
pageModelName,
appName
)
).build();
}
} }
/** /**
* Deletes the {@link ContainerModel} identified by the provided parameters. * Deletes the {@link ContainerModel} identified by the provided parameters.
* *
* @param appPath The path of the {@link CcmApplication}. * @param appName The path of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}. * @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key identifying the {@link ContainerModel} to * @param containerKey The key identifying the {@link ContainerModel} to
* delete. * delete.
*
* @return A HTTP response with the status code {@code 200} if the container
* model was deleted successfully.
*/ */
@DELETE @DELETE
@Path(PageModelsApp.CONTAINER_PATH) @Path("/{containerKey}")
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public void deleteContainer( public Response deleteContainer(
@PathParam(PageModelsApp.APP_NAME) final String appPath, @PathParam("appName") final String appName,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName, @PathParam("pageModelName") final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey) { @PathParam("containerKey") final String containerKey
) {
final CcmApplication app = controller.findCcmApplication( final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appPath)); String.format("/%s/", appName)
);
final PageModel pageModel = controller.findPageModel(app, final PageModel pageModel = controller.findPageModel(
pageModelName); app, pageModelName
);
final ContainerModel container = controller.findContainer(app, final ContainerModel container = controller.findContainer(
pageModel, app, pageModel, containerKey
containerKey); );
pageModelManager.removeContainerModel(pageModel, container); pageModelManager.removeContainerModel(pageModel, container);
return Response.ok(
String.format(
"Container model %s of page model %s of application %s deleted"
+ "successfully.",
containerKey,
pageModelName,
appName
)
).build();
} }
/** /**

View File

@ -16,14 +16,13 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA * MA 02110-1301 USA
*/ */
package org.libreccm.pagemodel.rs; package org.libreccm.api.pagemodels;
import com.arsdigita.kernel.KernelConfig; import com.arsdigita.kernel.KernelConfig;
import org.libreccm.configuration.ConfigurationManager; import org.libreccm.configuration.ConfigurationManager;
import org.libreccm.core.CoreConstants; import org.libreccm.core.CoreConstants;
import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.pagemodel.ComponentModel;
import org.libreccm.pagemodel.ContainerModel; import org.libreccm.pagemodel.ContainerModel;
import org.libreccm.pagemodel.PageModel; import org.libreccm.pagemodel.PageModel;
import org.libreccm.pagemodel.PageModelManager; import org.libreccm.pagemodel.PageModelManager;
@ -32,6 +31,8 @@ import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.RequiresPrivilege; import org.libreccm.security.RequiresPrivilege;
import org.libreccm.web.CcmApplication; import org.libreccm.web.CcmApplication;
import java.net.URI;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.inject.Inject; import javax.inject.Inject;
import javax.json.Json; import javax.json.Json;
@ -50,11 +51,11 @@ 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 java.util.Date;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/** /**
* Provides RESTful endpoints for retrieving, creating, updating and deleting * Provides RESTful endpoints for retrieving, creating, updating and deleting
@ -63,7 +64,7 @@ import java.util.stream.Collectors;
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@RequestScoped @RequestScoped
@Path("/") @Path("/{appName}")
public class PageModels { public class PageModels {
@Inject @Inject
@ -85,29 +86,29 @@ public class PageModels {
* Retrieves all {@link PageModel}s available for an {@link * Retrieves all {@link PageModel}s available for an {@link
* CcmApplication}. * CcmApplication}.
* *
* @param appPath The path of the {@code app}. * @param appName The path of the {@code app}.
* *
* @return A JSON array with the data of all {@link PageModel}s of the * @return A JSON array with the data of all {@link PageModel}s of the
* {@link CcmApplication} {@code app}. * {@link CcmApplication} {@code app}.
* *
* @throws NotFoundException If there is no {@link CcmApplication} with the * @throws NotFoundException If there is no {@link CcmApplication} with the
* primary URL {@code appPath} an {@link * primary URL {@code appPath} an {@link
* NotFoundException} thrown resulting in 404 * NotFoundException} thrown resulting in 404 response.
* response.
*/ */
@GET @GET
@Path(PageModelsApp.PAGE_MODELS_PATH) @Path("/")
@Produces("application/json; charset=utf-8") @Produces(MediaType.APPLICATION_JSON)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonArray getAllPageModels( public JsonArray getAllPageModels(
@PathParam(PageModelsApp.APP_NAME) String appPath) { @PathParam("appName") String appName
) {
Objects.requireNonNull(appName);
Objects.requireNonNull(appPath); final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appName)
final CcmApplication app = controller );
.findCcmApplication(String.format("/%s/", appPath));
final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder(); final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
pageModelRepo pageModelRepo
.findDraftByApplication(app) .findDraftByApplication(app)
@ -121,65 +122,73 @@ public class PageModels {
/** /**
* Retrieves a specific {@link PageModel}. * Retrieves a specific {@link PageModel}.
* *
* @param appPath The path ({@link CcmApplication#primaryUrl} of the * @param appName The path ({@link CcmApplication#primaryUrl} of the
* {@link CcmApplication} to which the {@link * {@link CcmApplication} to which the {@link
* PageModel} belongs (see * PageModel} belongs (see {@link PageModel#application}).
* {@link PageModel#application}).
* @param pageModelName The name of the {@link PageModel} to retrieve (see * @param pageModelName The name of the {@link PageModel} to retrieve (see
* {@link PageModel#name}). * {@link PageModel#name}).
* *
* @return A JSON object containing the data of the {@link PageModel}. * @return A JSON object containing the data of the {@link PageModel}.
* *
* @throws NotFoundException If there is not {@link CcmApplication} with the * @throws NotFoundException If there is not {@link CcmApplication} with the
* primary URL {@code appPath} a {@link * primary URL {@code appPath} a
* NotFoundException} is thrown resulting in a 404 * {@link NotFoundException} is thrown resulting
* response. A {@link NotFoundException} is also * in a 404 response. A {@link NotFoundException}
* thrown if there no {@link PageModel} identified * is also thrown if there no {@link PageModel}
* by {@code pageModelName} for the {@link * identified by {@code pageModelName} for the
* CcmApplication} with the primary URL {@code * {@link CcmApplication} with the primary URL
* appPath}. * {@code appPath}.
*/ */
@GET @GET
@Path(PageModelsApp.PAGE_MODEL_PATH) @Path("/{pageModelName}")
@Produces("application/json; charset=utf-8") @Produces(MediaType.APPLICATION_JSON)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject getPageModel( public JsonObject getPageModel(
@PathParam(PageModelsApp.APP_NAME) final String appPath, @PathParam("appName") final String appName,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName) { @PathParam("pageModelName") final String pageModelName
) {
Objects.requireNonNull(appPath); Objects.requireNonNull(appName);
Objects.requireNonNull(pageModelName); Objects.requireNonNull(pageModelName);
final CcmApplication app = controller final CcmApplication app = controller.findCcmApplication(
.findCcmApplication(String.format("/%s/", appPath)); String.format("/%s/", appName)
final PageModel pageModel = controller.findPageModel(app, );
pageModelName); final PageModel pageModel = controller.findPageModel(
app, pageModelName
);
return mapPageModelToJson(pageModel); return mapPageModelToJson(pageModel);
} }
@POST @POST
@Path(PageModelsApp.PAGE_MODEL_PATH) @Path("/{pageModelName}")
@Produces("application/json; charset=utf-8") @Produces(MediaType.APPLICATION_JSON)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject publishPageModel( public Response publishPageModel(
@PathParam(PageModelsApp.APP_NAME) final String appPath, @PathParam("appName") final String appName,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName, @PathParam("pageModelName") final String pageModelName,
@FormParam("action") String action) { @FormParam("action") String action
) {
if ("publish".equals(action)) { if ("publish".equals(action)) {
final CcmApplication app = controller.findCcmApplication(
final CcmApplication app = controller String.format("/%s/", appName)
.findCcmApplication(String.format("/%s/", appPath)); );
final PageModel pageModel = controller.findPageModel(app, final PageModel pageModel = controller.findPageModel(
pageModelName); app, pageModelName
);
pageModelManager.publish(pageModel); pageModelManager.publish(pageModel);
} }
return getPageModel(appPath, pageModelName); return Response.ok(
String.format(
"Page model %s of application %s successfully published.",
pageModelName,
appName
)
).build();
} }
/** /**
@ -191,82 +200,123 @@ public class PageModels {
* {@link PageModel} is created and associated with the {@link * {@link PageModel} is created and associated with the {@link
* CcmApplication} identified by the primary URL {@code appPath}. * CcmApplication} identified by the primary URL {@code appPath}.
* *
* @param appPath The primary URL of the {@link CcmApplication} to * @param appName The primary URL of the {@link CcmApplication} to
* which the {@link PageModel} belongs. * which the {@link PageModel} belongs.
* @param pageModelName The name of the {@link PageModel}. * @param pageModelName The name of the {@link PageModel}.
* @param pageModelData The data for creating or updating the {@link * @param pageModelData The data for creating or updating the {@link
* PageModel}. * PageModel}.
* *
* @return The new or updated {@link PageModel}. * @return A HTTP response with status code {@code 200} if the page model
* was updated successfully or with status code {@code 201} if the
* page model was successfully created. If the page model was
* created the response also contains the URI of the new page model.
*/ */
@PUT @PUT
@Path(PageModelsApp.PAGE_MODEL_PATH) @Path("/{pageModelName}")
@Consumes("application/json; charset=utf-8") @Consumes(MediaType.APPLICATION_JSON)
@Produces("application/json; charset=utf-8") @Produces(MediaType.APPLICATION_JSON)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject putPageModel( public Response putPageModel(
@PathParam(PageModelsApp.APP_NAME) final String appPath, @PathParam("appName") final String appName,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName, @PathParam("pageModelName") final String pageModelName,
final JsonObject pageModelData) { final JsonObject pageModelData
) {
Objects.requireNonNull(appPath); Objects.requireNonNull(appName);
Objects.requireNonNull(pageModelName); Objects.requireNonNull(pageModelName);
final CcmApplication app = controller final CcmApplication app = controller.findCcmApplication(
.findCcmApplication(String.format("/%s/", appPath)); String.format("/%s/", appName)
);
final KernelConfig kernelConfig = confManager final KernelConfig kernelConfig = confManager
.findConfiguration(KernelConfig.class); .findConfiguration(KernelConfig.class);
final PageModel pageModel; final PageModel pageModel;
final boolean created;
if (controller.existsPageModel(app, pageModelName)) { if (controller.existsPageModel(app, pageModelName)) {
pageModel = controller.findPageModel(app, pageModelName); pageModel = controller.findPageModel(app, pageModelName);
created = false;
} else { } else {
pageModel = pageModelManager.createPageModel(pageModelName, app); pageModel = pageModelManager.createPageModel(pageModelName, app);
created = true;
} }
if (pageModelData.containsKey("title")) { if (pageModelData.containsKey("title")) {
pageModel.getTitle().addValue(kernelConfig.getDefaultLocale(), pageModel.getTitle().addValue(
pageModelData.getString("title")); kernelConfig.getDefaultLocale(),
pageModelData.getString("title")
);
} }
if (pageModelData.containsKey("description")) { if (pageModelData.containsKey("description")) {
pageModel pageModel
.getDescription() .getDescription()
.addValue(kernelConfig.getDefaultLocale(), .addValue(
pageModelData.getString("description")); kernelConfig.getDefaultLocale(),
pageModelData.getString("description")
);
} }
controller.savePageModel(pageModel); controller.savePageModel(pageModel);
return mapPageModelToJson(pageModel); if (created) {
return Response.created(
URI.create(
String.format(
"/page-models/%s/%s",
appName,
pageModelName
)
)
).build();
} else {
return Response.ok(
String.format(
"Page model %s of application %s updated successfully",
pageModelName,
appName
)
).build();
}
} }
/** /**
* Deletes a {@link PageModel}. * Deletes a {@link PageModel}.
* *
* @param appPath The primary URL of the {@link CcmApplication} to * @param appName The primary URL of the {@link CcmApplication} to
* which the {@link PageModel} belongs. * which the {@link PageModel} belongs.
* @param pageModelName The name of the {@link PageModel} to delete. * @param pageModelName The name of the {@link PageModel} to delete.
*
* @return A HTTP response with status code {@code 200} if the page model
* was deleted sucessfully.
*/ */
@DELETE @DELETE
@Path(PageModelsApp.PAGE_MODEL_PATH) @Path("/{appName}/{pageModelName}")
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public void deletePageModel( public Response deletePageModel(
@PathParam(PageModelsApp.APP_NAME) final String appPath, @PathParam("appName") final String appName,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName) { @PathParam("pageModelName") final String pageModelName
) {
Objects.requireNonNull(appPath); Objects.requireNonNull(appName);
Objects.requireNonNull(pageModelName); Objects.requireNonNull(pageModelName);
final CcmApplication app = controller final CcmApplication app = controller.findCcmApplication(
.findCcmApplication(String.format("/%s/", appPath)); String.format("/%s/", appName)
final PageModel pageModel = controller.findPageModel(app, );
pageModelName); final PageModel pageModel = controller.findPageModel(
app, pageModelName
);
pageModelRepo.delete(pageModel); pageModelRepo.delete(pageModel);
return Response.ok(
String.format(
"Page model %s of application %s deleted successfully.",
pageModelName,
appName
)
).build();
} }
/** /**
@ -282,10 +332,11 @@ public class PageModels {
Objects.requireNonNull(pageModel); Objects.requireNonNull(pageModel);
final long lastPublished; final long lastPublished;
final Optional<PageModel> liveModel = pageModelManager final Optional<PageModel> liveModel = pageModelManager.getLiveVersion(
.getLiveVersion(pageModel); pageModel
);
if (liveModel.isPresent() if (liveModel.isPresent()
&& liveModel.get().getLastModified() != null) { && liveModel.get().getLastModified() != null) {
lastPublished = liveModel.get().getLastModified().getTime(); lastPublished = liveModel.get().getLastModified().getTime();
} else { } else {
lastPublished = 0; lastPublished = 0;
@ -300,27 +351,33 @@ public class PageModels {
return Json return Json
.createObjectBuilder() .createObjectBuilder()
.add("containers", mapContainersToJson(pageModel)) .add("containers", mapContainersToJson(pageModel))
.add("description", .add(
globalizationHelper "description",
.getValueFromLocalizedString(pageModel.getDescription())) globalizationHelper.getValueFromLocalizedString(
pageModel.getDescription()
)
)
.add("modelUuid", pageModel.getModelUuid()) .add("modelUuid", pageModel.getModelUuid())
.add("name", pageModel.getName()) .add("name", pageModel.getName())
.add("pageModelId", Long.toString(pageModel.getPageModelId())) .add("pageModelId", Long.toString(pageModel.getPageModelId()))
.add("title", .add(
globalizationHelper "title",
.getValueFromLocalizedString(pageModel.getTitle())) globalizationHelper.getValueFromLocalizedString(
pageModel.getTitle()
)
)
.add("type", pageModel.getType()) .add("type", pageModel.getType())
.add("uuid", pageModel.getUuid()) .add("uuid", pageModel.getUuid())
.add("version", pageModel.getVersion().toString()) .add("version", pageModel.getVersion().toString())
.add("publicationStatus", .add("publicationStatus",
getPublicationStatus(pageModel).toString()) getPublicationStatus(pageModel).toString()
)
.add("lastModified", lastModified) .add("lastModified", lastModified)
.add("lastPublished", lastPublished) .add("lastPublished", lastPublished)
.build(); .build();
} }
private JsonArray mapContainersToJson(final PageModel pageModel) { private JsonArray mapContainersToJson(final PageModel pageModel) {
final JsonArrayBuilder containers = Json.createArrayBuilder(); final JsonArrayBuilder containers = Json.createArrayBuilder();
pageModel pageModel
@ -333,7 +390,6 @@ public class PageModels {
} }
private JsonObject mapContainerToJson(final ContainerModel container) { private JsonObject mapContainerToJson(final ContainerModel container) {
return Json return Json
.createObjectBuilder() .createObjectBuilder()
.add("containerUuid", container.getContainerUuid()) .add("containerUuid", container.getContainerUuid())
@ -350,7 +406,6 @@ public class PageModels {
* @return * @return
*/ */
private PublicationStatus getPublicationStatus(final PageModel pageModel) { private PublicationStatus getPublicationStatus(final PageModel pageModel) {
final PageModel draftModel = pageModelManager final PageModel draftModel = pageModelManager
.getDraftVersion(pageModel); .getDraftVersion(pageModel);
final Optional<PageModel> liveModel = pageModelManager final Optional<PageModel> liveModel = pageModelManager
@ -361,7 +416,7 @@ public class PageModels {
// Fallback if one the last modified dates is null // Fallback if one the last modified dates is null
if (draftModel.getLastModified() == null if (draftModel.getLastModified() == null
|| liveModel.get().getLastModified() == null) { || liveModel.get().getLastModified() == null) {
return PublicationStatus.NEEDS_UPDATE; return PublicationStatus.NEEDS_UPDATE;
} else if (draftModel } else if (draftModel

View File

@ -16,7 +16,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA * MA 02110-1301 USA
*/ */
package org.libreccm.pagemodel.rs; package org.libreccm.api.pagemodels;
import org.libreccm.pagemodel.PageModel; import org.libreccm.pagemodel.PageModel;
@ -42,34 +42,9 @@ import javax.ws.rs.core.Application;
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@ApplicationPath("/page-models") @ApplicationPath("/api/page-models")
public class PageModelsApp extends Application { public class PageModelsApp extends Application {
protected static final String APP_NAME = "appName";
protected static final String PAGE_MODEL_NAME = "pageModelName";
protected static final String CONTAINER_KEY = "containerKey";
protected static final String COMPONENT_KEY = "componentKey";
protected static final String PAGE_MODELS_PATH = "/{" + APP_NAME + "}";
protected static final String PAGE_MODEL_PATH = PAGE_MODELS_PATH
+ "/{"
+ PAGE_MODEL_NAME
+ "}";
protected static final String CONTAINERS_PATH = PAGE_MODEL_PATH
+ "/containers";
protected static final String CONTAINER_PATH = CONTAINERS_PATH
+ "/{"
+ CONTAINER_KEY
+ "}";
protected static final String COMPONENTS_PATH = CONTAINER_PATH
+ "/components";
protected static final String COMPONENT_PATH = COMPONENTS_PATH
+ "/{"
+ COMPONENT_KEY
+ "}";
protected static final String STYLES_PATH = CONTAINER_PATH + "/styles";
@Override @Override
public Set<Class<?>> getClasses() { public Set<Class<?>> getClasses() {
@ -77,9 +52,6 @@ public class PageModelsApp extends Application {
classes.add(PageModels.class); classes.add(PageModels.class);
classes.add(Containers.class); classes.add(Containers.class);
classes.add(Components.class); classes.add(Components.class);
classes.add(StylesRs.class);
classes.add(StylesMediaRule.class);
classes.add(StylesRule.class);
return classes; return classes;
} }

View File

@ -16,7 +16,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA * MA 02110-1301 USA
*/ */
package org.libreccm.pagemodel.rs; package org.libreccm.api.pagemodels;
import org.libreccm.pagemodel.ComponentModel; import org.libreccm.pagemodel.ComponentModel;
import org.libreccm.pagemodel.ComponentModelRepository; import org.libreccm.pagemodel.ComponentModelRepository;
@ -67,9 +67,12 @@ class PageModelsController {
return appRepo return appRepo
.retrieveApplicationForPath(Objects.requireNonNull(appPath)) .retrieveApplicationForPath(Objects.requireNonNull(appPath))
.orElseThrow(() -> new NotFoundException(String .orElseThrow(() -> new NotFoundException(
.format("No application with path \"%s\" found.", String.format(
appPath))); "No application with path \"%s\" found.",
appPath)
)
);
} }
/** /**
@ -88,18 +91,23 @@ class PageModelsController {
final CcmApplication app, final CcmApplication app,
final PageModel pageModel, final PageModel pageModel,
final ContainerModel containerModel, final ContainerModel containerModel,
final String componentKey) { final String componentKey
) {
return componentModelRepo return componentModelRepo
.findComponentByContainerAndKey(containerModel, componentKey) .findComponentByContainerAndKey(containerModel, componentKey)
.orElseThrow(() -> new NotFoundException(String .orElseThrow(
.format( () -> new NotFoundException(
"The Container \"%s\" of the PageModel \"%s\" of application" String.format(
+ "\"%s\" does not contain a component with the key \"%s\".", "The Container \"%s\" of the PageModel \"%s\" of"
containerModel.getKey(), + " application \"%s\" does not contain a "
pageModel.getName(), + "component with the key \"%s\".",
app.getPrimaryUrl(), containerModel.getKey(),
componentKey))); pageModel.getName(),
app.getPrimaryUrl(),
componentKey
)
)
);
} }
/** /**
@ -113,20 +121,26 @@ class PageModelsController {
* {@code containerKey}. * {@code containerKey}.
*/ */
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
protected ContainerModel findContainer(final CcmApplication app, protected ContainerModel findContainer(
final PageModel pageModel, final CcmApplication app,
final String containerKey) { final PageModel pageModel,
final String containerKey
) {
return containerRepo return containerRepo
.findContainerByKeyAndPageModel( .findContainerByKeyAndPageModel(
Objects.requireNonNull(containerKey), Objects.requireNonNull(containerKey),
Objects.requireNonNull(pageModel)) Objects.requireNonNull(pageModel))
.orElseThrow(() -> new NotFoundException(String .orElseThrow(
.format("The PageModel \"%s\" of application \"%s\" does not have " () -> new NotFoundException(
+ "a container identified by the key \"%s\".", String.format(
pageModel.getName(), "The PageModel \"%s\" of application \"%s\" does not "
app.getPrimaryUrl(), + "have a container identified by the key \"%s\".",
containerKey))); pageModel.getName(),
app.getPrimaryUrl(),
containerKey)
)
);
} }
/** /**
@ -142,8 +156,9 @@ class PageModelsController {
* {@link CcmApplication} {@code app}, {@code false} otherwise. * {@link CcmApplication} {@code app}, {@code false} otherwise.
*/ */
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
protected boolean existsPageModel(final CcmApplication app, protected boolean existsPageModel(
final String pageModelName) { final CcmApplication app, final String pageModelName
) {
return pageModelRepo return pageModelRepo
.findDraftByApplicationAndName(app, pageModelName) .findDraftByApplicationAndName(app, pageModelName)
.isPresent(); .isPresent();
@ -160,16 +175,22 @@ class PageModelsController {
* {@code pageModelName} for the {@link CcmApplication} {@code app}. * {@code pageModelName} for the {@link CcmApplication} {@code app}.
*/ */
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
protected PageModel findPageModel(final CcmApplication app, protected PageModel findPageModel(
final String pageModelName) { final CcmApplication app, final String pageModelName
) {
return pageModelRepo return pageModelRepo
.findDraftByApplicationAndName( .findDraftByApplicationAndName(
Objects.requireNonNull(app), Objects.requireNonNull(app),
Objects.requireNonNull(pageModelName)) Objects.requireNonNull(pageModelName)
.orElseThrow(() -> new NotFoundException(String.format( )
"No PageModel with name \"%s\" for application \"%s\".", .orElseThrow(
pageModelName, app.getPrimaryUrl()))); () -> new NotFoundException(
String.format(
"No PageModel with name \"%s\" for application \"%s\".",
pageModelName, app.getPrimaryUrl()
)
)
);
} }
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)

View File

@ -1,4 +1,4 @@
package org.libreccm.pagemodel.rs; package org.libreccm.api.pagemodels;
public enum PublicationStatus { public enum PublicationStatus {

View File

@ -22,4 +22,4 @@
* editors. * editors.
* *
*/ */
package org.libreccm.pagemodel.rs; package org.libreccm.api.pagemodels;

View File

@ -1,627 +0,0 @@
/*
* Copyright (C) 2018 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.pagemodel.rs;
import org.libreccm.core.CoreConstants;
import org.libreccm.pagemodel.ComponentModel;
import org.libreccm.pagemodel.ComponentModelJsonConverter;
import org.libreccm.pagemodel.ComponentModelRepository;
import org.libreccm.pagemodel.ContainerModel;
import org.libreccm.pagemodel.ContainerModelManager;
import org.libreccm.pagemodel.ConvertsComponentModel;
import org.libreccm.pagemodel.PageModel;
import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.RequiresPrivilege;
import org.libreccm.web.CcmApplication;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
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.json.Json;
import javax.json.JsonArray;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObject;
import javax.json.JsonValue;
import javax.transaction.Transactional;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
/**
* Provides RESTful endpoints for retrieving, creating, updating and deleting
* {@link ComponentModel}s of a {@link ContainerModel}.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Path("/")
public class Components {
@Inject
private ComponentModelRepository componentRepo;
@Inject
private ContainerModelManager containerManager;
@Inject
private PageModelsController controller;
@Inject
@Any
private Instance<ComponentModelJsonConverter> jsonConverters;
/**
* Retrieve all {@link ComponentModel} of a {@link ContainerModel}.
*
* @param appPath The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
*
* @return A JSON array containing the data of all {@link ComponentModel} of
* the {@link ContainerModel}.
*/
@GET
@Path(PageModelsApp.COMPONENTS_PATH)
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonArray getComponents(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appPath));
final PageModel pageModel = controller.findPageModel(app,
pageModelName);
final ContainerModel container = controller.findContainer(app,
pageModel,
containerKey);
final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
container
.getComponents()
.stream()
.map(component -> mapComponentModelToJson(component))
.forEach(arrayBuilder::add);
return arrayBuilder.build();
}
/**
* Retrieves a specific {@link ComponentModel}.
*
* @param appPath The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
* @param componentKey The key of the {@link ComponentModel}.
*
* @return A JSON object containing the data of the {@link ComponentModel}.
*/
@GET
@Path(PageModelsApp.COMPONENT_PATH)
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject getComponent(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(PageModelsApp.COMPONENT_KEY) final String componentKey) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(componentKey);
final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appPath));
final PageModel pageModel = controller.findPageModel(app,
pageModelName);
final ContainerModel container = controller.findContainer(app,
pageModel,
containerKey);
final ComponentModel component = controller
.findComponentModel(app, pageModel, container, componentKey);
return mapComponentModelToJson(component);
}
/**
* Creates or updates a {@link ComponentModel}.
*
* If a {@link ComponentModel} with provided {@code componentKey} already
* exists in the container identified by {@code appPath},
* {@code pageModelName} and {@code containerKey} the {@link ComponentModel}
* is updated with the data from {@code componentModelData}.
*
* Otherwise a new {@link ComponentModel} is created using the data from
* {@code componentModelData}.
*
* @param appPath The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
* @param componentKey The key of the {@link ComponentModel} to create
* or update.
* @param componentModelData The data for creating or updating the
* {@link ComponentModel}.
*
* @return The new or updated {@link ComponentModel}.
*/
@PUT
@Path(PageModelsApp.COMPONENT_PATH)
@Consumes("application/json; charset=utf-8")
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject putComponent(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(PageModelsApp.COMPONENT_KEY) final String componentKey,
final JsonObject componentModelData) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(componentKey);
Objects.requireNonNull(componentModelData);
final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appPath));
final PageModel pageModel = controller.findPageModel(app,
pageModelName);
final ContainerModel container = controller.findContainer(app,
pageModel,
containerKey);
final Optional<ComponentModel> result = container
.getComponents()
.stream()
.filter(c -> c.getKey().equals(componentKey))
.findAny();
final ComponentModel componentModel;
if (result.isPresent()) {
componentModel = result.get();
} else {
componentModel = createComponentModel(componentModelData);
componentModel.setKey(componentKey);
containerManager.addComponentModel(container, componentModel);
}
setComponentPropertiesFromJson(componentModelData, componentModel);
componentRepo.save(componentModel);
final ComponentModel saved = controller
.findComponentModel(app, pageModel, container, componentKey);
return mapComponentModelToJson(saved);
}
/**
* Deletes a {@link ComponentModel}.
*
* @param appPath The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
* @param componentKey The key of the {@link ComponentModel} to delete.
*
*/
@DELETE
@Path(PageModelsApp.COMPONENT_PATH)
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public void deleteComponent(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(PageModelsApp.COMPONENT_KEY) final String componentKey) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(componentKey);
final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appPath));
final PageModel pageModel = controller.findPageModel(app,
pageModelName);
final ContainerModel container = controller.findContainer(app,
pageModel,
containerKey);
final ComponentModel component = controller
.findComponentModel(app, pageModel, container, componentKey);
containerManager.removeComponentModel(container, component);
}
/**
* Helper method for mapping a {@link ComponentModel} to JSON.
*
* @param componentModel The {@link ComponentModel} to map.
*
* @return The JSON representation of the
* {@link ComponentModel} {@code componentModel}.
*/
private JsonObject mapComponentModelToJson(
final ComponentModel componentModel) {
final Class<? extends ComponentModel> clazz = Objects
.requireNonNull(componentModel)
.getClass();
final ComponentModelJsonConverter jsonConverter
= findJsonConverter(clazz)
.orElseThrow(() -> new WebApplicationException(String.format(
"No JSON converter available for component model \"%s\".",
clazz.getName())));
return jsonConverter.toJson(componentModel);
// Objects.requireNonNull(componentModel);
//
// final JsonObjectBuilder objectBuilder = Json
// .createObjectBuilder()
// .add("componentModelId",
// Long.toString(componentModel.getComponentModelId()))
// .add("uuid", componentModel.getUuid())
// .add("modelUuid", componentModel.getModelUuid())
// .add("key", componentModel.getKey())
// .add("type", componentModel.getClass().getName());
//
// if (componentModel.getIdAttribute() != null) {
// objectBuilder.add("idAttribute", componentModel.getIdAttribute());
// }
//
// if (componentModel.getClassAttribute() != null) {
// objectBuilder.add("classAttribute",
// componentModel.getClassAttribute());
// }
//
// if (componentModel.getStyleAttribute() != null) {
// objectBuilder.add("styleAttribute",
// componentModel.getStyleAttribute());
// }
//
// final Class<? extends ComponentModel> clazz = componentModel.getClass();
// final BeanInfo beanInfo;
// try {
// beanInfo = Introspector.getBeanInfo(clazz);
// } catch (IntrospectionException ex) {
// throw new WebApplicationException(ex);
// }
//
// for (final PropertyDescriptor propertyDescriptor
// : beanInfo.getPropertyDescriptors()) {
//
// final Method readMethod = propertyDescriptor.getReadMethod();
// final Object value;
// try {
// value = readMethod.invoke(componentModel);
// } catch (IllegalAccessException
// | InvocationTargetException ex) {
// throw new WebApplicationException(ex);
// }
//
// final String valueStr;
// if (value == null) {
// valueStr = "";
// } else {
// valueStr = value.toString();
// }
//
// objectBuilder.add(propertyDescriptor.getName(), valueStr);
//
// }
//
// return objectBuilder.build();
}
/**
* Creates a new {@link ComponentModel} instance.
*
* Uses reflection and the value of {@code type} property from the JSON
* {@code data} to determine the correct class.
*
* @param data The data from which the new {@link ComponentModel} is
* created.
*
* @return The new {@link ComponentModel}.
*/
private ComponentModel createComponentModel(final JsonObject data) {
Objects.requireNonNull(data);
if (!data.containsKey("type")) {
throw new BadRequestException("The JSON data for creating the "
+ "component has no value for the type of the component to "
+ "create.");
}
final String type = data.getString("type");
final Class<? extends ComponentModel> clazz = findComponentModelClass(
type);
final ComponentModel componentModel;
try {
componentModel = clazz.getConstructor().newInstance();
} catch (IllegalAccessException
| InstantiationException
| InvocationTargetException
| NoSuchMethodException ex) {
throw new WebApplicationException(ex);
}
return componentModel;
}
/**
* Helper method for finding the correct subclass of {@link ComponentModel}
* using the fully qualified name the class.
*
* @param type The fully qualified name of the subclass of
* {@link ComponentModel}.
*
* @return The subclass of {@link ComponentModel}.
*
* @throws BadRequestException If there is no subclass of
* {@link ComponentModel} with the fully
* qualified name provided by the {@code type}
* parameter.
*/
@SuppressWarnings("unchecked")
private Class<? extends ComponentModel> findComponentModelClass(
final String type) {
Objects.requireNonNull(type);;
try {
final Class<?> clazz = Class.forName(type);
if (ComponentModel.class.isAssignableFrom(clazz)) {
return (Class<? extends ComponentModel>) clazz;
} else {
throw new BadRequestException(String.format(
"The type \"%s\" is not a subclass of \"%s\".",
type,
ComponentModel.class.getName()));
}
} catch (ClassNotFoundException ex) {
throw new BadRequestException(String.format(
"The component model type \"%s\" "
+ "does not exist.",
type));
}
}
/**
* Helper method for setting the properties of a {@link ComponentModel} from
* the JSON data.
*
* @param data The JSON data.
* @param componentModel The {@link ComponentModel}.
*/
private void setComponentPropertiesFromJson(
final JsonObject data,
final ComponentModel componentModel) {
final Class<? extends ComponentModel> clazz = Objects
.requireNonNull(componentModel)
.getClass();
final ComponentModelJsonConverter jsonConverter
= findJsonConverter(clazz)
.orElseThrow(() -> new WebApplicationException(String.format(
"No JSON converter available for component model \"%s\".",
clazz.getName())));
jsonConverter.fromJson(data, componentModel);
// final BeanInfo beanInfo;
// try {
// beanInfo = Introspector.getBeanInfo(componentModel.getClass());
// } catch (IntrospectionException ex) {
// throw new WebApplicationException(ex);
// }
//
// Arrays
// .stream(beanInfo.getPropertyDescriptors())
// .forEach(
// propertyDesc -> setComponentPropertyFromJson(componentModel,
// propertyDesc,
// data));
}
/**
* Helper emthod for setting a property of a {@link ComponentModel} using a
* value from JSON data.
*
* @param componentModel The {@link ComponentModel}
* @param propertyDesc The {@link PropertyDescriptor} for the property to
* set.
* @param data The JSON data containing the new value of the
* property.
*/
private void setComponentPropertyFromJson(
final ComponentModel componentModel,
final PropertyDescriptor propertyDesc,
final JsonObject data) {
// Ignore key and type (handled by other methods).
if ("key".equals(propertyDesc.getName())
|| "type".equals(propertyDesc.getName())) {
return;
}
if (data.containsKey(propertyDesc.getName())) {
final Method writeMethod = propertyDesc.getWriteMethod();
final Class<?> propertyType = propertyDesc.getPropertyType();
if (writeMethod != null) {
try {
// final String value = data.getString(propertyDesc.getName());
final JsonValue value = data.get(propertyDesc.getName());
// final JsonValue.ValueType valueType = value.getValueType();
if (propertyType == Boolean.TYPE) {
writeMethod.invoke(
componentModel,
Boolean.parseBoolean(value.toString()));
} else if (propertyType == Double.TYPE) {
writeMethod.invoke(
componentModel,
Double.parseDouble(value.toString()));
} else if (propertyType == Float.TYPE) {
writeMethod.invoke(componentModel,
Float.parseFloat(value.toString()));
} else if (propertyType == Integer.TYPE) {
writeMethod.invoke(componentModel,
Integer.parseInt(value.toString()));
} else if (propertyType == Long.TYPE) {
writeMethod.invoke(componentModel,
Long.parseLong(value.toString()));
} else if (propertyType == String.class) {
writeMethod.invoke(componentModel, value.toString());
} else if (propertyType == List.class) {
final JsonValue valueObj = data
.get(propertyDesc.getName());
if (valueObj.getValueType()
== JsonValue.ValueType.ARRAY) {
final JsonArray dataArray = data
.getJsonArray(propertyDesc.getName());
final List<String> values = dataArray
.stream()
.map(jsonValue -> jsonValue.toString())
.collect(Collectors.toList());
writeMethod.invoke(componentModel, values);
} else {
String valueStr = value.toString();
if (valueStr.startsWith("[")) {
valueStr = valueStr.substring(1);
}
if (valueStr.endsWith("]")) {
valueStr = valueStr
.substring(valueStr.length() - 1);
}
final String[] tokens = valueStr.split(",");
final List<String> values = Arrays
.stream(tokens)
.map(token -> token.trim())
.collect(Collectors.toList());
writeMethod.invoke(componentModel, values);
}
} else {
throw new IllegalArgumentException(
"Unsupported property type.");
}
} catch (IllegalAccessException
| InvocationTargetException ex) {
throw new WebApplicationException(ex);
}
}
}
}
private Optional<ComponentModelJsonConverter>
findJsonConverter(
final Class<? extends ComponentModel> componentModelClass) {
final ConvertsComponentModelLiteral literal
= new ConvertsComponentModelLiteral(
componentModelClass);
final Instance<ComponentModelJsonConverter> instance = jsonConverters
.select(literal);
if (instance.isUnsatisfied()) {
return Optional.empty();
} else if (instance.isAmbiguous()) {
throw new IllegalStateException(String.format(
"Multiple JSONConverter for \"%s\".",
componentModelClass.getName()));
} else {
final Iterator<ComponentModelJsonConverter> iterator = instance
.iterator();
@SuppressWarnings("unchecked")
final ComponentModelJsonConverter converter = iterator.next();
return Optional.of(converter);
}
}
private static class ConvertsComponentModelLiteral
extends AnnotationLiteral<ConvertsComponentModel>
implements ConvertsComponentModel {
private static final long serialVersionUID = 1L;
private final Class<? extends ComponentModel> componentModel;
public ConvertsComponentModelLiteral(
final Class<? extends ComponentModel> componentModel) {
this.componentModel = componentModel;
}
@Override
public Class<? extends ComponentModel> componentModel() {
return componentModel;
}
}
}

View File

@ -1,200 +0,0 @@
/*
* Copyright (C) 2018 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.pagemodel.rs;
import org.libreccm.pagemodel.styles.CssProperty;
import org.libreccm.pagemodel.styles.Dimension;
import org.libreccm.pagemodel.styles.MediaQuery;
import org.libreccm.pagemodel.styles.MediaRule;
import org.libreccm.pagemodel.styles.Rule;
import java.util.List;
import java.util.Objects;
import javax.enterprise.context.RequestScoped;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObject;
/**
* Utility class for mapping the entities from the
* {@link org.libreccm.pagemodel.styles} package to JSON.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
class StylesJsonMapper {
/**
* Map a {@link Dimension} object to JSON.
*
* @param dimension The {@link Dimension} object to map.
*
* @return A JSON object representing the provided {@link Dimension} object.
*/
protected JsonObject mapDimensionToJson(final Dimension dimension) {
Objects.requireNonNull(dimension);
return Json
.createObjectBuilder()
.add("value", dimension.getValue())
.add("unit", dimension.getUnit().toString())
.build();
}
/**
* Maps a List of {@link MediaRule} objects to JSON.
*
* @param mediaRules The {@link MediaRule}s to map.
*
* @return An JSON array with the data from the {@link MediaRule} objects in
* the list.
*/
protected JsonArray mapMediaRulesToJson(final List<MediaRule> mediaRules) {
final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
Objects
.requireNonNull(mediaRules)
.stream()
.map(this::mapMediaRuleToJson)
.forEach(arrayBuilder::add);
return arrayBuilder.build();
}
/**
* Maps a {@link MediaRule} object to JSON.
*
* @param mediaRule The {@link MediaRule} object to map.
*
* @return The JSON representation of the provided {@link MediaRule} object.
*/
protected JsonObject mapMediaRuleToJson(final MediaRule mediaRule) {
Objects.requireNonNull(mediaRule);
return Json
.createObjectBuilder()
.add("mediaRuleId", mediaRule.getMediaRuleId())
.add("mediaQuery", mapMediaQueryToJson(mediaRule.getMediaQuery()))
.add("rules", mapRulesToJson(mediaRule.getRules()))
.build();
}
/**
* Maps a {@link MediaQuery} object to JSON.
*
* @param mediaQuery The {@link MediaQuery} object to map.
*
* @return The JSON representation of the provided {@link MediaQuery}
* object.
*/
protected JsonObject mapMediaQueryToJson(final MediaQuery mediaQuery) {
Objects.requireNonNull(mediaQuery);
return Json
.createObjectBuilder()
.add("mediaQueryId", mediaQuery.getMediaQueryId())
.add("mediaType", mediaQuery.getMediaType().toString())
.add("minWidth", mapDimensionToJson(mediaQuery.getMinWidth()))
.add("maxWidth", mapDimensionToJson(mediaQuery.getMaxWidth()))
.build();
}
/**
* Maps a list of {@link Rule} objects to JSON.
*
* @param rules The list of {@link Rule} objects to map.
*
* @return A JSON array with the JSON representations of the {@link Rule}
* objects in the list.
*/
protected JsonArray mapRulesToJson(final List<Rule> rules) {
final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
Objects
.requireNonNull(rules)
.stream()
.map(this::mapRuleToJson)
.forEach(arrayBuilder::add);
return arrayBuilder.build();
}
/**
* Maps a {@link Rule} object to JSON.
*
* @param rule The {@link Rule} object to map.
*
* @return The JSON representation of the provided {@link RuleObject}.
*/
protected JsonObject mapRuleToJson(final Rule rule) {
Objects.requireNonNull(rule);
return Json
.createObjectBuilder()
.add("ruleId", rule.getRuleId())
.add("selector", rule.getSelector())
.add("properties", mapPropertiesToJson(rule.getProperties()))
.build();
}
/**
* Maps a list of {@link CssProperty} objects to JSON.
*
* @param properties The list of {@link CssProperty} objects to map.
*
* @return A JSON array containing the JSON representations of the
* {@link CssProperty} objects in the list.
*/
protected JsonArray mapPropertiesToJson(final List<CssProperty> properties) {
final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
Objects
.requireNonNull(properties)
.stream()
.map(this::mapCssPropertyToJson)
.forEach(arrayBuilder::add);
return arrayBuilder.build();
}
/**
* Maps a {@link CssProperty} object to JSON.
*
* @param property The {@link CssProperty} to map.
* @return The JSON representation of the provided {@link CssProperty}.
*/
protected JsonObject mapCssPropertyToJson(final CssProperty property) {
Objects.requireNonNull(property);
return Json
.createObjectBuilder()
.add("propertyId", property.getPropertyId())
.add("name", property.getName())
.add("value", property.getValue())
.build();
}
}

View File

@ -1,622 +0,0 @@
/*
* Copyright (C) 2018 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.pagemodel.rs;
import org.libreccm.core.CoreConstants;
import org.libreccm.pagemodel.ContainerModel;
import org.libreccm.pagemodel.styles.CssProperty;
import org.libreccm.pagemodel.styles.MediaRule;
import org.libreccm.pagemodel.styles.Rule;
import org.libreccm.pagemodel.styles.StylesManager;
import org.libreccm.pagemodel.styles.StylesRepository;
import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.RequiresPrivilege;
import org.libreccm.web.CcmApplication;
import java.io.Serializable;
import java.util.Objects;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.transaction.Transactional;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
/**
* Provides RESTful endpoints for retrieving, creating, updating and deleting
* {@link MediaRule}s.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Path(StylesRs.MEDIA_RULE_PATH)
public class StylesMediaRule implements Serializable {
private static final long serialVersionUID = 3257114872624583807L;
protected final static String PROPERTY_ID = "propertyId";
protected final static String RULE_ID = "ruleId";
protected final static String RULES_PATH = "/rules";
protected final static String RULE_PATH = RULES_PATH
+ "/{"
+ RULE_ID
+ "}";
protected final static String PROPERTIES_PATH = RULE_PATH + "/properties";
protected final static String PROPERTY_PATH = PROPERTIES_PATH
+ "/{"
+ PROPERTY_ID
+ "}";
@Inject
private StylesJsonMapper stylesJsonMapper;
@Inject
private StylesManager stylesManager;
@Inject
private StylesRepository stylesRepo;
@Inject
private StylesRs stylesRs;
/**
* Retrieves all {@link Rule}s of a {@link MediaRule}.
*
* @param appPath The path of the {@link CcmApplication} to which
* the {@link PageModel} belongs.
* @param pageModelName The name of the {@link PageModel} to which the
* {@link ContainerModel} belongs.
* @param containerKey The key of the {@link ContainerModel} to which
* the {@link MediaRule} belongs.
* @param mediaRuleIdParam The ID of the {@link MediaRule}.
*
* @return A JSON array with the JSON representations of all {@link Rule}s
* belonging the {@link MediaRule} identified by the provided path.
*/
@GET
@Path(RULES_PATH)
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonArray getRules(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(StylesRs.MEDIA_RULE_ID) final String mediaRuleIdParam) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(mediaRuleIdParam);
final MediaRule mediaRule = stylesRs.findMediaRule(appPath,
pageModelName,
containerKey,
mediaRuleIdParam);
return stylesJsonMapper.mapRulesToJson(mediaRule.getRules());
}
/**
* Retrieves a specific {@link Rule} from a {@link MediaRule}.
*
* @param appPath The path of the {@link CcmApplication} to which
* the {@link PageModel} belongs.
* @param pageModelName The name of the {@link PageModel} to which the
* {@link ContainerModel} belongs.
* @param containerKey The key of the {@link ContainerModel} to which
* the {@link MediaRule} belongs.
* @param mediaRuleIdParam The ID of the {@link MediaRule}.
* @param ruleIdParam The ID of the {@link Rule} to retrieve.
*
* @return The JSON representation of the {@link Rule} identified by the
* provided path.
*/
@GET
@Path(RULE_PATH)
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject getRule(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(StylesRs.MEDIA_RULE_ID) final String mediaRuleIdParam,
@PathParam(RULE_ID) String ruleIdParam) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(mediaRuleIdParam);
Objects.requireNonNull(ruleIdParam);
final MediaRule mediaRule = stylesRs.findMediaRule(appPath,
pageModelName,
containerKey,
mediaRuleIdParam);
return stylesJsonMapper.mapRuleToJson(findRule(mediaRule,
ruleIdParam));
}
/**
* Creates a new {@link Rule} for a {@link MediaRule}.
*
* @param appPath The path of the {@link CcmApplication} to which
* the {@link PageModel} belongs.
* @param pageModelName The name of the {@link PageModel} to which the
* {@link ContainerModel} belongs.
* @param containerKey The key of the {@link ContainerModel} to which
* the {@link MediaRule} belongs.
* @param mediaRuleIdParam The ID of the {@link MediaRule}.
* @param ruleData The data from which the new {@link Rule} is
* created.
*
* @return The JSON representation of the new {@link Rule}.
*/
@POST
@Path(RULES_PATH)
@Consumes("application/json; charset=utf-8")
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject createRule(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(StylesRs.MEDIA_RULE_ID) final String mediaRuleIdParam,
final JsonObject ruleData) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(mediaRuleIdParam);
Objects.requireNonNull(ruleData);
final MediaRule mediaRule = stylesRs.findMediaRule(appPath,
pageModelName,
containerKey,
mediaRuleIdParam);
final Rule rule = new Rule();
rule.setSelector(ruleData.getString("selector"));
stylesManager.addRuleToMediaRule(rule, mediaRule);
return stylesJsonMapper.mapRuleToJson(rule);
}
/**
* Updates an existing {@link Rule}
*
* @param appPath The path of the {@link CcmApplication} to which
* the {@link PageModel} belongs.
* @param pageModelName The name of the {@link PageModel} to which the
* {@link ContainerModel} belongs.
* @param containerKey The key of the {@link ContainerModel} to which
* the {@link MediaRule} belongs.
* @param mediaRuleIdParam The ID of the {@link MediaRule}.
* @param ruleIdParam The ID of the {@link Rule} to update.
* @param ruleData The data from which used to update the
* {@link Rule}.
*
* @return The JSON representation of the updated {@link Rule}.
*/
@PUT
@Path(RULE_PATH)
@Consumes("application/json; charset=utf-8")
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject updateRule(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(StylesRs.MEDIA_RULE_ID) final String mediaRuleIdParam,
@PathParam(RULE_ID) String ruleIdParam,
final JsonObject ruleData) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(mediaRuleIdParam);
Objects.requireNonNull(ruleIdParam);
Objects.requireNonNull(ruleData);
final MediaRule mediaRule = stylesRs.findMediaRule(appPath,
pageModelName,
containerKey,
mediaRuleIdParam);
final Rule rule = findRule(mediaRule, ruleIdParam);
rule.setSelector(ruleData.getString("selector"));
stylesManager.addRuleToMediaRule(rule, mediaRule);
return stylesJsonMapper.mapRuleToJson(rule);
}
@DELETE
@Path(RULE_PATH)
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public void deleteRule(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(StylesRs.MEDIA_RULE_ID) final String mediaRuleIdParam,
@PathParam(RULE_ID) String ruleIdParam) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(mediaRuleIdParam);
Objects.requireNonNull(ruleIdParam);
final MediaRule mediaRule = stylesRs.findMediaRule(appPath,
pageModelName,
containerKey,
mediaRuleIdParam);
final Rule rule = findRule(mediaRule, ruleIdParam);
stylesManager.removeRuleFromMediaRule(rule, mediaRule);
stylesRepo.deleteRule(rule);
}
/**
* Retrieves all {@link CssProperty} objects assigned to {@link Rule} which
* is assigned to {@link MediaRule}.
*
* @param appPath The path of the {@link CcmApplication} to which
* the {@link PageModel} belongs.
* @param pageModelName The name of the {@link PageModel} to which the
* {@link ContainerModel} belongs.
* @param containerKey The key of the {@link ContainerModel} to which
* the {@link MediaRule} belongs.
* @param mediaRuleIdParam The ID of the {@link MediaRule}.
* @param ruleIdParam The ID of the {@link Rule} from which the
* {@link CssProperty} objects are retrieved.
*
* @return A JSON array with the JSON representations of the
* {@link CssProperty} objects.
*/
@GET
@Path(PROPERTIES_PATH)
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonArray getProperties(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(StylesRs.MEDIA_RULE_ID) final String mediaRuleIdParam,
@PathParam(RULE_ID) String ruleIdParam) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(mediaRuleIdParam);
Objects.requireNonNull(ruleIdParam);
final MediaRule mediaRule = stylesRs.findMediaRule(appPath,
pageModelName,
containerKey,
mediaRuleIdParam);
final Rule rule = findRule(mediaRule, ruleIdParam);
return stylesJsonMapper.mapPropertiesToJson(rule.getProperties());
}
/**
* Retrieve a {@link CssProperty} assigned to {@link Rule} which is assigned
* to {@link MediaRule}.
*
* @param appPath The path of the {@link CcmApplication} to which
* the {@link PageModel} belongs.
* @param pageModelName The name of the {@link PageModel} to which the
* {@link ContainerModel} belongs.
* @param containerKey The key of the {@link ContainerModel} to which
* the {@link MediaRule} belongs.
* @param mediaRuleIdParam The ID of the {@link MediaRule}.
* @param ruleIdParam The ID of the {@link Rule} from which the
* {@link CssProperty} is retrieved.
* @param propertyIdParam The ID of the {@link CssProperty} to retrieve.
*
* @return The JSON representation of the {@link CssProperty} identified by
* the provided path.
*/
@GET
@Path(PROPERTY_PATH)
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject getProperty(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(StylesRs.MEDIA_RULE_ID) final String mediaRuleIdParam,
@PathParam(RULE_ID) final String ruleIdParam,
@PathParam(PROPERTY_ID) final String propertyIdParam) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(mediaRuleIdParam);
Objects.requireNonNull(ruleIdParam);
Objects.requireNonNull(propertyIdParam);
final MediaRule mediaRule = stylesRs.findMediaRule(appPath,
pageModelName,
containerKey,
mediaRuleIdParam);
final Rule rule = findRule(mediaRule, ruleIdParam);
final CssProperty property = findProperty(rule, propertyIdParam);
return stylesJsonMapper.mapCssPropertyToJson(property);
}
/**
* Creates a new {@link CssProperty} for a {@link Rule} of a
* {@link MediaRule}.
*
* @param appPath The path of the {@link CcmApplication} to which
* the {@link PageModel} belongs.
* @param pageModelName The name of the {@link PageModel} to which the
* {@link ContainerModel} belongs.
* @param containerKey The key of the {@link ContainerModel} to which
* the {@link MediaRule} belongs.
* @param mediaRuleIdParam The ID of the {@link MediaRule}.
* @param ruleIdParam The ID of the {@link Rule} for which the new
* {@link CssProperty} is created.
* @param propertyData The data from which the new {@link CssProperty}
* is created.
*
* @return The JSON representation of the new {@link CssProperty}.
*/
@POST
@Path(PROPERTIES_PATH)
@Consumes("application/json; charset=utf-8")
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject createProperty(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(StylesRs.MEDIA_RULE_ID) final String mediaRuleIdParam,
@PathParam(RULE_ID) String ruleIdParam,
final JsonObject propertyData) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(mediaRuleIdParam);
Objects.requireNonNull(ruleIdParam);
Objects.requireNonNull(propertyData);
final MediaRule mediaRule = stylesRs.findMediaRule(appPath,
pageModelName,
containerKey,
mediaRuleIdParam);
final Rule rule = findRule(mediaRule, ruleIdParam);
final CssProperty property = new CssProperty();
setCssPropertyData(property, propertyData);
stylesManager.addCssPropertyToRule(property, rule);
return stylesJsonMapper.mapCssPropertyToJson(property);
}
/**
* Updates an existing {@link CssProperty} of {@link Rule} of a
* {@link MediaRule}.
*
* @param appPath The path of the {@link CcmApplication} to which
* the {@link PageModel} belongs.
* @param pageModelName The name of the {@link PageModel} to which the
* {@link ContainerModel} belongs.
* @param containerKey The key of the {@link ContainerModel} to which
* the {@link MediaRule} belongs.
* @param mediaRuleIdParam The ID of the {@link MediaRule}.
* @param ruleIdParam The ID of the {@link Rule} to which the
* {@link CssProperty} belongs.
* @param propertyIdParam The ID of the {@link CssProperty} to update.
* @param propertyData The data which is used to update the
* {@link CssProperty}.
*
* @return The JSON representation of the updated {@link CssProperty}.
*/
@PUT
@Path(PROPERTY_PATH)
@Consumes("application/json; charset=utf-8")
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject updateProperty(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(StylesRs.MEDIA_RULE_ID) final String mediaRuleIdParam,
@PathParam(RULE_ID) String ruleIdParam,
@PathParam(PROPERTY_ID) final String propertyIdParam,
final JsonObject propertyData) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(mediaRuleIdParam);
Objects.requireNonNull(ruleIdParam);
Objects.requireNonNull(propertyIdParam);
Objects.requireNonNull(propertyData);
final MediaRule mediaRule = stylesRs.findMediaRule(appPath,
pageModelName,
containerKey,
mediaRuleIdParam);
final Rule rule = findRule(mediaRule, ruleIdParam);
final CssProperty property = findProperty(rule, propertyIdParam);
setCssPropertyData(property, propertyData);
stylesRepo.saveCssProperty(property);
return stylesJsonMapper.mapCssPropertyToJson(property);
}
/**
* Deletes a {@link CssProperty} of a {@link Rule} assigned to a
* {@link MediaRule}.
*
* @param appPath The path of the {@link CcmApplication} to which
* the {@link PageModel} belongs.
* @param pageModelName The name of the {@link PageModel} to which the
* {@link ContainerModel} belongs.
* @param containerKey The key of the {@link ContainerModel} to which
* the {@link MediaRule} belongs.
* @param mediaRuleIdParam The ID of the {@link MediaRule}.
* @param ruleIdParam The ID of the {@link Rule} to which the
* {@link CssProperty} belongs.
* @param propertyIdParam The ID of the {@link CssProperty} to delete.
*/
@DELETE
@Path(PROPERTY_PATH)
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public void deleteProperty(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(StylesRs.MEDIA_RULE_ID) final String mediaRuleIdParam,
@PathParam(RULE_ID) String ruleIdParam,
@PathParam(PROPERTY_ID) final String propertyIdParam) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(mediaRuleIdParam);
Objects.requireNonNull(ruleIdParam);
Objects.requireNonNull(propertyIdParam);
final MediaRule mediaRule = stylesRs.findMediaRule(appPath,
pageModelName,
containerKey,
mediaRuleIdParam);
final Rule rule = findRule(mediaRule, ruleIdParam);
final CssProperty property = findProperty(rule, propertyIdParam);
stylesManager.removeCssPropertyFromRule(property, rule);
stylesRepo.deleteCssProperty(property);
}
/**
* Helper method for finding a {@link CssProperty}.
*
* @param rule The {@link Rule} to which the {@link CssProperty}
* belongs.
* @param propertyIdParam The ID of the {@link CssProperty} to find.
*
* @return The {@link CssProperty}.
*/
private CssProperty findProperty(final Rule rule,
final String propertyIdParam) {
final long propertyId;
try {
propertyId = Long.parseLong(propertyIdParam);
} catch (NumberFormatException ex) {
throw new WebApplicationException(ex);
}
return rule
.getProperties()
.stream()
.filter(property -> propertyId == property.getPropertyId())
.findAny()
.orElseThrow(() -> new NotFoundException());
}
/**
* Helper method for finding a {@link Rule} assigned to {@link MediaRule}.
*
* @param mediaRule The {@link MediaRule}.
* @param ruleIdParam The ID of {@link Rule} to find.
*
* @return The {@link Rule}.
*/
private Rule findRule(final MediaRule mediaRule,
final String ruleIdParam) {
final long ruleId;
try {
ruleId = Long.parseLong(ruleIdParam);
} catch (NumberFormatException ex) {
throw new WebApplicationException(ex);
}
Objects.requireNonNull(mediaRule);
return mediaRule
.getRules()
.stream()
.filter(rule -> ruleId == rule.getRuleId())
.findAny()
.orElseThrow(() -> new NotFoundException());
}
/**
* Helper method for updating the values of the properties of
* {@link CssProperty} object from its JSON representation.
*
* @param property The {@link CssProperty}.
* @param propertyData The {@link JsonObject} containing the data.
*/
private void setCssPropertyData(final CssProperty property,
final JsonObject propertyData) {
Objects.requireNonNull(property);
Objects.requireNonNull(propertyData);
property.setName(propertyData.getString("name"));
property.setValue(propertyData.getString("value"));
}
}

View File

@ -1,666 +0,0 @@
/*
* Copyright (C) 2018 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.pagemodel.rs;
import org.libreccm.core.CoreConstants;
import org.libreccm.pagemodel.ContainerModel;
import org.libreccm.pagemodel.PageModel;
import org.libreccm.pagemodel.styles.Dimension;
import org.libreccm.pagemodel.styles.MediaRule;
import org.libreccm.pagemodel.styles.MediaType;
import org.libreccm.pagemodel.styles.Rule;
import org.libreccm.pagemodel.styles.Styles;
import org.libreccm.pagemodel.styles.StylesManager;
import org.libreccm.pagemodel.styles.StylesRepository;
import org.libreccm.pagemodel.styles.Unit;
import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.RequiresPrivilege;
import org.libreccm.web.CcmApplication;
import java.util.Objects;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.transaction.Transactional;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
/**
* Provides RESTful endpoints for managing the (CSS) styles of a
* {@link ContainerModel}.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Path(PageModelsApp.STYLES_PATH)
public class StylesRs {
protected static final String MEDIA_RULE_ID = "mediaRuleId";
protected static final String RULE_ID = "ruleId";
protected static final String MEDIA_RULES_PATH = "/media-rules";
protected static final String MEDIA_RULE_PATH = MEDIA_RULES_PATH
+ "/{"
+ MEDIA_RULE_ID
+ "}";
protected static final String RULES_PATH = "/rules";
protected static final String RULE_PATH = RULES_PATH
+ "/{"
+ RULE_ID
+ "}";
@Inject
private PageModelsController controller;
@Inject
private StylesJsonMapper stylesJsonMapper;
@Inject
private StylesManager stylesManager;
@Inject
private StylesRepository stylesRepo;
/**
* Retrieves all {@link MediaRule}s from the {@link Styles} entity of a
* {@link ContainerModel}.
*
* @param appPath The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
*
* @return The JSON Array with the JSON representations of all
* {@link MediaRule}s of the {@link ContainerModel} identified by
* the provided path.
*/
@GET
@Path(MEDIA_RULES_PATH)
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonArray getMediaRules(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appPath));
final PageModel pageModel = controller.findPageModel(app,
pageModelName);
final ContainerModel container = controller.findContainer(app,
pageModel,
containerKey);
final Styles styles = container.getStyles();
return stylesJsonMapper.mapMediaRulesToJson(styles.getMediaRules());
}
/**
* Retrieves a specific {@link MediaRule} from the {@link Styles} entity of
* a {@link ContainerModel}.
*
* @param appPath The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
* @param mediaRuleIdParam The ID of the {@link MediaRule} to retrieve.
*
* @return The JSON representation of the {@link MediaRule}.
*/
@GET
@Path(MEDIA_RULE_PATH)
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject getMediaRule(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(MEDIA_RULE_ID) final String mediaRuleIdParam) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(mediaRuleIdParam);
return stylesJsonMapper
.mapMediaRuleToJson(findMediaRule(appPath,
pageModelName,
containerKey,
mediaRuleIdParam));
}
/**
* Creates a new {@link MediaRule}.
*
* @param appPath The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
* @param mediaRuleData The data for the new {@link MediaRule}.
*
* @return The JSON representation of the new {@link MediaRule}.
*/
@POST
@Path(MEDIA_RULES_PATH)
@Consumes("application/json; charset=utf-8")
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject createMediaRule(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
final JsonObject mediaRuleData) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(mediaRuleData);
final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appPath));
final PageModel pageModel = controller.findPageModel(app,
pageModelName);
final ContainerModel container = controller.findContainer(app,
pageModel,
containerKey);
final Styles styles = container.getStyles();
final MediaRule mediaRule = new MediaRule();
setMediaRuleProperties(mediaRuleData, mediaRule);
stylesManager.addMediaRuleToStyles(mediaRule, styles);
return stylesJsonMapper.mapMediaRuleToJson(mediaRule);
}
/**
* Update a {@link MediaRule}.
*
* @param appPath The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
* @param mediaRuleIdParam The ID of the {@link MediaRule} to update.
* @param mediaRuleData The data for updating the {@link MediaRule}.
*
* @return The JSON representation of the updated {@link MediaRule}.
*/
@PUT
@Path(MEDIA_RULE_PATH)
@Consumes("application/json; charset=utf-8")
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject updateMediaRule(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(MEDIA_RULE_ID) final String mediaRuleIdParam,
final JsonObject mediaRuleData) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(mediaRuleIdParam);
Objects.requireNonNull(mediaRuleData);
final MediaRule mediaRule = findMediaRule(appPath,
pageModelName,
containerKey,
mediaRuleIdParam);
setMediaRuleProperties(mediaRuleData, mediaRule);
stylesRepo.saveMediaRule(mediaRule);
return stylesJsonMapper.mapMediaRuleToJson(mediaRule);
}
/**
* Deletes a {@link MediaRule}.
*
* @param appPath The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
* @param mediaRuleIdParam The ID of the {@link MediaRule} to delete.
*/
@DELETE
@Path(MEDIA_RULE_PATH)
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public void deleteMediaRule(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(MEDIA_RULE_ID) final String mediaRuleIdParam) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(mediaRuleIdParam);
final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appPath));
final PageModel pageModel = controller.findPageModel(app,
pageModelName);
final ContainerModel container = controller.findContainer(app,
pageModel,
containerKey);
final Styles styles = container.getStyles();
final MediaRule mediaRule = findMediaRule(pageModel,
container,
mediaRuleIdParam);
stylesManager.removeMediaRuleFromStyles(mediaRule, styles);
}
/**
* Retrieves all {@link Rule}s from the {@link Styles} entity of a
* {@link ContainerModel}.
*
* @param appPath The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
*
* @return A JSON array with the JSON representation of all {@link Rule}s of
* the {@link ContainerModel}.
*/
@GET
@Path(RULES_PATH)
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonArray getRules(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appPath));
final PageModel pageModel = controller.findPageModel(app,
pageModelName);
final ContainerModel container = controller.findContainer(app,
pageModel,
containerKey);
final Styles styles = container.getStyles();
return stylesJsonMapper.mapRulesToJson(styles.getRules());
}
/**
* Retrieves a specific {@link Rule} from the {@link Styles} entity of a
* {@link ContainerModel}.
*
* @param appPath The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
* @param ruleIdParam The ID of the {@link Rule} to retrieve.
*
* @return The JSON representation of the {@link Rule}.
*/
@GET
@Path(RULE_PATH)
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject getRule(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(RULE_ID) final String ruleIdParam) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(ruleIdParam);
return stylesJsonMapper
.mapRuleToJson(findRule(appPath,
pageModelName,
containerKey,
ruleIdParam));
}
/**
* Creates a new {@link Rule}.
*
* @param appPath The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
* @param ruleData The data for the new {@link Rule}.
*
* @return The JSON representation of the new {@link Rule}.
*/
@POST
@Path(RULES_PATH)
@Consumes("application/json; charset=utf-8")
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject createRule(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
final JsonObject ruleData) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(ruleData);
final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appPath));
final PageModel pageModel = controller.findPageModel(app,
pageModelName);
final ContainerModel container = controller.findContainer(app,
pageModel,
containerKey);
final Styles styles = container.getStyles();
final Rule rule = new Rule();
rule.setSelector(ruleData.getString("selector"));
stylesManager.addRuleToStyles(rule, styles);
return stylesJsonMapper.mapRuleToJson(rule);
}
/**
* Updates an existing {@link Rule}.
*
* @param appPath The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
* @param ruleIdParam The ID of the {@link Rule} to update.
* @param ruleData The data for updating the {@link Rule}.
*
* @return The JSON representation of the updated {@link Rule}.
*/
@PUT
@Path(RULE_PATH)
@Consumes("application/json; charset=utf-8")
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject updateRule(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(RULE_ID) final String ruleIdParam,
final JsonObject ruleData) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(ruleIdParam);
Objects.requireNonNull(ruleData);
final Rule rule = findRule(appPath,
pageModelName,
containerKey,
ruleIdParam);
rule.setSelector(ruleData.getString("selector"));
stylesRepo.saveRule(rule);
return stylesJsonMapper.mapRuleToJson(rule);
}
/**
* Deletes a {@link Rule}.
*
* @param appPath The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
* @param ruleIdParam The ID of the {@link Rule} to delete.
*/
@DELETE
@Path(RULE_PATH)
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public void deleteRule(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(RULE_ID) final String ruleIdParam) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(ruleIdParam);
final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appPath));
final PageModel pageModel = controller.findPageModel(app,
pageModelName);
final ContainerModel container = controller.findContainer(app,
pageModel,
containerKey);
final Styles styles = container.getStyles();
final Rule rule = findRule(pageModel, container, ruleIdParam);
stylesManager.removeRuleFromStyles(rule, styles);
}
/**
* An utility method for finding a {@link MediaRule}.
*
* @param appPath The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
* @param mediaRuleIdParam The ID of the {@link MediaRule} to find.
*
* @return The {@link MediaRule} with the provided {@code mediaRuleId}.
*/
protected MediaRule findMediaRule(final String appPath,
final String pageModelName,
final String containerKey,
final String mediaRuleIdParam) {
final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appPath));
final PageModel pageModel = controller.findPageModel(app,
pageModelName);
final ContainerModel container = controller.findContainer(app,
pageModel,
containerKey);
return findMediaRule(pageModel, container, mediaRuleIdParam);
}
/**
* An utility method for finding a {@link MediaRule}.
*
* @param pageModel The {@link PageModel} to which the
* {@link ContainerModel} belongs.
* @param container The {@link ContainerModel} to which the
* {@link MediaRule} belongs.
* @param mediaRuleIdParam The ID of the {@link MediaRule} to find.
*
* @return The {@link MediaRule} with the ID {@code mediaRuleIdParam}.
*/
private MediaRule findMediaRule(final PageModel pageModel,
final ContainerModel container,
final String mediaRuleIdParam) {
final Styles styles = container.getStyles();
final long mediaRuleId;
try {
mediaRuleId = Long.parseLong(mediaRuleIdParam);
} catch (NumberFormatException ex) {
throw new WebApplicationException(String.format(
"The provided mediaRuleId \"%s\" numeric.", mediaRuleIdParam));
}
return styles
.getMediaRules()
.stream()
.filter(mediaRule -> mediaRuleId == mediaRule.getMediaRuleId())
.findAny()
.orElseThrow(() -> new NotFoundException(String.format(
"No MediaRule with ID %d available in the Styles for "
+ "Container \"%s\" of PageModel \"%s\".",
mediaRuleId,
container.getKey(),
pageModel.getName())));
}
/**
* Utility method for finding a {@link Rule}.
*
* @param appPath The primary URL of the {@link CcmApplication}.
* @param pageModelName The name of the {@link PageModel}.
* @param containerKey The key of the {@link ContainerModel}.
* @param ruleIdParam The ID of the {@link Rule} to find.
*
* @return The {@link Rule} identified by {@code ruleIdParam}.
*/
protected Rule findRule(final String appPath,
final String pageModelName,
final String containerKey,
final String ruleIdParam) {
final CcmApplication app = controller.findCcmApplication(
String.format("/%s/", appPath));
final PageModel pageModel = controller.findPageModel(app,
pageModelName);
final ContainerModel container = controller.findContainer(app,
pageModel,
containerKey);
return findRule(pageModel, container, ruleIdParam);
}
/**
* An utility method for finding a {@link Rule}.
*
* @param pageModel The {@link PageModel} to which the
* {@link ContainerModel} belongs.
* @param container The {@link ContainerModel} to which the {@link Rule}
* belongs.
* @param ruleIdParam The ID of the {@link Rule} to find.
*
* @return The {@link Rule} with the ID {@code ruleIdParam}.
*/
private Rule findRule(final PageModel pageModel,
final ContainerModel container,
final String ruleIdParam) {
final Styles styles = container.getStyles();
final long ruleId;
try {
ruleId = Long.parseLong(ruleIdParam);
} catch (NumberFormatException ex) {
throw new WebApplicationException(String.format(
"The provided mediaRuleId \"%s\" numeric.", ruleIdParam));
}
return styles
.getRules()
.stream()
.filter(rule -> ruleId == rule.getRuleId())
.findAny()
.orElseThrow(() -> new NotFoundException(String.format(
"No Rule with ID %d available in the Styles for "
+ "Container \"%s\" of PageModel \"%s\".",
ruleId,
container.getKey(),
pageModel.getName())));
}
/**
* Helper method for setting the values of the properties of a
* {@link MediaRule} using the data from a JSON object.
*
* @param mediaRuleData The JSON object providing the data.
* @param mediaRule The {@link MediaRule}.
*/
private void setMediaRuleProperties(final JsonObject mediaRuleData,
final MediaRule mediaRule) {
Objects.requireNonNull(mediaRuleData);
Objects.requireNonNull(mediaRule);
final JsonObject mediaQueryData = mediaRuleData
.getJsonObject("mediaQuery");
final JsonObject maxWidthData = mediaQueryData
.getJsonObject("maxWidth");
final JsonObject minWidthData = mediaQueryData
.getJsonObject("minWidth");
final Dimension maxWidth = new Dimension();
maxWidth.setUnit(Unit.valueOf(maxWidthData.getString("unit")));
maxWidth.setValue(maxWidthData.getJsonNumber("value").doubleValue());
final MediaType mediaType = MediaType.valueOf(mediaQueryData
.getString("mediaType"));
final Dimension minWidth = new Dimension();
minWidth.setUnit(Unit.valueOf(minWidthData.getString("unit")));
minWidth.setValue(minWidthData.getJsonNumber("minWidth").doubleValue());
mediaRule.getMediaQuery().setMaxWidth(maxWidth);
mediaRule.getMediaQuery().setMediaType(mediaType);
mediaRule.getMediaQuery().setMinWidth(minWidth);
}
}

View File

@ -1,355 +0,0 @@
/*
* Copyright (C) 2018 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.pagemodel.rs;
import org.libreccm.core.CoreConstants;
import org.libreccm.pagemodel.ContainerModel;
import org.libreccm.pagemodel.styles.CssProperty;
import org.libreccm.pagemodel.styles.Rule;
import org.libreccm.pagemodel.styles.StylesManager;
import org.libreccm.pagemodel.styles.StylesRepository;
import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.RequiresPrivilege;
import org.libreccm.web.CcmApplication;
import java.io.Serializable;
import java.util.Objects;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.transaction.Transactional;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
/**
* Provides RESTful endpoints for retrieving, creating, updating and deleting
* {@link Rule} objects.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Path(StylesRs.RULE_PATH)
public class StylesRule implements Serializable {
private static final long serialVersionUID = -8447970787677773230L;
protected final static String PROPERTY_ID = "propertyId";
protected final static String PROPERTIES_PATH = "/properties";
protected final static String PROPERTY_PATH = PROPERTIES_PATH
+ "/{"
+ PROPERTY_ID
+ "}";
@Inject
private StylesJsonMapper stylesJsonMapper;
@Inject
private StylesManager stylesManager;
@Inject
private StylesRepository stylesRepo;
@Inject
private StylesRs stylesRs;
/**
* Retrieves all {@link CssProperty} objects of a {@link Rule} assigned to
* the {@link Styles} entity of a {@link ContainerModel}.
*
* @param appPath The primary URL of the {@link CcmApplication} to
* which the {@link PageModel} belongs.
* @param pageModelName The name of the {@link PageModel} to which the
* {@link ContainerModel} belongs.
* @param containerKey The key of the {@link ContainerModel} to which the
* {@link Rule} belongs.
* @param ruleIdParam The ID of the {@link Rule} from which the
* {@link CssProperty} objects are retrieved.
*
* @return A JSON array with the JSON representation of the
* {@link CssProperty} objects of the {@link Rule} identified by the
* provided path.
*/
@GET
@Path(PROPERTIES_PATH)
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonArray getProperties(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(StylesRs.RULE_ID) final String ruleIdParam) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(ruleIdParam);
final Rule rule = stylesRs.findRule(appPath,
pageModelName,
containerKey,
ruleIdParam);
return stylesJsonMapper.mapPropertiesToJson(rule.getProperties());
}
/**
* Retrieves a specific {@link CssProperty} from a {@link Rule} assigned to
* the {@link Styles} entity of a {@link ContainerModel}.
*
* @param appPath The primary URL of the {@link CcmApplication} to
* which the {@link PageModel} belongs.
* @param pageModelName The name of the {@link PageModel} to which the
* {@link ContainerModel} belongs.
* @param containerKey The key of the {@link ContainerModel} to which the
* {@link Rule} belongs.
* @param ruleIdParam The ID of the {@link Rule} to which the
* {@link CssProperty} is assigned.
* @param propertyIdParam The ID of the {@link CssProperty} to retrieve.
*
* @return The JSON representation of the {@link CssProperty}.
*/
@GET
@Path(PROPERTY_PATH)
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject getProperty(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(StylesRs.RULE_ID) final String ruleIdParam,
@PathParam(PROPERTY_ID) final String propertyIdParam) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(ruleIdParam);
Objects.requireNonNull(propertyIdParam);
final Rule rule = stylesRs.findRule(appPath,
pageModelName,
containerKey,
ruleIdParam);
return stylesJsonMapper
.mapCssPropertyToJson(findProperty(rule,
propertyIdParam));
}
/**
* Creates a new {@link CssProperty} for a {@link Rule} assigned to the
* {@link Styles} entity of a {@link ContainerModel}.
*
* @param appPath The primary URL of the {@link CcmApplication} to
* which the {@link PageModel} belongs.
* @param pageModelName The name of the {@link PageModel} to which the
* {@link ContainerModel} belongs.
* @param containerKey The key of the {@link ContainerModel} to which the
* {@link Rule} belongs.
* @param ruleIdParam The ID of the {@link Rule} to which the
* {@link CssProperty} is assigned.
* @param propertyData The data used to create the new {@link CssProperty}.
*
* @return The JSON representation of the new {@link CssProperty}.
*/
@POST
@Path(PROPERTIES_PATH)
@Consumes("application/json; charset=utf-8")
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject createProperty(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(StylesRs.RULE_ID) final String ruleIdParam,
final JsonObject propertyData) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(ruleIdParam);
Objects.requireNonNull(propertyData);
final Rule rule = stylesRs.findRule(appPath,
pageModelName,
containerKey,
ruleIdParam);
final CssProperty property = new CssProperty();
setCssPropertyData(property, propertyData);
stylesManager.addCssPropertyToRule(property, rule);
return stylesJsonMapper.mapCssPropertyToJson(property);
}
/**
* Updates an existing {@link CssProperty} for a {@link Rule} assigned to
* the {@link Styles} entity of a {@link ContainerModel}.
*
* @param appPath The primary URL of the {@link CcmApplication} to
* which the {@link PageModel} belongs.
* @param pageModelName The name of the {@link PageModel} to which the
* {@link ContainerModel} belongs.
* @param containerKey The key of the {@link ContainerModel} to which the
* {@link Rule} belongs.
* @param ruleIdParam The ID of the {@link Rule} to which the
* {@link CssProperty} is assigned.
* @param propertyIdParam The ID of the {@link CssProperty} to update.
* @param propertyData The data used to update the {@link CssProperty}.
*
* @return The JSON representation of the updated {@link CssProperty}.
*/
@PUT
@Path(PROPERTY_PATH)
@Consumes("application/json; charset=utf-8")
@Produces("application/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public JsonObject updateProperty(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(StylesRs.RULE_ID) final String ruleIdParam,
@PathParam(PROPERTY_ID) final String propertyIdParam,
final JsonObject propertyData) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(ruleIdParam);
Objects.requireNonNull(propertyIdParam);
Objects.requireNonNull(propertyData);
final Rule rule = stylesRs.findRule(appPath,
pageModelName,
containerKey,
ruleIdParam);
final CssProperty property = findProperty(rule, propertyIdParam);
setCssPropertyData(property, propertyData);
stylesRepo.saveCssProperty(property);
return stylesJsonMapper.mapCssPropertyToJson(property);
}
/**
* Deletes{@link CssProperty} for a {@link Rule} assigned to the
* {@link Styles} entity of a {@link ContainerModel}.
*
* @param appPath The primary URL of the {@link CcmApplication} to
* which the {@link PageModel} belongs.
* @param pageModelName The name of the {@link PageModel} to which the
* {@link ContainerModel} belongs.
* @param containerKey The key of the {@link ContainerModel} to which the
* {@link Rule} belongs.
* @param ruleIdParam The ID of the {@link Rule} to which the
* {@link CssProperty} is assigned.
* @param propertyIdParam The ID of the {@link CssProperty} to delete.
*/
@DELETE
@Path(PROPERTY_PATH)
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
public void deleteProperty(
@PathParam(PageModelsApp.APP_NAME) final String appPath,
@PathParam(PageModelsApp.PAGE_MODEL_NAME) final String pageModelName,
@PathParam(PageModelsApp.CONTAINER_KEY) final String containerKey,
@PathParam(StylesRs.RULE_ID) final String ruleIdParam,
@PathParam(PROPERTY_ID) final String propertyIdParam) {
Objects.requireNonNull(appPath);
Objects.requireNonNull(pageModelName);
Objects.requireNonNull(containerKey);
Objects.requireNonNull(ruleIdParam);
Objects.requireNonNull(propertyIdParam);
final Rule rule = stylesRs.findRule(appPath,
pageModelName,
containerKey,
ruleIdParam);
final CssProperty property = findProperty(rule, propertyIdParam);
stylesManager.removeCssPropertyFromRule(property, rule);
stylesRepo.deleteCssProperty(property);
}
/**
* Helper method for finding a {@link CssProperty} assigned to {@link Rule}.
*
* @param rule The {@link Rule}.
* @param propertyIdParam The ID of the {@link CssProperty} to find.
*
* @return The {@link CssProperty} identified by {@code propertyIdParam}.
*/
private CssProperty findProperty(final Rule rule,
final String propertyIdParam) {
Objects.requireNonNull(rule);
Objects.requireNonNull(propertyIdParam);
final long propertyId;
try {
propertyId = Long.parseLong(propertyIdParam);
} catch (NumberFormatException ex) {
throw new WebApplicationException(ex);
}
return rule
.getProperties()
.stream()
.filter(property -> propertyId == property.getPropertyId())
.findAny()
.orElseThrow(() -> new NotFoundException());
}
/**
* Helper method for updating a {@link CssProperty} object with data from
* its JSON representation.
*
* @param property The {@link CssProperty}.
* @param propertyData The data.
*/
private void setCssPropertyData(final CssProperty property,
final JsonObject propertyData) {
Objects.requireNonNull(property);
Objects.requireNonNull(propertyData);
property.setName(propertyData.getString("name"));
property.setValue(propertyData.getString("value"));
}
}

File diff suppressed because it is too large Load Diff