diff --git a/.travis.yml b/.travis.yml index e358d2576..9bcf99945 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,3 @@ language: java jdk: - - oraclejdk7 + - oraclejdk8 diff --git a/README.md b/README.md index 0067392c6..360cee1bd 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Bennu covers the following core features: mvn archetype:generate \ -DarchetypeGroupId=org.fenixedu \ -DarchetypeArtifactId=bennu-webapp-archetype \ - -DarchetypeVersion=3.2.0 \ + -DarchetypeVersion=3.3.0 \ -DarchetypeRepository=https://fenix-ashes.ist.utl.pt/nexus/content/groups/fenix-ashes-maven-repository ``` Make sure to supply the information required by the archetype. @@ -53,7 +53,7 @@ Make sure to supply the information required by the archetype. mvn archetype:generate \ -DarchetypeGroupId=org.fenixedu \ -DarchetypeArtifactId=bennu-project-archetype \ - -DarchetypeVersion=3.2.0 \ + -DarchetypeVersion=3.3.0 \ -DarchetypeRepository=https://fenix-ashes.ist.utl.pt/nexus/content/groups/fenix-ashes-maven-repository ``` diff --git a/bennu-portal/pom.xml b/bennu-portal/pom.xml index 277a2ec9e..65a528808 100644 --- a/bennu-portal/pom.xml +++ b/bennu-portal/pom.xml @@ -5,7 +5,7 @@ org.fenixedu bennu - 3.2.0 + 3.3.0 bennu-portal diff --git a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/BennuPortalConfiguration.java b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/BennuPortalConfiguration.java new file mode 100644 index 000000000..38804ca05 --- /dev/null +++ b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/BennuPortalConfiguration.java @@ -0,0 +1,22 @@ +package org.fenixedu.bennu.portal; + +import org.fenixedu.commons.configuration.ConfigurationInvocationHandler; +import org.fenixedu.commons.configuration.ConfigurationManager; +import org.fenixedu.commons.configuration.ConfigurationProperty; + +public class BennuPortalConfiguration { + + @ConfigurationManager(description = "Bennu Portal Configuration") + public static interface ConfigurationProperties { + + @ConfigurationProperty(key = "theme.development.mode", defaultValue = "false", + description = "Disables Theme Caching and allows live-reloading of themes") + public Boolean themeDevelopmentMode(); + + } + + public static ConfigurationProperties getConfiguration() { + return ConfigurationInvocationHandler.getConfiguration(ConfigurationProperties.class); + } + +} diff --git a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/client/ClientSidePortalBackend.java b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/client/ClientSidePortalBackend.java index 42b7a88c8..9d56b52f1 100644 --- a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/client/ClientSidePortalBackend.java +++ b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/client/ClientSidePortalBackend.java @@ -1,13 +1,8 @@ package org.fenixedu.bennu.portal.client; -import java.io.IOException; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; +import javax.servlet.RequestDispatcher; import javax.servlet.http.HttpServletResponse; -import org.fenixedu.bennu.portal.domain.MenuFunctionality; import org.fenixedu.bennu.portal.servlet.PortalBackend; import org.fenixedu.bennu.portal.servlet.SemanticURLHandler; @@ -17,14 +12,15 @@ public class ClientSidePortalBackend implements PortalBackend { @Override public SemanticURLHandler getSemanticURLHandler() { - return new SemanticURLHandler() { - @Override - public void handleRequest(MenuFunctionality functionality, HttpServletRequest request, HttpServletResponse response, - FilterChain chain) throws IOException, ServletException { - String forwardUrl = - "/" + functionality.getParent().getPath() + "/" - + (functionality.getPath().startsWith("#") ? "" : functionality.getPath()); - request.getRequestDispatcher(forwardUrl).forward(request, response); + return (functionality, request, response, chain) -> { + String forwardUrl = + "/" + functionality.getParent().getPath() + "/" + + (functionality.getPath().startsWith("#") ? "" : functionality.getPath()); + RequestDispatcher requestDispatcher = request.getRequestDispatcher(forwardUrl); + if (requestDispatcher != null) { + requestDispatcher.forward(request, response); + } else { + response.sendError(HttpServletResponse.SC_NOT_FOUND, "No forward url could be processed"); } }; } diff --git a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/domain/MenuContainer.java b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/domain/MenuContainer.java index f1353471f..8e8e2da92 100644 --- a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/domain/MenuContainer.java +++ b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/domain/MenuContainer.java @@ -3,6 +3,8 @@ import java.util.Collections; import java.util.Set; import java.util.TreeSet; +import java.util.stream.Collector; +import java.util.stream.Stream; import org.fenixedu.bennu.core.domain.User; import org.fenixedu.bennu.portal.model.Application; @@ -131,6 +133,14 @@ public Set getOrderedChild() { return Collections.unmodifiableSet(new TreeSet<>(getChildSet())); } + /** + * Returns a {@link Set} containing all the children of this container, that are available to the + * current user. + * + * @deprecated + * Use {@link MenuContainer#getUserMenuStream()} and apply a {@link Collector}. + */ + @Deprecated public Set getUserMenu() { return FluentIterable.from(getChildSet()).filter(new Predicate() { @Override @@ -143,7 +153,11 @@ public boolean apply(MenuItem menu) { /** * Returns an {@link Iterable} containing all the child {@link MenuContainer}s of this container, that are available to the * current user. + * + * @deprecated + * Use {@link MenuContainer#getUserMenuStream()} and apply another filter */ + @Deprecated public Iterable getAvailableChildContainers() { return FluentIterable.from(getChildSet()).filter(MenuContainer.class).filter(new Predicate() { @Override @@ -153,6 +167,17 @@ public boolean apply(MenuContainer container) { }).toSortedList(Ordering.natural()); } + /** + * Returns the User Menu as a lazy {@link Stream}. This method is preferred + * to the alternatives (returning {@link Set}), as it allows further optimizations. + * + * @return + * The User's Menu as a Stream + */ + public Stream getUserMenuStream() { + return getChildSet().stream().filter((item) -> item.isVisible() && item.isItemAvailableForCurrentUser()).sorted(); + } + /** * Deletes this container, as well as all its children. */ diff --git a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/domain/PortalConfiguration.java b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/domain/PortalConfiguration.java index 7e41867f5..c32ed4135 100644 --- a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/domain/PortalConfiguration.java +++ b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/domain/PortalConfiguration.java @@ -35,28 +35,25 @@ private PortalConfiguration() { setApplicationCopyright(new LocalizedString(I18N.getLocale(), "Organization Copyright")); setHtmlTitle(getApplicationTitle()); setTheme("default"); - InputStream stream = this.getClass().getResourceAsStream("/img/bennu-logo.png"); - if (stream == null) { - logger.error("Default logo not found in: img/bennu-logo.png"); - } else { - try { + try (InputStream stream = this.getClass().getResourceAsStream("/img/logo_bennu.svg")) { + if (stream == null) { + logger.error("Default logo not found in: img/logo_bennu.svg"); + } else { setLogo(ByteStreams.toByteArray(stream)); - setLogoType("image/png"); - } catch (IOException e) { - logger.error("Default logo could not be read from: img/bennu-logo.png"); + setLogoType("image/svg+xml"); } + } catch (IOException e) { + logger.error("Default logo could not be read from: img/logo_bennu.svg"); } - - stream = this.getClass().getResourceAsStream("/img/bennu-favicon.png"); - if (stream == null) { - logger.error("Default favicon not found in: img/bennu-favicon.png"); - } else { - try { + try (InputStream stream = this.getClass().getResourceAsStream("/img/favicon_bennu.png")) { + if (stream == null) { + logger.error("Default favicon not found in: img/favicon_bennu.png"); + } else { setFavicon(ByteStreams.toByteArray(stream)); setFaviconType("image/png"); - } catch (IOException e) { - logger.error("Default logo could not be read from: img/bennu-favicon.png"); } + } catch (IOException e) { + logger.error("Default logo could not be read from: img/favicon_bennu.png"); } new MenuContainer(this); } diff --git a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/rest/json/PortalMenuViewer.java b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/rest/json/PortalMenuViewer.java index 6fd5e181b..473541dca 100644 --- a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/rest/json/PortalMenuViewer.java +++ b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/rest/json/PortalMenuViewer.java @@ -11,7 +11,7 @@ public class PortalMenuViewer extends PortalConfigurationAdapter { public JsonElement view(PortalConfiguration configuration, JsonBuilder ctx) { final JsonObject view = super.view(configuration, ctx).getAsJsonObject(); if (configuration.getMenu() != null) { - view.add("menu", ctx.view(configuration.getMenu().getUserMenu())); + view.add("menu", ctx.view(configuration.getMenu().getUserMenuStream())); } return view; } diff --git a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/rest/json/UserMenuViewer.java b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/rest/json/UserMenuViewer.java index 171d742fb..fc0f57fc7 100644 --- a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/rest/json/UserMenuViewer.java +++ b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/rest/json/UserMenuViewer.java @@ -27,7 +27,7 @@ public JsonElement view(MenuItem obj, JsonBuilder ctx) { if (container.isRoot()) { json.add("title", ctx.view(PortalConfiguration.getInstance().getApplicationTitle())); } - json.add("menu", ctx.view(container.getUserMenu())); + json.add("menu", ctx.view(container.getUserMenuStream())); } return json; } diff --git a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/ForwarderPortalBackend.java b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/ForwarderPortalBackend.java index b6ca35ada..72f6d9b5a 100644 --- a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/ForwarderPortalBackend.java +++ b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/ForwarderPortalBackend.java @@ -1,10 +1,6 @@ package org.fenixedu.bennu.portal.servlet; -import java.io.IOException; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; +import javax.servlet.RequestDispatcher; import javax.servlet.http.HttpServletResponse; import org.fenixedu.bennu.portal.domain.MenuFunctionality; @@ -18,17 +14,18 @@ */ public class ForwarderPortalBackend implements PortalBackend { - private static final SemanticURLHandler HANDLER = new SemanticURLHandler() { - @Override - public void handleRequest(MenuFunctionality functionality, HttpServletRequest request, HttpServletResponse response, - FilterChain chain) throws IOException, ServletException { - request.getRequestDispatcher(functionality.getItemKey()).forward(request, response); - } - }; - @Override public SemanticURLHandler getSemanticURLHandler() { - return HANDLER; + return (functionality, request, response, chain) -> { + // Remove the functionality, allowing the target to choose the proper one + BennuPortalDispatcher.selectFunctionality(request, null); + RequestDispatcher requestDispatcher = request.getRequestDispatcher(functionality.getItemKey()); + if (requestDispatcher != null) { + requestDispatcher.forward(request, response); + } else { + response.sendError(HttpServletResponse.SC_NOT_FOUND, "No forward url could be processed"); + } + }; } @Override diff --git a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/LazyForTokenParser.java b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/LazyForTokenParser.java new file mode 100644 index 000000000..d7187d141 --- /dev/null +++ b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/LazyForTokenParser.java @@ -0,0 +1,136 @@ +package org.fenixedu.bennu.portal.servlet; + +import java.io.IOException; +import java.io.Writer; +import java.util.Arrays; +import java.util.Collection; +import java.util.stream.Stream; + +import com.mitchellbosecke.pebble.error.ParserException; +import com.mitchellbosecke.pebble.error.PebbleException; +import com.mitchellbosecke.pebble.extension.NodeVisitor; +import com.mitchellbosecke.pebble.lexer.Token; +import com.mitchellbosecke.pebble.lexer.TokenStream; +import com.mitchellbosecke.pebble.node.AbstractRenderableNode; +import com.mitchellbosecke.pebble.node.BodyNode; +import com.mitchellbosecke.pebble.node.RenderableNode; +import com.mitchellbosecke.pebble.node.expression.Expression; +import com.mitchellbosecke.pebble.template.EvaluationContext; +import com.mitchellbosecke.pebble.template.PebbleTemplateImpl; +import com.mitchellbosecke.pebble.tokenParser.AbstractTokenParser; +import com.mitchellbosecke.pebble.tokenParser.TokenParser; + +/** + * {@link TokenParser} for the 'lazyFor' node. + * + * This node allow lazy iteration over a {@link Stream}, without + * invoking extraneous terminal operations. In practice, this node + * is a wrapper for {@link Stream#forEach(java.util.function.Consumer)}. + * + * Unlike the regular 'for' node, no extra variables are defined, only + * the iteration variable, named in the token's usage. + * + * Accepted arguments are instance of {@link Stream}, {@link Collection} or arrays. + * + * Example: + * + *
+ *    {% lazyFor item in itemStream %}
+ *      {{item.name}}
+ *    {% endLazyFor %}
+ * 
+ * + * @author João Carvalho (joao.pedro.carvalho@tecnico.ulisboa.pt) + * + * @since 3.3 + * + */ +public class LazyForTokenParser extends AbstractTokenParser { + + @Override + public RenderableNode parse(Token token) throws ParserException { + TokenStream stream = this.parser.getStream(); + int lineNumber = token.getLineNumber(); + + // skip the 'lazyFor' token + stream.next(); + + // get the iteration variable + String iterationVariable = this.parser.getExpressionParser().parseNewVariableName(); + + stream.expect(Token.Type.NAME, "in"); + + // get the iterable variable + Expression iterable = this.parser.getExpressionParser().parseExpression(); + + stream.expect(Token.Type.EXECUTE_END); + + BodyNode body = this.parser.subparse((test) -> test.test(Token.Type.NAME, "endLazyFor")); + + // skip the 'endLazyFor' token + stream.next(); + + stream.expect(Token.Type.EXECUTE_END); + + return new ForNode(lineNumber, iterationVariable, iterable, body); + } + + private static final class ForNode extends AbstractRenderableNode { + + private final String variableName; + private final Expression iterableExpression; + private final BodyNode body; + + public ForNode(int lineNumber, String variableName, Expression iterableExpression, BodyNode body) { + super(lineNumber); + this.variableName = variableName; + this.iterableExpression = iterableExpression; + this.body = body; + } + + @Override + public void render(PebbleTemplateImpl self, Writer writer, EvaluationContext context) throws PebbleException, IOException { + Object value = iterableExpression.evaluate(self, context); + if (value == null) { + return; + } + + Stream stream = getStream(value); + + context.pushScope(); + stream.forEach((obj) -> { + try { + context.put(variableName, obj); + body.render(self, writer, context); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + context.popScope(); + } + + private Stream getStream(Object obj) { + if (obj instanceof Stream) { + return (Stream) obj; + } else if (obj instanceof Collection) { + return ((Collection) obj).stream(); + } else if (obj.getClass().isArray()) { + return Arrays.stream((Object[]) obj); + } else { + throw new ClassCastException(obj + " cannot be cast to Stream"); + } + } + + @Override + public void accept(NodeVisitor visitor) { + visitor.visit(this); + } + + } + + @Override + public String getTag() { + return "lazyFor"; + } + +} diff --git a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/PortalExceptionHandler.java b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/PortalExceptionHandler.java index 9a2c054b0..a7194a8d7 100644 --- a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/PortalExceptionHandler.java +++ b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/PortalExceptionHandler.java @@ -20,12 +20,12 @@ import org.fenixedu.bennu.core.security.Authenticate; import org.fenixedu.bennu.core.servlets.ExceptionHandlerFilter.ExceptionHandler; +import org.fenixedu.bennu.portal.BennuPortalConfiguration; import org.fenixedu.bennu.portal.domain.PortalConfiguration; import org.fenixedu.commons.i18n.I18N; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Joiner; import com.mitchellbosecke.pebble.PebbleEngine; import com.mitchellbosecke.pebble.error.LoaderException; import com.mitchellbosecke.pebble.error.PebbleException; @@ -66,6 +66,9 @@ public Reader getReader(String themeName) throws LoaderException { protected PortalExceptionHandler(Loader loader) { this.engine = new PebbleEngine(loader); engine.addExtension(new PortalExtension()); + if (BennuPortalConfiguration.getConfiguration().themeDevelopmentMode()) { + engine.setTemplateCache(null); + } } @Override @@ -118,7 +121,7 @@ private Object getParameters(HttpServletRequest req) { Enumeration names = req.getParameterNames(); while (names.hasMoreElements()) { String name = names.nextElement(); - params.put(name, Joiner.on(" | ").join(req.getParameterValues(name))); + params.put(name, String.join(" | ", req.getParameterValues(name))); } return params.entrySet(); } diff --git a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/PortalExtension.java b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/PortalExtension.java index 7909a8882..e0297af24 100644 --- a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/PortalExtension.java +++ b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/PortalExtension.java @@ -14,6 +14,7 @@ import com.mitchellbosecke.pebble.extension.Filter; import com.mitchellbosecke.pebble.extension.Function; import com.mitchellbosecke.pebble.extension.Test; +import com.mitchellbosecke.pebble.tokenParser.TokenParser; class PortalExtension extends AbstractExtension { @@ -87,4 +88,9 @@ public boolean apply(Object input, Map args) { } } + @Override + public List getTokenParsers() { + return Collections.singletonList(new LazyForTokenParser()); + } + } diff --git a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/PortalLayoutInjector.java b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/PortalLayoutInjector.java index 4b6705da7..057c06093 100644 --- a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/PortalLayoutInjector.java +++ b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/PortalLayoutInjector.java @@ -21,6 +21,7 @@ import org.fenixedu.bennu.core.security.Authenticate; import org.fenixedu.bennu.core.util.CoreConfiguration; +import org.fenixedu.bennu.portal.BennuPortalConfiguration; import org.fenixedu.bennu.portal.domain.MenuFunctionality; import org.fenixedu.bennu.portal.domain.MenuItem; import org.fenixedu.bennu.portal.domain.PortalConfiguration; @@ -71,6 +72,11 @@ public Reader getReader(String templateName) throws LoaderException { // Disable auto-escaping engine.getExtension(EscaperExtension.class).setAutoEscaping(false); engine.addExtension(new PortalExtension()); + + if (BennuPortalConfiguration.getConfiguration().themeDevelopmentMode()) { + logger.info("Theme Development Mode Enabled!"); + engine.setTemplateCache(null); + } } @Override diff --git a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/PortalLoginServlet.java b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/PortalLoginServlet.java index b89b03267..b40571193 100644 --- a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/PortalLoginServlet.java +++ b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/PortalLoginServlet.java @@ -16,6 +16,7 @@ import javax.servlet.http.HttpServletResponse; import org.fenixedu.bennu.core.util.CoreConfiguration; +import org.fenixedu.bennu.portal.BennuPortalConfiguration; import org.fenixedu.bennu.portal.domain.PortalConfiguration; import org.fenixedu.commons.i18n.I18N; @@ -102,7 +103,7 @@ public void showLoginPage(HttpServletRequest req, HttpServletResponse resp, Stri * @author João Carvalho (joao.pedro.carvalho@tecnico.ulisboa.pt) * */ - private static class LocalLoginStrategy implements PortalLoginStrategy { + public static class LocalLoginStrategy implements PortalLoginStrategy { private volatile PebbleEngine engine; @@ -147,6 +148,9 @@ public Reader getReader(String themeName) throws LoaderException { } }); pebble.addExtension(new PortalExtension()); + if (BennuPortalConfiguration.getConfiguration().themeDevelopmentMode()) { + pebble.setTemplateCache(null); + } this.engine = pebble; return pebble; } diff --git a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/RedirectPortalBackend.java b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/RedirectPortalBackend.java index c8d9b070c..74f42fe55 100644 --- a/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/RedirectPortalBackend.java +++ b/bennu-portal/src/main/java/org/fenixedu/bennu/portal/servlet/RedirectPortalBackend.java @@ -1,12 +1,5 @@ package org.fenixedu.bennu.portal.servlet; -import java.io.IOException; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import org.fenixedu.bennu.portal.domain.MenuFunctionality; /** @@ -18,17 +11,9 @@ */ public class RedirectPortalBackend implements PortalBackend { - private static final SemanticURLHandler HANDLER = new SemanticURLHandler() { - @Override - public void handleRequest(MenuFunctionality functionality, HttpServletRequest request, HttpServletResponse response, - FilterChain chain) throws IOException, ServletException { - response.sendRedirect(functionality.getItemKey()); - } - }; - @Override public SemanticURLHandler getSemanticURLHandler() { - return HANDLER; + return (functionality, request, response, chain) -> response.sendRedirect(functionality.getItemKey()); } @Override diff --git a/bennu-portal/src/main/resources/img/bennu-favicon.png b/bennu-portal/src/main/resources/img/bennu-favicon.png deleted file mode 100644 index 0d599177f..000000000 Binary files a/bennu-portal/src/main/resources/img/bennu-favicon.png and /dev/null differ diff --git a/bennu-portal/src/main/resources/img/bennu-logo.png b/bennu-portal/src/main/resources/img/bennu-logo.png deleted file mode 100644 index 8aa575508..000000000 Binary files a/bennu-portal/src/main/resources/img/bennu-logo.png and /dev/null differ diff --git a/bennu-portal/src/main/resources/img/favicon_bennu.png b/bennu-portal/src/main/resources/img/favicon_bennu.png new file mode 100644 index 000000000..dba6ebb86 Binary files /dev/null and b/bennu-portal/src/main/resources/img/favicon_bennu.png differ diff --git a/bennu-portal/src/main/resources/img/logo_bennu.svg b/bennu-portal/src/main/resources/img/logo_bennu.svg new file mode 100644 index 000000000..e42373739 --- /dev/null +++ b/bennu-portal/src/main/resources/img/logo_bennu.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bennu + + Framework + + diff --git a/bennu-portal/src/main/webapp/bennu-portal/debugExceptionPage.html b/bennu-portal/src/main/webapp/bennu-portal/debugExceptionPage.html index 905212834..4485836e5 100644 --- a/bennu-portal/src/main/webapp/bennu-portal/debugExceptionPage.html +++ b/bennu-portal/src/main/webapp/bennu-portal/debugExceptionPage.html @@ -1,190 +1,298 @@ - - {{i18n('resources.BennuPortalResources', 'label.error.occurred')}} - - - - - -
-
-
-

{{exception.class.name}}

- {{exception.localizedMessage}} -
-
-

Bennu Framework

- Development Mode -
-
-
-
-

Request Information

-
-
Request URI
-
{{request.requestURI}}
-
-
-
Request URL
-
{{request.requestURL}}
-
-
-
Query String
-
{{request.queryString}}
-
-
-
Method
-
{{request.method}}
-
-
-
User Agent
-
{{userAgent}}
-
-
-
Referer
-
{{referer}}
-
-
-
-

User Information

-
-
Username
-
{{loggedUser.username}}
-
-
-
Locale
-
{{locale}}
-
-
-
-

Request Parameters

- {% for attr in parameters %} -
-
{{attr.key}}
-
{{attr.value}}
-
- {% endfor %} -
-
-

Session Parameters

- {% for attr in session %} -
-
{{attr.key}}
-
{{attr.value}}
-
- {% endfor %} -
-
-

Request Attributes

- {% for attr in attributes %} -
-
{{attr.key}}
-
{{attr.value}}
-
- {% endfor %} -
- -
-

Exception Information

-
- {% for stack in throwableInfos %} -
- - {% if stack.cause %} - Caused by: - {% else %} -
Exception in thread {{threadName}}:
- {% endif %} -
- {{stack.subject.class.package.name}}.{{stack.subject.class.simpleName}}: {{stack.localizedMessage | raw}} -
- {% for element in stack.subjectInfo %} -
- at: - {{element.packageName}}.{{element.simpleClassName}}.{{element.element.methodName}} - - {% if element.element.fileName != null %} - ({{element.element.fileName}}:{{element.element.lineNumber}}) - {% else %} - -->) - {% endif %} - {% else %} - {% if element.element.nativeMethod %} - (Native Method) - {% else %} - (Unknown Source) - {% endif %} - {% endif %} -
- {% endfor %} - {% endfor %} -
-
-
-
- - + + {{i18n('resources.BennuPortalResources', 'label.error.occurred')}} + + + + + + +
+
+
+
+
+

{{exception.class.name}}

+ {{exception.localizedMessage}} +
+
+
+
+

Request Information

+ +
+
Request URI
+
{{request.requestURI}}
+
+
+
Request URL
+
{{request.requestURL}}
+
+
+
Query String
+
{{request.queryString}}
+
+
+
Method
+
{{request.method}}
+
+
+
User Agent
+
{{userAgent}}
+
+
+
Referer
+
{{referer}}
+
+
+
+

User Information

+ +
+
Username
+
{{loggedUser.username}}
+
+
+
Locale
+
{{locale}}
+
+
+
+

Request Parameters

+ {% for attr in parameters %} +
+
{{attr.key}}
+
{{attr.value}}
+
+ {% endfor %} +
+
+

Session Parameters

+ {% for attr in session %} +
+
{{attr.key}}
+
{{attr.value}}
+
+ {% endfor %} +
+
+

Request Attributes

+ {% for attr in attributes %} +
+
{{attr.key}}
+
{{attr.value}}
+
+ {% endfor %} +
+ +
+

Exception Information

+ +
+ {% for stack in throwableInfos %} +
+ + {% if stack.cause %} + Caused by: + {% else %} +
Exception in thread {{threadName}}:
+ {% endif %} +
+ {{stack.subject.class.package.name}}.{{stack.subject.class.simpleName}}: + {{stack.localizedMessage | raw}} +
+ {% for element in stack.subjectInfo %} +
+ at: + {{element.packageName}}.{{element.simpleClassName}}.{{element.element.methodName}} + + {% if element.element.fileName != null %} + + ({{element.element.fileName}}:{{element.element.lineNumber}}) + {% else %} + -->) + {% endif %} + {% else %} + {% if element.element.nativeMethod %} + (Native Method) + {% else %} + (Unknown Source) + {% endif %} + {% endif %} +
+ {% endfor %} + {% endfor %} +
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/bennu-portal/src/main/webapp/bennu-portal/login.html b/bennu-portal/src/main/webapp/bennu-portal/login.html index 305eb12c0..e60617781 100644 --- a/bennu-portal/src/main/webapp/bennu-portal/login.html +++ b/bennu-portal/src/main/webapp/bennu-portal/login.html @@ -34,7 +34,7 @@
- +
-

FenixEdu Logo

+

Bennu Logo

@@ -55,7 +55,7 @@

{{bootstrapper.name[locale.key]}}

-

+

@@ -117,7 +117,7 @@

Bootstrap

-

FenixEdu Logo

+

Bennu Logo


diff --git a/server/bennu-core/src/main/webapp/bennu-core/css/bootstrapWizard.css b/server/bennu-core/src/main/webapp/bennu-core/css/bootstrapWizard.css index 668070eb2..59b2d8a05 100644 --- a/server/bennu-core/src/main/webapp/bennu-core/css/bootstrapWizard.css +++ b/server/bennu-core/src/main/webapp/bennu-core/css/bootstrapWizard.css @@ -1,21 +1,29 @@ .container { padding-top: 20px; padding-bottom: 5px; - background: #f7f7f7; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - border-radius: 2px; - -moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3); - -webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3); - box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3); + background: #fff; + border-bottom-right-radius: 10px; + border-bottom-left-radius: 10px; } body { - font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; + font-family: 'Roboto', sans-serif; font-size: 14px; - font-weight:300 !important; - background-color: #F6F4ED; - color: #4b565c; + font-weight:100 !important; + background-color: #F1F1F1; + color: #617383; +} + +legend { + color: #617383; +} + +a { + color: #67B6E5; +} + +.form-control:focus { + border-color:#617383; } .alert { diff --git a/server/bennu-core/src/main/webapp/img/logo_bennu.png b/server/bennu-core/src/main/webapp/img/logo_bennu.png deleted file mode 100644 index b87655b8c..000000000 Binary files a/server/bennu-core/src/main/webapp/img/logo_bennu.png and /dev/null differ diff --git a/server/bennu-core/src/main/webapp/img/logo_bennu.svg b/server/bennu-core/src/main/webapp/img/logo_bennu.svg new file mode 100644 index 000000000..e42373739 --- /dev/null +++ b/server/bennu-core/src/main/webapp/img/logo_bennu.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bennu + + Framework + + diff --git a/server/bennu-core/src/main/webapp/img/mysteryman.png b/server/bennu-core/src/main/webapp/img/mysteryman.png index 6da2502f2..66eb40d82 100644 Binary files a/server/bennu-core/src/main/webapp/img/mysteryman.png and b/server/bennu-core/src/main/webapp/img/mysteryman.png differ diff --git a/server/bennu-core/src/main/webapp/img/mysteryman.svg b/server/bennu-core/src/main/webapp/img/mysteryman.svg new file mode 100644 index 000000000..f7cc738e7 --- /dev/null +++ b/server/bennu-core/src/main/webapp/img/mysteryman.svg @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/server/bennu-io/pom.xml b/server/bennu-io/pom.xml index 1be5741da..4c01d63a3 100644 --- a/server/bennu-io/pom.xml +++ b/server/bennu-io/pom.xml @@ -5,7 +5,7 @@ org.fenixedu bennu-server-aggregator - 3.2.0 + 3.3.0 bennu-io diff --git a/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/DomainStorage.java b/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/DomainStorage.java index 1745a0ca4..73dfc3c3b 100644 --- a/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/DomainStorage.java +++ b/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/DomainStorage.java @@ -38,6 +38,7 @@ public byte[] read(String uniqueIdentification) { @Override public InputStream readAsInputStream(String uniqueIdentification) { - return new ByteArrayInputStream(read(uniqueIdentification)); + byte[] read = read(uniqueIdentification); + return read != null ? new ByteArrayInputStream(read) : null; } } \ No newline at end of file diff --git a/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/FileStorage.java b/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/FileStorage.java index 59c8e8934..a7958a295 100644 --- a/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/FileStorage.java +++ b/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/FileStorage.java @@ -54,4 +54,8 @@ public Boolean delete() { public boolean isCanBeDeleted() { return getFileSet().isEmpty(); } + + public boolean isDefault() { + return getFileSupportAsDefault() != null; + } } diff --git a/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/GenericFile.java b/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/GenericFile.java index cae98c3e5..d24d49cef 100644 --- a/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/GenericFile.java +++ b/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/GenericFile.java @@ -109,7 +109,9 @@ private void setContent(byte[] content) { } setContentKey(uniqueIdentification); - setContentType(detectContentType(content, getFilename())); + if (content != null) { + setContentType(detectContentType(content, getFilename())); + } } public byte[] getContent() { diff --git a/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/GroupBasedFile.java b/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/GroupBasedFile.java index 3f2435ea9..f5fa0bff3 100644 --- a/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/GroupBasedFile.java +++ b/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/GroupBasedFile.java @@ -25,7 +25,7 @@ public boolean isAccessible(User user) { @Override public void delete() { - setAccessGroup(null); + setGroup(null); super.delete(); } } diff --git a/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/LocalFileSystemStorage.java b/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/LocalFileSystemStorage.java index 94b7f9c44..4234c51ca 100644 --- a/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/LocalFileSystemStorage.java +++ b/server/bennu-io/src/main/java/org/fenixedu/bennu/io/domain/LocalFileSystemStorage.java @@ -82,10 +82,6 @@ public String store(String uniqueIdentification, byte[] content) { } Map map = getPerTxBox().get(); - if (map == null) { - map = new HashMap<>(); - fileIntentions.put(map); - } if (map.containsKey(uniqueIdentification)) { map.remove(uniqueIdentification); } @@ -147,7 +143,7 @@ private String transformIDInPath(final String uniqueIdentification) { public byte[] read(String uniqueIdentification) { try { Map map = getPerTxBox().get(); - if (map != null && map.containsKey(uniqueIdentification)) { + if (map.containsKey(uniqueIdentification)) { return map.get(uniqueIdentification).contents; } @@ -161,7 +157,7 @@ public byte[] read(String uniqueIdentification) { public InputStream readAsInputStream(String uniqueIdentification) { try { Map map = getPerTxBox().get(); - if (map != null && map.containsKey(uniqueIdentification)) { + if (map.containsKey(uniqueIdentification)) { return new ByteArrayInputStream(map.get(uniqueIdentification).contents); } @@ -174,12 +170,12 @@ public InputStream readAsInputStream(String uniqueIdentification) { private synchronized PerTxBox> getPerTxBox() { if (fileIntentions == null) { - fileIntentions = new PerTxBox>(null) { + fileIntentions = new PerTxBox>(new HashMap()) { @Override public void commit(Map map) { - for (String key : map.keySet()) { + for (FileWriteIntention i : map.values()) { try { - map.get(key).write(); + i.write(); } catch (IOException e) { throw new RuntimeException("error.store.file", e); } diff --git a/server/bennu-io/src/main/java/org/fenixedu/bennu/io/rest/FileStorageResource.java b/server/bennu-io/src/main/java/org/fenixedu/bennu/io/rest/FileStorageResource.java index 3af0ac442..597bcee08 100644 --- a/server/bennu-io/src/main/java/org/fenixedu/bennu/io/rest/FileStorageResource.java +++ b/server/bennu-io/src/main/java/org/fenixedu/bennu/io/rest/FileStorageResource.java @@ -23,6 +23,19 @@ @Path("/bennu-io/storage") public class FileStorageResource extends BennuRestResource { + @POST + @Path("/default/{storage}") + public String setDefault(@PathParam("storage") String storageId) { + accessControl("#managers"); + innerSetDefault(this. readDomainObject(storageId)); + return all(); + } + + @Atomic + private void innerSetDefault(FileStorage storage) { + FileSupport.getInstance().setDefaultStorage(storage); + } + @POST @Path("/domain/{name}") @Produces(MediaType.APPLICATION_JSON) diff --git a/server/bennu-io/src/main/java/org/fenixedu/bennu/io/rest/json/FileStorageAdapter.java b/server/bennu-io/src/main/java/org/fenixedu/bennu/io/rest/json/FileStorageAdapter.java index 4a3cadbe1..cda2c3fbc 100644 --- a/server/bennu-io/src/main/java/org/fenixedu/bennu/io/rest/json/FileStorageAdapter.java +++ b/server/bennu-io/src/main/java/org/fenixedu/bennu/io/rest/json/FileStorageAdapter.java @@ -27,6 +27,7 @@ public JsonElement view(FileStorage fs, JsonBuilder arg1) { json.addProperty("id", fs.getExternalId()); json.addProperty("name", fs.getName()); json.addProperty("type", fs.getClass().getSimpleName()); + json.addProperty("default", fs.isDefault()); json.addProperty("filesCount", fs.getFileSet().size()); return json; } diff --git a/server/bennu-io/src/main/java/org/fenixedu/bennu/io/servlets/FileDownloadServlet.java b/server/bennu-io/src/main/java/org/fenixedu/bennu/io/servlets/FileDownloadServlet.java index 748793b4b..4026fcb53 100644 --- a/server/bennu-io/src/main/java/org/fenixedu/bennu/io/servlets/FileDownloadServlet.java +++ b/server/bennu-io/src/main/java/org/fenixedu/bennu/io/servlets/FileDownloadServlet.java @@ -38,11 +38,15 @@ public void doGet(final HttpServletRequest request, final HttpServletResponse re } if (file.isAccessible(Authenticate.getUser())) { byte[] content = file.getContent(); - response.setContentType(file.getContentType()); - response.setContentLength(file.getSize().intValue()); - try (OutputStream stream = response.getOutputStream()) { - stream.write(content); - stream.flush(); + if (content != null) { + response.setContentType(file.getContentType()); + response.setContentLength(file.getSize().intValue()); + try (OutputStream stream = response.getOutputStream()) { + stream.write(content); + stream.flush(); + } + } else { + response.sendError(HttpServletResponse.SC_NO_CONTENT, "File empty"); } } else if (file.isPrivate() && !Authenticate.isLogged() && request.getAttribute(CasAuthenticationFilter.AUTHENTICATION_EXCEPTION_KEY) == null) { @@ -64,7 +68,7 @@ private String sendLoginRedirect(final GenericFile file) throws IOException { + URLEncoder.encode(getDownloadUrl(file), Charsets.UTF_8.name()); } - private static GenericFile getFileFromURL(String url) { + public static GenericFile getFileFromURL(String url) { try { // Remove trailing path, and split the tokens String[] parts = url.substring(url.indexOf(SERVLET_PATH)).replace(SERVLET_PATH, "").split("\\/"); diff --git a/server/bennu-scheduler/pom.xml b/server/bennu-scheduler/pom.xml index 5adf3283a..bbac63cc8 100644 --- a/server/bennu-scheduler/pom.xml +++ b/server/bennu-scheduler/pom.xml @@ -4,7 +4,7 @@ org.fenixedu bennu-server-aggregator - 3.2.0 + 3.3.0 bennu-scheduler diff --git a/server/bennu-scheduler/src/main/java/org/fenixedu/bennu/scheduler/domain/SchedulerSystem.java b/server/bennu-scheduler/src/main/java/org/fenixedu/bennu/scheduler/domain/SchedulerSystem.java index c9cc600be..419216c1e 100644 --- a/server/bennu-scheduler/src/main/java/org/fenixedu/bennu/scheduler/domain/SchedulerSystem.java +++ b/server/bennu-scheduler/src/main/java/org/fenixedu/bennu/scheduler/domain/SchedulerSystem.java @@ -198,7 +198,7 @@ public void setShouldRun(Boolean shouldRun) { /** * Initializes the scheduler. */ - private static void bootstrap() { + private synchronized static void bootstrap() { LOG.info("Running Scheduler bootstrap"); if (scheduler == null) { scheduler = new Scheduler(); @@ -417,7 +417,6 @@ public static String getTaskName(String className) { * * @return the physical absolute path of the logging storage. */ - @Atomic(mode = TxMode.READ) public static String getLogsPath() { if (getInstance().getLoggingStorage() == null) { diff --git a/server/bennu-search/pom.xml b/server/bennu-search/pom.xml index 4ee3e633f..3ec350701 100644 --- a/server/bennu-search/pom.xml +++ b/server/bennu-search/pom.xml @@ -5,7 +5,7 @@ org.fenixedu bennu-server-aggregator - 3.2.0 + 3.3.0 bennu-search diff --git a/server/bundle/pom.xml b/server/bundle/pom.xml index eeee8181d..c89ce78ef 100644 --- a/server/bundle/pom.xml +++ b/server/bundle/pom.xml @@ -5,7 +5,7 @@ org.fenixedu bennu-server-aggregator - 3.2.0 + 3.3.0 bennu-server diff --git a/server/pom.xml b/server/pom.xml index 3976c9626..55b5a7eb1 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -5,7 +5,7 @@ org.fenixedu bennu - 3.2.0 + 3.3.0 bennu-server-aggregator diff --git a/theme/default/pom.xml b/theme/default/pom.xml index 79e667055..e25fbfa92 100644 --- a/theme/default/pom.xml +++ b/theme/default/pom.xml @@ -4,7 +4,7 @@ org.fenixedu bennu-theme-aggregator - 3.2.0 + 3.3.0 default-theme diff --git a/theme/default/src/main/webapp/themes/default/default.html b/theme/default/src/main/webapp/themes/default/default.html index c51384993..308f7f24a 100644 --- a/theme/default/src/main/webapp/themes/default/default.html +++ b/theme/default/src/main/webapp/themes/default/default.html @@ -35,10 +35,10 @@