Index: lams_central/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -r1588362bc0e1320c4ddf1ae7f4bb37b2df910b50 -r7b0401fe54636737b72030c60fd9292956e79487 --- lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 1588362bc0e1320c4ddf1ae7f4bb37b2df910b50) +++ lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 7b0401fe54636737b72030c60fd9292956e79487) @@ -490,6 +490,8 @@ authoring.fla.open.button =Open authoring.fla.save.button =Save authoring.fla.import.button =Import +authoring.fla.readonly.checkbox =read-only +authoring.fla.readonly.forbidden =This sequence is read-only. If you want to modify it, you can create its copy. authoring.fla.folder =folder authoring.fla.sequence =sequence authoring.fla.sequence.not.valid =The sequence is not valid.
It needs to be corrected before it can be used in lessons. Index: lams_central/src/java/org/lamsfoundation/lams/authoring/web/AuthoringAction.java =================================================================== diff -u -r51c6e431368c11396aafddf0e3b37c92da2f5edc -r7b0401fe54636737b72030c60fd9292956e79487 --- lams_central/src/java/org/lamsfoundation/lams/authoring/web/AuthoringAction.java (.../AuthoringAction.java) (revision 51c6e431368c11396aafddf0e3b37c92da2f5edc) +++ lams_central/src/java/org/lamsfoundation/lams/authoring/web/AuthoringAction.java (.../AuthoringAction.java) (revision 7b0401fe54636737b72030c60fd9292956e79487) @@ -151,6 +151,10 @@ request.setAttribute("licenses", getAuthoringService().getAvailableLicenses()); + boolean canSetReadOnly = getUserManagementService().isUserSysAdmin() + || getUserManagementService().isUserGlobalGroupAdmin(); + request.setAttribute("canSetReadOnly", canSetReadOnly); + return mapping.findForward("openAutoring"); } @@ -217,6 +221,8 @@ // we'll go from top to bottom Collections.reverse(folderPath); ldJSON.put("folderPath", new JSONArray(gson.toJson(folderPath))); + + ldJSON.put("readOnlyEnabled", true); responseJSON.put("ld", ldJSON); List accessList = getAuthoringService().updateLearningDesignAccessByUser(userId); Index: lams_central/src/java/org/lamsfoundation/lams/workspace/service/WorkspaceManagementService.java =================================================================== diff -u -reeb8faaea5372ccf5445d7172f726931e9f26098 -r7b0401fe54636737b72030c60fd9292956e79487 --- lams_central/src/java/org/lamsfoundation/lams/workspace/service/WorkspaceManagementService.java (.../WorkspaceManagementService.java) (revision eeb8faaea5372ccf5445d7172f726931e9f26098) +++ lams_central/src/java/org/lamsfoundation/lams/workspace/service/WorkspaceManagementService.java (.../WorkspaceManagementService.java) (revision 7b0401fe54636737b72030c60fd9292956e79487) @@ -435,6 +435,7 @@ learningDesignJSON.put("canModify", WorkspaceFolder.OWNER_ACCESS.equals(folderContent.getPermissionCode()) || ((user != null) && isSysAuthorAdmin(user))); + learningDesignJSON.put("readOnly", folderContent.getReadOnly()); result.append("learningDesigns", learningDesignJSON); } } else { @@ -453,7 +454,8 @@ JSONArray result = new JSONArray(); Pattern searchPattern = searchString != null - ? Pattern.compile(Pattern.quote(searchString), Pattern.CASE_INSENSITIVE) : null; + ? Pattern.compile(Pattern.quote(searchString), Pattern.CASE_INSENSITIVE) + : null; FolderContentDTO userFolder = getUserWorkspaceFolder(userID); long numDesigns = 0; Index: lams_central/web/authoring/authoring.jsp =================================================================== diff -u -rf75e35d4e90a3c49f51ba59eebcc1f63d8ee0bcc -r7b0401fe54636737b72030c60fd9292956e79487 --- lams_central/web/authoring/authoring.jsp (.../authoring.jsp) (revision f75e35d4e90a3c49f51ba59eebcc1f63d8ee0bcc) +++ lams_central/web/authoring/authoring.jsp (.../authoring.jsp) (revision 7b0401fe54636737b72030c60fd9292956e79487) @@ -124,6 +124,8 @@ SEQUENCE_EXISTS_ERROR : decoderDiv.html('').text(), SEQUENCE_SAVE_ERROR : decoderDiv.html('').text(), + + READONLY_FORBIDDEN_ERROR : decoderDiv.html('').text(), SVG_SAVE_ERROR : decoderDiv.html('').text(), @@ -278,7 +280,8 @@ CONDITIONS_MAPPING_BROKEN_ERROR : decoderDiv.html('').text() }, - + + canSetReadOnly = ${canSetReadOnly}, isReadOnlyMode = false, activitiesOnlySelectable = false, initContentFolderID = '${contentFolderID}', @@ -555,6 +558,11 @@ + +
Index: lams_central/web/authoring/svgGenerator.jsp =================================================================== diff -u -rcb63a28069a1267691684394eb67c7eee0f77fde -r7b0401fe54636737b72030c60fd9292956e79487 --- lams_central/web/authoring/svgGenerator.jsp (.../svgGenerator.jsp) (revision cb63a28069a1267691684394eb67c7eee0f77fde) +++ lams_central/web/authoring/svgGenerator.jsp (.../svgGenerator.jsp) (revision 7b0401fe54636737b72030c60fd9292956e79487) @@ -47,6 +47,7 @@ }, paperMinWidth = 1000, + canSetReadOnly = false, isReadOnlyMode = true, activitiesOnlySelectable = ${param.selectable eq 'true'}, initLearningDesignID = '${param.learningDesignID}'; Index: lams_central/web/css/_authoring_base.scss =================================================================== diff -u -r18448ae0a8003d82f840fc3f20af8a0b5d197ba1 -r7b0401fe54636737b72030c60fd9292956e79487 --- lams_central/web/css/_authoring_base.scss (.../_authoring_base.scss) (revision 18448ae0a8003d82f840fc3f20af8a0b5d197ba1) +++ lams_central/web/css/_authoring_base.scss (.../_authoring_base.scss) (revision 7b0401fe54636737b72030c60fd9292956e79487) @@ -230,6 +230,10 @@ overflow: auto; } +div#ldStoreDialogContents div#ldStoreDialogTree .readOnly { + font-style: italic; +} + div#ldStoreDialogContents #ldStoreDialogAccessCell { border-top: $border-thin-dotted; border-right: $border-thin-dotted; @@ -281,6 +285,14 @@ width: 99%; } +div#ldStoreDialogContents #ldStoreDialogReadOnlyCheckbox { + vertical-align: top; +} + +div#ldStoreDialogContents #ldStoreDialogReadOnlySpan { + padding-left: 5px; +} + div#ldStoreDialogContents div#ldStoreDialogNameContainer { margin-left: 20px; } Index: lams_central/web/includes/javascript/authoring/authoringGeneral.js =================================================================== diff -u -r8f28a31b3105724e7edfa8dd2df58cf5a0473d77 -r7b0401fe54636737b72030c60fd9292956e79487 --- lams_central/web/includes/javascript/authoring/authoringGeneral.js (.../authoringGeneral.js) (revision 8f28a31b3105724e7edfa8dd2df58cf5a0473d77) +++ lams_central/web/includes/javascript/authoring/authoringGeneral.js (.../authoringGeneral.js) (revision 7b0401fe54636737b72030c60fd9292956e79487) @@ -134,7 +134,8 @@ 'selectEffect' : 'black', 'transition' : 'rgb(119,126,157)', // highlight TBL activities which should be grouped - 'activityRequireGrouping' : 'red' + 'activityRequireGrouping' : 'red', + 'activityReadOnly' : 'red' }, 'defaultTextAttributes' : { @@ -544,6 +545,7 @@ }); $('#ldStoreDialogSaveButton', ldStoreDialogContents).click(function(){ + debugger; var dialog = layout.ldStoreDialog, title = $('#ldStoreDialogNameContainer input', dialog).val().trim(); if (!title) { @@ -556,8 +558,7 @@ return; } - var learningDesignID = null, - folderNode = null, + var folderNode = null, folderID = null, tree = dialog.data('ldTree'), node = tree.getHighlightedNode(); @@ -588,21 +589,27 @@ // if a node is highlighted but user modified the title, // it is considered a new sequence // otherwise check if there is no other sequence with the same name + var nodeData = null; if (folderNode && folderNode.children) { $.each(folderNode.children, function(){ if (this.label == title) { this.highlight(); - learningDesignID = this.data.learningDesignId; + nodeData = this.data; return false; } }); } - if (learningDesignID - && !confirm(LABELS.SEQUENCE_OVERWRITE_CONFIRM)) { + if (nodeData && (!nodeData.canModify || (!canSetReadOnly && nodeData.readOnly))){ + alert(LABELS.READONLY_FORBIDDEN_ERROR); return; } - - var result = GeneralLib.saveLearningDesign(folderID, learningDesignID, title); + if (nodeData && !confirm(LABELS.SEQUENCE_OVERWRITE_CONFIRM)) { + return; + } + var readOnly = (nodeData && !nodeData.canModify) || + (canSetReadOnly && $('#ldStoreDialogReadOnlyCheckbox', dialog).prop('checked')), + learningDesignID = nodeData ? nodeData.learningDesignId : null, + result = GeneralLib.saveLearningDesign(folderID, learningDesignID, title, readOnly); if (result) { GeneralLib.openLearningDesign(); dialog.modal('hide'); @@ -754,8 +761,23 @@ $('.modal-title', layout.ldStoreDialog).text(dialogTitle); var rightButtons = $('#ldStoreDialogRightButtonContainer', layout.ldStoreDialog); $('button', rightButtons).hide(); + $('#ldStoreDialogReadOnlyLabel *', layout.ldStoreDialog).hide(); $('#ldStoreDialogNameContainer, #ldStoreDialogImportPartFrame', layout.ldStoreDialog).hide(); $(shownElementIDs, layout.ldStoreDialog).show(); + + var isOpenDialog = shownElementIDs.indexOf('ldStoreDialogOpenButton') >= 0; + if (isOpenDialog) { + // in open dialog display only information + $('#ldStoreDialogReadOnlySpan', layout.ldStoreDialog).css('color', layout.colors.activityReadOnly); + } else if (canSetReadOnly) { + // the first highlighted folder is user's private folder + $('#ldStoreDialogReadOnlyCheckbox', layout.ldStoreDialog).show() + .prop('disabled', false).prop('checked', false); + $('#ldStoreDialogReadOnlySpan', layout.ldStoreDialog).show().css('color', 'initial'); + } else { + $('#ldStoreDialogReadOnlySpan', layout.ldStoreDialog).css('color', layout.colors.activityReadOnly); + } + }, /** * Extracts a selected activity from another LD. @@ -927,7 +949,7 @@ // make folder contents load dynamically on open tree.setDynamicLoad(function(node, callback){ // load subfolder contents - var childNodeData = MenuLib.getFolderContents(node.data.folderID); + var childNodeData = MenuLib.getFolderContents(node.data.folderID, node.data.canSave, node.data.canHaveReadOnly); if (childNodeData) { $.each(childNodeData, function(){ // create and add a leaf @@ -943,7 +965,8 @@ }); tree.singleNodeHighlight = true; tree.subscribe('clickEvent', function(event) { - var isOpenDialog = $('#ldStoreDialogSaveButton', layout.ldStoreDialog).is(':hidden'); + var isOpenDialog = $('#ldStoreDialogSaveButton', layout.ldStoreDialog).is(':hidden') + nodeData = event.node.data; //prevent item from being deselected on any subsequent clicks if (isOpenDialog && event.node.highlightState == 1) { @@ -953,14 +976,35 @@ //disable edit buttons if no elements is selected $('#ldStoreDialogLeftButtonContainer button', layout.ldStoreDialog) .prop('disabled', event.node.highlightState > 0); - + + if (canSetReadOnly && !isOpenDialog) { + // detect which folders/sequences are marked as read-only + // and which ones are immutable + if (event.node.isLeaf) { + $('#ldStoreDialogReadOnlyCheckbox', layout.ldStoreDialog) + .prop('disabled', !nodeData.canModify || !nodeData.canHaveReadOnly) + .prop('checked', nodeData.readOnly || !nodeData.canModify); + } else { + $('#ldStoreDialogReadOnlyCheckbox', layout.ldStoreDialog) + .prop('disabled', !nodeData.canSave || !nodeData.canHaveReadOnly) + .prop('checked', !nodeData.canSave); + } + } else { + // is this is normal user or open dialog, only show/hide read-only label + if (event.node.isLeaf ? nodeData.readOnly || !nodeData.canModify : !nodeData.canSave){ + $('#ldStoreDialogReadOnlySpan', layout.ldStoreDialog).show(); + } else { + $('#ldStoreDialogReadOnlySpan', layout.ldStoreDialog).hide(); + } + } + // if it's a folder in load sequence dialog - highlight but stop processing - if (isOpenDialog && !event.node.data.learningDesignId){ + if (isOpenDialog && !nodeData.learningDesignId){ return true; } //show LearningDesign thumbnail and title - var learningDesignID = event.node.highlightState == 0 ? +event.node.data.learningDesignId : null, + var learningDesignID = event.node.highlightState == 0 ? +nodeData.learningDesignId : null, title = !isOpenDialog && learningDesignID ? event.node.label : null; GeneralLib.showLearningDesignThumbnail(learningDesignID, title); }); @@ -1642,8 +1686,7 @@ if (force) { layout.ld = { - 'maxUIID' : 0, - 'designType' : null + 'maxUIID' : 0 }; layout.activities = []; layout.regions = []; @@ -1708,7 +1751,8 @@ 'contentFolderID' : ld.contentFolderID, 'title' : ld.title, 'maxUIID' : 0, - 'designType' : ld.designType + 'readOnly' : ld.readOnly, + 'canModify' : ld.copyTypeID == 1 }; if (!isReadOnlyMode) { @@ -1721,6 +1765,8 @@ var arrangeNeeded = false, // if system gate is found, it is Live Edit systemGate = null, + // should we allow the author to enter activity authoring + activitiesReadOnly = !layout.ld.canModify || (!canSetReadOnly && layout.ld.readOnly), branchToBranching = {}, // helper for finding last activity in a branch branchToActivityDefs = {}; @@ -1749,7 +1795,7 @@ activityData.xCoord ? activityData.xCoord : 1, activityData.yCoord ? activityData.yCoord : 1, activityData.activityTitle, - activityData.readOnly, + activityData.readOnly || activitiesReadOnly, activityData.evaluation); // for later reference activityData.activity = activity; @@ -1797,7 +1843,7 @@ activityData.xCoord, activityData.yCoord, activityData.activityTitle, - activityData.readOnly, + activityData.readOnly || activitiesReadOnly, groupingData.groupingID, groupingData.groupingUIID, groupingType, @@ -1827,7 +1873,7 @@ activityData.yCoord, activityData.activityTitle, activityData.description, - activityData.readOnly, + activityData.readOnly || activitiesReadOnly, gateType, activityData.gateStartTimeOffset, activityData.gateActivityCompletionBased); @@ -1846,7 +1892,7 @@ activityData.xCoord, activityData.yCoord, activityData.activityTitle, - activityData.readOnly); + activityData.readOnly || activitiesReadOnly); // for later reference activityData.activity = activity; // for later reference @@ -1861,7 +1907,7 @@ activityData.xCoord, activityData.yCoord, activityData.activityTitle, - activityData.readOnly, + activityData.readOnly || activitiesReadOnly, activityData.minOptions, activityData.maxOptions); break; @@ -1880,7 +1926,7 @@ arrangeNeeded ? 0 : activityData.startXCoord, arrangeNeeded ? 0 : activityData.startYCoord, activityData.activityTitle, - activityData.readOnly, + activityData.readOnly || activitiesReadOnly, branchingType); layout.activities.push(branchingEdge); // for later reference @@ -2652,7 +2698,6 @@ 'licenseText' : $('#ldDescriptionLicenseSelect').val() == "0" || $('#ldDescriptionLicenseSelect option:selected').attr('url') ? null : $('#ldDescriptionLicenseText').val(), - 'designType' : layout.ld.designType, 'systemGate' : systemGate, 'activities' : activities, 'transitions' : transitions, @@ -2724,7 +2769,7 @@ /** * Stores the sequece in database. */ - saveLearningDesign : function(folderID, learningDesignID, title) { + saveLearningDesign : function(folderID, learningDesignID, title, readOnly) { var ld = GeneralLib.prepareLearningDesignData(true); if (!ld) { return false; @@ -2740,6 +2785,7 @@ ld.description = CKEDITOR.instances['ldDescriptionFieldDescription'].getData(); ld.saveMode = layout.ld.learningDesignID && layout.ld.learningDesignID != learningDesignID ? 1 : 0; + ld.readOnly = readOnly; ld.systemGate = null; $.ajax({ Index: lams_central/web/includes/javascript/authoring/authoringMenu.js =================================================================== diff -u -rc1a5012654a6a505a5f8cec825c080f4d7f56a78 -r7b0401fe54636737b72030c60fd9292956e79487 --- lams_central/web/includes/javascript/authoring/authoringMenu.js (.../authoringMenu.js) (revision c1a5012654a6a505a5f8cec825c080f4d7f56a78) +++ lams_central/web/includes/javascript/authoring/authoringMenu.js (.../authoringMenu.js) (revision 7b0401fe54636737b72030c60fd9292956e79487) @@ -338,7 +338,7 @@ /** * Loads subfolders and LDs from the server. */ - getFolderContents : function(folderID) { + getFolderContents : function(folderID, canSave, canHaveReadOnly) { var result = null; $.ajax({ @@ -353,26 +353,35 @@ dataType : 'json', success : function(response) { result = []; - // parse the response; extract folders and LDs if (response.folders) { - $.each(response.folders, function(){ - result.push({'type' : 'text', - 'label' : this.isRunSequencesFolder ? - LABELS.RUN_SEQUENCES_FOLDER : this.name, - 'folderID' : this.folderID, - 'canSave' : this.folderID > 0 && !this.isRunSequencesFolder, - 'canModify' : this.canModify + $.each(response.folders, function(index){ + // folderID == -2 is courses folder + var canSave = this.folderID > 0 && !this.isRunSequencesFolder; + result.push({'type' : 'text', + 'label' : this.isRunSequencesFolder ? + LABELS.RUN_SEQUENCES_FOLDER : this.name, + 'folderID' : this.folderID, + 'isRunSequenceFolder' : this.isRunSequencesFolder, + // either take parent's setting or take 2nd (courses) and 3rd (public) folder + 'canHaveReadOnly' : folderID ? canHaveReadOnly : index > 0, + 'canSave' : canSave, + 'canModify' : this.canModify && !this.isRunSequenceFolder, + 'labelStyle' : 'ygtvlabel' + (!canSave ? ' readOnly' : '') }); }); } if (response.learningDesigns) { $.each(response.learningDesigns, function(){ + var canModify = canSave && this.canModify; result.push({'type' : 'text', 'label' : this.name, 'isLeaf' : true, 'learningDesignId' : this.learningDesignId, - 'canModify' : this.canModify + 'canHaveReadOnly' : canHaveReadOnly, + 'canModify' : canModify, + 'readOnly' : this.readOnly, + 'labelStyle' : 'ygtvlabel' + (this.readOnly || !canModify ? ' readOnly' : '') }); }); } @@ -649,7 +658,11 @@ $('#saveButton').blur(); if (!showDialog && layout.ld.learningDesignID) { - GeneralLib.saveLearningDesign(layout.ld.folderID, layout.ld.learningDesignID, layout.ld.title); + if (!layout.ld.canModify || (!canSetReadOnly && layout.ld.readOnly)) { + alert(LABELS.READONLY_FORBIDDEN_ERROR); + } else { + GeneralLib.saveLearningDesign(layout.ld.folderID, layout.ld.learningDesignID, layout.ld.title, layout.ld.readOnly); + } return; }