diff --git c/api/src/java/org/sakaiproject/profile2/logic/SakaiProxy.java w/api/src/java/org/sakaiproject/profile2/logic/SakaiProxy.java index e130710..366d00e 100644 --- c/api/src/java/org/sakaiproject/profile2/logic/SakaiProxy.java +++ w/api/src/java/org/sakaiproject/profile2/logic/SakaiProxy.java @@ -834,7 +834,19 @@ public interface SakaiProxy { * *

This setting controls the display of the profile status section. * - * @return true or false. + * @return true or false. */ public boolean isProfileStatusEnabled(); + + /** + * Is the profile2.import.images flag set in sakai.properties? + * If not set, defaults to false + * + *

If enabled then at startup profile 2 will attempt to download any profile URLs set + * and upload them as profile images. + *

+ * + * @return true or false + */ + public boolean isProfileImageImportEnabled(); } diff --git c/api/src/java/org/sakaiproject/profile2/util/ProfileConstants.java w/api/src/java/org/sakaiproject/profile2/util/ProfileConstants.java index 2e3d398..c9868c8 100644 --- c/api/src/java/org/sakaiproject/profile2/util/ProfileConstants.java +++ w/api/src/java/org/sakaiproject/profile2/util/ProfileConstants.java @@ -211,6 +211,7 @@ public class ProfileConstants { public static final boolean SAKAI_PROP_PROFILE2_IMPORT_ENABLED = false; //profile2.import public static final boolean SAKAI_PROP_PROFILE2_PROFILE_FIELDS_ENABLED = true; //profile2.profile.fields.enabled public static final boolean SAKAI_PROP_PROFILE2_PROFILE_STATUS_ENABLED = true; //profile2.profile.status.enabled + public static final boolean SAKAI_PROP_PROFILE2_IMPORT_IMAGES_ENABLED = false; // profile2.import.images @@ -398,6 +399,7 @@ public class ProfileConstants { */ public static final String GOOGLE_REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob"; public static final String GOOGLE_DOCS_SCOPE = "https://docs.google.com/feeds/"; + } diff --git c/impl/src/java/org/sakaiproject/profile2/conversion/ProfileConverter.java w/impl/src/java/org/sakaiproject/profile2/conversion/ProfileConverter.java index 580fcad..79d8f84 100644 --- c/impl/src/java/org/sakaiproject/profile2/conversion/ProfileConverter.java +++ w/impl/src/java/org/sakaiproject/profile2/conversion/ProfileConverter.java @@ -2,6 +2,11 @@ package org.sakaiproject.profile2.conversion; import java.io.FileNotFoundException; import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -13,12 +18,10 @@ import lombok.Getter; import lombok.Setter; import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.builder.ReflectionToStringBuilder; import org.apache.log4j.Logger; import org.sakaiproject.api.common.edu.person.SakaiPerson; import org.sakaiproject.authz.api.SecurityAdvisor; import org.sakaiproject.authz.api.SecurityService; -import org.sakaiproject.authz.api.SecurityAdvisor.SecurityAdvice; import org.sakaiproject.profile2.dao.ProfileDao; import org.sakaiproject.profile2.exception.ProfileNotDefinedException; import org.sakaiproject.profile2.hbm.model.ProfileImageExternal; @@ -84,10 +87,6 @@ public class ProfileConverter { for(Iterator i = allUsers.iterator(); i.hasNext();) { String userUuid = (String)i.next(); - // - // Process iamge bytes - // - //get image record from dao directly, we don't need privacy/prefs here ProfileImageUploaded uploadedProfileImage = dao.getCurrentProfileImageRecord(userUuid); @@ -173,6 +172,115 @@ public class ProfileConverter { } /** + * This imports URL profile images into upload profile images. + */ + public void importProfileImages() { + + //get list of users + List allUsers = new ArrayList(dao.getAllSakaiPersonIds()); + + if(allUsers.isEmpty()){ + log.warn("Profile2 image converter: No SakaiPersons to process. Nothing to do!"); + return; + } + + //for each, do they have a profile image record. if so, skip (perhaps null the SakaiPerson JPEG_PHOTO bytes?) + for(Iterator i = allUsers.iterator(); i.hasNext();) { + String userUuid = i.next(); + + //get image record from dao directly, we don't need privacy/prefs here + ProfileImageUploaded uploadedProfileImage = dao.getCurrentProfileImageRecord(userUuid); + + ci = new ConvertedImage(); + ci.setUserUuid(userUuid); + + //if no record, we need to run all conversions + if(uploadedProfileImage == null) { + + //main + ProfileImageExternal externalProfileImage = dao.getExternalImageRecordForUser(userUuid); + if (externalProfileImage == null) { + log.info("No existing external profile images for "+ userUuid); + } else { + String mainUrl = externalProfileImage.getMainUrl(); + if (StringUtils.isNotBlank(mainUrl)) { + retrieveMainImage(userUuid, mainUrl); + } else { + log.info("No URL set for "+ userUuid); + } + } + + if(StringUtils.isNotBlank(ci.getMainResourceId())) { + //thumbnail + generateAndPersistThumbnail(); + //avatar + generateAndPersistAvatar(); + } + } + + //save image resource IDs + if(ci.isNeedsSaving()){ + ProfileImageUploaded convertedProfileImage = new ProfileImageUploaded(userUuid, ci.getMainResourceId(), ci.getThumbnailResourceId(), ci.getAvatarResourceId(), true); + + if(dao.addNewProfileImage(convertedProfileImage)){ + log.info("Profile2 image converter: Binary image converted and saved for " + userUuid); + } else { + log.warn("Profile2 image converter: Binary image conversion failed for " + userUuid); + } + } + } + } + + private void retrieveMainImage(String userUuid, String mainUrl) { + InputStream inputStream = null; + try { + URL url = new URL(mainUrl); + HttpURLConnection openConnection = (HttpURLConnection) url.openConnection(); + openConnection.setReadTimeout(5000); + openConnection.setConnectTimeout(5000); + openConnection.setInstanceFollowRedirects(true); + openConnection.connect(); + int responseCode = openConnection.getResponseCode(); + if (responseCode == HttpURLConnection.HTTP_OK) { + + String mimeType = openConnection.getContentType(); + inputStream = openConnection.getInputStream(); + // Convert the image. + byte[] imageMain = ProfileUtils.scaleImage(inputStream, ProfileConstants.MAX_IMAGE_XY, mimeType); + + //create resource ID + String mainResourceId = sakaiProxy.getProfileImageResourcePath(userUuid, ProfileConstants.PROFILE_IMAGE_MAIN); + log.info("Profile2 image converter: mainResourceId: " + mainResourceId); + + //save, if error, log and return. + if (!sakaiProxy.saveFile(mainResourceId, userUuid, DEFAULT_FILE_NAME, mimeType, imageMain)) { + log.error("Profile2 image importer: Saving main profile image failed."); + } else { + ci.setImage(imageMain); + ci.setMimeType(mimeType); + ci.setFileName(DEFAULT_FILE_NAME); + ci.setMainResourceId(mainResourceId); + ci.setNeedsSaving(true); + } + } else { + log.warn("Failed to get good response for user "+ userUuid+ " for "+ mainUrl+ " got "+ responseCode); + } + } catch (MalformedURLException e) { + log.info ("Invalid URL for user: "+ userUuid+ " of: "+ mainUrl); + } catch (IOException e) { + log.warn("Failed to download image for: "+ userUuid+ " from: "+ mainUrl+ " error of: "+ e.getMessage()); + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException ioe) { + log.info("Failed to close input stream for request to: "+ mainUrl); + } + } + } + } + + /** * Import profiles from the given CSV file * *

The CSV file may contain any of the following headings, in any order: diff --git c/impl/src/java/org/sakaiproject/profile2/logic/ProfileLogicImpl.java w/impl/src/java/org/sakaiproject/profile2/logic/ProfileLogicImpl.java index 18e7f10..cb63533 100644 --- c/impl/src/java/org/sakaiproject/profile2/logic/ProfileLogicImpl.java +++ w/impl/src/java/org/sakaiproject/profile2/logic/ProfileLogicImpl.java @@ -377,6 +377,15 @@ public class ProfileLogicImpl implements ProfileLogic { converter.convertProfileImages(); } + // Should we import profile image URLs to be uploaded profile images? + if (sakaiProxy.isProfileImageImportEnabled()) { + if (sakaiProxy.getProfilePictureType() != ProfileConstants.PICTURE_SETTING_UPLOAD) { + log.warn("I'm set to import images but profile2.picture.type=upload is not set. Not importing."); + } else { + converter.importProfileImages(); + } + } + //do we need to import profiles? if(sakaiProxy.isProfileImportEnabled()) { diff --git c/impl/src/java/org/sakaiproject/profile2/logic/SakaiProxyImpl.java w/impl/src/java/org/sakaiproject/profile2/logic/SakaiProxyImpl.java index 5751301..ee2054d 100644 --- c/impl/src/java/org/sakaiproject/profile2/logic/SakaiProxyImpl.java +++ w/impl/src/java/org/sakaiproject/profile2/logic/SakaiProxyImpl.java @@ -980,6 +980,15 @@ public class SakaiProxyImpl implements SakaiProxy { return serverConfigurationService.getBoolean("profile2.convert", ProfileConstants.SAKAI_PROP_PROFILE2_CONVERSION_ENABLED); } + + /** + * {@inheritDoc} + */ + public boolean isProfileImageImportEnabled() { + return serverConfigurationService.getBoolean("profile2.import.images", ProfileConstants.SAKAI_PROP_PROFILE2_IMPORT_IMAGES_ENABLED); + } + + /** * {@inheritDoc} */ diff --git c/pom.xml w/pom.xml index df175ba..56832ee 100644 --- c/pom.xml +++ w/pom.xml @@ -326,7 +326,7 @@ pack tool assembly - deploy + bundle diff --git c/util/src/java/org/sakaiproject/profile2/util/ProfileUtils.java w/util/src/java/org/sakaiproject/profile2/util/ProfileUtils.java index e0591db..2a06416 100644 --- c/util/src/java/org/sakaiproject/profile2/util/ProfileUtils.java +++ w/util/src/java/org/sakaiproject/profile2/util/ProfileUtils.java @@ -105,19 +105,36 @@ public class ProfileUtils { return "jpg"; } + + public static byte[] scaleImage(byte[] imageData, int maxSize, String mimeType) { + InputStream in = null; + try { + in = new ByteArrayInputStream(imageData); + return scaleImage(in, maxSize, mimeType); + + } finally { + if (in != null) { + try { + in.close(); + log.debug("Image stream closed."); + } + catch (IOException e) { + log.error("Error closing image stream: ", e); + } + } + } + } /** * Scale an image so it is fit within a give width and height, whilst maintaining its original proportions * * @param imageData bytes of the original image * @param maxSize maximum dimension in px */ - public static byte[] scaleImage(byte[] imageData, int maxSize, String mimeType) { + public static byte[] scaleImage(InputStream in, int maxSize, String mimeType) { - InputStream in = null; byte[] scaledImageBytes = null; try { //convert original image to inputstream - in = new ByteArrayInputStream(imageData); //original buffered image BufferedImage originalImage = ImageIO.read(in); @@ -136,19 +153,6 @@ public class ProfileUtils { log.error("Scaling image failed.", e); } - - finally { - if (in != null) { - try { - in.close(); - log.debug("Image stream closed."); - } - catch (IOException e) { - log.error("Error closing image stream: ", e); - } - } - } - return scaledImageBytes; }