diff --git a/assembly-multiuser/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/MultiUserCheWsMasterModule.java b/assembly-multiuser/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/MultiUserCheWsMasterModule.java index 9fd46a4b4f..999570bbdd 100644 --- a/assembly-multiuser/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/MultiUserCheWsMasterModule.java +++ b/assembly-multiuser/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/MultiUserCheWsMasterModule.java @@ -18,6 +18,8 @@ import org.eclipse.che.api.user.server.spi.PreferenceDao; import org.eclipse.che.api.user.server.spi.UserDao; import org.eclipse.che.api.workspace.server.hc.ServerCheckerFactoryImpl; import org.eclipse.che.inject.DynaModule; +import org.eclipse.che.mail.template.ST.STTemplateProcessorImpl; +import org.eclipse.che.mail.template.TemplateProcessor; import org.eclipse.che.multiuser.api.permission.server.AdminPermissionInitializer; import org.eclipse.che.multiuser.api.permission.server.PermissionChecker; import org.eclipse.che.multiuser.api.permission.server.PermissionCheckerImpl; @@ -41,6 +43,8 @@ public class MultiUserCheWsMasterModule extends AbstractModule { bind(InstallerConfigProvisioner.class).to(MultiuserInstallerConfigProvisioner.class); install(new OpenShiftInfraModule()); + bind(TemplateProcessor.class).to(STTemplateProcessorImpl.class); + bind(DataSource.class).toProvider(org.eclipse.che.core.db.JndiDataSourceProvider.class); install(new org.eclipse.che.multiuser.api.permission.server.jpa.SystemPermissionsJpaModule()); install(new org.eclipse.che.multiuser.api.permission.server.PermissionsModule()); diff --git a/assembly-multiuser/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties b/assembly-multiuser/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties index fc9cb75b0f..7b874e3e8f 100644 --- a/assembly-multiuser/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties +++ b/assembly-multiuser/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties @@ -74,11 +74,49 @@ che.limits.organization.workspaces.ram=-1 # additional workspaces. This applies to the total number of both running # and stopped workspaces. Since each workspace is saved as a snapshot, placing a # cap on this number limits the disk consumption for workspace storage. - che.limits.organization.workspaces.count=-1 + # The maximum number of running workspaces that a single organization is allowed. # If the organization has reached this threshold and they try to start an # additional workspace, they will be prompted with an error message. The # organization will need to stop a running workspace to activate another. che.limits.organization.workspaces.run.count=-1 +# Address that will be used as from email for email notifications +che.mail.from_email_address=che@noreply.com + +##### ORGANIZATIONS' NOTIFICATIONS SETTINGS ##### + +che.organization.email.member_added_subject=You've been added to a Che Organization +che.organization.email.member_added_template=st-html-templates/user_added_to_organization + +che.organization.email.member_removed_subject=You've been removed from a Che Organization +che.organization.email.member_removed_template=st-html-templates/user_removed_from_organization + +che.organization.email.org_removed_subject=Che Organization deleted +che.organization.email.org_removed_template=st-html-templates/organization_deleted + +che.organization.email.org_renamed_subject=Che Organization renamed +che.organization.email.org_renamed_template=st-html-templates/organization_renamed + +##### KEYCLOACK CONFIGURATION ##### + +# Url to keycloak identity provider server +che.keycloak.auth_server_url=http://${CHE_HOST}:5050/auth + +# Keycloak realm is used to authenticate users +che.keycloak.realm=che + +# Keycloak client id in che.keycloak.realm that is used by dashboard, ide and cli to authenticate users +che.keycloak.client_id=che-public + +# Redhat che specific configuration + +# URL to access OSO oauth tokens +che.keycloak.oso.endpoint=NULL + +# URL to access Github oauth tokens +che.keycloak.github.endpoint=NULL + +# The number of seconds to tolerate for clock skew when verifying exp or nbf claims. +che.keycloak.allowed_clock_skew_sec=3 diff --git a/core/che-core-api-core/src/test/java/org/eclipse/che/api/core/PagesTest.java b/core/che-core-api-core/src/test/java/org/eclipse/che/api/core/PagesTest.java index ec9cdc5efc..cf620f7634 100644 --- a/core/che-core-api-core/src/test/java/org/eclipse/che/api/core/PagesTest.java +++ b/core/che-core-api-core/src/test/java/org/eclipse/che/api/core/PagesTest.java @@ -55,7 +55,7 @@ public class PagesTest { } @Test - public void eagerlyIteratesAllElements() { + public void eagerlyIteratesAllElements() throws Exception { ArrayList result = Lists.newArrayList(Pages.iterate(testSource::getStrings, 2)); assertEquals(result, testSource.strings); diff --git a/core/commons/che-core-commons-mail/pom.xml b/core/commons/che-core-commons-mail/pom.xml index 68838583cd..216f88be28 100644 --- a/core/commons/che-core-commons-mail/pom.xml +++ b/core/commons/che-core-commons-mail/pom.xml @@ -42,6 +42,10 @@ javax.mail mail + + org.antlr + ST4 + org.eclipse.che.core che-core-commons-annotations diff --git a/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/EmailBean.java b/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/EmailBean.java index dc2193ad89..e950ca6356 100644 --- a/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/EmailBean.java +++ b/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/EmailBean.java @@ -21,6 +21,7 @@ import org.eclipse.che.commons.annotation.Nullable; * @author Alexander Garagatyi */ public class EmailBean { + private String from; private String to; private String replyTo; @@ -29,6 +30,36 @@ public class EmailBean { private String subject; private List attachments; + public EmailBean() {} + + public EmailBean(EmailBean email) { + this( + email.getFrom(), + email.getTo(), + email.getReplyTo(), + email.getMimeType(), + email.getBody(), + email.getSubject(), + email.getAttachments()); + } + + public EmailBean( + String from, + String to, + String replyTo, + String mimeType, + String body, + String subject, + List attachments) { + this.from = from; + this.to = to; + this.replyTo = replyTo; + this.mimeType = mimeType; + this.body = body; + this.subject = subject; + this.attachments = attachments; + } + public String getFrom() { return from; } @@ -123,8 +154,12 @@ public class EmailBean { @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof EmailBean)) return false; + if (this == o) { + return true; + } + if (!(o instanceof EmailBean)) { + return false; + } EmailBean emailBean = (EmailBean) o; return Objects.equals(getFrom(), emailBean.getFrom()) && Objects.equals(getTo(), emailBean.getTo()) diff --git a/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/MailSessionProvider.java b/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/MailSessionProvider.java index 29121ae989..5e545e0c4b 100644 --- a/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/MailSessionProvider.java +++ b/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/MailSessionProvider.java @@ -21,20 +21,23 @@ import javax.mail.Authenticator; import javax.mail.PasswordAuthentication; import javax.mail.Session; import org.eclipse.che.inject.ConfigurationProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** Provider of {@link Session} */ @Singleton public class MailSessionProvider implements Provider { + private static final Logger LOG = LoggerFactory.getLogger(MailSessionProvider.class); private final Session session; + /** - * Configuration can be injected from container with help of {@lin ConfigurationProperties} class. - * In this case all properties that starts with 'che.mail.' will be used to create {@link + * Configuration can be injected from container with help of {@link ConfigurationProperties} + * class. In this case all properties that starts with 'che.mail.' will be used to create {@link * Session}. First 4 letters 'che.' from property names will be removed. */ @Inject public MailSessionProvider(ConfigurationProperties configurationProperties) { - this( configurationProperties .getProperties("che.mail.*") @@ -70,6 +73,7 @@ public class MailSessionProvider implements Provider { this.session = Session.getInstance(props); } } else { + LOG.warn("Mail server is not configured. Sending of emails won't work."); this.session = null; } } @@ -77,7 +81,7 @@ public class MailSessionProvider implements Provider { @Override public Session get() { if (session == null) { - throw new RuntimeException("SMTP is not configured"); + throw new RuntimeException("Mail server is not configured"); } return session; } diff --git a/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/template/ST/STTemplateProcessorImpl.java b/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/template/ST/STTemplateProcessorImpl.java new file mode 100644 index 0000000000..e2163d9be5 --- /dev/null +++ b/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/template/ST/STTemplateProcessorImpl.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2012-2017 Red Hat, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.mail.template.ST; + +import com.google.common.io.CharStreams; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.Map; +import javax.inject.Singleton; +import org.eclipse.che.commons.lang.IoUtil; +import org.eclipse.che.mail.template.Template; +import org.eclipse.che.mail.template.TemplateProcessor; +import org.eclipse.che.mail.template.exception.TemplateException; +import org.eclipse.che.mail.template.exception.TemplateNotFoundException; +import org.stringtemplate.v4.ST; + +/** + * {@link TemplateProcessor} implementation based on {@link ST}. + * + * @author Sergii Leshchenko + */ +@Singleton +public class STTemplateProcessorImpl implements TemplateProcessor { + + @Override + public String process(String templateName, Map variables) + throws TemplateException { + ST st = new ST(resolve(templateName)); + variables.forEach(st::add); + return st.render(); + } + + @Override + public String process(Template template) throws TemplateException { + return process(template.getName(), template.getAttributes()); + } + + private String resolve(String template) throws TemplateNotFoundException { + try (Reader reader = new InputStreamReader(IoUtil.getResource(template))) { + return CharStreams.toString(reader); + } catch (IOException e) { + throw new TemplateNotFoundException(e.getMessage(), e); + } + } +} diff --git a/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/template/Template.java b/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/template/Template.java new file mode 100644 index 0000000000..5d99cc3881 --- /dev/null +++ b/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/template/Template.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2012-2017 Red Hat, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.mail.template; + +import java.util.Map; + +/** + * Holds information that is required for template processing. + * + * @author Sergii Leshchenko + */ +public class Template { + + private final String templateName; + private final Map attributes; + + public Template(String templateName, Map attributes) { + this.templateName = templateName; + this.attributes = attributes; + } + + /** + * Returns template name. + * + * @see ClassLoader#getResource(String) + */ + public String getName() { + return templateName; + } + + /** Returns attributes which will be used while template processing. */ + public Map getAttributes() { + return attributes; + } +} diff --git a/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/template/TemplateProcessor.java b/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/template/TemplateProcessor.java new file mode 100644 index 0000000000..856856e98c --- /dev/null +++ b/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/template/TemplateProcessor.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2012-2017 Red Hat, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.mail.template; + +import java.util.Map; +import org.eclipse.che.mail.template.exception.TemplateException; +import org.eclipse.che.mail.template.exception.TemplateNotFoundException; + +/** + * Provides ability to process templates. + * + *

Note that variables definition format is implementation specific. + * + * @author Anton Korneta + * @author Sergii Leshchenko + */ +public interface TemplateProcessor { + + /** + * Process specified template with given variables. + * + * @param templateName the template name which will be used for processing + * @param variables the variables to used while processing of the given template + * @return processed template as string + * @throws TemplateNotFoundException when given {@code template} not found + * @throws TemplateException when any another problem occurs during the template processing + * @see ClassLoader#getResource(String) + */ + String process(String templateName, Map variables) throws TemplateException; + + /** + * Process the specified template. + * + * @param template the template to process + * @return processed template as string + * @throws TemplateNotFoundException when given {@code template} not found + * @throws TemplateException when any another problem occurs during the template processing + */ + String process(Template template) throws TemplateException; +} diff --git a/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/template/exception/TemplateException.java b/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/template/exception/TemplateException.java new file mode 100644 index 0000000000..76f9a62f06 --- /dev/null +++ b/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/template/exception/TemplateException.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2012-2017 Red Hat, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.mail.template.exception; + +/** + * Should be thrown when any exception occurs unable while template processing. + * + * @author Sergii Leshchenko + */ +public class TemplateException extends Exception { + + public TemplateException(String message) { + super(message); + } + + public TemplateException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/template/exception/TemplateNotFoundException.java b/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/template/exception/TemplateNotFoundException.java new file mode 100644 index 0000000000..2975451874 --- /dev/null +++ b/core/commons/che-core-commons-mail/src/main/java/org/eclipse/che/mail/template/exception/TemplateNotFoundException.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2012-2017 Red Hat, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.mail.template.exception; + +/** + * Should be thrown when unable to resolve template by given path. + * + * @author Anton Korneta + */ +public class TemplateNotFoundException extends TemplateException { + + public TemplateNotFoundException(String message) { + super(message); + } + + public TemplateNotFoundException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/dashboard/src/app/dashboard/dashboard-config.ts b/dashboard/src/app/dashboard/dashboard-config.ts index fedb52fc2c..9ade83e06f 100644 --- a/dashboard/src/app/dashboard/dashboard-config.ts +++ b/dashboard/src/app/dashboard/dashboard-config.ts @@ -28,7 +28,7 @@ export class DashboardConfig { register.directive('dashboardPanel', DashboardPanel); // config routes - register.app.config(($routeProvider: ng.route.IRouteProvider) => { + register.app.config(($routeProvider: che.route.IRouteProvider) => { $routeProvider.accessWhen('/', { title: 'Dashboard', templateUrl: 'app/dashboard/dashboard.html', @@ -43,8 +43,6 @@ export class DashboardConfig { $location.path('/create-workspace'); } }); - - return cheService.fetchServices(); }] } }); diff --git a/dashboard/src/app/ide/ide-iframe/ide-iframe.service.ts b/dashboard/src/app/ide/ide-iframe/ide-iframe.service.ts index 067d384af2..e4ba7e3aed 100644 --- a/dashboard/src/app/ide/ide-iframe/ide-iframe.service.ts +++ b/dashboard/src/app/ide/ide-iframe/ide-iframe.service.ts @@ -46,7 +46,7 @@ class IdeIFrameSvc { } else if ("show-navbar" === event.data) { $rootScope.hideNavbar = false; - $mdSidenav('left').toggle(); + $mdSidenav('left').open(); } else if ("hide-navbar" === event.data) { $rootScope.hideNavbar = true; diff --git a/dashboard/src/app/index.module.ts b/dashboard/src/app/index.module.ts index 68f8650eb4..41c1d8f11a 100755 --- a/dashboard/src/app/index.module.ts +++ b/dashboard/src/app/index.module.ts @@ -110,7 +110,7 @@ promise.then((keycloakSettings: any) => { }).catch((error: any) => { console.error('Keycloak initialization failed with error: ', error); }).then(() => { - angular.bootstrap(document.body, ['userDashboard'], {strictDi: true}); // manually bootstrap Angular + angular.bootstrap(document, ['userDashboard'], {strictDi: true}); // manually bootstrap Angular }); // add a global resolve flag on all routes (user needs to be resolved first) diff --git a/dashboard/src/app/navbar/navbar.controller.ts b/dashboard/src/app/navbar/navbar.controller.ts index fbb925c262..0a6febfb87 100644 --- a/dashboard/src/app/navbar/navbar.controller.ts +++ b/dashboard/src/app/navbar/navbar.controller.ts @@ -11,6 +11,7 @@ 'use strict'; import {CheAPI} from '../../components/api/che-api.factory'; import {CheKeycloak} from '../../components/api/che-keycloak.factory'; +import {CheService} from '../../components/api/che-service.factory'; export class CheNavBarController { private menuItemUrl = { @@ -53,6 +54,8 @@ export class CheNavBarController { private hasPersonalAccount: boolean; private organizations: Array; private cheKeycloak: CheKeycloak; + private cheService: CheService; + private isPermissionServiceAvailable: boolean; /** * Default constructor @@ -65,7 +68,8 @@ export class CheNavBarController { cheAPI: CheAPI, $window: ng.IWindowService, chePermissions: che.api.IChePermissions, - cheKeycloak: CheKeycloak) { + cheKeycloak: CheKeycloak, + cheService: CheService) { this.$mdSidenav = $mdSidenav; this.$scope = $scope; this.$location = $location; @@ -74,6 +78,7 @@ export class CheNavBarController { this.$window = $window; this.chePermissions = chePermissions; this.cheKeycloak = cheKeycloak; + this.cheService = cheService; this.profile = cheAPI.getProfile().getProfile(); @@ -88,13 +93,31 @@ export class CheNavBarController { cheAPI.getWorkspace().fetchWorkspaces(); cheAPI.getFactory().fetchFactories(); - if (this.chePermissions.getSystemPermissions()) { - this.updateData(); - } else { - this.chePermissions.fetchSystemPermissions().finally(() => { - this.updateData(); - }); - } + this.isPermissionServiceAvailable = false; + this.resolvePermissionServiceAvailability().then((isAvailable: boolean) => { + this.isPermissionServiceAvailable = isAvailable; + + if (isAvailable) { + if (this.chePermissions.getSystemPermissions()) { + this.updateData(); + } else { + this.chePermissions.fetchSystemPermissions().finally(() => { + this.updateData(); + }); + } + } + }); + } + + /** + * Resolves promise with true if Permissions service is available. + * + * @returns {ng.IPromise} + */ + resolvePermissionServiceAvailability(): ng.IPromise { + return this.cheService.fetchServices().then(() => { + return this.cheService.isServiceAvailable(this.chePermissions.getPermissionsServicePath()); + }); } /** diff --git a/dashboard/src/app/navbar/navbar.html b/dashboard/src/app/navbar/navbar.html index cd5629df52..e251d1d363 100644 --- a/dashboard/src/app/navbar/navbar.html +++ b/dashboard/src/app/navbar/navbar.html @@ -15,8 +15,14 @@

- + +
+ +
+ +
@@ -69,7 +75,7 @@
- +