#79: POST /theme/color to change the main color

This commit is contained in:
jendib 2016-04-18 00:00:46 +02:00
parent 55cdca0c7d
commit 4e768e9103
11 changed files with 138 additions and 36 deletions

View File

@ -10,4 +10,8 @@ public enum ConfigType {
* Lucene directory storage type. * Lucene directory storage type.
*/ */
LUCENE_DIRECTORY_STORAGE, LUCENE_DIRECTORY_STORAGE,
/**
* Theme configuration.
*/
THEME
} }

View File

@ -33,4 +33,23 @@ public class ConfigDao {
return null; return null;
} }
} }
/**
* Updates a configuration parameter.
*
* @param id Configuration parameter ID
* @param value Configuration parameter value
*/
public void update(ConfigType id, String value) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
Config config = getById(id);
if (config == null) {
config = new Config();
config.setId(id);
config.setValue(value);
em.persist(config);
} else {
config.setValue(value);
}
}
} }

View File

@ -21,9 +21,7 @@ public class ContributorDao {
* Creates a new contributor. * Creates a new contributor.
* *
* @param contributor Contributor * @param contributor Contributor
* @param userId User ID
* @return New ID * @return New ID
* @throws Exception
*/ */
public String create(Contributor contributor) { public String create(Contributor contributor) {
// Create the UUID // Create the UUID
@ -72,7 +70,7 @@ public class ContributorDao {
int i = 0; int i = 0;
ContributorDto contributorDto = new ContributorDto(); ContributorDto contributorDto = new ContributorDto();
contributorDto.setUsername((String) o[i++]); contributorDto.setUsername((String) o[i++]);
contributorDto.setEmail((String) o[i++]); contributorDto.setEmail((String) o[i]);
contributorDtoList.add(contributorDto); contributorDtoList.add(contributorDto);
} }
return contributorDtoList; return contributorDtoList;

View File

@ -25,7 +25,6 @@ public class FileDao {
* @param file File * @param file File
* @param userId User ID * @param userId User ID
* @return New ID * @return New ID
* @throws Exception
*/ */
public String create(File file, String userId) { public String create(File file, String userId) {
// Create the UUID // Create the UUID
@ -169,7 +168,7 @@ public class FileDao {
/** /**
* Get files by document ID or all orphan files of an user. * Get files by document ID or all orphan files of an user.
* *
* @parma userId User ID * @param userId User ID
* @param documentId Document ID * @param documentId Document ID
* @return List of files * @return List of files
*/ */

View File

@ -108,7 +108,7 @@ public class TagDao {
sb.append(" where dt.DOT_IDDOCUMENT_C = :documentId and t.TAG_DELETEDATE_D is null "); sb.append(" where dt.DOT_IDDOCUMENT_C = :documentId and t.TAG_DELETEDATE_D is null ");
sb.append(" and t.TAG_IDUSER_C = :userId and dt.DOT_DELETEDATE_D is null "); sb.append(" and t.TAG_IDUSER_C = :userId and dt.DOT_DELETEDATE_D is null ");
sb.append(" order by t.TAG_NAME_C "); sb.append(" order by t.TAG_NAME_C ");
// Perform the query // Perform the query
Query q = em.createNativeQuery(sb.toString()); Query q = em.createNativeQuery(sb.toString());
q.setParameter("documentId", documentId); q.setParameter("documentId", documentId);
@ -116,14 +116,14 @@ public class TagDao {
List<Object[]> l = q.getResultList(); List<Object[]> l = q.getResultList();
// Assemble results // Assemble results
List<TagDto> tagDtoList = new ArrayList<TagDto>(); List<TagDto> tagDtoList = new ArrayList<>();
for (Object[] o : l) { for (Object[] o : l) {
int i = 0; int i = 0;
TagDto tagDto = new TagDto(); TagDto tagDto = new TagDto();
tagDto.setId((String) o[i++]); tagDto.setId((String) o[i++]);
tagDto.setName((String) o[i++]); tagDto.setName((String) o[i++]);
tagDto.setColor((String) o[i++]); tagDto.setColor((String) o[i++]);
tagDto.setParentId((String) o[i++]); tagDto.setParentId((String) o[i]);
tagDtoList.add(tagDto); tagDtoList.add(tagDto);
} }
return tagDtoList; return tagDtoList;
@ -132,7 +132,7 @@ public class TagDao {
/** /**
* Returns stats on tags. * Returns stats on tags.
* *
* @param documentId Document ID * @param userId User ID
* @return Stats by tag * @return Stats by tag
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -145,14 +145,14 @@ public class TagDao {
sb.append(" where t.TAG_IDUSER_C = :userId and t.TAG_DELETEDATE_D is null "); sb.append(" where t.TAG_IDUSER_C = :userId and t.TAG_DELETEDATE_D is null ");
sb.append(" group by t.TAG_ID_C "); sb.append(" group by t.TAG_ID_C ");
sb.append(" order by t.TAG_NAME_C "); sb.append(" order by t.TAG_NAME_C ");
// Perform the query // Perform the query
Query q = em.createNativeQuery(sb.toString()); Query q = em.createNativeQuery(sb.toString());
q.setParameter("userId", userId); q.setParameter("userId", userId);
List<Object[]> l = q.getResultList(); List<Object[]> l = q.getResultList();
// Assemble results // Assemble results
List<TagStatDto> tagStatDtoList = new ArrayList<TagStatDto>(); List<TagStatDto> tagStatDtoList = new ArrayList<>();
for (Object[] o : l) { for (Object[] o : l) {
int i = 0; int i = 0;
TagStatDto tagDto = new TagStatDto(); TagStatDto tagDto = new TagStatDto();
@ -160,7 +160,7 @@ public class TagDao {
tagDto.setName((String) o[i++]); tagDto.setName((String) o[i++]);
tagDto.setColor((String) o[i++]); tagDto.setColor((String) o[i++]);
tagDto.setParentId((String) o[i++]); tagDto.setParentId((String) o[i++]);
tagDto.setCount(((Number) o[i++]).intValue()); tagDto.setCount(((Number) o[i]).intValue());
tagStatDtoList.add(tagDto); tagStatDtoList.add(tagDto);
} }
return tagStatDtoList; return tagStatDtoList;
@ -172,7 +172,6 @@ public class TagDao {
* @param tag Tag * @param tag Tag
* @param userId User ID * @param userId User ID
* @return New ID * @return New ID
* @throws Exception
*/ */
public String create(Tag tag, String userId) { public String create(Tag tag, String userId) {
// Create the UUID // Create the UUID

View File

@ -11,7 +11,7 @@ public class ThreadLocalContext {
/** /**
* ThreadLocal to store the context. * ThreadLocal to store the context.
*/ */
public static final ThreadLocal<ThreadLocalContext> threadLocalContext = new ThreadLocal<ThreadLocalContext>(); private static final ThreadLocal<ThreadLocalContext> threadLocalContext = new ThreadLocal<>();
/** /**
* Entity manager. * Entity manager.
@ -46,15 +46,6 @@ public class ThreadLocalContext {
threadLocalContext.set(null); threadLocalContext.set(null);
} }
/**
* Returns true only if the entity manager is defined.
*
* @return Condition
*/
public boolean isInTransactionalContext() {
return entityManager != null;
}
/** /**
* Getter of entityManager. * Getter of entityManager.
* *

View File

@ -108,10 +108,9 @@ public class ValidationUtil {
* @param s String to validate * @param s String to validate
* @param name Name of the parameter * @param name Name of the parameter
* @param nullable True if the string can be empty or null * @param nullable True if the string can be empty or null
* @throws JSONException
*/ */
public static void validateHexColor(String s, String name, boolean nullable) throws ClientException { public static void validateHexColor(String s, String name, boolean nullable) throws ClientException {
ValidationUtil.validateLength(s, "name", 7, 7, nullable); ValidationUtil.validateLength(s, name, 7, 7, nullable);
} }
/** /**

View File

@ -57,9 +57,8 @@ public abstract class BaseResource {
* Checks if the user has a base function. Throw an exception if the check fails. * Checks if the user has a base function. Throw an exception if the check fails.
* *
* @param baseFunction Base function to check * @param baseFunction Base function to check
* @throws JSONException
*/ */
protected void checkBaseFunction(BaseFunction baseFunction) { void checkBaseFunction(BaseFunction baseFunction) {
if (!hasBaseFunction(baseFunction)) { if (!hasBaseFunction(baseFunction)) {
throw new ForbiddenClientException(); throw new ForbiddenClientException();
} }
@ -70,9 +69,8 @@ public abstract class BaseResource {
* *
* @param baseFunction Base function to check * @param baseFunction Base function to check
* @return True if the user has the base function * @return True if the user has the base function
* @throws JSONException
*/ */
protected boolean hasBaseFunction(BaseFunction baseFunction) { boolean hasBaseFunction(BaseFunction baseFunction) {
if (principal == null || !(principal instanceof UserPrincipal)) { if (principal == null || !(principal instanceof UserPrincipal)) {
return false; return false;
} }
@ -86,7 +84,7 @@ public abstract class BaseResource {
* @param shareId Share ID (optional) * @param shareId Share ID (optional)
* @return List of ACL target ID * @return List of ACL target ID
*/ */
protected List<String> getTargetIdList(String shareId) { List<String> getTargetIdList(String shareId) {
List<String> targetIdList = Lists.newArrayList(principal.getGroupIdSet()); List<String> targetIdList = Lists.newArrayList(principal.getGroupIdSet());
if (principal.getId() != null) { if (principal.getId() != null) {
targetIdList.add(principal.getId()); targetIdList.add(principal.getId());

View File

@ -64,7 +64,6 @@ public class TagResource extends BaseResource {
* Returns stats on tags. * Returns stats on tags.
* *
* @return Response * @return Response
* @throws JSONException
*/ */
@GET @GET
@Path("/stats") @Path("/stats")

View File

@ -1,12 +1,21 @@
package com.sismics.docs.rest.resource; package com.sismics.docs.rest.resource;
import javax.ws.rs.GET; import javax.json.*;
import javax.ws.rs.Path; import javax.ws.rs.*;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import com.google.common.base.Strings;
import com.sismics.docs.core.constant.ConfigType;
import com.sismics.docs.core.dao.jpa.ConfigDao;
import com.sismics.docs.core.model.jpa.Config;
import com.sismics.docs.rest.constant.BaseFunction;
import com.sismics.rest.exception.ForbiddenClientException;
import com.sismics.rest.util.ValidationUtil;
import com.sismics.util.css.Selector; import com.sismics.util.css.Selector;
import java.io.StringReader;
import java.util.Map;
/** /**
* Theme REST resources. * Theme REST resources.
* *
@ -23,8 +32,75 @@ public class ThemeResource extends BaseResource {
@Path("/stylesheet") @Path("/stylesheet")
@Produces("text/css") @Produces("text/css")
public Response stylesheet() { public Response stylesheet() {
JsonObject themeConfig = getThemeConfig();
// Build the stylesheet
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(new Selector("body")); sb.append(new Selector(".navbar")
.rule("background-color", themeConfig.getString("color", "inherit")));
return Response.ok().entity(sb.toString()).build(); return Response.ok().entity(sb.toString()).build();
} }
@POST
@Path("/color")
public Response color(@FormParam("color") String color) {
if (!authenticate()) {
throw new ForbiddenClientException();
}
checkBaseFunction(BaseFunction.ADMIN);
// Validate input data
ValidationUtil.validateHexColor(color, "color", true);
// Update the JSON
JsonObjectBuilder json = getMutableThemeConfig();
if (Strings.isNullOrEmpty(color)) {
json.add("color", JsonValue.NULL);
} else {
json.add("color", color);
}
// Persist the new configuration
ConfigDao configDao = new ConfigDao();
configDao.update(ConfigType.THEME, json.build().toString());
// Always return OK
JsonObjectBuilder response = Json.createObjectBuilder()
.add("status", "ok");
return Response.ok().entity(response.build()).build();
}
/**
* Returns the theme configuration object.
*
* @return Theme configuration
*/
private JsonObject getThemeConfig() {
ConfigDao configDao = new ConfigDao();
Config themeConfig = configDao.getById(ConfigType.THEME);
if (themeConfig == null) {
return Json.createObjectBuilder().build();
}
try (JsonReader reader = Json.createReader(new StringReader(themeConfig.getValue()))) {
return reader.readObject();
}
}
/**
* Returns a mutable theme configuration.
*
* @return Json builder
*/
private JsonObjectBuilder getMutableThemeConfig() {
JsonObject themeConfig = getThemeConfig();
JsonObjectBuilder json = Json.createObjectBuilder();
for (Map.Entry<String, JsonValue> entry : themeConfig.entrySet()) {
json.add(entry.getKey(), entry.getValue());
}
return json;
}
} }

View File

@ -1,7 +1,13 @@
package com.sismics.docs.rest; package com.sismics.docs.rest;
import com.sismics.util.filter.TokenBasedSecurityFilter;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import javax.json.JsonObject;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Form;
/** /**
* Test the theme resource. * Test the theme resource.
* *
@ -13,9 +19,23 @@ public class TestThemeResource extends BaseJerseyTest {
*/ */
@Test @Test
public void testThemeResource() { public void testThemeResource() {
// Login admin
String adminToken = clientUtil.login("admin", "admin", false);
// Get the stylesheet anonymously // Get the stylesheet anonymously
String stylesheet = target().path("/theme/stylesheet").request() String stylesheet = target().path("/theme/stylesheet").request()
.get(String.class); .get(String.class);
System.out.println(stylesheet); Assert.assertTrue(stylesheet.contains("background-color: inherit;"));
// Update the main color as admin
target().path("/theme/color").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
.post(Entity.form(new Form()
.param("color", "#ff0000")), JsonObject.class);
// Get the stylesheet anonymously
stylesheet = target().path("/theme/stylesheet").request()
.get(String.class);
Assert.assertTrue(stylesheet.contains("background-color: #ff0000;"));
} }
} }