Index: site-manage-impl/impl/src/java/org/sakaiproject/sitemanage/impl/ETSUserNotificationProviderImpl.java =================================================================== --- site-manage-impl/impl/src/java/org/sakaiproject/sitemanage/impl/ETSUserNotificationProviderImpl.java (revision 133106) +++ site-manage-impl/impl/src/java/org/sakaiproject/sitemanage/impl/ETSUserNotificationProviderImpl.java (working copy) @@ -61,8 +61,11 @@ private EmailService emailService; + // sfoster9@uwo.ca, bjones86@uwo.ca + // email template sent during site join; see JoinSiteDelegate class in kernel + private static final String JOIN_EMAIL_TEMPLATE_FILE_NAME = "joinNotification.xml"; + private static final String JOIN_EMAIL_TEMPLATE_KEY = "sitemanage.joinNotification"; // The name (key) of the email template - public void setEmailService(EmailService es) { emailService = es; } @@ -117,6 +120,10 @@ loadTemplate("notifySiteCreation.xml", NOTIFY_SITE_CREATION); loadTemplate("notifySiteCreationConfirmation.xml", NOTIFY_SITE_CREATION_CONFIRMATION); + // sfoster9@uwo.ca, bjones86@uwo.ca + // email sent during site join; see JoinSiteDelegate class in kernel + loadTemplate(JOIN_EMAIL_TEMPLATE_FILE_NAME, JOIN_EMAIL_TEMPLATE_KEY); + } public void notifyAddedParticipant(boolean newNonOfficialAccount, Index: site-manage-impl/impl/src/bundle/joinNotification.xml =================================================================== --- site-manage-impl/impl/src/bundle/joinNotification.xml (revision 0) +++ site-manage-impl/impl/src/bundle/joinNotification.xml (revision 0) @@ -0,0 +1,25 @@ + + + +${localSakaiName} Join Notification: Someone has joined your "${worksiteName}" site! +${currentUserFirstName} ${currentUserLastName} (${currentUserDisplayId}) has joined your "${worksiteName}" site on ${localSakaiName}. +You will now see ${currentUserDisplayName} listed in your site's Site Info participant list. + +If you would like to turn Join Notifications off for this site, please: + + 1) Log into ${localSakaiName} + 2) Navigate to ${worksiteName} + 3) Click 'Site Info' from the list of tools on the left hand side + 4) Click 'Manage Access' from the menu bar at the top + 5) Uncheck 'Notify site maintainers by email when a user joins this site' + 6) Click the 'Update' button + +Regards, + +The ${localSakaiName} Administrators +${institution} + +en +1 + + Index: site-manage-tool/tool/src/java/org/sakaiproject/site/tool/SiteAction.java =================================================================== --- site-manage-tool/tool/src/java/org/sakaiproject/site/tool/SiteAction.java (revision 133106) +++ site-manage-tool/tool/src/java/org/sakaiproject/site/tool/SiteAction.java (working copy) @@ -1146,6 +1146,9 @@ // lti tools state.removeAttribute(STATE_LTITOOL_EXISTING_SELECTED_LIST); state.removeAttribute(STATE_LTITOOL_SELECTED_LIST); + + // bjones86 - SAK-24423 - remove joinable site settings from the state + JoinableSiteSettings.removeJoinableSiteSettingsFromState( state ); } // cleanState @@ -1760,6 +1763,9 @@ context.put("published", Boolean.valueOf(siteInfo.published)); context.put("joinable", Boolean.valueOf(siteInfo.joinable)); context.put("joinerRole", siteInfo.joinerRole); + + // bjones86 - SAK-24423 - add joinable site settings to context + JoinableSiteSettings.addJoinableSiteSettingsToNewSiteConfirmContext( context, siteInfo ); context.put("importSiteTools", state .getAttribute(STATE_IMPORT_SITE_TOOL)); @@ -2501,6 +2507,10 @@ context.put("shoppingPeriodInstructorEditable", ServerConfigurationService.getBoolean("delegatedaccess.shopping.instructorEditable", false)); context.put("viewDelegatedAccessUsers", ServerConfigurationService.getBoolean("delegatedaccess.siteaccess.instructorViewable", false)); + + // bjones86 - SAK-24423 - add joinable site settings to context + JoinableSiteSettings.addJoinableSiteSettingsToEditAccessContextWhenSiteIsNotNull( context, state, site, !unJoinableSiteTypes.contains( siteType ) ); + if (siteType != null && !unJoinableSiteTypes.contains(siteType)) { // site can be set as joinable context.put("disableJoinable", Boolean.FALSE); @@ -2549,6 +2559,9 @@ context.put("disableJoinable", Boolean.TRUE); } + // bjones86 - SAK-24423 - add joinable site settings to context + JoinableSiteSettings.addJoinableSiteSettingsToEditAccessContextWhenSiteIsNull( context, siteInfo, true ); + // the template site, if using one Site templateSite = (Site) state.getAttribute(STATE_TEMPLATE_SITE); @@ -4953,6 +4966,9 @@ siteInfo.joinerRole = templateSite.getJoinerRole(); //siteInfo.include = false; + // bjones86 - SAK-24423 - update site info for joinable site settings + JoinableSiteSettings.updateSiteInfoFromSitePropertiesOnSelectTemplate( templateSite.getProperties(), siteInfo ); + List toolIdsSelected = new Vector(); List pageList = templateSite.getPages(); if (!((pageList == null) || (pageList.size() == 0))) { @@ -7337,6 +7353,9 @@ } } state.setAttribute(STATE_JOINERROLE, joinerRole); + + // bjones86 - SAK-24423 - update state for joinable site settings + JoinableSiteSettings.updateStateFromSitePropertiesOnEditAccessOrNewSite( site.getProperties(), state ); } catch (Exception e) { @@ -8136,6 +8155,9 @@ readInputAndUpdateStateVariable(state, params, "joinable", STATE_JOINABLE, true); readInputAndUpdateStateVariable(state, params, "joinerRole", STATE_JOINERROLE, false); + // bjones86 - SAK-24423 - get all joinable site settings from the form input + JoinableSiteSettings.getAllFormInputs( state, params ); + boolean publishUnpublish = state.getAttribute(STATE_SITE_ACCESS_PUBLISH) != null ? ((Boolean) state.getAttribute(STATE_SITE_ACCESS_PUBLISH)).booleanValue() : false; boolean include = state.getAttribute(STATE_SITE_ACCESS_INCLUDE) != null ? ((Boolean) state.getAttribute(STATE_SITE_ACCESS_INCLUDE)).booleanValue() : false; @@ -8171,6 +8193,9 @@ state.removeAttribute(STATE_JOINABLE); state.removeAttribute(STATE_JOINERROLE); + + // bjones86 - SAK-24423 - remove joinable site settings from the state + JoinableSiteSettings.removeJoinableSiteSettingsFromState( state ); } } else { // adding new site @@ -8185,6 +8210,10 @@ // joinable site or not boolean joinable = state.getAttribute(STATE_JOINABLE) != null ? ((Boolean) state.getAttribute(STATE_JOINABLE)).booleanValue() : null; + + // bjones86 - SAK-24423 - update site info for joinable site settings + JoinableSiteSettings.updateSiteInfoFromStateOnSiteUpdate( state, siteInfo, joinable ); + if (joinable) { siteInfo.joinable = true; String joinerRole = state.getAttribute(STATE_JOINERROLE) != null ? (String) state.getAttribute(STATE_JOINERROLE) : null; @@ -8265,6 +8294,10 @@ } else { addAlert(state, rb.getString("java.joinsite") + " "); } + + // bjones86 - SAK-24423 - update site properties for joinable site settings + JoinableSiteSettings.updateSitePropertiesFromStateOnSiteUpdate( sEdit.getPropertiesEdit(), state ); + } else if ( !joinable || (!joinable && ServerConfigurationService.getBoolean(CONVERT_NULL_JOINABLE_TO_UNJOINABLE, true))) { sEdit.setJoinable(false); @@ -9494,6 +9527,9 @@ // set the joiner role String joinerRole = (String) state.getAttribute("form_joinerRole"); s.setJoinerRole(joinerRole); + + // bjones86 - SAK-24423 - update site properties for joinable site settings + JoinableSiteSettings.updateSitePropertiesFromStateOnSiteInfoSaveGlobalAccess( s.getPropertiesEdit(), state ); } if (state.getAttribute(STATE_MESSAGE) == null) { @@ -9538,6 +9574,9 @@ } // Make changes and then put changed site back in state String id = site.getId(); + + // bjones86 - SAK-24423 - update site properties for joinable site settings + JoinableSiteSettings.updateSitePropertiesFromStateOnUpdateSiteAttributes( site, state ); try { SiteService.save(site); @@ -9640,6 +9679,9 @@ siteInfo.published = Boolean .valueOf(params.getString("itemStatus")).booleanValue(); } + + // bjones86 - SAK-24423 - update site info for joinable site settings + JoinableSiteSettings.updateSiteInfoFromParams( params, siteInfo ); // site contact information String name = StringUtils.trimToEmpty(params.getString("siteContactName")); @@ -10919,6 +10961,9 @@ // if the site was created from template rp.addProperty(TEMPLATE_USED, templateSite.getId()); } + + // bjones86 - SAK-24423 - update site properties for joinable site settings + JoinableSiteSettings.updateSitePropertiesFromSiteInfoOnAddNewSite( siteInfo, rp ); state.setAttribute(STATE_SITE_INSTANCE_ID, site.getId()); @@ -11024,6 +11069,9 @@ siteInfo.term = term; } + // bjones86 - SAK-24423 - update site info for joinable site settings + JoinableSiteSettings.updateSiteInfoFromSiteProperties( siteProperties, siteInfo ); + // site contact information String contactName = siteProperties.getProperty(Site.PROP_SITE_CONTACT_NAME); String contactEmail = siteProperties.getProperty(Site.PROP_SITE_CONTACT_EMAIL); @@ -12096,7 +12144,38 @@ public String term = NULL_STRING; // academic term - public ResourceProperties properties = new BaseResourcePropertiesEdit(); + public ResourceProperties properties = new BaseResourcePropertiesEdit(); + + // bjones86 - SAK-24423 - joinable site settings + public String joinerGroup = NULL_STRING; + public String getJoinerGroup() + { + return joinerGroup; + } + + public boolean joinNotifications = false; + public boolean getJoinNotifications() + { + return joinNotifications; + } + + public boolean joinExcludePublic = false; + public boolean getJoinExcludePublic() + { + return joinExcludePublic; + } + + public boolean joinLimitByAccountType = false; + public boolean getJoinLimitByAccountType() + { + return joinLimitByAccountType; + } + + public String joinLimitedAccountTypes = NULL_STRING; + public String getJoinLimitedAccountTypes() + { + return joinLimitedAccountTypes; + } // end joinable site settings public String getSiteId() { return site_id; @@ -13986,6 +14065,9 @@ siteInfo.joinerRole = templateSite.getJoinerRole(); state.setAttribute(STATE_SITE_INFO, siteInfo); + // bjones86 - SAK-24423 - update site info for joinable site settings + JoinableSiteSettings.updateSiteInfoFromParams( params, siteInfo ); + // whether to copy users or site content over? if (params.getBoolean("copyUsers")) state.setAttribute(STATE_TEMPLATE_SITE_COPY_USERS, Boolean.TRUE); else state.removeAttribute(STATE_TEMPLATE_SITE_COPY_USERS); if (params.getBoolean("copyContent")) state.setAttribute(STATE_TEMPLATE_SITE_COPY_CONTENT, Boolean.TRUE); else state.removeAttribute(STATE_TEMPLATE_SITE_COPY_CONTENT); Index: site-manage-tool/tool/src/java/org/sakaiproject/site/tool/SiteBrowserAction.java =================================================================== --- site-manage-tool/tool/src/java/org/sakaiproject/site/tool/SiteBrowserAction.java (revision 133106) +++ site-manage-tool/tool/src/java/org/sakaiproject/site/tool/SiteBrowserAction.java (working copy) @@ -245,8 +245,23 @@ { template = buildVisitContext(state, context); } + + // bjones86 - SAK-24423 - joinable site settings - join from site browser + else if( JoinableSiteSettings.SITE_BROWSER_JOIN_MODE.equalsIgnoreCase( mode ) ) + { + if( JoinableSiteSettings.isJoinFromSiteBrowserEnabled() ) + { + template = JoinableSiteSettings.buildJoinContextForSiteBrowser( state, context, rb ); + } else { + Log.warn( "chef", "SiteBrowserAction: mode = " + mode + ", but site browser join is disabled globally" ); + template = buildListContext( state, context ); + } + } + + else + { Log.warn("chef", "SiteBrowserAction: mode: " + mode); template = buildListContext(state, context); } @@ -288,6 +303,11 @@ List sites = prepPage(state); state.setAttribute(STATE_SITES, sites); context.put("sites", sites); + + // bjones86 - SAK-24423 - joinable site settings - put the necessary info into the context for the list interface + JoinableSiteSettings.putSiteMapInContextForSiteBrowser( context, sites ); + JoinableSiteSettings.putCurrentUserInContextForSiteBrowser( context ); + JoinableSiteSettings.putIsSiteBrowserJoinEnabledInContext( context ); if (state.getAttribute(STATE_NUM_MESSAGES) != null) context.put("allMsgNumber", state.getAttribute(STATE_NUM_MESSAGES).toString()); @@ -558,6 +578,11 @@ } context.put("contentTypeImageService", ContentTypeImageService.getInstance()); + + // bjones86 - SAK-24423 - joinable site settings - put info into the context for the visit UI + JoinableSiteSettings.putIsSiteBrowserJoinEnabledInContext( context ); + JoinableSiteSettings.putIsCurrentUserAlreadyMemberInContextForSiteBrowser( context, siteId ); + JoinableSiteSettings.putIsSiteExcludedFromPublic( context, siteId ); } catch (IdUnusedException err) { @@ -612,6 +637,26 @@ } } // doVisit + + /** + * Handle a request to join a site. + * + * @author bjones86 + * + * @param data + * the state to get the settings from + * @param context + * the object to put the settings into + */ + public void doJoin( RunData data, Context context ) + { + SessionState state = ( (JetspeedRunData) data).getPortletSessionState( ( (JetspeedRunData) data).getJs_peid() ); + String message = JoinableSiteSettings.doJoinForSiteBrowser( state, rb, data.getParameters().getString( "id" ) ); + if( message != null && !message.isEmpty() ) + { + addAlert( state, message ); + } + } // doJoin /** * Handle a request to return to the list. Index: site-manage-tool/tool/src/java/org/sakaiproject/site/tool/MembershipAction.java =================================================================== --- site-manage-tool/tool/src/java/org/sakaiproject/site/tool/MembershipAction.java (revision 133106) +++ site-manage-tool/tool/src/java/org/sakaiproject/site/tool/MembershipAction.java (working copy) @@ -38,6 +38,7 @@ import org.sakaiproject.exception.InUseException; import org.sakaiproject.exception.PermissionException; import org.sakaiproject.javax.PagingPosition; +import org.sakaiproject.site.api.Site; import org.sakaiproject.site.cover.SiteService; import org.sakaiproject.site.util.SiteTextEditUtil; import org.sakaiproject.user.api.User; @@ -98,8 +99,10 @@ else { List openSites = SiteService.getSites(org.sakaiproject.site.api.SiteService.SelectionType.JOINABLE, - // null, null, null, org.sakaiproject.service.legacy.site.SiteService.SortType.TITLE_ASC, null); null, search, null, org.sakaiproject.site.api.SiteService.SortType.TITLE_ASC, null); + + // bjones86 - SAK-24423 - joinable site settings - filter sites + JoinableSiteSettings.filterSitesListForMembership( openSites ); size = openSites.size(); } @@ -153,15 +156,23 @@ if (sortAsc) { - rv = SiteService.getSites(org.sakaiproject.site.api.SiteService.SelectionType.JOINABLE, + List sites = SiteService.getSites(org.sakaiproject.site.api.SiteService.SelectionType.JOINABLE, // null, null, null, org.sakaiproject.service.legacy.site.SiteService.SortType.TITLE_ASC, null); null, search, null, org.sakaiproject.site.api.SiteService.SortType.TITLE_ASC, page); + + // bjones86 - SAK-24423 - filter sites taking into account 'exclude from public list' setting and global toggle + JoinableSiteSettings.filterSitesListForMembership( sites ); + rv = sites; } else { - rv = SiteService.getSites(org.sakaiproject.site.api.SiteService.SelectionType.JOINABLE, - // null, null, null, org.sakaiproject.service.legacy.site.SiteService.SortType.TITLE_DESC, null); + List sites = SiteService.getSites(org.sakaiproject.site.api.SiteService.SelectionType.JOINABLE, + // null, null, null, org.sakaiproject.service.legacy.site.SiteService.SortType.TITLE_ASC, null); null, search, null, org.sakaiproject.site.api.SiteService.SortType.TITLE_DESC, page); + + // bjones86 - SAK-24423 - filter sites taking into account 'exclude from public list' setting and global toggle + JoinableSiteSettings.filterSitesListForMembership( sites ); + rv = sites; } } @@ -401,10 +412,15 @@ { try { - // join the site - SiteService.join(id); - String msg = rb.getString("mb.youhave2") + " " + SiteService.getSite(id).getTitle(); - addAlert(state, msg); + // bjones86 - SAK-24423 - joinable site settings - join the site + if( JoinableSiteSettings.doJoinForMembership( id ) ) + { + addAlert( state, rb.getString( "mb.youhave2" ) + " " + SiteService.getSite( id ).getTitle() ); + } + else + { + addAlert( state, rb.getString( "mb.join.notAllowed" ) ); + } // add to user auditing List userAuditList = new ArrayList(); Index: site-manage-tool/tool/src/java/org/sakaiproject/site/tool/JoinableSiteSettings.java =================================================================== --- site-manage-tool/tool/src/java/org/sakaiproject/site/tool/JoinableSiteSettings.java (revision 0) +++ site-manage-tool/tool/src/java/org/sakaiproject/site/tool/JoinableSiteSettings.java (revision 0) @@ -0,0 +1,1436 @@ +package org.sakaiproject.site.tool; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import java.util.Set; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.sakaiproject.cheftool.Context; +import org.sakaiproject.component.cover.ComponentManager; +import org.sakaiproject.entity.api.ResourceProperties; +import org.sakaiproject.entity.api.ResourcePropertiesEdit; +import org.sakaiproject.entitybroker.DeveloperHelperService; +import org.sakaiproject.event.api.SessionState; +import org.sakaiproject.exception.IdUnusedException; +import org.sakaiproject.exception.InUseException; +import org.sakaiproject.exception.PermissionException; +import org.sakaiproject.site.api.AllowedJoinableAccount; +import org.sakaiproject.site.api.Group; +import org.sakaiproject.site.api.Site; +import org.sakaiproject.site.api.SiteService; +import org.sakaiproject.site.tool.SiteAction.SiteInfo; +import org.sakaiproject.user.api.User; +import org.sakaiproject.user.api.UserDirectoryService; +import org.sakaiproject.util.ParameterParser; +import org.sakaiproject.util.ResourceLoader; + +/** + * This class handles all aspects of joinable site settings within the site-manage tool + * (Site Browser, Membership, Site Info->Edit Access, New Site->Edit Access). + * + * @author bjones86, sfoster9, plukasew + */ +public class JoinableSiteSettings +{ + // Logger + private static Log log = LogFactory.getLog( JoinableSiteSettings.class ); + + // API's + private static UserDirectoryService userDirectoryService = (UserDirectoryService) ComponentManager.get( UserDirectoryService.class ); + private static SiteService siteService = (SiteService) ComponentManager.get( SiteService.class ); + private static DeveloperHelperService developerHelperService = (DeveloperHelperService) ComponentManager.get( DeveloperHelperService.class ); + + // State variable names + private static final String STATE_JOIN_SITE_GROUP_ID = "state_join_site_group"; + private static final String STATE_JOIN_SITE_NOTIFICATION = "state_join_site_notification"; + private static final String STATE_JOIN_SITE_EXCLUDE_PUBLIC_LIST = "state_join_site_exclude_public_list"; + private static final String STATE_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE = "state_join_site_limit_by_account_type"; + private static final String STATE_JOIN_SITE_ACCOUNT_TYPES = "state_join_site_account_types"; + private static final String STATE_JOIN_SITE_ACCOUNT_TYPE_PREFIX = "state_join_site_account_type_"; + private static final String STATE_JOIN_SITE_SITE_BROWSER_SITE_ID = "state_join_site_site_browser_site_id"; + + // Site property names + private static final String SITE_PROP_JOIN_SITE_GROUP_ID = "joinerGroup"; + private static final String SITE_PROP_JOIN_SITE_GROUP_NO_SEL = "noSelection"; + private static final String SITE_PROP_JOIN_SITE_NOTIFACTION = "joinNotification"; + private static final String SITE_PROP_JOIN_SITE_EXCLUDE_PUBLIC_LIST = "joinExcludeFromPublicList"; + private static final String SITE_PROP_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE = "joinLimitByAccountType"; + private static final String SITE_PROP_JOIN_SITE_ACCOUNT_TYPES = "joinLimitedAccountTypes"; + + // Context variable/element names + private static final String CONTEXT_JOIN_SITE_GROUPS = "siteGroups"; + private static final String CONTEXT_JOIN_SITE_GROUP_DROP_DOWN = "selectJoinerGroup"; + private static final String CONTEXT_JOIN_SITE_NOTIFY_CHECKBOX = "chkJoinNotification"; + private static final String CONTEXT_JOIN_SITE_EXCLUDE_PUBLIC_LIST_CHECKBOX = "chkJoinExcludeFromPublicList"; + private static final String CONTEXT_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE_CHECKBOX = "chkJoinLimitByAccountType"; + private static final String CONTEXT_JOIN_SITE_ACCOUNT_TYPES = "joinableAccountTypes"; + private static final String CONTEXT_JOIN_SITE_ACCOUNT_CATEGORIES = "joinableAccountTypeCategories"; + private static final String CONTEXT_JOIN_SITE_ACCOUNT_TYPE_CHECKBOX_PREFIX = "chkJoin-"; + private static final String CONTEXT_JOIN_SITE_GROUP_ENABLED = "joinGroupEnabled"; + private static final String CONTEXT_JOIN_SITE_NOTIFICATION_ENABLED = "joinNotificationEnabled"; + private static final String CONTEXT_JOIN_SITE_EXCLUDE_PUBLIC_LIST_ENABLED = "joinExcludeFromPublicListEnabled"; + private static final String CONTEXT_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE_ENABLED = "joinLimitAccountTypesEnabled"; + private static final String CONTEXT_JOIN_SITE_GROUP_ID = SITE_PROP_JOIN_SITE_GROUP_ID; + private static final String CONTEXT_JOIN_SITE_NOTIFICATION = SITE_PROP_JOIN_SITE_NOTIFACTION; + private static final String CONTEXT_JOIN_SITE_EXCLUDE_PUBLIC_LIST = SITE_PROP_JOIN_SITE_EXCLUDE_PUBLIC_LIST; + private static final String CONTEXT_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE = SITE_PROP_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE; + private static final String CONTEXT_JOIN_SITE_LIMIT_ACCOUNT_TYPES = SITE_PROP_JOIN_SITE_ACCOUNT_TYPES; + private static final String CONTEXT_JOIN_SITE_SITE_MAP = "siteMap"; + private static final String CONTEXT_JOIN_SITE_CURRENT_USER = "currentUser"; + private static final String CONTEXT_JOIN_SITE_ALREADY_MEMBER = "alreadyMember"; + private static final String CONTEXT_JOIN_SITE_MSG = "message"; + private static final String CONTEXT_JOIN_SITE_SUCCESS = "success"; + private static final String CONTEXT_JOIN_SITE_SITE_TITLE = "siteTitle"; + private static final String CONTEXT_JOIN_SITE_LINK = "link"; + private static final String CONTEXT_JOIN_SITE_SITE_BROWSER_JOIN_ENABLED = "siteBrowserJoinEnabled"; + private static final String CONTEXT_JOIN_SITE_GROUP_ENABLED_LOCAL_DISABLED_GLOBAL = "joinGroupEnabledLocalDisabledGlobal"; + private static final String CONTEXT_JOIN_SITE_NOTIFICATION_ENABLED_LOCAL_DISABLED_GLOBAL = "joinNotifyEnabledLocalDisabledGlobal"; + private static final String CONTEXT_JOIN_SITE_EXCLUDE_ENABLED_LOCAL_DISABLED_GLOBAL = "joinExcludeEnabledLocalDisabledGlobal"; + private static final String CONTEXT_JOIN_SITE_LIMIT_ENABLED_LOCAL_DISABLED_GLOBAL = "joinLimitEnabledLocalDisabledGlobal"; + + // Message keys + private static final String MSG_KEY_UNJOINABLE = "join.unjoinable"; + private static final String MSG_KEY_LOGIN = "join.login"; + private static final String MSG_KEY_ALREADY_MEMBER_1 = "join.alreadyMember1"; + private static final String MSG_KEY_ALREADY_MEMBER_2 = "join.alreadyMember2"; + private static final String MSG_KEY_NOT_ALLOWED_TO_JOIN = "join.notAllowed"; + private static final String MSG_KEY_JOIN_SUCCESS = "join.success"; + private static final String MSG_KEY_JOIN_NOT_FOUND = "join.notFound"; + private static final String MSG_KEY_JOIN_FAIL_PERM = "join.failPermission"; + private static final String MSG_KEY_JOIN_FAIL = "join.fail"; + + // Random other things + private static final String CSV_DELIMITER = ","; + private static final String TRUE_STRING = "true"; + private static final String FALSE_STRING = "false"; + private static final String ON_STRING = "on"; + private static final String FORM_PREFIX = "form_"; + private static final String SITE_REF_PREFIX = "/site/"; + private static final String SITE_BROWSER_MODE = "sitebrowser.mode"; + public static final String SITE_BROWSER_JOIN_MODE = "join"; + + /********************************************************************************************** + ********************* SiteBrowserAction Methods (Site Browser tool) ************************** + **********************************************************************************************/ + + /** + * Prepare for the join context. Check all settings first before forwarding to build the context for the join mode + * + * @param state + * the object to get the settings from + * @param rb + * the object used to access internationalized messages + * @param siteID + * the ID of the site in question + * @return a message string indicating a failure of some sort, or an empty string indicating the user is able to join + * the site in question + */ + public static String doJoinForSiteBrowser( SessionState state, ResourceLoader rb, String siteID ) + { + String message = ""; + + try + { + // Get the site and the current user + Site site = siteService.getSite( siteID ); + User currentUser = userDirectoryService.getCurrentUser(); + + // If the site isn't joinable, create the UI alert message + if( !site.isJoinable() ) + { + message = rb.getString( MSG_KEY_UNJOINABLE ); + } + + // If the user isn't logged in, create the UI alert message + else if( currentUser == null || currentUser.getId() == null || "".equalsIgnoreCase( currentUser.getId() ) ) + { + message = rb.getString( MSG_KEY_LOGIN ); + } + + // If the user is already a member of the site, create the UI alert message + else if( siteService.isCurrentUserMemberOfSite( siteID ) ) + { + message = rb.getString( MSG_KEY_ALREADY_MEMBER_1 ); + } + + // If join limitations are toggled, and they're not in the list of allowed joiner roles, create the UI message + else if( siteService.isLimitByAccountTypeEnabled( siteID ) && !siteService.isAllowedToJoin( siteID ) ) + { + message = rb.getString( MSG_KEY_NOT_ALLOWED_TO_JOIN ); + } + + // Otherwise, tell it to build the context for the join mode + else + { + state.setAttribute( STATE_JOIN_SITE_SITE_BROWSER_SITE_ID, siteID ); + state.setAttribute( SITE_BROWSER_MODE, SITE_BROWSER_JOIN_MODE ); + } + } + catch( IdUnusedException ex ) + { + log.error( "doJoinForSiteBrowser()", ex ); + message = rb.getFormattedMessage( MSG_KEY_JOIN_NOT_FOUND, new Object[] { siteID } ); + } + + return message; + } + + /** + * Build the context for the join mode. + * + * @param state + * the object to get the settings from + * @param context + * the parameters being passed to the velocity template + * @param rb + * the object used to access internationalized messages + * @return the string postfix for the chef_sitebrowser_join.vm template + */ + public static String buildJoinContextForSiteBrowser( SessionState state, Context context, ResourceLoader rb ) + { + // Get the site ID from the state + String siteID = (String) state.getAttribute( STATE_JOIN_SITE_SITE_BROWSER_SITE_ID ); + String message = ""; + String siteTitle = ""; + String link = ""; + boolean success = false; + + try + { + // Get the user, site ID, realm ID, the current session and the joiner role + Site site = siteService.getSite( siteID ); + User currentUser = userDirectoryService.getCurrentUser(); + siteTitle = site.getTitle(); + + // If the site isn't joinable, create the UI message + if( !site.isJoinable() ) + { + message = rb.getString( MSG_KEY_UNJOINABLE ); + } + + // If the user isn't logged in, create the UI message + else if( currentUser == null || currentUser.getId() == null || "".equalsIgnoreCase( currentUser.getId() ) ) + { + message = rb.getString( MSG_KEY_LOGIN ); + } + + // If the user is already a member, create the UI message and the link + else if( siteService.isCurrentUserMemberOfSite( siteID ) ) + { + message = rb.getString( MSG_KEY_ALREADY_MEMBER_1 ) + " " + rb.getString( MSG_KEY_ALREADY_MEMBER_2 ); + link = developerHelperService.getLocationReferenceURL( SITE_REF_PREFIX + siteID ); + success = true; + } + + // If join limitations are toggled, and they're not in the list of allowed joiner roles, create the UI message + else if( !siteService.isAllowedToJoin( siteID ) ) + { + message = rb.getString( MSG_KEY_NOT_ALLOWED_TO_JOIN ); + } + + // Otherwise, they're logged in, the site exists, it's joinable and either limit by account types is disabled globally or for the site, + // Or limit by account types is enabled for this site and the user is of one of the correct account types allowed to join + else + { + try + { + // Join the site, log success message, create the UI message and the link + siteService.join( siteID ); + log.info( "Successfully added user '" + currentUser.getEid() + "' to site with ID '" + siteID + "'" ); + message = rb.getFormattedMessage( MSG_KEY_JOIN_SUCCESS, new Object[] { site.getTitle() } ); + link = developerHelperService.getLocationReferenceURL( SITE_REF_PREFIX + siteID ); + success = true; + } + catch( IdUnusedException ex ) + { + log.debug( "buildJoinContextForSiteBrowser()", ex ); + message = rb.getFormattedMessage( MSG_KEY_JOIN_NOT_FOUND, new Object[] { siteID } ); + } + catch( PermissionException ex ) + { + log.debug( "buildJoinContextForSiteBrowser()", ex ); + message = rb.getString( MSG_KEY_JOIN_FAIL_PERM ); + } + catch( IllegalArgumentException ex ) + { + log.debug( "buildJoinContextForSiteBrowser()", ex ); + message = rb.getString( MSG_KEY_JOIN_FAIL ); + } + } + } + catch( IdUnusedException ex ) + { + log.debug( "buildJoinContextForSiteBrowser()", ex ); + message = rb.getFormattedMessage( MSG_KEY_JOIN_NOT_FOUND, new Object[] { siteID } ); + } + + // Load up the context object and return the string postfix for the chef_sitebrowser_join.vm template + context.put( CONTEXT_JOIN_SITE_MSG, message ); + context.put( CONTEXT_JOIN_SITE_SUCCESS, success ); + context.put( CONTEXT_JOIN_SITE_SITE_TITLE, siteTitle ); + context.put( CONTEXT_JOIN_SITE_LINK, link ); + return "_" + SITE_BROWSER_JOIN_MODE; + } + + /** + * Put the value of the exclude from public setting for the given site into the context + * for the Site Browser's visit interface + * + * @param context + * the parameters being passed to the velocity template + * @param siteID + * the ID of the site in question + * @return status (true/false) + */ + public static boolean putIsSiteExcludedFromPublic( Context context, String siteID ) + { + if( context == null || siteID == null ) + { + return false; + } + + boolean excludePublic = false; + try + { + Site site = siteService.getSite( siteID ); + ResourceProperties rp = site.getProperties(); + try { excludePublic = rp.getBooleanProperty( SITE_PROP_JOIN_SITE_EXCLUDE_PUBLIC_LIST ); } + catch( Exception ex ) { excludePublic = false; } + } + catch( IdUnusedException ex ) { excludePublic = false; } + context.put( CONTEXT_JOIN_SITE_EXCLUDE_PUBLIC_LIST, excludePublic ); + + return true; + } + + /** + * Put a boolean value into the context which indicates if the current user is already a member of the site in question + * + * @param context + * the parameters being passed to the velocity template + * @param siteID + * the ID of the site in question + * @return status (true/false) + */ + public static boolean putIsCurrentUserAlreadyMemberInContextForSiteBrowser( Context context, String siteID ) + { + User currentUser = userDirectoryService.getCurrentUser(); + if( currentUser != null && currentUser.getEid() != null ) + { + context.put( CONTEXT_JOIN_SITE_ALREADY_MEMBER, siteService.isCurrentUserMemberOfSite( siteID ) ); + return true; + } + else + { + context.put( CONTEXT_JOIN_SITE_ALREADY_MEMBER, false ); + return false; + } + } + + /** + * Put the current user object into the context for the site browser + * + * @param context + * the parameters being passed to the velocity template + * @return status (true/false) + */ + public static boolean putCurrentUserInContextForSiteBrowser( Context context ) + { + User currentUser = userDirectoryService.getCurrentUser(); + if( context == null || currentUser == null ) + { + return false; + } + + context.put( CONTEXT_JOIN_SITE_CURRENT_USER, currentUser ); + return true; + } + + /** + * Put the global toggle value for joining with the site browser into the context + * + * @param context + * the object to dump the settings into + * @return status (true/false) + */ + public static boolean putIsSiteBrowserJoinEnabledInContext( Context context ) + { + if( context == null ) + { + return false; + } + + context.put( CONTEXT_JOIN_SITE_SITE_BROWSER_JOIN_ENABLED, siteService.isGlobalJoinFromSiteBrowserEnabled() ); + return true; + } + + /** + * Put a map of site IDs->exclude from public site list setting into the context for the site + * browser's list mode. + * + * @param context + * the parameters being passed to the velocity template + * @param sites + * the list of sites to create the map for + * @return status (true/false) + */ + @SuppressWarnings( "rawtypes" ) + public static boolean putSiteMapInContextForSiteBrowser( Context context, List sites ) + { + if( context == null || sites == null ) + { + return false; + } + + // Loop through all the sites to create the map of site IDs->exclude from public setting + Map siteMap = new HashMap(); + for( Object obj : sites ) + { + Site site = (Site) obj; + ResourceProperties rp = site.getProperties(); + boolean pubExcl = false; + try { pubExcl = rp.getBooleanProperty( SITE_PROP_JOIN_SITE_EXCLUDE_PUBLIC_LIST ); } + catch( Exception ex ) { pubExcl = false; } + siteMap.put( site.getId(), Boolean.valueOf( pubExcl ) ); + } + + // Put the site map into the context + context.put( CONTEXT_JOIN_SITE_SITE_MAP, siteMap ); + return true; + } + + /** + * Helper method to determine if joining from the site browser has been enabled globally + * + * @return true/false (enabled/disabled) + */ + public static boolean isJoinFromSiteBrowserEnabled() + { + return siteService.isGlobalJoinFromSiteBrowserEnabled(); + } + + /********************************************************************************************** + ********************* MembershipAction Methods (Membership tool) ***************************** + **********************************************************************************************/ + + /** + * Perform the steps needed to join a site. This includes determining if the global switch for + * join limited by account type is enabled, as well as if it's enabled for the current site along + * with the allowed account types set for the current site. The joiner group is also checked, and + * joined if necessary. It will also send email notifications on join only if it is enabled both + * globally and for the site. + * + * Update: (Dec 2013 - sfoster9@uwo.ca) these checks are now in kernel's join method, so just call join + * + * @param siteID + * the ID of the site in question + * @return status (true/false) + * @throws IdUnusedException + * the ID (of the site) is un-used + * @throws PermissionException + * not enough permissions to join the site + * @throws InUseException + * the site is in an edit state somewhere else + */ + public static boolean doJoinForMembership( String siteID ) throws IdUnusedException, PermissionException, InUseException + { + // Get the current user + User currentUser = userDirectoryService.getCurrentUser(); + + if( siteID == null || siteID.isEmpty() || currentUser == null ) + { + return false; + } + + // If the user is allowed to join, join. + if( siteService.isAllowedToJoin( siteID ) ) + { + // Join the site (without catching, so the try/catch in MembershipAction will catch the appropriate exception + // and create the corresponding user facing error message) + siteService.join( siteID ); + log.info( "Successfully added user '" + currentUser.getEid() + "' to site '" + siteID + "'" ); + + return true; + } + + return false; + } + + /** + * Filter the given list of sites, taking into account the site's setting for the 'exclude from + * public list' joinable site setting, and the master toggle. + * + * @param sites + * the list of sites to filter + * @return status (true/false) + */ + public static boolean filterSitesListForMembership( List sites ) + { + // If the exclude from public list setting is disabled globally, don't do anything to the list (no filter) + if( !siteService.isGlobalJoinExcludedFromPublicListEnabled() ) + { + return false; + } + + // Otherwise remove any sites that have the exclude from public list setting enabled + else + { + Iterator itr = sites.iterator(); + while( itr.hasNext() ) + { + ResourceProperties rp = itr.next().getProperties(); + boolean excludePublic = false; + try{ excludePublic = rp.getBooleanProperty( SITE_PROP_JOIN_SITE_EXCLUDE_PUBLIC_LIST ); } + catch( Exception ex ) { excludePublic = false; } + if( excludePublic ) + { + itr.remove(); + } + } + } + + return true; + } + + /*********************************************************************************************** + ********************* SiteAction Methods (Site Info tool) ************************************* + ***********************************************************************************************/ + + /** + * Take the joinable site settings from the given ParameterParser object and dump them into the SiteInfo object + * + * @param params + * the object to get the settings from + * @param siteInfo + * the object to dump the settings into + * @return status (true/false) + */ + public static boolean updateSiteInfoFromParams( ParameterParser params, SiteInfo siteInfo ) + { + if( params == null || siteInfo == null ) + { + return false; + } + + if( siteService.isGlobalJoinGroupEnabled() && params.getString( SITE_PROP_JOIN_SITE_GROUP_ID ) != null ) + { + siteInfo.joinerGroup = params.getString( SITE_PROP_JOIN_SITE_GROUP_ID ); + } + + if( siteService.isGlobalJoinNotificationEnabled() && params.getString( SITE_PROP_JOIN_SITE_NOTIFACTION ) != null ) + { + siteInfo.joinNotifications = Boolean.valueOf( params.getString( SITE_PROP_JOIN_SITE_NOTIFACTION ) ); + } + + if( siteService.isGlobalJoinExcludedFromPublicListEnabled() && params.getString( SITE_PROP_JOIN_SITE_EXCLUDE_PUBLIC_LIST ) != null ) + { + siteInfo.joinExcludePublic = Boolean.valueOf( params.getString( SITE_PROP_JOIN_SITE_EXCLUDE_PUBLIC_LIST ) ); + } + + if( siteService.isGlobalJoinLimitByAccountTypeEnabled() && params.getString( SITE_PROP_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE ) != null ) + { + siteInfo.joinLimitByAccountType = Boolean.valueOf( params.getString( SITE_PROP_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE ) ); + } + + if( siteService.isGlobalJoinLimitByAccountTypeEnabled() && params.getString( SITE_PROP_JOIN_SITE_ACCOUNT_TYPES ) != null ) + { + siteInfo.joinLimitedAccountTypes = params.getString( SITE_PROP_JOIN_SITE_ACCOUNT_TYPES ); + } + + return true; + } + + /** + * Take the joinable site settings from the given ResourceProperties (site properties) object and dump them into the SiteInfo object + * + * @param props + * the object to get the settings from + * @param siteInfo + * the object to dumpt he settings into + * @return status (true/false) + */ + public static boolean updateSiteInfoFromSiteProperties( ResourceProperties props, SiteInfo siteInfo ) + { + if( props == null || siteInfo == null ) + { + return false; + } + + if( siteService.isGlobalJoinGroupEnabled() && props.getProperty( SITE_PROP_JOIN_SITE_GROUP_ID ) != null ) + { + siteInfo.joinerGroup = props.getProperty( SITE_PROP_JOIN_SITE_GROUP_ID ); + } + + if( siteService.isGlobalJoinNotificationEnabled() && props.getProperty( SITE_PROP_JOIN_SITE_NOTIFACTION ) != null ) + { + try { siteInfo.joinNotifications = Boolean.valueOf( props.getBooleanProperty( SITE_PROP_JOIN_SITE_NOTIFACTION ) ); } + catch( Exception ex ) { siteInfo.joinNotifications = false; } + } + + if( siteService.isGlobalJoinExcludedFromPublicListEnabled() && props.getProperty( SITE_PROP_JOIN_SITE_EXCLUDE_PUBLIC_LIST ) != null ) + { + try { siteInfo.joinExcludePublic = Boolean.valueOf( props.getBooleanProperty( SITE_PROP_JOIN_SITE_EXCLUDE_PUBLIC_LIST ) ); } + catch( Exception ex ) { siteInfo.joinExcludePublic = false; } + } + + if( siteService.isGlobalJoinLimitByAccountTypeEnabled() && props.getProperty( SITE_PROP_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE ) != null ) + { + try { siteInfo.joinLimitByAccountType = Boolean.valueOf( props.getBooleanProperty( SITE_PROP_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE ) ); } + catch( Exception ex ) { siteInfo.joinLimitByAccountType = false; } + } + + if( siteService.isGlobalJoinLimitByAccountTypeEnabled() && props.getProperty( SITE_PROP_JOIN_SITE_ACCOUNT_TYPES ) != null ) + { + siteInfo.joinLimitedAccountTypes = props.getProperty( SITE_PROP_JOIN_SITE_ACCOUNT_TYPES ); + } + + return true; + } + + /** + * On add new site; take the joinable site settings from the given SiteInfo object and dump them into + * the ResourcePropertiesEdit (site properties) object + * + * @param siteInfo + * the object to get the settings from + * @param props + * the object to dump the settings into + * @return status (true/false) + */ + public static boolean updateSitePropertiesFromSiteInfoOnAddNewSite( SiteInfo siteInfo, ResourcePropertiesEdit props ) + { + if( props == null || siteInfo == null ) + { + return false; + } + + if( siteService.isGlobalJoinGroupEnabled() ) + { + props.addProperty( SITE_PROP_JOIN_SITE_GROUP_ID, siteInfo.joinerGroup ); + } + + if( siteService.isGlobalJoinNotificationEnabled() ) + { + props.addProperty( SITE_PROP_JOIN_SITE_NOTIFACTION, Boolean.toString( siteInfo.joinNotifications ) ); + } + + if( siteService.isGlobalJoinExcludedFromPublicListEnabled() ) + { + props.addProperty( SITE_PROP_JOIN_SITE_EXCLUDE_PUBLIC_LIST, Boolean.toString( siteInfo.joinExcludePublic ) ); + } + + if( siteService.isGlobalJoinLimitByAccountTypeEnabled() ) + { + props.addProperty( SITE_PROP_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE, Boolean.toString( siteInfo.joinLimitByAccountType ) ); + props.addProperty( SITE_PROP_JOIN_SITE_ACCOUNT_TYPES, siteInfo.joinLimitedAccountTypes ); + } + + return true; + } + + /** + * On update of site attributes; take the joinable site settings from the state and dump them into the site's properties + * + * @param site + * the site to dump the settings into + * @param state + * the object to get the settings from + * @return status (true/false) + */ + public static boolean updateSitePropertiesFromStateOnUpdateSiteAttributes( Site site, SessionState state ) + { + if( site == null || state == null ) + { + return false; + } + + if( site.isJoinable() ) + { + return updateSitePropertiesFromStateOnSiteUpdate( site.getPropertiesEdit(), state ); + } + + return false; + } + + /** + * On save of Modify Access in Site Info; take the joinable site settings from the state and dump them into the site's properties + * + * @param props + * the object to dump the settings into + * @param state + * the object to get the settings from + * @return status (true/false) + */ + public static boolean updateSitePropertiesFromStateOnSiteInfoSaveGlobalAccess( ResourcePropertiesEdit props, SessionState state ) + { + if( props == null || state == null ) + { + return false; + } + + if( siteService.isGlobalJoinGroupEnabled() ) + { + props.addProperty( SITE_PROP_JOIN_SITE_GROUP_ID, (String) state.getAttribute( FORM_PREFIX + CONTEXT_JOIN_SITE_GROUP_DROP_DOWN ) ); + } + + if( siteService.isGlobalJoinNotificationEnabled() ) + { + props.addProperty( SITE_PROP_JOIN_SITE_NOTIFACTION, (String) state.getAttribute( FORM_PREFIX + CONTEXT_JOIN_SITE_NOTIFY_CHECKBOX ) ); + } + + if( siteService.isGlobalJoinExcludedFromPublicListEnabled() ) + { + props.addProperty( SITE_PROP_JOIN_SITE_EXCLUDE_PUBLIC_LIST, (String) state.getAttribute( FORM_PREFIX + CONTEXT_JOIN_SITE_EXCLUDE_PUBLIC_LIST_CHECKBOX ) ); + } + + if( siteService.isGlobalJoinLimitByAccountTypeEnabled() ) + { + props.addProperty( SITE_PROP_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE, (String) state.getAttribute( FORM_PREFIX + CONTEXT_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE_CHECKBOX ) ); + aggregateSelectedAccountTypesAndAddToSiteProps( props, state ); + } + + return true; + } + + /** + * On site update; take joinable site settings from the state and dump them into the site's properties + * + * @param props + * the object to dump the settings into + * @param state + * the object to get the settings from + * @return status (true/false) + */ + public static boolean updateSitePropertiesFromStateOnSiteUpdate( ResourcePropertiesEdit props, SessionState state ) + { + if( props == null || state == null ) + { + return false; + } + + if( siteService.isGlobalJoinGroupEnabled() ) + { + props.addProperty( SITE_PROP_JOIN_SITE_GROUP_ID, state.getAttribute( STATE_JOIN_SITE_GROUP_ID ).toString() ); + } + + if( siteService.isGlobalJoinNotificationEnabled() ) + { + props.addProperty( SITE_PROP_JOIN_SITE_NOTIFACTION, state.getAttribute( STATE_JOIN_SITE_NOTIFICATION ).toString() ); + } + + if( siteService.isGlobalJoinExcludedFromPublicListEnabled() ) + { + props.addProperty( SITE_PROP_JOIN_SITE_EXCLUDE_PUBLIC_LIST, state.getAttribute( STATE_JOIN_SITE_EXCLUDE_PUBLIC_LIST ).toString() ); + } + + if( siteService.isGlobalJoinLimitByAccountTypeEnabled() ) + { + props.addProperty( SITE_PROP_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE, state.getAttribute( STATE_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE ).toString() ); + aggregateSelectedAccountTypesAndAddToSiteProps( props, state ); + } + + return true; + } + + /** + * On site update; take the joinable site settings from the state and dump them into the SiteInfo object + * + * @param state + * the object to get the settings from + * @param siteInfo + * the object to dump the settings into + * @param isSiteJoinable + * is the site joinable or not (true/fasle) + * @return status (true/false) + */ + public static boolean updateSiteInfoFromStateOnSiteUpdate( SessionState state, SiteInfo siteInfo, boolean isSiteJoinable ) + { + if( state == null || siteInfo == null ) + { + return false; + } + + if( isSiteJoinable ) + { + if( siteService.isGlobalJoinGroupEnabled() && state.getAttribute( STATE_JOIN_SITE_GROUP_ID ) != null ) + { + siteInfo.joinerGroup = (String) state.getAttribute( STATE_JOIN_SITE_GROUP_ID ); + } + else + { + siteInfo.joinerGroup = ""; + } + + if( siteService.isGlobalJoinNotificationEnabled() && state.getAttribute( STATE_JOIN_SITE_NOTIFICATION ) != null ) + { + siteInfo.joinNotifications = Boolean.valueOf( state.getAttribute( STATE_JOIN_SITE_NOTIFICATION ).toString() ); + } + else + { + siteInfo.joinNotifications = false; + } + + if( siteService.isGlobalJoinExcludedFromPublicListEnabled() && state.getAttribute( STATE_JOIN_SITE_EXCLUDE_PUBLIC_LIST ) != null ) + { + siteInfo.joinExcludePublic = Boolean.valueOf( state.getAttribute( STATE_JOIN_SITE_EXCLUDE_PUBLIC_LIST ).toString() ); + } + else + { + siteInfo.joinExcludePublic = false; + } + + if( siteService.isGlobalJoinLimitByAccountTypeEnabled() && state.getAttribute( STATE_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE ) != null ) + { + siteInfo.joinLimitByAccountType = Boolean.valueOf( state.getAttribute( STATE_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE ).toString() ); + } + else + { + siteInfo.joinLimitByAccountType = false; + } + + if( siteService.isGlobalJoinLimitByAccountTypeEnabled() ) + { + Set selectedAccountTypes = new HashSet(); + for( AllowedJoinableAccount account : siteService.getAllowedJoinableAccounts() ) + { + if( state.getAttribute( STATE_JOIN_SITE_ACCOUNT_TYPE_PREFIX + account.getType() ) != null ) + { + if( TRUE_STRING.equalsIgnoreCase( state.getAttribute( STATE_JOIN_SITE_ACCOUNT_TYPE_PREFIX + account.getType() ).toString() ) ) + { + if( selectedAccountTypes.contains( account.getType() ) ) + { + continue; + } + + selectedAccountTypes.add( account.getType() ); + } + } + } + + StringBuilder sb = new StringBuilder(); + String prefix = ""; + for( String accountType : selectedAccountTypes ) + { + sb.append( prefix ).append( accountType ); + prefix = CSV_DELIMITER; + } + + siteInfo.joinLimitedAccountTypes = sb.toString(); + } + else + { + siteInfo.joinLimitedAccountTypes = ""; + } + } + else + { + siteInfo.joinerGroup = null; + } + + return true; + } + + /** + * Get all the form inputs for the joinable site settings (get the from the ParametersParser and put them into the state) + * + * @param state + * the object to dump the settings into + * @param params + * the object to get the settings from + * @return status (true/false) + */ + public static boolean getAllFormInputs( SessionState state, ParameterParser params ) + { + if( state == null || params == null ) + { + return false; + } + + if( siteService.isGlobalJoinGroupEnabled() ) + { + readInputAndUpdateStateVariable( state, params, CONTEXT_JOIN_SITE_GROUP_DROP_DOWN, STATE_JOIN_SITE_GROUP_ID, false ); + } + + if( siteService.isGlobalJoinNotificationEnabled() ) + { + readInputAndUpdateStateVariable( state, params, CONTEXT_JOIN_SITE_NOTIFY_CHECKBOX, STATE_JOIN_SITE_NOTIFICATION, true ); + } + + if( siteService.isGlobalJoinExcludedFromPublicListEnabled() ) + { + readInputAndUpdateStateVariable( state, params, CONTEXT_JOIN_SITE_EXCLUDE_PUBLIC_LIST_CHECKBOX, STATE_JOIN_SITE_EXCLUDE_PUBLIC_LIST, true ); + } + + if( siteService.isGlobalJoinLimitByAccountTypeEnabled() ) + { + readInputAndUpdateStateVariable( state, params, CONTEXT_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE_CHECKBOX, STATE_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE, true ); + for( String account : siteService.getAllowedJoinableAccountTypes() ) + { + readInputAndUpdateStateVariable( state, params, CONTEXT_JOIN_SITE_ACCOUNT_TYPE_CHECKBOX_PREFIX + account, + STATE_JOIN_SITE_ACCOUNT_TYPE_PREFIX + account, true ); + } + } + + return true; + } + + /** + * On new site creation or Site Info->Edit Access; take the joinable site settings from the site's properties + * and dump them into the state + * + * @param props + * the object to get the settings from + * @param state + * the object to dump the settings into + * @return status (true/false) + */ + public static boolean updateStateFromSitePropertiesOnEditAccessOrNewSite( ResourceProperties props, SessionState state ) + { + if( props == null || state == null ) + { + return false; + } + + // Get these site properties regardless of if the global toggles are disabled, as we may need them in the state anyways + // for clarity to the user (the checkboxes will still hold their initial choices, but will be disabled) + state.setAttribute( STATE_JOIN_SITE_GROUP_ID, props.getProperty( SITE_PROP_JOIN_SITE_GROUP_ID ) ); + + try { state.setAttribute( STATE_JOIN_SITE_NOTIFICATION, Boolean.valueOf( props.getBooleanProperty( SITE_PROP_JOIN_SITE_NOTIFACTION ) ) ); } + catch( Exception ex ) { state.setAttribute( STATE_JOIN_SITE_NOTIFICATION, Boolean.FALSE ); } + + try { state.setAttribute( STATE_JOIN_SITE_EXCLUDE_PUBLIC_LIST, Boolean.valueOf( props.getBooleanProperty( SITE_PROP_JOIN_SITE_EXCLUDE_PUBLIC_LIST ) ) ); } + catch( Exception ex ) { state.setAttribute( STATE_JOIN_SITE_EXCLUDE_PUBLIC_LIST, Boolean.FALSE ); } + + try { state.setAttribute( STATE_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE, Boolean.valueOf( props.getBooleanProperty( SITE_PROP_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE ) ) ); } + catch( Exception ex ) { state.setAttribute( STATE_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE, Boolean.FALSE ); } + + state.setAttribute( STATE_JOIN_SITE_ACCOUNT_TYPES, props.getProperty( SITE_PROP_JOIN_SITE_ACCOUNT_TYPES ) ); + + return true; + } + + /** + * When a template site is selected; take the joinable site settings from the template site's properties + * and dump them into the SiteInfo object + * + * @param props + * the object to get the settings from + * @param siteInfo + * the object to put the settings into + * @return status (true/false) + */ + public static boolean updateSiteInfoFromSitePropertiesOnSelectTemplate( ResourceProperties props, SiteInfo siteInfo ) + { + if( props == null || siteInfo == null ) + { + return false; + } + + try { siteInfo.joinerGroup = props.getProperty( SITE_PROP_JOIN_SITE_GROUP_ID ); } + catch( Exception ex ) { siteInfo.joinerGroup = SITE_PROP_JOIN_SITE_GROUP_NO_SEL; } + + try { siteInfo.joinNotifications = props.getBooleanProperty( SITE_PROP_JOIN_SITE_NOTIFACTION ); } + catch( Exception ex ) { siteInfo.joinNotifications = false; } + + try { siteInfo.joinExcludePublic = props.getBooleanProperty( SITE_PROP_JOIN_SITE_EXCLUDE_PUBLIC_LIST ); } + catch( Exception ex ) { siteInfo.joinExcludePublic = false; } + + try { siteInfo.joinLimitByAccountType = props.getBooleanProperty( SITE_PROP_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE ); } + catch( Exception ex ) { siteInfo.joinLimitByAccountType = false; } + + try { siteInfo.joinLimitedAccountTypes = props.getProperty( SITE_PROP_JOIN_SITE_ACCOUNT_TYPES ); } + catch( Exception ex ) { siteInfo.joinLimitedAccountTypes = ""; } + + return true; + } + + /** + * Put the joinable site settings into the context for Site Info->Edit Access when the site is null + * + * @param context + * the parameters being passed to the velocity template + * @param siteInfo + * the object that contains (default) values for the joinable site settings + * @param isSiteJoinable + * true/false (joinable/not joinable) + * @return status (true/false) + */ + public static boolean addJoinableSiteSettingsToEditAccessContextWhenSiteIsNull( Context context, SiteInfo siteInfo, boolean isSiteJoinable ) + { + if( context == null || siteInfo == null ) + { + return false; + } + + if( isSiteJoinable ) + { + putGlobalEnabledSettingsIntoContext( context ); + updateContextFromSiteInfo( context, siteInfo ); + } + + putAllowedJoinableAccountListsIntoContext( context ); + + return true; + } + + /** + * Put the joinable site settings into the context for Site Info->Edit Access when the site is not null + * + * @param context + * the parameters being passed to the velocity template + * @param state + * the object that contains values for the joinable site settings + * @param site + * the site in question + * @param isSiteJoinable + * if the type of site is in the list of joinable site types + * @return status (true/false) + */ + public static boolean addJoinableSiteSettingsToEditAccessContextWhenSiteIsNotNull( Context context, SessionState state, Site site, boolean isSiteJoinable ) + { + if( context == null || state == null ) + { + return false; + } + + if( isSiteJoinable ) + { + putGlobalEnabledSettingsIntoContext( context ); + + // If join group is enabled globally... + if( siteService.isGlobalJoinGroupEnabled() ) + { + // And they've previously made a selection for join group, add their selection to the context + if( state.getAttribute( STATE_JOIN_SITE_GROUP_ID ) != null ) + { + context.put( CONTEXT_JOIN_SITE_GROUP_ID, state.getAttribute( STATE_JOIN_SITE_GROUP_ID ) ); + } + + // Add available site groups to the context + putSiteGroupsIntoContext( site, context ); + } + + // If the join group is disabled globally... + else + { + // And they've preivously made a selction for join group, add their selection to the context, as well as the site groups, + // and the flag to indicate this feature has been enabled locally but disabled globally + if( state.getAttribute( STATE_JOIN_SITE_GROUP_ID ) != null && + !"".equals( state.getAttribute( STATE_JOIN_SITE_GROUP_ID ).toString() ) && + !SITE_PROP_JOIN_SITE_GROUP_NO_SEL.equals( state.getAttribute( STATE_JOIN_SITE_GROUP_ID ).toString() ) ) + { + context.put( CONTEXT_JOIN_SITE_GROUP_ID, state.getAttribute( STATE_JOIN_SITE_GROUP_ID ) ); + putSiteGroupsIntoContext( site, context ); + context.put( CONTEXT_JOIN_SITE_GROUP_ENABLED_LOCAL_DISABLED_GLOBAL, Boolean.TRUE ); + } + } + + // Repeat the above process for join notification + if( siteService.isGlobalJoinNotificationEnabled() ) + { + if( state.getAttribute( STATE_JOIN_SITE_NOTIFICATION ) != null ) + { + context.put( CONTEXT_JOIN_SITE_NOTIFICATION, state.getAttribute( STATE_JOIN_SITE_NOTIFICATION ) ); + } + } + else + { + if( state.getAttribute( STATE_JOIN_SITE_NOTIFICATION ) != null && + Boolean.valueOf( state.getAttribute( STATE_JOIN_SITE_NOTIFICATION ).toString() ) == Boolean.TRUE ) + { + context.put( CONTEXT_JOIN_SITE_NOTIFICATION, state.getAttribute( STATE_JOIN_SITE_NOTIFICATION ) ); + context.put( CONTEXT_JOIN_SITE_NOTIFICATION_ENABLED_LOCAL_DISABLED_GLOBAL, Boolean.TRUE ); + } + } + + // Repeat the above process for exclude from public + if( siteService.isGlobalJoinExcludedFromPublicListEnabled() ) + { + if( state.getAttribute( STATE_JOIN_SITE_EXCLUDE_PUBLIC_LIST ) != null ) + { + context.put( CONTEXT_JOIN_SITE_EXCLUDE_PUBLIC_LIST, state.getAttribute( STATE_JOIN_SITE_EXCLUDE_PUBLIC_LIST ) ); + } + } + else + { + if( state.getAttribute( STATE_JOIN_SITE_EXCLUDE_PUBLIC_LIST ) != null && + Boolean.valueOf( state.getAttribute( STATE_JOIN_SITE_EXCLUDE_PUBLIC_LIST ).toString() ) == Boolean.TRUE) + { + context.put( CONTEXT_JOIN_SITE_EXCLUDE_PUBLIC_LIST, state.getAttribute( STATE_JOIN_SITE_EXCLUDE_PUBLIC_LIST ) ); + context.put( CONTEXT_JOIN_SITE_EXCLUDE_ENABLED_LOCAL_DISABLED_GLOBAL, Boolean.TRUE ); + } + } + + // Repeat the above process for limit by account types + if( siteService.isGlobalJoinLimitByAccountTypeEnabled() ) + { + if( state.getAttribute( STATE_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE ) != null ) + { + context.put( CONTEXT_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE, state.getAttribute( STATE_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE ) ); + } + if( putAllowedJoinableAccountListsIntoContext( context ) ) + { + if( state.getAttribute( STATE_JOIN_SITE_ACCOUNT_TYPES ) != null ) + { + context.put( CONTEXT_JOIN_SITE_LIMIT_ACCOUNT_TYPES, Arrays.asList( + state.getAttribute( STATE_JOIN_SITE_ACCOUNT_TYPES ).toString().split( CSV_DELIMITER ) ) ); + } + } + } + else + { + if( state.getAttribute( STATE_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE ) != null && + Boolean.valueOf( state.getAttribute( STATE_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE ).toString() ) == Boolean.TRUE) + { + context.put( CONTEXT_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE, state.getAttribute( STATE_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE ) ); + context.put( CONTEXT_JOIN_SITE_LIMIT_ENABLED_LOCAL_DISABLED_GLOBAL, Boolean.TRUE ); + if( putAllowedJoinableAccountListsIntoContext( context ) ) + { + if( state.getAttribute( STATE_JOIN_SITE_ACCOUNT_TYPES ) != null ) + { + context.put( CONTEXT_JOIN_SITE_LIMIT_ACCOUNT_TYPES, Arrays.asList( + state.getAttribute( STATE_JOIN_SITE_ACCOUNT_TYPES ).toString().split( CSV_DELIMITER ) ) ); + } + } + } + else + { + context.put( CONTEXT_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE, Boolean.FALSE ); + } + } + } + + return true; + } + + /** + * Put the joinable site settings into the context for the new site UI + * + * @param context + * the parameters being passed to the velocity template + * @param siteInfo + * the object containing (default) values for the joinable site settings + * @return status (true/false) + */ + public static boolean addJoinableSiteSettingsToNewSiteConfirmContext( Context context, SiteInfo siteInfo ) + { + if( context == null || siteInfo == null ) + { + return false; + } + + putGlobalEnabledSettingsIntoContext( context ); + updateContextFromSiteInfo( context, siteInfo ); + + return true; + } + + /** + * Removes the joinable site settings from the state + * + * @param state + * the state object to be modified + * @return status (true/false) + */ + public static boolean removeJoinableSiteSettingsFromState( SessionState state ) + { + if( state == null ) + { + return false; + } + + else + { + if( siteService.isGlobalJoinGroupEnabled() ) + { + state.removeAttribute( STATE_JOIN_SITE_GROUP_ID ); + } + + if( siteService.isGlobalJoinNotificationEnabled() ) + { + state.removeAttribute( STATE_JOIN_SITE_NOTIFICATION ); + } + + if( siteService.isGlobalJoinExcludedFromPublicListEnabled() ) + { + state.removeAttribute( STATE_JOIN_SITE_EXCLUDE_PUBLIC_LIST ); + } + + if( siteService.isGlobalJoinLimitByAccountTypeEnabled() ) + { + state.removeAttribute( STATE_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE ); + state.removeAttribute( STATE_JOIN_SITE_ACCOUNT_TYPES ); + } + } + + return true; + } + + /********************************************************************************************** + ******************************* Private Utility Methods ************************************** + **********************************************************************************************/ + + /** + * Put all the groups for the given site into the context, excluding official rosters. + * + * @param site + * the site in question + * @param context + * the object to put the list of groups into + * @return status (true/false) + */ + private static boolean putSiteGroupsIntoContext( Site site, Context context ) + { + if( site == null || context == null ) + { + return false; + } + + // Strip out any rosters from the list of groups + Collection groups = site.getGroups(); + Iterator itr = groups.iterator(); + while( itr.hasNext() ) + { + Group group = itr.next(); + if( group.getProviderGroupId() != null ) + { + itr.remove(); + } + } + + // Sort the list of groups based on group title + List sortedGroupsWithoutRosters = new ArrayList( groups ); + Collections.sort( sortedGroupsWithoutRosters, new GroupTitleComparator() ); + groups = sortedGroupsWithoutRosters; + context.put( CONTEXT_JOIN_SITE_GROUPS, groups ); + + return true; + } + + /** + * Put the allowed joinable account types/categories lists into the context. This method + * will also determine if the account type properties are valid and put the corresponding + * flag into the context, so that if the feature is either: + * 1) enabled globally + * 2) disabled globally but enabled locally + * AND (for both 1 and 2) the sakai.properties for the account types are invalid; the + * account type checkboxes will not be produced for the UI + * + * @param context + * the parameters being passed to the velocity template + * @return true if account type lists are valid, false otherwise + */ + private static boolean putAllowedJoinableAccountListsIntoContext( Context context ) + { + if( siteService.isGlobalJoinLimitByAccountTypeEnabled() ) + { + context.put( CONTEXT_JOIN_SITE_ACCOUNT_TYPES, siteService.getAllowedJoinableAccounts() ); + context.put( CONTEXT_JOIN_SITE_ACCOUNT_CATEGORIES, siteService.getAllowedJoinableAccountTypeCategories() ); + context.put( CONTEXT_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE_ENABLED, Boolean.TRUE ); + return true; + } + else + { + context.put( CONTEXT_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE_ENABLED, Boolean.FALSE ); + return false; + } + } + + /** + * Put the values from the SiteInfo object into the context (if the setting is enabled) + * + * @param context + * the parameters being passed to the velocity template + * @param siteInfo + * the object to get the values from + */ + private static void updateContextFromSiteInfo( Context context, SiteInfo siteInfo ) + { + if( context == null || siteInfo == null ) + { + return; + } + + if( siteService.isGlobalJoinGroupEnabled() ) + { + context.put( CONTEXT_JOIN_SITE_GROUP_ID, siteInfo.joinerGroup ); + } + + if( siteService.isGlobalJoinNotificationEnabled() ) + { + context.put( CONTEXT_JOIN_SITE_NOTIFICATION, Boolean.valueOf( siteInfo.joinNotifications ) ); + } + + if( siteService.isGlobalJoinExcludedFromPublicListEnabled() ) + { + context.put( CONTEXT_JOIN_SITE_EXCLUDE_PUBLIC_LIST, Boolean.valueOf( siteInfo.joinExcludePublic ) ); + } + + if( siteService.isGlobalJoinLimitByAccountTypeEnabled() ) + { + context.put( CONTEXT_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE, Boolean.valueOf( siteInfo.joinLimitByAccountType ) ); + context.put( CONTEXT_JOIN_SITE_LIMIT_ACCOUNT_TYPES, Arrays.asList( siteInfo.joinLimitedAccountTypes.split( CSV_DELIMITER ) ) ); + } + } + + /** + * Put the master switches (enabled/disabled) for each joinable setting into the context object + * + * @param context + * the parameters being passed to the velocity template + */ + private static void putGlobalEnabledSettingsIntoContext( Context context ) + { + if( context == null ) + { + return; + } + + context.put( CONTEXT_JOIN_SITE_GROUP_ENABLED, Boolean.valueOf( siteService.isGlobalJoinGroupEnabled() ) ); + context.put( CONTEXT_JOIN_SITE_NOTIFICATION_ENABLED, Boolean.valueOf( siteService.isGlobalJoinNotificationEnabled() ) ); + context.put( CONTEXT_JOIN_SITE_EXCLUDE_PUBLIC_LIST_ENABLED, Boolean.valueOf( siteService.isGlobalJoinExcludedFromPublicListEnabled() ) ); + context.put( CONTEXT_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE_ENABLED, Boolean.valueOf( siteService.isGlobalJoinLimitByAccountTypeEnabled() ) ); + } + + /** + * Aggregate the selected allowed joinable account types from the state and add the (comma seperated) list to the site's properties + * + * @param props + * the site's properties to add the list to + * @param state + * the state the contains the selected values + */ + private static void aggregateSelectedAccountTypesAndAddToSiteProps( ResourcePropertiesEdit props, SessionState state ) + { + if( props == null || state == null ) + { + return; + } + + String attribute = ""; + String propertyList = ""; + String prefix = ""; + + // Loop through all the account types + StringBuilder sb = new StringBuilder(); + List selectedAccountTypes = new ArrayList(); + for( String account : siteService.getAllowedJoinableAccountTypes() ) + { + // If the account type was selected, add it to the list + attribute = state.getAttribute( STATE_JOIN_SITE_ACCOUNT_TYPE_PREFIX + account ).toString(); + if( TRUE_STRING.equalsIgnoreCase( attribute ) ) + { + if( selectedAccountTypes.contains( account ) ) + { + continue; + } + + selectedAccountTypes.add( account ); + } + } + + // Create the csv string of account types selected + for( String accountType : selectedAccountTypes ) + { + sb.append( prefix ).append( accountType ); + prefix = CSV_DELIMITER; + } + + // Add the csv list to the site properties + propertyList = sb.toString(); + props.addProperty( SITE_PROP_JOIN_SITE_ACCOUNT_TYPES, propertyList ); + } + + /** + * Read in form field values from the ParameterParser and update/remove values from the state + * + * @param state + * the object to be updated with the form field values + * @param params + * the object that contains the form field values + * @param paramName + * the name of the form field value to retrieve + * @param stateAttributeName + * the name of the state attribute to update + * @param isBoolean + * denotes if the form field value is boolean (true/false) + */ + private static void readInputAndUpdateStateVariable( SessionState state, ParameterParser params, String paramName, String stateAttributeName, boolean isBoolean ) + { + if( state == null || params == null || paramName == null || stateAttributeName == null ) + { + return; + } + + // Get the param value + String paramValue = StringUtils.trimToNull( params.getString( paramName ) ); + + // If the state attribute name is one of the joinable site setting's, flip the value from 'on'/'off' to 'true'/'false' + if( STATE_JOIN_SITE_NOTIFICATION.equalsIgnoreCase( stateAttributeName ) || STATE_JOIN_SITE_LIMIT_BY_ACCOUNT_TYPE.equalsIgnoreCase( stateAttributeName ) || + STATE_JOIN_SITE_EXCLUDE_PUBLIC_LIST.equalsIgnoreCase( stateAttributeName ) || stateAttributeName.startsWith( STATE_JOIN_SITE_ACCOUNT_TYPE_PREFIX ) ) + { + if( paramValue != null && paramValue.equalsIgnoreCase( ON_STRING ) ) + { + paramValue = TRUE_STRING; + } + else + { + paramValue = FALSE_STRING; + } + } + + // If the param value is not null, update the value in the state + if( paramValue != null ) + { + if( isBoolean ) + { + state.setAttribute( stateAttributeName, Boolean.valueOf( paramValue ) ); + } + else + { + state.setAttribute( stateAttributeName, paramValue ); + } + } + + // If the param value is null, and the state attribute name is the joiner group ID, this means that no joiner group was selected, + // so we need to make the param value that of the 'noSelection' constant + else if( STATE_JOIN_SITE_GROUP_ID.equalsIgnoreCase( stateAttributeName ) ) + { + paramValue = SITE_PROP_JOIN_SITE_GROUP_NO_SEL; + } + + // Otherwise, remove the attribute from the state + else + { + state.removeAttribute( stateAttributeName ); + } + } + + /********************************************************************************************** + ********************************** Sub-classes *********************************************** + **********************************************************************************************/ + + /** + * Comparator class used to compare Group objects based on title + * + * @author bjones86 + */ + public static class GroupTitleComparator implements Comparator + { + @Override + public int compare( Group group1, Group group2 ) + { + return group1.getTitle().compareToIgnoreCase( group2.getTitle() ); + } + } +} Index: site-manage-tool/tool/src/bundle/sitesetupgeneric.properties =================================================================== --- site-manage-tool/tool/src/bundle/sitesetupgeneric.properties (revision 133106) +++ site-manage-tool/tool/src/bundle/sitesetupgeneric.properties (working copy) @@ -1158,3 +1158,15 @@ importQueued=Your import process has been queued. You should receive an email when the import has completed. The email should appear within 15 minutes. Until you receive this email no imported material will appear in this course. importQueuedNoEmail=Your import process has been queued. Please allow up to 15 minutes for the site import process to finish. + +## bjones86 - SAK-24423 - joinable site settings +ediacc.joinNotification = Notify site maintainers by email when a user joins this site +ediacc.joinExcludeFromPublic = Exclude this site from the publicly available list of joinable sites +ediacc.joinLimitByAccountType = Limit join to specific accounts +ediacc.additionJoinAccessOptions = Additional Options: +ediacc.joinToGroup = Add joining members to specified group: +ediacc.pleSelGroup = Please select a group: +ediacc.groupEnabledLocalDiabledGlobal = This option has been disabled system-wide. Joining members will not be placed into a group. +ediacc.notifyEnabledLocalDisabledGlobal = This option has been disabled system-wide. You will not receive email notifications when someone joins your site. +ediacc.excludeEnabledLocalDisabledGlobal = This option has been disabled system-wide. This site will appear in the publicly available list of joinable sites. +ediacc.limitEnabledLocalDisabledGlobal = This option has been disabled system-wide. Any user can join this site. Index: site-manage-tool/tool/src/bundle/membership.properties =================================================================== --- site-manage-tool/tool/src/bundle/membership.properties (revision 133106) +++ site-manage-tool/tool/src/bundle/membership.properties (working copy) @@ -49,3 +49,6 @@ mb.list.select=To operate the combo box, first press Alt+Down Arrow to open it, and then use the up and down arrow keys to scroll through the options. mb.list.of=of mb.nosd=No short site description has been provided + +# bjones86 - SAK-24423 - joinable site settings - membership tool messages +mb.join.notAllowed = This site has limited access. You cannot join the site with your current account. Contact the site's owner for more information. Index: site-manage-tool/tool/src/bundle/sitebrowser.properties =================================================================== --- site-manage-tool/tool/src/bundle/sitebrowser.properties (revision 133106) +++ site-manage-tool/tool/src/bundle/sitebrowser.properties (working copy) @@ -64,3 +64,19 @@ vi.there2 = There is no public syllabus for this site. list.not.selected=No Site Selected vi.size = Size: + +# bjones86 - SAK-24423 - joinable site settings - messages for site browser +list.join = Join +list.alreadyMember = (already a member) +list.joinableButExclude = Yes (contact the instructor) +join.alreadyMember1 = You are already a member of this site! +join.alreadyMember2 = Click the link below to navigate there now. +join.unjoinable =: This site is not joinable; you cannot join this site. +join.notAllowed =: This site has limited access. You cannot join the site with your current account. Contact the site's owner for more information. +join.login =: You need to be logged in to join this site. Please log in and try again. +join.success = Success! You have just joined the site ”{0}“. Click the link below to navigate there now! +join.failPermission = Failed to join the site. You do not have the correct persmissions to join this site. +join.fail = Failed to join the site. Please contact support. +join.joinNow = Join Now +join.notJoinable = No +join.notFound = Site not found. Index: site-manage-tool/tool/src/webapp/vm/sitesetup/chef_site-siteInfo-editAccess.vm =================================================================== --- site-manage-tool/tool/src/webapp/vm/sitesetup/chef_site-siteInfo-editAccess.vm (revision 133106) +++ site-manage-tool/tool/src/webapp/vm/sitesetup/chef_site-siteInfo-editAccess.vm (working copy) @@ -45,7 +45,22 @@ } } + ## bjones86 - SAK-24423 - joinable site settings - checkbox synchronization + function doCategoryCheck( clickedElement ) + { + var clickedElementName = clickedElement.getAttribute( "name" ); + var isChecked = clickedElement.checked; + + var checkboxes = document.getElementsByName( clickedElementName ); + var maxBoxes = checkboxes.length; + + for( var i = 0; i < maxBoxes; i++ ) + { + checkboxes[i].checked = isChecked; + } + } + // -->
@@ -222,8 +237,130 @@ #end

