Merge branch 'develop'

This commit is contained in:
Paulo Gustavo Veiga 2022-09-11 13:21:26 -07:00
commit 3658d3347e
36 changed files with 934 additions and 340 deletions

View File

@ -46,12 +46,17 @@ In order to reduce the life-cycle to develop UI backend testing, you can do the
A quick and dirty solution to share changes in the UI is to manually compile the dist. This will make the loader file available without the need to publish: A quick and dirty solution to share changes in the UI is to manually compile the dist. This will make the loader file available without the need to publish:
`yarn --cwd wisemapping-frontend build;cp -r wisemapping-frontend/packages/mindplot/dist/* wisemapping-open-source/wise-ui/target/wisemapping-mindplot/package/dist;cp -r wisemapping-frontend/packages/mindplot/dist/* wisemapping-open-source/wise-ui/target/wisemapping-mindplot/package/dist` `yarn --cwd wisemapping-frontend build;cp -r wisemapping-frontend/packages/mindplot/dist/* wisemapping-open-source/wise-ui/target/wisemapping-mindplot/package/dist;cp -r wisemapping-frontend/packages/webapp/dist/* wisemapping-open-source/wise-ui/target/wisemapping-webapp/package/dist`
### Compiling and running with docker-compose ### Compiling and running with docker-compose
Check out the [docker section](./docker/README. Check out the [docker section](./docker/README.)
### Test reports
Individual test result reports can be found in wisemapping-open-source/wise-webapp/target/failsafe-reports/index.html
Test coverage report of unit and integration test can be found in wisemapping-open-source/wise-webapp/target/site/jacoco and wisemapping-open-source/wise-webapp/target/site/jacoco-it folders. Coverage report is generated in the verify phase of [lifecicle](https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#introduction-to-the-build-lifecyclea) using [jacoco](https://www.jacoco.org/jacoco/trunk/doc/maven.html)
## Members ## Members
@ -60,13 +65,10 @@ Check out the [docker section](./docker/README.
* Paulo Veiga <pveiga@wisemapping.com> * Paulo Veiga <pveiga@wisemapping.com>
* Pablo Luna <pablo@wisemapping.com> * Pablo Luna <pablo@wisemapping.com>
### Individual Contributors
* Ezequiel Bergamaschi <ezequielbergamaschi@gmail.com>
### Past Individual Contributors ### Past Individual Contributors
* Ignacio Manzano * Ignacio Manzano
* Ezequiel Bergamaschi <ezequielbergamaschi@gmail.com>
## License ## License

View File

@ -9,15 +9,27 @@ There are multiple ways to run WiseMapping depending on your database configurat
## Option 1: Running HSQL within the image storage ## Option 1: Running HSQL within the image storage
> $ docker run -it --rm -p 8080:8080 veigap/wisemapping:latest> > $ docker run -it --rm -p 8080:8080 wisemapping/wisemapping:latest
Then, open your browser at `http://localhost:8888`. A default user is available for testing `test@wisemapping.com` and password `test`. Then, open your browser at `http://localhost:8888`. A default user is available for testing `test@wisemapping.org` and password `test`.
***This option, all changes will be lost once the image is stopped. Use it for testing only*** ***This option, all changes will be lost once the image is stopped. Use it for testing only***
## Option 2: Running HSQL with mounted directory ## Option 2: Running HSQL with mounted directory
> $ docker run -it --rm -p 8080:8080 veigap/wisemapping:latest Only one time, copy the empty default out of the container:
> $ mkdir your- db-dir-store-path
>
> $ docker run --name wiseapp -d --mount type=bind,source=your-db-dir-store-path,target=/var/lib/wise-db wisemapping/wisemapping:latest
>
> $ docker cp wiseapp:/var/lib/wisemapping/db your-db-dir-store-path
>
> $ docker stop wiseapp;docker rm wiseapp
Then, execute the container mounting tbe directory:
> $ docker run --mount type=bind,source=your-db-dir-store-path/db,target=/var/lib/wisemapping/db -it --rm -p 8080:8080 wisemapping/wisemapping:latest
## Option 3: External MySQL/PostgreSQL ## Option 3: External MySQL/PostgreSQL
@ -36,7 +48,7 @@ Download `app.properties` configuration file and configure the required sections
Run the application mounting your previously configured `app.properties` Run the application mounting your previously configured `app.properties`
> $ docker run --mount type=bind,source=your-file-path/app.properties,target=/usr/local/tomcat/webapps/ROOT/WEB-INF/app.properties -it --rm -p 8080:8080 veigap/wisemapping:latest > $ docker run --mount type=bind,source=your-file-path/app.properties,target=/usr/local/tomcat/webapps/ROOT/WEB-INF/app.properties -it --rm -p 8080:8080 wisemapping/wisemapping:latest
# Advanced configuration # Advanced configuration

View File

@ -4,7 +4,7 @@
http://maven.apache.org/xsd/maven-4.0.0.xsd"> http://maven.apache.org/xsd/maven-4.0.0.xsd">
<properties> <properties>
<com.wisemapping.version>5.0.11</com.wisemapping.version> <com.wisemapping.version>5.0.12</com.wisemapping.version>
<superpom.dir>${project.basedir}/wise-webapps</superpom.dir> <superpom.dir>${project.basedir}/wise-webapps</superpom.dir>
</properties> </properties>
@ -16,7 +16,7 @@
<groupId>org.wisemapping</groupId> <groupId>org.wisemapping</groupId>
<artifactId>wisemapping</artifactId> <artifactId>wisemapping</artifactId>
<name>WiseMapping Project</name> <name>WiseMapping Project</name>
<version>5.0.11</version> <version>5.0.12</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<licenses> <licenses>

View File

@ -12,7 +12,7 @@
<groupId>org.wisemapping</groupId> <groupId>org.wisemapping</groupId>
<artifactId>wisemapping</artifactId> <artifactId>wisemapping</artifactId>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
<version>5.0.11</version> <version>5.0.12</version>
</parent> </parent>
<build> <build>
@ -29,11 +29,11 @@
<mkdir dir="target/wisemapping-mindplot"/> <mkdir dir="target/wisemapping-mindplot"/>
<exec executable="npm" dir="target" failonerror="true"> <exec executable="npm" dir="target" failonerror="true">
<arg value="pack"/> <arg value="pack"/>
<arg value="@wisemapping/mindplot@5.0.12"/> <arg value="@wisemapping/mindplot@5.0.16"/>
</exec> </exec>
<exec executable="tar" dir="target" failonerror="true"> <exec executable="tar" dir="target" failonerror="true">
<arg value="-xvzf"/> <arg value="-xvzf"/>
<arg value="wisemapping-mindplot-5.0.12.tgz"/> <arg value="wisemapping-mindplot-5.0.16.tgz"/>
<arg value="-C"/> <arg value="-C"/>
<arg value="wisemapping-mindplot"/> <arg value="wisemapping-mindplot"/>
</exec> </exec>
@ -42,11 +42,11 @@
<mkdir dir="target/wisemapping-webapp"/> <mkdir dir="target/wisemapping-webapp"/>
<exec executable="npm" dir="target" failonerror="true"> <exec executable="npm" dir="target" failonerror="true">
<arg value="pack"/> <arg value="pack"/>
<arg value="@wisemapping/webapp@5.0.12"/> <arg value="@wisemapping/webapp@5.0.15"/>
</exec> </exec>
<exec executable="tar" dir="target" failonerror="true"> <exec executable="tar" dir="target" failonerror="true">
<arg value="-xvzf"/> <arg value="-xvzf"/>
<arg value="wisemapping-webapp-5.0.12.tgz"/> <arg value="wisemapping-webapp-5.0.15.tgz"/>
<arg value="-C"/> <arg value="-C"/>
<arg value="wisemapping-webapp"/> <arg value="wisemapping-webapp"/>
</exec> </exec>

View File

@ -1,5 +1,4 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>wise-webapp</artifactId> <artifactId>wise-webapp</artifactId>
<packaging>war</packaging> <packaging>war</packaging>
@ -9,13 +8,13 @@
<groupId>org.wisemapping</groupId> <groupId>org.wisemapping</groupId>
<artifactId>wisemapping</artifactId> <artifactId>wisemapping</artifactId>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
<version>5.0.11</version> <version>5.0.12</version>
</parent> </parent>
<properties> <properties>
<org.springframework.version>5.3.18</org.springframework.version> <org.springframework.version>5.3.22</org.springframework.version>
<org.springframework.addons>5.6.2</org.springframework.addons> <org.springframework.addons>5.6.2</org.springframework.addons>
<hibernate.version>5.6.5.Final</hibernate.version> <hibernate.version>5.6.11.Final</hibernate.version>
<hibernate-validator.version>6.0.21.Final</hibernate-validator.version> <hibernate-validator.version>6.0.21.Final</hibernate-validator.version>
<spring-security-taglibs.version>5.6.1</spring-security-taglibs.version> <spring-security-taglibs.version>5.6.1</spring-security-taglibs.version>
</properties> </properties>
@ -452,6 +451,69 @@
</systemProperties> </systemProperties>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.8</version>
<executions>
<execution>
<id>default-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>default-prepare-agent-integration</id>
<phase>pre-integration-test</phase>
<goals>
<goal>prepare-agent-integration</goal>
</goals>
<configuration>
<excludes>
<exclude>**/*Test*</exclude>
</excludes>
<propertyName>integrationTestArgLine</propertyName>
</configuration>
</execution>
<execution>
<id>default-check</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>COMPLEXITY</counter>
<value>COVEREDRATIO</value>
<minimum>0.10</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
<!-- Confirm why there is a NPE -->
<!-- <execution>-->
<!-- <id>default-report-integration</id>-->
<!-- <phase>verify</phase>-->
<!-- <goals>-->
<!-- <goal>report-integration</goal>-->
<!-- </goals>-->
<!-- </execution>-->
<execution>
<id>default-report</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
@ -459,6 +521,9 @@
<version>9.4.34.v20201102</version> <version>9.4.34.v20201102</version>
<configuration> <configuration>
<stopKey>foo</stopKey> <stopKey>foo</stopKey>
<httpConnector>
<port>8080</port>
</httpConnector>
<stopPort>9999</stopPort> <stopPort>9999</stopPort>
<war>${project.build.directory}/wisemapping.war</war> <war>${project.build.directory}/wisemapping.war</war>
<reload>automatic</reload> <reload>automatic</reload>
@ -488,9 +553,13 @@
<goal>run-forked</goal> <goal>run-forked</goal>
</goals> </goals>
<configuration> <configuration>
<useTestClasspath>true</useTestClasspath>
<useTestScope>true</useTestScope>
<scanIntervalSeconds>0</scanIntervalSeconds> <scanIntervalSeconds>0</scanIntervalSeconds>
<daemon>true</daemon>
<waitForChild>false</waitForChild> <waitForChild>false</waitForChild>
<jvmArgs>-Ddatabase.base.url=${project.build.directory}</jvmArgs> <maxStartupLines>200</maxStartupLines>
<jvmArgs>${integrationTestArgLine} -Ddatabase.base.url=${project.build.directory} -Djetty.port=8080</jvmArgs>
</configuration> </configuration>
</execution> </execution>
<execution> <execution>
@ -499,6 +568,9 @@
<goals> <goals>
<goal>stop</goal> <goal>stop</goal>
</goals> </goals>
<configuration>
<stopWait>1</stopWait>
</configuration>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
@ -514,6 +586,7 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -52,6 +52,7 @@ public class UserManagerImpl
@Override @Override
@Nullable
public User getUserBy(@NotNull final String email) { public User getUserBy(@NotNull final String email) {
User user = null; User user = null;

View File

@ -0,0 +1,39 @@
/*
* Copyright [2022] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.wisemapping.exceptions;
import org.jetbrains.annotations.NotNull;
public class CollabChangeException
extends ClientException
{
private static final String MSG_KEY = "OWNER_ROLE_CAN_NOT_BE_CHANGED";
public CollabChangeException(@NotNull String email)
{
super("Collab email can not be change. " + email + " is the the owner.",Severity.WARNING);
}
@NotNull
@Override
protected String getMsgBundleKey() {
return MSG_KEY;
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright [2022] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.wisemapping.exceptions;
import javax.validation.constraints.NotNull;
public class TooManyInactiveAccountsExceptions
extends ClientException {
private static final String TOO_MANY_INACTIVE_ACCOUNTS = "TOO_MANY_INACTIVE_ACCOUNTS";
public TooManyInactiveAccountsExceptions(@NotNull long accounts) {
super("Too many inactive accounts:" + accounts, Severity.WARNING);
}
@NotNull
@Override
protected String getMsgBundleKey() {
return TOO_MANY_INACTIVE_ACCOUNTS;
}
}

View File

@ -73,6 +73,11 @@ final public class NotificationService {
model.put("message", message); model.put("message", message);
model.put("doNotReplay", messageSource.getMessage("EMAIL.DO_NOT_REPLAY", new Object[]{mailer.getSupportEmail()}, locale)); model.put("doNotReplay", messageSource.getMessage("EMAIL.DO_NOT_REPLAY", new Object[]{mailer.getSupportEmail()}, locale));
// To resolve resources on templates ...
model.put("noArg", new Object[]{});
model.put("messages", messageSource);
model.put("locale", locale);
mailer.sendEmail(formMail, collabEmail, subject, model, "newCollaboration.vm"); mailer.sendEmail(formMail, collabEmail, subject, model, "newCollaboration.vm");
} catch (Exception e) { } catch (Exception e) {
handleException(e); handleException(e);
@ -122,6 +127,11 @@ final public class NotificationService {
model.put("supportEmail", mailer.getSupportEmail()); model.put("supportEmail", mailer.getSupportEmail());
model.put("doNotReplay", messageSource.getMessage("EMAIL.DO_NOT_REPLAY", new Object[]{mailer.getSupportEmail()}, locale)); model.put("doNotReplay", messageSource.getMessage("EMAIL.DO_NOT_REPLAY", new Object[]{mailer.getSupportEmail()}, locale));
// To resolve resources on templates ...
model.put("noArg", new Object[]{});
model.put("messages", messageSource);
model.put("locale", locale);
logger.debug("Email properties->" + model); logger.debug("Email properties->" + model);
mailer.sendEmail(mailer.getServerSenderEmail(), user.getEmail(), mailSubject, model, "baseLayout.vm"); mailer.sendEmail(mailer.getServerSenderEmail(), user.getEmail(), mailSubject, model, "baseLayout.vm");
} catch (Exception e) { } catch (Exception e) {

View File

@ -66,7 +66,7 @@ public class Mindmap implements Serializable {
@OneToMany(mappedBy = "mindMap", orphanRemoval = true, cascade = {CascadeType.ALL}) @OneToMany(mappedBy = "mindMap", orphanRemoval = true, cascade = {CascadeType.ALL})
private Set<Collaboration> collaborations = new HashSet<>(); private Set<Collaboration> collaborations = new HashSet<>();
@ManyToMany(cascade = CascadeType.ALL) @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.MERGE})
@JoinTable( @JoinTable(
name = "R_LABEL_MINDMAP", name = "R_LABEL_MINDMAP",
joinColumns = @JoinColumn(name = "mindmap_id"), joinColumns = @JoinColumn(name = "mindmap_id"),

View File

@ -1,20 +1,20 @@
/* /*
* Copyright [2022] [wisemapping] * Copyright [2022] [wisemapping]
* *
* Licensed under WiseMapping Public License, Version 1.0 (the "License"). * Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the * It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page; * "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the license at * You may obtain a copy of the license at
* *
* http://www.wisemapping.org/license * http://www.wisemapping.org/license
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.wisemapping.rest; package com.wisemapping.rest;
@ -26,19 +26,16 @@ import com.wisemapping.model.User;
import com.wisemapping.rest.model.RestUser; import com.wisemapping.rest.model.RestUser;
import com.wisemapping.service.MindmapService; import com.wisemapping.service.MindmapService;
import com.wisemapping.service.UserService; import com.wisemapping.service.UserService;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Calendar;
import java.util.List; import java.util.List;
import java.util.regex.Pattern;
@Controller @Controller
public class AdminController extends BaseController { public class AdminController extends BaseController {
@ -70,7 +67,7 @@ public class AdminController extends BaseController {
return new RestUser(user); return new RestUser(user);
} }
@RequestMapping(method = RequestMethod.POST, value = "admin/users", consumes = { "application/json"}, produces = {"application/json"}) @RequestMapping(method = RequestMethod.POST, value = "admin/users", consumes = {"application/json"}, produces = {"application/json"})
@ResponseStatus(value = HttpStatus.CREATED) @ResponseStatus(value = HttpStatus.CREATED)
public void createUser(@RequestBody RestUser user, HttpServletResponse response) throws WiseMappingException { public void createUser(@RequestBody RestUser user, HttpServletResponse response) throws WiseMappingException {
if (user == null) { if (user == null) {
@ -133,7 +130,7 @@ public class AdminController extends BaseController {
final List<Collaboration> collaborations = mindmapService.findCollaborations(user); final List<Collaboration> collaborations = mindmapService.findCollaborations(user);
for (Collaboration collaboration : collaborations) { for (Collaboration collaboration : collaborations) {
final Mindmap mindmap = collaboration.getMindMap(); final Mindmap mindmap = collaboration.getMindMap();
mindmapService.removeMindmap(mindmap,user); mindmapService.removeMindmap(mindmap, user);
} }
userService.removeUser(user); userService.removeUser(user);

View File

@ -29,6 +29,7 @@ import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -41,7 +42,6 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
@Controller @Controller
@ -58,6 +58,13 @@ public class MindmapController extends BaseController {
@Autowired @Autowired
private LabelService labelService; private LabelService labelService;
@Qualifier("userService")
@Autowired
private UserService userService;
@Value("${accounts.maxInactive:20}")
private int maxAccountsInactive;
@RequestMapping(method = RequestMethod.GET, value = "/maps/{id}", produces = {"application/json"}) @RequestMapping(method = RequestMethod.GET, value = "/maps/{id}", produces = {"application/json"})
@ResponseBody @ResponseBody
public RestMindmap retrieve(@PathVariable int id) throws WiseMappingException { public RestMindmap retrieve(@PathVariable int id) throws WiseMappingException {
@ -169,7 +176,7 @@ public class MindmapController extends BaseController {
/** /**
* The intention of this method is the update of several properties at once ... * The intention of this method is the update of several properties at once ...
*/ */
@RequestMapping(method = RequestMethod.PUT, value = "/maps/{id}", consumes = { "application/json"}, produces = {"application/json"}) @RequestMapping(method = RequestMethod.PUT, value = "/maps/{id}", consumes = {"application/json"}, produces = {"application/json"})
@ResponseStatus(value = HttpStatus.NO_CONTENT) @ResponseStatus(value = HttpStatus.NO_CONTENT)
public void updateProperties(@RequestBody RestMindmap restMindmap, @PathVariable int id, @RequestParam(required = false) boolean minor) throws IOException, WiseMappingException { public void updateProperties(@RequestBody RestMindmap restMindmap, @PathVariable int id, @RequestParam(required = false) boolean minor) throws IOException, WiseMappingException {
@ -243,7 +250,7 @@ public class MindmapController extends BaseController {
@RequestMapping(method = RequestMethod.POST, value = "/maps/{id}/collabs/", consumes = {"application/json"}, produces = {"application/json"}) @RequestMapping(method = RequestMethod.POST, value = "/maps/{id}/collabs/", consumes = {"application/json"}, produces = {"application/json"})
@ResponseStatus(value = HttpStatus.NO_CONTENT) @ResponseStatus(value = HttpStatus.NO_CONTENT)
public void updateCollabs(@PathVariable int id, @NotNull @RequestBody RestCollaborationList restCollabs) throws CollaborationException, MapCouldNotFoundException, AccessDeniedSecurityException, InvalidEmailException { public void updateCollabs(@PathVariable int id, @NotNull @RequestBody RestCollaborationList restCollabs) throws CollaborationException, MapCouldNotFoundException, AccessDeniedSecurityException, InvalidEmailException, TooManyInactiveAccountsExceptions {
final Mindmap mindMap = findMindmapById(id); final Mindmap mindMap = findMindmapById(id);
// Only owner can change collaborators... // Only owner can change collaborators...
@ -252,6 +259,9 @@ public class MindmapController extends BaseController {
throw new IllegalArgumentException("No enough permissions"); throw new IllegalArgumentException("No enough permissions");
} }
// Do not allow more than 20 collabs not active
verifyActiveCollabs(restCollabs, user);
// Compare one by one if some of the elements has been changed .... // Compare one by one if some of the elements has been changed ....
final Set<Collaboration> collabsToRemove = new HashSet<>(mindMap.getCollaborations()); final Set<Collaboration> collabsToRemove = new HashSet<>(mindMap.getCollaborations());
for (RestCollaboration restCollab : restCollabs.getCollaborations()) { for (RestCollaboration restCollab : restCollabs.getCollaborations()) {
@ -279,7 +289,6 @@ public class MindmapController extends BaseController {
if (role != CollaborationRole.OWNER) { if (role != CollaborationRole.OWNER) {
mindmapService.addCollaboration(mindMap, restCollab.getEmail(), role, restCollabs.getMessage()); mindmapService.addCollaboration(mindMap, restCollab.getEmail(), role, restCollabs.getMessage());
} }
} }
// Remove all collaborations that no applies anymore .. // Remove all collaborations that no applies anymore ..
@ -290,7 +299,7 @@ public class MindmapController extends BaseController {
@RequestMapping(method = RequestMethod.PUT, value = "/maps/{id}/collabs/", consumes = {"application/json"}, produces = {"application/json"}) @RequestMapping(method = RequestMethod.PUT, value = "/maps/{id}/collabs/", consumes = {"application/json"}, produces = {"application/json"})
@ResponseStatus(value = HttpStatus.NO_CONTENT) @ResponseStatus(value = HttpStatus.NO_CONTENT)
public void addCollab(@PathVariable int id, @NotNull @RequestBody RestCollaborationList restCollabs) throws CollaborationException, MapCouldNotFoundException, AccessDeniedSecurityException, InvalidEmailException { public void addCollab(@PathVariable int id, @NotNull @RequestBody RestCollaborationList restCollabs) throws CollaborationException, MapCouldNotFoundException, AccessDeniedSecurityException, InvalidEmailException, TooManyInactiveAccountsExceptions, CollabChangeException {
final Mindmap mindMap = findMindmapById(id); final Mindmap mindMap = findMindmapById(id);
// Only owner can change collaborators... // Only owner can change collaborators...
@ -299,6 +308,9 @@ public class MindmapController extends BaseController {
throw new AccessDeniedSecurityException("User must be owner to share mindmap"); throw new AccessDeniedSecurityException("User must be owner to share mindmap");
} }
// Do not allow more than 20 collabs not active
verifyActiveCollabs(restCollabs, user);
// Is valid email address ? // Is valid email address ?
final EmailValidator emailValidator = EmailValidator.getInstance(); final EmailValidator emailValidator = EmailValidator.getInstance();
final Set<String> invalidEmails = restCollabs final Set<String> invalidEmails = restCollabs
@ -312,41 +324,42 @@ public class MindmapController extends BaseController {
} }
// Has any role changed ?. Just removed it. // Has any role changed ?. Just removed it.
final Map<String, Collaboration> mapsByEmail = mindMap final Map<String, Collaboration> collabByEmail = mindMap
.getCollaborations() .getCollaborations()
.stream() .stream()
.collect(Collectors.toMap(collaboration -> collaboration.getCollaborator().getEmail(), collaboration -> collaboration)); .collect(Collectors.toMap(collaboration -> collaboration.getCollaborator().getEmail(), collaboration -> collaboration));
restCollabs
.getCollaborations()
.forEach(collab -> {
final String email = collab.getEmail();
if (mapsByEmail.containsKey(email)) {
try {
mindmapService.removeCollaboration(mindMap, mapsByEmail.get(email));
} catch (CollaborationException e) {
logger.error(e);
}
}
});
// Great, let's add all the collabs again ... // Great, let's add all the collabs again ...
for (RestCollaboration restCollab : restCollabs.getCollaborations()) { for (RestCollaboration restCollab : restCollabs.getCollaborations()) {
final Collaboration collaboration = mindMap.findCollaboration(restCollab.getEmail()); // Validate newRole format ...
// Validate role format ... final String roleStr = restCollab.getRole();
String roleStr = restCollab.getRole();
if (roleStr == null) { if (roleStr == null) {
throw new IllegalArgumentException(roleStr + " is not a valid role"); throw new IllegalArgumentException(roleStr + " is not a valid newRole");
} }
// Is owner ? // Had the newRole changed ?. Otherwise, don't touch it.
final CollaborationRole role = CollaborationRole.valueOf(roleStr.toUpperCase()); final CollaborationRole newRole = CollaborationRole.valueOf(roleStr.toUpperCase());
if (role == CollaborationRole.OWNER) { final String collabEmail = restCollab.getEmail();
throw new IllegalArgumentException("Owner can not be added as part of the collaboration list."); final Collaboration currentCollab = collabByEmail.get(collabEmail);
if (currentCollab == null || currentCollab.getRole() != newRole) {
// Are we trying to change the owner ...
if (currentCollab != null && currentCollab.getRole() == CollaborationRole.OWNER) {
throw new CollabChangeException(collabEmail);
} }
mindmapService.addCollaboration(mindMap, restCollab.getEmail(), role, restCollabs.getMessage()); // Role can not be changed ...
if (newRole == CollaborationRole.OWNER) {
throw new CollabChangeException(collabEmail);
}
// This is collaboration that with different newRole, try to change it ...
if (currentCollab != null) {
mindmapService.removeCollaboration(mindMap, currentCollab);
}
mindmapService.addCollaboration(mindMap, collabEmail, newRole, restCollabs.getMessage());
}
} }
} }
@ -503,7 +516,7 @@ public class MindmapController extends BaseController {
response.setHeader("ResourceId", Integer.toString(mindmap.getId())); response.setHeader("ResourceId", Integer.toString(mindmap.getId()));
} }
@RequestMapping(method = RequestMethod.POST, value = "/maps/{id}", consumes = {"application/json"}, produces = { "application/json", "text/plain"}) @RequestMapping(method = RequestMethod.POST, value = "/maps/{id}", consumes = {"application/json"}, produces = {"application/json", "text/plain"})
@ResponseStatus(value = HttpStatus.CREATED) @ResponseStatus(value = HttpStatus.CREATED)
public void createDuplicate(@RequestBody RestMindmapInfo restMindmap, @PathVariable int id, @NotNull HttpServletResponse response) throws IOException, WiseMappingException { public void createDuplicate(@RequestBody RestMindmapInfo restMindmap, @PathVariable int id, @NotNull HttpServletResponse response) throws IOException, WiseMappingException {
// Validate ... // Validate ...
@ -588,4 +601,25 @@ public class MindmapController extends BaseController {
} }
return result; return result;
} }
private void verifyActiveCollabs(@NotNull RestCollaborationList restCollabs, User user) throws TooManyInactiveAccountsExceptions {
// Do not allow more than 20 new accounts per mindmap...
final List<Mindmap> userMindmaps = mindmapService.findMindmapsByUser(user);
final Set<String> allEmails = userMindmaps
.stream()
.filter(m -> m.hasPermissions(user, CollaborationRole.OWNER))
.map(Mindmap::getCollaborations)
.flatMap(Collection::stream)
.map(c -> c.getCollaborator().getEmail())
.collect(Collectors.toSet());
allEmails.addAll(restCollabs
.getCollaborations().stream()
.map(RestCollaboration::getEmail)
.collect(Collectors.toSet()));
long inactiveAccounts = allEmails.stream().filter(e -> userService.getUserBy(e) == null).count();
if (inactiveAccounts > maxAccountsInactive) {
throw new TooManyInactiveAccountsExceptions(inactiveAccounts);
}
}
} }

View File

@ -38,6 +38,8 @@ import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
@Controller @Controller
@CrossOrigin @CrossOrigin
@ -52,6 +54,9 @@ public class UserController extends BaseController {
@Value("${google.recaptcha2.enabled}") @Value("${google.recaptcha2.enabled}")
private Boolean recatchaEnabled; private Boolean recatchaEnabled;
@Value("${accounts.exclusion.domain:''}")
private String domainBanExclusion;
private static final Logger logger = Logger.getLogger(UserController.class); private static final Logger logger = Logger.getLogger(UserController.class);
private static final String REAL_IP_ADDRESS_HEADER = "X-Real-IP"; private static final String REAL_IP_ADDRESS_HEADER = "X-Real-IP";
@ -115,5 +120,12 @@ public class UserController extends BaseController {
if (errors.hasErrors()) { if (errors.hasErrors()) {
throw errors; throw errors;
} }
// Is excluded ?.
final List<String> excludedDomains = Arrays.asList(domainBanExclusion.split(","));
final String emailDomain = registration.getEmail().split("@")[1];
if (excludedDomains.contains(emailDomain)) {
throw new IllegalArgumentException("Email is part of ban exclusion list due to abuse. Please, contact site admin if you think this is an error." + emailDomain);
}
} }
} }

View File

@ -40,15 +40,18 @@ import java.util.TimeZone;
public class RestMindmapHistory { public class RestMindmapHistory {
static private final SimpleDateFormat sdf; static private final SimpleDateFormat sdf;
private final int id; private int id;
private final Calendar creation; private Calendar creation;
private final String creator; private String creator;
static { static {
sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("UTC")); sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
} }
public RestMindmapHistory() {
}
public RestMindmapHistory(@NotNull MindMapHistory history) { public RestMindmapHistory(@NotNull MindMapHistory history) {
this.id = history.getId(); this.id = history.getId();
this.creation = history.getCreationTime(); this.creation = history.getCreationTime();
@ -61,18 +64,18 @@ public class RestMindmapHistory {
} }
public void setCreationTime() { public void setCreationTime() {
} }
public String getCreator() { public String getCreator() {
return creator; return creator;
} }
public void setCreator() { public void setCreator(String creator) {
// Do nothing ...
} }
public void setId(int id) { public void setId(int id) {
this.id=id;
} }
private String toISO8601(@NotNull Date date) { private String toISO8601(@NotNull Date date) {

View File

@ -24,11 +24,7 @@ import com.wisemapping.model.Collaborator;
import com.wisemapping.model.Mindmap; import com.wisemapping.model.Mindmap;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -45,7 +41,7 @@ public class RestMindmapList {
this(Collections.emptyList(), null); this(Collections.emptyList(), null);
} }
public RestMindmapList(@NotNull List<Mindmap> mindmaps, @NotNull Collaborator collaborator) { public RestMindmapList(@NotNull List<Mindmap> mindmaps, Collaborator collaborator) {
this.mindmapsInfo = mindmaps.stream() this.mindmapsInfo = mindmaps.stream()
.map(m->new RestMindmapInfo(m, collaborator)) .map(m->new RestMindmapInfo(m, collaborator))
.collect(Collectors.toList()); .collect(Collectors.toList());

View File

@ -45,6 +45,12 @@ public class AuthenticationProvider implements org.springframework.security.auth
if (user == null || credentials == null || !encoder.matches(user.getPassword(), credentials)) { if (user == null || credentials == null || !encoder.matches(user.getPassword(), credentials)) {
throw new BadCredentialsException("Username/Password does not match for " + auth.getPrincipal()); throw new BadCredentialsException("Username/Password does not match for " + auth.getPrincipal());
} }
// User has been disabled ...
if (!user.isActive()) {
throw new BadCredentialsException("User has been disabled for login " + auth.getPrincipal());
}
userDetailsService.getUserService().auditLogin(user); userDetailsService.getUserService().auditLogin(user);
return new UsernamePasswordAuthenticationToken(userDetails, credentials, userDetails.getAuthorities()); return new UsernamePasswordAuthenticationToken(userDetails, credentials, userDetails.getAuthorities());
} }

View File

@ -23,6 +23,7 @@ import com.wisemapping.model.User;
import com.wisemapping.service.UserService; import com.wisemapping.service.UserService;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
@ -43,77 +44,6 @@ public class UserDetailsService
} }
} }
// @Override
// @NotNull
// public UserDetails loadUserDetails(@NotNull OpenIDAuthenticationToken token) throws UsernameNotFoundException {
//
// final User tUser = buildUserFromToken(token);
// final User dbUser = userService.getUserBy(tUser.getEmail());
//
// final User result;
// if (dbUser != null) {
// if (!token.getIdentityUrl().equals(dbUser.getAuthenticatorUri())) {
// throw new IllegalStateException("Identity url for this user can not change:" + token.getIdentityUrl());
// }
// result = dbUser;
// } else {
// try {
// tUser.setAuthenticationType(AuthenticationType.OPENID);
// tUser.setAuthenticatorUri(token.getIdentityUrl());
//
// result = userService.createUser(tUser, false, false);
// } catch (WiseMappingException e) {
// throw new IllegalStateException(e);
// }
//
// }
// return new UserDetails(result, isAdmin(result.getEmail()));
// }
// @NotNull
// private User buildUserFromToken(@NotNull OpenIDAuthenticationToken token) {
// final User result = new User();
//
// String lastName = null;
// String firstName = null;
// String email = null;
// String fullName = null;
//
// final List<OpenIDAttribute> attributes = token.getAttributes();
// for (OpenIDAttribute attribute : attributes) {
// if (attribute.getName().equals("email")) {
// email = attribute.getValues().get(0);
// }
//
// if (attribute.getName().equals("firstname")) {
// firstName = attribute.getValues().get(0);
//
// }
//
// if (attribute.getName().equals("lastname")) {
// lastName = attribute.getValues().get(0);
// }
//
// if (attribute.getName().equals("fullname")) {
// fullName = attribute.getValues().get(0);
// }
//
// }
// if (lastName == null || firstName == null) {
// result.setFirstname(fullName);
// result.setLastname("");
// } else {
// result.setLastname(lastName);
// result.setFirstname(firstName);
// }
// result.setEmail(email);
// result.setPassword("");
//
// final Calendar now = Calendar.getInstance();
// result.setActivationDate(now);
// return result;
// }
private boolean isAdmin(@Nullable String email) { private boolean isAdmin(@Nullable String email) {
return email != null && adminUser != null && email.trim().endsWith(adminUser); return email != null && adminUser != null && email.trim().endsWith(adminUser);
} }

