Merge branch 'develop'

main
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

@ -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 {

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 {
@ -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());
}
} }
} }
@ -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,5 +1,6 @@
<html> <html>
<body> <body>
<div>
<h3>Welcome to WiseMapping!</h3> <h3>Welcome to WiseMapping!</h3>
<p> <p>
@ -20,10 +21,9 @@
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.
<a href="http://www.wisemapping.com">WiseMapping Site</a>
</p> </p>
<div>
</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,6 +71,11 @@ 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 {
@ -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,15 +16,46 @@
<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"> <a href="${requestScope['site.homepage']}" target="new">
<div id="footerLogo"></div> <div id="footerLogo"></div>
@ -51,6 +83,7 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<script type="text/javascript"> <script type="text/javascript">
// Hook zoom events ... // Hook zoom events ...
@ -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);