Index: lams_admin/.classpath =================================================================== RCS file: /usr/local/cvsroot/lams_admin/.classpath,v diff -u -r1.9 -r1.10 --- lams_admin/.classpath 17 Aug 2012 14:12:31 -0000 1.9 +++ lams_admin/.classpath 16 Jul 2014 20:17:15 -0000 1.10 @@ -17,5 +17,10 @@ + + + + + Index: lams_admin/conf/language/lams/ApplicationResources.properties =================================================================== RCS file: /usr/local/cvsroot/lams_admin/conf/language/lams/ApplicationResources.properties,v diff -u -r1.99 -r1.100 --- lams_admin/conf/language/lams/ApplicationResources.properties 3 Jul 2014 19:39:51 -0000 1.99 +++ lams_admin/conf/language/lams/ApplicationResources.properties 16 Jul 2014 20:17:15 -0000 1.100 @@ -507,5 +507,15 @@ config.user.validation.emails =Enforce properly formatted emails config.cache.refresh =Configuration cache refresh interval (minutes) +tool.groups.open.button =Manage tool groups +tool.groups.open.button.tooltip =Assign tools to groups for later use in Authoring +tool.groups.dialog.title =Tool groups management +tool.groups.dialog.instructions =Manage groups by dragging and dropping tools +tool.groups.add.group.button =Add group +tool.groups.remove.group.button.tooltip =Remove group +tool.groups.group.default.name =Untitled +tool.groups.remove.confirm =Are you sure you want to remove this group? +tool.groups.group.name.error =A group name can not be blank +tool.groups.save.error =Error while saving groups #======= End labels: Exported 500 labels for en AU ===== Index: lams_admin/src/java/org/lamsfoundation/lams/admin/web/ToolContentListAction.java =================================================================== RCS file: /usr/local/cvsroot/lams_admin/src/java/org/lamsfoundation/lams/admin/web/ToolContentListAction.java,v diff -u -r1.6 -r1.7 --- lams_admin/src/java/org/lamsfoundation/lams/admin/web/ToolContentListAction.java 17 Jan 2012 15:48:22 -0000 1.6 +++ lams_admin/src/java/org/lamsfoundation/lams/admin/web/ToolContentListAction.java 16 Jul 2014 20:17:15 -0000 1.7 @@ -23,11 +23,13 @@ /* $Id$ */ package org.lamsfoundation.lams.admin.web; +import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -40,7 +42,12 @@ import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; +import org.apache.tomcat.util.json.JSONArray; +import org.apache.tomcat.util.json.JSONException; +import org.apache.tomcat.util.json.JSONObject; import org.lamsfoundation.lams.admin.service.AdminServiceProxy; +import org.lamsfoundation.lams.learningdesign.LearningLibrary; +import org.lamsfoundation.lams.learningdesign.LearningLibraryGroup; import org.lamsfoundation.lams.learningdesign.dto.LearningLibraryDTO; import org.lamsfoundation.lams.learningdesign.dto.LibraryActivityDTO; import org.lamsfoundation.lams.learningdesign.service.ILearningDesignService; @@ -60,6 +67,7 @@ * @struts:action path="/toolcontentlist" scope="request" validate="false" * * @struts:action-forward name="toolcontentlist" path=".toolcontentlist" + * @struts:action-forward name="groups" path="/toolcontent/learningLibraryGroup.jsp" * @struts.action-forward name="error" path=".error" */ public class ToolContentListAction extends Action { @@ -75,21 +83,24 @@ private static final String ATTRIBUTE_DATABASE_VERSIONS = "dbVersions"; private static final String FORWARD_SUCCESS = "toolcontentlist"; + private static final String FORWARD_GROUPS = "groups"; private static final String FORWARD_ERROR = "error"; private static final String ACTION_ENABLE = "enable"; private static final String ACTION_DISABLE = "disable"; + private static final String ACTION_OPEN_GROUPS = "openLearningLibraryGroups"; + private static final String ACTION_SAVE_GROUPS = "saveLearningLibraryGroups"; private static final String QUERY_DATABASE_VERSIONS = "select system_name, patch_level from patches"; private static ILearningDesignService learningDesignService; private static IUserManagementService userManagementService; private static DataSource dataSource; + @SuppressWarnings("unchecked") @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { - // check permission if (!(request.isUserInRole(Role.SYSADMIN) || request.isUserInRole(Role.AUTHOR_ADMIN))) { request.setAttribute(ToolContentListAction.ATTRIBUTE_ERROR_NAME, "ToolContentListAction"); @@ -99,7 +110,7 @@ "error.authorisation")); return mapping.findForward(ToolContentListAction.FORWARD_ERROR); } - + // not just display, but enable/disable a learning library String param = request.getParameter(ToolContentListAction.PARAM_ACTION); if (StringUtils.equals(param, ToolContentListAction.ACTION_ENABLE)) { @@ -108,14 +119,17 @@ } else { return mapping.findForward(ToolContentListAction.FORWARD_ERROR); } - } else { - if (StringUtils.equals(param, ToolContentListAction.ACTION_DISABLE)) { - if (checkPriviledge(request)) { - disableLibrary(mapping, form, request, response); - } else { - return mapping.findForward(ToolContentListAction.FORWARD_ERROR); - } + } else if (StringUtils.equals(param, ToolContentListAction.ACTION_DISABLE)) { + if (checkPriviledge(request)) { + disableLibrary(mapping, form, request, response); + } else { + return mapping.findForward(ToolContentListAction.FORWARD_ERROR); } + } else if (StringUtils.equals(param, ToolContentListAction.ACTION_OPEN_GROUPS)) { + return openLearningLibraryGroups(mapping, form, request, response); + } else if (StringUtils.equals(param, ToolContentListAction.ACTION_SAVE_GROUPS)) { + saveLearningLibraryGroups(mapping, form, request, response); + return null; } // get learning library dtos and their validity @@ -150,6 +164,7 @@ } // returns full list of learning libraries, valid or not + @SuppressWarnings("unchecked") private ArrayList filterMultipleToolEntries(List learningLibraryDTOs, HashMap learningLibraryValidity) { ArrayList activeTools = new ArrayList(); @@ -220,6 +235,73 @@ } + /** + * Loads groups and libraries and displays the management dialog. + */ + private ActionForward openLearningLibraryGroups(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws JSONException, IOException { + // build full list of available learning libraries + List learningLibraries = getLearningDesignService().getAllLearningLibraryDetails( + getUserLanguage()); + JSONArray learningLibrariesJSON = new JSONArray(); + for (LearningLibraryDTO learningLibrary : learningLibraries) { + JSONObject learningLibraryJSON = new JSONObject(); + learningLibraryJSON.put("learningLibraryId", learningLibrary.getLearningLibraryID()); + learningLibraryJSON.put("title", learningLibrary.getTitle()); + learningLibrariesJSON.put(learningLibraryJSON); + } + request.setAttribute("learningLibraries", learningLibrariesJSON.toString()); + + // build list of existing groups + List groups = getLearningDesignService().getLearningLibraryGroups(); + JSONArray groupsJSON = new JSONArray(); + for (LearningLibraryGroup group : groups) { + JSONObject groupJSON = new JSONObject(); + groupJSON.put("groupId", group.getGroupId()); + groupJSON.put("name", group.getName()); + for (LearningLibrary learningLibrary : group.getLearningLibraries()) { + JSONObject learningLibraryJSON = new JSONObject(); + learningLibraryJSON.put("learningLibraryId", learningLibrary.getLearningLibraryId()); + learningLibraryJSON.put("title", learningLibrary.getTitle()); + groupJSON.append("learningLibraries", learningLibraryJSON); + } + groupsJSON.put(groupJSON); + } + request.setAttribute("groups", groupsJSON.toString()); + + return mapping.findForward(ToolContentListAction.FORWARD_GROUPS); + } + + + private void saveLearningLibraryGroups(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws JSONException, IOException { + // extract groups from JSON and persist them + JSONArray groupsJSON = new JSONArray(request.getParameter("groups")); + List groups = new ArrayList(groupsJSON.length()); + + for (int groupIndex = 0; groupIndex < groupsJSON.length(); groupIndex++) { + LearningLibraryGroup group = new LearningLibraryGroup(); + groups.add(group); + + JSONObject groupJSON = groupsJSON.getJSONObject(groupIndex); + long groupId = groupJSON.optLong("groupId"); + if (groupId > 0) { + group.setGroupId(groupId); + } + group.setName(groupJSON.getString("name")); + + group.setLearningLibraries(new HashSet()); + JSONArray learningLibrariesJSON = groupJSON.getJSONArray("learningLibraries"); + for (int learningLibraryIndex = 0; learningLibraryIndex < learningLibrariesJSON.length(); learningLibraryIndex++) { + long learningLibraryId = learningLibrariesJSON.getLong(learningLibraryIndex); + LearningLibrary learningLibrary = getLearningDesignService().getLearningLibrary(learningLibraryId); + group.getLearningLibraries().add(learningLibrary); + } + } + + getLearningDesignService().saveLearningLibraryGroups(groups); + } + private ILearningDesignService getLearningDesignService() { if (ToolContentListAction.learningDesignService == null) { WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServlet() Index: lams_admin/web/template.jsp =================================================================== RCS file: /usr/local/cvsroot/lams_admin/web/template.jsp,v diff -u -r1.20 -r1.21 --- lams_admin/web/template.jsp 5 Jun 2007 05:53:43 -0000 1.20 +++ lams_admin/web/template.jsp 16 Jul 2014 20:17:15 -0000 1.21 @@ -7,6 +7,7 @@ <fmt:message key="${title}"/> + Index: lams_admin/web/css/learningLibraryGroup.css =================================================================== RCS file: /usr/local/cvsroot/lams_admin/web/css/learningLibraryGroup.css,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ lams_admin/web/css/learningLibraryGroup.css 16 Jul 2014 20:17:15 -0000 1.1 @@ -0,0 +1,93 @@ +html, body { + height: 100%; +} + +div#titleDiv { + padding: 3px 5px; + font-size: small; + font-weight: bold; + border-bottom: thin dotted #2E6E9E; +} + +table#groupsTable { + height: 375px; +} + +table#groupsTable td { + vertical-align: top; +} + +td#learningLibraryCell { + width: 200px; + padding: 5px; + border-right: thin dotted #2E6E9E; +} + +#groupTemplate { + display: none; +} + +#newGroupPlaceholder { + border: thin dashed #2E6E9E; + cursor: pointer; +} + +#newGroupPlaceholder > div { + text-align: center; + margin: 60px 5px 0 5px; +} + +.groupContainer { + float: left; + width: 180px; + height: 150px; + margin: 0 10px 10px 0; + padding: 5px; + border: thin solid #2E6E9E; +} + +.learningLibraryContainerTitle { + font-weight: bold; + text-align: center; +} + +.removeGroupButton { + float: left; + cursor: pointer; + width: 16px; + height: 16px; +} + +.learningLibraryContainer { + overflow: auto; + height: 123px; + margin-top: 5px; +} + +td#learningLibraryCell .learningLibraryContainer { + height: 100%; +} + +.groupContainer input { + width: 135px; + margin-right: 5px; +} + +.groupContainer .removeGroupButton { + padding-top: 3px; +} + +.draggableLearningLibrary { + padding: 3px 0px 3px 0px; + cursor: default; +} + +.droppableHighlight { + padding : 1px !important; + border: 5px solid #5c9ccc !important; +} + +.draggableLearningLibrarySelected { + background-color: #5c9ccc !important; + color: white !important; +} \ No newline at end of file Index: lams_admin/web/includes/javascript/learningLibraryGroup.js =================================================================== RCS file: /usr/local/cvsroot/lams_admin/web/includes/javascript/learningLibraryGroup.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ lams_admin/web/includes/javascript/learningLibraryGroup.js 16 Jul 2014 20:17:15 -0000 1.1 @@ -0,0 +1,248 @@ +var lastSelected = {}; + +$(document).ready(function(){ + // show all learning libraries on the left + fillGroup(learningLibraries, $('#learningLibraryCell')); + // add existing groups + $.each(groups, function(){ + var group = addGroup(this); + }); + + $('#newGroupPlaceholder').click(function(){ + addGroup(); + }); +}); + + +/** + * Add a group and fills it with given learning libraries; + */ +function addGroup(groupData) { + var group = $('#groupTemplate').clone() + .attr({ + // remove template's HTML ID + 'id' : null, + 'groupId' : groupData ? groupData.groupId : null + }) + .css('display', null) + .insertBefore('#newGroupPlaceholder'); + + group.find('input').val(groupData ? groupData.name : LABELS.GROUP_DEFAULT_NAME); + fillGroup(groupData ? groupData.learningLibraries : null, group); + + return group; +} + + +/** + * Makes a list of learning libraries and adds drag&drop functionality to them. + */ +function fillGroup(learningLibraries, container) { + if (learningLibraries) { + // create learning library DIVs + $.each(learningLibraries, function(index, learningLibraryJSON) { + var learningLibraryDiv = createLearningLibraryDiv(learningLibraryJSON.learningLibraryId, learningLibraryJSON.title); + $('.learningLibraryContainer', container).append(learningLibraryDiv); + }); + + colorDraggableLearningLibraries(container); + } + + $(container).droppable({ + 'activeClass' : 'droppableHighlight', + 'tolerance' : 'pointer', + 'drop' : function (event, ui) { + var draggableLearningLibraryContainer = $(ui.draggable).parent(), + thisLearningLibraryContainer = $('.learningLibraryContainer', this); + // do not do anything if it is the same container + // using "accept" feature breaks the layout + if (draggableLearningLibraryContainer[0] != thisLearningLibraryContainer[0]) { + transferLearningLibraries(draggableLearningLibraryContainer, thisLearningLibraryContainer); + } + } + }); + + $('.removeGroupButton', container).click(function(){ + removeGroup(container); + }); +} + + +/** + * Constructs a single learning library label with events. + */ +function createLearningLibraryDiv(learningLibraryId, title) { + return $('
').attr('learningLibraryId', learningLibraryId) + .addClass('draggableLearningLibrary') + .text(title) + .draggable({ + 'appendTo' : 'body', + 'containment' : '#groupsTable', + 'revert' : 'invalid', + 'distance' : 20, + 'cursor' : 'move', + 'helper' : function(event){ + // include the learning library from which dragging started + $(this).addClass('draggableLearningLibrarySelected'); + + // copy selected learning libraries + var helperContainer = $('
'); + $(this).siblings('.draggableLearningLibrarySelected').andSelf().each(function(){ + $(this).clone().appendTo(helperContainer); + }); + return helperContainer; + } + }) + + .click(function(event){ + var wasSelected = $(this).hasClass('draggableLearningLibrarySelected'), + parentId = $(this).parent().parent().attr('id'), + // this is needed for shift+click + lastSelectedLearningLibrary = lastSelected[parentId]; + + if (event.shiftKey && lastSelectedLearningLibrary && lastSelectedLearningLibrary != this) { + // clear current selection + $(this).siblings().andSelf().removeClass('draggableLearningLibrarySelected'); + + // find range of learning libraries to select + var lastSelectedIndex = $(lastSelectedLearningLibrary).index(), + index = $(this).index(). + startingElem = lastSelectedIndex > index ? this : lastSelectedLearningLibrary, + endingElem = lastSelectedIndex > index ? lastSelectedLearningLibrary : this; + + $(startingElem).nextUntil(endingElem).andSelf().add(endingElem) + .addClass('draggableLearningLibrarySelected'); + } else { + if (!event.ctrlKey) { + // clear current sleection + $(this).siblings().andSelf().removeClass('draggableLearningLibrarySelected'); + } + + if (wasSelected && !event.shiftKey){ + $(this).removeClass('draggableLearningLibrarySelected'); + lastSelected[parentId] = null; + } else { + $(this).addClass('draggableLearningLibrarySelected'); + lastSelected[parentId] = this; + } + } + }); +} + + +/** + * Move learning library DIVs from one group to another + */ +function transferLearningLibraries(fromContainer, toContainer) { + var selectedLearningLibraries = $('.draggableLearningLibrarySelected', fromContainer); + if (selectedLearningLibraries.length > 0){ + if (toContainer.parent().attr('id') == 'learningLibraryCell'){ + // just remove the selected items as they already exist in the full learning library list + selectedLearningLibraries.remove(); + } else { + // find out which learning libraries are already there and do not move them + var filteredLearningLibraries = [], + existingLearningLibraries = $('.draggableLearningLibrary', toContainer), + isAdd = fromContainer.parent().attr('id') == 'learningLibraryCell'; + $.each(selectedLearningLibraries, function(){ + var selectedLearningLibrary = $(this); + $.each(existingLearningLibraries, function(){ + if ($(this).attr('learningLibraryId') == selectedLearningLibrary.attr('learningLibraryId')){ + selectedLearningLibrary = null; + return false; + } + }); + if (selectedLearningLibrary) { + if (isAdd) { + // the selected items were pulled out from the full library list, + // so do not remove them from the source container + selectedLearningLibrary = createLearningLibraryDiv(selectedLearningLibrary.attr('learningLibraryId'), + selectedLearningLibrary.text()); + } + filteredLearningLibraries.push(selectedLearningLibrary); + } + }); + + // move the selected learning libraries + $.each(filteredLearningLibraries, function(){ + $(this).css({'top' : '0px', + 'left' : '0px', + }).removeClass('draggableLearningLibrarySelected') + .appendTo(toContainer); + }); + } + // recolour both containers + colorDraggableLearningLibraries(toContainer); + colorDraggableLearningLibraries(fromContainer); + } +} + + +function colorDraggableLearningLibraries(container) { + // every second line is different + $(container).find('div.draggableLearningLibrary').each(function(index, learningLibraryDiv){ + // exact colour should be defined in CSS, but it's easier this way... + $(this).css('background-color', index % 2 ? '#dfeffc' : 'inherit'); + }); +} + + +/** + * Remove a group from the page. + */ +function removeGroup(container) { + if (confirm(LABELS.GROUP_REMOVE_CONFIRM)){ + container.remove(); + } +} + + +/** + * Save groups and let parent window that it can close the dialog + */ +function saveGroups(){ + var groups = [], + result = false; + + $('#groupsTable .groupContainer').not('#newGroupPlaceholder').each(function(){ + var learningLibraries = $('div.draggableLearningLibrary', this), + learningLibraryIds = [], + name = $('input', this).val(); + if (!name || name.trim() == '') { + alert(LABELS.GROUP_NAME_VALIDATION_ERROR); + groups = null; + return false; + } + $.each(learningLibraries, function(){ + learningLibraryIds.push($(this).attr('learningLibraryId')); + }); + + // add the group JSON + groups.push({ + 'groupId' : $(this).attr('groupId'), + 'name' : name, + 'learningLibraries' : learningLibraryIds + }); + }); + + if (groups){ + $.ajax({ + 'async' : false, + 'cache' : false, + 'url' : 'toolcontentlist.do', + 'data' : { + 'action' : 'saveLearningLibraryGroups', + 'groups' : JSON.stringify(groups) + }, + 'type' : 'POST', + 'success' : function(){ + result = true; + }, + 'error' : function(){ + alert(LABELS.SAVE_ERROR); + } + }); + } + + return result; +} \ No newline at end of file Index: lams_admin/web/toolcontent/learningLibraryGroup.jsp =================================================================== RCS file: /usr/local/cvsroot/lams_admin/web/toolcontent/learningLibraryGroup.jsp,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ lams_admin/web/toolcontent/learningLibraryGroup.jsp 16 Jul 2014 20:17:14 -0000 1.1 @@ -0,0 +1,57 @@ +<%@ page contentType="text/html; charset=utf-8" language="java"%> +<%@ taglib uri="tags-lams" prefix="lams"%> +<%@ taglib uri="tags-fmt" prefix="fmt"%> + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+
+
+
+
+
+
+ + +
+
+ + +
+
+
+ +
\ No newline at end of file Index: lams_admin/web/toolcontent/toolcontentlist.jsp =================================================================== RCS file: /usr/local/cvsroot/lams_admin/web/toolcontent/toolcontentlist.jsp,v diff -u -r1.9 -r1.10 --- lams_admin/web/toolcontent/toolcontentlist.jsp 17 Jan 2012 15:48:22 -0000 1.9 +++ lams_admin/web/toolcontent/toolcontentlist.jsp 16 Jul 2014 20:17:14 -0000 1.10 @@ -1,5 +1,40 @@ <%@ include file="/taglibs.jsp"%> + + + + +

@@ -12,6 +47,16 @@

+ + + + Index: lams_build/lib/lams/lams-central.jar =================================================================== RCS file: /usr/local/cvsroot/lams_build/lib/lams/lams-central.jar,v diff -u -r1.80 -r1.81 Binary files differ Index: lams_build/lib/lams/lams.jar =================================================================== RCS file: /usr/local/cvsroot/lams_build/lib/lams/lams.jar,v diff -u -r1.419 -r1.420 Binary files differ Index: lams_central/conf/language/lams/ApplicationResources.properties =================================================================== RCS file: /usr/local/cvsroot/lams_central/conf/language/lams/ApplicationResources.properties,v diff -u -r1.137 -r1.138 --- lams_central/conf/language/lams/ApplicationResources.properties 1 Jul 2014 14:12:20 -0000 1.137 +++ lams_central/conf/language/lams/ApplicationResources.properties 16 Jul 2014 20:17:07 -0000 1.138 @@ -635,4 +635,5 @@ authoring.fla.page.download.wait =Please wait for the download. authoring.fla.page.download.close =Close the dialog when the download is finished. authoring.fla.page.svg.generator.title =SVG Generator +authoring.fla.tool.groups.all =All #======= End labels: Exported 436 labels for en AU ===== Index: lams_central/src/java/org/lamsfoundation/lams/authoring/web/AuthoringAction.java =================================================================== RCS file: /usr/local/cvsroot/lams_central/src/java/org/lamsfoundation/lams/authoring/web/AuthoringAction.java,v diff -u -r1.52 -r1.53 --- lams_central/src/java/org/lamsfoundation/lams/authoring/web/AuthoringAction.java 24 Jun 2014 12:34:58 -0000 1.52 +++ lams_central/src/java/org/lamsfoundation/lams/authoring/web/AuthoringAction.java 16 Jul 2014 20:17:07 -0000 1.53 @@ -52,6 +52,8 @@ import org.lamsfoundation.lams.authoring.service.IAuthoringService; import org.lamsfoundation.lams.learningdesign.LearningDesign; import org.lamsfoundation.lams.learningdesign.LearningDesignAccess; +import org.lamsfoundation.lams.learningdesign.LearningLibrary; +import org.lamsfoundation.lams.learningdesign.LearningLibraryGroup; import org.lamsfoundation.lams.learningdesign.dto.LearningDesignDTO; import org.lamsfoundation.lams.learningdesign.dto.LicenseDTO; import org.lamsfoundation.lams.learningdesign.dto.ValidationErrorDTO; @@ -71,8 +73,6 @@ import org.lamsfoundation.lams.usermanagement.exception.WorkspaceFolderException; import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; import org.lamsfoundation.lams.util.CentralConstants; -import org.lamsfoundation.lams.util.Configuration; -import org.lamsfoundation.lams.util.ConfigurationKeys; import org.lamsfoundation.lams.util.FileUtil; import org.lamsfoundation.lams.util.FileUtilException; import org.lamsfoundation.lams.util.WebUtil; @@ -120,18 +120,31 @@ } public ActionForward openAuthoring(ActionMapping mapping, ActionForm form, HttpServletRequest request, - HttpServletResponse response) throws IOException { - request.setAttribute("tools", getLearningDesignService().getToolDTOs(true, request.getRemoteUser())); + HttpServletResponse response) throws IOException, JSONException { request.setAttribute(AttributeNames.PARAM_CONTENT_FOLDER_ID, FileUtil.generateUniqueContentFolderID()); + + request.setAttribute("tools", getLearningDesignService().getToolDTOs(true, request.getRemoteUser())); + // build list of existing learning library groups + List groups = getLearningDesignService().getLearningLibraryGroups(); + JSONArray groupsJSON = new JSONArray(); + for (LearningLibraryGroup group : groups) { + JSONObject groupJSON = new JSONObject(); + groupJSON.put("name", group.getName()); + for (LearningLibrary learningLibrary : group.getLearningLibraries()) { + groupJSON.append("learningLibraries", learningLibrary.getLearningLibraryId()); + } + groupsJSON.put(groupJSON); + } + request.setAttribute("learningLibraryGroups", groupsJSON.toString()); List accessList = getAuthoringService().getLearningDesignAccessByUser(getUserId()); accessList = accessList.subList(0, Math.min(accessList.size(), AuthoringAction.LEARNING_DESIGN_ACCESS_ENTRIES_LIMIT - 1)); Gson gson = new GsonBuilder().create(); request.setAttribute("access", gson.toJson(accessList)); - + request.setAttribute("licenses", getAuthoringService().getAvailableLicenses()); - + return mapping.findForward("openAutoring"); } Index: lams_central/web/authoring/authoring.jsp =================================================================== RCS file: /usr/local/cvsroot/lams_central/web/authoring/authoring.jsp,v diff -u -r1.5 -r1.6 --- lams_central/web/authoring/authoring.jsp 1 Jul 2014 14:12:20 -0000 1.5 +++ lams_central/web/authoring/authoring.jsp 16 Jul 2014 20:17:07 -0000 1.6 @@ -154,6 +154,7 @@ activitiesOnlySelectable = false, initContentFolderID = '${contentFolderID}', initLearningDesignID = '${param.learningDesignID}', + learningLibraryGroups = ${learningLibraryGroups}, initAccess = ${access}; @@ -261,7 +262,10 @@
-
+ +
0) { - group.insertBefore(newGroupPlaceholder); - } else { - // there is no placeholder in read-only mode - $('#groupsCell').append(group); - } - - fillGroup(users, group); - return group; } /** - * Makes a list of users and adds drag&drop functionality to them. + * Makes a list of learning libraries and adds drag&drop functionality to them. */ -function fillGroup(users, container) { - // make calls to server or just create users in HTML - var createOnServer = lessonMode && !skipAssigningWhenCreatingGroup - && container.attr('id') != 'unassignedUserCell'; - if (users) { - // create user DIVs - $.each(users, function(index, userJSON) { - var userDiv = $('
').attr('userId', userJSON.id) - .addClass('draggableUser') - .text(userJSON.firstName + ' ' + userJSON.lastName - + ' (' + userJSON.login + ')' - ); - - // if teacher can not edit, then no drag&drop is available - if (canEdit) { - userDiv.draggable({ - 'appendTo' : 'body', - 'containment' : '#groupsTable', - 'revert' : 'invalid', - 'distance' : 20, - 'scroll' : false, - 'cursor' : 'move', - 'helper' : function(event){ - // include the user from which dragging started - $(this).addClass('draggableUserSelected'); - - // copy selected users - var helperContainer = $('
'); - $(this).siblings('.draggableUserSelected').andSelf().each(function(){ - $(this).clone().appendTo(helperContainer); - }); - return helperContainer; - } - }) - - .click(function(event){ - var wasSelected = $(this).hasClass('draggableUserSelected'); - var parentId = $(this).parent().parent().attr('id'); - // this is needed for shift+click - var lastSelectedUser = lastSelectedUsers[parentId]; - - if (event.shiftKey && lastSelectedUser && lastSelectedUser != this) { - // clear current selection - $(this).siblings().andSelf().removeClass('draggableUserSelected'); - - // find range of users to select - var lastSelectedIndex = $(lastSelectedUser).index(); - var index = $(this).index(); - - var startingElem = lastSelectedIndex > index ? this : lastSelectedUser; - var endingElem = lastSelectedIndex > index ? lastSelectedUser : this; - - $(startingElem).nextUntil(endingElem).andSelf().add(endingElem) - .addClass('draggableUserSelected'); - } else { - if (!event.ctrlKey) { - // clear current sleection - $(this).siblings().andSelf().removeClass('draggableUserSelected'); - } - - if (wasSelected && !event.shiftKey){ - $(this).removeClass('draggableUserSelected'); - lastSelectedUsers[parentId] = null; - } else { - $(this).addClass('draggableUserSelected'); - lastSelectedUsers[parentId] = this; - } - } - }); - } - - if (createOnServer) { - // copy course groups as lesson groups, creating new instances - if (assignUsersToGroup([userJSON.id], container)) { - $('.userContainer', container).append(userDiv); - } else { - // if it fails, put them in unassigned list - $('#unassignedUserCell .userContainer').append(userDiv); - } - } else { - $('.userContainer', container).append(userDiv); - } +function fillGroup(learningLibraries, container) { + if (learningLibraries) { + // create learning library DIVs + $.each(learningLibraries, function(index, learningLibraryJSON) { + var learningLibraryDiv = createLearningLibraryDiv(learningLibraryJSON.learningLibraryId, learningLibraryJSON.title); + $('.learningLibraryContainer', container).append(learningLibraryDiv); }); - sortUsers(container); - } else if (createOnServer) { - // just createa an empty group - assignUsersToGroup(null, container); + colorDraggableLearningLibraries(container); } - $('.sortUsersButton', container).click(function(){ - sortUsers(container); + $(container).droppable({ + 'activeClass' : 'droppableHighlight', + 'tolerance' : 'pointer', + 'drop' : function (event, ui) { + var draggableLearningLibraryContainer = $(ui.draggable).parent(), + thisLearningLibraryContainer = $('.learningLibraryContainer', this); + // do not do anything if it is the same container + // using "accept" feature breaks the layout + if (draggableLearningLibraryContainer[0] != thisLearningLibraryContainer[0]) { + transferLearningLibraries(draggableLearningLibraryContainer, thisLearningLibraryContainer); + } + } }); - if (canEdit) { - $(container).droppable({ - 'activeClass' : 'droppableHighlight', - 'tolerance' : 'pointer', - 'drop' : function (event, ui) { - var draggableUserContainer = $(ui.draggable).parent(); - var thisUserContainer = $('.userContainer', this); - // do not do anything if it is the same container - // using "accept" feature breaks the layout - if (draggableUserContainer[0] != thisUserContainer[0]) { - var executeDrop = !lessonMode; - if (!executeDrop) { - var transferToLocked = $(this).hasClass('locked'); - // make sure user wants to transfer learners to a group which is already in use - executeDrop = !transferToLocked || confirm(LABELS.TRANSFER_LOCKED_LABEL); - if (executeDrop) { - var userIds = []; - $('div.draggableUserSelected', draggableUserContainer).each(function(){ - userIds.push($(this).attr('userId')); - }); - // execute transfer on server side - executeDrop = assignUsersToGroup(userIds, $(this)); - } - } - - if (executeDrop) { - transferUsers(draggableUserContainer, thisUserContainer); - } - } - } - }); - + $('.removeGroupButton', container).click(function(){ + removeGroup(container); + }); +} + + +function createLearningLibraryDiv(learningLibraryId, title) { + return $('
').attr('learningLibraryId', learningLibraryId) + .addClass('draggableLearningLibrary') + .text(title) + .draggable({ + 'appendTo' : 'body', + 'containment' : '#groupsTable', + 'revert' : 'invalid', + 'distance' : 20, + 'cursor' : 'move', + 'helper' : function(event){ + // include the learning library from which dragging started + $(this).addClass('draggableLearningLibrarySelected'); + + // copy selected learning libraries + var helperContainer = $('
'); + $(this).siblings('.draggableLearningLibrarySelected').andSelf().each(function(){ + $(this).clone().appendTo(helperContainer); + }); + return helperContainer; + } + }) + + .click(function(event){ + var wasSelected = $(this).hasClass('draggableLearningLibrarySelected'), + parentId = $(this).parent().parent().attr('id'), + // this is needed for shift+click + lastSelectedLearningLibrary = lastSelected[parentId]; - $('.removeGroupButton', container).click(function(){ - removeGroup(container); - }); - - if (lessonMode) { - $('input', container).blur(function(){ - renameGroup(container); - }); + if (event.shiftKey && lastSelectedLearningLibrary && lastSelectedLearningLibrary != this) { + // clear current selection + $(this).siblings().andSelf().removeClass('draggableLearningLibrarySelected'); + + // find range of learning libraries to select + var lastSelectedIndex = $(lastSelectedLearningLibrary).index(), + index = $(this).index(). + startingElem = lastSelectedIndex > index ? this : lastSelectedLearningLibrary, + endingElem = lastSelectedIndex > index ? lastSelectedLearningLibrary : this; + + $(startingElem).nextUntil(endingElem).andSelf().add(endingElem) + .addClass('draggableLearningLibrarySelected'); + } else { + if (!event.ctrlKey) { + // clear current sleection + $(this).siblings().andSelf().removeClass('draggableLearningLibrarySelected'); + } + + if (wasSelected && !event.shiftKey){ + $(this).removeClass('draggableLearningLibrarySelected'); + lastSelected[parentId] = null; + } else { + $(this).addClass('draggableLearningLibrarySelected'); + lastSelected[parentId] = this; + } } - } + }); } + /** - * Move user DIVs from one group to another + * Move learning library DIVs from one group to another */ -function transferUsers(fromContainer, toContainer) { - var selectedUsers = $('.draggableUserSelected', fromContainer); - var locked = toContainer.parent().hasClass('locked'); - if (selectedUsers.length > 0){ - // move the selected users - selectedUsers.each(function(){ - $(this).css({'top' : '0px', - 'left' : '0px', - }).removeClass('draggableUserSelected') - .appendTo(toContainer); - - if (locked) { - $(this).draggable('disable'); - } - }); - +function transferLearningLibraries(fromContainer, toContainer) { + var selectedLearningLibraries = $('.draggableLearningLibrarySelected', fromContainer); + if (selectedLearningLibraries.length > 0){ + if (toContainer.parent().attr('id') == 'learningLibraryCell'){ + selectedLearningLibraries.remove(); + } else { + var filteredLearningLibraries = [], + existingLearningLibraries = $('.draggableLearningLibrary', toContainer), + isAdd = fromContainer.parent().attr('id') == 'learningLibraryCell'; + $.each(selectedLearningLibraries, function(){ + var selectedLearningLibrary = $(this); + $.each(existingLearningLibraries, function(){ + if ($(this).attr('learningLibraryId') == selectedLearningLibrary.attr('learningLibraryId')){ + selectedLearningLibrary = null; + return false; + } + }); + if (selectedLearningLibrary) { + if (isAdd) { + selectedLearningLibrary = createLearningLibraryDiv(selectedLearningLibrary.attr('learningLibraryId'), + selectedLearningLibrary.text()); + } + filteredLearningLibraries.push(selectedLearningLibrary); + } + }); + + // move the selected learning libraries + $.each(filteredLearningLibraries, function(){ + $(this).css({'top' : '0px', + 'left' : '0px', + }).removeClass('draggableLearningLibrarySelected') + .appendTo(toContainer); + }); + } // recolour both containers - colorDraggableUsers(toContainer); - colorDraggableUsers(fromContainer); + colorDraggableLearningLibraries(toContainer); + colorDraggableLearningLibraries(fromContainer); } } -function colorDraggableUsers(container) { +function colorDraggableLearningLibraries(container) { // every second line is different - $(container).find('div.draggableUser').each(function(index, userDiv){ + $(container).find('div.draggableLearningLibrary').each(function(index, learningLibraryDiv){ // exact colour should be defined in CSS, but it's easier this way... $(this).css('background-color', index % 2 ? '#dfeffc' : 'inherit'); }); } /** - * Sort users according to their names. + * Remove a group from the page. */ -function sortUsers(container) { - var containerId = container.attr('id'); - var users = $('div.draggableUser', container); - if (users.length > 1) { - var sortOrderAsc = sortOrderAscending[containerId]; - - users.each(function(){ - $(this).detach(); - }).sort(function(a, b){ - var keyA = $(a).text().toLowerCase(); - var keyB = $(b).text().toLowerCase(); - var result = keyA > keyB ? 1 : keyA < keyB ? -1 : 0; - return sortOrderAsc ? -result : result; - }).each(function(){ - $(this).appendTo($('.userContainer', container)); - }); - - var button = container.find('.sortUsersButton'); - if (sortOrderAsc) { - button.html('▼'); - sortOrderAscending[containerId] = false; - } else { - button.html('▲'); - sortOrderAscending[containerId] = true; - } - - colorDraggableUsers(container); - } -} - -/** - * Remove a group both from the server and the page. - */ function removeGroup(container) { - // no groupId means this group was just added and it was not persisted in DB yet - var groupId = container.attr('groupId'); - var executeDelete = true; - if (groupId) { - executeDelete = confirm(LABELS.GROUP_REMOVE_LABEL); + if (confirm(LABELS.GROUP_REMOVE_CONFIRM)){ + container.remove(); } - if (executeDelete) { - executeDelete = !lessonMode; - - if (lessonMode) { - $.ajax({ - async : false, - cache : false, - dataType : 'json', - url : LAMS_URL + 'monitoring/grouping.do', - data : { - 'method' : 'removeGroupJSON', - 'activityID' : groupingActivityId, - 'groupID' : groupId - }, - type : 'POST', - success : function(response) { - executeDelete = response.result; - } - }); - } - - if (executeDelete) { - $('#unassignedUserCell .userContainer').append($('.userContainer div.draggableUser', container)); - container.remove(); - toggleBackButton(); - } - } } -/** - * Change name of a group both on the server and on the page. - */ -function renameGroup(container) { - var groupId = $(container).attr('groupId'); - var nameInput = $('input', container); - var inputEditable = !nameInput.attr('readonly'); - // only lesson groups which exist on the server need to have their names changed immediatelly - if (lessonMode && groupId && inputEditable) { - var groupName = nameInput.val(); - $.ajax({ - cache : false, - url : LAMS_URL + 'monitoring/grouping.do', - data : { - 'method' : 'changeGroupNameJSON', - 'groupID' : groupId, - 'name' : groupName - }, - type : 'POST' - }); - } -} -/** - * Save a course grouping. - */ function saveGroups(){ - if (!canEdit || lessonMode) { - return false; - } - $('.errorMessage').hide(); + var groups = [], + result = false; - var groupingName = $('#groupingName').val(); - if (!groupingName) { - // course grouping name can not be blank - $('#groupingNameBlankError').show(); - return false; - } - - // ask if removing new, empty groups is OK - var acceptEmptyGroups = true; - var groupContainers = $('#groupsTable .groupContainer').not('#newGroupPlaceholder'); - $.each(groupContainers.not('[groupId]'), function(){ - if ($('div.draggableUser', this).length == 0) { - acceptEmptyGroups = false; - return false; + $('#groupsTable .groupContainer').not('#newGroupPlaceholder').each(function(){ + var learningLibraries = $('div.draggableLearningLibrary', this), + learningLibraryIds = [], + name = $('input', this).val(); + if (!name || name.trim() == '') { + alert('A group name can not be blank'); + groups = null; + return false; } - }); - if (!acceptEmptyGroups) { - acceptEmptyGroups = confirm(LABELS.EMPTY_GROUP_SAVE_LABEL); - } - if (!acceptEmptyGroups) { - return false; - } - - var groupsSaved = false; - var newGrouping = { - 'groupingId' : grouping.groupingId, - 'name' : groupingName, - 'groups' : [] - }; - groupContainers.each(function(){ - var groupId = $(this).attr('groupId'); - var users = $('div.draggableUser', this); - if (!groupId && users.length == 0) { - return true; - } - - var userIds = []; - $.each(users, function(){ - userIds.push($(this).attr('userId')); + $.each(learningLibraries, function(){ + learningLibraryIds.push($(this).attr('learningLibraryId')); }); - // add the group JSON to grouping JSON - newGrouping.groups.push({ - 'groupId' : groupId, - 'name' : $('input', this).val(), - 'users' : userIds + // add the group JSON + groups.push({ + 'groupId' : $(this).attr('groupId'), + 'name' : name, + 'learningLibraries' : learningLibraryIds }); }); - $.ajax({ - async : false, - cache : false, - url : LAMS_URL + 'OrganisationGroup.do', - data : { - 'method' : 'save', - 'organisationID' : grouping.organisationId, - 'grouping' : JSON.stringify(newGrouping) - }, - type : 'POST', - success : function() { - groupsSaved = true; - } - }); - - return groupsSaved; -} - -/** - * Transfer learners between groups on the server. - */ -function assignUsersToGroup(userIds, groupContainer) { - // if removing from any groups, set group ID as -1; server understands this - var groupId = groupContainer.attr('id') == 'unassignedUserCell' ? -1 : groupContainer.attr('groupId'); - // name is only needed when creating a new group, i.e. a group which does not have ID yet - var groupName = groupId ? null : $('input', groupContainer).val(); - var result = false; - - $.ajax({ - async : false, - cache : false, - dataType : 'json', - url : LAMS_URL + 'monitoring/grouping.do', - data : { - 'method' : 'addMembersJSON', - 'activityID' : groupingActivityId, - 'groupID' : groupId, - 'name' : groupName, - 'members' : userIds ? userIds.join() : null - }, - type : 'POST', - success : function(response) { - result = response.result; - if (response.groupId) { - groupContainer.attr('groupId', response.groupId); + if (groups){ + $.ajax({ + 'async' : false, + 'cache' : false, + 'url' : 'toolcontentlist.do', + 'data' : { + 'action' : 'saveLearningLibraryGroups', + 'groups' : JSON.stringify(groups) + }, + 'type' : 'POST', + 'success' : function(){ + result = true; + }, + 'error' : function(){ + alert('Error while saving groups'); } - if (!result && response.locked) { - // the server says that the group became in use while teacher was working with this screen - markGroupLocked($('div[userId="' + userIds[0] + '"]').parents('.groupContainer')); - alert(LABELS.GROUP_LOCK_LABEL); - } - toggleBackButton(); - } - }); + }); + } return result; -} - -/** - * If there are any existing (not new) groups, forbid going back to grouping list. - */ -function toggleBackButton() { - if (lessonMode) { - var backButton = $('#backButton'); - var disabled = $('.groupContainer[groupId]').length > 0; - if (disabled) { - backButton.off('click').button('option', 'disabled', true); - } else { - backButton.click(function(){ - document.location.href = LAMS_URL + 'OrganisationGroup.do?method=viewGroupings&activityID=' - + groupingActivityId + '&organisationID=' + grouping.organisationId; - }).button('option', 'disabled', false); - } - } -} - -/** - * Makes a group almost immutable as server does not allow changed. - */ -function markGroupLocked(container) { - container.addClass('locked'); - $('.removeGroupButton', container).remove(); - // $('input', container).attr('readonly', 'readonly'); - $('div.draggableUser', container).off('click').draggable('disable'); } \ No newline at end of file Index: lams_central/web/includes/javascript/authoring/authoringGeneral.js =================================================================== RCS file: /usr/local/cvsroot/lams_central/web/includes/javascript/authoring/authoringGeneral.js,v diff -u -r1.50 -r1.51 --- lams_central/web/includes/javascript/authoring/authoringGeneral.js 10 Jul 2014 19:57:16 -0000 1.50 +++ lams_central/web/includes/javascript/authoring/authoringGeneral.js 16 Jul 2014 20:17:06 -0000 1.51 @@ -125,6 +125,7 @@ * Draw boxes with Tool Activity templates in the panel on the left. */ initTemplates : function(){ + // store some template data in JS structures $('.template').each(function(){ var learningLibraryID = +$(this).attr('learningLibraryId'), activityCategoryID = +$(this).attr('activityCategoryId'), @@ -161,7 +162,48 @@ if (toolName.text().length > 12){ toolName.css('padding-top', '8px'); } + } + }); + + if (!isReadOnlyMode){ + // create list of learning libraries for each group + var templateContainerCell = $('#templateContainerCell'), + learningLibraryGroupSelect = $('select', templateContainerCell), + allGroup = $('option', learningLibraryGroupSelect), + allTemplates = $('#templateContainerCell .templateContainer').show(); + + learningLibraryGroupSelect.change(function(){ + $('.templateContainer').hide(); + // show DIV with the selected learning libraries group + $('option:selected', this).data('templates').show(); + }); + allGroup.data('templates', allTemplates); + + $.each(learningLibraryGroups, function(){ + var templates = allTemplates.clone().appendTo(templateContainerCell), + learningLibraries = this.learningLibraries; + // cloned everything, now remove ones that are not in the list + $('.template', templates).each(function(){ + var learningLibraryId = $(this).attr('learningLibraryId'), + found = false; + $.each(learningLibraries, function(){ + if (learningLibraryId == this) { + found = true; + return false; + } + }); + + if (!found) { + $(this).remove(); + } + }); + $('