View File

@ -22,13 +22,13 @@
</table> </table>
</div> </div>
<div style="font-size: 13px; background-color: #FFF; padding: 10px 7px 7px 7px; min-height: 100px"> <div style="font-size: 13px; background-color: #FFF; padding: 10px 7px 7px 7px; min-height: 100px">
Hi ${firstName}: ${messages.getMessage("EMAIL.GREETINGS",$noArgs,$locale)} ${firstName}:
<p> <p>
${messageBody} ${messageBody}
</p> </p>
<p style="color: #898989;">Regards,<br/> <p style="color: #898989;">
The WiseMapping Team<br/> <a href="http://www.wisemapping.com">The WiseMapping Team</a><br/>
</p> </p>
</div> </div>
</div> </div>

View File

@ -1,29 +1,29 @@
<html> <html>
<body> <body>
<h3>Welcome to WiseMapping!</h3> <div>
<h3>Welcome to WiseMapping!</h3>
<p> <p>
To active your account and verify your e-mail address, please click on the following link. To active your account and verify your e-mail address, please click on the following link.
</p> </p>
<a href="${emailcheck}">${emailcheck}</a> <a href="${emailcheck}">${emailcheck}</a>
</p> </p>
<p> <p>
If you have received this mail by error, you do not need to take any action to cancel the account. The account will If you have received this mail by error, you do not need to take any action to cancel the account. The account will
not be activated, and you will not receive any further emails. not be activated, and you will not receive any further emails.
</p> </p>
<p> <p>
If clicking the link above does not work, copy and paste the URL in a new browser window instead. If clicking the link above does not work, copy and paste the URL in a new browser window instead.
</p> </p>
<p> <p>
For questions or concerns regarding your account, send us an email to support@wisemapping.com. For questions or concerns regarding your account, send us an email to support@wisemapping.com.
</p> </p>
<p> <p style="color: #898989;">
Cheers, <br/> <a href="http://www.wisemapping.com">The WiseMapping Team</a><br/>
The WiseMapping Team. </p>
<a href="http://www.wisemapping.com">WiseMapping Site</a> <div>
</p>
</body> </body>
</html> </html>