+ + ## bjones86 - SAK-24423 - joinable site settings title + #if( $joinGroupEnabled || $joinNotificationEnabled || $joinExcludeFromPublicListEnabled || $joinLimitAccountTypesEnabled || + $joinGroupEnabledLocalDisabledGlobal || $joinNotifyEnabledLocalDisabledGlobal || $joinExcludeEnabledLocalDisabledGlobal || + $joinLimitEnabledLocalDisabledGlobal ) +

$tlang.getString( "ediacc.additionJoinAccessOptions" )

+ #end + + ## bjones86 - SAK-24423 - joinable site settings - joiner group + #if( $joinGroupEnabled || $joinGroupEnabledLocalDisabledGlobal ) +

+ + + #if( $joinGroupEnabledLocalDisabledGlobal ) +

$tlang.getString( "ediacc.groupEnabledLocalDiabledGlobal" )

+ #end +

+ #end + + ## bjones86 - SAK-24423 - joinable site settings - join notification + #if( $joinNotificationEnabled || $joinNotifyEnabledLocalDisabledGlobal ) +

+ + +

+ #if( $joinNotifyEnabledLocalDisabledGlobal ) +

$tlang.getString( "ediacc.notifyEnabledLocalDisabledGlobal" )

+ #end + #end + + ## bjones86 - SAK-24423 - joinable site settings - exclude from public list + #if( $joinExcludeFromPublicListEnabled || $joinExcludeEnabledLocalDisabledGlobal ) +

