diff --git a/cms-plugin/README.md b/cms-plugin/README.md
index b7847bb84b..efc3b4bcf2 100644
--- a/cms-plugin/README.md
+++ b/cms-plugin/README.md
@@ -17,19 +17,6 @@ entando-plugin-jacms
CMS is a plugin that allows to registered users to manage in the Back Office dynamic contents and digital assets.
-**Installation**
-
-In order to install the CMS plugin, you must insert the following dependency in the pom.xml file of your project:
-
-```
-
- org.entando.entando.bundles.app-view
- entando-app-view-cms-default
- ${entando.version}
- war
-
-```
-
# Developing against local versions of upstream projects (e.g. admin-console, entando-engine).
Full instructions on how to develop against local versions of upstream projects are available in the
diff --git a/engine/src/main/java/com/agiletec/aps/system/SystemConstants.java b/engine/src/main/java/com/agiletec/aps/system/SystemConstants.java
index 93676ee290..5d070030ba 100644
--- a/engine/src/main/java/com/agiletec/aps/system/SystemConstants.java
+++ b/engine/src/main/java/com/agiletec/aps/system/SystemConstants.java
@@ -296,11 +296,6 @@ private SystemConstants(){}
public static final String USER_PROFILE_ATTRIBUTE_DISABLING_CODE_ON_EDIT = "userprofile:onEdit";
- /**
- * The name of the role for attribute attribute that contains the profile picture file name
- */
- public static final String USER_PROFILE_ATTRIBUTE_ROLE_PROFILE_PICTURE = "userprofile:profilepicture";
-
public static final String ENTANDO_THREAD_NAME_PREFIX = "EntandoThread_";
public static final String API_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
diff --git a/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/IUserPreferencesDAO.java b/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/IUserPreferencesDAO.java
index ac96cca259..2fb201cefd 100644
--- a/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/IUserPreferencesDAO.java
+++ b/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/IUserPreferencesDAO.java
@@ -54,4 +54,5 @@ public interface IUserPreferencesDAO {
* @throws EntException the ent exception
*/
void deleteUserPreferences(String username) throws EntException;
+
}
diff --git a/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/IUserPreferencesManager.java b/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/IUserPreferencesManager.java
index 8ad6fd4bca..24ed516b2b 100644
--- a/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/IUserPreferencesManager.java
+++ b/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/IUserPreferencesManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-Present Entando Inc. (http://www.entando.com) All rights reserved.
+ * Copyright 2023-Present Entando Inc. (http://www.entando.com) All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
@@ -54,4 +54,18 @@ public interface IUserPreferencesManager {
* @throws EntException the ent exception
*/
void deleteUserPreferences(String username) throws EntException;
-}
\ No newline at end of file
+
+ boolean isUserGravatarEnabled(String username) throws EntException;
+
+ void updateUserGravatarPreference(String username, boolean enabled) throws EntException;
+
+ public default UserPreferences createDefaultUserPreferences(String username) {
+ UserPreferences userPreferences = new UserPreferences();
+ userPreferences.setUsername(username);
+ userPreferences.setWizard(true);
+ userPreferences.setTranslationWarning(true);
+ userPreferences.setLoadOnPageSelect(true);
+ return userPreferences;
+ }
+
+}
diff --git a/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/UserPreferences.java b/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/UserPreferences.java
index a17819e890..b4cdacbab4 100644
--- a/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/UserPreferences.java
+++ b/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/UserPreferences.java
@@ -21,7 +21,7 @@
@XmlRootElement(name = "userPreferences")
@XmlType(propOrder = {"username", "wizard", "loadOnPageSelect", "translationWarning", "defaultPageOwnerGroup",
"defaultPageJoinGroups", "defaultContentOwnerGroup", "defaultContentJoinGroups", "defaultWidgetOwnerGroup",
- "defaultWidgetJoinGroups", "disableContentMenu"})
+ "defaultWidgetJoinGroups", "disableContentMenu", "gravatar"})
public class UserPreferences implements Serializable {
private String username;
@@ -35,6 +35,7 @@ public class UserPreferences implements Serializable {
private String defaultWidgetOwnerGroup;
private String defaultWidgetJoinGroups;
private boolean disableContentMenu;
+ private boolean gravatar;
@XmlElement(name = "username", required = true)
public String getUsername() {
@@ -135,6 +136,15 @@ public void setDisableContentMenu(boolean disableContentMenu) {
this.disableContentMenu = disableContentMenu;
}
+ @XmlElement(name = "gravatar")
+ public boolean isGravatar() {
+ return gravatar;
+ }
+
+ public void setGravatar(boolean gravatar) {
+ this.gravatar = gravatar;
+ }
+
@Override
public String toString() {
return "UserPreferences{" +
@@ -148,7 +158,8 @@ public String toString() {
", defaultContentJoinGroups='" + defaultContentJoinGroups + '\'' +
", defaultWidgetOwnerGroup='" + defaultWidgetOwnerGroup + '\'' +
", defaultWidgetJoinGroups='" + defaultWidgetJoinGroups + '\'' +
- ", disableContentMenu='" + disableContentMenu +
+ ", disableContentMenu='" + disableContentMenu + '\'' +
+ ", gravatar='" + gravatar +
'}';
}
}
diff --git a/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/UserPreferencesDAO.java b/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/UserPreferencesDAO.java
index dbab2edaab..a340dc67d8 100644
--- a/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/UserPreferencesDAO.java
+++ b/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/UserPreferencesDAO.java
@@ -29,19 +29,19 @@ public class UserPreferencesDAO extends AbstractDAO implements IUserPreferencesD
private static final String LOAD_USER_PREFERENCES =
"SELECT wizard, loadonpageselect, translationwarning, defaultpageownergroup, defaultpagejoingroups, "
+ "defaultcontentownergroup, defaultcontentjoingroups, defaultwidgetownergroup, "
- + "defaultwidgetjoingroups, disableContentMenu FROM userpreferences WHERE username = ? ";
+ + "defaultwidgetjoingroups, disableContentMenu, gravatar FROM userpreferences WHERE username = ? ";
private static final String ADD_USER_PREFERENCES =
"INSERT INTO userpreferences (username, wizard, loadonpageselect, translationwarning, "
+ "defaultpageownergroup, defaultpagejoingroups, defaultcontentownergroup, "
- + "defaultcontentjoingroups, defaultwidgetownergroup, defaultwidgetjoingroups, disableContentMenu) "
- + "VALUES ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )";
+ + "defaultcontentjoingroups, defaultwidgetownergroup, defaultwidgetjoingroups, disableContentMenu, gravatar) "
+ + "VALUES ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )";
private static final String UPDATE_USER_PREFERENCES =
"UPDATE userpreferences SET wizard = ? , loadonpageselect = ? , translationwarning = ? , "
+ "defaultpageownergroup = ? , defaultpagejoingroups = ? , defaultcontentownergroup = ? , "
+ "defaultcontentjoingroups = ? , defaultwidgetownergroup = ?, defaultwidgetjoingroups = ? , "
- + "disableContentMenu = ? WHERE username = ? ";
+ + "disableContentMenu = ? , gravatar = ? WHERE username = ? ";
private static final String DELETE_USER_PREFERENCES =
"DELETE FROM userpreferences WHERE username = ? ";
@@ -70,6 +70,7 @@ public UserPreferences loadUserPreferences(String username) throws EntException
response.setDefaultWidgetOwnerGroup(res.getString(8));
response.setDefaultWidgetJoinGroups(res.getString(9));
response.setDisableContentMenu(1 == res.getInt(10));
+ response.setGravatar(1 == res.getInt(11));
}
} catch (SQLException e) {
_logger.error("Error loading user preferences for user {}", username, e);
@@ -99,6 +100,7 @@ public void addUserPreferences(UserPreferences userPreferences) throws EntExcept
stat.setString(9, userPreferences.getDefaultWidgetOwnerGroup());
stat.setString(10, userPreferences.getDefaultWidgetJoinGroups());
stat.setInt(11, userPreferences.getDisableContentMenu() ? 1 : 0);
+ stat.setInt(12, userPreferences.isGravatar() ? 1 : 0);
stat.executeUpdate();
conn.commit();
} catch (SQLException e) {
@@ -128,7 +130,8 @@ public void updateUserPreferences(UserPreferences userPreferences) throws EntExc
stat.setString(8, userPreferences.getDefaultWidgetOwnerGroup());
stat.setString(9, userPreferences.getDefaultWidgetJoinGroups());
stat.setInt(10, userPreferences.getDisableContentMenu() ? 1 : 0);
- stat.setString(11, userPreferences.getUsername());
+ stat.setInt(11, userPreferences.isGravatar() ? 1 : 0);
+ stat.setString(12, userPreferences.getUsername());
stat.executeUpdate();
conn.commit();
} catch (SQLException e) {
@@ -158,4 +161,5 @@ public void deleteUserPreferences(String username) throws EntException {
closeDaoResources(null, stat, conn);
}
}
+
}
diff --git a/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/UserPreferencesManager.java b/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/UserPreferencesManager.java
index 6a932060f6..fa439aae90 100644
--- a/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/UserPreferencesManager.java
+++ b/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/UserPreferencesManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-Present Entando Inc. (http://www.entando.com) All rights reserved.
+ * Copyright 2023-Present Entando Inc. (http://www.entando.com) All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
@@ -13,6 +13,7 @@
*/
package org.entando.entando.aps.system.services.userpreferences;
+import java.util.Optional;
import org.entando.entando.ent.exception.EntException;
public class UserPreferencesManager implements IUserPreferencesManager {
@@ -34,6 +35,24 @@ public void updateUserPreferences(UserPreferences userPreferences) throws EntExc
userPreferencesDAO.updateUserPreferences(userPreferences);
}
+ @Override
+ public boolean isUserGravatarEnabled(String username) throws EntException {
+ return Optional.ofNullable(this.getUserPreferences(username)).map(p -> p.isGravatar()).orElse(Boolean.FALSE);
+ }
+
+ @Override
+ public void updateUserGravatarPreference(String username, boolean enabled) throws EntException {
+ UserPreferences userPreferences = this.getUserPreferences(username);
+ if (null != userPreferences) {
+ userPreferences.setGravatar(enabled);
+ this.updateUserPreferences(userPreferences);
+ } else {
+ userPreferences = this.createDefaultUserPreferences(username);
+ userPreferences.setGravatar(enabled);
+ this.addUserPreferences(userPreferences);
+ }
+ }
+
@Override
public void deleteUserPreferences(String username) throws EntException {
userPreferencesDAO.deleteUserPreferences(username);
@@ -42,4 +61,5 @@ public void deleteUserPreferences(String username) throws EntException {
public void setUserPreferencesDAO(UserPreferencesDAO userPreferencesDAO) {
this.userPreferencesDAO = userPreferencesDAO;
}
+
}
diff --git a/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/UserPreferencesService.java b/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/UserPreferencesService.java
index d43ca0ea65..0d407fa1ee 100644
--- a/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/UserPreferencesService.java
+++ b/engine/src/main/java/org/entando/entando/aps/system/services/userpreferences/UserPreferencesService.java
@@ -95,6 +95,9 @@ public UserPreferencesDto updateUserPreferences(String username, UserPreferences
if (request.getDisableContentMenu() != null) {
userPreferences.setDisableContentMenu(request.getDisableContentMenu());
}
+ if (request.getGravatar() != null) {
+ userPreferences.setGravatar(request.getGravatar());
+ }
userPreferencesManager.updateUserPreferences(userPreferences);
return new UserPreferencesDto(userPreferencesManager.getUserPreferences(username));
} else {
@@ -109,12 +112,8 @@ public UserPreferencesDto updateUserPreferences(String username, UserPreferences
private void createNewDefaultUserPreferences(String username) {
try {
- UserPreferences userPreferences = new UserPreferences();
- userPreferences.setUsername(username);
- userPreferences.setWizard(true);
- userPreferences.setTranslationWarning(true);
- userPreferences.setLoadOnPageSelect(true);
- userPreferencesManager.addUserPreferences(userPreferences);
+ UserPreferences userPreferences = this.userPreferencesManager.createDefaultUserPreferences(username);
+ this.userPreferencesManager.addUserPreferences(userPreferences);
} catch (EntException e) {
logger.error("Error in creating new default userPreferences for {}", username, e);
throw new RestServerError("Error creating new default userPreferences", e);
diff --git a/engine/src/main/java/org/entando/entando/aps/system/services/userprofile/AvatarService.java b/engine/src/main/java/org/entando/entando/aps/system/services/userprofile/AvatarService.java
index bda1d64c13..5b601dfbab 100644
--- a/engine/src/main/java/org/entando/entando/aps/system/services/userprofile/AvatarService.java
+++ b/engine/src/main/java/org/entando/entando/aps/system/services/userprofile/AvatarService.java
@@ -1,13 +1,22 @@
+/*
+ * Copyright 2023-Present Entando Inc. (http://www.entando.com) All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ */
package org.entando.entando.aps.system.services.userprofile;
-import com.agiletec.aps.system.SystemConstants;
-import com.agiletec.aps.system.common.entity.model.attribute.AttributeInterface;
-import com.agiletec.aps.system.common.entity.model.attribute.MonoTextAttribute;
import com.agiletec.aps.system.services.user.UserDetails;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;
-import java.util.function.Consumer;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
@@ -16,10 +25,9 @@
import org.entando.entando.aps.system.exception.RestServerError;
import org.entando.entando.aps.system.services.storage.IFileBrowserService;
import org.entando.entando.aps.system.services.storage.model.BasicFileAttributeViewDto;
+import org.entando.entando.aps.system.services.userpreferences.IUserPreferencesManager;
import org.entando.entando.aps.system.services.userprofile.model.AvatarDto;
-import org.entando.entando.aps.system.services.userprofile.model.IUserProfile;
import org.entando.entando.ent.exception.EntException;
-import org.entando.entando.ent.exception.EntRuntimeException;
import org.entando.entando.web.entity.validator.EntityValidator;
import org.entando.entando.web.filebrowser.model.FileBrowserFileRequest;
import org.entando.entando.web.userprofile.model.ProfileAvatarRequest;
@@ -28,10 +36,9 @@
@Slf4j
@RequiredArgsConstructor
public class AvatarService implements IAvatarService {
-
- // Services
+
private final IFileBrowserService fileBrowserService;
- private final IUserProfileManager userProfileManager;
+ private final IUserPreferencesManager userPreferencesManager;
// CONSTANTS
private static final String DEFAULT_AVATAR_PATH = "static/profile";
@@ -39,7 +46,11 @@ public class AvatarService implements IAvatarService {
@Override
public AvatarDto getAvatarData(UserDetails userDetails) {
try {
- String fileName = this.getAvatarFilename(userDetails);
+ boolean isGravatarEnabled = this.userPreferencesManager.isUserGravatarEnabled(userDetails.getUsername());
+ if (isGravatarEnabled) {
+ return AvatarDto.builder().gravatar(true).build();
+ }
+ String fileName = this.getAvatarFilenameByUsername(userDetails.getUsername());
if (StringUtils.isEmpty(fileName)) {
throw new ResourceNotFoundException(EntityValidator.ERRCODE_ENTITY_DOES_NOT_EXIST, "image",
userDetails.getUsername());
@@ -68,16 +79,13 @@ public AvatarDto getAvatarData(UserDetails userDetails) {
public String updateAvatar(ProfileAvatarRequest request, UserDetails userDetails, BindingResult bindingResult) {
try {
String username = userDetails.getUsername();
- IUserProfile userProfile = userProfileManager.getProfile(username);
// remove previous image if present
- deletePrevUserAvatarFromFileSystemIfPresent(username, userProfile);
- // add profile picture file
- FileBrowserFileRequest fileBrowserFileRequest = addProfileImageToFileSystem(request, userDetails, bindingResult);
- // update profile picture attribute or add a new one if no image was already set by user
- if (getProfilePictureAttribute(userProfile).isPresent()) {
- this.setProfilePictureAttribute(userProfile, fileBrowserFileRequest.getFilename());
- userProfileManager.updateProfile(userProfile.getId(), userProfile);
+ deletePrevUserAvatarFromFileSystemIfPresent(username);
+ this.userPreferencesManager.updateUserGravatarPreference(userDetails.getUsername(), request.isUseGravatar());
+ if (request.isUseGravatar()) {
+ return null;
}
+ FileBrowserFileRequest fileBrowserFileRequest = addProfileImageToFileSystem(request, userDetails, bindingResult);
return fileBrowserFileRequest.getFilename();
} catch (Exception e) {
log.error("Error updating avatar", e);
@@ -89,15 +97,8 @@ public String updateAvatar(ProfileAvatarRequest request, UserDetails userDetails
public void deleteAvatar(UserDetails userDetails, BindingResult bindingResult) {
try {
String username = userDetails.getUsername();
- IUserProfile userProfile = userProfileManager.getProfile(username);
- // remove previous image if present
- this.deletePrevUserAvatarFromFileSystemIfPresent(username, userProfile);
- // update profile picture attribute (if present) with an empty value
- if (getProfilePictureAttribute(userProfile).isPresent()) {
- this.setProfilePictureAttribute(userProfile, null);
- // update user profile with the fresh data related to profile picture
- userProfileManager.updateProfile(userProfile.getId(), userProfile);
- }
+ this.deletePrevUserAvatarFromFileSystemIfPresent(username);
+ this.userPreferencesManager.updateUserGravatarPreference(userDetails.getUsername(), false);
} catch (Exception e) {
log.error("Error deleting avatar", e);
throw new RestServerError("Error deleting avatar", e);
@@ -115,31 +116,19 @@ private FileBrowserFileRequest addProfileImageToFileSystem(
return fileBrowserFileRequest;
}
- private void deletePrevUserAvatarFromFileSystemIfPresent(String username, IUserProfile userProfile) {
- Consumer deleteFile = filename -> {
- if (null == filename) {
- return;
- }
- String profilePicturePath = Paths.get(DEFAULT_AVATAR_PATH, filename).toString();
- this.removePictureFromFilesystem(profilePicturePath);
- };
- this.getProfilePictureAttribute(userProfile)
- .ifPresentOrElse(attribute -> deleteFile.accept((String) attribute.getValue()),
- () -> deleteFile.accept(this.getAvatarFilenameByUsername(username)));
- }
-
- private String getAvatarFilename(UserDetails userDetails) {
- try {
- IUserProfile userProfile = userProfileManager.getProfile(userDetails.getUsername());
- return getProfilePictureAttribute(userProfile).map(pr -> (String)pr.getValue()).orElseGet(() ->
- this.getAvatarFilenameByUsername(userDetails.getUsername())
- );
- } catch (Exception e) {
- throw new EntRuntimeException("Error extracting avatar " + userDetails.getUsername(), e);
+ private void deletePrevUserAvatarFromFileSystemIfPresent(String username) throws EntException {
+ String filename = this.getAvatarFilenameByUsername(username);
+ if (null == filename) {
+ return;
}
+ String profilePicturePath = Paths.get(DEFAULT_AVATAR_PATH, filename).toString();
+ fileBrowserService.deleteFile(profilePicturePath, false);
}
- private String getAvatarFilenameByUsername(String username) {
+ private String getAvatarFilenameByUsername(String username) throws EntException {
+ if (!fileBrowserService.exists(DEFAULT_AVATAR_PATH)) {
+ return null;
+ }
List fileAttributes = fileBrowserService.browseFolder(DEFAULT_AVATAR_PATH, Boolean.FALSE);
Optional fileAvatar = fileAttributes.stream().filter(bfa -> !bfa.getDirectory())
.filter(bfa -> {
@@ -150,16 +139,6 @@ private String getAvatarFilenameByUsername(String username) {
return fileAvatar.orElse(null);
}
- private void removePictureFromFilesystem(String profilePicturePath) throws EntRuntimeException {
- try {
- if (fileBrowserService.exists(profilePicturePath)) {
- fileBrowserService.deleteFile(profilePicturePath, false);
- }
- } catch (EntException e) {
- throw new EntRuntimeException("Error in checking file existence on the filesystem", e);
- }
- }
-
private static FileBrowserFileRequest convertToFileBrowserFileRequest(ProfileAvatarRequest request,
UserDetails userDetails) {
FileBrowserFileRequest fileBrowserFileRequest = new FileBrowserFileRequest();
@@ -172,15 +151,4 @@ private static FileBrowserFileRequest convertToFileBrowserFileRequest(ProfileAva
return fileBrowserFileRequest;
}
- private Optional getProfilePictureAttribute(IUserProfile userProfile) {
- return Optional.ofNullable(userProfile).map(up -> up.getAttributeByRole(SystemConstants.USER_PROFILE_ATTRIBUTE_ROLE_PROFILE_PICTURE));
- }
-
- private void setProfilePictureAttribute(IUserProfile userProfile, String value) {
- getProfilePictureAttribute(userProfile).ifPresent(attribute -> {
- MonoTextAttribute textAtt = (MonoTextAttribute) attribute;
- textAtt.setText(value);
- });
- }
-
}
diff --git a/engine/src/main/java/org/entando/entando/aps/system/services/userprofile/attributeRoles.xml b/engine/src/main/java/org/entando/entando/aps/system/services/userprofile/attributeRoles.xml
index b0e9ee6029..a91d81b10a 100644
--- a/engine/src/main/java/org/entando/entando/aps/system/services/userprofile/attributeRoles.xml
+++ b/engine/src/main/java/org/entando/entando/aps/system/services/userprofile/attributeRoles.xml
@@ -23,10 +23,4 @@
Monotext,Email
TEXT
-
- userprofile:profilepicture
- The Attribute containing the profile picture file name
- Monotext
- TEXT
-
diff --git a/engine/src/main/java/org/entando/entando/aps/system/services/userprofile/model/AvatarDto.java b/engine/src/main/java/org/entando/entando/aps/system/services/userprofile/model/AvatarDto.java
index 312359ed21..148c60e131 100644
--- a/engine/src/main/java/org/entando/entando/aps/system/services/userprofile/model/AvatarDto.java
+++ b/engine/src/main/java/org/entando/entando/aps/system/services/userprofile/model/AvatarDto.java
@@ -1,3 +1,16 @@
+/*
+ * Copyright 2023-Present Entando Inc. (http://www.entando.com) All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ */
package org.entando.entando.aps.system.services.userprofile.model;
import lombok.AllArgsConstructor;
@@ -16,5 +29,6 @@ public class AvatarDto {
String filename;
byte[] base64;
String prevPath;
+ boolean gravatar;
}
diff --git a/engine/src/main/java/org/entando/entando/web/userpreferences/model/UserPreferencesDto.java b/engine/src/main/java/org/entando/entando/web/userpreferences/model/UserPreferencesDto.java
index 62108c488a..566af5833b 100644
--- a/engine/src/main/java/org/entando/entando/web/userpreferences/model/UserPreferencesDto.java
+++ b/engine/src/main/java/org/entando/entando/web/userpreferences/model/UserPreferencesDto.java
@@ -35,6 +35,7 @@ public class UserPreferencesDto {
private String defaultWidgetOwnerGroup;
private List defaultWidgetJoinGroups;
private Boolean disableContentMenu;
+ private Boolean gravatar;
public UserPreferencesDto(UserPreferences userPreferences) {
wizard = userPreferences.isWizard();
diff --git a/engine/src/main/java/org/entando/entando/web/userpreferences/model/UserPreferencesRequest.java b/engine/src/main/java/org/entando/entando/web/userpreferences/model/UserPreferencesRequest.java
index dfb80d5be8..8e07bc59ba 100644
--- a/engine/src/main/java/org/entando/entando/web/userpreferences/model/UserPreferencesRequest.java
+++ b/engine/src/main/java/org/entando/entando/web/userpreferences/model/UserPreferencesRequest.java
@@ -30,6 +30,7 @@ public class UserPreferencesRequest {
private String defaultWidgetOwnerGroup;
private List defaultWidgetJoinGroups;
private Boolean disableContentMenu;
+ private Boolean gravatar;
@Override
public String toString() {
@@ -42,8 +43,9 @@ public String toString() {
", defaultContentOwnerGroup='" + defaultContentOwnerGroup + '\'' +
", defaultContentJoinGroups=" + defaultContentJoinGroups +
", defaultWidgetOwnerGroup='" + defaultWidgetOwnerGroup + '\'' +
- ", defaultWidgetJoinGroups=" + defaultWidgetJoinGroups +
- ", disableContentMenu=" + disableContentMenu +
+ ", defaultWidgetJoinGroups=" + defaultWidgetJoinGroups + '\'' +
+ ", disableContentMenu=" + disableContentMenu + '\'' +
+ ", gravatar=" + gravatar +
'}';
}
}
diff --git a/engine/src/main/java/org/entando/entando/web/userprofile/ProfileController.java b/engine/src/main/java/org/entando/entando/web/userprofile/ProfileController.java
index c201cdad57..f44aaedf05 100644
--- a/engine/src/main/java/org/entando/entando/web/userprofile/ProfileController.java
+++ b/engine/src/main/java/org/entando/entando/web/userprofile/ProfileController.java
@@ -37,7 +37,6 @@
import org.entando.entando.web.common.model.SimpleRestResponse;
import org.entando.entando.web.entity.validator.EntityValidator;
import org.entando.entando.web.userprofile.model.ProfileAvatarRequest;
-import org.entando.entando.web.userprofile.model.ProfileAvatarResponse;
import org.entando.entando.web.userprofile.validator.ProfileAvatarValidator;
import org.entando.entando.web.userprofile.validator.ProfileValidator;
import org.springframework.http.HttpStatus;
@@ -45,6 +44,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.MapBindingResult;
+import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -79,7 +79,6 @@ public class ProfileController {
public static final String PROTECTED_FOLDER = "protectedFolder";
-
public static final String PREV_PATH = "prevPath";
@RestAccessControl(permission = {Permission.MANAGE_USER_PROFILES, Permission.MANAGE_USERS})
@@ -173,7 +172,7 @@ public ResponseEntity> updateUserProfile(@PathVari
@PutMapping(value = "/myUserProfile", produces = MediaType.APPLICATION_JSON_VALUE)
@RestAccessControl(permission = Permission.ENTER_BACKEND)
public ResponseEntity> updateMyUserProfile(@RequestAttribute("user") UserDetails user,
- @Valid @RequestBody EntityDto bodyRequest, BindingResult bindingResult) {
+ /*@Valid*/ @RequestBody EntityDto bodyRequest, BindingResult bindingResult) {
logger.debug("Update profile for the logged user {} -> {}", user.getUsername(), bodyRequest);
profileValidator.validateBodyName(user.getUsername(), bodyRequest, bindingResult);
if (bindingResult.hasErrors()) {
@@ -185,7 +184,7 @@ public ResponseEntity> updateMyUserProfile(@Reques
}
return new ResponseEntity<>(new SimpleRestResponse<>(response), HttpStatus.OK);
}
-
+
@GetMapping(path = "/userProfiles/avatar", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity, Map>> getAvatar(
@RequestAttribute("user") UserDetails userDetails) {
@@ -197,37 +196,33 @@ public ResponseEntity, Map>> ge
result.put("isDirectory", false);
result.put("path", avatarData.getCurrentPath());
result.put("filename", avatarData.getFilename());
+ result.put("useGravatar", avatarData.isGravatar());
result.put("base64", avatarData.getBase64());
Map metadata = new HashMap<>();
metadata.put(PREV_PATH, avatarData.getPrevPath());
return new ResponseEntity<>(new RestResponse<>(result, metadata), HttpStatus.OK);
}
-
@PostMapping(path = "/userProfiles/avatar", produces = MediaType.APPLICATION_JSON_VALUE)
- public ResponseEntity> addAvatar(
- @Valid @RequestBody ProfileAvatarRequest request,
+ public ResponseEntity>> addAvatar(
+ @Validated @RequestBody ProfileAvatarRequest request,
@RequestAttribute("user") UserDetails user,
BindingResult bindingResult) {
-
// validate input dto to check for consistency of input
- profileAvatarValidator.validate(request, bindingResult);
+ profileAvatarValidator.validate(request, user, bindingResult);
if (bindingResult.hasErrors()) {
throw new ValidationGenericException(bindingResult);
}
- // update the profile picture saving the received image in the file system, eventually deleting the previous
- // existing image
String pictureFileName = avatarService.updateAvatar(request, user, bindingResult);
-
if (bindingResult.hasErrors()) {
throw new ValidationGenericException(bindingResult);
}
- return new ResponseEntity<>(new SimpleRestResponse<>(new ProfileAvatarResponse(pictureFileName)),
- HttpStatus.OK);
+ Map response = null != pictureFileName ? Map.of("filename", pictureFileName) : Map.of("useGravatar", true);
+ return new ResponseEntity<>(new SimpleRestResponse<>(response), HttpStatus.OK);
}
@DeleteMapping(path = "/userProfiles/avatar")
- public ResponseEntity deleteAvatar(@RequestAttribute("user") UserDetails user) {
+ public ResponseEntity>> deleteAvatar(@RequestAttribute("user") UserDetails user) {
avatarService.deleteAvatar(user, new MapBindingResult(new HashMap<>(), "user"));
Map payload = new HashMap<>();
payload.put("username", user.getUsername());
diff --git a/engine/src/main/java/org/entando/entando/web/userprofile/model/ProfileAvatarRequest.java b/engine/src/main/java/org/entando/entando/web/userprofile/model/ProfileAvatarRequest.java
index c13caabfd9..6487c041ea 100644
--- a/engine/src/main/java/org/entando/entando/web/userprofile/model/ProfileAvatarRequest.java
+++ b/engine/src/main/java/org/entando/entando/web/userprofile/model/ProfileAvatarRequest.java
@@ -13,24 +13,19 @@
*/
package org.entando.entando.web.userprofile.model;
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotEmpty;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
-/**
- * @author E.Santoboni
- */
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class ProfileAvatarRequest {
-
- @NotBlank(message = "avatar.filename.notBlank")
+
private String filename;
- @NotEmpty(message = "fileBrowser.base64.notBlank")
private byte[] base64;
+ private boolean useGravatar;
+
}
diff --git a/engine/src/main/java/org/entando/entando/web/userprofile/model/ProfileAvatarResponse.java b/engine/src/main/java/org/entando/entando/web/userprofile/model/ProfileAvatarResponse.java
deleted file mode 100644
index 06b7368a48..0000000000
--- a/engine/src/main/java/org/entando/entando/web/userprofile/model/ProfileAvatarResponse.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2018-Present Entando Inc. (http://www.entando.com) All rights reserved.
- *
- * This library is free software; you can redistribute it and/or modify it under
- * the terms of the GNU Lesser General Public License as published by the Free
- * Software Foundation; either version 2.1 of the License, or (at your option)
- * any later version.
- *
- * This library is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
- * details.
- */
-package org.entando.entando.web.userprofile.model;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-
-/**
- * @author E.Santoboni
- */
-@NoArgsConstructor
-@AllArgsConstructor
-@Getter
-@Setter
-public class ProfileAvatarResponse {
-
- private String filename;
-
-}
diff --git a/engine/src/main/java/org/entando/entando/web/userprofile/validator/ProfileAvatarValidator.java b/engine/src/main/java/org/entando/entando/web/userprofile/validator/ProfileAvatarValidator.java
index f3a6fa9f1f..4164ca1f37 100644
--- a/engine/src/main/java/org/entando/entando/web/userprofile/validator/ProfileAvatarValidator.java
+++ b/engine/src/main/java/org/entando/entando/web/userprofile/validator/ProfileAvatarValidator.java
@@ -13,12 +13,20 @@
*/
package org.entando.entando.web.userprofile.validator;
+import com.agiletec.aps.system.SystemConstants;
+import com.agiletec.aps.system.services.user.UserDetails;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
+import java.util.Optional;
import javax.imageio.ImageIO;
+import lombok.AllArgsConstructor;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
+import org.entando.entando.aps.system.services.userprofile.IUserProfileManager;
+import org.entando.entando.ent.exception.EntException;
+import org.entando.entando.ent.exception.EntRuntimeException;
+import org.entando.entando.web.common.RestErrorCodes;
import org.entando.entando.web.userprofile.model.ProfileAvatarRequest;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
@@ -26,33 +34,60 @@
import org.springframework.validation.Validator;
@Component
+@AllArgsConstructor
public class ProfileAvatarValidator implements Validator {
public static final String ERRCODE_INVALID_FILE_NAME = "1";
public static final String ERRCODE_INVALID_FILE_TYPE = "2";
+ public static final String ERRCODE_MISSING_EMAIL_ATTRIBUTE = "3";
+
+ private IUserProfileManager userProfileManager;
@Override
public boolean supports(@NonNull Class> paramClass) {
return (ProfileAvatarRequest.class.equals(paramClass));
}
-
+
@Override
public void validate(@NonNull Object target, @NonNull Errors errors) {
ProfileAvatarRequest request = (ProfileAvatarRequest) target;
-
String filename = request.getFilename();
- if (StringUtils.isEmpty(FilenameUtils.getExtension(filename))) {
+ if (StringUtils.isBlank(filename)) {
+ errors.rejectValue("filename", RestErrorCodes.NOT_BLANK, new String[]{},
+ "avatar.filename.notBlank");
+ } else if (StringUtils.isEmpty(FilenameUtils.getExtension(filename))) {
errors.rejectValue("filename", ERRCODE_INVALID_FILE_NAME, new String[]{filename},
"fileBrowser.filename.invalidFilename");
return;
}
-
- try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(request.getBase64())) {
- if (ImageIO.read(byteArrayInputStream) == null) {
- errors.rejectValue("base64", ERRCODE_INVALID_FILE_TYPE, "fileBrowser.file.invalidType");
+ byte[] base64 = request.getBase64();
+ if (null == base64) {
+ errors.rejectValue("base64", RestErrorCodes.NOT_EMPTY, new String[]{},
+ "fileBrowser.base64.notBlank");
+ } else {
+ try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(request.getBase64())) {
+ if (ImageIO.read(byteArrayInputStream) == null) {
+ errors.rejectValue("base64", ERRCODE_INVALID_FILE_TYPE, "fileBrowser.file.invalidType");
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
}
- } catch (IOException e) {
- throw new UncheckedIOException(e);
+ }
+ }
+
+ public void validate(@NonNull Object target, UserDetails user, @NonNull Errors errors) {
+ ProfileAvatarRequest request = (ProfileAvatarRequest) target;
+ if (!request.isUseGravatar()) {
+ this.validate(target, errors);
+ return;
+ }
+ try {
+ Optional.ofNullable(this.userProfileManager.getProfile(user.getUsername()))
+ .map(up -> up.getAttributeByRole(SystemConstants.USER_PROFILE_ATTRIBUTE_ROLE_MAIL)).ifPresentOrElse(up -> {
+ }, () -> errors.rejectValue("useGravatar", ERRCODE_MISSING_EMAIL_ATTRIBUTE, new String[]{},
+ "avatar.emailAttribute.missing"));
+ } catch (EntException e) {
+ throw new EntRuntimeException("Error validating user avatar", e);
}
}
diff --git a/engine/src/main/resources/liquibase/changeSetPort.xml b/engine/src/main/resources/liquibase/changeSetPort.xml
index 95140223cb..88cf7b8418 100644
--- a/engine/src/main/resources/liquibase/changeSetPort.xml
+++ b/engine/src/main/resources/liquibase/changeSetPort.xml
@@ -26,6 +26,7 @@
-
-
-
\ No newline at end of file
+
+
+
+
diff --git a/engine/src/main/resources/liquibase/port/20240112000000_user_preference_avatar.xml b/engine/src/main/resources/liquibase/port/20240112000000_user_preference_avatar.xml
new file mode 100644
index 0000000000..58d41a0804
--- /dev/null
+++ b/engine/src/main/resources/liquibase/port/20240112000000_user_preference_avatar.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engine/src/main/resources/liquibase/port/clob/production/sysconfig_3.xml b/engine/src/main/resources/liquibase/port/clob/production/sysconfig_3.xml
index 8786c502a8..9e1d5a2425 100644
--- a/engine/src/main/resources/liquibase/port/clob/production/sysconfig_3.xml
+++ b/engine/src/main/resources/liquibase/port/clob/production/sysconfig_3.xml
@@ -1,28 +1,23 @@
-
-
-
-
- true
-
-
- userprofile:fullname
-
-
-
-
- true
-
-
- userprofile:email
-
-
-
+
+
+
+
+ true
+
- userprofile:profilepicture
+ userprofile:fullname
-
-
+
+
+ true
+
+
+ userprofile:email
+
+
+
+
\ No newline at end of file
diff --git a/engine/src/main/resources/liquibase/port/clob/test/sysconfig_3.xml b/engine/src/main/resources/liquibase/port/clob/test/sysconfig_3.xml
index a0c63d7a78..574c0f381a 100644
--- a/engine/src/main/resources/liquibase/port/clob/test/sysconfig_3.xml
+++ b/engine/src/main/resources/liquibase/port/clob/test/sysconfig_3.xml
@@ -1,171 +1,194 @@
-
-
-
-
- true
-
-
- userprofile:fullname
-
-
-
-
- true
-
-
-
- userprofile:email
-
-
-
-
-
-
-
-
-
-
-
-
- true
-
-
- userprofile:firstname
-
-
-
-
- true
-
-
- userprofile:surname
-
-
-
-
- true
-
-
- userprofile:email
-
-
-
+
+
+
+
+ true
+
- userprofile:profilepicture
+ userprofile:fullname
-
-
-
-
-
- true
-
-
- true
-
-
- true
-
-
-
-
- 25/11/2026
-
-
-
- true
+
+
+ true
+
+
+
+ userprofile:email
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ userprofile:firstname
+
+
+
+
+ true
+
+
+ userprofile:surname
+
+
+
+
+ true
+
+
+ userprofile:email
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+ true
+
+
+
+
+ true
+
+
+
+
+
+ 25/11/2026
+
+
+
+
+ true
+
-
-
- true
+
+
+
+ true
+
-
-
- true
-
-
- true
-
-
- true
-
-
- true
-
-
-
- true
- 15
- 30
-
-
-
-
- true
-
-
-
- true
- 50
- 300
-
-
-
- true
-
- jacms:title
-
-
-
-
- true
- 15
- 30
-
-
-
-
- true
-
-
- true
-
-
- true
-
-
-
-
-
-
- 10/10/2030
-
-
-
-
-
-
-
-
-
- #entity.getAttribute('Number').value)]]>
-
-
-
-
-
-
-
-
-
-
-
-
- true
-
-
-
-
+
+
+
+ true
+
+
+
+
+ true
+
+
+
+
+ true
+
+
+
+
+ true
+
+
+
+
+ true
+ 15
+ 30
+
+
+
+
+
+ true
+
+
+
+
+ true
+ 50
+ 300
+
+
+
+
+ true
+
+
+ jacms:title
+
+
+
+
+ true
+ 15
+ 30
+
+
+
+
+
+ true
+
+
+
+
+ true
+
+
+
+
+ true
+
+
+
+
+
+
+
+ 10/10/2030
+
+
+
+
+
+
+
+
+
+ #entity.getAttribute('Number').value)]]>
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
\ No newline at end of file
diff --git a/engine/src/main/resources/rest/messages.properties b/engine/src/main/resources/rest/messages.properties
index 6c2f39a945..9db69e923f 100644
--- a/engine/src/main/resources/rest/messages.properties
+++ b/engine/src/main/resources/rest/messages.properties
@@ -397,3 +397,4 @@ components.usage.type.invalid=Requested occurrence for position ''{0}'' - Invali
# Avatar
avatar.filename.notBlank=''fileName'' is required
+avatar.emailAttribute.missing=Missing email attribute in current user
diff --git a/engine/src/test/java/org/entando/entando/aps/system/services/userprofile/AvatarServiceTest.java b/engine/src/test/java/org/entando/entando/aps/system/services/userprofile/AvatarServiceTest.java
index 281a97cc47..7fff096693 100644
--- a/engine/src/test/java/org/entando/entando/aps/system/services/userprofile/AvatarServiceTest.java
+++ b/engine/src/test/java/org/entando/entando/aps/system/services/userprofile/AvatarServiceTest.java
@@ -1,3 +1,16 @@
+/*
+ * Copyright 2024-Present Entando Inc. (http://www.entando.com) All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ */
package org.entando.entando.aps.system.services.userprofile;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -7,23 +20,20 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import com.agiletec.aps.system.SystemConstants;
-import com.agiletec.aps.system.common.entity.model.attribute.MonoTextAttribute;
import com.agiletec.aps.system.services.user.UserDetails;
import java.util.List;
import org.entando.entando.aps.system.exception.ResourceNotFoundException;
import org.entando.entando.aps.system.exception.RestServerError;
import org.entando.entando.aps.system.services.storage.IFileBrowserService;
import org.entando.entando.aps.system.services.storage.model.BasicFileAttributeViewDto;
+import org.entando.entando.aps.system.services.userpreferences.IUserPreferencesManager;
import org.entando.entando.aps.system.services.userprofile.model.AvatarDto;
-import org.entando.entando.aps.system.services.userprofile.model.IUserProfile;
-import org.entando.entando.aps.system.services.userprofile.model.UserProfile;
import org.entando.entando.ent.exception.EntException;
import org.entando.entando.web.userprofile.model.ProfileAvatarRequest;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@@ -31,222 +41,110 @@
@ExtendWith(MockitoExtension.class)
class AvatarServiceTest {
-
- @Mock
- private IUserProfileManager userProfileManager;
+
@Mock
private IFileBrowserService fileBrowserService;
+ @Mock
+ private IUserPreferencesManager userPreferencesManager;
IAvatarService avatarService;
@BeforeEach
void init() {
- avatarService = new AvatarService(fileBrowserService, userProfileManager);
+ avatarService = new AvatarService(fileBrowserService, userPreferencesManager);
}
@Test
void shouldGetAvatarDataReturnAvatarInfo() throws EntException {
- IUserProfile profile = this.buildValidUserProfile("username", "image.png");
- when(userProfileManager.getProfile(any())).thenReturn(profile);
+ UserDetails user = Mockito.mock(UserDetails.class);
+ when(user.getUsername()).thenReturn("username_test");
+ when(userPreferencesManager.isUserGravatarEnabled(user.getUsername())).thenReturn(false);
+ BasicFileAttributeViewDto dto = this.createMockFileAttributeDto("username_test", "png");
+ when(fileBrowserService.exists("static/profile")).thenReturn(true);
+ when(fileBrowserService.browseFolder("static/profile", false)).thenReturn(List.of(dto));
when(fileBrowserService.getFileStream(any(), any())).thenReturn(new byte[0]);
-
- AvatarDto avatarData = avatarService.getAvatarData(mock(UserDetails.class));
-
- assertEquals("image.png", avatarData.getFilename());
- assertEquals("static/profile/image.png", avatarData.getCurrentPath());
- }
-
- @Test
- void shouldGetAvatarDataThrowResourceServerError() throws EntException {
- when(userProfileManager.getProfile(any())).thenThrow(EntException.class);
- assertThrows(RestServerError.class, () -> avatarService.getAvatarData(mock(UserDetails.class)));
+ AvatarDto avatarData = avatarService.getAvatarData(user);
+ assertEquals("username_test.png", avatarData.getFilename());
+ assertEquals("static/profile/username_test.png", avatarData.getCurrentPath());
+ Assertions.assertFalse(avatarData.isGravatar());
}
@Test
- void shouldGetAvatarDataThrowResourceNotFoundExceptionIfNoImageIsPresent() throws EntException {
- when(userProfileManager.getProfile(any())).thenReturn(new UserProfile());
- assertThrows(ResourceNotFoundException.class, () -> avatarService.getAvatarData(mock(UserDetails.class)));
+ void shouldGetAvatarDataReturnAvatarInfoWithGravatarEnabled() throws EntException {
+ UserDetails user = Mockito.mock(UserDetails.class);
+ when(user.getUsername()).thenReturn("username_test");
+ when(userPreferencesManager.isUserGravatarEnabled(user.getUsername())).thenReturn(true);
+ AvatarDto avatarData = avatarService.getAvatarData(user);
+ verify(fileBrowserService, Mockito.times(0)).browseFolder(any(), any());
+ verify(fileBrowserService, Mockito.times(0)).getFileStream(any(), any());
+ Assertions.assertNull(avatarData.getFilename());
+ Assertions.assertNull(avatarData.getCurrentPath());
+ Assertions.assertTrue(avatarData.isGravatar());
}
@Test
- void shouldGetAvatarDataThrowResourceNotFoundExceptionIfImageInProfileAttributeIsEmpty() throws EntException {
- IUserProfile profile = this.buildValidUserProfile("username", "");
- when(userProfileManager.getProfile(any())).thenReturn(profile);
+ void shouldGetAvatarDataThrowResourceNotFoundExceptionIfNoImageIsPresent() throws EntException {
assertThrows(ResourceNotFoundException.class, () -> avatarService.getAvatarData(mock(UserDetails.class)));
}
-
- @Test
- void shouldUpdateAvatarWithNullProfile() throws EntException {
- when(userProfileManager.getProfile("username")).thenReturn(null);
- UserDetails userDetails = mock(UserDetails.class);
- when(userDetails.getUsername()).thenReturn("username");
- when(fileBrowserService.browseFolder(Mockito.anyString(), Mockito.eq(false))).thenReturn(List.of());
- avatarService.updateAvatar(mock(ProfileAvatarRequest.class), userDetails,
- mock(BindingResult.class));
- verify(fileBrowserService, Mockito.times(0)).deleteFile(any(), any());
- verify(userProfileManager, Mockito.times(0)).addProfile(Mockito.eq("username"), Mockito.any(IUserProfile.class));
- }
-
- @Test
- void shouldUpdateAvatarDeletePreviousProfilePictureIfPresent() throws EntException {
- IUserProfile profile = this.buildValidUserProfile("username", "prevImage.png");
- when(userProfileManager.getProfile(any())).thenReturn(profile);
- //pretend image exists on filesystem
- when(fileBrowserService.exists(any())).thenReturn(true);
- avatarService.updateAvatar(mock(ProfileAvatarRequest.class), mock(UserDetails.class),
- mock(BindingResult.class));
- verify(fileBrowserService, Mockito.times(1)).deleteFile(any(), any());
- }
-
+
@Test
void shouldUpdateAvatarAddProfilePictureFromTheRequest() throws EntException {
- when(userProfileManager.getProfile(any())).thenReturn(new UserProfile());
-
BasicFileAttributeViewDto dtoDirectory = new BasicFileAttributeViewDto();
dtoDirectory.setDirectory(true);
dtoDirectory.setName("folder");
- BasicFileAttributeViewDto dto = new BasicFileAttributeViewDto();
- dto.setDirectory(false);
- dto.setName("test_username.jpg");
+ BasicFileAttributeViewDto dto = this.createMockFileAttributeDto("test_username", "jpg");
+ when(fileBrowserService.exists("static/profile")).thenReturn(true);
when(fileBrowserService.browseFolder(Mockito.anyString(), Mockito.eq(false))).thenReturn(List.of(dtoDirectory, dto));
UserDetails userDetails = mock(UserDetails.class);
when(userDetails.getUsername()).thenReturn("test_username");
-
- avatarService.updateAvatar(mock(ProfileAvatarRequest.class),userDetails, mock(BindingResult.class));
+ avatarService.updateAvatar(mock(ProfileAvatarRequest.class), userDetails, mock(BindingResult.class));
+ verify(userPreferencesManager, Mockito.times(1)).updateUserGravatarPreference("test_username", false);
+ verify(fileBrowserService, Mockito.times(1)).deleteFile(any(), any());
verify(fileBrowserService, Mockito.times(1)).addFile(any(), any());
}
-
- @Test
- void shouldUpdateAvatarUserProfileAndRenameProfilePictureWithUserName() throws EntException {
- // set previous profile picture
- IUserProfile profile = this.buildValidUserProfile("user1", "prevImage.png");
- when(userProfileManager.getProfile(any())).thenReturn(profile);
- // set POST request DTO
- ProfileAvatarRequest profileAvatarRequest = new ProfileAvatarRequest();
- profileAvatarRequest.setFilename("image.png");
- profileAvatarRequest.setBase64(new byte[0]);
- // set user details to return desired username
- UserDetails userDetails = mock(UserDetails.class);
- when(userDetails.getUsername()).thenReturn("user1");
-
- avatarService.updateAvatar(profileAvatarRequest, userDetails, mock(BindingResult.class));
-
- ArgumentCaptor captorProfile = ArgumentCaptor.forClass(IUserProfile.class);
- verify(userProfileManager, Mockito.times(1)).updateProfile(Mockito.eq("user1"), captorProfile.capture());
- assertEquals("user1.png", captorProfile.getValue()
- .getAttributeByRole(SystemConstants.USER_PROFILE_ATTRIBUTE_ROLE_PROFILE_PICTURE).getValue());
- }
-
+
@Test
- void shouldUpdateAvatarUserProfileAndSetRenamedProfilePictureIfNoPreviousPictureWasPresent() throws EntException {
- // set previous profile picture
- IUserProfile profile = this.buildValidUserProfile("user1", null);
- when(userProfileManager.getProfile(any())).thenReturn(profile);
- // set POST request DTO
- ProfileAvatarRequest profileAvatarRequest = new ProfileAvatarRequest();
- profileAvatarRequest.setFilename("image.png");
- profileAvatarRequest.setBase64(new byte[0]);
- // set user details to return desired username
+ void shouldUpdateAvatarWithGravatar() throws EntException {
+ ProfileAvatarRequest request = new ProfileAvatarRequest(null, null, true);
UserDetails userDetails = mock(UserDetails.class);
- when(userDetails.getUsername()).thenReturn("user1");
-
- avatarService.updateAvatar(profileAvatarRequest, userDetails, mock(BindingResult.class));
-
- ArgumentCaptor captorProfile = ArgumentCaptor.forClass(IUserProfile.class);
- verify(userProfileManager, Mockito.times(1)).updateProfile(Mockito.eq("user1"), captorProfile.capture());
- assertEquals("user1.png", captorProfile.getValue()
- .getAttributeByRole(SystemConstants.USER_PROFILE_ATTRIBUTE_ROLE_PROFILE_PICTURE).getValue());
+ when(userDetails.getUsername()).thenReturn("test_username_gravatar");
+ when(fileBrowserService.exists("static/profile")).thenReturn(false);
+ avatarService.updateAvatar(request, userDetails, mock(BindingResult.class));
+ verify(userPreferencesManager, Mockito.times(1)).updateUserGravatarPreference("test_username_gravatar", true);
+ verify(fileBrowserService, Mockito.times(0)).deleteFile(any(), any());
+ verify(fileBrowserService, Mockito.times(0)).addFile(any(), any());
}
-
+
@Test
- void shouldDeleteAvatarFromFilesystemAndResetUserProfilePictureAttribute() throws EntException {
- // set previous profile picture
- IUserProfile profile = this.buildValidUserProfile("user1", "user1.png");
- when(userProfileManager.getProfile(any())).thenReturn(profile);
-
- // set user details to return desired username
+ void shouldDeleteAvatar() throws EntException {
UserDetails userDetails = mock(UserDetails.class);
when(userDetails.getUsername()).thenReturn("user1");
-
- //pretend image exists on filesystem
- when(fileBrowserService.exists(any())).thenReturn(true);
-
+ BasicFileAttributeViewDto dto = this.createMockFileAttributeDto("user1", "png");
+ when(fileBrowserService.exists("static/profile")).thenReturn(true);
+ when(fileBrowserService.browseFolder(Mockito.anyString(), Mockito.eq(false))).thenReturn(List.of(dto));
avatarService.deleteAvatar(userDetails, mock(BindingResult.class));
-
- ArgumentCaptor captorProfile = ArgumentCaptor.forClass(IUserProfile.class);
- verify(userProfileManager, Mockito.times(1)).updateProfile(Mockito.eq("user1"), captorProfile.capture());
- assertEquals("", captorProfile.getValue()
- .getAttributeByRole(SystemConstants.USER_PROFILE_ATTRIBUTE_ROLE_PROFILE_PICTURE).getValue());
verify(fileBrowserService, Mockito.times(1)).deleteFile(any(), any());
+ verify(userPreferencesManager, Mockito.times(1)).updateUserGravatarPreference("user1", false);
}
-
-
- @Test
- void shouldDeleteAvatarDoNothingAndRunSmoothlyIfUserImageIsNotSetInTheProfile() throws EntException {
- // set previous profile picture
- IUserProfile profile = this.buildValidUserProfile("user1", "");
- when(userProfileManager.getProfile(any())).thenReturn(profile);
-
- // set user details to return desired username
- UserDetails userDetails = mock(UserDetails.class);
- when(userDetails.getUsername()).thenReturn("user1");
-
- avatarService.deleteAvatar(userDetails, mock(BindingResult.class));
-
- ArgumentCaptor captorProfile = ArgumentCaptor.forClass(IUserProfile.class);
- verify(userProfileManager, Mockito.times(1)).updateProfile(Mockito.eq("user1"), captorProfile.capture());
- assertEquals("", captorProfile.getValue()
- .getAttributeByRole(SystemConstants.USER_PROFILE_ATTRIBUTE_ROLE_PROFILE_PICTURE).getValue());
- verify(fileBrowserService, Mockito.times(0)).deleteFile(any(), any());
- }
-
+
@Test
- void shouldDeleteAvatarThrowExceptionIfProfilePictureCheckImageGoesInError() throws EntException {
- // set previous profile picture
- IUserProfile profile = this.buildValidUserProfile("user1", "user1.png");
- when(userProfileManager.getProfile(any())).thenReturn(profile);
-
+ void shouldDeleteAvatarThrowException() throws EntException {
// set user details to return desired username
UserDetails userDetails = mock(UserDetails.class);
when(userDetails.getUsername()).thenReturn("user1");
-
- //pretend fileBrowserService.exists goes in error
- when(fileBrowserService.exists(any())).thenThrow(EntException.class);
-
+ BasicFileAttributeViewDto dto = this.createMockFileAttributeDto("user1", "jpg");
+ when(fileBrowserService.exists("static/profile")).thenReturn(true);
+ when(fileBrowserService.browseFolder(Mockito.anyString(), Mockito.eq(false))).thenReturn(List.of(dto));
+ Mockito.doThrow(RuntimeException.class).when(fileBrowserService).deleteFile(Mockito.any(), Mockito.eq(false));
assertThrows(RestServerError.class,
() -> avatarService.deleteAvatar(userDetails, mock(BindingResult.class)));
}
-
- @Test
- void shouldUpdateAvatarThrowExceptionIfProfilePictureCheckImageGoesInError() throws EntException {
- // set previous profile picture
- IUserProfile profile = this.buildValidUserProfile("user1", "user1.png");
- when(userProfileManager.getProfile(any())).thenReturn(profile);
-
- // set user details to return desired username
- UserDetails userDetails = mock(UserDetails.class);
- when(userDetails.getUsername()).thenReturn("user1");
-
- // set POST request DTO
- ProfileAvatarRequest profileAvatarRequest = new ProfileAvatarRequest();
- profileAvatarRequest.setFilename("image.png");
- profileAvatarRequest.setBase64(new byte[0]);
-
- //pretend fileBrowserService.exists goes in error
- when(fileBrowserService.exists(any())).thenThrow(EntException.class);
-
- assertThrows(RestServerError.class,
- () -> avatarService.updateAvatar(profileAvatarRequest, userDetails, mock(BindingResult.class)));
- }
- private IUserProfile buildValidUserProfile(String username, String attributeValue) {
- IUserProfile profile = Mockito.mock(IUserProfile.class);
- Mockito.lenient().when(profile.getId()).thenReturn(username);
- MonoTextAttribute attribute = new MonoTextAttribute();
- attribute.setName("profilepicture");
- attribute.setText(attributeValue);
- when(profile.getAttributeByRole(SystemConstants.USER_PROFILE_ATTRIBUTE_ROLE_PROFILE_PICTURE)).thenReturn(attribute);
- return profile;
+ private BasicFileAttributeViewDto createMockFileAttributeDto(String username, String fileExtention) {
+ BasicFileAttributeViewDto dto = new BasicFileAttributeViewDto();
+ dto.setDirectory(Boolean.FALSE);
+ dto.setName(username + "." + fileExtention);
+ return dto;
}
}
diff --git a/engine/src/test/java/org/entando/entando/aps/system/services/userprofile/UserProfileManagerIntegrationTest.java b/engine/src/test/java/org/entando/entando/aps/system/services/userprofile/UserProfileManagerIntegrationTest.java
index 7d6791e045..1afa81bad6 100644
--- a/engine/src/test/java/org/entando/entando/aps/system/services/userprofile/UserProfileManagerIntegrationTest.java
+++ b/engine/src/test/java/org/entando/entando/aps/system/services/userprofile/UserProfileManagerIntegrationTest.java
@@ -279,7 +279,7 @@ private void verifyRecordOrder(List records, String[] order) {
@Test
void testLoadRoles() throws Exception {
List roles = this.profileManager.getAttributeRoles();
- assertEquals(8, roles.size());
+ assertEquals(7, roles.size());
AttributeRole role1 = this.profileManager.getAttributeRole("userprofile:surname");
assertNotNull(role1);
assertEquals(1, role1.getAllowedAttributeTypes().size());
diff --git a/engine/src/test/java/org/entando/entando/web/userpreferences/UserPreferencesControllerIntegrationTest.java b/engine/src/test/java/org/entando/entando/web/userpreferences/UserPreferencesControllerIntegrationTest.java
index 965434a258..f7f396d151 100644
--- a/engine/src/test/java/org/entando/entando/web/userpreferences/UserPreferencesControllerIntegrationTest.java
+++ b/engine/src/test/java/org/entando/entando/web/userpreferences/UserPreferencesControllerIntegrationTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-Present Entando Inc. (http://www.entando.com) All rights reserved.
+ * Copyright 2023-Present Entando Inc. (http://www.entando.com) All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
@@ -33,7 +33,6 @@
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
-import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
class UserPreferencesControllerIntegrationTest extends AbstractControllerIntegrationTest {
diff --git a/engine/src/test/java/org/entando/entando/web/userprofile/12_POST_valid.json b/engine/src/test/java/org/entando/entando/web/userprofile/12_POST_valid.json
deleted file mode 100644
index 7f34fde71a..0000000000
--- a/engine/src/test/java/org/entando/entando/web/userprofile/12_POST_valid.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "id": "new_user_2",
- "typeCode":"OTH",
- "typeDescription":"Other user profile",
- "description":"Profile of user with profilepicture",
- "mainGroup":"free",
- "groups":[],
- "attributes": [
- {
- "code": "firstname",
- "value": "Eric"
- },{
- "code": "surname",
- "value": "Brown"
- },{
- "code": "email",
- "value": "eric.brown@entando.com"
- },{
- "code": "profilepicture",
- "value": "picture.png"
- }
- ]
-}
diff --git a/engine/src/test/java/org/entando/entando/web/userprofile/12_PUT_valid.json b/engine/src/test/java/org/entando/entando/web/userprofile/12_PUT_valid.json
deleted file mode 100644
index e444d38177..0000000000
--- a/engine/src/test/java/org/entando/entando/web/userprofile/12_PUT_valid.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "id": "new_user_2",
- "typeCode": "OTH",
- "typeDescription": "Type for test OTH",
- "description": "Profile of user",
- "mainGroup": "free",
- "groups": [
- "group1",
- "group2"
- ],
- "attributes": [
- {
- "code": "profilepicture",
- "value": "picture2.png"
- }
- ]
-}
diff --git a/engine/src/test/java/org/entando/entando/web/userprofile/ProfileTypeControllerIntegrationTest.java b/engine/src/test/java/org/entando/entando/web/userprofile/ProfileTypeControllerIntegrationTest.java
index 84f612b530..326f81e77e 100644
--- a/engine/src/test/java/org/entando/entando/web/userprofile/ProfileTypeControllerIntegrationTest.java
+++ b/engine/src/test/java/org/entando/entando/web/userprofile/ProfileTypeControllerIntegrationTest.java
@@ -376,7 +376,7 @@ void testGetUserProfileAttributeType_1() throws Exception {
result.andExpect(jsonPath("$.payload.code", is("Monotext")));
result.andExpect(jsonPath("$.payload.multilingual", is(false)));
result.andExpect(jsonPath("$.payload.dateFilterSupported", is(false)));
- result.andExpect(jsonPath("$.payload.allowedRoles", Matchers.hasSize(7)));
+ result.andExpect(jsonPath("$.payload.allowedRoles", Matchers.hasSize(6)));
result.andExpect(jsonPath("$.payload.simple", is(true)));
result.andExpect(jsonPath("$.errors", Matchers.hasSize(0)));
result.andExpect(jsonPath("$.metaData.size()", is(0)));
@@ -417,7 +417,7 @@ void testGetUserProfileAttributeType_3() throws Exception {
result.andExpect(jsonPath("$.payload.assignedRoles.size()", is(2)));
result.andExpect(jsonPath("$.payload.assignedRoles.userprofile:fullname", is("fullname")));
result.andExpect(jsonPath("$.payload.assignedRoles.userprofile:email", is("email")));
- result.andExpect(jsonPath("$.payload.allowedRoles", Matchers.hasSize(7)));
+ result.andExpect(jsonPath("$.payload.allowedRoles", Matchers.hasSize(6)));
result.andExpect(jsonPath("$.payload.dateFilterSupported", is(false)));
result.andExpect(jsonPath("$.payload.simple", is(true)));
result.andExpect(jsonPath("$.errors", Matchers.hasSize(0)));
diff --git a/engine/src/test/java/org/entando/entando/web/userprofile/UserProfileControllerIntegrationTest.java b/engine/src/test/java/org/entando/entando/web/userprofile/UserProfileControllerIntegrationTest.java
index 4dd91d152d..2fd400b765 100644
--- a/engine/src/test/java/org/entando/entando/web/userprofile/UserProfileControllerIntegrationTest.java
+++ b/engine/src/test/java/org/entando/entando/web/userprofile/UserProfileControllerIntegrationTest.java
@@ -16,7 +16,6 @@
import com.agiletec.aps.system.SystemConstants;
import com.agiletec.aps.system.common.entity.IEntityTypesConfigurer;
import com.agiletec.aps.system.common.entity.model.attribute.ListAttribute;
-import com.agiletec.aps.system.common.entity.model.attribute.MonoTextAttribute;
import com.agiletec.aps.system.services.group.Group;
import com.agiletec.aps.system.services.role.Permission;
import com.agiletec.aps.system.services.user.IUserManager;
@@ -26,7 +25,6 @@
import com.agiletec.aps.util.FileTextReader;
import org.entando.entando.aps.system.common.entity.model.attribute.EmailAttribute;
import org.entando.entando.aps.system.services.userprofile.IUserProfileManager;
-import org.entando.entando.aps.system.services.userprofile.IUserProfileService;
import org.entando.entando.aps.system.services.userprofile.model.IUserProfile;
import org.entando.entando.web.AbstractControllerIntegrationTest;
import org.entando.entando.web.utils.OAuth2TestUtils;
@@ -48,13 +46,22 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import com.agiletec.aps.system.common.entity.model.attribute.ITextAttribute;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.io.IOUtils;
+import org.entando.entando.aps.system.services.userpreferences.IUserPreferencesManager;
+import org.entando.entando.aps.system.services.userpreferences.UserPreferences;
+import org.entando.entando.web.userprofile.model.ProfileAvatarRequest;
+import org.hamcrest.CoreMatchers;
+import org.springframework.core.io.ClassPathResource;
+
class UserProfileControllerIntegrationTest extends AbstractControllerIntegrationTest {
@Autowired
- private IUserProfileService userProfileService;
+ private IUserProfileManager userProfileManager;
@Autowired
- private IUserProfileManager userProfileManager;
+ private IUserPreferencesManager userPreferencesManager;
@Autowired
private IUserManager userManager;
@@ -494,43 +501,95 @@ void testPostMyProfileOk() throws Exception {
}
}
}
-
+
@Test
- void testAddUserProfileWithProfilePicture() throws Exception {
+ void shouldPostFileAvatarReturn200OnRightInput() throws Exception {
+ UserPreferences userPreferences = this.userPreferencesManager.getUserPreferences("jack_bauer");
+ Assertions.assertNull(userPreferences);
try {
String accessToken = this.createAccessToken();
-
- this.executeProfilePost("12_POST_valid.json", accessToken, status().isOk()).andDo(resultPrint())
- .andExpect(jsonPath("$.payload.id", is("new_user_2")))
- .andExpect(jsonPath("$.errors.size()", is(0)))
- .andExpect(jsonPath("$.metaData.size()", is(0)));
-
- IUserProfile profile = this.userProfileManager.getProfile("new_user_2");
- Assertions.assertNotNull(profile);
- MonoTextAttribute profilePicture = (MonoTextAttribute) profile.getAttribute("profilepicture");
- Assertions.assertEquals("picture.png", profilePicture.getText());
-
- executeProfileGet("new_user_2", accessToken, status().isOk())
- .andExpect(jsonPath("$.payload.id", is("new_user_2")))
- .andExpect(jsonPath("$.payload.typeCode", is("OTH")))
- .andExpect(jsonPath("$.payload.attributes[0].value", is("Eric")))
- .andExpect(jsonPath("$.payload.attributes[1].value", is("Brown")))
- .andExpect(jsonPath("$.payload.attributes[2].value", is("eric.brown@entando.com")))
- .andExpect(jsonPath("$.payload.attributes[3].value", is("picture.png")));
-
- executeProfilePut("12_PUT_valid.json", "new_user_2", accessToken, status().isOk())
- .andExpect(jsonPath("$.payload.id", is("new_user_2")))
- .andExpect(jsonPath("$.payload.typeCode", is("OTH")))
- .andExpect(jsonPath("$.payload.attributes[0].value", is("Eric")))
- .andExpect(jsonPath("$.payload.attributes[1].value", is("Brown")))
- .andExpect(jsonPath("$.payload.attributes[2].value", is("eric.brown@entando.com")))
- .andExpect(jsonPath("$.payload.attributes[3].value", is("picture2.png")));
+ ProfileAvatarRequest profileAvatarRequest = new ProfileAvatarRequest("myFile.png",
+ IOUtils.toByteArray(new ClassPathResource("userprofile/image.png").getInputStream()), false);
+ ResultActions result = mockMvc.perform(
+ post("/userProfiles/avatar")
+ .content(new ObjectMapper().writeValueAsString(profileAvatarRequest))
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header("Authorization", "Bearer " + accessToken));
+ result.andExpect(status().isOk())
+ .andExpect(jsonPath("$.payload.filename").value("jack_bauer.png"));
+ userPreferences = this.userPreferencesManager.getUserPreferences("jack_bauer");
+ Assertions.assertNotNull(userPreferences);
+ Assertions.assertFalse(userPreferences.isGravatar());
} finally {
- this.userProfileManager.deleteProfile("new_user_2");
- Assertions.assertNull(this.userProfileManager.getProfile("new_user_2"));
+ this.userPreferencesManager.deleteUserPreferences("jack_bauer");
}
}
-
+
+ @Test
+ void shouldPostDeleteGravatarReturn200() throws Exception {
+ UserPreferences userPreferences = this.userPreferencesManager.getUserPreferences("jack_bauer");
+ Assertions.assertNull(userPreferences);
+ try {
+ IUserProfile profile = this.userProfileManager.getDefaultProfileType();
+ ITextAttribute emailAttribute = (ITextAttribute) profile.getAttributeByRole(SystemConstants.USER_PROFILE_ATTRIBUTE_ROLE_MAIL);
+ emailAttribute.setText("jack_bauer@jack_bauer.com", "it");
+ profile.setId("jack_bauer");
+ this.userProfileManager.addProfile("jack_bauer", profile);
+ String accessToken = this.createAccessToken();
+ ProfileAvatarRequest profileAvatarRequest = new ProfileAvatarRequest(null, null, true);
+ ResultActions resultPost = mockMvc.perform(
+ post("/userProfiles/avatar")
+ .content(new ObjectMapper().writeValueAsString(profileAvatarRequest))
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header("Authorization", "Bearer " + accessToken));
+ resultPost.andExpect(status().isOk())
+ .andExpect(jsonPath("$.payload.useGravatar").value(true));
+ userPreferences = this.userPreferencesManager.getUserPreferences("jack_bauer");
+ Assertions.assertNotNull(userPreferences);
+ Assertions.assertTrue(userPreferences.isGravatar());
+
+ ResultActions resultGet = mockMvc.perform(
+ get("/userProfiles/avatar")
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header("Authorization", "Bearer " + accessToken));
+ resultGet.andExpect(status().isOk())
+ .andExpect(jsonPath("$.payload.useGravatar").value("true"));
+
+ ResultActions resultDelete = mockMvc.perform(
+ delete("/userProfiles/avatar")
+ .header("Authorization", "Bearer " + accessToken));
+ resultDelete.andExpect(status().isOk())
+ .andExpect(jsonPath("$.payload.username").value("jack_bauer"))
+ .andExpect(jsonPath("$.errors.size()", CoreMatchers.is(0)))
+ .andExpect(jsonPath("$.metaData.size()", CoreMatchers.is(0)));
+ userPreferences = this.userPreferencesManager.getUserPreferences("jack_bauer");
+ Assertions.assertNotNull(userPreferences);
+ Assertions.assertFalse(userPreferences.isGravatar());
+ } finally {
+ this.userPreferencesManager.deleteUserPreferences("jack_bauer");
+ this.userProfileManager.deleteProfile("jack_bauer");
+ }
+ }
+
+ @Test
+ void shouldReturnErrorOnPostGravatarWithNullProfile() throws Exception {
+ try {
+ String accessToken = this.createAccessToken();
+ ProfileAvatarRequest profileAvatarRequest = new ProfileAvatarRequest(null, null, true);
+ ResultActions result = mockMvc.perform(
+ post("/userProfiles/avatar")
+ .content(new ObjectMapper().writeValueAsString(profileAvatarRequest))
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .header("Authorization", "Bearer " + accessToken));
+ result.andExpect(status().isBadRequest());
+ UserPreferences userPreferences = this.userPreferencesManager.getUserPreferences("jack_bauer");
+ Assertions.assertNull(userPreferences);
+ } finally {
+ this.userPreferencesManager.deleteUserPreferences("jack_bauer");
+ this.userProfileManager.deleteProfile("jack_bauer");
+ }
+ }
+
private String createAccessToken() throws Exception {
UserDetails user = new OAuth2TestUtils.UserBuilder("jack_bauer", "0x24")
.withAuthorization(Group.FREE_GROUP_NAME, "manageUserProfile", Permission.MANAGE_USER_PROFILES)
@@ -604,5 +663,11 @@ private ResultActions executeProfileTypePost(String fileName, String accessToken
result.andDo(resultPrint()).andExpect(expected);
return result;
}
-
+
+
+
+
+
+
+
}
diff --git a/engine/src/test/java/org/entando/entando/web/userprofile/UserProfileControllerTest.java b/engine/src/test/java/org/entando/entando/web/userprofile/UserProfileControllerTest.java
index 3f38250d51..088af0dc7a 100644
--- a/engine/src/test/java/org/entando/entando/web/userprofile/UserProfileControllerTest.java
+++ b/engine/src/test/java/org/entando/entando/web/userprofile/UserProfileControllerTest.java
@@ -14,7 +14,6 @@
package org.entando.entando.web.userprofile;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@@ -28,6 +27,7 @@
import com.agiletec.aps.system.services.user.UserDetails;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.stream.Stream;
+import org.apache.commons.io.IOUtils;
import org.entando.entando.aps.system.exception.ResourceNotFoundException;
import org.entando.entando.aps.system.services.entity.model.EntityDto;
import org.entando.entando.aps.system.services.userprofile.IAvatarService;
@@ -51,6 +51,7 @@
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.stubbing.Answer;
+import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
@@ -71,7 +72,6 @@ class UserProfileControllerTest extends AbstractControllerTest {
@Mock
private IUserProfileManager userProfileManager;
- @Mock
private ProfileAvatarValidator profileAvatarValidator;
@Mock
@@ -79,6 +79,7 @@ class UserProfileControllerTest extends AbstractControllerTest {
@BeforeEach
public void setUp() throws Exception {
+ profileAvatarValidator = new ProfileAvatarValidator(userProfileManager);
ProfileController controller = new ProfileController(userProfileService, profileValidator,
profileAvatarValidator, userManager,
userProfileManager, avatarService);
@@ -188,17 +189,14 @@ void shouldGetAvatarReturn200AndWellFormedResponseIfImageExists() throws Excepti
@Test
void shouldPostAvatarReturn400OnIllegalInput() throws Exception {
String accessToken = this.createAccessToken();
-
Answer ans = invocation -> {
Object[] args = invocation.getArguments();
((BindingResult) args[1]).rejectValue("filename", "1", new String[]{"fileName_without_extension"},
"fileBrowser.filename.invalidFilename");
return null;
};
- doAnswer(ans).when(profileAvatarValidator).validate(any(), any());
ProfileAvatarRequest profileAvatarRequest = new ProfileAvatarRequest("fileName_without_extension",
- new byte[1]);
-
+ new byte[1], false);
ResultActions result = mockMvc.perform(
post("/userProfiles/avatar")
.content(new ObjectMapper().writeValueAsString(profileAvatarRequest))
@@ -210,9 +208,9 @@ void shouldPostAvatarReturn400OnIllegalInput() throws Exception {
@Test
void shouldPostAvatarReturn200OnRightInput() throws Exception {
String accessToken = this.createAccessToken();
- ProfileAvatarRequest profileAvatarRequest = new ProfileAvatarRequest("myFile.png", new byte[1]);
+ ProfileAvatarRequest profileAvatarRequest = new ProfileAvatarRequest("myFile.png",
+ IOUtils.toByteArray(new ClassPathResource("userprofile/image.png").getInputStream()), false);
when(avatarService.updateAvatar(any(), any(), any())).thenReturn("jack_bauer.png");
-
ResultActions result = mockMvc.perform(
post("/userProfiles/avatar")
.content(new ObjectMapper().writeValueAsString(profileAvatarRequest))
@@ -225,17 +223,15 @@ void shouldPostAvatarReturn200OnRightInput() throws Exception {
@Test
void shouldPostAvatarReturn400OnFileServiceAddFailureIfFileAlreadyPresent() throws Exception {
String accessToken = this.createAccessToken();
-
Answer ans = invocation -> {
Object[] args = invocation.getArguments();
((BindingResult) args[2]).reject("2", new String[]{"static/profile/jack-bauer.png", "false"},
"fileBrowser.file.exists");
return null;
};
- doAnswer(ans).when(avatarService).updateAvatar(any(), any(), any());
+ Mockito.lenient().doAnswer(ans).when(avatarService).updateAvatar(any(), any(), any());
ProfileAvatarRequest profileAvatarRequest = new ProfileAvatarRequest("image.png",
- new byte[1]);
-
+ new byte[1], false);
ResultActions result = mockMvc.perform(
post("/userProfiles/avatar")
.content(new ObjectMapper().writeValueAsString(profileAvatarRequest))
@@ -249,7 +245,6 @@ void shouldPostAvatarReturn400OnFileServiceAddFailureIfFileAlreadyPresent() thro
@MethodSource("provideValuesFor400")
void shouldPostAvatarReturn400(String request, String expectedErrorCode) throws Exception {
String accessToken = this.createAccessToken();
-
ResultActions result = mockMvc.perform(
post("/userProfiles/avatar")
.content(request)
@@ -258,8 +253,7 @@ void shouldPostAvatarReturn400(String request, String expectedErrorCode) throws
result.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.errors[0].code").value(expectedErrorCode));
}
-
-
+
@Test
void shouldDeleteAvatarReturn200() throws Exception {
String accessToken = this.createAccessToken();
@@ -271,16 +265,15 @@ void shouldDeleteAvatarReturn200() throws Exception {
.andExpect(jsonPath("$.errors.size()", CoreMatchers.is(0)))
.andExpect(jsonPath("$.metaData.size()", CoreMatchers.is(0)));
}
-
-
+
private static Stream provideValuesFor400() {
return Stream.of(
- Arguments.of("{\"filenam\":\"image.png\",\"base64\":\"AA==\"}", "NotBlank"),
- Arguments.of("{\"base64\":\"AA==\"}", "NotBlank"),
- Arguments.of("{\"filename\":\"\",\"base64\":\"AA==\"}", "NotBlank"),
- Arguments.of("{\"filename\":\"image.png\",\"base6\":\"AA==\"}", "NotEmpty"),
- Arguments.of("{\"filename\":\"image.png\"}", "NotEmpty"),
- Arguments.of("{\"filename\":\"image.png\",\"base64\":\"\"}", "NotEmpty")
+ Arguments.of("{\"filenam\":\"image.png\",\"base64\":\"AA==\"}", "52"),
+ Arguments.of("{\"base64\":\"AA==\"}", "52"),
+ Arguments.of("{\"filename\":\"\",\"base64\":\"AA==\"}", "52"),
+ Arguments.of("{\"filename\":\"image.png\",\"base6\":\"AA==\"}", "53"),
+ Arguments.of("{\"filename\":\"image.png\"}", "53"),
+ Arguments.of("{\"filename\":\"image.png\",\"base64\":\"\"}", "2")
);
}
@@ -314,4 +307,5 @@ private String createAccessToken() throws Exception {
UserDetails user = new OAuth2TestUtils.UserBuilder("jack_bauer", "0x24").grantedToRoleAdmin().build();
return mockOAuthInterceptor(user);
}
+
}
diff --git a/engine/src/test/java/org/entando/entando/web/userprofile/validator/ProfileAvatarValidatorTest.java b/engine/src/test/java/org/entando/entando/web/userprofile/validator/ProfileAvatarValidatorTest.java
index 1a4b382f0e..5b281291f9 100644
--- a/engine/src/test/java/org/entando/entando/web/userprofile/validator/ProfileAvatarValidatorTest.java
+++ b/engine/src/test/java/org/entando/entando/web/userprofile/validator/ProfileAvatarValidatorTest.java
@@ -19,71 +19,122 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import com.agiletec.aps.system.SystemConstants;
+import com.agiletec.aps.system.services.user.UserDetails;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import javax.imageio.ImageIO;
import org.apache.commons.io.IOUtils;
+import org.entando.entando.aps.system.services.userprofile.IUserProfileManager;
+import org.entando.entando.aps.system.services.userprofile.model.IUserProfile;
+import org.entando.entando.ent.exception.EntException;
+import org.entando.entando.ent.exception.EntRuntimeException;
import org.entando.entando.web.userprofile.model.ProfileAvatarRequest;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ClassPathResource;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.FieldError;
+@ExtendWith(MockitoExtension.class)
class ProfileAvatarValidatorTest {
-
+
+ @Mock
+ private IUserProfileManager userProfileManager;
+
+ @InjectMocks
+ private ProfileAvatarValidator profileAvatarValidator;
@Test
void shouldSupportOnlyProfileAvatarRequest() {
- assertTrue(new ProfileAvatarValidator().supports(ProfileAvatarRequest.class));
- assertFalse(new ProfileAvatarValidator().supports(Object.class));
+ assertTrue(profileAvatarValidator.supports(ProfileAvatarRequest.class));
+ assertFalse(profileAvatarValidator.supports(Object.class));
}
@Test
void shouldNotValidateFileNamesMissingExtensions() throws IOException {
ProfileAvatarRequest request = new ProfileAvatarRequest("missing_extension_filename",
- IOUtils.toByteArray(new ClassPathResource("userprofile/image.png").getInputStream()));
+ IOUtils.toByteArray(new ClassPathResource("userprofile/image.png").getInputStream()), false);
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(request, "profileAvatarRequest");
- new ProfileAvatarValidator().validate(request, errors);
+ profileAvatarValidator.validate(request, Mockito.mock(UserDetails.class), errors);
assertEquals(1, errors.getErrorCount());
assertEquals("fileBrowser.filename.invalidFilename", errors.getAllErrors().get(0).getDefaultMessage());
assertEquals("missing_extension_filename", ((FieldError) errors.getAllErrors().get(0)).getRejectedValue());
}
+ @Test
+ void shouldNotValidateUserInCaseOfMissingProfile() throws Exception {
+ ProfileAvatarRequest request = new ProfileAvatarRequest(null, null, true);
+ UserDetails user = Mockito.mock(UserDetails.class);
+ Mockito.when(user.getUsername()).thenReturn("username_test");
+ Mockito.when(userProfileManager.getProfile("username_test")).thenReturn(null);
+ BeanPropertyBindingResult errors = new BeanPropertyBindingResult(request, "profileAvatarRequest");
+ profileAvatarValidator.validate(request, user, errors);
+ assertEquals(1, errors.getErrorCount());
+ assertEquals("avatar.emailAttribute.missing", errors.getAllErrors().get(0).getDefaultMessage());
+ assertEquals("useGravatar", ((FieldError) errors.getAllErrors().get(0)).getField());
+ }
+
+ @Test
+ void shouldNotValidateUserInCaseOfMissingEmailAttribute() throws Exception {
+ ProfileAvatarRequest request = new ProfileAvatarRequest(null, null, true);
+ UserDetails user = Mockito.mock(UserDetails.class);
+ Mockito.when(user.getUsername()).thenReturn("username_test");
+ IUserProfile userProfile = Mockito.mock(IUserProfile.class);
+ Mockito.when(userProfileManager.getProfile("username_test")).thenReturn(userProfile);
+ Mockito.when(userProfile.getAttributeByRole(SystemConstants.USER_PROFILE_ATTRIBUTE_ROLE_MAIL)).thenReturn(null);
+ BeanPropertyBindingResult errors = new BeanPropertyBindingResult(request, "profileAvatarRequest");
+ profileAvatarValidator.validate(request, user, errors);
+ assertEquals(1, errors.getErrorCount());
+ assertEquals("avatar.emailAttribute.missing", errors.getAllErrors().get(0).getDefaultMessage());
+ assertEquals("useGravatar", ((FieldError) errors.getAllErrors().get(0)).getField());
+ }
+
+ @Test
+ void shouldThrowServerErrorInCaseOfErrorGettingUserProfile() throws Exception {
+ ProfileAvatarRequest request = new ProfileAvatarRequest(null, null, true);
+ UserDetails user = Mockito.mock(UserDetails.class);
+ Mockito.when(user.getUsername()).thenReturn("username_test");
+ Mockito.when(userProfileManager.getProfile("username_test")).thenThrow(EntException.class);
+ BeanPropertyBindingResult errors = new BeanPropertyBindingResult(request, "profileAvatarRequest");
+ assertThrows(EntRuntimeException.class, () -> profileAvatarValidator.validate(request, user, errors));
+ }
+
@Test
void shouldNotValidateFilesOtherThanImages() {
String notValidBase64Image = "bm90IGFuIGltYWdl";
- ProfileAvatarRequest request = new ProfileAvatarRequest("valid_filename.txt", notValidBase64Image.getBytes());
+ ProfileAvatarRequest request = new ProfileAvatarRequest("valid_filename.txt", notValidBase64Image.getBytes(), false);
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(request, "profileAvatarRequest");
- new ProfileAvatarValidator().validate(request, errors);
+ profileAvatarValidator.validate(request, Mockito.mock(UserDetails.class), errors);
assertEquals(1, errors.getErrorCount());
assertEquals("fileBrowser.file.invalidType", errors.getAllErrors().get(0).getDefaultMessage());
assertEquals("base64", ((FieldError) errors.getAllErrors().get(0)).getField());
-
}
-
@Test
void shouldThrowUncheckedIOExceptionIfImageReadingFails() throws IOException {
ProfileAvatarRequest request = new ProfileAvatarRequest("image.png",
- IOUtils.toByteArray(new ClassPathResource("userprofile/image.png").getInputStream()));
+ IOUtils.toByteArray(new ClassPathResource("userprofile/image.png").getInputStream()), false);
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(request, "profileAvatarRequest");
try (MockedStatic mockStatic = Mockito.mockStatic(ImageIO.class)) {
mockStatic.when(() -> ImageIO.read(any(InputStream.class))).thenThrow(IOException.class);
- ProfileAvatarValidator profileAvatarValidator = new ProfileAvatarValidator();
- assertThrows(UncheckedIOException.class, () -> profileAvatarValidator.validate(request, errors));
+ assertThrows(UncheckedIOException.class, () -> profileAvatarValidator.validate(request, Mockito.mock(UserDetails.class), errors));
}
}
@Test
void shouldValidateAcceptValidImageWithValidFileName() throws IOException {
ProfileAvatarRequest request = new ProfileAvatarRequest("image.png",
- IOUtils.toByteArray(new ClassPathResource("userprofile/image.png").getInputStream()));
+ IOUtils.toByteArray(new ClassPathResource("userprofile/image.png").getInputStream()), false);
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(request, "profileAvatarRequest");
- new ProfileAvatarValidator().validate(request, errors);
+ profileAvatarValidator.validate(request, Mockito.mock(UserDetails.class), errors);
assertTrue(errors.getAllErrors().isEmpty());
-
}
+
}
diff --git a/pom.xml b/pom.xml
index ca7f4382ae..10854e5cb8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -80,8 +80,6 @@
agile
jdbc:derby:memory:testPort;create=true
jdbc:derby:memory:testServ;create=true
- 11
- 11
5.3.27
5.5.7
@@ -155,7 +153,7 @@
0.8
2.18.0
4.4
- 1.18.20
+ 1.18.30
3.3.0
3.0.0-M4
2.5
diff --git a/seo-plugin/README.md b/seo-plugin/README.md
index a2ae27761a..bc1c641d05 100644
--- a/seo-plugin/README.md
+++ b/seo-plugin/README.md
@@ -15,19 +15,6 @@ entando-plugin-jpseo
The SEO plugin enables some functionality inside the page configuration (new parameters and friendly url), in the content handling (new role attribute to pilot friendly url) and to extract the sitemap.
-## Installation
-
-In order to install the SEO plugin, you must insert the following dependency in the pom.xml file of your project:
-
-```
-
- org.entando.entando.plugins
- entando-plugin-jpseo
- ${entando.version}
- war
-
-```
-
## How to use
###### Modify of web.xml
diff --git a/versioning-plugin/README.md b/versioning-plugin/README.md
index 637c0b188e..b3c443f772 100644
--- a/versioning-plugin/README.md
+++ b/versioning-plugin/README.md
@@ -19,18 +19,6 @@ This plugin adds new administration interfaces related to the content tracking;
Please also be aware that a resource referenced by older versions of the content cannot be deleted to avoid inconsistency on restore.
Though the plugin installation is not difficult at all, we are going to modify the system tables, so a backup of your database is highly recommended. Furthermore, you may be required to customize the scripts to your needs before installation.
-**Installation**
-
-In order to install the Versioning Plugin, you must insert the following dependency in the pom.xml file of your project:
-```
-
- org.entando.entando.plugins
- entando-plugin-jpversioning
- ${entando.version}
- war
-
-```
-
**Configuration**
You can access to Versioning Plugin’s functionality through the usual left menu: _Plugins_ -> _Versioning_ in the Administration Area.