View File

@ -33,6 +33,8 @@
</div> </div>
</div> </div>
<p style="font-size: 13px;font-family: Arial, sans-serif">${doNotReplay}</p> <p style="color: #898989;">
<a href="http://www.wisemapping.com">The WiseMapping Team</a><br/>
</p>
</body> </body>
</html> </html>

View File

@ -67,3 +67,5 @@ INVALID_MINDMAP_FORMAT=Ungültiges Mindmap-Format.
TOO_BIG_MINDMAP=Sie haben das Limit von 5000 Themen in einer Mindmap erreicht. TOO_BIG_MINDMAP=Sie haben das Limit von 5000 Themen in einer Mindmap erreicht.
SHARE_MAP.EMAIL_SUBJECT={0} hat eine Mindmap mit Ihnen geteilt SHARE_MAP.EMAIL_SUBJECT={0} hat eine Mindmap mit Ihnen geteilt
EMAIL.DO_NOT_REPLAY=Wichtig: Antworten Sie nicht auf diese E-Mail. Wenn Sie weitere Hilfe benötigen oder Bedenken bezüglich Ihres Kontos haben, kontaktieren Sie uns <a href="mailto:{0}">hier</a>. EMAIL.DO_NOT_REPLAY=Wichtig: Antworten Sie nicht auf diese E-Mail. Wenn Sie weitere Hilfe benötigen oder Bedenken bezüglich Ihres Kontos haben, kontaktieren Sie uns <a href="mailto:{0}">hier</a>.
EMAIL.GREETINGS=Hallo
OWNER_ROLE_CAN_NOT_BE_CHANGED=Owner role can not be change. Please, remove owner from the change list.