+ + +

+ #if( $joinExcludeEnabledLocalDisabledGlobal ) +

$tlang.getString( "ediacc.excludeEnabledLocalDisabledGlobal" )

+ #end + #end + + ## bjones86 - SAK-24423 - joinable site settings - limit by account types + #if( $joinLimitAccountTypesEnabled || $joinLimitEnabledLocalDisabledGlobal ) +

+ + +

+ + #if( $joinLimitEnabledLocalDisabledGlobal ) +

$tlang.getString( "ediacc.limitEnabledLocalDisabledGlobal" )

+ #end + +
+ #foreach( $category in $joinableAccountTypeCategories ) + #if( !$category ) + #foreach( $accType in $joinableAccountTypes ) +

+ + +

+ #end + #end + +

+ $category + #foreach( $accType in $joinableAccountTypes ) + #if( $accType.category == $category ) + #set( $checkboxName = "$accType.category$accType.type" ) +

+ + +

+ #end + #end +

+ #end
+ #end +
+

Index: site-manage-tool/tool/src/webapp/vm/sitebrowser/chef_sitebrowser_list.vm =================================================================== --- site-manage-tool/tool/src/webapp/vm/sitebrowser/chef_sitebrowser_list.vm (revision 133106) +++ site-manage-tool/tool/src/webapp/vm/sitebrowser/chef_sitebrowser_list.vm (working copy) @@ -134,11 +134,62 @@ - #if ($site.isJoinable()) - $tlang.getString("list.joinable") + #if( $site.isJoinable() ) + + ## bjones86 - SAK-24423 - if site browser joining is disabled, just use the normal strings + #if( !$siteBrowserJoinEnabled ) + $tlang.getString( "list.joinable" ) + + ## bjones86 - SAK-24423 - otherwise, provide the ability to join from the site browser #else - $tlang.getString("list.notjoinable") + #if( !$currentUser.Id || $currentUser.Id == "" ) + + $tlang.getString( "join.joinNow" ) + + #else + + ## Check if they're already a member of the site + #set( $currentUserEid = $currentUser.Eid ) + #set( $alreadyMember = false ) + #set( $members = $site.Members ) + #foreach( $member in $members ) + #if( $member.UserEid == $currentUserEid ) + #set( $alreadyMember = true ) + #break #end + #end + + ## Check if the site is joinable but public excluded + #set( $publicExcluded = false ) + #foreach( $key in $siteMap.keySet() ) + #if( $key == $site.Id ) + #set( $publicExcluded = $siteMap.get( $key ) ) + #break + #end + #end + + #if( $alreadyMember ) + $tlang.getString( "list.alreadyMember" ) + + #elseif( $publicExcluded ) + $tlang.getString( "list.joinableButExclude" ) + + #else + + $tlang.getString( "join.joinNow" ) + + #end + #end + #end + #else + #if( !$siteBrowserJoinEnabled ) + $tlang.getString( "list.notjoinable" ) + #else + $tlang.getString( "join.notJoinable" ) + #end + #end Index: site-manage-tool/tool/src/webapp/vm/sitebrowser/chef_sitebrowser_visit.vm =================================================================== --- site-manage-tool/tool/src/webapp/vm/sitebrowser/chef_sitebrowser_visit.vm (revision 133106) +++ site-manage-tool/tool/src/webapp/vm/sitebrowser/chef_sitebrowser_visit.vm (working copy) @@ -9,6 +9,11 @@ #toolbar($menu) #end + ## bjones86 - SAK-24423 - alert messages needed for site browser join + #if( $alertMessage ) +

$tlang.getString( "gen.alert" ) $validator.escapeHtml( $alertMessage )
+ #end +