Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SAK-50752 Announcements support in CC+Archive archive / merge #13317

Merged
merged 9 commits into from
Feb 19, 2025
Merged

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,8 @@
// import
import java.sql.Connection;
import java.sql.ResultSet;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.Collections;

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -38,7 +34,6 @@
import org.w3c.dom.Element;

import org.sakaiproject.announcement.api.AnnouncementMessage;
import org.sakaiproject.announcement.api.ViewableFilter;
import org.sakaiproject.authz.api.AuthzGroup;
import org.sakaiproject.authz.api.GroupNotDefinedException;
import org.sakaiproject.authz.api.Role;
Expand Down Expand Up @@ -490,7 +485,7 @@ public Object readSqlResultRecord(ResultSet result)
else
{
// m.getReference() won't work cause we didn't give it its channel...
Reference channel = m_entityManager.newReference(channelId);
Reference channel = entityManager.newReference(channelId);
String ref = messageReference(channel.getContext(), channel.getId(), m.getId());
pubview = getPubView(ref);

Expand Down Expand Up @@ -572,7 +567,7 @@ protected boolean getPubView(String ref)
// get the realm
try
{
AuthzGroup realm = m_authzGroupService.getAuthzGroup(ref);
AuthzGroup realm = authzGroupService.getAuthzGroup(ref);

// if the announcement realm has "pubview" role, then the announcement is publicly viewable
Role pubview = realm.getRole("pubview");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -851,38 +851,9 @@ private boolean makePage(Element element, String oldServer, String siteId, Strin
log.debug("Created new content item: {}", sakaiId);
}
}
} else if (type == SimplePageItem.TEXT) {
String html = itemElement.getAttribute("html");
// TODO: SAK-46983 - Check carefully
Pattern idPattern = Pattern.compile("(https?://[^/]+/access/[basic]*lti/site)/" + Pattern.quote(oldSiteId) + "/content:([0-9]+)");
Matcher matcher = idPattern.matcher(html);
StringBuffer sb = new StringBuffer();
boolean foundLtiLink = false;
while(matcher.find()) {
String urlFirstPart = matcher.group(1);
Long ltiContentId = Long.valueOf(matcher.group(2));
log.info("Updating reference: {}", matcher.group(0));
foundLtiLink = true;
try {
Map<String, Object> ltiContent = ltiService.getContentDao(ltiContentId, oldSiteId, securityService.isSuperUser());
String newSakaiId = copyLTIContent(ltiContent, siteId, oldSiteId);
if ( newSakaiId != null ) sakaiId = newSakaiId;
String[] bltiId = sakaiId.split("/");
ltiContentId = Long.valueOf(bltiId[2]);
} catch (Exception e) {
log.warn("Unable to import LTI tool to new site: {}", e);
} finally {
String updatedReference = urlFirstPart + "/" + siteId + "/content:" + ltiContentId;
log.info("New reference: {}", updatedReference);
matcher.appendReplacement(sb, Matcher.quoteReplacement(updatedReference));
}
}

if(foundLtiLink) {
matcher.appendTail(sb);
explanation = sb.toString();
log.info("Updated at least one LTI reference lesson HTML");
}
} else if (type == SimplePageItem.TEXT) {
String html = itemElement.getAttribute("html");
explanation = ltiService.fixLtiLaunchUrls(html, siteId, ltiContentItems);
} else if (type == SimplePageItem.PAGE) {
// sakaiId should be the new page ID
Long newPageId = pageMap.get(Long.valueOf(sakaiId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3730,17 +3730,17 @@ public static List<String> extractLtiLaunchUrls(String html) {
}

/**
* Extract the lti-launch url and site id from a rich edtext editor string
* Extract the site id and content key from a LTI Launch URL
*
* @param html
* @return an array of two strings, the first is the blti or lti, the second is the site id
* @param url
* @return an array of two strings, the first is the site id, the second is the content key
*/
public static String[] getLtiLaunchUrlAndSiteId(String html) {
if (html == null) {
public static String[] getContentKeyAndSiteId(String url) {
if (url == null) {
return null;
}
Pattern pattern = Pattern.compile("https?://[^\\s\"']+/access/(?:b)?lti/site/([^\\s\"'/]+)/content:(\\d+)");
Matcher matcher = pattern.matcher(html);
Matcher matcher = pattern.matcher(url);
if (matcher.find()) {
return new String[] { matcher.group(1), matcher.group(2) };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1030,39 +1030,39 @@ public void testExtractLtiLaunchUrls() {
}

@Test
public void testGetLtiLaunchUrlAndSiteId() {
public void testGetContentKeyAndSiteId() {
// Test standard LTI URL
String html1 = "http://localhost:8080/access/lti/site/7d529bf7/content:1";
String[] result1 = SakaiLTIUtil.getLtiLaunchUrlAndSiteId(html1);
String[] result1 = SakaiLTIUtil.getContentKeyAndSiteId(html1);
assertNotNull(result1);
assertEquals("7d529bf7", result1[0]);
assertEquals("1", result1[1]);

// Test BLTI URL
String html2 = "https://localhost:8080/access/blti/site/abc123def/content:42";
String[] result2 = SakaiLTIUtil.getLtiLaunchUrlAndSiteId(html2);
String[] result2 = SakaiLTIUtil.getContentKeyAndSiteId(html2);
assertNotNull(result2);
assertEquals("abc123def", result2[0]);
assertEquals("42", result2[1]);

// Test URL embedded in HTML
String html3 = "<p><a href=\"https://localhost:8080/access/lti/site/xyz789/content:99\">Link</a></p>";
String[] result3 = SakaiLTIUtil.getLtiLaunchUrlAndSiteId(html3);
String[] result3 = SakaiLTIUtil.getContentKeyAndSiteId(html3);
assertNotNull(result3);
assertEquals("xyz789", result3[0]);
assertEquals("99", result3[1]);

// Test invalid URL
String html4 = "http://localhost:8080/access/other/site/invalid/content:1";
String[] result4 = SakaiLTIUtil.getLtiLaunchUrlAndSiteId(html4);
String[] result4 = SakaiLTIUtil.getContentKeyAndSiteId(html4);
assertNull(result4);

// Test null input
String[] result5 = SakaiLTIUtil.getLtiLaunchUrlAndSiteId(null);
String[] result5 = SakaiLTIUtil.getContentKeyAndSiteId(null);
assertNull(result5);

// Test empty string
String[] result6 = SakaiLTIUtil.getLtiLaunchUrlAndSiteId("");
String[] result6 = SakaiLTIUtil.getContentKeyAndSiteId("");
assertNull(result6);
}
}
Expand Down
50 changes: 37 additions & 13 deletions lti/lti-impl/src/java/org/sakaiproject/lti/impl/BaseLTIService.java
Original file line number Diff line number Diff line change
Expand Up @@ -1283,35 +1283,59 @@ protected String fixLtiLaunchUrls(String text, String fromContext, String toCont
if (StringUtils.isBlank(text)) return text;
List<String> urls = SakaiLTIUtil.extractLtiLaunchUrls(text);
for (String url : urls) {
String[] pieces = SakaiLTIUtil.getLtiLaunchUrlAndSiteId(url);
String[] pieces = SakaiLTIUtil.getContentKeyAndSiteId(url);
if (pieces != null) {
String linkSiteId = pieces[0];
String linkContextId = pieces[1];
String linkContentId = pieces[1];

if ( transversalMap != null && transversalMap.containsKey(url) ) {
log.debug("Found transversal map entry for {} -> {}", url, transversalMap.get(url));
text = text.replace(url, transversalMap.get(url));
return text;
continue;
}

// We need to load up the content item from the old context which should be successful
Long contentKey = Long.parseLong(linkContextId);
// Check if we can load up the content item and tool from the old context
Long toolKey = null;
Map<String, Object> tool = null;
Long contentKey = Long.parseLong(linkContentId);
Map<String, Object> content = this.getContent(contentKey, linkSiteId);
if ( content != null ) {
toolKey = Foorm.getLongNull(content.get(LTIService.LTI_TOOL_ID));
// Make sure we can retrieve the tool in this site
if ( toolKey != null ) tool = this.getTool(toolKey, toContext);
if ( tool != null ) {
content.put(LTIService.LTI_LAUNCH, tool.get(LTI_LAUNCH));
log.debug("Copied launch url into content item {}",content.get(LTIService.LTI_TOOL_ID));
} else {
log.debug("Found content item {} could not load associated tool {}", contentKey, toolKey);
content = null;
toolKey = null;
}
}

// If we cannot find the content item and tool on in this server, get skeleton data
// from the basiclti.xml import
if ( content == null && ltiContentItems != null ) {
log.debug("Could not find content item {} / {} in site {}, checking ltiContentItems", linkContentId, contentKey, linkSiteId);
content = ltiContentItems.get(contentKey);
tool = null; // force creation of a new tool in findOrCreateToolForContentItem
}

if (content == null) {
log.error("Could not find content item {} in site {}",contentKey,linkSiteId);
log.error("Could not find content item {} / {} in site {} or imported content items",linkContentId, contentKey,linkSiteId);
continue;
}

// Get the tool id from the content item from the old site
Map<String, Object> tool = null;
Long newToolId = findOrCreateToolForContentItem(content, tool, toContext, fromContext, ltiContentItems);
if (newToolId == null) {
log.error("Could not associate new content item {} with a tool in site {}", contentKey, toContext);
continue;
if ( toolKey == null ) {
toolKey = findOrCreateToolForContentItem(content, tool, toContext, fromContext, ltiContentItems);
if (toolKey == null) {
log.error("Could not associate new content item {} with a tool in site {}", contentKey, toContext);
continue;
}
}

content.put(LTIService.LTI_SITE_ID, toContext);
content.put(LTIService.LTI_TOOL_ID, newToolId.toString());
content.put(LTIService.LTI_TOOL_ID, toolKey.toString());
Object result = this.insertContent(content, toContext);
if (result instanceof Long) {
Long newContentId = (Long) result;
Expand Down
4 changes: 4 additions & 0 deletions mailarchive/mailarchive-impl/impl/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@
<groupId>org.sakaiproject.message</groupId>
<artifactId>sakai-message-util</artifactId>
</dependency>
<dependency>
<groupId>org.sakaiproject.lti</groupId>
<artifactId>lti-api</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,9 @@ public abstract class BaseMailArchiveService extends BaseMessage implements Mail
* Constructors, Dependencies and their setter methods
*********************************************************************************************************************************************************************************************************************************************************/

/** Dependency: NotificationService. */
@Setter protected NotificationService notificationService;

/** Dependency: AliasService */
@Setter protected AliasService aliasService;

@Setter private FunctionManager functionManager;
@Setter protected FunctionManager functionManager;
@Setter protected NotificationService notificationService;

/**********************************************************************************************************************************************************************************************************************************************************
* Init and Destroy
Expand Down Expand Up @@ -120,7 +116,7 @@ public void init()
functionManager.registerFunction(eventId(SECURE_REMOVE_ANY), true);

// entity producer registration
m_entityManager.registerEntityProducer(this, REFERENCE_ROOT);
entityManager.registerEntityProducer(this, REFERENCE_ROOT);

log.info("init()");
}
Expand Down Expand Up @@ -531,7 +527,7 @@ public String[] myToolIds()
protected void enableMailbox(String siteId)
{
// form the email channel name
String channelRef = channelReference(siteId, m_siteService.MAIN_CONTAINER);
String channelRef = channelReference(siteId, siteService.MAIN_CONTAINER);

// see if there's a channel
MessageChannel channel = null;
Expand Down Expand Up @@ -601,7 +597,7 @@ protected void enableMailbox(String siteId)
protected void disableMailbox(String siteId)
{
// form the email channel name
String channelRef = channelReference(siteId, m_siteService.MAIN_CONTAINER);
String channelRef = channelReference(siteId, siteService.MAIN_CONTAINER);

// see if there's a channel
MessageChannel channel = null;
Expand Down Expand Up @@ -819,9 +815,9 @@ public MailArchiveMessage addMailArchiveMessage(String subject, String fromAddre
List attachments, String[] body) throws PermissionException
{
StringBuilder alertMsg = new StringBuilder();
final String cleanedHtml = m_formattedText.processFormattedText(body[1], alertMsg);
final String cleanedText = m_formattedText.encodeUnicode(body[0]);
final String cleanedSubject = m_formattedText.processFormattedText(subject, alertMsg);
final String cleanedHtml = formattedText.processFormattedText(body[1], alertMsg);
final String cleanedText = formattedText.encodeUnicode(body[0]);
final String cleanedSubject = formattedText.processFormattedText(subject, alertMsg);

MailArchiveMessageEdit edit = (MailArchiveMessageEdit) addMessage();
MailArchiveMessageHeaderEdit archiveHeaders = edit.getMailArchiveHeaderEdit();
Expand Down Expand Up @@ -923,7 +919,7 @@ public void setOpen(boolean setting)
*/
public boolean allowAddMessage(User user)
{
return m_securityService.unlock(user, eventId(SECURE_ADD), getReference());
return securityService.unlock(user, eventId(SECURE_ADD), getReference());
}

/*
Expand Down Expand Up @@ -1072,7 +1068,7 @@ else if (element.getTagName().equals("body"))
{
// convert from plaintext messages to formatted text messages
m_body = element.getChildNodes().item(0).getNodeValue();
if (m_body != null) m_body = m_formattedText.convertPlaintextToFormattedText(m_body);
if (m_body != null) m_body = formattedText.convertPlaintextToFormattedText(m_body);
}
if (m_body == null)
{
Expand Down Expand Up @@ -1131,7 +1127,7 @@ public void setHtmlBody(String body)
*/
public String getHtmlBody()
{
return m_formattedText.getHtmlBody(m_html_body);
return formattedText.getHtmlBody(m_html_body);
} // getHtmlBody

/**
Expand All @@ -1144,7 +1140,7 @@ public String getFormattedBody()
if ( getHtmlBody() != null && getHtmlBody().length() > 0 )
return getHtmlBody();
else
return m_formattedText.encodeUrlsAsHtml( m_formattedText.convertPlaintextToFormattedText( m_formattedText.decodeNumericCharacterReferences(m_body)) );
return formattedText.encodeUrlsAsHtml( formattedText.convertPlaintextToFormattedText( formattedText.decodeNumericCharacterReferences(m_body)) );

} // getHtmlBody

Expand Down Expand Up @@ -1233,7 +1229,7 @@ public BaseMailArchiveMessageHeaderEdit(Message msg, Element el)
// now extract the subject, from address, date sent
m_subject = el.getAttribute("subject");
m_fromAddress = el.getAttribute("mail-from");
m_dateSent = m_timeService.newTimeGmt(el.getAttribute("mail-date"));
m_dateSent = timeService.newTimeGmt(el.getAttribute("mail-date"));

// mail headers
NodeList children = el.getChildNodes();
Expand Down Expand Up @@ -1341,7 +1337,7 @@ public Time getDateSent()

@Override
public void setInstantSent(Instant sent) {
m_dateSent = m_timeService.newTime(sent.toEpochMilli());
m_dateSent = timeService.newTime(sent.toEpochMilli());
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
<property name="aliasService"><ref bean="org.sakaiproject.alias.api.AliasService"/></property>
<property name="functionManager"><ref bean="org.sakaiproject.authz.api.FunctionManager"/></property>
<property name="formattedText" ref="org.sakaiproject.util.api.FormattedText" />
<property name="ltiService"><ref bean="org.sakaiproject.lti.api.LTIService" /></property>
<property name="contentHostingService"><ref bean="org.sakaiproject.content.api.ContentHostingService"/></property>
<property name="containerTableName"><value>MAILARCHIVE_CHANNEL</value></property>
<property name="resourceTableName"><value>MAILARCHIVE_MESSAGE</value></property>
<property name="locksInDb"><value>false</value></property>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,5 +312,28 @@ public List<Message> getMessages(String channelRef, Time afterDate, int limitedT
*/
public Map getSummary(String ref, int items, int days) throws IdUsedException, IdInvalidException, PermissionException;

/**
* Allows a service extending MessageService to indicate if it wants things imported as draft
*/
public boolean importAsDraft();

/**
* Allows a service extending MessageService to approve a message sender
*
* @param userId
* The user id of the message sender
* @return true if the message sender is approved, false otherwise
*/
public boolean approveMessageSender(String userId);

/**
* Allows a service extending MessageService to return its common tool title
*
* @param url
* If present, it is the url of the imported attachment (old)
* @return true if the message sender is approved, false otherwise
*/
public String getToolTitle(String url);

} // MessageService

Loading
Loading