View File

@ -68,3 +68,6 @@ PASSWORD_CHANGED.EMAIL_TITLE=Your password has been changed successfully
PASSWORD_CHANGED.EMAIL_BODY=<p>This is only an notification that your password has been changed. No further action is required.</p> PASSWORD_CHANGED.EMAIL_BODY=<p>This is only an notification that your password has been changed. No further action is required.</p>
SHARE_MAP.EMAIL_SUBJECT={0} has shared a mind map with you SHARE_MAP.EMAIL_SUBJECT={0} has shared a mind map with you
EMAIL.DO_NOT_REPLAY=Important: Do not reply this email. If you need further help or have any concerns regarding your account, contact us to <a href="mailto:{0}">here</a>. EMAIL.DO_NOT_REPLAY=Important: Do not reply this email. If you need further help or have any concerns regarding your account, contact us to <a href="mailto:{0}">here</a>.
EMAIL.GREETINGS=Hi
TOO_MANY_INACTIVE_ACCOUNTS=You have shared your mindmaps to more than 20 user that have not registered yet. Please, remove inactive accounts or ask them to register.
OWNER_ROLE_CAN_NOT_BE_CHANGED=Owner role can not be change. Please, remove owner from the change list.

View File

@ -58,7 +58,7 @@ INVALID_MINDMAP_FORMAT=Formato de mapa mental no válido.
TOO_BIG_MINDMAP=Ha alcanzado el límite de 5000 nodos en un mapa mental. TOO_BIG_MINDMAP=Ha alcanzado el límite de 5000 nodos en un mapa mental.
REGISTRATION.EMAIL_SUBJECT=Bienvenido/a a WiseMapping ! REGISTRATION.EMAIL_SUBJECT=Bienvenido/a a WiseMapping !
REGISTRATION.EMAIL_TITLE=Tu cuenta ha sido creada exitosamente REGISTRATION.EMAIL_TITLE=Tu cuenta ha sido creada exitosamente
REGISTRATION.EMAIL_BODY=<p> Gracias por tu interest en WiseMapping. Hace click <a href='https://app.wisemapping.com/c/login'>aqui</a> para empezar a crear y compatir tus mapas mentales. Ideas y sugerencias, no dudes en contactarnos a <a href=\"mailto:feedback@wisemapping.com\">feedback@wisemapping.com</a>.</p> REGISTRATION.EMAIL_BODY=<p> Gracias por tu interes en WiseMapping. Hace click <a href='https://app.wisemapping.com/c/login'>aqui</a> para empezar a crear y compatir tus mapas mentales. Ideas y sugerencias, no dudes en contactarnos a <a href=\"mailto:feedback@wisemapping.com\">feedback@wisemapping.com</a>.</p>
CHANGE_PASSWORD.EMAIL_SUBJECT=Su contraseña ha sido restablecida CHANGE_PASSWORD.EMAIL_SUBJECT=Su contraseña ha sido restablecida
CHANGE_PASSWORD.EMAIL_TITLE=Se ha generado una contraseña temporal CHANGE_PASSWORD.EMAIL_TITLE=Se ha generado una contraseña temporal
CHANGE_PASSWORD.EMAIL_BODY=<p>Alguien, muy probablemente usted, solicitó una nueva contraseña para su cuenta de WiseMapping. </p><p><strong>Esta es su nueva contraseña: {0} </strong></p><p>Puede iniciar sesión haciendo clic <a href='{1}/c/login'>aquí </a>. Te recomendamos que cambie la contraseña lo antes posible.</p> CHANGE_PASSWORD.EMAIL_BODY=<p>Alguien, muy probablemente usted, solicitó una nueva contraseña para su cuenta de WiseMapping. </p><p><strong>Esta es su nueva contraseña: {0} </strong></p><p>Puede iniciar sesión haciendo clic <a href='{1}/c/login'>aquí </a>. Te recomendamos que cambie la contraseña lo antes posible.</p>
@ -67,3 +67,5 @@ PASSWORD_CHANGED.EMAIL_TITLE=Su contraseña ha sido cambiada con éxito
PASSWORD_CHANGED.EMAIL_BODY=<p>Esto es solo una notificación de que su contraseña ha sido cambiada. No se requiere ninguna otra acción.</p> PASSWORD_CHANGED.EMAIL_BODY=<p>Esto es solo una notificación de que su contraseña ha sido cambiada. No se requiere ninguna otra acción.</p>
SHARE_MAP.EMAIL_SUBJECT={0} te ha compartido un mapa mental SHARE_MAP.EMAIL_SUBJECT={0} te ha compartido un mapa mental
EMAIL.DO_NOT_REPLAY=Importante: No responda este correo electrónico. Si necesita más ayuda o tiene alguna inquietud con respecto a su cuenta, comuníquese con nosotros a <a href="mailto:{0}">aquí</a>. EMAIL.DO_NOT_REPLAY=Importante: No responda este correo electrónico. Si necesita más ayuda o tiene alguna inquietud con respecto a su cuenta, comuníquese con nosotros a <a href="mailto:{0}">aquí</a>.
EMAIL.GREETINGS=Hola
OWNER_ROLE_CAN_NOT_BE_CHANGED=Owner role can not be change. Please, remove owner from the change list.

View File

@ -67,3 +67,5 @@ INVALID_MINDMAP_FORMAT=Format de carte mentale non valide.
TOO_BIG_MINDMAP=Vous avez atteint la limite de 5000 sujets dans une carte mentale. TOO_BIG_MINDMAP=Vous avez atteint la limite de 5000 sujets dans une carte mentale.
SHARE_MAP.EMAIL_SUBJECT={0} a partagé une carte mentale avec vous SHARE_MAP.EMAIL_SUBJECT={0} a partagé une carte mentale avec vous
EMAIL.DO_NOT_REPLAY=Important : Ne répondez pas à cet e-mail. Si vous avez besoin d'aide supplémentaire ou si vous avez des inquiétudes concernant votre compte, contactez-nous <a href="mailto:{0}">ici</a>. EMAIL.DO_NOT_REPLAY=Important : Ne répondez pas à cet e-mail. Si vous avez besoin d'aide supplémentaire ou si vous avez des inquiétudes concernant votre compte, contactez-nous <a href="mailto:{0}">ici</a>.
EMAIL.GREETINGS=Salut
OWNER_ROLE_CAN_NOT_BE_CHANGED=Owner role can not be change. Please, remove owner from the change list.

