/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/announcement/trunk/announcement-impl/impl/src/java/org/sakaiproject/announcement/impl/BaseAnnouncementService.java $ * $Id: BaseAnnouncementService.java 131324 2013-11-07 20:22:01Z matthew@longsight.com $ *********************************************************************************** * * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008 The Sakai Foundation * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ECL-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **********************************************************************************/ package org.sakaiproject.announcement.impl; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Vector; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Stack; import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sakaiproject.alias.api.Alias; import org.sakaiproject.alias.api.AliasService; import org.sakaiproject.announcement.api.AnnouncementChannel; import org.sakaiproject.announcement.api.AnnouncementChannelEdit; import org.sakaiproject.announcement.api.AnnouncementMessage; import org.sakaiproject.announcement.api.AnnouncementMessageEdit; import org.sakaiproject.announcement.api.AnnouncementMessageHeader; import org.sakaiproject.announcement.api.AnnouncementMessageHeaderEdit; import org.sakaiproject.announcement.api.AnnouncementService; import org.sakaiproject.authz.api.FunctionManager; import org.sakaiproject.authz.api.SecurityService; import org.sakaiproject.component.cover.ServerConfigurationService; import org.sakaiproject.content.api.ContentHostingService; import org.sakaiproject.content.api.ContentResource; import org.sakaiproject.entity.api.ContextObserver; import org.sakaiproject.entity.api.Edit; import org.sakaiproject.entity.api.Entity; import org.sakaiproject.entity.api.EntityNotDefinedException; import org.sakaiproject.entity.api.EntityPermissionException; import org.sakaiproject.entity.api.EntityTransferrer; import org.sakaiproject.entity.api.EntityTransferrerRefMigrator; import org.sakaiproject.entity.api.HttpAccess; import org.sakaiproject.entity.api.Reference; import org.sakaiproject.entity.api.ResourceProperties; import org.sakaiproject.entity.api.ResourcePropertiesEdit; import org.sakaiproject.event.api.NotificationEdit; import org.sakaiproject.event.api.NotificationService; import org.sakaiproject.exception.IdInvalidException; import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.exception.IdUsedException; import org.sakaiproject.exception.InUseException; import org.sakaiproject.exception.PermissionException; import org.sakaiproject.javax.Filter; import org.sakaiproject.message.api.Message; import org.sakaiproject.message.api.MessageChannel; import org.sakaiproject.message.api.MessageHeader; import org.sakaiproject.message.api.MessageHeaderEdit; import org.sakaiproject.message.util.BaseMessage; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.api.SiteService; import org.sakaiproject.site.api.ToolConfiguration; import org.sakaiproject.time.api.Time; import org.sakaiproject.time.api.TimeService; import org.sakaiproject.tool.api.SessionManager; import org.sakaiproject.tool.api.ToolManager; import org.sakaiproject.util.MergedList; import org.sakaiproject.util.ResourceLoader; import org.sakaiproject.util.StringUtil; import org.sakaiproject.util.Validator; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * <p> * BaseAnnouncementService extends the BaseMessage for the specifics of Announcement. * </p> */ public abstract class BaseAnnouncementService extends BaseMessage implements AnnouncementService, ContextObserver, EntityTransferrer, EntityTransferrerRefMigrator { /** Our logger. */ private static Log M_log = LogFactory.getLog(BaseAnnouncementService.class); /** private constants definitions */ private final static String SAKAI_ANNOUNCEMENT_TOOL_ID = "sakai.announcements"; private static final String PORTLET_CONFIG_PARM_MERGED_CHANNELS = "mergedAnnouncementChannels"; /** Messages, for the http access. */ protected static ResourceLoader rb = new ResourceLoader("annc-access"); // XML DocumentBuilder and Transformer for RSS Feed private DocumentBuilder docBuilder = null; private Transformer docTransformer = null; private ContentHostingService contentHostingService; /** * Dependency: contentHostingService. * * @param service * The NotificationService. */ public void setContentHostingService(ContentHostingService service) { contentHostingService = service; } private FunctionManager functionManager; public void setFunctionManager(FunctionManager functionManager) { this.functionManager = functionManager; } private AliasService aliasService; public void setAliasService(AliasService aliasService) { this.aliasService = aliasService; } private TimeService timeService; public void setTimeService(TimeService timeService) { this.timeService = timeService; super.setTimeService(timeService); } private SessionManager sessionManager; public void setSessionManager(SessionManager sessionManager) { this.sessionManager = sessionManager; super.setSessionManager(sessionManager); } private ToolManager toolManager; public void setToolManager(ToolManager toolManager) { this.toolManager = toolManager; } private SecurityService securityService; public void setSecurityService(SecurityService securityService) { this.securityService = securityService; super.setSecurityService(securityService); } /********************************************************************************************************************************************************************************************************************************************************** * Constructors, Dependencies and their setter methods *********************************************************************************************************************************************************************************************************************************************************/ /** Dependency: NotificationService. */ protected NotificationService m_notificationService = null; /** * Dependency: NotificationService. * * @param service * The NotificationService. */ public void setNotificationService(NotificationService service) { m_notificationService = service; } /********************************************************************************************************************************************************************************************************************************************************** * Init and Destroy *********************************************************************************************************************************************************************************************************************************************************/ /** * Final initialization, once all dependencies are set. */ public void init() { try { super.init(); // register a transient notification for announcements NotificationEdit edit = m_notificationService.addTransientNotification(); // set functions edit.setFunction(eventId(SECURE_ADD)); edit.addFunction(eventId(SECURE_UPDATE_OWN)); edit.addFunction(eventId(SECURE_UPDATE_ANY)); // set the filter to any announcement resource (see messageReference()) edit.setResourceFilter(getAccessPoint(true) + Entity.SEPARATOR + REF_TYPE_MESSAGE); // set the action edit.setAction(new SiteEmailNotificationAnnc()); // register functions functionManager.registerFunction(eventId(SECURE_READ)); functionManager.registerFunction(eventId(SECURE_ADD)); functionManager.registerFunction(eventId(SECURE_REMOVE_ANY)); functionManager.registerFunction(eventId(SECURE_REMOVE_OWN)); functionManager.registerFunction(eventId(SECURE_UPDATE_ANY)); functionManager.registerFunction(eventId(SECURE_UPDATE_OWN)); functionManager.registerFunction(eventId(SECURE_ALL_GROUPS)); // Sakai v2.4: UI end says hidden, 'under the covers' says draft // Done so import from old sites causes drafts to 'become' hidden in new sites functionManager.registerFunction(eventId(SECURE_READ_DRAFT)); // entity producer registration m_entityManager.registerEntityProducer(this, REFERENCE_ROOT); // create DocumentBuilder for RSS Feed DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setIgnoringComments(true); // ignore comments factory.setNamespaceAware(true); // namespace aware should be true factory.setValidating(false); // we're not validating docBuilder = factory.newDocumentBuilder(); TransformerFactory tFactory = TransformerFactory.newInstance(); docTransformer = tFactory.newTransformer(); M_log.info("init()"); } catch (Throwable t) { M_log.warn("init(): ", t); } } // init /********************************************************************************************************************************************************************************************************************************************************** * StorageUser implementation *********************************************************************************************************************************************************************************************************************************************************/ /** * Construct a new continer given just ids. * * @param ref * The container reference. * @return The new containe Resource. */ public Entity newContainer(String ref) { return new BaseAnnouncementChannelEdit(ref); } /** * Construct a new container resource, from an XML element. * * @param element * The XML. * @return The new container resource. */ public Entity newContainer(Element element) { return new BaseAnnouncementChannelEdit(element); } /** * Construct a new container resource, as a copy of another * * @param other * The other contianer to copy. * @return The new container resource. */ public Entity newContainer(Entity other) { return new BaseAnnouncementChannelEdit((MessageChannel) other); } /** * Construct a new rsource given just an id. * * @param container * The Resource that is the container for the new resource (may be null). * @param id * The id for the new object. * @param others * (options) array of objects to load into the Resource's fields. * @return The new resource. */ public Entity newResource(Entity container, String id, Object[] others) { return new BaseAnnouncementMessageEdit((MessageChannel) container, id); } /** * Construct a new resource, from an XML element. * * @param container * The Resource that is the container for the new resource (may be null). * @param element * The XML. * @return The new resource from the XML. */ public Entity newResource(Entity container, Element element) { return new BaseAnnouncementMessageEdit((MessageChannel) container, element); } /** * Construct a new resource from another resource of the same type. * * @param container * The Resource that is the container for the new resource (may be null). * @param other * The other resource. * @return The new resource as a copy of the other. */ public Entity newResource(Entity container, Entity other) { return new BaseAnnouncementMessageEdit((MessageChannel) container, (Message) other); } /** * Construct a new continer given just ids. * * @param ref * The container reference. * @return The new containe Resource. */ public Edit newContainerEdit(String ref) { BaseAnnouncementChannelEdit rv = new BaseAnnouncementChannelEdit(ref); rv.activate(); return rv; } /** * Construct a new container resource, from an XML element. * * @param element * The XML. * @return The new container resource. */ public Edit newContainerEdit(Element element) { BaseAnnouncementChannelEdit rv = new BaseAnnouncementChannelEdit(element); rv.activate(); return rv; } /** * Construct a new container resource, as a copy of another * * @param other * The other contianer to copy. * @return The new container resource. */ public Edit newContainerEdit(Entity other) { BaseAnnouncementChannelEdit rv = new BaseAnnouncementChannelEdit((MessageChannel) other); rv.activate(); return rv; } /** * Construct a new rsource given just an id. * * @param container * The Resource that is the container for the new resource (may be null). * @param id * The id for the new object. * @param others * (options) array of objects to load into the Resource's fields. * @return The new resource. */ public Edit newResourceEdit(Entity container, String id, Object[] others) { BaseAnnouncementMessageEdit rv = new BaseAnnouncementMessageEdit((MessageChannel) container, id); rv.activate(); return rv; } /** * Construct a new resource, from an XML element. * * @param container * The Resource that is the container for the new resource (may be null). * @param element * The XML. * @return The new resource from the XML. */ public Edit newResourceEdit(Entity container, Element element) { BaseAnnouncementMessageEdit rv = new BaseAnnouncementMessageEdit((MessageChannel) container, element); rv.activate(); return rv; } /** * Construct a new resource from another resource of the same type. * * @param container * The Resource that is the container for the new resource (may be null). * @param other * The other resource. * @return The new resource as a copy of the other. */ public Edit newResourceEdit(Entity container, Entity other) { BaseAnnouncementMessageEdit rv = new BaseAnnouncementMessageEdit((MessageChannel) container, (Message) other); rv.activate(); return rv; } /** * Collect the fields that need to be stored outside the XML (for the resource). * * @return An array of field values to store in the record outside the XML (for the resource). */ public Object[] storageFields(Entity r) { Object[] rv = new Object[5]; rv[0] = ((Message) r).getHeader().getDate(); rv[1] = ((Message) r).getHeader().getFrom().getId(); rv[2] = ((AnnouncementMessage) r).getAnnouncementHeader().getDraft() ? "1" : "0"; rv[3] = r.getProperties().getProperty(ResourceProperties.PROP_PUBVIEW) == null ? "0" : "1"; rv[4] = ((Message) r).getHeader().getMessage_order(); // rv[3] = ((AnnouncementMessage) r).getAnnouncementHeader().getAccess() == MessageHeader.MessageAccess.PUBLIC ? "1" : "0"; return rv; } /** * Check if this resource is in draft mode. * * @param r * The resource. * @return true if the resource is in draft mode, false if not. */ public boolean isDraft(Entity r) { return ((AnnouncementMessage) r).getAnnouncementHeader().getDraft(); } /** * Access the resource owner user id. * * @param r * The resource. * @return The resource owner user id. */ public String getOwnerId(Entity r) { return ((Message) r).getHeader().getFrom().getId(); } /** * Access the resource date. * * @param r * The resource. * @return The resource date. */ public Time getDate(Entity r) { return ((Message) r).getHeader().getDate(); } /** * Access the resource Message Order. * * @param r * The resource. * @return The resource order. */ public Integer getMessage_order(Entity r) { return ((Message) r).getHeader().getMessage_order(); } /********************************************************************************************************************************************************************************************************************************************************** * Abstractions, etc. satisfied *********************************************************************************************************************************************************************************************************************************************************/ /** * Report the Service API name being implemented. */ protected String serviceName() { return AnnouncementService.class.getName(); } /** * Construct a new message header from XML in a DOM element. * * @param id * The message Id. * @return The new message header. */ protected MessageHeaderEdit newMessageHeader(Message msg, String id) { return new BaseAnnouncementMessageHeaderEdit(msg, id); } // newMessageHeader /** * Construct a new message header from XML in a DOM element. * * @param el * The XML DOM element that has the header information. * @return The new message header. */ protected MessageHeaderEdit newMessageHeader(Message msg, Element el) { return new BaseAnnouncementMessageHeaderEdit(msg, el); } // newMessageHeader /** * Construct a new message header as a copy of another. * * @param other * The other header to copy. * @return The new message header. */ protected MessageHeaderEdit newMessageHeader(Message msg, MessageHeader other) { return new BaseAnnouncementMessageHeaderEdit(msg, other); } // newMessageHeader /** * Form a tracking event string based on a security function string. * * @param secure The security function string. * NOTE: if this input is null or blank then event id will default to "annc.INVALID_KEY", * this is only because throwing an exception here would be very disruptive and returning * a null or blank for the event id would cause other failures (SAK-23804) * @return The event tracking string. */ protected String eventId(String secure) { // https://jira.sakaiproject.org/browse/SAK-23804 if (StringUtils.isBlank(secure)) { try { throw new IllegalArgumentException("anouncement eventId() input cannot be null or blank"); } catch (Exception e) { secure = "INVALID_KEY"; M_log.error("Bad call to BaseAnnouncementService.eventId(String) - input string is blank, generating '"+secure+"' event name and logging trace", e); } } return SECURE_ANNC_ROOT + secure; } // eventId /** * Return the reference rooot for use in resource references and urls. * * @return The reference rooot for use in resource references and urls. */ protected String getReferenceRoot() { return REFERENCE_ROOT; } // getReferenceRoot /** * {@inheritDoc} */ public boolean parseEntityReference(String reference, Reference ref) { if (reference.startsWith(REFERENCE_ROOT)) { String[] parts = StringUtil.split(reference, Entity.SEPARATOR); String id = null; String subType = null; String context = null; String container = null; // the first part will be null, then next the service, the third will be: "msg", "channel", "announcement" or "rss" if (parts.length > 2) { subType = parts[2]; if (REF_TYPE_CHANNEL.equals(subType)) { // next is the context id if (parts.length > 3) { context = parts[3]; // next is the channel id if (parts.length > 4) { id = parts[4]; } } } else if (REF_TYPE_MESSAGE.equals(subType)) { // next three parts are context, channel (container) and mesage id if (parts.length > 5) { context = parts[3]; container = parts[4]; id = parts[5]; } } else if (REF_TYPE_ANNOUNCEMENT_RSS.equals(subType) || REF_TYPE_ANNOUNCEMENT.equals(subType)) { // next is the context id if (parts.length > 3) context = parts[3]; } else M_log.warn("parse(): unknown message subtype: " + subType + " in ref: " + reference); } // Translate context alias into site id (only for rss) if necessary if (REF_TYPE_ANNOUNCEMENT_RSS.equals(subType) &&(context != null) && (context.length() > 0)) { if (!m_siteService.siteExists(context)) { try { String aliasTarget = aliasService.getTarget(context); if (aliasTarget.startsWith(REFERENCE_ROOT)) // only support announcement aliases { parts = StringUtil.split(aliasTarget, Entity.SEPARATOR); if (parts.length > 3) context = parts[3]; } } catch (Exception e) { M_log.debug(this+".parseEntityReference(): "+e.toString()); return false; } } // if context still isn't valid, then no valid alias or site was specified if (!m_siteService.siteExists(context)) { M_log.warn(this+".parseEntityReference() no valid site or alias: " + context); return false; } } ref.set(APPLICATION_ID, subType, id, container, context); return true; } return false; } /** * @inheritDoc */ public Reference getAnnouncementReference(String context) { StringBuilder refString = new StringBuilder(); refString.append(getAccessPoint(true)); refString.append(Entity.SEPARATOR); refString.append(REF_TYPE_ANNOUNCEMENT); refString.append(Entity.SEPARATOR); refString.append(context); return m_entityManager.newReference( refString.toString() ); } /** * @inheritDoc */ @Override public String channelReference(String context, String id) { /* SAK-19516: for MOTD and Admin Workspace Announcements, the channel reference * is not calculated based on the context and id, but pulled from SAKAI_TOOL_PROPERTY 'channel'. */ String channelRef = null; try { ToolConfiguration tool = m_siteService.getSite(context).getToolForCommonId(SAKAI_ANNOUNCEMENT_TOOL_ID); if (tool != null) { channelRef = tool.getConfig().getProperty(ANNOUNCEMENT_CHANNEL_PROPERTY, null); } } catch (IdUnusedException e) { // ignore the error, continue with the default method M_log.debug("Could not find channelRef in channel property, falling back to default method..."); } if (channelRef == null || channelRef.trim().length() == 0) { channelRef = super.channelReference(context, id); } return channelRef; } // channelReference /** * @inheritDoc */ public String getRssUrl(Reference ref) { String alias = null; List aliasList = aliasService.getAliases( ref.getReference() ); if ( ! aliasList.isEmpty() ) alias = ((Alias)aliasList.get(0)).getId(); StringBuilder rssUrlString = new StringBuilder(); rssUrlString.append( m_serverConfigurationService.getAccessUrl() ); rssUrlString.append(getAccessPoint(true)); rssUrlString.append(Entity.SEPARATOR); rssUrlString.append(REF_TYPE_ANNOUNCEMENT_RSS); rssUrlString.append(Entity.SEPARATOR); if ( alias != null) rssUrlString.append(alias); else rssUrlString.append(ref.getContext()); return rssUrlString.toString(); } /** * {@inheritDoc} */ public boolean isMessageViewable(AnnouncementMessage message) { final ResourceProperties messageProps = message.getProperties(); final Time now = timeService.newTime(); try { final Time releaseDate = message.getProperties().getTimeProperty(RELEASE_DATE); if (now.before(releaseDate)) { return false; } } catch (Exception e) { // Just not using/set Release Date } try { final Time retractDate = message.getProperties().getTimeProperty(RETRACT_DATE); if (now.after(retractDate)) { return false; } } catch (Exception e) { // Just not using/set Retract Date } return true; } /** * {@inheritDoc} */ public void contextCreated(String context, boolean toolPlacement) { if (toolPlacement) enableMessageChannel(context); } /** * {@inheritDoc} */ public void contextUpdated(String context, boolean toolPlacement) { if (toolPlacement) enableMessageChannel(context); } /** * {@inheritDoc} */ public void contextDeleted(String context, boolean toolPlacement) { disableMessageChannel(context); } /** * {@inheritDoc} */ public String[] myToolIds() { String[] toolIds = { "sakai.announcements" }; return toolIds; } /** ** Generate RSS Item element for specified assignment **/ protected Element generateItemElement( Document doc, AnnouncementMessage msg, Reference msgRef ) { Element item = doc.createElement("item"); Element el = doc.createElement("title"); el.appendChild(doc.createTextNode( msg.getAnnouncementHeader().getSubject() )); item.appendChild(el); el = doc.createElement("author"); el.appendChild(doc.createTextNode( msg.getHeader().getFrom().getEmail() )); item.appendChild(el); el = doc.createElement("link"); el.appendChild(doc.createTextNode( msgRef.getUrl() )); item.appendChild(el); el = doc.createElement("description"); el.appendChild(doc.createTextNode( msg.getBody()) ); item.appendChild(el); el = doc.createElement("pubDate"); Date date = new Date(msg.getHeader().getDate().getTime()); String pubDate = new SimpleDateFormat("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z", Locale.ENGLISH).format(date); el.appendChild(doc.createTextNode(pubDate)); item.appendChild(el); el = doc.createElement("message_order"); el.appendChild(doc.createTextNode( msg.getHeader().getMessage_order().toString())); item.appendChild(el); // attachments List attachments = msg.getAnnouncementHeader().getAttachments(); if (attachments.size() > 0) { for (Iterator iAttachments = attachments.iterator(); iAttachments.hasNext();) { Reference attachment = (Reference) iAttachments.next(); el = doc.createElement("enclosure"); el.setAttribute("url",attachment.getUrl()); el.setAttribute("type",attachment.getType()); item.appendChild(el); } } return item; } /** ** Print all Announcements as RSS Feed **/ protected void printAnnouncementRss( OutputStream out, Reference rssRef ) { try { Site site = m_siteService.getSite(rssRef.getContext()); Document doc = docBuilder.newDocument(); Element root = doc.createElement("rss"); root.setAttribute("version","2.0"); doc.appendChild(root); Element channel = doc.createElement("channel"); root.appendChild(channel); // add title Element el = doc.createElement("title"); el.appendChild(doc.createTextNode("Announcements for "+site.getTitle())); channel.appendChild(el); // add description el = doc.createElement("description"); String desc = (site.getDescription()!=null)?site.getDescription():site.getTitle(); el.appendChild(doc.createTextNode(desc)); channel.appendChild(el); // add link el = doc.createElement("link"); StringBuilder siteUrl = new StringBuilder( m_serverConfigurationService.getServerUrl() ); siteUrl.append( m_serverConfigurationService.getString("portalPath") ); siteUrl.append( site.getReference() ); el.appendChild(doc.createTextNode(siteUrl.toString())); channel.appendChild(el); // add lastBuildDate el = doc.createElement("lastBuildDate"); String now = new SimpleDateFormat("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z", Locale.ENGLISH).format(new Date()); el.appendChild(doc.createTextNode( now )); channel.appendChild(el); // add generator el = doc.createElement("generator"); el.appendChild(doc.createTextNode("Sakai Announcements RSS Generator")); channel.appendChild(el); // get list of public announcements AnnouncementChannel anncChan = (AnnouncementChannel)getChannelPublic( channelReference(rssRef.getContext(), SiteService.MAIN_CONTAINER) ); if ( anncChan == null ) { M_log.warn(this+".printAnnouncementRss invalid request "+rssRef.getContext()); return; } List anncList = anncChan.getMessagesPublic(null,false); for ( Iterator it=anncList.iterator(); it.hasNext(); ) { AnnouncementMessage msg = (AnnouncementMessage)it.next(); if ( isMessageViewable(msg) ) { Reference msgRef = m_entityManager.newReference( msg.getReference() ); Element item = generateItemElement( doc, msg, msgRef ); channel.appendChild(item); } } docTransformer.transform( new DOMSource(doc), new StreamResult(out) ); } catch (Exception e) { M_log.warn(this+"printAnnouncementRss ", e); } } /** ** Print specified Announcement as HTML Page **/ protected void printAnnouncementHtml( PrintWriter out, Reference ref ) throws EntityPermissionException, EntityNotDefinedException { try { // check security on the message, if not a public message (throws if not permitted) if ( ref.getProperties().getProperty(ResourceProperties.PROP_PUBVIEW) == null || !ref.getProperties().getProperty(ResourceProperties.PROP_PUBVIEW).equals(Boolean.TRUE.toString()) ) { unlock(SECURE_READ, ref.getReference()); } AnnouncementMessage msg = (AnnouncementMessage) ref.getEntity(); AnnouncementMessageHeader hdr = (AnnouncementMessageHeader) msg.getAnnouncementHeader(); out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" + "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">\n" + "<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n" + "<style type=\"text/css\">body{margin:0px;padding:1em;font-family:Verdana,Arial,Helvetica,sans-serif;font-size:80%;}</style>\n" + "<title>" + rb.getString("announcement") + ": " + Validator.escapeHtml(hdr.getSubject()) + "</title>" + "</head>\n<body>"); out.println("<h1>" + rb.getString("announcement") + "</h1>"); // header out.println("<table><tr><td><b>" + rb.getString("from") + ":</b></td><td>" + Validator.escapeHtml(hdr.getFrom().getDisplayName()) + "</td></tr>"); out.println("<tr><td><b>" + rb.getString("date") + ":</b></td><td>" + Validator.escapeHtml(hdr.getDate().toStringLocalFull()) + "</td></tr>"); out.println("<tr><td><b>" + rb.getString("subject") + ":</b></td><td>" + Validator.escapeHtml(hdr.getSubject()) + "</td></tr></table>"); // body out.println("<p>" + Validator.escapeHtmlFormattedText(msg.getBody()) + "</p>"); // attachments List attachments = hdr.getAttachments(); if (attachments.size() > 0) { out.println("<p><b>" + rb.getString("attachments") + ":</b></p><p>"); for (Iterator iAttachments = attachments.iterator(); iAttachments.hasNext();) { Reference attachment = (Reference) iAttachments.next(); out.println("<a href=\"" + Validator.escapeHtml(attachment.getUrl()) + "\">" + Validator.escapeHtml(attachment.getUrl()) + "</a><br />"); } out.println("</p>"); } out.println("</body></html>"); } catch (PermissionException e) { throw new EntityPermissionException(e.getUser(), e.getLock(), e.getResource()); } } /** * {@inheritDoc} */ public HttpAccess getHttpAccess() { return new HttpAccess() { public void handleAccess(HttpServletRequest req, HttpServletResponse res, Reference ref, Collection copyrightAcceptedRefs) throws EntityPermissionException, EntityNotDefinedException //, EntityAccessOverloadException, EntityCopyrightException { // we only access the msg & rss reference if ( !REF_TYPE_MESSAGE.equals(ref.getSubType()) && !REF_TYPE_ANNOUNCEMENT_RSS.equals(ref.getSubType()) ) throw new EntityNotDefinedException(ref.getReference()); try { if ( REF_TYPE_MESSAGE.equals(ref.getSubType()) ) { res.setContentType("text/html; charset=UTF-8"); printAnnouncementHtml( res.getWriter(), ref ); } else { res.setContentType("application/xml"); res.setCharacterEncoding("UTF-8"); printAnnouncementRss( res.getOutputStream(), ref ); } } catch ( IOException e ) { throw new EntityNotDefinedException(ref.getReference()); } } }; } /********************************************************************************************************************************************************************************************************************************************************** * AnnouncementService implementation *********************************************************************************************************************************************************************************************************************************************************/ /** * Return a specific announcement channel. * * @param ref * The channel reference. * @return the AnnouncementChannel that has the specified name. * @exception IdUnusedException * If this name is not defined for a announcement channel. * @exception PermissionException * If the user does not have any permissions to the channel. */ public AnnouncementChannel getAnnouncementChannel(String ref) throws IdUnusedException, PermissionException { return (AnnouncementChannel) getChannel(ref); } // getAnnouncementChannel /** * Add a new announcement channel. * * @param ref * The channel reference. * @return The newly created channel. * @exception IdUsedException * if the id is not unique. * @exception IdInvalidException * if the id is not made up of valid characters. * @exception PermissionException * if the user does not have permission to add a channel. */ public AnnouncementChannelEdit addAnnouncementChannel(String ref) throws IdUsedException, IdInvalidException, PermissionException { return (AnnouncementChannelEdit) addChannel(ref); } // addAnnouncementChannel /** * Return a list of messages from the provided channel (merged flag returns merged messages) * @param channelReference * Channel's reference String * @param filter * A filtering object to accept messages, or null if no filtering is desired. * @param order * Order of messages, ascending if true, descending if false * @param merged * flag to include merged channel messages, true returns ALL messages including merged sites/channels * @return a list of Message objects or specializations of Message objects (may be empty). * @exception IdUnusedException * If this name is not defined for a announcement channel. * @exception PermissionException * if the user does not have read permission to the channel. * @exception NullPointerException */ public List getMessages(String channelReference,Filter filter, boolean order, boolean merged) throws IdUnusedException, PermissionException, NullPointerException { List<Message> messageList = new Vector(); filter = new PrivacyFilter(filter); // filter out drafts this user cannot see Site site = null; String initMergeList = null; try{ site = org.sakaiproject.site.cover.SiteService.getSite(getAnnouncementChannel(channelReference).getContext()); ToolConfiguration tc=site.getToolForCommonId(SAKAI_ANNOUNCEMENT_TOOL_ID); if (tc!=null){ initMergeList = tc.getPlacementConfig().getProperty(PORTLET_CONFIG_PARM_MERGED_CHANNELS); } MergedList mergedAnnouncementList = new MergedList(); String[] channelArrayFromConfigParameterValue = null; //get array of associated channels: similar logic as found in AnnouncementAction.getMessages() for viewing channelArrayFromConfigParameterValue = mergedAnnouncementList.getChannelReferenceArrayFromDelimitedString(channelReference, initMergeList); //get messages for each channel for(int i=0; i<channelArrayFromConfigParameterValue.length;i++) { MessageChannel siteChannel = getAnnouncementChannel(channelArrayFromConfigParameterValue[i]); if (siteChannel != null) { if (allowGetChannel(siteChannel.getReference())) { //merged flag = true then add all channel's messages //merged flag = false only add the calling channel's messages if(merged || siteChannel.getContext().equals(site.getId())) messageList.addAll(siteChannel.getMessages(filter,order)); } } } //sort messages Collections.sort(messageList); if (!order) { Collections.reverse(messageList); } } catch (IdUnusedException e) { M_log.warn(e.getMessage()); } catch (PermissionException e) { M_log.warn(e.getMessage()); } catch (NullPointerException e) { M_log.warn(e.getMessage()); } return messageList; } // getMessages /********************************************************************************************************************************************************************************************************************************************************** * ResourceService implementation *********************************************************************************************************************************************************************************************************************************************************/ /** * {@inheritDoc} */ public String getLabel() { return "announcement"; } /********************************************************************************************************************************************************************************************************************************************************** * getSummaryFromHeader implementation *********************************************************************************************************************************************************************************************************************************************************/ protected String getSummaryFromHeader(Message item, MessageHeader header) { String newText; if ( header instanceof AnnouncementMessageHeader) { AnnouncementMessageHeader hdr = (AnnouncementMessageHeader) header; newText = hdr.getSubject(); } else { newText = item.getBody(); if ( newText.length() > 50 ) newText = newText.substring(1,49); } newText = newText + ", " + header.getFrom().getDisplayName() + ", " + header.getDate().toStringLocalFull(); return newText; } /** * {@inheritDoc} */ public void transferCopyEntities(String fromContext, String toContext, List resourceIds) { transferCopyEntitiesRefMigrator(fromContext, toContext, resourceIds); } public Map<String, String> transferCopyEntitiesRefMigrator(String fromContext, String toContext, List resourceIds) { // Map<String, String> transversalMap = new HashMap<String, String>(); // get the channel associated with this site String oChannelRef = channelReference(fromContext, SiteService.MAIN_CONTAINER); AnnouncementChannel oChannel = null; try { oChannel = (AnnouncementChannel) getChannel(oChannelRef); // the "to" message channel String nChannelRef = channelReference(toContext, SiteService.MAIN_CONTAINER); AnnouncementChannel nChannel = null; try { nChannel = (AnnouncementChannel) getChannel(nChannelRef); } catch (IdUnusedException e) { try { commitChannel(addChannel(nChannelRef)); try { nChannel = (AnnouncementChannel) getChannel(nChannelRef); } catch (Exception eee) { // ignore } } catch (Exception ee) { // ignore } } if (nChannel != null) { // pass the DOM to get new message ids, record the mapping from old to new, and adjust attachments List oMessageList = oChannel.getMessages(null, true); AnnouncementMessage oMessage = null; AnnouncementMessageHeader oMessageHeader = null; AnnouncementMessageEdit nMessage = null; for (int i = 0; i < oMessageList.size(); i++) { // the "from" message oMessage = (AnnouncementMessage) oMessageList.get(i); String oMessageId = oMessage.getId(); boolean toBeImported = true; if (resourceIds != null && resourceIds.size() > 0) { toBeImported = false; for (int m = 0; m < resourceIds.size() && !toBeImported; m++) { if (((String) resourceIds.get(m)).equals(oMessageId)) { toBeImported = true; } } } // not to import any assignment-generated announcement String assignmentReference = StringUtil.trimToNull(oMessage.getProperties().getProperty(AnnouncementService.ASSIGNMENT_REFERENCE)); if (toBeImported && assignmentReference != null) { toBeImported = false; } if (toBeImported) { oMessageHeader = (AnnouncementMessageHeaderEdit) oMessage.getHeader(); ResourceProperties oProperties = oMessage.getProperties(); // the "to" message nMessage = (AnnouncementMessageEdit) nChannel.addMessage(); nMessage.setBody(oMessage.getBody()); // message header AnnouncementMessageHeaderEdit nMessageHeader = (AnnouncementMessageHeaderEdit) nMessage.getHeaderEdit(); nMessageHeader.setDate(oMessageHeader.getDate()); nMessageHeader.setMessage_order(oMessageHeader.getMessage_order()); // when importing, refer to property to determine draft status if ("false".equalsIgnoreCase(m_serverConfigurationService.getString("import.importAsDraft"))) { nMessageHeader.setDraft(oMessageHeader.getDraft()); } else { nMessageHeader.setDraft(true); } nMessageHeader.setFrom(oMessageHeader.getFrom()); nMessageHeader.setSubject(oMessageHeader.getSubject()); // attachment List oAttachments = oMessageHeader.getAttachments(); List nAttachments = m_entityManager.newReferenceList(); for (int n = 0; n < oAttachments.size(); n++) { Reference oAttachmentRef = (Reference) oAttachments.get(n); String oAttachmentId = ((Reference) oAttachments.get(n)).getId(); if (oAttachmentId.indexOf(fromContext) != -1) { // replace old site id with new site id in attachments String nAttachmentId = oAttachmentId.replaceAll(fromContext, toContext); try { ContentResource attachment = contentHostingService.getResource(nAttachmentId); nAttachments.add(m_entityManager.newReference(attachment.getReference())); } catch (IdUnusedException e) { try { ContentResource oAttachment = contentHostingService.getResource(oAttachmentId); try { if (contentHostingService.isAttachmentResource(nAttachmentId)) { // add the new resource into attachment collection area ContentResource attachment = contentHostingService.addAttachmentResource( Validator.escapeResourceName(oAttachment.getProperties().getProperty(ResourceProperties.PROP_DISPLAY_NAME)), //toolManager.getCurrentPlacement().getContext(), toContext, //don't use toolManager.getCurrentPlacement()! toolManager.getTool("sakai.announcements").getTitle(), oAttachment.getContentType(), oAttachment.getContent(), oAttachment.getProperties()); // add to attachment list nAttachments.add(m_entityManager.newReference(attachment.getReference())); } else { // add the new resource into resource area ContentResource attachment = contentHostingService.addResource( Validator.escapeResourceName(oAttachment.getProperties().getProperty(ResourceProperties.PROP_DISPLAY_NAME)), //toolManager.getCurrentPlacement().getContext(), toContext, //don't use toolManager.getCurrentPlacement()! 1, oAttachment.getContentType(), oAttachment.getContent(), oAttachment.getProperties(), NotificationService.NOTI_NONE); // add to attachment list nAttachments.add(m_entityManager.newReference(attachment.getReference())); } } catch (Exception eeAny) { // if the new resource cannot be added M_log.warn(" cannot add new attachment with id=" + nAttachmentId); } } catch (Exception eAny) { // if cannot find the original attachment, do nothing. M_log.warn(" cannot find the original attachment with id=" + oAttachmentId); } } catch (Exception any) { M_log.info(any.getMessage()); } } else { nAttachments.add(oAttachmentRef); } } nMessageHeader.replaceAttachments(nAttachments); // properties ResourcePropertiesEdit p = nMessage.getPropertiesEdit(); p.clear(); p.addAll(oProperties); // complete the edit nChannel.commitMessage(nMessage, NotificationService.NOTI_NONE); // transversalMap.put(oMessage.getReference(), nMessage.getReference()); } } } // if transferSynopticOptions(fromContext, toContext); } catch (IdUnusedException e) { M_log.warn(" MessageChannel " + fromContext + " cannot be found. "); } catch (Exception any) { M_log.warn(".importResources(): exception in handling " + serviceName() + " : ", any); } return null; } /** * {@inheritDoc} */ public void updateEntityReferences(String toContext, Map<String, String> transversalMap){ if(transversalMap != null && transversalMap.size() > 0){ try { Set<Entry<String, String>> entrySet = (Set<Entry<String, String>>) transversalMap.entrySet(); String channelId = ServerConfigurationService.getString(ANNOUNCEMENT_CHANNEL_PROPERTY, null); String toSiteId = toContext; if (channelId == null) { channelId = channelReference(toSiteId, SiteService.MAIN_CONTAINER); try { AnnouncementChannel aChannel = getAnnouncementChannel(channelId); //need to clear the cache to grab the newly saved messages m_threadLocalManager.set(aChannel.getReference() + ".msgs", null); List mList = aChannel.getMessages(null, true); for(Iterator iter = mList.iterator(); iter.hasNext();) { AnnouncementMessage msg = (AnnouncementMessage) iter.next(); String msgBody = msg.getBody(); boolean updated = false; Iterator<Entry<String, String>> entryItr = entrySet.iterator(); while(entryItr.hasNext()) { Entry<String, String> entry = (Entry<String, String>) entryItr.next(); String fromContextRef = entry.getKey(); if(msgBody.contains(fromContextRef)){ msgBody = msgBody.replace(fromContextRef, entry.getValue()); updated = true; } } if(updated){ AnnouncementMessageEdit editMsg = aChannel.editAnnouncementMessage(msg.getId()); editMsg.setBody(msgBody); aChannel.commitMessage(editMsg, NotificationService.NOTI_NONE); } } } catch(Exception e) { M_log.debug("Unable to remove Announcements " + e); } } } catch (Exception e) { M_log.debug("transferCopyEntities: End removing Announcement data"); } } } /********************************************************************************************************************************************************************************************************************************************************** * AnnouncementChannel implementation *********************************************************************************************************************************************************************************************************************************************************/ public class BaseAnnouncementChannelEdit extends BaseMessageChannelEdit implements AnnouncementChannelEdit { /** * Construct with a reference. * * @param ref * The channel reference. */ public BaseAnnouncementChannelEdit(String ref) { super(ref); } // BaseAnnouncementChannelEdit /** * Construct as a copy of another message. * * @param other * The other message to copy. */ public BaseAnnouncementChannelEdit(MessageChannel other) { super(other); } // BaseAnnouncementChannelEdit /** * Construct from a channel (and possibly messages) already defined in XML in a DOM tree. The Channel is added to storage. * * @param el * The XML DOM element defining the channel. */ public BaseAnnouncementChannelEdit(Element el) { super(el); } // BaseAnnouncementChannelEdit /** * Return a specific announcement channel message, as specified by message name. * * @param messageId * The id of the message to get. * @return the AnnouncementMessage that has the specified id. * @exception IdUnusedException * If this name is not a defined message in this announcement channel. * @exception PermissionException * If the user does not have any permissions to read the message. */ public AnnouncementMessage getAnnouncementMessage(String messageId) throws IdUnusedException, PermissionException { AnnouncementMessage msg = (AnnouncementMessage) getMessage(messageId); // filter out drafts not by this user (unless this user is a super user or has access_draft ability) if ((msg.getAnnouncementHeader()).getDraft() && (!securityService.isSuperUser()) && (!msg.getHeader().getFrom().getId().equals(sessionManager.getCurrentSessionUserId())) && (!unlockCheck(SECURE_READ_DRAFT, msg.getReference()))) { throw new PermissionException(sessionManager.getCurrentSessionUserId(), SECURE_READ, msg.getReference()); } return msg; } // getAnnouncementMessage /** * Return a list of all or filtered messages in the channel. The order in which the messages will be found in the iteration is by date, oldest first if ascending is true, newest first if ascending is false. * * @param filter * A filtering object to accept messages, or null if no filtering is desired. * @param ascending * Order of messages, ascending if true, descending if false * @return a list on channel Message objects or specializations of Message objects (may be empty). * @exception PermissionException * if the user does not have read permission to the channel. */ public List getMessages(Filter filter, boolean ascending) throws PermissionException { // filter out drafts this user cannot see filter = new PrivacyFilter(filter); return super.getMessages(filter, ascending); } // getMessages /** * A (AnnouncementMessageEdit) cover for editMessage. Return a specific channel message, as specified by message name, locked for update. Must commitEdit() to make official, or cancelEdit() when done! * * @param messageId * The id of the message to get. * @return the Message that has the specified id. * @exception IdUnusedException * If this name is not a defined message in this channel. * @exception PermissionException * If the user does not have any permissions to read the message. * @exception InUseException * if the current user does not have permission to mess with this user. */ public AnnouncementMessageEdit editAnnouncementMessage(String messageId) throws IdUnusedException, PermissionException, InUseException { return (AnnouncementMessageEdit) editMessage(messageId); } // editAnnouncementMessage /** * A cover for removeMessage. Deletes the messages specified by the message id. * * @param messageId * The id of the message to get. * @exception PermissionException * If the user does not have any permissions to delete the message. */ public void removeAnnouncementMessage(String messageId) throws PermissionException { removeMessage(messageId); //return (AnnouncementMessageEdit) removeMessage(messageId); } // editAnnouncementMessage /** * A (AnnouncementMessageEdit) cover for addMessage. Add a new message to this channel. Must commitEdit() to make official, or cancelEdit() when done! * * @return The newly added message, locked for update. * @exception PermissionException * If the user does not have write permission to the channel. */ public AnnouncementMessageEdit addAnnouncementMessage() throws PermissionException { return (AnnouncementMessageEdit) addMessage(); } // addAnnouncementMessage /** * a (AnnouncementMessage) cover for addMessage to add a new message to this channel. * * @param subject * The message header subject. * @param draft * The message header draft indication. * @param attachments * The message header attachments, a vector of Reference objects. * @param body * The message body. * @return The newly added message. * @exception PermissionException * If the user does not have write permission to the channel. */ public AnnouncementMessage addAnnouncementMessage(String subject, boolean draft, List attachments, String body) throws PermissionException { AnnouncementMessageEdit edit = (AnnouncementMessageEdit) addMessage(); AnnouncementMessageHeaderEdit header = edit.getAnnouncementHeaderEdit(); edit.setBody(body); header.replaceAttachments(attachments); header.setSubject(subject); header.setDraft(draft); commitMessage(edit); return edit; } // addAnnouncementMessage } // class BaseAnnouncementChannelEdit /********************************************************************************************************************************************************************************************************************************************************** * AnnouncementMessage implementation *********************************************************************************************************************************************************************************************************************************************************/ public class BaseAnnouncementMessageEdit extends BaseMessageEdit implements AnnouncementMessageEdit { /** * Construct. * * @param channel * The channel in which this message lives. * @param id * The message id. */ public BaseAnnouncementMessageEdit(MessageChannel channel, String id) { super(channel, id); } // BaseAnnouncementMessageEdit /** * Construct as a copy of another message. * * @param other * The other message to copy. */ public BaseAnnouncementMessageEdit(MessageChannel channel, Message other) { super(channel, other); } // BaseAnnouncementMessageEdit /** * Construct from an existing definition, in xml. * * @param channel * The channel in which this message lives. * @param el * The message in XML in a DOM element. */ public BaseAnnouncementMessageEdit(MessageChannel channel, Element el) { super(channel, el); } // BaseAnnouncementMessageEdit /** * Access the announcement message header. * * @return The announcement message header. */ public AnnouncementMessageHeader getAnnouncementHeader() { return (AnnouncementMessageHeader) getHeader(); } // getAnnouncementHeader /** * Access the announcement message header. * * @return The announcement message header. */ public AnnouncementMessageHeaderEdit getAnnouncementHeaderEdit() { return (AnnouncementMessageHeaderEdit) getHeader(); } // getAnnouncementHeaderEdit } // class BasicAnnouncementMessageEdit /********************************************************************************************************************************************************************************************************************************************************** * AnnouncementMessageHeaderEdit implementation *********************************************************************************************************************************************************************************************************************************************************/ public class BaseAnnouncementMessageHeaderEdit extends BaseMessageHeaderEdit implements AnnouncementMessageHeaderEdit { /** The subject for the announcement. */ protected String m_subject = null; /** * Construct. * * @param id * The unique (within the channel) message id. * @param from * The User who sent the message to the channel. * @param attachments * The message header attachments, a vector of Reference objects. */ public BaseAnnouncementMessageHeaderEdit(Message msg, String id) { super(msg, id); } // BaseAnnouncementMessageHeaderEdit /** * Construct, from an already existing XML DOM element. * * @param el * The header in XML in a DOM element. */ public BaseAnnouncementMessageHeaderEdit(Message msg, Element el) { super(msg, el); // extract the subject m_subject = el.getAttribute("subject"); } // BaseAnnouncementMessageHeaderEdit /** * Construct as a copy of another header. * * @param other * The other message header to copy. */ public BaseAnnouncementMessageHeaderEdit(Message msg, MessageHeader other) { super(msg, other); m_subject = ((AnnouncementMessageHeader) other).getSubject(); } // BaseAnnouncementMessageHeaderEdit /** * Access the subject of the announcement. * * @return The subject of the announcement. */ public String getSubject() { return ((m_subject == null) ? "" : m_subject); } // getSubject /** * Set the subject of the announcement. * * @param subject * The subject of the announcement. */ public void setSubject(String subject) { if (StringUtil.different(subject, m_subject)) { m_subject = subject; } } // setSubject /** * Serialize the resource into XML, adding an element to the doc under the top of the stack element. * * @param doc * The DOM doc to contain the XML (or null for a string return). * @param stack * The DOM elements, the top of which is the containing element of the new "resource" element. * @return The newly added element. */ public Element toXml(Document doc, Stack stack) { // get the basic work done Element header = super.toXml(doc, stack); // add draft, subject header.setAttribute("subject", getSubject()); header.setAttribute("draft", new Boolean(getDraft()).toString()); return header; } // toXml } // BaseAnnouncementMessageHeader /** * A filter that will reject announcement message drafts not from the current user, and otherwise use another filter, if defined, for acceptance. */ protected class PrivacyFilter implements Filter { /** The other filter to check with. May be null. */ protected Filter m_filter = null; /** * Construct * * @param filter * The other filter we check with. */ public PrivacyFilter(Filter filter) { m_filter = filter; } // PrivacyFilter /** * Does this object satisfy the criteria of the filter? * * @return true if the object is accepted by the filter, false if not. */ public boolean accept(Object o) { // first if o is a announcement message that's a draft from another user, reject it if (o instanceof AnnouncementMessage) { AnnouncementMessage msg = (AnnouncementMessage) o; if ((msg.getAnnouncementHeader()).getDraft() && (!securityService.isSuperUser()) && (!msg.getHeader().getFrom().getId().equals(sessionManager.getCurrentSessionUserId())) && (!unlockCheck(SECURE_READ_DRAFT, msg.getReference()))) { return false; } } // now, use the real filter, if present if (m_filter != null) return m_filter.accept(o); return true; } // accept } // PrivacyFilter /* (non-Javadoc) * @see org.sakaiproject.entity.api.EntitySummary#summarizableToolIds() */ public String[] summarizableToolIds() { return new String[] { "sakai.announcements", "sakai.motd" }; } public String getSummarizableReference(String siteId, String toolIdentifier) { if ( "sakai.motd".equals(toolIdentifier) ) { return "/announcement/channel/!site/motd"; } else { return super.getSummarizableReference(siteId, toolIdentifier); } } public void transferCopyEntities(String fromContext, String toContext, List ids, boolean cleanup) { transferCopyEntitiesRefMigrator(fromContext, toContext, ids, cleanup); } public Map<String, String> transferCopyEntitiesRefMigrator(String fromContext, String toContext, List ids, boolean cleanup) { // Map<String, String> transversalMap = new HashMap<String, String>(); try { if(cleanup == true) { String channelId = ServerConfigurationService.getString(ANNOUNCEMENT_CHANNEL_PROPERTY, null); String toSiteId = toContext; if (channelId == null) { channelId = channelReference(toSiteId, SiteService.MAIN_CONTAINER); try { AnnouncementChannel aChannel = getAnnouncementChannel(channelId); List mList = aChannel.getMessages(null, true); for(Iterator iter = mList.iterator(); iter.hasNext();) { AnnouncementMessage msg = (AnnouncementMessage) iter.next(); aChannel.removeMessage(msg.getId()); } } catch(Exception e) { M_log.debug("Unable to remove Announcements " + e); } } } } catch (Exception e) { M_log.debug("transferCopyEntities: End removing Announcement data"); } transferCopyEntitiesRefMigrator(fromContext, toContext, ids); return null; } public void clearMessagesCache(String channelRef){ m_threadLocalManager.set(channelRef + ".msgs", null); } }