diff --git a/assignment/tool/src/java/org/sakaiproject/assignment/tool/AssignmentAction.java b/assignment/tool/src/java/org/sakaiproject/assignment/tool/AssignmentAction.java index 9baaaeef3ee2..7fd542b66935 100644 --- a/assignment/tool/src/java/org/sakaiproject/assignment/tool/AssignmentAction.java +++ b/assignment/tool/src/java/org/sakaiproject/assignment/tool/AssignmentAction.java @@ -9100,24 +9100,13 @@ private void post_save_assignment(RunData data, String postOrSave) { } if ((newAssignment && !a.getDraft()) || (!a.getDraft() && !newAssignment)) { - - Collection aGroups = a.getGroups(); - if (aGroups.size() != 0) { - // If already open - if (openTime.isBefore(Instant.now())) { - eventTrackingService.post(eventTrackingService.newEvent(AssignmentConstants.EVENT_UPDATE_ASSIGNMENT_ACCESS, assignmentReference, true)); - } else { - // Not open yet, delay the event - eventTrackingService.delay(eventTrackingService.newEvent(AssignmentConstants.EVENT_AVAILABLE_ASSIGNMENT, assignmentReference, - true), openTime); - } + // If already open + if (openTime.isBefore(Instant.now())) { + // post new assignment event since it is fully initialized by now + eventTrackingService.post(eventTrackingService.newEvent(AssignmentConstants.EVENT_ADD_ASSIGNMENT, assignmentReference, true)); } else { - if (openTime.isBefore(Instant.now())) { - // post new assignment event since it is fully initialized by now - eventTrackingService.post(eventTrackingService.newEvent(AssignmentConstants.EVENT_ADD_ASSIGNMENT, assignmentReference, true)); - } else { - eventTrackingService.delay(eventTrackingService.newEvent(AssignmentConstants.EVENT_AVAILABLE_ASSIGNMENT, assignmentReference, true), openTime); - } + // Not open yet, delay the event + eventTrackingService.delay(eventTrackingService.newEvent(AssignmentConstants.EVENT_AVAILABLE_ASSIGNMENT, assignmentReference, true), openTime); } } } diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/GradeSummaryTablePanel.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/GradeSummaryTablePanel.java index 8595fd46547b..fe518f4d63dd 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/GradeSummaryTablePanel.java +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/GradeSummaryTablePanel.java @@ -398,12 +398,12 @@ public boolean isVisible() { sakaiRubricButton.setVisible(true); } + String ownerId = studentUuid; if (assignment.getExternallyMaintained()) { sakaiRubricButton.add(AttributeModifier.append("tool-id", AssignmentConstants.TOOL_ID)); String[] bits = assignment.getExternalId().split("/"); if (bits != null && bits.length >= 1) { String assignmentId = bits[bits.length-1]; - String ownerId = studentUuid; if (assignment.getExternalAppName().equals(assignmentService.getToolId())) { try { org.sakaiproject.assignment.api.model.Assignment assignmentsAssignment = assignmentService.getAssignment(assignmentId); @@ -419,7 +419,8 @@ public boolean isVisible() { }).findAny(); if (groupId.isPresent()) { - ownerId = groupId.get(); + String[] groupBits = groupId.get().split("/"); + ownerId = groupBits[groupBits.length-1]; } else { log.error("Assignment {} is a group assignment, but {} was not in any of the groups", assignmentId, studentUuid); } @@ -454,7 +455,7 @@ public boolean isVisible() { } } - sakaiRubricButton.add(AttributeModifier.append("evaluated-item-owner-id", studentUuid)); + sakaiRubricButton.add(AttributeModifier.append("evaluated-item-owner-id", ownerId)); gradeScore.add(sakaiRubricButton); } diff --git a/lessonbuilder/api/src/resources/lessons.properties b/lessonbuilder/api/src/resources/lessons.properties index c2d0d4c80d6a..ce460081329c 100644 --- a/lessonbuilder/api/src/resources/lessons.properties +++ b/lessonbuilder/api/src/resources/lessons.properties @@ -275,10 +275,12 @@ simplepage.additional-instructions-label=Frequently Asked Questions about multim simplepage.additional-website-instructions-label=Frequently Asked Questions about uploading content from ZIP file simplepage.link=Add Link +simplepage.linkTitle=Link Title simplepage.addlink_header=Add A New Link simplepage.addLink_label=URL: simplepage.addLink_label_add=Add a URL: simplepage.addlink_label_name = Custom name to display for URL [optional] +simplepage.addFile_label_name = Custom name for uploaded file [optional] simplepage.addLink_label_add_or=Or add a URL or "embed code" simplepage.editText=Edit This Text simplepage.editItem=Edit @@ -675,8 +677,7 @@ simplepage.format.heading=Display Options simplepage.format.window=Open in New Window simplepage.format.inline=Embed on page simplepage.format.page=Open in New Lessons tool Page with 'next' and 'back' buttons -simplepage.format.item_removed=

ERROR: The item that should have been displayed here has been removed. Please use the "Edit" button next to this item and pick "Change External Tool" to recreate the item or choose a new one.

- +simplepage.format.item_removed_text=The item that should have been displayed here has been removed. Please use the "Edit" button next to this item and pick "Change External Tool" to recreate the item or choose a new one. simplepage.more-tools=More Tools simplepage.forum-descrip=Link to a Forum or Topic simplepage.blti-descrip=Link to a web service that uses the IMS LTI standard to integrate with this system @@ -961,3 +962,7 @@ simplepage.max-file-upload-size=The upload size limit of simplepage.max-file-upload-size-save=MB has been exceeded. Remove one or more files below to proceed. Other files may be uploaded assuming the total file size does not exceed this limit. lessons_comment=Lessons Comment + +lti.tool.missing=Content Item has no LTI tool, please re-select +lti.tool.import.incomplete=LTI tool for this item needs further configuration +lti.tool.is.draft=LTI tool for this item is still draft, needs configuration diff --git a/lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/BltiEntity.java b/lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/BltiEntity.java index 94a21280d79e..e26ea77b0a72 100644 --- a/lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/BltiEntity.java +++ b/lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/BltiEntity.java @@ -33,6 +33,8 @@ import java.util.HashMap; import java.util.Properties; +import org.apache.commons.lang3.StringUtils; + import org.json.simple.JSONObject; import lombok.extern.slf4j.Slf4j; @@ -51,6 +53,7 @@ import org.sakaiproject.site.api.ToolConfiguration; import org.sakaiproject.site.api.SiteService; import org.sakaiproject.tool.api.ToolManager; +import org.sakaiproject.util.ResourceLoader; import org.tsugi.lti.LTIUtil; import org.sakaiproject.lti.util.SakaiLTIUtil; @@ -67,6 +70,7 @@ public class BltiEntity implements LessonEntity, BltiInterface { private static Cache bltiCache = null; protected static final int DEFAULT_EXPIRATION = 10 * 60; + protected static ResourceLoader rb = new ResourceLoader("lessons"); private SimplePageBean simplePageBean; @@ -313,27 +317,37 @@ public String getIcon() { } private String getErrorUrl() { - return "javascript:document.write('" + messageLocator.getMessage("simplepage.format.item_removed").replace("'", "\\'") + "')"; + return "javascript:alert('" + messageLocator.getMessage("simplepage.format.item_removed_text").replace("'", "\\'") + "')"; + } + + public String getEditNote() { + loadContent(); + if ( content == null ) return null; // Lessons will show *deleted* + + if ( tool == null ) { + return rb.getString("lti.tool.missing"); + } + + + String siteId = getSiteId(); + if ( StringUtils.isNotEmpty(siteId) && siteId.equals((String) tool.get(LTIService.LTI_SITE_ID)) + && LTIService.LTI_SECRET_INCOMPLETE.equals((String) tool.get(LTIService.LTI_SECRET)) + && LTIService.LTI_SECRET_INCOMPLETE.equals((String) tool.get(LTIService.LTI_CONSUMERKEY)) ) { + return rb.getString("lti.tool.import.incomplete"); + } + + if ( ltiService.isDraft(tool) ) { + return rb.getString("lti.tool.is.draft"); + } + + return null; } - // TODO: Concern regarding the lack of the returnUrl when this is called public String getUrl() { loadContent(); - // If I return null here, it appears that I cause an NPE in LB if ( content == null ) return getErrorUrl(); String ret = (String) content.get("launch_url"); - if ( ltiService != null && tool != null && ltiService.isMaintain(getSiteId()) - && LTIService.LTI_SECRET_INCOMPLETE.equals((String) tool.get(LTIService.LTI_SECRET)) - && LTIService.LTI_SECRET_INCOMPLETE.equals((String) tool.get(LTIService.LTI_CONSUMERKEY)) ) { - - String toolId = getCurrentTool("sakai.siteinfo"); - if ( toolId != null ) { - ret = editItemUrl(toolId); - ret = ret + "&secretonly=true"; - return ret; - } - } - + if ( ret == null ) return getErrorUrl(); ret = ServerConfigurationService.getServerUrl() + ret; return ret; } @@ -457,6 +471,7 @@ public String doImportTool(String launchUrl, String bltiTitle, String strXml, St // Allow the content item to choose open in popup so the user can change it in the Lessons UI props.setProperty(LTIService.LTI_NEWPAGE, "2"); props.setProperty(LTIService.LTI_XMLIMPORT,strXml); + props.setProperty(LTIService.LTI13,"0"); if (custom != null) props.setProperty(LTIService.LTI_CUSTOM, custom); Object result = ltiService.insertTool(props, simplePageBean.getCurrentSiteId()); diff --git a/lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/LessonEntity.java b/lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/LessonEntity.java index 9b7dbfec14d3..b4550ca7a438 100644 --- a/lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/LessonEntity.java +++ b/lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/LessonEntity.java @@ -112,6 +112,10 @@ public interface LessonEntity { public String getTitle(); public String getDescription(); public String getUrl(); + // Returns a note to display near the link for editors (i.e. like Deleted) + default public String getEditNote() { + return null; + } public Date getDueDate(); // for forums, where we have a hiearchy of topics public int getLevel(); diff --git a/lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/tool/producers/ShowPageProducer.java b/lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/tool/producers/ShowPageProducer.java index a34397817e96..dd6e31e429d4 100644 --- a/lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/tool/producers/ShowPageProducer.java +++ b/lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/tool/producers/ShowPageProducer.java @@ -1530,6 +1530,7 @@ public void printSubpage(List itemList, boolean first, UIBranchC // javascript that prepares // the jQuery dialogs String itemGroupString = null; + String editNote = null; boolean entityDeleted = false; boolean notPublished = false; if (canEditPage) { @@ -1606,19 +1607,18 @@ else if (assignment.notPublished()) UIOutput.make(tableRow, "type", "b"); LessonEntity blti = (bltiEntity == null ? null : bltiEntity.getEntity(i.getSakaiId())); if (blti != null) { - String editUrl = blti.editItemUrl(simplePageBean); - if (editUrl != null) - UIOutput.make(tableRow, "edit-url", editUrl); - UIOutput.make(tableRow, "item-format", i.getFormat()); - - if (i.getHeight() != null) - UIOutput.make(tableRow, "item-height", i.getHeight()); - itemGroupString = simplePageBean.getItemGroupString(i, null, true); - UIOutput.make(tableRow, "item-groups", itemGroupString ); - if (!blti.objectExists()) - entityDeleted = true; - else if (blti.notPublished()) - notPublished = true; + String editUrl = blti.editItemUrl(simplePageBean); + editNote = blti.getEditNote(); + if (editUrl != null) UIOutput.make(tableRow, "edit-url", editUrl); + UIOutput.make(tableRow, "item-format", i.getFormat()); + + if (i.getHeight() != null) UIOutput.make(tableRow, "item-height", i.getHeight()); + itemGroupString = simplePageBean.getItemGroupString(i, null, true); + UIOutput.make(tableRow, "item-groups", itemGroupString ); + if (!blti.objectExists()) + entityDeleted = true; + else if (blti.notPublished()) + notPublished = true; } } else if (i.getType() == SimplePageItem.FORUM) { UIOutput.make(tableRow, "extra-info"); @@ -1656,6 +1656,7 @@ else if (forum.notPublished()) } // end of canEditPage + if (i.getType() == SimplePageItem.PAGE) { UIOutput.make(tableRow, "type", "page"); UIOutput.make(tableRow, "page-next", Boolean.toString(i.getNextPage())); @@ -1708,8 +1709,9 @@ else if (lessonEntity.notPublished()) notPublished = true; break; case SimplePageItem.BLTI: - if (bltiEntity != null) - lessonEntity = bltiEntity.getEntity(i.getSakaiId()); + if (bltiEntity != null) { + lessonEntity = bltiEntity.getEntity(i.getSakaiId()); + } if (lessonEntity != null) itemGroupString = simplePageBean.getItemGroupString(i, null, true); if (!lessonEntity.objectExists()) @@ -1749,6 +1751,10 @@ else if (lessonEntity.notPublished()) else itemGroupString = messageLocator.getMessage("simplepage.not-published"); } + if ( StringUtils.isNotEmpty(editNote) ) { + if (StringUtils.isEmpty(itemGroupString) ) itemGroupString= ""; + itemGroupString = itemGroupString + " " + editNote; + } if (entityDeleted) { if (itemGroupString != null) itemGroupString = itemGroupString + " " + diff --git a/lessonbuilder/tool/src/webapp/js/show-page.js b/lessonbuilder/tool/src/webapp/js/show-page.js index 8d622e0e2320..b0afdc70944a 100644 --- a/lessonbuilder/tool/src/webapp/js/show-page.js +++ b/lessonbuilder/tool/src/webapp/js/show-page.js @@ -3253,9 +3253,9 @@ $(function () { if (i === 0 && previousTitle){ valueContent = 'value="' + previousTitle + '"'; } - newStuff = newStuff + '

'; + newStuff = newStuff + '

'; } else { - newStuff = newStuff + '
'; + newStuff = newStuff + '
'; } newStuff = newStuff + '

' lastInput.after(newStuff); diff --git a/lessonbuilder/tool/src/webapp/templates/ShowPage.html b/lessonbuilder/tool/src/webapp/templates/ShowPage.html index 55fc803147da..e7058da09957 100644 --- a/lessonbuilder/tool/src/webapp/templates/ShowPage.html +++ b/lessonbuilder/tool/src/webapp/templates/ShowPage.html @@ -3329,6 +3329,8 @@

+

+

diff --git a/lti/lti-api/src/java/org/sakaiproject/lti/api/LTIService.java b/lti/lti-api/src/java/org/sakaiproject/lti/api/LTIService.java index 33de3cb88c7f..d3e060d9a0b3 100644 --- a/lti/lti-api/src/java/org/sakaiproject/lti/api/LTIService.java +++ b/lti/lti-api/src/java/org/sakaiproject/lti/api/LTIService.java @@ -348,6 +348,9 @@ public interface LTIService extends LTISubstitutionsFilter { String validateTool(Map newProps); + // Returns whether or not a tool needs further configuration + boolean isDraft(Map tool); + Object insertTool(Properties newProps, String siteId); Object insertTool(Map newProps, String siteId); diff --git a/lti/lti-common/src/java/org/sakaiproject/lti/util/SakaiLTIUtil.java b/lti/lti-common/src/java/org/sakaiproject/lti/util/SakaiLTIUtil.java index 667cb2b4bc8f..f8bcbee69312 100644 --- a/lti/lti-common/src/java/org/sakaiproject/lti/util/SakaiLTIUtil.java +++ b/lti/lti-common/src/java/org/sakaiproject/lti/util/SakaiLTIUtil.java @@ -1178,7 +1178,7 @@ public static String[] postLaunchHTML(Map content, Map" + getRB(rb, "error.tool.partial", "Tool item is incomplete, missing a key and secret.") + "

"); } - boolean isLTI13 = isLTI13(tool, content); + boolean isLTI13 = isLTI13(tool); log.debug("isLTI13={}", isLTI13); @@ -1488,7 +1488,7 @@ public static String[] postContentItemSelectionRequest(Long toolKey, Map deserializeMap(String mapSer) { } /** - * Converts a string from a semicolon-separated list of legacy lti roles mapped ot a modern LTI role to a + * Converts a string from a semicolon-separated list of legacy lti roles mapped to a modern LTI role to a * Map. Each role mapping should be of the form: * Learner=http://purl.imsglobal.org/vocab/lis/v2/membership#Learner */ @@ -3481,19 +3481,28 @@ public static Map convertLegacyRoleMapPropToMap(String roleMapPr } /** - * Check if we are an LTI 1.3 launch or not + * Check if we are an LTI 1.1 launch or not */ - public static boolean isLTI13(Map tool, Map content) { - // 0=inherit from tool, 1=LTI 1.1, 2=LTI 1.3 - if ( content != null ) { - Long contentLTI13 = Foorm.getLong(content.get(LTIService.LTI13)); - if ( contentLTI13.equals(2L)) return true; - if ( contentLTI13.equals(1L)) return false; - } + public static boolean isLTI11(Map tool) { + if ( tool == null ) return false; + Long toolLTI13 = Foorm.getLong(tool.get(LTIService.LTI13)); + if ( toolLTI13.equals(LTIService.LTI13_LTI11) ) return true; + if ( toolLTI13.equals(LTIService.LTI13_LTI13) ) return false; + if ( toolLTI13.equals(LTIService.LTI13_BOTH) ) return true; + return true; + } + + /** + * Check if we are an LTI 1.3 launch or not + */ + public static boolean isLTI13(Map tool) { if ( tool == null ) return false; Long toolLTI13 = Foorm.getLong(tool.get(LTIService.LTI13)); - return ! toolLTI13.equals(0L); + if ( toolLTI13.equals(LTIService.LTI13_LTI11) ) return false; + if ( toolLTI13.equals(LTIService.LTI13_LTI13) ) return true; + if ( toolLTI13.equals(LTIService.LTI13_BOTH) ) return true; + return false; // Default of null or other funky value is LTI 1.1 } /** diff --git a/lti/lti-impl/src/java/org/sakaiproject/lti/impl/BaseLTIService.java b/lti/lti-impl/src/java/org/sakaiproject/lti/impl/BaseLTIService.java index aeafea2f7a57..1b2a4c1b987f 100644 --- a/lti/lti-impl/src/java/org/sakaiproject/lti/impl/BaseLTIService.java +++ b/lti/lti-impl/src/java/org/sakaiproject/lti/impl/BaseLTIService.java @@ -479,6 +479,28 @@ public String validateTool(Properties newProps) { return validateTool((Map) newProps); } + @Override + public boolean isDraft(Map tool) { + boolean retval = true; + if ( tool == null ) return retval; + if ( StringUtils.isEmpty((String) tool.get(LTI_LAUNCH)) ) return true; + if ( SakaiLTIUtil.isLTI11(tool) ) { + String consumerKey = (String) tool.get(LTI_CONSUMERKEY); + String consumerSecret = (String) tool.get(LTI_SECRET); + if ( StringUtils.isNotEmpty(consumerSecret) && StringUtils.isNotEmpty(consumerSecret) + && (! LTI_SECRET_INCOMPLETE.equals(consumerSecret)) + && (! LTI_SECRET_INCOMPLETE.equals(consumerKey)) ) retval = false; + } + + if ( SakaiLTIUtil.isLTI13(tool) + && StringUtils.isNotEmpty((String) tool.get(LTI13_CLIENT_ID)) + && StringUtils.isNotEmpty((String) tool.get(LTI13_TOOL_KEYSET)) + && StringUtils.isNotEmpty((String) tool.get(LTI13_TOOL_ENDPOINT)) + && StringUtils.isNotEmpty((String) tool.get(LTI13_TOOL_REDIRECT))) retval = false; + + return retval; + } + @Override public String validateTool(Map newProps) { StringBuffer sb = new StringBuffer(); diff --git a/lti/lti-impl/src/java/org/sakaiproject/lti/impl/LTISecurityServiceImpl.java b/lti/lti-impl/src/java/org/sakaiproject/lti/impl/LTISecurityServiceImpl.java index ce01b9d1723b..7bab3fbbfaf2 100644 --- a/lti/lti-impl/src/java/org/sakaiproject/lti/impl/LTISecurityServiceImpl.java +++ b/lti/lti-impl/src/java/org/sakaiproject/lti/impl/LTISecurityServiceImpl.java @@ -345,7 +345,7 @@ private boolean sanityCheck(HttpServletRequest req, HttpServletResponse res, { String oidc_endpoint = (String) tool.get(LTIService.LTI13_TOOL_ENDPOINT); - if (SakaiLTIUtil.isLTI13(tool, content) && StringUtils.isBlank(oidc_endpoint) ) { + if (SakaiLTIUtil.isLTI13(tool) && StringUtils.isBlank(oidc_endpoint) ) { String errorMessage = "

" + SakaiLTIUtil.getRB(rb, "error.no.oidc_endpoint", "Missing oidc_endpoint value for LTI 1.3 launch") + "

"; org.tsugi.lti.LTIUtil.sendHTMLPage(res, errorMessage); return false; @@ -414,7 +414,7 @@ public void handleAccess(HttpServletRequest req, HttpServletResponse res, Refere // Sanity check for missing config data if ( ! sanityCheck(req, res, null, tool, rb) ) return; - if (SakaiLTIUtil.isLTI13(tool, null) && StringUtils.isNotBlank(oidc_endpoint) && + if (SakaiLTIUtil.isLTI13(tool) && StringUtils.isNotBlank(oidc_endpoint) && ( StringUtils.isEmpty(state) || StringUtils.isEmpty(state) ) ) { redirectOIDC(req, res, null, tool, oidc_endpoint, rb); return; @@ -494,7 +494,7 @@ else if ( refId.startsWith("content:") && refId.length() > 8 ) // Sanity check for missing config data if ( ! sanityCheck(req, res, content, tool, rb) ) return; - if (SakaiLTIUtil.isLTI13(tool, content) && StringUtils.isNotBlank(oidc_endpoint) && + if (SakaiLTIUtil.isLTI13(tool) && StringUtils.isNotBlank(oidc_endpoint) && (StringUtils.isEmpty(state) || StringUtils.isEmpty(nonce) ) ) { redirectOIDC(req, res, content, tool, oidc_endpoint, rb); return; diff --git a/master/pom.xml b/master/pom.xml index fc73ad566e18..3efa5308a339 100644 --- a/master/pom.xml +++ b/master/pom.xml @@ -797,7 +797,7 @@ commons-codec commons-codec - 1.17.2 + 1.18.0 provided diff --git a/samigo/samigo-app/src/webapp/jsf/evaluation/questionScore.jsp b/samigo/samigo-app/src/webapp/jsf/evaluation/questionScore.jsp index 1d7e72323859..07d411e5ce39 100755 --- a/samigo/samigo-app/src/webapp/jsf/evaluation/questionScore.jsp +++ b/samigo/samigo-app/src/webapp/jsf/evaluation/questionScore.jsp @@ -350,7 +350,7 @@ $Id$ type="org.sakaiproject.tool.assessment.ui.listener.evaluation.QuestionScoreListener" /> -