View File

@ -61,3 +61,5 @@ REGISTRATION.EMAIL_TITLE=Ваша учетная запись успешно с
REGISTRATION.EMAIL_BODY=<p> Благодарим вас за интерес к WiseMapping. Нажмите <a href='https://app.wisemapping.com/c/login'>здесь</a>, чтобы начать создавать и публиковать новые интеллект-карты. Если у вас есть какие-либо отзывы или идеи, отправьте нам электронное письмо по адресу <a href=\"mailto:feedback@wisemapping.com\">feedback@wisemapping.com</a>. Мы будем рады услышать от вас.</p > REGISTRATION.EMAIL_BODY=<p> Благодарим вас за интерес к WiseMapping. Нажмите <a href='https://app.wisemapping.com/c/login'>здесь</a>, чтобы начать создавать и публиковать новые интеллект-карты. Если у вас есть какие-либо отзывы или идеи, отправьте нам электронное письмо по адресу <a href=\"mailto:feedback@wisemapping.com\">feedback@wisemapping.com</a>. Мы будем рады услышать от вас.</p >
SHARE_MAP.EMAIL_SUBJECT={0} has shared a mindmap with you SHARE_MAP.EMAIL_SUBJECT={0} has shared a mindmap with you
EMAIL.DO_NOT_REPLAY=Important: Do not reply this email. If you need further help or have any concerns regarding your account, contact us to <a href="mailto:{0}">here</a>. EMAIL.DO_NOT_REPLAY=Important: Do not reply this email. If you need further help or have any concerns regarding your account, contact us to <a href="mailto:{0}">here</a>.
EMAIL.GREETINGS=Hi
OWNER_ROLE_CAN_NOT_BE_CHANGED=Owner role can not be change. Please, remove owner from the change list.

View File

@ -67,3 +67,5 @@ PASSWORD_CHANGED.EMAIL_TITLE=你已经成功更改密码
PASSWORD_CHANGED.EMAIL_BODY=<p>这只是您的密码已更改的通知。无需进一步操作。</p> PASSWORD_CHANGED.EMAIL_BODY=<p>这只是您的密码已更改的通知。无需进一步操作。</p>
SHARE_MAP.EMAIL_SUBJECT={0} 与您分享了一张思维导图 SHARE_MAP.EMAIL_SUBJECT={0} 与您分享了一张思维导图
EMAIL.DO_NOT_REPLAY=Important: Do not reply this email. If you need further help or have any concerns regarding your account, contact us to <a href="mailto:{0}">here</a>. EMAIL.DO_NOT_REPLAY=Important: Do not reply this email. If you need further help or have any concerns regarding your account, contact us to <a href="mailto:{0}">here</a>.
EMAIL.GREETINGS=Hi
OWNER_ROLE_CAN_NOT_BE_CHANGED=Owner role can not be change. Please, remove owner from the change list.

View File

@ -134,6 +134,11 @@ security.ldap.auth.attribute=mail
security.ldap.lastName.attribute=sn security.ldap.lastName.attribute=sn
security.ldap.firstName.attribute=givenName security.ldap.firstName.attribute=givenName
# User Account filtering policies
# Coma separated list of domains and emails ban
#accounts.exclusion.domain=

View File

@ -71,8 +71,13 @@ div#position {
background-size: 40px 40px; background-size: 40px 40px;
background-color: #FFF; background-color: #FFF;
border-radius: 8px; border-radius: 8px;
padding: 0;
} }
#position-button>img {
vertical-align: middle;
}
#zoom-button { #zoom-button {
width: 40px; width: 40px;
border: 0; border: 0;
@ -88,6 +93,13 @@ div#position {
background-position: center; background-position: center;
cursor: pointer; cursor: pointer;
background-color: #FFF; background-color: #FFF;
padding: 0;
}
#zoom-plus,
#zoom-minus
>img {
vertical-align: middle;
} }
#zoom-plus { #zoom-plus {

View File

@ -1,16 +0,0 @@
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:if test="${requestScope['google.analytics.enabled']}">
<!-- Google Analytics -->
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', '${requestScope['google.analytics.account']}', 'auto');
ga('send', 'pageview');
</script>
<!-- End Google Analytics -->
</c:if>

View File

@ -23,7 +23,7 @@
<script> <script>
window.serverconfig = { window.serverconfig = {
apiBaseUrl: '', apiBaseUrl: '${requestScope['site.baseurl']}',
analyticsAccount: '${requestScope['google.analytics.account']}', analyticsAccount: '${requestScope['google.analytics.account']}',
clientType: 'rest', clientType: 'rest',
recaptcha2Enabled: ${requestScope['google.recaptcha2.enabled']}, recaptcha2Enabled: ${requestScope['google.recaptcha2.enabled']},
@ -44,7 +44,7 @@
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div> <div id="root" class="mindplot-root"></div>
<script type="text/javascript" src="${requestScope['site.static.js.url']}/webapp/vendors.bundle.js" crossorigin="anonymous"></script> <script type="text/javascript" src="${requestScope['site.static.js.url']}/webapp/vendors.bundle.js" crossorigin="anonymous"></script>
<script type="text/javascript" src="${requestScope['site.static.js.url']}/webapp/app.bundle.js" crossorigin="anonymous"></script> <script type="text/javascript" src="${requestScope['site.static.js.url']}/webapp/app.bundle.js" crossorigin="anonymous"></script>

View File

@ -7,6 +7,7 @@
<html> <html>
<head> <head>
<meta name="viewport" content="initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<base href="${requestScope['site.baseurl']}/static/mindplot/"> <base href="${requestScope['site.baseurl']}/static/mindplot/">
<title>${mindmap.title} | <spring:message code="SITE.TITLE"/></title> <title>${mindmap.title} | <spring:message code="SITE.TITLE"/></title>
@ -15,28 +16,59 @@
<script type="text/javascript"> <script type="text/javascript">
var mapId = '${mindmap.id}'; var mapId = '${mindmap.id}';
var historyId = '${hid}'; var historyId = '${hid}';
var readOnly = true;
var userOptions = ${mindmap.properties}; var userOptions = ${mindmap.properties};
var locale = '${locale}'; var locale = '${locale}';
var isAuth = ${principal != null}; var isAuth = ${principal != null};
</script> </script>
<%@ include file="/jsp/googleAnalytics.jsf" %> <c:if test="${requestScope['google.analytics.enabled']}">
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=${requestScope['google.analytics.account']}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${requestScope['google.analytics.account']}',
{
'page_title' : 'Public View'
});
</script>
</c:if>
<style>
body {
height: 100vh;
width: 100vw;
min-width: 100vw;
min-height: 100vh;
margin: 0px;
}
.mindplot-root {
height: 100%;
width: 100%;
}
</style>
</head> </head>
<body> <body>
<div id="mindplot"></div> <div id="root" class="mindplot-root">
<mindplot-component id="mindmap-comp"></mindplot-component>
<div id="mindplot-tooltips" className="wise-editor"></div>
<a href="${requestScope['site.homepage']}" target="new">
<div id="footerLogo"></div>
</a>
<div id="mapDetails"> <a href="${requestScope['site.homepage']}" target="new">
<div id="footerLogo"></div>
</a>
<div id="mapDetails">
<span class="title"><spring:message code="CREATOR"/>:</span><span>${mindmap.creator.fullName}</span> <span class="title"><spring:message code="CREATOR"/>:</span><span>${mindmap.creator.fullName}</span>
<span class="title"><spring:message code="DESCRIPTION"/>:</span><span>${mindmap.title}</span> <span class="title"><spring:message code="DESCRIPTION"/>:</span><span>${mindmap.title}</span>
</div> </div>
<script type="text/javascript" src="${requestScope['site.static.js.url']}/mindplot/loader.js" crossorigin="anonymous"></script> <script type="text/javascript" src="${requestScope['site.static.js.url']}/mindplot/loader.js" crossorigin="anonymous"></script>
<div id="floating-panel"> <div id="floating-panel">
<div id="zoom-button"> <div id="zoom-button">
<button id="zoom-plus"> <button id="zoom-plus">
<img src="../../images/add.svg" /> <img src="../../images/add.svg" />
@ -50,9 +82,10 @@
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div>
<script type="text/javascript"> <script type="text/javascript">
// Hook zoom events ... // Hook zoom events ...
const zoomInButton = document.getElementById('zoom-plus'); const zoomInButton = document.getElementById('zoom-plus');
if (zoomInButton) { if (zoomInButton) {
@ -74,7 +107,6 @@
designer.zoomToFit(); designer.zoomToFit();
}); });
} }
</script>
</script>
</body> </body>
</html> </html>

View File

