diff --git a/config/configuration/bundles/src/bundle/org/sakaiproject/config/bundle/default.sakai.properties b/config/configuration/bundles/src/bundle/org/sakaiproject/config/bundle/default.sakai.properties index 13a406f63a8a..1145aeaf2476 100644 --- a/config/configuration/bundles/src/bundle/org/sakaiproject/config/bundle/default.sakai.properties +++ b/config/configuration/bundles/src/bundle/org/sakaiproject/config/bundle/default.sakai.properties @@ -2806,6 +2806,10 @@ # DEFAULT: false # roster.showVisits=true +# Include user properties in roster +# DEFAULT: true +# roster_view_user_properties=false + ## SEARCH # Elastic search is the default search as of Sakai 10 # For more information please see this confluence page diff --git a/library/src/morpheus-master/sass/modules/tool/roster/_roster.scss b/library/src/morpheus-master/sass/modules/tool/roster/_roster.scss index 3db1f7faf766..fcd56689246e 100644 --- a/library/src/morpheus-master/sass/modules/tool/roster/_roster.scss +++ b/library/src/morpheus-master/sass/modules/tool/roster/_roster.scss @@ -103,6 +103,10 @@ font-weight: bold; padding-right: 10px; } + + .roster-user-properties { + list-style: none; + } } .roster-last-visit-time-cell { font-size: 0.9em; diff --git a/roster2/src/handlebars/enrollment_status.handlebars b/roster2/src/handlebars/enrollment_status.handlebars index 13de68bd27d9..c2b143204a18 100644 --- a/roster2/src/handlebars/enrollment_status.handlebars +++ b/roster2/src/handlebars/enrollment_status.handlebars @@ -8,6 +8,9 @@ {{#if viewEmail}} {{translate 'facet_email'}} {{/if}} + {{#if viewUserProperty}} + {{translate 'facet_userProperties'}} + {{/if}} {{translate 'facet_status'}} {{translate 'facet_credits'}} @@ -22,6 +25,9 @@ {{#if viewEmail}} {{/if}} + {{#if viewUserProperty}} + + {{/if}} @@ -48,6 +54,11 @@ {{#if ../../viewEmail}} {{email}} {{/if}} + {{#if ../../viewUserProperty}} + {{#each userProperties}} +
  • {{@key}}: {{this}}
  • + {{/each}} + {{/if}} {{enrollmentStatus}} {{credits}} diff --git a/roster2/src/handlebars/members.handlebars b/roster2/src/handlebars/members.handlebars index 7d1f4d307b0d..f1dc1caf68e8 100644 --- a/roster2/src/handlebars/members.handlebars +++ b/roster2/src/handlebars/members.handlebars @@ -90,5 +90,17 @@ {{email}} {{/if}} + + {{#if ../viewUserProperty}} +
    +
    {{translate 'facet_userProperties'}}
    + +
    + {{/if}} + {{/each}} diff --git a/roster2/src/handlebars/members_header.handlebars b/roster2/src/handlebars/members_header.handlebars index 146867a7d15b..f2f4d026fc3f 100644 --- a/roster2/src/handlebars/members_header.handlebars +++ b/roster2/src/handlebars/members_header.handlebars @@ -28,6 +28,9 @@ {{#if viewEmail}}
    {{translate 'facet_email'}}
    {{/if}} + {{#if viewUserProperty}} +
    {{translate 'facet_userProperties'}}
    + {{/if}}
    diff --git a/roster2/src/handlebars/permissions.handlebars b/roster2/src/handlebars/permissions.handlebars index 3a7d0b563d74..421ca460e8b7 100644 --- a/roster2/src/handlebars/permissions.handlebars +++ b/roster2/src/handlebars/permissions.handlebars @@ -46,6 +46,12 @@ {{/each}} + + {{translate 'permissions_user_properties'}} + {{#each roles}} + + {{/each}} + {{translate 'permissions_photo'}} {{#each roles}} diff --git a/roster2/src/i18n/org/sakaiproject/roster/i18n/ui.properties b/roster2/src/i18n/org/sakaiproject/roster/i18n/ui.properties index b88014c482c9..5d5049e2914f 100644 --- a/roster2/src/i18n/org/sakaiproject/roster/i18n/ui.properties +++ b/roster2/src/i18n/org/sakaiproject/roster/i18n/ui.properties @@ -64,6 +64,7 @@ profile_picture_alt = Photo of facet_picture = Picture facet_name = Name facet_userId = User ID +facet_userProperties = User Properties facet_email = Email facet_role = Role facet_status = Status @@ -93,9 +94,10 @@ permissions_header = Permission permissions_allmembers = View all participants permissions_hidden = View hidden participants permissions_groups = View groups -permissions_enrollment = View participants' enrollment status -permissions_profile = View participants' profile -permissions_email = View participants' email -permissions_photo = View participants' official photo -permissions_visits = View participants' site visits +permissions_enrollment = View participant's enrollment status +permissions_profile = View participant's profile +permissions_email = View participant's email +permissions_photo = View participant's official photo +permissions_visits = View participant's site visits permissions_export = Export roster +permissions_user_properties= View participant's properties \ No newline at end of file diff --git a/roster2/src/java/org/sakaiproject/roster/api/RosterFunctions.java b/roster2/src/java/org/sakaiproject/roster/api/RosterFunctions.java index d582fbe7a74c..0c91fe79bc55 100644 --- a/roster2/src/java/org/sakaiproject/roster/api/RosterFunctions.java +++ b/roster2/src/java/org/sakaiproject/roster/api/RosterFunctions.java @@ -58,5 +58,6 @@ public interface RosterFunctions { public static final String ROSTER_FUNCTION_VIEWEMAIL = ROSTER_FUNCTION_PREFIX + "viewemail"; public static final String ROSTER_FUNCTION_VIEWOFFICIALPHOTO = ROSTER_FUNCTION_PREFIX + "viewofficialphoto"; public static final String ROSTER_FUNCTION_VIEWSITEVISITS = ROSTER_FUNCTION_PREFIX + "viewsitevisits"; + public static final String ROSTER_FUNCTION_VIEWUSERPROPERTIES = ROSTER_FUNCTION_PREFIX + "viewuserproperties"; } diff --git a/roster2/src/java/org/sakaiproject/roster/api/RosterMember.java b/roster2/src/java/org/sakaiproject/roster/api/RosterMember.java index b74bd33616c7..050ec814ca2a 100644 --- a/roster2/src/java/org/sakaiproject/roster/api/RosterMember.java +++ b/roster2/src/java/org/sakaiproject/roster/api/RosterMember.java @@ -60,6 +60,8 @@ public class RosterMember { @Getter private Map groups = new HashMap(); @Getter @Setter + private Map userProperties = new HashMap<>(); + @Getter @Setter private int connectionStatus; // connection status to the current user @Getter @Setter private int totalSiteVisits; diff --git a/roster2/src/java/org/sakaiproject/roster/api/SakaiProxy.java b/roster2/src/java/org/sakaiproject/roster/api/SakaiProxy.java index 42c8fc8e46d8..4e03e1b44466 100644 --- a/roster2/src/java/org/sakaiproject/roster/api/SakaiProxy.java +++ b/roster2/src/java/org/sakaiproject/roster/api/SakaiProxy.java @@ -47,6 +47,7 @@ public interface SakaiProxy { public final static Boolean DEFAULT_VIEW_EMAIL = true; public final static Boolean DEFAULT_VIEW_CONNECTIONS = true; public final static Boolean DEFAULT_VIEW_USER_DISPLAY_ID = true; + public final static Boolean DEFAULT_VIEW_USER_PROPERTIES = true; public final static Integer DEFAULT_ROSTER_STATE = 0; /** @@ -144,6 +145,21 @@ public interface SakaiProxy { */ public Boolean getViewUserDisplayId(); + /** + * Returns the value of the roster_view_user_properties Sakai property. + * + * @return the value of the roster_view_user_properties Sakai property. + */ + public Boolean getViewUserProperty(); + + /** + * Returns the value of the roster_view_user_properties Sakai property. + * + * @param siteId a site + * @return the value of the roster_view_user_properties Sakai property. + */ + public Boolean getViewUserProperty(String siteId); + /** * Returns the value of the roster.display.officialPicturesByDefault Sakai property. * diff --git a/roster2/src/java/org/sakaiproject/roster/impl/SakaiProxyImpl.java b/roster2/src/java/org/sakaiproject/roster/impl/SakaiProxyImpl.java index fb13fb9219a2..455651b6f7bd 100644 --- a/roster2/src/java/org/sakaiproject/roster/impl/SakaiProxyImpl.java +++ b/roster2/src/java/org/sakaiproject/roster/impl/SakaiProxyImpl.java @@ -22,6 +22,7 @@ import java.util.*; import org.apache.commons.lang.ArrayUtils; +import org.sakaiproject.entity.api.serialize.SerializablePropertiesAccess; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sakaiproject.api.privacy.PrivacyManager; @@ -133,6 +134,10 @@ public void init() { functionManager.registerFunction(RosterFunctions.ROSTER_FUNCTION_VIEWSITEVISITS, true); } + if (!registered.contains(RosterFunctions.ROSTER_FUNCTION_VIEWUSERPROPERTIES)) { + functionManager.registerFunction(RosterFunctions.ROSTER_FUNCTION_VIEWUSERPROPERTIES, true); + } + eventTrackingService.addObserver(this); memberComparator = new RosterMemberComparator(getFirstNameLastName()); @@ -285,6 +290,19 @@ public Boolean getViewUserDisplayId() { "roster.display.userDisplayId", DEFAULT_VIEW_USER_DISPLAY_ID); } + @Override + public Boolean getViewUserProperty() { + return getViewUserProperty(getCurrentSiteId()); + } + + @Override + public Boolean getViewUserProperty(String siteId) { + if(serverConfigurationService.getBoolean("roster_view_user_properties", DEFAULT_VIEW_USER_PROPERTIES)) { + return hasUserSitePermission(getCurrentUserId(), RosterFunctions.ROSTER_FUNCTION_VIEWUSERPROPERTIES, siteId); + } + return false; + } + /** * {@inheritDoc} */ @@ -599,6 +617,13 @@ private RosterMember getRosterMember(Map userMap, Collection userPropertiesMap = new HashMap<>(); + Map props = ((SerializablePropertiesAccess) user.getProperties()).getSerializableProperties(); + for (String propKey : props.keySet() ) { + userPropertiesMap.put(propKey, (String) props.get(propKey)); + } + rosterMember.setUserProperties(userPropertiesMap); + for (Group group : groups) { if (group.getMember(userId) != null) { rosterMember.addGroup(group.getId(), group.getTitle()); diff --git a/roster2/src/java/org/sakaiproject/roster/tool/RosterTool.java b/roster2/src/java/org/sakaiproject/roster/tool/RosterTool.java index d98e865c8be6..3546874b6d5a 100644 --- a/roster2/src/java/org/sakaiproject/roster/tool/RosterTool.java +++ b/roster2/src/java/org/sakaiproject/roster/tool/RosterTool.java @@ -113,6 +113,7 @@ protected void doGet(HttpServletRequest request, request.setAttribute("firstNameLastName", sakaiProxy.getFirstNameLastName()); request.setAttribute("hideSingleGroupFilter", sakaiProxy.getHideSingleGroupFilter()); request.setAttribute("viewUserDisplayId", sakaiProxy.getViewUserDisplayId()); + request.setAttribute("viewUserProperty", sakaiProxy.getViewUserProperty()); request.setAttribute("officialPicturesByDefault", sakaiProxy.getOfficialPicturesByDefault()); request.setAttribute("viewEmail", sakaiProxy.getViewEmail()); request.setAttribute("superUser", sakaiProxy.isSuperUser()); diff --git a/roster2/src/java/org/sakaiproject/roster/tool/entityprovider/RosterPOIEntityProvider.java b/roster2/src/java/org/sakaiproject/roster/tool/entityprovider/RosterPOIEntityProvider.java index c3b8f0777b8f..b3fa5163d947 100644 --- a/roster2/src/java/org/sakaiproject/roster/tool/entityprovider/RosterPOIEntityProvider.java +++ b/roster2/src/java/org/sakaiproject/roster/tool/entityprovider/RosterPOIEntityProvider.java @@ -28,6 +28,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import javax.servlet.http.HttpServletResponse; @@ -389,6 +390,11 @@ private void addOverviewRows(final List> dataInRows, row.add(member.getEmail()); } + if (this.sakaiProxy.getViewUserProperty(siteId)) { + List props = member.getUserProperties().entrySet().stream().map(e -> e.getKey() + ":" + e.getValue()).collect(Collectors.toList()); + row.add(String.join(",", props)); + } + row.add(member.getRole()); if (this.sakaiProxy.hasUserSitePermission(userId, RosterFunctions.ROSTER_FUNCTION_VIEWGROUP, siteId)) { @@ -443,6 +449,11 @@ private void addEnrollmentStatusRows(final List> dataInRows, row.add(member.getEmail()); } + if (this.sakaiProxy.getViewUserProperty(siteId)) { + List props = member.getUserProperties().entrySet().stream().sorted(Map.Entry.comparingByKey()).map(e -> e.getKey() + ":" + e.getValue()).collect(Collectors.toList()); + row.add(String.join(",", props)); + } + row.add(member.getEnrollmentStatusText()); row.add(member.getCredits()); @@ -507,10 +518,15 @@ private List createColumnHeader(final String viewType, final String site if (this.sakaiProxy.getViewUserDisplayId()) { header.add(rl.getString("facet_userId")); } + if (this.sakaiProxy.getViewEmail(siteId)) { header.add(rl.getString("facet_email")); } + if (this.sakaiProxy.getViewUserProperty(siteId)) { + header.add(rl.getString("facet_userProperties")); + } + if (VIEW_OVERVIEW.equals(viewType)) { header.add(rl.getString("facet_role")); } else if (VIEW_ENROLLMENT_STATUS.equals(viewType)) { diff --git a/roster2/src/webapp/WEB-INF/bootstrap.jsp b/roster2/src/webapp/WEB-INF/bootstrap.jsp index 61e1523c7ff4..ec0a9ff994e3 100644 --- a/roster2/src/webapp/WEB-INF/bootstrap.jsp +++ b/roster2/src/webapp/WEB-INF/bootstrap.jsp @@ -37,6 +37,7 @@ firstNameLastName: ${firstNameLastName}, hideSingleGroupFilter: ${hideSingleGroupFilter}, viewUserDisplayId: ${viewUserDisplayId}, + viewUserProperty: ${viewUserProperty}, //officialPicturesByDefault: ${officialPicturesByDefault}, officialPictureMode: ${officialPicturesByDefault}, viewEmail: ${viewEmail}, diff --git a/roster2/src/webapp/js/roster.js b/roster2/src/webapp/js/roster.js index 5940255dd2c8..e63a850efba1 100644 --- a/roster2/src/webapp/js/roster.js +++ b/roster2/src/webapp/js/roster.js @@ -328,13 +328,14 @@ roster.render('members_header', { viewEmail: roster.viewEmail, viewUserDisplayId: roster.viewUserDisplayId, + viewUserProperty: roster.viewUserProperty, viewProfile: roster.currentUserPermissions.viewProfile, viewGroup : roster.currentUserPermissions.viewGroup, viewPicture: true, viewSiteVisits: roster.currentUserPermissions.viewSiteVisits, viewConnections: ((undefined != window.friendStatus) && roster.viewConnections), enrollmentsMode: enrollmentsMode, - showVisits: roster.showVisits + showVisits: roster.showVisits, }, 'roster-members-content'); } @@ -562,6 +563,7 @@ firstNameLastName: roster.firstNameLastName, viewEmail: roster.viewEmail, viewUserDisplayId: roster.viewUserDisplayId, + viewUserProperty: roster.viewUserProperty, viewProfile: roster.currentUserPermissions.viewProfile, viewGroup : roster.currentUserPermissions.viewGroup, viewPicture: true,