/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/kernel/trunk/kernel-impl/src/main/java/org/sakaiproject/content/impl/SiteEmailNotificationContent.java $ * $Id: SiteEmailNotificationContent.java 122028 2013-04-01 19:49:35Z azeckoski@unicon.net $ *********************************************************************************** * * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008 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.content.impl; import java.util.List; import org.sakaiproject.authz.api.SecurityService; import org.sakaiproject.component.api.ServerConfigurationService; import org.sakaiproject.component.cover.ComponentManager; import org.sakaiproject.content.api.ContentCollection; import org.sakaiproject.content.api.ContentHostingService; import org.sakaiproject.content.api.ContentResource; import org.sakaiproject.entity.api.Entity; import org.sakaiproject.entity.api.EntityManager; import org.sakaiproject.entity.api.Reference; import org.sakaiproject.entity.api.ResourceProperties; import org.sakaiproject.event.api.Event; import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.exception.PermissionException; import org.sakaiproject.exception.TypeException; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.api.SiteService; import org.sakaiproject.user.api.User; import org.sakaiproject.user.api.UserDirectoryService; import org.sakaiproject.util.EmailNotification; import org.sakaiproject.util.api.FormattedText; import org.sakaiproject.util.Resource; import org.sakaiproject.util.ResourceLoader; import org.sakaiproject.util.SiteEmailNotification; import org.sakaiproject.util.StringUtil; /** * <p> * SiteEmailNotificationContent fills the notification message and headers with details from the content change that triggered the notification event. * </p> * * @author Sakai Software Development Team */ public class SiteEmailNotificationContent extends SiteEmailNotification { /* property bundles */ private static final String DEFAULT_RESOURCECLASS = "org.sakaiproject.localization.util.SiteemaconProperties"; private static final String DEFAULT_RESOURCEBUNDLE = "org.sakaiproject.localization.bundle.siteemacon.siteemacon"; private static final String RESOURCECLASS = "resource.class.siteemacon"; private static final String RESOURCEBUNDLE = "resource.bundle.siteemacon"; private String resourceClass; private String resourceBundle; private ResourceLoader rb; private static Object LOCK = new Object(); private static FormattedText formattedText; private SecurityService securityService; private ServerConfigurationService serverConfigurationService; private ContentHostingService contentHostingService; private EntityManager entityManager; private SiteService siteService; protected static FormattedText getFormattedText() { if (formattedText == null) { synchronized (LOCK) { FormattedText component = (FormattedText) ComponentManager.get(FormattedText.class); if (component == null) { throw new IllegalStateException("Unable to find the FormattedText using the ComponentManager"); } else { formattedText = component; } } } return formattedText; } protected String plainTextContent(Event event) { return generateContentForType(false, event); } protected String htmlContent(Event event) { return generateContentForType(true, event); } private String generateContentForType(boolean shouldProduceHtml, Event event) { // get the content & properties Reference ref = entityManager.newReference(event.getResource()); // TODO: ResourceProperties props = ref.getProperties(); // get the function String function = event.getEvent(); String subject = getSubject(event); // use either the configured site, or if not configured, the site (context) of the resource String siteId = (getSite() != null) ? getSite() : ref.getContext(); // get a site title String title = siteId; try { Site site = siteService.getSite(siteId); title = site.getTitle(); } catch (Exception ignore) { } StringBuilder buf = new StringBuilder(); addMessageText(buf, ref, subject, title, function, shouldProduceHtml); return buf.toString(); } public SiteEmailNotificationContent(SecurityService securityService, ServerConfigurationService serverConfigurationService, ContentHostingService contentHostingService, EntityManager entityManager, SiteService siteService) { this.securityService = securityService; this.serverConfigurationService = serverConfigurationService; this.contentHostingService = contentHostingService; this.entityManager = entityManager; this.siteService = siteService; loadResources(serverConfigurationService); } private void loadResources(ServerConfigurationService serverConfigurationService) { resourceClass = serverConfigurationService.getString(RESOURCECLASS, DEFAULT_RESOURCECLASS); resourceBundle = serverConfigurationService.getString(RESOURCEBUNDLE, DEFAULT_RESOURCEBUNDLE); rb = new Resource().getLoader(resourceClass, resourceBundle); } /** * The preferred form for construction is to supply the needed items rather than having to do a lookup. This constructor was * left in place for compatibility with any custom tool that might currently be using it, but should be considered deprecated. * * @deprecated */ public SiteEmailNotificationContent(String siteId) { super(siteId); this.securityService = (SecurityService) ComponentManager.get("org.sakaiproject.authz.api.SecurityService"); this.contentHostingService = (ContentHostingService) ComponentManager.get("org.sakaiproject.content.api.ContentHostingService"); this.entityManager = (EntityManager) ComponentManager.get("org.sakaiproject.entity.api.EntityManager"); this.siteService = (SiteService) ComponentManager.get("org.sakaiproject.site.api.SiteService"); this.serverConfigurationService = (ServerConfigurationService) ComponentManager.get("org.sakaiproject.component.api.ServerConfigurationService"); loadResources(serverConfigurationService); } /** * @inheritDoc */ protected String getResourceAbility() { return contentHostingService.EVENT_RESOURCE_READ; } protected EmailNotification makeEmailNotification() { return new SiteEmailNotificationContent(securityService, serverConfigurationService, contentHostingService, entityManager, siteService); } /** * @inheritDoc */ private void addMessageText(StringBuilder buf, Reference ref, String subject, String title, String function, boolean doHtml) { ResourceProperties props = ref.getProperties(); String resourceName = props.getPropertyFormatted(ResourceProperties.PROP_DISPLAY_NAME); String description = props.getPropertyFormatted(ResourceProperties.PROP_DESCRIPTION); String url = ref.getUrl(); String blankLine = "\n\n"; String newLine = "\n"; if ( doHtml ) { title = getFormattedText().escapeHtmlFormattedTextarea(title); //subject = FormattedText.escapeHtmlFormattedTextarea(subject); resourceName = getFormattedText().escapeHtmlFormattedTextarea(resourceName); description = getFormattedText().escapeHtmlFormattedTextarea(description); blankLine = "\n</p><p>\n"; newLine = "<br/>\n"; } // get the resource copyright alert property boolean copyrightAlert = props.getProperty(ResourceProperties.PROP_COPYRIGHT_ALERT) != null ? true : false; // Now build up the message text. if (doHtml) { buf.append("<p>"); } if (contentHostingService.EVENT_RESOURCE_AVAILABLE.equals(function)) { buf.append(rb.getString("anewres")); } else { buf.append(rb.getString("anewres2")); } buf.append(" "); buf.append(rb.getString("the")); buf.append(" \""); buf.append(title); buf.append("\" "); buf.append(rb.getString("sitat")); buf.append(" "); if ( doHtml ) { buf.append("<a href=\""); buf.append(serverConfigurationService.getPortalUrl()); buf.append("\">"); buf.append(serverConfigurationService.getString("ui.service", "Sakai")); buf.append("</a>"); } else { buf.append(serverConfigurationService.getString("ui.service", "Sakai")); buf.append(" ("); buf.append(serverConfigurationService.getPortalUrl()); buf.append(")"); } buf.append(blankLine); // add location String path = constructPath(ref.getReference()); buf.append(rb.getString("locsit") + " \"" + title + "\" > " + rb.getString("reso") + " " + path + " > "); if ( doHtml ) { buf.append("<a href=\""); buf.append(url); buf.append("\">"); buf.append(resourceName); buf.append("</a>"); } else { buf.append(resourceName); } if (copyrightAlert) { buf.append(" (c)"); } buf.append(blankLine); // resource description if ((description != null) && (description.length() > 0)) { buf.append(rb.getString("descrip") + " " + description); buf.append(blankLine); } // add a reference to the resource for non-HTML if ( ! doHtml ) { buf.append("\n" + rb.getString("resour") + " " + resourceName); if (copyrightAlert) { buf.append(" (c)"); } buf.append(" " + url); buf.append("\n\n"); // End on a blank line } // Add the tag if (doHtml) { buf.append("<hr/>" + newLine + rb.getString("this") + " " + serverConfigurationService.getString("ui.service", "Sakai") + " (<a href=\"" + serverConfigurationService.getPortalUrl() + "\" >" + serverConfigurationService.getPortalUrl() + "</a>) " + rb.getString("forthe") + " " + title + " " + rb.getString("site") + newLine + rb.getString("youcan")); } else { buf.append(rb.getString("separator") + newLine + rb.getString("this") + " " + serverConfigurationService.getString("ui.service", "Sakai") + " (" + serverConfigurationService.getPortalUrl() + ") " + rb.getString("forthe") + " " + title + " " + rb.getString("site") + newLine + rb.getString("youcan")); } if (doHtml) { buf.append("</p>"); } } /** * @inheritDoc */ protected List<String> getHeaders(Event event) { List<String> rv = super.getHeaders(event); // the Subject rv.add("Subject: " + getSubject(event)); // from rv.add(getFrom(event)); // to rv.add(getTo(event)); return rv; } /** * @inheritDoc */ protected String getTag(String title, boolean shouldUseHtml) { // tbd: move here from generateContentForType return ""; } /** * Form a "Bread Crumb" style path showing the folders in which this referenced resource lives. * * @param ref * The reference string to the resource. * @return The path string for this resource. */ protected String constructPath(String ref) { StringBuilder buf = new StringBuilder(); // expect the ref to be /content/group/site/folder/folder2/folderEtc/file.ext String[] parts = StringUtil.split(ref, Entity.SEPARATOR); // 0 is null, 1 is "content", 2 is "group" or whatever, 3 is the site, the last is the file name if (parts.length > 4) { // grow this collection id as we descend into the collections StringBuilder root = new StringBuilder(Entity.SEPARATOR + parts[2] + Entity.SEPARATOR + parts[3] + Entity.SEPARATOR); // take all the collection parts for (int i = 4; i < parts.length - 1; i++) { buf.append(" > "); String collectionId = parts[i]; root.append(collectionId + Entity.SEPARATOR); try { // get the display name ContentCollection collection = contentHostingService.getCollection(root.toString()); buf.append(collection.getProperties().getPropertyFormatted(ResourceProperties.PROP_DISPLAY_NAME)); } catch (Exception any) { // use the id if there's a problem buf.append(collectionId); } } } return buf.toString(); } /** * Get the subject for the email. * * @param event * The event that matched criteria to cause the notification. * @return the subject for the email. */ protected String getSubject(Event event) { Reference ref = entityManager.newReference(event.getResource()); ResourceProperties props = ref.getProperties(); // get the function String function = event.getEvent(); // use either the configured site, or if not configured, the site (context) of the resource String siteId = (getSite() != null) ? getSite() : ref.getContext(); // get a site title String title = siteId; try { Site site = siteService.getSite(siteId); title = site.getTitle(); } catch (Exception ignore) { } // use the message's subject String resourceName = props.getPropertyFormatted(ResourceProperties.PROP_DISPLAY_NAME); return "[ " + title + " - " + (contentHostingService.EVENT_RESOURCE_AVAILABLE.equals(function) ? rb.getString("new") : rb.getString("chan")) + " " + rb.getString("reso2") + " ] " + resourceName; } /** * Add to the user list any other users who should be notified about this ref's change. * * @param users * The user list, already populated based on site visit and resource ability. * @param ref * The entity reference. */ protected void addSpecialRecipients(List<User> users, Reference ref) { // include any users who have AnnouncementService.SECURE_ALL_GROUPS and getResourceAbility() in the context String contextRef = siteService.siteReference(ref.getContext()); // get the list of users who have SECURE_ALL_GROUPS List<User> allGroupUsers = securityService.unlockUsers(contentHostingService.AUTH_RESOURCE_ALL_GROUPS, contextRef); // filter down by the permission if (getResourceAbility() != null) { boolean hidden = false; if (!contentHostingService.isCollection(ref.getId())) { try { ContentResource resource = contentHostingService.getResource(ref.getId()); hidden = resource.isHidden(); //we need to check the containing folder too ContentCollection folder = resource.getContainingCollection(); if (folder.isHidden()) { hidden = folder.isHidden(); } } catch (PermissionException e) { e.printStackTrace(); } catch (IdUnusedException e) { e.printStackTrace(); } catch (TypeException e) { e.printStackTrace(); } } List<User> allGroupUsers2 = null; if (!hidden) { //resource is visible get all users allGroupUsers2 = securityService.unlockUsers(getResourceAbility(), contextRef); } else { allGroupUsers2 = securityService.unlockUsers(contentHostingService.AUTH_RESOURCE_HIDDEN, contextRef); //we need to remove all users from the list as that is too open in this case users.clear(); } allGroupUsers.retainAll(allGroupUsers2); } // remove any in the list already allGroupUsers.removeAll(users); // combine users.addAll(allGroupUsers); } }