0001: /**********************************************************************************
0002: * $URL: https://source.sakaiproject.org/svn/podcasts/tags/sakai_2-4-1/podcasts-impl/impl/src/java/org/sakaiproject/component/app/podcasts/PodcastServiceImpl.java $
0003: * $Id: PodcastServiceImpl.java 29142 2007-04-19 00:54:27Z ajpoland@iupui.edu $
0004: ***********************************************************************************
0005: *
0006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
0007: *
0008: * Licensed under the Educational Community License, Version 1.0 (the "License");
0009: * you may not use this file except in compliance with the License.
0010: * You may obtain a copy of the License at
0011: *
0012: * http://www.opensource.org/licenses/ecl1.php
0013: *
0014: * Unless required by applicable law or agreed to in writing, software
0015: * distributed under the License is distributed on an "AS IS" BASIS,
0016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: * See the License for the specific language governing permissions and
0018: * limitations under the License.
0019: *
0020: **********************************************************************************/package org.sakaiproject.component.app.podcasts;
0021:
0022: import java.text.SimpleDateFormat;
0023: import java.util.ArrayList;
0024: import java.util.Calendar;
0025: import java.util.Collections;
0026: import java.util.Date;
0027: import java.util.Iterator;
0028: import java.util.List;
0029: import java.util.ResourceBundle;
0030:
0031: import org.apache.commons.logging.Log;
0032: import org.apache.commons.logging.LogFactory;
0033: import org.sakaiproject.api.app.podcasts.PodcastService;
0034: import org.sakaiproject.authz.api.SecurityAdvisor;
0035: import org.sakaiproject.authz.api.SecurityAdvisor.SecurityAdvice;
0036: import org.sakaiproject.authz.cover.SecurityService;
0037: import org.sakaiproject.content.api.ContentCollection;
0038: import org.sakaiproject.content.api.ContentCollectionEdit;
0039: import org.sakaiproject.content.api.ContentHostingService;
0040: import org.sakaiproject.content.api.ContentResource;
0041: import org.sakaiproject.content.api.ContentResourceEdit;
0042: import org.sakaiproject.entity.api.Entity;
0043: import org.sakaiproject.entity.api.EntityPropertyNotDefinedException;
0044: import org.sakaiproject.entity.api.EntityPropertyTypeException;
0045: import org.sakaiproject.entity.api.Reference;
0046: import org.sakaiproject.entity.api.ResourceProperties;
0047: import org.sakaiproject.entity.api.ResourcePropertiesEdit;
0048: import org.sakaiproject.event.api.Event;
0049: import org.sakaiproject.event.api.NotificationService;
0050: import org.sakaiproject.event.cover.EventTrackingService;
0051: import org.sakaiproject.exception.IdInvalidException;
0052: import org.sakaiproject.exception.IdLengthException;
0053: import org.sakaiproject.exception.IdUniquenessException;
0054: import org.sakaiproject.exception.IdUnusedException;
0055: import org.sakaiproject.exception.IdUsedException;
0056: import org.sakaiproject.exception.InUseException;
0057: import org.sakaiproject.exception.InconsistentException;
0058: import org.sakaiproject.exception.OverQuotaException;
0059: import org.sakaiproject.exception.PermissionException;
0060: import org.sakaiproject.exception.ServerOverloadException;
0061: import org.sakaiproject.exception.TypeException;
0062: import org.sakaiproject.site.cover.SiteService;
0063: import org.sakaiproject.time.api.Time;
0064: import org.sakaiproject.time.cover.TimeService;
0065: import org.sakaiproject.tool.api.ToolManager;
0066: import org.sakaiproject.tool.cover.SessionManager;
0067: import org.sakaiproject.user.cover.UserDirectoryService;
0068: import org.sakaiproject.util.Validator;
0069:
0070: public class PodcastServiceImpl implements PodcastService {
0071: /** Used to retrieve global podcast title and description from podcast folder collection **/
0072: private final String PODFEED_TITLE = "podfeedTitle";
0073: private final String PODFEED_DESCRIPTION = "podfeedDescription";
0074:
0075: /** Used to grab the default feed title prefix */
0076: private final String FEED_TITLE_STRING = "feed_title";
0077:
0078: /** Used to get the default feed description pieces from the message bundle */
0079: private final String FEED_DESC1_STRING = "feed_desc1";
0080: private final String FEED_DESC2_STRING = "feed_desc2";
0081:
0082: /** Used to pull message bundle */
0083: private final String PODFEED_MESSAGE_BUNDLE = "org.sakaiproject.api.podcasts.bundle.Messages";
0084:
0085: /** Used for event tracking of podcasts - adding a podcast **/
0086: private final String EVENT_ADD_PODCAST = "podcast.add";
0087:
0088: /** Used for event tracking of podcasts - revisiong a podcast **/
0089: private final String EVENT_REVISE_PODCAST = "podcast.revise";
0090:
0091: /** Used for event tracking of podcasts - deleting a podcast **/
0092: private final String EVENT_DELETE_PODCAST = "podcast.delete";
0093:
0094: /** Options. 0 = Display to non-members, 1 = Display to Site * */
0095: private final int PUBLIC = 0;
0096: private final int SITE = 1;
0097:
0098: private Log LOG = LogFactory.getLog(PodcastServiceImpl.class);
0099: private ResourceBundle resbud = ResourceBundle
0100: .getBundle(PODFEED_MESSAGE_BUNDLE);
0101:
0102: private Reference siteRef;
0103:
0104: // injected beans
0105: private ContentHostingService contentHostingService;
0106: private ToolManager toolManager;
0107: private SessionManager sessionManager;
0108:
0109: // FUTURE; TO BE IMPLEMENTED
0110: // private NotificationService notificationService;
0111:
0112: /** Needed for when Notification implemented * */
0113: protected String m_relativeAccessPoint = null;
0114:
0115: PodcastServiceImpl() {
0116: }
0117:
0118: /** Injects ContentHostingService into this service **/
0119: public void setContentHostingService(ContentHostingService chs) {
0120: this .contentHostingService = chs;
0121: }
0122:
0123: /** Injects ToolManager into this service **/
0124: public void setToolManager(ToolManager tm) {
0125: toolManager = tm;
0126: }
0127:
0128: /**
0129: * Retrieve the site id
0130: */
0131: public String getSiteId() {
0132: return toolManager.getCurrentPlacement().getContext();
0133: }
0134:
0135: /**
0136: * Retrieve the current user id
0137: */
0138: public String getUserId() {
0139: return SessionManager.getCurrentSessionUserId();
0140: }
0141:
0142: /**
0143: * Retrieve the current user display name
0144: */
0145: public String getUserName() {
0146: return UserDirectoryService.getCurrentUser().getDisplayName();
0147: }
0148:
0149: /**
0150: * Returns the site URL as a string
0151: *
0152: * @return
0153: * String containing the sites URL
0154: */
0155: public String getSiteURL() {
0156: return contentHostingService.getEntityUrl(siteRef);
0157: }
0158:
0159: /**
0160: * Returns only those podcasts whose DISPLAY_DATE property is today or earlier
0161: *
0162: * @param resourcesList
0163: * List of podcasts
0164: *
0165: * @return List
0166: * List of podcasts whose DISPLAY_DATE is today or before
0167: */
0168: public List filterPodcasts(List resourcesList) {
0169:
0170: List filteredPodcasts = new ArrayList();
0171:
0172: final Time now = TimeService.newTime();
0173:
0174: // loop to check if DISPLAY_DATE has been set. If not, set it
0175: final Iterator podcastIter = resourcesList.iterator();
0176: ContentResource aResource = null;
0177: ResourceProperties itsProperties = null;
0178:
0179: // for each bean
0180: while (podcastIter.hasNext()) {
0181: // get its properties from ContentHosting
0182: aResource = (ContentResource) podcastIter.next();
0183: itsProperties = aResource.getProperties();
0184:
0185: try {
0186: final Time podcastTime = itsProperties
0187: .getTimeProperty(DISPLAY_DATE);
0188:
0189: if (podcastTime.getTime() <= now.getTime()) {
0190: filteredPodcasts.add(aResource);
0191:
0192: }
0193: } catch (Exception e) {
0194: // catches EntityPropertyNotDefinedException, EntityPropertyTypeException
0195: // any problems, skip this one
0196: LOG.warn(
0197: "EntityPropertyNotDefinedException for podcast item: "
0198: + aResource + ". SKIPPING...", e);
0199:
0200: }
0201: }
0202:
0203: return filteredPodcasts;
0204: }
0205:
0206: /**
0207: * Get ContentCollection object for podcasts
0208: *
0209: * @param String
0210: * The siteId to grab the correct podcasts
0211: *
0212: * @return ContentCollection The ContentCollection object containing the
0213: * podcasts
0214: */
0215: public ContentCollection getContentCollection(String siteId)
0216: throws IdUnusedException, PermissionException {
0217:
0218: ContentCollection collection = null;
0219:
0220: try {
0221: final String podcastsCollection = retrievePodcastFolderId(siteId);
0222:
0223: collection = contentHostingService
0224: .getCollection(podcastsCollection);
0225:
0226: } catch (TypeException e) {
0227: LOG.error(
0228: "TypeException when trying to get podcast collection for site: "
0229: + siteId + ": " + e.getMessage(), e);
0230: throw new Error(e);
0231:
0232: } catch (IdUnusedException e) {
0233: LOG.warn(
0234: "IdUnusedException while attempting to get podcast collection. "
0235: + "for site: " + siteId + ". "
0236: + e.getMessage(), e);
0237: throw e;
0238:
0239: } catch (PermissionException e) {
0240: // catches PermissionException, IdUnusedException
0241: LOG.warn(
0242: "PermissionException when trying to get podcast collection for site: "
0243: + siteId + ": " + e.getMessage(), e);
0244: throw e;
0245: }
0246:
0247: return collection;
0248: }
0249:
0250: /**
0251: * Get ContentCollection object for podcasts
0252: *
0253: * @param String
0254: * The siteId to grab the correct podcasts
0255: *
0256: * @return ContentCollection
0257: * The ContentCollection object containing the podcasts
0258: */
0259: public ContentCollectionEdit getContentCollectionEditable(
0260: String siteId) throws IdUnusedException,
0261: PermissionException, InUseException {
0262:
0263: ContentCollectionEdit collection = null;
0264: String podcastsCollection = "";
0265:
0266: try {
0267: podcastsCollection = retrievePodcastFolderId(siteId);
0268:
0269: collection = contentHostingService
0270: .editCollection(podcastsCollection);
0271:
0272: } catch (TypeException e) {
0273: LOG.error(
0274: "TypeException when trying to get podcast collection for site: "
0275: + siteId + ": " + e.getMessage(), e);
0276: throw new Error(e);
0277:
0278: } catch (IdUnusedException e) {
0279: LOG.error(
0280: "IdUnusedException when trying to get podcast collection for edit in site: "
0281: + siteId + " " + e.getMessage(), e);
0282: throw e;
0283:
0284: } catch (PermissionException e) {
0285: LOG.error(
0286: "PermissionException when trying to get podcast collection for edit in site: "
0287: + siteId + " " + e.getMessage(), e);
0288: throw e;
0289:
0290: } catch (InUseException e) {
0291: LOG.warn(
0292: "InUseException attempting to retrieve podcast folder "
0293: + podcastsCollection + " for site: "
0294: + siteId + ". " + e.getMessage(), e);
0295: throw e;
0296:
0297: }
0298:
0299: return collection;
0300: }
0301:
0302: /**
0303: * Remove on file resources from list of potential podcasts
0304: *
0305: * @param resourcesList
0306: * The list of potential podcasts
0307: *
0308: * @return List
0309: * List of files to make up the podcasts
0310: */
0311: public List filterResources(List resourcesList) {
0312: List filteredResources = new ArrayList();
0313: ContentResource aResource = null;
0314:
0315: // loop to check if objects are collections (folders) or resources
0316: // (files)
0317: final Iterator podcastIter = resourcesList.iterator();
0318:
0319: // for each bean
0320: while (podcastIter.hasNext()) {
0321: // get its properties from ContentHosting
0322: try {
0323:
0324: aResource = (ContentResource) podcastIter.next();
0325:
0326: if (aResource.isResource() /*&& aResource.getProperties().getProperty(ResourceProperties.PROP_ORIGINAL_FILENAME) != null */) {
0327: filteredResources.add(aResource);
0328:
0329: }
0330: } catch (ClassCastException e) {
0331: LOG
0332: .info("Non-file resource in podcasts folder, so ignoring. ");
0333:
0334: }
0335:
0336: }
0337:
0338: return filteredResources;
0339: }
0340:
0341: /**
0342: * Returns podcast folder id using either 'podcasts' or 'Podcasts'. If it
0343: * does not exist in either form, will create it.
0344: *
0345: * @param siteId
0346: * The site to search
0347: *
0348: * @return String
0349: * Contains the complete id for the podcast folder
0350: *
0351: * @throws PermissionException
0352: * Access denied or Not found so not available
0353: * @throws IdInvalidException
0354: * Constructed Id not valid
0355: * @throws IdUsedException
0356: * When attempting to create Podcast folder, id is a duplicate
0357: */
0358: public String retrievePodcastFolderId(String siteId)
0359: throws PermissionException {
0360:
0361: final String siteCollection = contentHostingService
0362: .getSiteCollection(siteId);
0363: String podcastsCollection = siteCollection
0364: + COLLECTION_PODCASTS + Entity.SEPARATOR;
0365:
0366: try {
0367: contentHostingService.checkCollection(podcastsCollection);
0368:
0369: return podcastsCollection;
0370:
0371: } catch (PermissionException e) {
0372: // Sometimes it converts an IdUnusedException into a permission
0373: // exception so try this. Have tried 'Podcasts', now try 'podcasts'
0374: // If PermissionException thrown again, pass it along
0375: podcastsCollection = siteCollection
0376: + COLLECTION_PODCASTS_ALT + Entity.SEPARATOR;
0377:
0378: try {
0379: contentHostingService
0380: .checkCollection(podcastsCollection);
0381:
0382: return podcastsCollection;
0383:
0384: } catch (TypeException e1) {
0385: LOG.error(
0386: "TypeException while trying to determine correct podcast folder Id String "
0387: + " for site: " + siteId + ". "
0388: + e1.getMessage(), e1);
0389: throw new Error(e);
0390:
0391: } catch (IdUnusedException e1) {
0392: LOG.warn(
0393: "IdUnusedException while trying to determine correct podcast folder id "
0394: + " for site: " + siteId + ". "
0395: + e1.getMessage(), e1);
0396:
0397: } catch (PermissionException e1) {
0398: // If thrown here, it truly is a PermissionException
0399: LOG
0400: .warn(
0401: "PermissionException while trying to determine correct podcast folder Id String "
0402: + " for site: "
0403: + siteId
0404: + ". "
0405: + e1.getMessage(), e1);
0406: throw e1;
0407:
0408: }
0409: } catch (IdUnusedException e) {
0410: // 'Podcasts' - no luck, try 'podcasts'
0411: podcastsCollection = siteCollection
0412: + COLLECTION_PODCASTS_ALT + Entity.SEPARATOR;
0413:
0414: try {
0415: contentHostingService
0416: .checkCollection(podcastsCollection);
0417:
0418: return podcastsCollection;
0419:
0420: } catch (IdUnusedException e1) {
0421: // Does not exist, so try to create it
0422: podcastsCollection = siteCollection
0423: + COLLECTION_PODCASTS + Entity.SEPARATOR;
0424:
0425: createPodcastsFolder(podcastsCollection, siteId);
0426:
0427: return podcastsCollection;
0428:
0429: } catch (PermissionException e1) {
0430: // so try this. If PermissionException thrown again, there is
0431: // truly a problem.
0432: LOG.warn(
0433: "PermissionException thrown on second attempt at retrieving podcasts folder. "
0434: + " for site: " + siteId + ". "
0435: + e1.getMessage(), e1);
0436: throw e1;
0437:
0438: } catch (TypeException e1) {
0439: LOG.error(
0440: "TypeException while getting podcasts folder using 'Podcasts' string: "
0441: + e1.getMessage(), e1);
0442: throw new Error(e);
0443:
0444: }
0445: } catch (TypeException e) {
0446: LOG.error(
0447: "TypeException while getting podcasts folder using 'podcasts' string: "
0448: + e.getMessage(), e);
0449: throw new Error(e);
0450:
0451: }
0452:
0453: return null;
0454:
0455: }
0456:
0457: /**
0458: * Retrieve Podcasts for site and if podcast folder does not exist, create
0459: * it. Used within tool since siteId known
0460: *
0461: * @return List
0462: * A List of podcast resources
0463: */
0464: public List getPodcasts() throws PermissionException,
0465: InUseException, IdInvalidException, InconsistentException,
0466: IdUsedException {
0467: return getPodcasts(getSiteId());
0468:
0469: }
0470:
0471: /**
0472: * Retrieve Podcasts for site and if podcast folder does not exist, create
0473: * it. Used by feed since no context to pull siteId from
0474: *
0475: * @param String
0476: * The siteId the feed needs the podcasts from
0477: *
0478: * @return List
0479: * A List of podcast resources
0480: */
0481: public List getPodcasts(String siteId) throws PermissionException,
0482: InUseException, IdInvalidException, InconsistentException,
0483: IdUsedException {
0484:
0485: List resourcesList = null;
0486: final String podcastsCollection = retrievePodcastFolderId(siteId);
0487:
0488: try {
0489: checkForFeedInfo(podcastsCollection, siteId);
0490:
0491: // just in case anything added from Resources so it does
0492: // not have DISPLAY_DATE property
0493: final ContentCollection collectionEdit = getContentCollection(siteId);
0494:
0495: resourcesList = collectionEdit.getMemberResources();
0496:
0497: // remove non-file resources from collection
0498: resourcesList = filterResources(resourcesList);
0499:
0500: // if added from Resources will not have this property.
0501: // if not, this will call a method to set it.
0502: resourcesList = checkDISPLAY_DATE(resourcesList);
0503:
0504: // sort based on display (publish) date, most recent first
0505: PodcastComparator podcastComparator = new PodcastComparator(
0506: DISPLAY_DATE, false);
0507: Collections.sort(resourcesList, podcastComparator);
0508:
0509: } catch (IdUnusedException ex) {
0510: // Does not exist, attempt to create it
0511: createPodcastsFolder(podcastsCollection, siteId);
0512: }
0513:
0514: return resourcesList;
0515: }
0516:
0517: /**
0518: * Pulls a ContentResourceEdit from ContentHostingService.
0519: *
0520: * @param String
0521: * The resourceId of the resource to get
0522: *
0523: * @return ContentResourceEdit
0524: * If found, null otherwise
0525: */
0526: public ContentResourceEdit getAResource(String resourceId)
0527: throws PermissionException, IdUnusedException {
0528: ContentResourceEdit crEdit = null;
0529:
0530: try {
0531: crEdit = (ContentResourceEdit) contentHostingService
0532: .getResource(resourceId);
0533:
0534: } catch (TypeException e) {
0535: LOG
0536: .error("TypeException while attempting to pull resource: "
0537: + resourceId
0538: + " for site: "
0539: + getSiteId()
0540: + ". " + e.getMessage());
0541: throw new Error(e);
0542:
0543: }
0544:
0545: return crEdit;
0546: }
0547:
0548: /**
0549: * Add a podcast to the site's resources
0550: *
0551: * @param title
0552: * The title of this podcast resource
0553: * @param displayDate
0554: * The display date for this podcast resource
0555: * @param description
0556: * The description of this podcast resource
0557: * @param body
0558: * The bytes of this podcast
0559: *
0560: * @throws OverQuotaException
0561: * To display Over Quota Alert to user
0562: * @throws ServerOverloadException
0563: * To display Internal Server Error Alert to user
0564: * @throws InconsistentException
0565: * To display Internal Server Error Alert to user
0566: * @throws IdInvalidException
0567: * To display Invalid Id Alert to user
0568: * @throws IdLengthException
0569: * To display File path too long Alert to user
0570: * @throws PermissionException
0571: * To display Permission denied Alert to user
0572: * @throws IdUniquenessException
0573: * To display Duplicate id used Alert to user
0574: */
0575: public void addPodcast(String title, Date displayDate,
0576: String description, byte[] body, String filename,
0577: String contentType) throws OverQuotaException,
0578: ServerOverloadException, InconsistentException,
0579: IdInvalidException, IdLengthException, PermissionException,
0580: IdUniquenessException {
0581:
0582: final int idVariationLimit = 0;
0583:
0584: final ResourcePropertiesEdit resourceProperties = contentHostingService
0585: .newResourceProperties();
0586:
0587: final String resourceCollection = retrievePodcastFolderId(getSiteId());
0588:
0589: resourceProperties.addProperty(
0590: ResourceProperties.PROP_IS_COLLECTION, Boolean.FALSE
0591: .toString());
0592:
0593: resourceProperties.addProperty(
0594: ResourceProperties.PROP_DISPLAY_NAME, title);
0595:
0596: resourceProperties.addProperty(
0597: ResourceProperties.PROP_DESCRIPTION, description);
0598:
0599: final SimpleDateFormat formatter = new SimpleDateFormat(
0600: "yyyyMMddHHmmssSSS");
0601: formatter.setTimeZone(TimeService.getLocalTimeZone());
0602:
0603: resourceProperties.addProperty(DISPLAY_DATE, formatter
0604: .format(displayDate));
0605:
0606: resourceProperties.addProperty(
0607: ResourceProperties.PROP_ORIGINAL_FILENAME, filename);
0608:
0609: resourceProperties.addProperty(
0610: ResourceProperties.PROP_CONTENT_LENGTH, new Integer(
0611: body.length).toString());
0612:
0613: // TODO: change NotificationService based on user input
0614: contentHostingService.addResource(Validator
0615: .escapeResourceName(filename), resourceCollection,
0616: idVariationLimit, contentType, body,
0617: resourceProperties, NotificationService.NOTI_NONE);
0618:
0619: // add entry for event tracking
0620: final Event event = EventTrackingService.newEvent(
0621: EVENT_ADD_PODCAST, getEventMessage(filename), true,
0622: NotificationService.NOTI_NONE);
0623: EventTrackingService.post(event);
0624:
0625: }
0626:
0627: /**
0628: * Removes a podcast
0629: *
0630: * @param id
0631: * The podcast to be removed from ContentHosting
0632: */
0633: public void removePodcast(String resourceId)
0634: throws IdUnusedException, InUseException, TypeException,
0635: PermissionException {
0636:
0637: ContentResourceEdit edit = null;
0638:
0639: edit = contentHostingService.editResource(resourceId);
0640:
0641: contentHostingService.removeResource(edit);
0642:
0643: // add entry for event tracking
0644: final Event event = EventTrackingService.newEvent(
0645: EVENT_DELETE_PODCAST, edit.getReference(), true,
0646: NotificationService.NOTI_NONE);
0647: EventTrackingService.post(event);
0648:
0649: }
0650:
0651: /**
0652: * Tests whether the podcasts folder exists and create it if it does not
0653: *
0654: * @return True - if exists, false - otherwise
0655: */
0656: public boolean checkPodcastFolder() throws PermissionException,
0657: InUseException {
0658: return (retrievePodcastFolderId(getSiteId()) != null);
0659:
0660: }
0661:
0662: /**
0663: * Determines if folder contains actual files
0664: *
0665: * @return boolean true if files are stored there, false otherwise
0666: */
0667: public boolean checkForActualPodcasts() {
0668:
0669: try {
0670: final String podcastsCollection = retrievePodcastFolderId(getSiteId());
0671:
0672: if (podcastsCollection != null) {
0673:
0674: final ContentCollection collection = contentHostingService
0675: .getCollection(podcastsCollection);
0676:
0677: final List resourcesList = collection
0678: .getMemberResources();
0679:
0680: if (resourcesList != null) {
0681: if (resourcesList.isEmpty())
0682: return false;
0683: else
0684: return true;
0685: } else
0686: return false;
0687:
0688: }
0689: } catch (Exception e) {
0690: // catches IdUnusedException, TypeException, PermissionException
0691: LOG.warn(e.getMessage()
0692: + " while checking for files in podcast folder: "
0693: + " for site: " + getSiteId() + ". "
0694: + e.getMessage(), e);
0695:
0696: }
0697:
0698: return false;
0699: }
0700:
0701: /**
0702: * Will apply changes made (if any) to podcast
0703: *
0704: * @param String
0705: * The resourceId
0706: * @param String
0707: * The title
0708: * @param Date
0709: * The display/publish date
0710: * @param String
0711: * The description
0712: * @param byte[]
0713: * The actual file contents
0714: * @param String
0715: * The filename
0716: */
0717: public void revisePodcast(String resourceId, String title,
0718: Date date, String description, byte[] body, String filename)
0719: throws PermissionException, InUseException,
0720: OverQuotaException, ServerOverloadException {
0721:
0722: try {
0723: // get Resource to modify
0724: ContentResourceEdit podcastEditable = null;
0725:
0726: podcastEditable = contentHostingService
0727: .editResource(resourceId);
0728:
0729: final ResourcePropertiesEdit podcastResourceEditable = podcastEditable
0730: .getPropertiesEdit();
0731:
0732: if (!title.equals(podcastResourceEditable
0733: .getProperty(ResourceProperties.PROP_DISPLAY_NAME))) {
0734:
0735: podcastResourceEditable
0736: .removeProperty(ResourceProperties.PROP_DISPLAY_NAME);
0737:
0738: podcastResourceEditable.addProperty(
0739: ResourceProperties.PROP_DISPLAY_NAME, title);
0740:
0741: }
0742:
0743: if (!description.equals(podcastResourceEditable
0744: .getProperty(ResourceProperties.PROP_DESCRIPTION))) {
0745:
0746: podcastResourceEditable
0747: .removeProperty(ResourceProperties.PROP_DESCRIPTION);
0748:
0749: podcastResourceEditable.addProperty(
0750: ResourceProperties.PROP_DESCRIPTION,
0751: description);
0752:
0753: }
0754:
0755: if (date != null) {
0756: podcastResourceEditable.removeProperty(DISPLAY_DATE);
0757:
0758: final SimpleDateFormat formatter = new SimpleDateFormat(
0759: "yyyyMMddHHmmssSSS");
0760: formatter.setTimeZone(TimeService.getLocalTimeZone());
0761:
0762: podcastResourceEditable.addProperty(DISPLAY_DATE,
0763: formatter.format(date));
0764:
0765: }
0766:
0767: if (!filename
0768: .equals(podcastResourceEditable
0769: .getProperty(ResourceProperties.PROP_ORIGINAL_FILENAME))) {
0770:
0771: podcastResourceEditable
0772: .removeProperty(ResourceProperties.PROP_ORIGINAL_FILENAME);
0773:
0774: podcastResourceEditable.addProperty(
0775: ResourceProperties.PROP_ORIGINAL_FILENAME,
0776: Validator.escapeResourceName(filename));
0777:
0778: podcastResourceEditable
0779: .removeProperty(ResourceProperties.PROP_CONTENT_LENGTH);
0780:
0781: podcastResourceEditable.addProperty(
0782: ResourceProperties.PROP_CONTENT_LENGTH,
0783: new Integer(body.length).toString());
0784:
0785: podcastResourceEditable
0786: .removeProperty(ResourceProperties.PROP_DISPLAY_NAME);
0787:
0788: podcastResourceEditable.addProperty(
0789: ResourceProperties.PROP_DISPLAY_NAME, Validator
0790: .escapeResourceName(filename));
0791: }
0792:
0793: // Set for no notification. TODO: when notification implemented,
0794: // need to revisit 2nd parameter.
0795: contentHostingService.commitResource(podcastEditable,
0796: NotificationService.NOTI_NONE);
0797:
0798: // add entry for event tracking
0799: Event event = EventTrackingService.newEvent(
0800: EVENT_REVISE_PODCAST, podcastEditable
0801: .getReference(), true);
0802: EventTrackingService.post(event);
0803:
0804: } catch (IdUnusedException e) {
0805: LOG.error(e.getMessage()
0806: + " while revising podcasts for site: "
0807: + getSiteId() + ". ", e);
0808: throw new Error(e);
0809:
0810: } catch (TypeException e) {
0811: LOG.error(e.getMessage()
0812: + " while revising podcasts for site: "
0813: + getSiteId() + ". ", e);
0814: throw new Error(e);
0815:
0816: }
0817: }
0818:
0819: /**
0820: * Checks if podcast resources have a DISPLAY_DATE set. Occurs when files
0821: * uploaded to podcast folder from Resources.
0822: *
0823: * @param List
0824: * The list of podcast resources
0825: *
0826: * @return List The list of podcast resource all with DISPLAY_DATE property
0827: */
0828: public List checkDISPLAY_DATE(List resourcesList) {
0829: // recreate the list in case needed to
0830: // add DISPLAY_DATE to podcast(s)
0831: List revisedList = new ArrayList();
0832:
0833: final Iterator podcastIter = resourcesList.iterator();
0834: ContentResource aResource = null;
0835: ResourceProperties itsProperties = null;
0836:
0837: // for each bean
0838: // loop to check if DISPLAY_DATE has been set. If not, set it
0839: while (podcastIter.hasNext()) {
0840: // get its properties from ContentHosting
0841: aResource = (ContentResource) podcastIter.next();
0842:
0843: itsProperties = aResource.getProperties();
0844:
0845: try {
0846: itsProperties.getTimeProperty(DISPLAY_DATE);
0847:
0848: revisedList.add(aResource);
0849:
0850: } catch (EntityPropertyNotDefinedException e) {
0851: // DISPLAY_DATE does not exist, add it
0852: LOG.info("DISPLAY_DATE does not exist for "
0853: + aResource.getId() + " attempting to add.");
0854:
0855: ContentResourceEdit aResourceEdit = null;
0856: try {
0857: try {
0858: aResourceEdit = contentHostingService
0859: .editResource(aResource.getId());
0860: } catch (PermissionException e1) {
0861: // Not able to access, add a SecurityAdvisor to force it to work
0862: enablePodcastSecurityAdvisor();
0863:
0864: aResourceEdit = contentHostingService
0865: .editResource(aResource.getId());
0866: }
0867:
0868: setDISPLAY_DATE(aResourceEdit.getPropertiesEdit());
0869:
0870: contentHostingService.commitResource(aResourceEdit,
0871: NotificationService.NOTI_NONE);
0872:
0873: // add entry for event tracking
0874: final Event event = EventTrackingService
0875: .newEvent(
0876: EVENT_REVISE_PODCAST,
0877: getEventMessage(aResourceEdit
0878: .getProperties()
0879: .getProperty(
0880: ResourceProperties.PROP_ORIGINAL_FILENAME)),
0881: true, NotificationService.NOTI_NONE);
0882: EventTrackingService.post(event);
0883:
0884: revisedList.add(aResourceEdit);
0885:
0886: } catch (Exception e1) {
0887: // catches PermissionException IdUnusedException
0888: // TypeException InUseException
0889: LOG
0890: .error("Problem getting resource for editing while trying to set DISPLAY_DATE for site "
0891: + getSiteId()
0892: + ". "
0893: + e.getMessage());
0894:
0895: if (aResourceEdit != null) {
0896: contentHostingService
0897: .cancelResource(aResourceEdit);
0898: }
0899:
0900: throw new Error(e);
0901: }
0902:
0903: } catch (EntityPropertyTypeException e) {
0904: // not a file, skip over it
0905: LOG
0906: .debug("EntityPropertyTypeException while checking for DISPLAY_DATE. "
0907: + " Possible non-resource (aka a folder) exists in podcasts folder. "
0908: + "SKIPPING..." + e.getMessage());
0909:
0910: } finally {
0911: SecurityService.clearAdvisors();
0912: }
0913: }
0914:
0915: return revisedList;
0916: }
0917:
0918: /**
0919: * Sets the DISPLAY_DATE property to the creation date of the podcast.
0920: * Needed if file added using Resources. Time stored is GMT so when pulled
0921: * need to convert to local.
0922: *
0923: * @param ResourceProperties
0924: * The ResourceProperties that need DISPLAY_DATE added
0925: */
0926: public void setDISPLAY_DATE(ResourceProperties rp) {
0927: final SimpleDateFormat formatterProp = new SimpleDateFormat(
0928: "yyyyMMddHHmmssSSS");
0929: formatterProp.setTimeZone(TimeService.getLocalTimeZone());
0930:
0931: Date tempDate = null;
0932:
0933: try {
0934: // Convert GMT time stored by Resources into local time
0935: tempDate = formatterProp.parse(rp.getTimeProperty(
0936: ResourceProperties.PROP_MODIFIED_DATE)
0937: .toStringLocal());
0938:
0939: rp
0940: .addProperty(DISPLAY_DATE, formatterProp
0941: .format(tempDate));
0942:
0943: } catch (Exception e) {
0944: // catches EntityPropertyNotDefinedException
0945: // EntityPropertyTypeException, ParseException
0946: LOG.error(e.getMessage()
0947: + " while setting DISPLAY_DATE for "
0948: + "for file in site " + getSiteId() + ". "
0949: + e.getMessage(), e);
0950: throw new Error(e);
0951:
0952: }
0953:
0954: }
0955:
0956: private void checkForFeedInfo(String podcastsCollection,
0957: String siteId) {
0958: try {
0959: final ContentCollection podcasts = contentHostingService
0960: .getCollection(podcastsCollection);
0961:
0962: final ResourceProperties rp = podcasts.getProperties();
0963:
0964: final String podfeedTitle = rp.getProperty(PODFEED_TITLE);
0965:
0966: if (podfeedTitle == null) {
0967: // Podfeed Title does not exist, so add it
0968: final ContentCollectionEdit podcastsEdit = contentHostingService
0969: .editCollection(podcastsCollection);
0970:
0971: final ResourcePropertiesEdit resourceProperties = podcastsEdit
0972: .getPropertiesEdit();
0973:
0974: resourceProperties.addProperty(
0975: ResourceProperties.PROP_DISPLAY_NAME,
0976: COLLECTION_PODCASTS_TITLE);
0977:
0978: resourceProperties.addProperty(
0979: ResourceProperties.PROP_DESCRIPTION,
0980: COLLECTION_PODCASTS_DESCRIPTION);
0981:
0982: try {
0983: // Set default feed title and description
0984: resourceProperties.addProperty(PODFEED_TITLE,
0985: "Podcasts for "
0986: + SiteService.getSite(siteId)
0987: .getTitle());
0988:
0989: final String feedDescription = "This is the official podcast for course "
0990: + SiteService.getSite(siteId).getTitle()
0991: + ". Please check back throughout the semester for updates.";
0992:
0993: resourceProperties.addProperty(PODFEED_DESCRIPTION,
0994: feedDescription);
0995:
0996: commitContentCollection(podcastsEdit);
0997: } catch (IdUnusedException e) {
0998: LOG
0999: .error("IdUnusedException attempting to get site info to set feed title and description for site "
1000: + siteId);
1001: }
1002: }
1003: } catch (IdUnusedException e) {
1004: LOG
1005: .error("IdUnusedException attempting to retrive podcast folder collection to check "
1006: + "if feed info exists for site " + siteId);
1007: } catch (TypeException e) {
1008: LOG
1009: .error("TypeException attempting to retrive podcast folder collection to check "
1010: + "if feed info exists for site " + siteId);
1011: } catch (PermissionException e) {
1012: LOG
1013: .error("PermissionException attempting to retrive podcast folder collection to check "
1014: + "if feed info exists for site " + siteId);
1015: } catch (InUseException e) {
1016: LOG
1017: .info("InUsedException attempting to retrive podcast folder collection to check "
1018: + "if feed info exists for site " + siteId);
1019: }
1020: }
1021:
1022: /**
1023: * Returns the file's URL
1024: *
1025: * @param String
1026: * The resource Id
1027: *
1028: * @return String The URL for the resource
1029: */
1030: public String getPodcastFileURL(String resourceId)
1031: throws PermissionException, IdUnusedException {
1032:
1033: String Url = null;
1034: try {
1035: Url = contentHostingService.getResource(resourceId)
1036: .getUrl();
1037:
1038: } catch (TypeException e) {
1039: LOG.error("TypeException while getting the resource "
1040: + resourceId + "'s URL. Resource from site "
1041: + getSiteId() + ". " + e.getMessage());
1042: throw new Error(e);
1043: }
1044:
1045: return Url;
1046: }
1047:
1048: /**
1049: * Determine whether user and update the site
1050: *
1051: * @param siteId
1052: * The siteId for the site to test
1053: *
1054: * @return True
1055: * True if can update, False otherwise
1056: */
1057: public boolean canUpdateSite(String siteId) {
1058: return SecurityService.unlock(UPDATE_PERMISSIONS, "/site/"
1059: + siteId);
1060: }
1061:
1062: public boolean hasPerm(String function) {
1063: try {
1064: return SecurityService.unlock(function, "/content"
1065: + retrievePodcastFolderId(getSiteId()));
1066: } catch (PermissionException e) {
1067: // weirdness since displaying main page so user should have permission
1068: LOG
1069: .error("PermissionException while trying to determine if user can update site "
1070: + getSiteId());
1071: }
1072:
1073: return false;
1074: }
1075:
1076: /**
1077: * Determine whether user and update the site
1078: *
1079: * @param siteId
1080: * The siteId for the site to test
1081: *
1082: * @return
1083: * True if can update, False otherwise
1084: */
1085: public boolean canUpdateSite() {
1086: return canUpdateSite(getSiteId());
1087:
1088: }
1089:
1090: /**
1091: * FUTURE: needed to implement Notification services
1092: *
1093: */
1094: public void init() {
1095: /* EntityManager.registerEntityProducer(this, REFERENCE_ROOT);
1096:
1097: m_relativeAccessPoint = REFERENCE_ROOT;
1098:
1099: NotificationEdit edit = notificationService.addTransientNotification();
1100:
1101: edit.setFunction(EVENT_PODCAST_ADD);
1102: edit.addFunction(EVENT_PODCAST_REVISE);
1103:
1104: edit.setResourceFilter(getAccessPoint(true));
1105:
1106: edit.setAction(new SiteEmailNotificationPodcasts());
1107: */
1108: }
1109:
1110: public void destroy() {
1111: }
1112:
1113: /**
1114: * Changes the podcast folder view status (either PUBLIC or SITE)
1115: *
1116: * @param boolean
1117: * True means PUBLIC view, FALSE means private
1118: */
1119: public void reviseOptions(boolean option) {
1120:
1121: String podcastsCollection = null;
1122:
1123: try {
1124: podcastsCollection = retrievePodcastFolderId(getSiteId());
1125:
1126: contentHostingService
1127: .setPubView(podcastsCollection, option);
1128:
1129: } catch (PermissionException e) {
1130: LOG
1131: .warn("PermissionException attempting to retrieve podcast folder id "
1132: + " for site "
1133: + getSiteId()
1134: + ". "
1135: + e.getMessage());
1136: throw new Error(e);
1137:
1138: }
1139: }
1140:
1141: /**
1142: * Returns (from content hosting) whether the podcast folder is PUBLIC or
1143: * SITE
1144: *
1145: * @return int
1146: * 0 = Display to non-members, 1 = Display to Site
1147: */
1148: public int getOptions() {
1149:
1150: String podcastsCollection = null;
1151: try {
1152: podcastsCollection = retrievePodcastFolderId(getSiteId());
1153:
1154: if (isPublic(podcastsCollection)) {
1155: return PUBLIC;
1156:
1157: } else {
1158: return SITE;
1159:
1160: }
1161: } catch (PermissionException e) {
1162: LOG
1163: .warn("PermissionException attempting to retrieve podcast folder id "
1164: + " for site "
1165: + getSiteId()
1166: + ". "
1167: + e.getMessage());
1168: throw new Error(e);
1169:
1170: }
1171:
1172: }
1173:
1174: /**
1175: * Commits changes to ContentHosting (releases the lock)
1176: *
1177: * @param ContentCollectionEdit
1178: * The ContentCollection to be saved
1179: */
1180: public void commitContentCollection(ContentCollectionEdit cce) {
1181: contentHostingService.commitCollection(cce);
1182:
1183: }
1184:
1185: /**
1186: * Cancels attempt at changing this collection (releases the lock)
1187: *
1188: * @param cce
1189: * The ContentCollectionEdit that is not to be changed
1190: */
1191: public void cancelContentCollection(ContentCollectionEdit cce) {
1192: contentHostingService.cancelCollection(cce);
1193: }
1194:
1195: /**
1196: * Returns boolean TRUE = Display to non-members FALSE - Display to Site
1197: *
1198: * @param podcastFolderId
1199: * The podcast folder id to check
1200: *
1201: * @return boolean
1202: * TRUE - Display to non-members, FALSE - Display to Site
1203: */
1204: public boolean isPublic(String podcastFolderId) {
1205: return contentHostingService.isPubView(podcastFolderId);
1206: }
1207:
1208: /**
1209: * Creates the podcasts folder in Resources
1210: *
1211: * @param podcastsCollection
1212: * The id to be used for the podcasts folder
1213: *
1214: * @param siteId
1215: * The site id for whom the folder is to be created
1216: */
1217: private void createPodcastsFolder(String podcastsCollection,
1218: String siteId) {
1219: try {
1220: LOG
1221: .info("Could not find podcast folder, attempting to create.");
1222:
1223: // Refactored 11-20-06 since add method is being deprecated
1224: ContentCollectionEdit collection = contentHostingService
1225: .addCollection(podcastsCollection);
1226:
1227: final ResourcePropertiesEdit resourceProperties = collection
1228: .getPropertiesEdit();
1229:
1230: resourceProperties.addProperty(
1231: ResourceProperties.PROP_DISPLAY_NAME,
1232: COLLECTION_PODCASTS_TITLE);
1233:
1234: resourceProperties.addProperty(
1235: ResourceProperties.PROP_DESCRIPTION,
1236: COLLECTION_PODCASTS_DESCRIPTION);
1237:
1238: // Set default feed title and description
1239: resourceProperties.addProperty(PODFEED_TITLE, SiteService
1240: .getSite(siteId).getTitle()
1241: + getMessageBundleString(FEED_TITLE_STRING));
1242:
1243: final String feedDescription = SiteService.getSite(siteId)
1244: .getTitle()
1245: + getMessageBundleString(FEED_DESC1_STRING)
1246: + getMessageBundleString(FEED_DESC2_STRING);
1247:
1248: resourceProperties.addProperty(PODFEED_DESCRIPTION,
1249: feedDescription);
1250:
1251: contentHostingService.commitCollection(collection);
1252:
1253: contentHostingService.setPubView(collection.getId(), true);
1254:
1255: /* final ResourcePropertiesEdit resourceProperties = contentHostingService
1256: .newResourceProperties();
1257:
1258: resourceProperties.addProperty(
1259: ResourceProperties.PROP_DISPLAY_NAME,
1260: COLLECTION_PODCASTS_TITLE);
1261:
1262: resourceProperties.addProperty(
1263: ResourceProperties.PROP_DESCRIPTION,
1264: COLLECTION_PODCASTS_DESCRIPTION);
1265:
1266: // Set default feed title and description
1267: resourceProperties.addProperty(PODFEED_TITLE,
1268: "Podcasts for " + SiteService.getSite(siteId).getTitle());
1269:
1270: final String feedDescription = "This is the official podcast for course "
1271: + SiteService.getSite(siteId).getTitle()
1272: + ". Please check back throughout the semester for updates.";
1273:
1274: resourceProperties.addProperty(PODFEED_DESCRIPTION,
1275: feedDescription);
1276:
1277: ContentCollection collection = contentHostingService
1278: .addCollection(podcastsCollection,
1279: resourceProperties);
1280:
1281: contentHostingService.setPubView(collection.getId(), true);
1282:
1283: ContentCollectionEdit edit = contentHostingService
1284: .editCollection(collection.getId());
1285:
1286: commitContentCollection(edit);
1287: */
1288: } catch (Exception e) {
1289: // catches IdUnusedException, TypeException
1290: // InconsistentException, IdUsedException
1291: // IdInvalidException PermissionException
1292: // InUseException
1293: LOG.error(e.getMessage()
1294: + " while attempting to create Podcasts folder: "
1295: + " for site: " + siteId + ". NOT CREATED... "
1296: + e.getMessage(), e);
1297: throw new Error(e);
1298: }
1299: }
1300:
1301: /**
1302: * Returns the date set in GMT time
1303: *
1304: * @param date
1305: * The date represented as a long value
1306: *
1307: * @return Date
1308: * The Date object set in GMT time
1309: */
1310: public Date getGMTdate(long date) {
1311: final Calendar cal = Calendar.getInstance(TimeService
1312: .getLocalTimeZone());
1313: cal.setTimeInMillis(date);
1314:
1315: int gmtoffset = cal.get(Calendar.ZONE_OFFSET)
1316: + cal.get(Calendar.DST_OFFSET);
1317:
1318: return new Date(date - gmtoffset);
1319: }
1320:
1321: /**
1322: * Generates a message for EventTracking
1323: *
1324: * @param object
1325: * The object that is part of the event
1326: *
1327: * @return
1328: * The String to be used to post the event
1329: */
1330: private String getEventMessage(Object object) {
1331: return "/content/group/podcast/" + getCurrentUser()
1332: + Entity.SEPARATOR + object.toString();
1333: }
1334:
1335: private String getCurrentUser() {
1336: return sessionManager.getCurrentSessionUserId();
1337: }
1338:
1339: /**
1340: * Determines if authenticated user has 'read' access to podcast collection folder
1341: *
1342: * @param id
1343: * The id for the podcast collection folder
1344: *
1345: * @return
1346: * TRUE - has read access, FALSE - does not
1347: */
1348: public boolean allowAccess(String id) {
1349: return contentHostingService.allowGetCollection(id);
1350: }
1351:
1352: /**
1353: * Sets the Faces error message by pulling the message from the
1354: * MessageBundle using the name passed in
1355: *
1356: * @param key
1357: * The name in the MessageBundle for the message wanted
1358: *
1359: * @return String
1360: * The string that is the value of the message
1361: */
1362: private String getMessageBundleString(String key) {
1363: return resbud.getString(key);
1364: }
1365:
1366: /**
1367: * Establish a security advisor to allow the "embedded" azg work to occur
1368: * with no need for additional security permissions.
1369: */
1370: protected void enablePodcastSecurityAdvisor() {
1371: // put in a security advisor so we can do our podcast work without need
1372: // of further permissions
1373: SecurityService.pushAdvisor(new SecurityAdvisor() {
1374: public SecurityAdvice isAllowed(String userId,
1375: String function, String reference) {
1376: return SecurityAdvice.ALLOWED;
1377: }
1378: });
1379: }
1380:
1381: }
|