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'}}
+
+ {{#each userProperties}}
+ - {{@key}}: {{this}}
+ {{/each}}
+
+
+ {{/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,