@ -14,7 +14,7 @@
<script> <script>
window.serverconfig = { window.serverconfig = {
apiBaseUrl: '', apiBaseUrl: '${requestScope['site.baseurl']}',
analyticsAccount: '${requestScope['google.analytics.account']}', analyticsAccount: '${requestScope['google.analytics.account']}',
clientType: 'rest', clientType: 'rest',
recaptcha2Enabled: ${requestScope['google.recaptcha2.enabled']}, recaptcha2Enabled: ${requestScope['google.recaptcha2.enabled']},

View File

@ -1,7 +1,6 @@
Sitemap: https://www.wisemapping.com/sitemap.xml Sitemap: https://www.wisemapping.com/sitemap.xml
# https://www.robotstxt.org/robotstxt.html
User-agent: * User-agent: *
Allow: / Allow: /
Disallow: /c/restful/maps/*/document/xml-pub Disallow: /c/restful/maps/*/document/xml-pub
Disallow: /c/maps/*/edit

View File

@ -83,7 +83,7 @@ public class RestAdminITCase {
} }
} }
public String createNewUser(final @NotNull MediaType mediaType) { public RestUser createNewUserAndGetUser(final @NotNull MediaType mediaType) {
// Configure media types ... // Configure media types ...
final HttpHeaders requestHeaders = createHeaders(mediaType); final HttpHeaders requestHeaders = createHeaders(mediaType);
@ -99,6 +99,13 @@ public class RestAdminITCase {
ResponseEntity<RestUser> result = findUser(requestHeaders, templateRest, location); ResponseEntity<RestUser> result = findUser(requestHeaders, templateRest, location);
assertEquals(result.getBody().getEmail(), restUser.getEmail(), "Returned object object seems not be the same."); assertEquals(result.getBody().getEmail(), restUser.getEmail(), "Returned object object seems not be the same.");
return result.getBody();
}
public String createNewUser(final @NotNull MediaType mediaType) {
// Fill user data ...
final RestUser restUser = createNewUserAndGetUser(mediaType);
// Find by email and check ... // Find by email and check ...
// @todo: review find by email... It's failing with 406 // @todo: review find by email... It's failing with 406
// findUser(requestHeaders, templateRest, location); // findUser(requestHeaders, templateRest, location);
@ -119,7 +126,7 @@ public class RestAdminITCase {
return templateRest.exchange(url, HttpMethod.GET, findUserEntity, RestUser.class); return templateRest.exchange(url, HttpMethod.GET, findUserEntity, RestUser.class);
} }
private ResponseEntity<RestUser> findUserByEmail(HttpHeaders requestHeaders, RestTemplate templateRest, final String email) { public ResponseEntity<RestUser> findUserByEmail(HttpHeaders requestHeaders, RestTemplate templateRest, final String email) {
HttpEntity<RestUser> findUserEntity = new HttpEntity<>(requestHeaders); HttpEntity<RestUser> findUserEntity = new HttpEntity<>(requestHeaders);
// Add extension only to avoid the fact that the last part is extracted ... // Add extension only to avoid the fact that the last part is extracted ...

View File

@ -78,6 +78,7 @@ public class RestLabelITCase {
@Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function") @Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function")
public void createLabelWithoutRequiredField(final @NotNull MediaType mediaType) throws IOException, WiseMappingException { public void createLabelWithoutRequiredField(final @NotNull MediaType mediaType) throws IOException, WiseMappingException {
final HttpHeaders requestHeaders = RestHelper.createHeaders(mediaType); final HttpHeaders requestHeaders = RestHelper.createHeaders(mediaType);
requestHeaders.set(HttpHeaders.ACCEPT_LANGUAGE, "en");
final RestTemplate template = RestHelper.createTemplate(userEmail + ":" + "admin"); final RestTemplate template = RestHelper.createTemplate(userEmail + ":" + "admin");
try { try {

View File

@ -14,10 +14,18 @@ import org.testng.annotations.Test;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import static com.wisemapping.test.rest.RestHelper.*; import static com.wisemapping.test.rest.RestHelper.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.springframework.web.client.RestClientException;
import static org.testng.Assert.*; import static org.testng.Assert.*;
@Test @Test
@ -25,11 +33,11 @@ public class RestMindmapITCase {
private String userEmail = "admin@wisemapping.com"; private String userEmail = "admin@wisemapping.com";
private static final String ICON = "glyphicon glyphicon-tag"; private static final String ICON = "glyphicon glyphicon-tag";
final RestAdminITCase restAdminITCase = new RestAdminITCase();
@BeforeClass @BeforeClass
void createUser() { void createUser() {
final RestAdminITCase restAdminITCase = new RestAdminITCase();
userEmail = restAdminITCase.createNewUser(MediaType.APPLICATION_JSON); userEmail = restAdminITCase.createNewUser(MediaType.APPLICATION_JSON);
userEmail += ":" + "admin"; userEmail += ":" + "admin";
} }
@ -46,12 +54,8 @@ public class RestMindmapITCase {
final String title2 = "List Maps 2 - " + mediaType; final String title2 = "List Maps 2 - " + mediaType;
addNewMap(template, title2); addNewMap(template, title2);
// Check that the map has been created ...
final HttpEntity findMapEntity = new HttpEntity(requestHeaders);
final ResponseEntity<RestMindmapList> response = template.exchange(BASE_REST_URL + "/maps/", HttpMethod.GET, findMapEntity, RestMindmapList.class);
// Validate that the two maps are there ... // Validate that the two maps are there ...
final RestMindmapList body = response.getBody(); final RestMindmapList body = fetchMaps(requestHeaders, template);
final List<RestMindmapInfo> mindmaps = body.getMindmapsInfo(); final List<RestMindmapInfo> mindmaps = body.getMindmapsInfo();
boolean found1 = false; boolean found1 = false;
@ -96,11 +100,7 @@ public class RestMindmapITCase {
// Create a sample map ... // Create a sample map ...
final URI resourceUri = addNewMap(template, "Map to change title - " + mediaType); final URI resourceUri = addNewMap(template, "Map to change title - " + mediaType);
// Change map title ... String newTitle = changeMapTitle(requestHeaders, mediaType, template, resourceUri);
requestHeaders.setContentType(MediaType.TEXT_PLAIN);
final String newTitle = "New map to change title - " + mediaType;
final HttpEntity<String> updateEntity = new HttpEntity<>(newTitle, requestHeaders);
template.put(HOST_PORT + resourceUri + "/title", updateEntity);
// Load map again .. // Load map again ..
final RestMindmap map = findMap(requestHeaders, template, resourceUri); final RestMindmap map = findMap(requestHeaders, template, resourceUri);
@ -110,6 +110,7 @@ public class RestMindmapITCase {
@Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function") @Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function")
public void validateMapsCreation(final @NotNull MediaType mediaType) { // Configure media types ... public void validateMapsCreation(final @NotNull MediaType mediaType) { // Configure media types ...
final HttpHeaders requestHeaders = createHeaders(mediaType); final HttpHeaders requestHeaders = createHeaders(mediaType);
requestHeaders.set(HttpHeaders.ACCEPT_LANGUAGE, "en");
final RestTemplate template = createTemplate(userEmail); final RestTemplate template = createTemplate(userEmail);
// Create a sample map ... // Create a sample map ...
@ -160,16 +161,25 @@ public class RestMindmapITCase {
// Update map xml content ... // Update map xml content ...
final String resourceUrl = HOST_PORT + resourceUri.toString(); final String resourceUrl = HOST_PORT + resourceUri.toString();
requestHeaders.setContentType(MediaType.TEXT_PLAIN); String newXmlContent = updateMapDocument(requestHeaders, template, resourceUrl);
final String newXmlContent = "<map>this is not valid</map>";
HttpEntity<String> updateEntity = new HttpEntity<>(newXmlContent, requestHeaders);
template.put(resourceUrl + "/document/xml", updateEntity);
// Check that the map has been updated ... // Check that the map has been updated ...
final RestMindmap response = findMap(requestHeaders, template, resourceUri); final RestMindmap response = findMap(requestHeaders, template, resourceUri);
assertEquals(response.getXml(), newXmlContent); assertEquals(response.getXml(), newXmlContent);
} }
private String updateMapDocument(final HttpHeaders requestHeaders, final RestTemplate template, final String resourceUrl, String content) throws RestClientException {
requestHeaders.setContentType(MediaType.TEXT_PLAIN);
final String newXmlContent = content != null ? content : "<map>this is not valid</map>";
HttpEntity<String> updateEntity = new HttpEntity<>(newXmlContent, requestHeaders);
template.put(resourceUrl + "/document/xml", updateEntity);
return newXmlContent;
}
private String updateMapDocument(final HttpHeaders requestHeaders, final RestTemplate template, final String resourceUrl) throws RestClientException {
return updateMapDocument(requestHeaders, template, resourceUrl, null);
}
@Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function") @Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function")
public void cloneMap(final @NotNull MediaType mediaType) throws IOException { // Configure media types ... public void cloneMap(final @NotNull MediaType mediaType) throws IOException { // Configure media types ...
final HttpHeaders requestHeaders = createHeaders(mediaType); final HttpHeaders requestHeaders = createHeaders(mediaType);
@ -194,9 +204,67 @@ public class RestMindmapITCase {
assertEquals(response.getXml(), xml); assertEquals(response.getXml(), xml);
} }
@Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function")
public void updateStarred(final @NotNull MediaType mediaType) { // Configure media types ...
final HttpHeaders requestHeaders = createHeaders(mediaType);
RestTemplate template = createTemplate(userEmail);
// Create a sample map ...
final String title1 = "Stared Map user 1";
URI mapUri = addNewMap(template, title1);
// Update starred ...
final String resourceUrl = HOST_PORT + mapUri.toString() + "/starred";
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
final HttpHeaders textContentType = new HttpHeaders();
textContentType.setContentType(MediaType.TEXT_PLAIN);
final HttpEntity<String> updateEntity = new HttpEntity<>("true", textContentType);
template.put(resourceUrl, updateEntity);
// Has been updated ?.
final String mapId = mapUri.getPath().replace("/service/maps/", "");
Optional<RestMindmapInfo> mindmapInfo = fetchMap(requestHeaders, template, mapId);
// @todo: Enforce check...
// assertTrue(mindmapInfo.get().getStarred() == true);
}
@Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function") @Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function")
public void verifyMapOwnership(final @NotNull MediaType mediaType) { // Configure media types ... public void verifyMapOwnership(final @NotNull MediaType mediaType) { // Configure media types ...
throw new SkipException("missing test: removeUserShouldOnlyDeleteOnwedMap"); final RestAdminITCase restAdminITCase = new RestAdminITCase();
final HttpHeaders requestHeaders = createHeaders(mediaType);
RestTemplate template = createTemplate(userEmail);
// Create a sample map ...
final String title1 = "verifyMapOwnership Map user 1";
addNewMap(template, title1);
//create another user
RestUser secondUser = restAdminITCase.createNewUserAndGetUser(MediaType.APPLICATION_JSON);
final RestTemplate secondTemplate = createTemplate(secondUser.getEmail() + ":admin");
final String title2 = "verifyMapOwnership Map user 2";
addNewMap(secondTemplate, title2);
// Delete user ...
String authorisation = "admin@wisemapping.org" + ":" + "test";
RestTemplate superadminTemplate = createTemplate(authorisation);
superadminTemplate.delete(BASE_REST_URL + "/admin/users/" + secondUser.getId());
// Validate that the two maps are there ...
final RestMindmapList body = fetchMaps(requestHeaders, template);
final List<RestMindmapInfo> mindmaps = body.getMindmapsInfo();
boolean found1 = false;
for (RestMindmapInfo mindmap : mindmaps) {
if (mindmap.getTitle().equals(title1)) {
found1 = true;
break;
}
}
assertTrue(found1, "Map could not be found");
} }
@Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function") @Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function")
@ -242,25 +310,10 @@ public class RestMindmapITCase {
// Create a sample map ... // Create a sample map ...
final URI resourceUri = addNewMap(template, "Map for addCollabs - " + mediaType); final URI resourceUri = addNewMap(template, "Map for addCollabs - " + mediaType);
// Add a new collaboration ... String newCollab = addNewCollaboration(requestHeaders, template, resourceUri);
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
final RestCollaborationList collabs = new RestCollaborationList();
collabs.setMessage("Adding new permission");
final String newCollab = "new-collab@example.com";
String role = "editor";
final RestCollaboration collab = new RestCollaboration();
collab.setEmail(newCollab);
collab.setRole(role);
collabs.addCollaboration(collab);
final HttpEntity<RestCollaborationList> updateEntity = new HttpEntity<>(collabs, requestHeaders);
template.put(HOST_PORT + resourceUri + "/collabs/", updateEntity);
// Has been added ? // Has been added ?
final ResponseEntity<RestCollaborationList> response = fetchCollabs(requestHeaders, template, resourceUri); RestCollaborationList responseCollbs = fetchAndGetCollabs(requestHeaders, template, resourceUri);
RestCollaborationList responseCollbs = response.getBody();
// Has been added ? // Has been added ?
assertEquals(responseCollbs.getCount(), 2); assertEquals(responseCollbs.getCount(), 2);
@ -286,17 +339,13 @@ public class RestMindmapITCase {
final String newCollab = "new-collab@example.com"; final String newCollab = "new-collab@example.com";
String role = "editor"; String role = "editor";
final RestCollaboration collab = new RestCollaboration(); final RestCollaboration collab = addCollabToList(newCollab, role, collabs);
collab.setEmail(newCollab);
collab.setRole(role);
collabs.addCollaboration(collab);
final HttpEntity<RestCollaborationList> updateEntity = new HttpEntity<>(collabs, requestHeaders); final HttpEntity<RestCollaborationList> updateEntity = new HttpEntity<>(collabs, requestHeaders);
template.put(HOST_PORT + resourceUri + "/collabs/", updateEntity); template.put(HOST_PORT + resourceUri + "/collabs/", updateEntity);
// Has been added ? // Has been added ?
final ResponseEntity<RestCollaborationList> response = fetchCollabs(requestHeaders, template, resourceUri); RestCollaborationList responseCollbs = fetchAndGetCollabs(requestHeaders, template, resourceUri);
RestCollaborationList responseCollbs = response.getBody();
assertEquals(responseCollbs.getCount(), 2); assertEquals(responseCollbs.getCount(), 2);
// Update the collaboration type ... // Update the collaboration type ...
@ -318,25 +367,10 @@ public class RestMindmapITCase {
// Create a sample map ... // Create a sample map ...
final URI resourceUri = addNewMap(template, "Map for deleteCollabs - " + mediaType); final URI resourceUri = addNewMap(template, "Map for deleteCollabs - " + mediaType);
// Add a new collaboration ... String newCollab = addNewCollaboration(requestHeaders, template, resourceUri);
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
final RestCollaborationList collabs = new RestCollaborationList();
collabs.setMessage("Adding new permission");
final String newCollab = "new-collab@example.com";
String role = "editor";
final RestCollaboration collab = new RestCollaboration();
collab.setEmail(newCollab);
collab.setRole(role);
collabs.addCollaboration(collab);
final HttpEntity<RestCollaborationList> updateEntity = new HttpEntity<>(collabs, requestHeaders);
template.put(HOST_PORT + resourceUri + "/collabs/", updateEntity);
// Has been added ? // Has been added ?
final ResponseEntity<RestCollaborationList> response = fetchCollabs(requestHeaders, template, resourceUri); RestCollaborationList responseCollbs = fetchAndGetCollabs(requestHeaders, template, resourceUri);
RestCollaborationList responseCollbs = response.getBody();
// Has been added ? // Has been added ?
assertEquals(responseCollbs.getCount(), 2); assertEquals(responseCollbs.getCount(), 2);
@ -349,6 +383,82 @@ public class RestMindmapITCase {
assertEquals(afterDeleteResponse.getBody().getCollaborations().size(), 1); assertEquals(afterDeleteResponse.getBody().getCollaborations().size(), 1);
} }
private String addNewCollaboration(final HttpHeaders requestHeaders, final RestTemplate template, final URI resourceUri) throws RestClientException {
// Add a new collaboration ...
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
final RestCollaborationList collabs = new RestCollaborationList();
collabs.setMessage("Adding new permission");
final String newCollab = "new-collab@example.com";
String role = "editor";
addCollabToList(newCollab, role, collabs);
final HttpEntity<RestCollaborationList> updateEntity = new HttpEntity<>(collabs, requestHeaders);
template.put(HOST_PORT + resourceUri + "/collabs/", updateEntity);
return newCollab;
}
@Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function")
public void deleteCollabsWithInvalidEmail(final @NotNull MediaType mediaType) {
final HttpHeaders requestHeaders = createHeaders(mediaType);
final RestTemplate template = createTemplate(userEmail);
// Create a sample map ...
final URI resourceUri = addNewMap(template, "deleteCollabsWithInvalidEmail");
// Remove with invalid email ...
try {
template.delete(HOST_PORT + resourceUri + "/collabs?email=invalidEmail");
} catch (HttpClientErrorException e) {
assertEquals(e.getRawStatusCode(), 400);
assertTrue(e.getMessage().contains("Invalid email exception:"));
}
// Check that it has been removed ...
final ResponseEntity<RestCollaborationList> afterDeleteResponse = fetchCollabs(requestHeaders, template, resourceUri);
assertEquals(afterDeleteResponse.getBody().getCollaborations().size(), 1);
}
@Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function")
public void deleteCollabsWithoutOwnerPermission(final @NotNull MediaType mediaType) {
final HttpHeaders requestHeaders = createHeaders(mediaType);
RestTemplate template = createTemplate(userEmail);
// Create a sample map ...
final URI resourceUri = addNewMap(template, "deleteWithoutOwnerPermission");
final String newCollab = restAdminITCase.createNewUser(MediaType.APPLICATION_JSON);
template = createTemplate(newCollab + ":admin");
// Remove with invalid email ...
try {
template.delete(HOST_PORT + resourceUri + "/collabs?email=" + newCollab);
} catch (HttpClientErrorException e) {
assertEquals(e.getRawStatusCode(), 400);
assertTrue(e.getMessage().contains("No enough permissions"));
}
}
@Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function")
public void deleteOwnerCollab(final @NotNull MediaType mediaType) {
final HttpHeaders requestHeaders = createHeaders(mediaType);
final RestTemplate template = createTemplate(userEmail);
// Create a sample map ...
final URI resourceUri = addNewMap(template, "Map for deleteOwnerCollab");
// Now, remove owner collab ...
try {
template.delete(HOST_PORT + resourceUri + "/collabs?email=" + userEmail.replace(":admin", ""));
} catch (HttpClientErrorException e) {
assertEquals(e.getRawStatusCode(), 400);
assertTrue(e.getMessage().contains("Can not remove owner collab"));
}
}
@NotNull @NotNull
private ResponseEntity<RestCollaborationList> fetchCollabs(HttpHeaders requestHeaders, RestTemplate template, URI resourceUri) { private ResponseEntity<RestCollaborationList> fetchCollabs(HttpHeaders requestHeaders, RestTemplate template, URI resourceUri) {
final HttpEntity findCollabs = new HttpEntity(requestHeaders); final HttpEntity findCollabs = new HttpEntity(requestHeaders);
@ -361,7 +471,7 @@ public class RestMindmapITCase {
final HttpHeaders requestHeaders = createHeaders(mediaType); final HttpHeaders requestHeaders = createHeaders(mediaType);
final RestTemplate template = createTemplate(userEmail); final RestTemplate template = createTemplate(userEmail);
// Create a sample map ... // Create a sample map ...fetchAndGetCollabs(requestHeaders, template, resourceUri);
final URI resourceUri = addNewMap(template, "Map for Collaboration - " + mediaType); final URI resourceUri = addNewMap(template, "Map for Collaboration - " + mediaType);
// Add a new collaboration ... // Add a new collaboration ...
@ -370,19 +480,48 @@ public class RestMindmapITCase {
collabs.setMessage("Adding new permission"); collabs.setMessage("Adding new permission");
// Validate that owner can not be added. // Validate that owner can not be added.
final RestCollaboration collab = new RestCollaboration(); addCollabToList("newCollab@example", "owner", collabs);
final String newCollab = "new-collab@example.com";
collab.setEmail(newCollab);
collab.setRole("owner");
collabs.addCollaboration(collab);
final HttpEntity<RestCollaborationList> updateEntity = new HttpEntity<>(collabs, requestHeaders); final HttpEntity<RestCollaborationList> updateEntity = new HttpEntity<>(collabs, requestHeaders);
template.put(HOST_PORT + resourceUri + "/collabs/", updateEntity); template.put(HOST_PORT + resourceUri + "/collabs/", updateEntity);
} }
@Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function") @Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function")
public void removeLabelFromMindmap(final @NotNull MediaType mediaType) { // Configure media types ... public void removeLabelFromMindmap(final @NotNull MediaType mediaType) throws IOException, WiseMappingException { // Configure media types ...
throw new SkipException("missing test: label removal from map"); final HttpHeaders requestHeaders = createHeaders(mediaType);
final RestTemplate template = createTemplate(userEmail);
// Create a new label
final String titleLabel = "removeLabelFromMindmap";
final URI labelUri = RestLabelITCase.addNewLabel(requestHeaders, template, titleLabel, COLOR, ICON);
// Create a sample map ...
final String mapTitle = "removeLabelFromMindmap";
final URI mindmapUri = addNewMap(template, mapTitle);
final String mapId = mindmapUri.getPath().replace("/service/maps/", "");
// Assign label to map ...
String labelId = labelUri.getPath().replace("/service/labels/", "");
HttpEntity<String> labelEntity = new HttpEntity<>(labelId, requestHeaders);
template.postForLocation(BASE_REST_URL + "/maps/" + mapId + "/labels", labelEntity);
// Remove label from map
template.delete(BASE_REST_URL + "/maps/" + mapId + "/labels/" + labelId);
Optional<RestMindmapInfo> mindmapInfo = fetchMap(requestHeaders, template, mapId);
assertTrue(mindmapInfo.get().getLabels().size() == 0);
}
@NotNull
private Optional<RestMindmapInfo> fetchMap(HttpHeaders requestHeaders, RestTemplate template, @NotNull String mapId) {
// Check that the label has been removed ...
final List<RestMindmapInfo> mindmapsInfo = fetchMaps(requestHeaders, template).getMindmapsInfo();
Optional<RestMindmapInfo> mindmapInfo = mindmapsInfo
.stream()
.filter(m -> m.getId() == Integer.parseInt(mapId))
.findAny();
return mindmapInfo;
} }
@Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function") @Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function")
@ -410,18 +549,266 @@ public class RestMindmapITCase {
template.postForLocation(BASE_REST_URL + "/maps/" + mapId + "/labels", labelEntity); template.postForLocation(BASE_REST_URL + "/maps/" + mapId + "/labels", labelEntity);
// Check that the label has been assigned ... // Check that the label has been assigned ...
final HttpEntity findMapEntity = new HttpEntity(requestHeaders); Optional<RestMindmapInfo> mindmapInfo = fetchMap(requestHeaders, template, mapId);
final ResponseEntity<RestMindmapList> mindmapList = template.exchange(BASE_REST_URL + "/maps/", HttpMethod.GET, findMapEntity, RestMindmapList.class);
final List<RestMindmapInfo> mindmapsInfo = mindmapList.getBody().getMindmapsInfo();
Optional<RestMindmapInfo> mindmapInfo = mindmapsInfo
.stream()
.filter(m -> m.getId() == Integer.parseInt(mapId))
.findAny();
assertTrue(mindmapInfo.get().getLabels().size() == 1); assertTrue(mindmapInfo.get().getLabels().size() == 1);
} }
@Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function")
public void updateCollabs(final @NotNull MediaType mediaType) {
// Create a sample map ...
final RestTemplate template = createTemplate(userEmail);
final URI resourceUri = addNewMap(template, "Map for updateCollabs - " + mediaType);
final HttpHeaders requestHeaders = createHeaders(mediaType);
// Add a new collaboration ...
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
RestCollaborationList collabs = new RestCollaborationList();
collabs.setMessage("Adding new permission");
String newCollab = "new-collab@example.com";
String role = "editor";
addCollabToList(newCollab, role, collabs);
HttpEntity<RestCollaborationList> updateEntity = new HttpEntity<>(collabs, requestHeaders);
template.put(HOST_PORT + resourceUri + "/collabs/", updateEntity);
collabs = fetchAndGetCollabs(requestHeaders, template, resourceUri);
//delete one collab
collabs.setCollaborations(collabs.getCollaborations().stream().filter(c -> c.getRole().equals("owner")).collect(Collectors.toList()));
//Add another collaborationMediaType
newCollab = "another-collab@example.com";
addCollabToList(newCollab, role, collabs);
//add owner to list
addCollabToList(userEmail.replace(":admin", ""), "owner", collabs);
updateEntity = new HttpEntity<>(collabs, requestHeaders);
template.postForLocation(HOST_PORT + resourceUri + "/collabs/", updateEntity);
RestCollaborationList responseCollbs = fetchAndGetCollabs(requestHeaders, template, resourceUri);
// Has been another-collaboration list updated ?
assertTrue(responseCollbs.getCollaborations().stream().anyMatch(x -> x.getEmail().equals("another-collab@example.com")));
assertEquals(responseCollbs.getCount(), 2);
}
@Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function")
public void updateProperties(final @NotNull MediaType mediaType) throws IOException, WiseMappingException { // Configure media types ...
final HttpHeaders requestHeaders = createHeaders(mediaType);
final RestTemplate template = createTemplate(userEmail);
// Create a sample map ...
final String title = "updateProperties map";
final URI resourceUri = addNewMap(template, title);
// Build map to update ...
final RestMindmap mapToUpdate = new RestMindmap();
mapToUpdate.setXml("<map>this is not valid</map>");
mapToUpdate.setProperties("{zoom:x}");
mapToUpdate.setTitle("new title for map");
mapToUpdate.setDescription("updated map description");
// Update map ...
final String resourceUrl = HOST_PORT + resourceUri.toString();
final HttpEntity<RestMindmap> updateEntity = new HttpEntity<>(mapToUpdate, requestHeaders);
template.put(resourceUrl, updateEntity);
// Check that the map has been updated ...
HttpEntity<RestUser> findMapEntity = new HttpEntity<>(requestHeaders);
final ResponseEntity<RestMindmap> response = template.exchange(HOST_PORT + resourceUri, HttpMethod.GET, findMapEntity, RestMindmap.class);
assertEquals(response.getBody().getTitle(), mapToUpdate.getTitle());
assertEquals(response.getBody().getDescription(), mapToUpdate.getDescription());
assertEquals(response.getBody().getXml(), mapToUpdate.getXml());
assertEquals(response.getBody().getProperties(), mapToUpdate.getProperties());
}
@Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function")
public void batchDelete(final @NotNull MediaType mediaType) { // Configure media types ...
final HttpHeaders requestHeaders = createHeaders(mediaType);
final RestTemplate template = createTemplate(userEmail);
// Create a sample map ...
final String title1 = "Batch delete map 1";
addNewMap(template, title1);
final String title2 = "Batch delete map 2";
addNewMap(template, title2);
String maps;
maps = fetchMaps(requestHeaders, template).getMindmapsInfo().stream().map(map -> {
return String.valueOf(map.getId());
}).collect(Collectors.joining(","));
template.delete(BASE_REST_URL + "/maps/batch?ids=" + maps);
// Validate that the two maps are there ...
final RestMindmapList body = fetchMaps(requestHeaders, template);
assertEquals(body.getMindmapsInfo().size(), 0);
}
@Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function")
public void updatePublishState(final @NotNull MediaType mediaType) throws IOException, WiseMappingException { // Configure media types ...
final HttpHeaders requestHeaders = createHeaders(mediaType);
final RestTemplate template = createTemplate(userEmail);
// Create a sample map ...
final String mapTitle = "updatePublishState";
final URI mindmapUri = addNewMap(template, mapTitle);
final String mapId = mindmapUri.getPath().replace("/service/maps/", "");
// Change map status ...
requestHeaders.setContentType(MediaType.TEXT_PLAIN);
//final String newPublicState = "true";
final HttpEntity<String> updateEntity = new HttpEntity<>(Boolean.TRUE.toString(), requestHeaders);
template.put(HOST_PORT + mindmapUri + "/publish", updateEntity);
//fetch public view
final HttpEntity findMapEntity = new HttpEntity(requestHeaders);
ResponseEntity<String> publicView = template.exchange(HOST_PORT + "/c/" + mapId + "/public", HttpMethod.GET, findMapEntity, String.class);
assertNotNull(publicView.getBody());
assertEquals(publicView.getStatusCodeValue(), 200);
}
@Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function")
public void fetchMapHistory(final @NotNull MediaType mediaType) { // Configure media types ...
final HttpHeaders requestHeaders = createHeaders(mediaType);
final RestTemplate template = createTemplate(userEmail);
// Create a sample map ...
final URI resourceUri = addNewMap(template, "Map to change title - " + mediaType);
updateMapDocument(requestHeaders, template, HOST_PORT + resourceUri.toString());
//fetch map history
final HttpEntity findMapEntity = new HttpEntity(requestHeaders);
final ResponseEntity<RestMindmapHistoryList> maps = template.exchange(HOST_PORT + resourceUri + "/history/", HttpMethod.GET, findMapEntity, RestMindmapHistoryList.class);
assertEquals(maps.getBody().getCount(), 1);
}
@Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function")
public void updateRevertMindmap(final @NotNull MediaType mediaType) throws IOException { // Configure media types ...
final HttpHeaders requestHeaders = createHeaders(mediaType);
final RestTemplate template = createTemplate(userEmail);
// Create a sample map ...
final URI resourceUri = addNewMap(template, "map to test revert changes");
updateMapDocument(requestHeaders, template, HOST_PORT + resourceUri.toString(), "<map><node text='this is an xml to test revert changes service'></map>");
updateMapDocument(requestHeaders, template, HOST_PORT + resourceUri.toString(), "<map><node text='this is an xml with modification to be reverted'></map>");
//fetch map history
final HttpEntity findMapEntity = new HttpEntity(requestHeaders);
final ResponseEntity<RestMindmapHistoryList> mapHistories = template.exchange(HOST_PORT + resourceUri + "/history/", HttpMethod.GET, findMapEntity, RestMindmapHistoryList.class);
//aply revert
final HttpEntity<String> cloneEntity = new HttpEntity<>(requestHeaders);
template.postForLocation(HOST_PORT + resourceUri + "/history/latest", cloneEntity);
final RestMindmap latestStoredMap = findMap(requestHeaders, template, resourceUri);
template.postForLocation(HOST_PORT + resourceUri + "/history/" + mapHistories.getBody().getChanges().get(1).getId(), cloneEntity);
final RestMindmap firstVersionMap = findMap(requestHeaders, template, resourceUri);
//verify revert
assertEquals(firstVersionMap.getXml(), "<map><node text='this is an xml to test revert changes service'></map>");
assertEquals(latestStoredMap.getXml(), "<map><node text='this is an xml with modification to be reverted'></map>");
}
@Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function")
public void addCollabWhitoutOwnerPermission(final @NotNull MediaType mediaType) {
final HttpHeaders requestHeaders = createHeaders(mediaType);
RestTemplate template = createTemplate(userEmail);
// Create a sample map ...
final URI resourceUri = addNewMap(template, "MaddCollabWhitoutOwnerPermission");
// Add a new collaboration ...
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
final RestCollaborationList collabs = new RestCollaborationList();
collabs.setMessage("Adding new permission");
final String newCollab = restAdminITCase.createNewUser(MediaType.APPLICATION_JSON);
String role = "editor";
addCollabToList(newCollab, role, collabs);
final HttpEntity<RestCollaborationList> updateEntity = new HttpEntity<>(collabs, requestHeaders);
template.put(HOST_PORT + resourceUri + "/collabs/", updateEntity);
template = createTemplate(newCollab + ":admin");
//add collab again with the new user expecting the Exception
try {
template.put(HOST_PORT + resourceUri + "/collabs/", updateEntity);
} catch (HttpClientErrorException e) {
assertEquals(e.getRawStatusCode(), 400);
assertTrue(e.getMessage().contains("User must be owner to share mindmap"));
}
}
@Test(dataProviderClass = RestHelper.class, dataProvider = "ContentType-Provider-Function")
public void addCollabWhitOwnerRole(final @NotNull MediaType mediaType) {
final HttpHeaders requestHeaders = createHeaders(mediaType);
RestTemplate template = createTemplate(userEmail);
// Create a sample map ...
final URI resourceUri = addNewMap(template, "addCollabWhitOwnerRole");
// Add a new collaboration ...
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
final RestCollaborationList collabs = new RestCollaborationList();
collabs.setMessage("Adding new permission");
final String newCollab = "new-collaborator@mail.com";
String role = "owner";
addCollabToList(newCollab, role, collabs);
final HttpEntity<RestCollaborationList> updateEntity = new HttpEntity<>(collabs, requestHeaders);
try {
template.put(HOST_PORT + resourceUri + "/collabs/", updateEntity);
} catch (HttpClientErrorException e) {
assertEquals(e.getRawStatusCode(), 400);
assertTrue(e.getMessage().contains("Collab email can not be change"));
}
}
private String changeMapTitle(final HttpHeaders requestHeaders, final MediaType mediaType, final RestTemplate template, final URI resourceUri) throws RestClientException {
// Change map title ...
requestHeaders.setContentType(MediaType.TEXT_PLAIN);
final String newTitle = "New map to change title - " + mediaType;
final HttpEntity<String> updateEntity = new HttpEntity<>(newTitle, requestHeaders);
template.put(HOST_PORT + resourceUri + "/title", updateEntity);
return newTitle;
}
private RestMindmapList fetchMaps(final HttpHeaders requestHeaders, final RestTemplate template) throws RestClientException {
final HttpEntity findMapEntity = new HttpEntity(requestHeaders);
final ResponseEntity<RestMindmapList> maps = template.exchange(BASE_REST_URL + "/maps/", HttpMethod.GET, findMapEntity, RestMindmapList.class);
return maps.getBody();
}
private RestCollaborationList fetchAndGetCollabs(final HttpHeaders requestHeaders, final RestTemplate template, final URI resourceUri) {
final ResponseEntity<RestCollaborationList> response = fetchCollabs(requestHeaders, template, resourceUri);
RestCollaborationList responseCollbs = response.getBody();
return responseCollbs;
}
private RestCollaboration addCollabToList(String newCollab, String role, RestCollaborationList collabs) {
RestCollaboration collab = new RestCollaboration();
collab.setEmail(newCollab);
collab.setRole(role);
collabs.addCollaboration(collab);
return collab;
}
private RestMindmap findMap(HttpHeaders requestHeaders, RestTemplate template, URI resourceUri) { private RestMindmap findMap(HttpHeaders requestHeaders, RestTemplate template, URI resourceUri) {
final HttpEntity findMapEntity = new HttpEntity(requestHeaders); final HttpEntity findMapEntity = new HttpEntity(requestHeaders);
final ResponseEntity<RestMindmap> response = template.exchange(HOST_PORT + resourceUri.toString(), HttpMethod.GET, findMapEntity, RestMindmap.class); final ResponseEntity<RestMindmap> response = template.exchange(HOST_PORT + resourceUri.toString(), HttpMethod.GET, findMapEntity, RestMindmap.class);