Skip to content
Snippets Groups Projects
Commit 6402bace authored by srosse's avatar srosse
Browse files

OO-2515: save the master file untouched, deliver more pixels for the print...

OO-2515: save the master file untouched, deliver  more pixels for the print view of the members course element, add REST api methods to check if a particular size of portrait is available
parent cce55938
No related branches found
No related tags found
No related merge requests found
Showing
with 248 additions and 20 deletions
......@@ -467,7 +467,7 @@ public class MembersCourseNodeRunController extends FormBasicController {
@Override
public Controller createController(UserRequest lureq, WindowControl lwControl) {
lwControl.getWindowBackOffice().getChiefController().addBodyCssClass("o_cmembers_print");
return new MembersPrintController(lureq, lwControl, courseEnv, avatarBaseURL, userPropertyHandlers,
return new MembersPrintController(lureq, lwControl, courseEnv, userPropertyHandlers,
ownerList, coachList, participantList);
}
};
......
......@@ -21,6 +21,11 @@ package org.olat.course.nodes.members;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.olat.core.commons.modules.bc.meta.MetaInfo;
import org.olat.core.commons.modules.bc.meta.tagged.MetaTagged;
import org.olat.core.dispatcher.mapper.Mapper;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.panel.MainPanel;
......@@ -28,8 +33,15 @@ import org.olat.core.gui.components.velocity.VelocityContainer;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.control.controller.BasicController;
import org.olat.core.gui.media.MediaResource;
import org.olat.core.gui.media.NotFoundMediaResource;
import org.olat.core.util.vfs.VFSLeaf;
import org.olat.core.util.vfs.VFSMediaResource;
import org.olat.course.run.environment.CourseEnvironment;
import org.olat.user.DisplayPortraitManager;
import org.olat.user.UserManager;
import org.olat.user.propertyhandlers.UserPropertyHandler;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
......@@ -44,11 +56,17 @@ public class MembersPrintController extends BasicController {
private final List<UserPropertyHandler> userPropertyHandlers;
@Autowired
private UserManager userManager;
@Autowired
private DisplayPortraitManager portraitManager;
public MembersPrintController(UserRequest ureq, WindowControl wControl,
CourseEnvironment courseEnv, String avatarBaseURL, List<UserPropertyHandler> userPropertyHandlers,
CourseEnvironment courseEnv, List<UserPropertyHandler> userPropertyHandlers,
List<Member> owners, List<Member> coaches, List<Member> participants) {
super(ureq, wControl);
this.avatarBaseURL = avatarBaseURL;
avatarBaseURL = registerCacheableMapper(ureq, "avatars-members-high-quality", new UserAvatarHQMapper());
this.userPropertyHandlers = userPropertyHandlers;
mainVC = createVelocityContainer("print");
......@@ -90,4 +108,34 @@ public class MembersPrintController extends BasicController {
protected void doDispose() {
//
}
private class UserAvatarHQMapper implements Mapper {
@Override
public MediaResource handle(String relPath, HttpServletRequest request) {
MediaResource rsrc = null;
if(relPath != null) {
if(relPath.startsWith("/")) {
relPath = relPath.substring(1, relPath.length());
}
int endKeyIndex = relPath.indexOf('/');
if(endKeyIndex > 0) {
String idKey = relPath.substring(0, endKeyIndex);
Long key = Long.parseLong(idKey);
String username = userManager.getUsername(key);
VFSLeaf portrait = portraitManager.getLargestVFSPortrait(username);
if(portrait instanceof MetaTagged) {
MetaInfo meta = ((MetaTagged)portrait).getMetaInfo();
portrait = meta.getThumbnail(300, 300, false);
}
if(portrait == null) {
return new NotFoundMediaResource(relPath);
}
return new VFSMediaResource(portrait);
}
}
return rsrc;
}
}
}
......@@ -26,18 +26,27 @@
package org.olat.user;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.List;
import org.olat.basesecurity.BaseSecurityManager;
import org.olat.core.CoreSpringFactory;
import org.olat.core.commons.modules.bc.FolderConfig;
import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl;
import org.olat.core.commons.services.image.ImageService;
import org.olat.core.commons.services.image.Size;
import org.olat.core.gui.media.FileMediaResource;
import org.olat.core.gui.media.MediaResource;
import org.olat.core.id.Identity;
import org.olat.core.manager.BasicManager;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.FileUtils;
import org.olat.core.util.StringHelper;
import org.olat.core.util.vfs.VFSContainer;
import org.olat.core.util.vfs.VFSItem;
import org.olat.core.util.vfs.VFSLeaf;
/**
* Description: <br>
......@@ -47,17 +56,20 @@ import org.olat.core.util.StringHelper;
* Initial Date: Sept 08, 2005 <br>
* @author Alexander Schneider
*/
public class DisplayPortraitManager extends BasicManager implements UserDataDeletable {
public class DisplayPortraitManager implements UserDataDeletable {
private static final OLog log = Tracing.createLoggerFor(DisplayPortraitManager.class);
private static DisplayPortraitManager singleton;
private static final String LOGO_PREFIX_FILENAME = "logo";
private static final String LOGO_BIG_FILENAME = LOGO_PREFIX_FILENAME + "_big";
private static final String LOGO_SMALL_FILENAME = LOGO_PREFIX_FILENAME + "_small";
private static final String LOGO_MASTER_FILENAME = LOGO_PREFIX_FILENAME + "_master";
private static final String PORTRAIT_PREFIX_FILENAME = "portrait";
private static final String PORTRAIT_BIG_FILENAME = PORTRAIT_PREFIX_FILENAME + "_big";
private static final String PORTRAIT_SMALL_FILENAME = PORTRAIT_PREFIX_FILENAME + "_small";
private static final String PORTRAIT_MASTER_FILENAME = PORTRAIT_PREFIX_FILENAME + "_master";
// The following class names refer to CSS class names in olat.css
public static final String AVATAR_BIG_CSS_CLASS = "o_portrait_avatar";
public static final String AVATAR_SMALL_CSS_CLASS = "o_portrait_avatar_small";
......@@ -112,6 +124,13 @@ public class DisplayPortraitManager extends BasicManager implements UserDataDele
return getPortraitResource(identityKey, PORTRAIT_BIG_FILENAME);
}
public MediaResource getMasterPortraitResource(String String) {
return getPortraitResource(String, PORTRAIT_MASTER_FILENAME);
}
public MediaResource getMasterPortraitResource(Long identityKey) {
return getPortraitResource(identityKey, PORTRAIT_MASTER_FILENAME);
}
public MediaResource getSmallLogoResource(String username) {
return getPortraitResource(username, LOGO_SMALL_FILENAME);
}
......@@ -162,6 +181,32 @@ public class DisplayPortraitManager extends BasicManager implements UserDataDele
return getPortraitFile(username, PORTRAIT_BIG_FILENAME);
}
public File getMasterPortrait(String username) {
return getPortraitFile(username, PORTRAIT_MASTER_FILENAME);
}
public VFSLeaf getLargestVFSPortrait(String username) {
VFSLeaf portrait = getPortraitLeaf(username, PORTRAIT_MASTER_FILENAME);
if(portrait == null || !portrait.exists()) {
portrait = getPortraitLeaf(username, PORTRAIT_BIG_FILENAME);
}
if(portrait == null || !portrait.exists()) {
portrait = getPortraitLeaf(username, PORTRAIT_SMALL_FILENAME);
}
return portrait;
}
public File getLargestPortrait(String username) {
File portrait = getPortraitFile(username, PORTRAIT_MASTER_FILENAME);
if(portrait == null || !portrait.exists()) {
portrait = getPortraitFile(username, PORTRAIT_BIG_FILENAME);
}
if(portrait == null || !portrait.exists()) {
portrait = getPortraitFile(username, PORTRAIT_SMALL_FILENAME);
}
return portrait;
}
public File getSmallLogo(String username) {
return getPortraitFile(username, LOGO_SMALL_FILENAME);
}
......@@ -170,6 +215,17 @@ public class DisplayPortraitManager extends BasicManager implements UserDataDele
return getPortraitFile(username, LOGO_BIG_FILENAME);
}
public File getLargestLogo(String username) {
File portrait = getPortraitFile(username, LOGO_MASTER_FILENAME);
if(portrait == null || !portrait.exists()) {
portrait = getPortraitFile(username, LOGO_BIG_FILENAME);
}
if(portrait == null || !portrait.exists()) {
portrait = getPortraitFile(username, LOGO_SMALL_FILENAME);
}
return portrait;
}
public boolean hasPortrait(String username) {
File portraitDir = getPortraitDir(username);
if(portraitDir != null) {
......@@ -200,17 +256,35 @@ public class DisplayPortraitManager extends BasicManager implements UserDataDele
return null;
}
private VFSLeaf getPortraitLeaf(String username, String prefix) {
VFSContainer portraitDir = getPortraitFolder(username);
if(portraitDir != null) {
List<VFSItem> portraits = portraitDir.getItems();
if(portraits.size() > 0) {
for(VFSItem file:portraits) {
if(file.getName().startsWith(prefix) && file instanceof VFSLeaf) {
return (VFSLeaf)file;
}
}
}
}
return null;
}
public void setPortrait(File file, String filename, String username) {
setImage(file, filename, username, PORTRAIT_PREFIX_FILENAME, PORTRAIT_BIG_FILENAME, PORTRAIT_SMALL_FILENAME,
setImage(file, filename, username, PORTRAIT_PREFIX_FILENAME,
PORTRAIT_MASTER_FILENAME, PORTRAIT_BIG_FILENAME, PORTRAIT_SMALL_FILENAME,
WIDTH_PORTRAIT_BIG, WIDTH_PORTRAIT_SMALL);
}
public void setLogo(File file, String filename, String username) {
setImage(file, filename, username, LOGO_PREFIX_FILENAME, LOGO_BIG_FILENAME, LOGO_SMALL_FILENAME,
setImage(file, filename, username, LOGO_PREFIX_FILENAME,
LOGO_MASTER_FILENAME, LOGO_BIG_FILENAME, LOGO_SMALL_FILENAME,
WIDTH_LOGO_BIG, WIDTH_LOGO_SMALL);
}
private void setImage(File file, String filename, String username, String prefix, String largeImagePrefix, String smallImagePrefix,
private void setImage(File file, String filename, String username, String prefix,
String masterImagePrefix, String largeImagePrefix, String smallImagePrefix,
int maxBigWidth, int maxSmallWidth) {
File directory = getPortraitDir(username);
if(directory != null) {
......@@ -232,6 +306,14 @@ public class DisplayPortraitManager extends BasicManager implements UserDataDele
extension = "png";
}
}
try {
File masterFile = new File(directory, masterImagePrefix + "." + extension);
Files.copy(file.toPath(), masterFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
log.error("", e);
}
File bigFile = new File(directory, largeImagePrefix + "." + extension);
File smallFile = new File(directory, smallImagePrefix + "." + extension);
ImageService imageHelper = CoreSpringFactory.getImpl(ImageService.class);
......@@ -288,6 +370,14 @@ public class DisplayPortraitManager extends BasicManager implements UserDataDele
return portraitDir;
}
public OlatRootFolderImpl getPortraitFolder(String identityName) {
OlatRootFolderImpl folder = new OlatRootFolderImpl(FolderConfig.getUserHomePage(identityName) + "/portrait", null);
if(!folder.exists()) {
folder.getBasefile().mkdirs();
}
return folder;
}
/**
* Delete home-page config-file of a certain user.
* @see org.olat.user.UserDataDeletable#deleteUserData(org.olat.core.id.Identity)
......@@ -299,6 +389,6 @@ public class DisplayPortraitManager extends BasicManager implements UserDataDele
if(portraitDir.exists()) {
FileUtils.deleteDirsAndFiles(portraitDir, true, true);
}
logDebug("Homepage-config file deleted for identity=" + identity);
log.debug("Homepage-config file deleted for identity=" + identity);
}
}
\ No newline at end of file
......@@ -242,7 +242,7 @@ public class ProfileFormController extends FormBasicController {
groupContainer.setFormTitle(translate("ul.header"));
formLayout.add(groupContainer);
File portraitFile = dps.getBigPortrait(identityToModify.getName());
File portraitFile = dps.getLargestPortrait(identityToModify.getName());
// Init upload controller
Set<String> mimeTypes = new HashSet<String>();
mimeTypes.add("image/gif");
......@@ -267,7 +267,7 @@ public class ProfileFormController extends FormBasicController {
groupContainer.setFormTitle(translate("logo.header"));
formLayout.add(groupContainer);
File logoFile = dps.getBigLogo(identityToModify.getName());
File logoFile = dps.getLargestLogo(identityToModify.getName());
logoUpload = uifactory.addFileElement(getWindowControl(), "logo.select", "logo.select", groupContainer);
logoUpload.setMaxUploadSizeKB(10000, null, null);
logoUpload.setPreview(ureq.getUserSession(), true);
......@@ -385,7 +385,7 @@ public class ProfileFormController extends FormBasicController {
if (source == portraitUpload) {
if(event instanceof FileElementEvent) {
if(FileElementEvent.DELETE.equals(event.getCommand())) {
File img = dps.getBigPortrait(identityToModify.getName());
File img = dps.getLargestPortrait(identityToModify.getName());
if(portraitUpload.getUploadFile() != null) {
portraitUpload.reset();
if(img != null) {
......@@ -405,7 +405,7 @@ public class ProfileFormController extends FormBasicController {
} else if (source == logoUpload) {
if(event instanceof FileElementEvent) {
if(FileElementEvent.DELETE.equals(event.getCommand())) {
File img = dps.getBigLogo(identityToModify.getName());
File img = dps.getLargestLogo(identityToModify.getName());
if(logoUpload.getUploadFile() != null) {
logoUpload.reset();
if(img != null) {
......
......@@ -573,6 +573,45 @@ public class UserWebService {
}
}
/**
* Retrieves the portrait of an user
* @response.representation.200.mediaType application/octet-stream
* @response.representation.200.doc The portrait as image
* @response.representation.404.doc The identity or the portrait not found
* @param identityKey The identity key of the user being searched
* @return The image
*/
@HEAD
@Path("{identityKey}/portrait/{size}")
@Produces({"image/jpeg","image/jpg",MediaType.APPLICATION_OCTET_STREAM})
public Response getOriginalPortraitHead(@PathParam("identityKey") Long identityKey, @PathParam("size") String size) {
try {
IdentityShort identity = BaseSecurityManager.getInstance().loadIdentityShortByKey(identityKey);
if(identity == null) {
return Response.serverError().status(Status.NOT_FOUND).build();
}
DisplayPortraitManager portraitManager = DisplayPortraitManager.getInstance();
File portrait = null;
if("master".equals(size)) {
portrait = portraitManager.getMasterPortrait(identity.getName());
} else if("big".equals(size)) {
portrait = portraitManager.getBigPortrait(identity.getName());
} else if("small".equals(size)) {
portrait = portraitManager.getSmallPortrait(identity.getName());
}
if(portrait == null || !portrait.exists()) {
return Response.serverError().status(Status.NOT_FOUND).build();
}
Date lastModified = new Date(portrait.lastModified());
return Response.ok().lastModified(lastModified).build();
} catch (Throwable e) {
throw new WebApplicationException(e);
}
}
/**
* Retrieves the portrait of an user
* @response.representation.200.mediaType application/octet-stream
......
......@@ -207,6 +207,8 @@
border-radius: 0px;
margin: auto;
display:block;
max-width: 100px;
max-height:100px;
}
}
......
This diff is collapsed.
source diff could not be displayed: it is too large. Options to address this: view the blob.
source diff could not be displayed: it is too large. Options to address this: view the blob.
This diff is collapsed.
source diff could not be displayed: it is too large. Options to address this: view the blob.
source diff could not be displayed: it is too large. Options to address this: view the blob.
......@@ -1616,6 +1616,55 @@ public class UserMgmtTest extends OlatJerseyTestCase {
EntityUtils.consume(headNoResponse.getEntity());
}
/**
* Check the 3 sizes
* @throws IOException
* @throws URISyntaxException
*/
@Test
public void testPortrait_HEAD_sizes() throws IOException, URISyntaxException {
Identity id = JunitTestHelper.createAndPersistIdentityAsRndUser("portrait-3");
URL portraitUrl = UserMgmtTest.class.getResource("portrait.jpg");
Assert.assertNotNull(portraitUrl);
File portrait = new File(portraitUrl.toURI());
RestConnection conn = new RestConnection();
Assert.assertTrue(conn.login(id.getName(), "A6B7C8"));
//upload portrait
URI request = UriBuilder.fromUri(getContextURI())
.path("users").path(id.getKey().toString()).path("portrait").build();
HttpPost method = conn.createPost(request, MediaType.APPLICATION_JSON);
conn.addMultipart(method, "portrait.jpg", portrait);
HttpResponse response = conn.execute(method);
assertEquals(200, response.getStatusLine().getStatusCode());
EntityUtils.consume(response.getEntity());
//check 200
URI headMasterRequest = UriBuilder.fromUri(getContextURI())
.path("users").path(id.getKey().toString()).path("portrait").path("master").build();
HttpHead headMasterMethod = conn.createHead(headMasterRequest, MediaType.APPLICATION_OCTET_STREAM, true);
HttpResponse headMasterResponse = conn.execute(headMasterMethod);
assertEquals(200, headMasterResponse.getStatusLine().getStatusCode());
EntityUtils.consume(headMasterResponse.getEntity());
//check 200
URI headBigRequest = UriBuilder.fromUri(getContextURI())
.path("users").path(id.getKey().toString()).path("portrait").path("big").build();
HttpHead headBigMethod = conn.createHead(headBigRequest, MediaType.APPLICATION_OCTET_STREAM, true);
HttpResponse headBigResponse = conn.execute(headBigMethod);
assertEquals(200, headBigResponse.getStatusLine().getStatusCode());
EntityUtils.consume(headBigResponse.getEntity());
//check 200
URI headSmallRequest = UriBuilder.fromUri(getContextURI())
.path("users").path(id.getKey().toString()).path("portrait").path("small").build();
HttpHead headSmallMethod = conn.createHead(headSmallRequest, MediaType.APPLICATION_OCTET_STREAM, true);
HttpResponse headSmallResponse = conn.execute(headSmallMethod);
assertEquals(200, headSmallResponse.getStatusLine().getStatusCode());
EntityUtils.consume(headSmallResponse.getEntity());
}
protected List<UserVO> parseUserArray(InputStream body) {
try {
ObjectMapper mapper = new ObjectMapper(jsonFactory);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment