diff --git a/dspace/etc/clean-database.sql b/dspace/etc/clean-database.sql index d7863cc968..6d2443126d 100644 --- a/dspace/etc/clean-database.sql +++ b/dspace/etc/clean-database.sql @@ -82,6 +82,7 @@ DROP TABLE EPersonGroup2EPerson; DROP TABLE ResourcePolicy; DROP TABLE Collection2Item; DROP TABLE Community2Collection; +DROP TABLE Community2Community; DROP TABLE Collection; DROP TABLE Community; DROP TABLE DCValue; @@ -109,6 +110,7 @@ DROP SEQUENCE bundle2bitstream_seq; DROP SEQUENCE dctyperegistry_seq; DROP SEQUENCE dcvalue_seq; DROP SEQUENCE community_seq; +DROP SEQUENCE community2community_seq; DROP SEQUENCE collection_seq; DROP SEQUENCE community2collection_seq; DROP SEQUENCE collection2item_seq; diff --git a/dspace/etc/database_schema.sql b/dspace/etc/database_schema.sql index c3b4bf2158..fb5f728c84 100644 --- a/dspace/etc/database_schema.sql +++ b/dspace/etc/database_schema.sql @@ -91,6 +91,7 @@ CREATE SEQUENCE dctyperegistry_seq; CREATE SEQUENCE dcvalue_seq; CREATE SEQUENCE community_seq; CREATE SEQUENCE collection_seq; +CREATE SEQUENCE community2community_seq; CREATE SEQUENCE community2collection_seq; CREATE SEQUENCE collection2item_seq; CREATE SEQUENCE resourcepolicy_seq; @@ -298,6 +299,16 @@ CREATE TABLE Collection workflow_step_3 INTEGER REFERENCES EPersonGroup( eperson_group_id ) ); +------------------------------------------------------- +-- Community2Community table +------------------------------------------------------- +CREATE TABLE Community2Community +( + id INTEGER PRIMARY KEY, + parent_comm_id INTEGER REFERENCES Community(community_id), + child_comm_id INTEGER REFERENCES Community(community_id) +); + ------------------------------------------------------- -- Community2Collection table ------------------------------------------------------- diff --git a/dspace/etc/update-sequences.sql b/dspace/etc/update-sequences.sql index 93705a0d0d..cb2dbed21f 100644 --- a/dspace/etc/update-sequences.sql +++ b/dspace/etc/update-sequences.sql @@ -67,6 +67,7 @@ SELECT setval('bundle2bitstream_seq', max(id)) FROM bundle2bitstream; SELECT setval('dctyperegistry_seq', max(dc_type_id)) FROM dctyperegistry; SELECT setval('dcvalue_seq', max(dc_value_id)) FROM dcvalue; SELECT setval('community_seq', max(community_id)) FROM community; +SELECT setval('community2community_seq', max(id)) FROM community2community; SELECT setval('collection_seq', max(collection_id)) FROM collection; SELECT setval('community2collection_seq', max(id)) FROM community2collection; SELECT setval('collection2item_seq', max(id)) FROM collection2item; diff --git a/dspace/jsp/collection-home.jsp b/dspace/jsp/collection-home.jsp index cef6eb021e..588cf7bfea 100644 --- a/dspace/jsp/collection-home.jsp +++ b/dspace/jsp/collection-home.jsp @@ -107,10 +107,7 @@ %> - + diff --git a/dspace/jsp/community-home.jsp b/dspace/jsp/community-home.jsp index 08195ee329..48dcd27859 100644 --- a/dspace/jsp/community-home.jsp +++ b/dspace/jsp/community-home.jsp @@ -44,6 +44,7 @@ - Attributes required: - community - Community to render home page for - collections - array of Collections in this community + - subcommunities - array of Sub-communities in this community - last.submitted.titles - String[] of titles of recently submitted items - last.submitted.urls - String[] of URLs of recently submitted items - admin_button - Boolean, show admin 'edit' button @@ -64,6 +65,8 @@ Community community = (Community) request.getAttribute( "community" ); Collection[] collections = (Collection[]) request.getAttribute("collections"); + Community[] subcommunities = + (Community[]) request.getAttribute("subcommunities"); String[] lastSubmittedTitles = (String[]) request.getAttribute("last.submitted.titles"); @@ -93,7 +96,7 @@ %> - +
@@ -161,19 +172,14 @@ value="<%=community.getID()%> <%= intro %> -

Collections in this community

+<% + if (collections.length != 0) + { +%> + +

Collections in this community

-<% - if (collections.length == 0) - { -%> -

This community contains no collections.

-<% - } - else - { -%> -
@@ -139,6 +142,14 @@ value="<%=community.getID()%> <% } +%> +<% + for (int j = 0; j < subcommunities.length; j++) + { +%> + +<% + } %>
@@ -103,6 +103,13 @@ + + + <% Collection[] collections = diff --git a/dspace/jsp/index.jsp b/dspace/jsp/index.jsp index 690a799a16..b171bdc04b 100644 --- a/dspace/jsp/index.jsp +++ b/dspace/jsp/index.jsp @@ -73,7 +73,7 @@ context = UIUtil.obtainContext(request); // Home page shows community list - Community[] communities = Community.findAll(context); + Community[] communities = Community.findAllTop(context); request.setAttribute("communities", communities); // Show home page JSP diff --git a/dspace/src/org/dspace/app/webui/jsptag/LayoutTag.java b/dspace/src/org/dspace/app/webui/jsptag/LayoutTag.java index 852e3749c9..6c3ace98e0 100644 --- a/dspace/src/org/dspace/app/webui/jsptag/LayoutTag.java +++ b/dspace/src/org/dspace/app/webui/jsptag/LayoutTag.java @@ -174,6 +174,20 @@ public class LayoutTag extends TagSupport parentLinks.add(parentLink); } } + else if (locbar.equalsIgnoreCase("commLink")) + { + // "commLink" mode - show all parent communities + Community[] comms = (Community[]) + request.getAttribute("dspace.communities"); + if (comms != null) + { + for (int i = 0; i < comms.length; i++) + { + parents.add(comms[i].getMetadata("name")); + parentLinks.add("/handle/" + comms[i].getHandle()); + } + } + } else if (locbar.equalsIgnoreCase("nolink")) { // "nolink" mode - next thing in location bar is taken from @@ -188,17 +202,18 @@ public class LayoutTag extends TagSupport { // Grab parents from the URL - these should have been picked up // by the HandleServlet - - Community com = (Community) - request.getAttribute("dspace.community"); Collection col = (Collection) request.getAttribute("dspace.collection"); + Community[] comms = (Community[]) + request.getAttribute("dspace.communities"); - if (com != null) - { - parents.add(com.getMetadata("name")); - parentLinks.add("/handle/" + com.getHandle()); - + if (comms != null) + { + for (int i = 0; i < comms.length; i++) + { + parents.add(comms[i].getMetadata("name")); + parentLinks.add("/handle/" + comms[i].getHandle()); + } if (col != null) { parents.add(col.getMetadata("name")); diff --git a/dspace/src/org/dspace/app/webui/servlet/AdvancedSearchServlet.java b/dspace/src/org/dspace/app/webui/servlet/AdvancedSearchServlet.java index 5dff9ed8b6..fa310475a5 100644 --- a/dspace/src/org/dspace/app/webui/servlet/AdvancedSearchServlet.java +++ b/dspace/src/org/dspace/app/webui/servlet/AdvancedSearchServlet.java @@ -74,8 +74,8 @@ public class AdvancedSearchServlet extends DSpaceServlet HttpServletResponse response) throws ServletException, IOException, SQLException, AuthorizeException { - // just build a list of communities and pass along to the jsp - Community[] communities = Community.findAll(context); + // just build a list of top-level communities and pass along to the jsp + Community[] communities = Community.findAllTop(context); request.setAttribute("communities", communities); diff --git a/dspace/src/org/dspace/app/webui/servlet/CommunityListServlet.java b/dspace/src/org/dspace/app/webui/servlet/CommunityListServlet.java index e760fc69d6..eb3a553028 100644 --- a/dspace/src/org/dspace/app/webui/servlet/CommunityListServlet.java +++ b/dspace/src/org/dspace/app/webui/servlet/CommunityListServlet.java @@ -79,8 +79,10 @@ public class CommunityListServlet extends DSpaceServlet // This will map community IDs to arrays of collections Map colMap = new HashMap(); + // This will map communityIDs to arrays of sub-communities + Map commMap = new HashMap(); - Community[] communities = Community.findAll(context); + Community[] communities = Community.findAllTop(context); for (int com = 0; com < communities.length; com++) { @@ -89,10 +91,16 @@ public class CommunityListServlet extends DSpaceServlet // Find collections in community Collection[] colls = communities[com].getCollections(); colMap.put(comID, colls); + + // Find subcommunties in community + Community[] comms = communities[com].getSubcommunities(); + commMap.put(comID, comms); + } request.setAttribute("communities", communities); request.setAttribute("collections.map", colMap); + request.setAttribute("subcommunities.map", commMap); JSPManager.showJSP(request, response, "/community-list.jsp"); } } diff --git a/dspace/src/org/dspace/app/webui/servlet/HandleServlet.java b/dspace/src/org/dspace/app/webui/servlet/HandleServlet.java index 44c145a992..1b1aa6553b 100644 --- a/dspace/src/org/dspace/app/webui/servlet/HandleServlet.java +++ b/dspace/src/org/dspace/app/webui/servlet/HandleServlet.java @@ -185,6 +185,13 @@ public class HandleServlet extends DSpaceServlet Community[] parents = c.getCommunities(); request.setAttribute("dspace.community", parents[0]); + /* + * Find all the "parent" communities for the collection for + * "breadcrumbs" + */ + request.setAttribute("dspace.communities", getParents(parents[0],true)); + + // home page, or forward to another page? if (extraPathInfo == null) { @@ -204,6 +211,11 @@ public class HandleServlet extends DSpaceServlet // Store collection location in request request.setAttribute("dspace.community", c); + /* + * Find all the "parent" communities for the community + */ + request.setAttribute("dspace.communities", getParents(c,false)); + // home page, or forward to another page? if (extraPathInfo == null) @@ -269,6 +281,11 @@ public class HandleServlet extends DSpaceServlet request.setAttribute("dspace.collection", collections[0]); Community[] comms = collections[0].getCommunities(); request.setAttribute("dspace.community", comms[0]); + /* + * Find all the "parent" communities for the collection + */ + request.setAttribute("dspace.communities", getParents(comms[0],true)); + // Full or simple display? boolean displayAll = false; @@ -310,6 +327,9 @@ public class HandleServlet extends DSpaceServlet // Get the collections within the community Collection[] collections = community.getCollections(); + // get any subcommunities of the community + Community[] subcommunities = community.getSubcommunities(); + // Find the 5 last submitted items BrowseScope scope = new BrowseScope(context); scope.setScope(community); @@ -334,6 +354,7 @@ public class HandleServlet extends DSpaceServlet request.setAttribute("last.submitted.urls", itemLinks); request.setAttribute("community", community); request.setAttribute("collections", collections); + request.setAttribute("subcommunities", subcommunities); JSPManager.showJSP(request, response, "/community-home.jsp"); } } @@ -589,4 +610,31 @@ public class HandleServlet extends DSpaceServlet return urls; } + + /** + * Utility method to produce a list of parent communities + * for a given community, ending with the passed community, + * if include is true. If commmunity is top-level, + * the array will be empty, or contain only the passed community, + * if include is true. The array is ordered highest level to lowest + */ + private Community[] getParents(Community c, boolean include) + throws SQLException + { + // Find all the "parent" communities for the community + Community[] parents = c.getAllParents(); + // put into an array in reverse order + int revLength = include ? parents.length + 1 : parents.length; + Community[] reversedParents = new Community[revLength]; + int index = parents.length - 1; + for (int i = 0; i < parents.length; i++) + { + reversedParents[i] = parents[index-i]; + } + if (include) + { + reversedParents[revLength-1] = c; + } + return reversedParents; + } } diff --git a/dspace/src/org/dspace/app/webui/servlet/admin/EditCommunitiesServlet.java b/dspace/src/org/dspace/app/webui/servlet/admin/EditCommunitiesServlet.java index 4773d38208..c45ded3df0 100644 --- a/dspace/src/org/dspace/app/webui/servlet/admin/EditCommunitiesServlet.java +++ b/dspace/src/org/dspace/app/webui/servlet/admin/EditCommunitiesServlet.java @@ -303,9 +303,21 @@ public class EditCommunitiesServlet extends DSpaceServlet { if (request.getParameter("create").equals("true")) { - // We need to create a new community - community = Community.create(context); - + // if there is a parent community id specified, create community + // as its child; otherwise, create it as a top-level community + int parentCommunityID = UIUtil.getIntParameter(request, "parent_community_id"); + if (parentCommunityID != -1) + { + Community parent = Community.find(context, parentCommunityID); + if (parent != null) + { + community = parent.createSubcommunity(); + } + } + else + { + community = Community.create(context); + } // Set attribute request.setAttribute("community", community); } diff --git a/dspace/src/org/dspace/content/Community.java b/dspace/src/org/dspace/content/Community.java index e6eb6227ba..be76536872 100644 --- a/dspace/src/org/dspace/content/Community.java +++ b/dspace/src/org/dspace/content/Community.java @@ -259,6 +259,50 @@ public class Community extends DSpaceObject return communityArray; } + /** + * Get a list of all top-level communities in the system. These are alphabetically + * sorted by community name. A top-level community is one without a parent community. + * + * @param context DSpace context object + * + * @return the top-level communities in the system + */ + public static Community[] findAllTop(Context context) + throws SQLException + { + // get all communities that are not children + TableRowIterator tri = DatabaseManager.query(context, + "community", + "SELECT * FROM community WHERE NOT community_id IN " + + "(SELECT child_comm_id FROM community2community) " + + "ORDER BY name;" ); + + List topCommunities = new ArrayList(); + + while (tri.hasNext()) + { + TableRow row = tri.next(); + + // First check the cache + Community fromCache = (Community) context.fromCache( + Community.class, row.getIntColumn("community_id")); + + if (fromCache != null) + { + topCommunities.add(fromCache); + } + else + { + topCommunities.add(new Community(context, row)); + } + } + + Community[] communityArray = new Community[topCommunities.size()]; + communityArray = (Community[]) topCommunities.toArray(communityArray); + + return communityArray; + } + /** * Get the internal ID of this collection @@ -443,6 +487,112 @@ public class Community extends DSpaceObject return collectionArray; } + /** + * Get the immediate sub-communities of this community. Throws an SQLException because + * creating a community object won't load in all collections. + * + * @return array of Community objects + */ + public Community[] getSubcommunities() + throws SQLException + { + List subcommunities = new ArrayList(); + + // Get the table rows + TableRowIterator tri = DatabaseManager.query(ourContext, + "community", + "SELECT community.* FROM community, community2community WHERE " + + "community2community.child_comm_id=community.community_id " + + "AND community2community.parent_comm_id=" + getID() + + " ORDER BY community.name" ); + + // Make Community objects + while (tri.hasNext()) + { + TableRow row = tri.next(); + + // First check the cache + Community fromCache = (Community) ourContext.fromCache( + Community.class, row.getIntColumn("community_id")); + + if (fromCache != null) + { + subcommunities.add(fromCache); + } + else + { + subcommunities.add(new Community(ourContext, row)); + } + } + + // Put them in an array + Community[] communityArray = new Community[subcommunities.size()]; + communityArray = (Community[]) subcommunities.toArray(communityArray); + + return communityArray; + } + + /** + * Return the parent community of this community, or null if + * the community is top-level + * @return the immediate parent community, or null if top-level + */ + public Community getParentCommunity() + throws SQLException + { + Community parentCommunity = null; + + // Get the table rows + TableRowIterator tri = DatabaseManager.query(ourContext, + "community", + "SELECT community.* FROM community, community2community WHERE " + + "community2community.parent_comm_id=community.community_id " + + "AND community2community.child_comm_id=" + getID() + ";" ); + + // Make Community object + if (tri.hasNext()) + { + TableRow row = tri.next(); + // First check the cache + Community fromCache = (Community) ourContext.fromCache( + Community.class, row.getIntColumn("community_id")); + + if (fromCache != null) + { + parentCommunity = fromCache; + } + else + { + parentCommunity = new Community(ourContext, row); + } + } + + return parentCommunity; + } + + /** + * Return an array of parent communities of this community, + * in ascending order. If community is top-level, return an empty + * array. + * @return an array of parent communities, empty if top-level + */ + public Community[] getAllParents() + throws SQLException + { + List parentList = new ArrayList(); + Community parent = getParentCommunity(); + while (parent != null) + { + parentList.add(parent); + parent = parent.getParentCommunity(); + } + // Put them in an array + Community[] communityArray = new Community[parentList.size()]; + communityArray = (Community[]) parentList.toArray(communityArray); + + return communityArray; + } + /** * Create a new collection within this community. The collection is @@ -496,6 +646,56 @@ public class Community extends DSpaceObject } } + /** + * Create a new sub-community within this community. + * + * @return the new community + */ + public Community createSubcommunity() + throws SQLException, AuthorizeException + { + // Check authorisation + AuthorizeManager.authorizeAction(ourContext, this, Constants.ADD); + + Community c = create(ourContext); + addSubcommunity(c); + return c; + } + + + /** + * Add an exisiting community as a subcommunity to the community + * + * @param c subcommunity to add + */ + public void addSubcommunity(Community c) + throws SQLException, AuthorizeException + { + // Check authorisation + AuthorizeManager.authorizeAction(ourContext, this, Constants.ADD); + + log.info(LogManager.getHeader(ourContext, + "add_subcommunity", + "parent_comm_id=" + getID() + ",child_comm_id=" + c.getID())); + + // Find out if mapping exists + TableRowIterator tri = DatabaseManager.query(ourContext, + "community2community", + "SELECT * FROM community2community WHERE parent_comm_id=" + + getID() + " AND child_comm_id=" + c.getID() + ";"); + + if (!tri.hasNext()) + { + // No existing mapping, so add one + TableRow mappingRow = DatabaseManager.create(ourContext, + "community2community"); + + mappingRow.setColumn("parent_comm_id", getID()); + mappingRow.setColumn("child_comm_id", c.getID()); + + DatabaseManager.update(ourContext, mappingRow); + } + } /** * Remove a collection. Any items then orphaned are deleted. @@ -529,10 +729,42 @@ public class Community extends DSpaceObject } } + /** + * Remove a subcommunity. Any substructure then orphaned is deleted. + * + * @param c subcommunity to remove + */ + public void removeSubcommunity(Community c) + throws SQLException, AuthorizeException, IOException + { + // Check authorisation + AuthorizeManager.authorizeAction(ourContext, this, Constants.REMOVE); + + log.info(LogManager.getHeader(ourContext, + "remove_subcommunity", + "parent_comm_id=" + getID() + ",child_comm_id=" + c.getID())); + + // Remove any mappings + DatabaseManager.updateQuery(ourContext, + "DELETE FROM community2community WHERE parent_comm_id=" + + getID() + " AND child_comm_id=" + c.getID() + ";"); + + // Is the subcommunity an orphan? + TableRowIterator tri = DatabaseManager.query(ourContext, + "SELECT * FROM community2community WHERE child_comm_id=" + + c.getID()); + + if (!tri.hasNext()) + { + // Orphan; delete it + c.delete(); + } + } + /** * Delete the community, including the metadata and logo. Collections - * that are then orphans are deleted. + * and subcommunities that are then orphans are deleted. */ public void delete() throws SQLException, AuthorizeException, IOException @@ -564,6 +796,14 @@ public class Community extends DSpaceObject removeCollection(cols[i]); } + // Remove subcommunities + Community[] comms = getSubcommunities(); + + for (int j = 0; j < comms.length; j++) + { + removeSubcommunity(comms[j]); + } + // Remove the logo setLogo(null); @@ -601,4 +841,21 @@ public class Community extends DSpaceObject { return Constants.COMMUNITY; } + + /** + * return TRUE if context's user can edit community, false otherwise + * + * @return boolean true = current user can edit community + */ + public boolean canEdit() + throws java.sql.SQLException + { + // can this person write to the community? + if( AuthorizeManager.authorizeActionBoolean(ourContext, this, Constants.WRITE) ) + { + return true; + } + + return false; + } }
+ + + +