/// <summary> /// Processes the binary file request. /// </summary> /// <param name="context">The context.</param> private void ProcessBinaryFileRequest(HttpContext context) { int fileId = context.Request.QueryString["id"].AsInteger(); Guid fileGuid = context.Request.QueryString["guid"].AsGuid(); if (fileId == 0 && fileGuid.Equals(Guid.Empty)) { SendNotFound(context); } var rockContext = new RockContext(); var binaryFileQuery = new BinaryFileService(rockContext).Queryable(); if (fileGuid != Guid.Empty) { binaryFileQuery = binaryFileQuery.Where(a => a.Guid == fileGuid); } else { binaryFileQuery = binaryFileQuery.Where(a => a.Id == fileId); } //// get just the binaryFileMetaData (not the file content) just in case we can get the filecontent faster from the cache //// a null ModifiedDateTime shouldn't happen, but just in case, set it to DateTime.MaxValue so we error on the side of not getting it from the cache var binaryFileMetaData = binaryFileQuery.Select(a => new { BinaryFileType_AllowCaching = a.BinaryFileType.AllowCaching, BinaryFileType_RequiresViewSecurity = a.BinaryFileType.RequiresViewSecurity, ModifiedDateTime = a.ModifiedDateTime ?? DateTime.MaxValue, a.MimeType, a.FileName }).FirstOrDefault(); if (binaryFileMetaData == null) { SendNotFound(context); return; } //// if the binaryFile's BinaryFileType requires view security, check security //// note: we put a RequiresViewSecurity flag on BinaryFileType because checking security for every image would be slow (~40ms+ per image request) if (binaryFileMetaData.BinaryFileType_RequiresViewSecurity) { var currentUser = new UserLoginService(rockContext).GetByUserName(UserLogin.GetCurrentUserName()); Person currentPerson = currentUser != null ? currentUser.Person : null; BinaryFile binaryFileAuth = new BinaryFileService(rockContext).Queryable("BinaryFileType").First(a => a.Guid == fileGuid || a.Id == fileId); if (!binaryFileAuth.IsAuthorized(Authorization.VIEW, currentPerson)) { SendNotAuthorized(context); return; } } Stream fileContent = null; try { // Is it cached string cacheName = UrlQueryToCachedFileName(context.Request.QueryString, binaryFileMetaData.MimeType); string physCachedFilePath = context.Request.MapPath(string.Format("~/App_Data/Cache/{0}", cacheName)); if (binaryFileMetaData.BinaryFileType_AllowCaching && File.Exists(physCachedFilePath)) { //// Compare the File's Creation DateTime (which comes from the OS's clock), adjust it for the Rock OrgTimeZone, then compare to BinaryFile's ModifiedDateTime (which is already in OrgTimeZone). //// If the BinaryFile record in the database is less recent than the last time this was cached, it is safe to use the Cached version. //// NOTE: A BinaryFile record is typically just added and never modified (a modify is just creating a new BinaryFile record and deleting the old one), so the cached version will probably always be the correct choice. DateTime cachedFileDateTime = RockDateTime.ConvertLocalDateTimeToRockDateTime(File.GetCreationTime(physCachedFilePath)); if (binaryFileMetaData.ModifiedDateTime < cachedFileDateTime) { // NOTE: the cached file has already been resized (the size is part of the cached file's filename), so we don't need to resize it again fileContent = FetchFromCache(physCachedFilePath); } } if (fileContent == null) { // If we didn't get it from the cache, get it from the binaryFileService BinaryFile binaryFile = GetFromBinaryFileService(context, fileId, fileGuid); if (binaryFile != null) { fileContent = binaryFile.ContentStream; } // If we got the image from the binaryFileService, it might need to be resized and cached if (fileContent != null) { // If more than 1 query string param is passed in, or the mime type is TIFF, assume resize is needed // Note: we force "image/tiff" to get resized so that it gets converted into a jpg (browsers don't like tiffs) if (context.Request.QueryString.Count > 1 || binaryFile.MimeType == "image/tiff") { // if it isn't an SVG file, do a Resize if (binaryFile.MimeType != "image/svg+xml") { fileContent = GetResized(context.Request.QueryString, fileContent); } } if (binaryFileMetaData.BinaryFileType_AllowCaching) { Cache(fileContent, physCachedFilePath); } } } if (fileContent == null) { // if we couldn't get the file from the binaryFileServie or the cache, respond with NotFound SendNotFound(context); return; } // respond with File if (binaryFileMetaData.BinaryFileType_AllowCaching) { // if binaryFileType is set to allowcaching, also tell the browser to cache it for 365 days context.Response.Cache.SetLastModified(binaryFileMetaData.ModifiedDateTime); context.Response.Cache.SetMaxAge(new TimeSpan(365, 0, 0, 0)); } context.Response.ContentType = binaryFileMetaData.MimeType != "image/tiff" ? binaryFileMetaData.MimeType : "image/jpg"; using (var responseStream = fileContent) { context.Response.AddHeader("content-disposition", "inline;filename=" + binaryFileMetaData.FileName); if (responseStream.CanSeek) { responseStream.Seek(0, SeekOrigin.Begin); } responseStream.CopyTo(context.Response.OutputStream); context.Response.Flush(); } } finally { if (fileContent != null) { fileContent.Dispose(); } } }
public IHttpActionResult PostInteractions([FromBody] List <MobileInteractionSession> sessions, Guid?personalDeviceGuid = null) { var person = GetPerson(); var ipAddress = System.Web.HttpContext.Current?.Request?.UserHostAddress; using (var rockContext = new Data.RockContext()) { var interactionChannelService = new InteractionChannelService(rockContext); var interactionComponentService = new InteractionComponentService(rockContext); var interactionSessionService = new InteractionSessionService(rockContext); var interactionService = new InteractionService(rockContext); var userLoginService = new UserLoginService(rockContext); var channelMediumTypeValue = DefinedValueCache.Get(SystemGuid.DefinedValue.INTERACTIONCHANNELTYPE_WEBSITE); var pageEntityTypeId = EntityTypeCache.Get(typeof(Model.Page)).Id; // // Check to see if we have a site and the API key is valid. // if (MobileHelper.GetCurrentApplicationSite() == null) { return(StatusCode(System.Net.HttpStatusCode.Forbidden)); } // // Get the personal device identifier if they provided it's unique identifier. // int?personalDeviceId = null; if (personalDeviceGuid.HasValue) { personalDeviceId = new PersonalDeviceService(rockContext).GetId(personalDeviceGuid.Value); } // // Create a quick way to cache data since we have to loop twice. // var interactionComponentLookup = new Dictionary <string, int>(); // // Helper method to get a cache key for looking up the component Id. // string GetComponentCacheKey(MobileInteraction mi) { return($"{mi.AppId}:{mi.PageGuid}:{mi.ChannelGuid}:{mi.ChannelId}:{mi.ComponentId}:{mi.ComponentName}"); } // // Interactions Components will now try to load from cache which // causes problems if we are inside a transaction. So first loop through // everything and make sure all our components and channels exist. // var prePassInteractions = sessions.SelectMany(a => a.Interactions) .DistinctBy(a => GetComponentCacheKey(a)); // // It's safe to do this pre-pass outside the transaction since we are just creating // the channels and components (if necessary), which is going to have to be done at // at some point no matter what. // foreach (var mobileInteraction in prePassInteractions) { // // Lookup the interaction channel, and create it if it doesn't exist // if (mobileInteraction.AppId.HasValue && mobileInteraction.PageGuid.HasValue) { var site = SiteCache.Get(mobileInteraction.AppId.Value); var page = PageCache.Get(mobileInteraction.PageGuid.Value); if (site == null || page == null) { continue; } // // Try to find an existing interaction channel. // var interactionChannelId = InteractionChannelCache.GetChannelIdByTypeIdAndEntityId(channelMediumTypeValue.Id, site.Id, site.Name, pageEntityTypeId, null); // // Get an existing or create a new component. // var interactionComponentId = InteractionComponentCache.GetComponentIdByChannelIdAndEntityId(interactionChannelId, page.Id, page.InternalName); interactionComponentLookup.AddOrReplace(GetComponentCacheKey(mobileInteraction), interactionComponentId); } else if (mobileInteraction.ChannelId.HasValue || mobileInteraction.ChannelGuid.HasValue) { int?interactionChannelId = null; if (mobileInteraction.ChannelId.HasValue) { interactionChannelId = mobileInteraction.ChannelId.Value; } else if (mobileInteraction.ChannelGuid.HasValue) { interactionChannelId = InteractionChannelCache.Get(mobileInteraction.ChannelGuid.Value)?.Id; } if (interactionChannelId.HasValue) { if (mobileInteraction.ComponentId.HasValue) { // Use the provided component identifier. interactionComponentLookup.AddOrReplace(GetComponentCacheKey(mobileInteraction), mobileInteraction.ComponentId.Value); } else if (mobileInteraction.ComponentName.IsNotNullOrWhiteSpace()) { int interactionComponentId; // Get or create a new component with the details we have. if (mobileInteraction.ComponentEntityId.HasValue) { interactionComponentId = InteractionComponentCache.GetComponentIdByChannelIdAndEntityId(interactionChannelId.Value, mobileInteraction.ComponentEntityId, mobileInteraction.ComponentName); } else { var interactionComponent = interactionComponentService.GetComponentByComponentName(interactionChannelId.Value, mobileInteraction.ComponentName); rockContext.SaveChanges(); interactionComponentId = interactionComponent.Id; } interactionComponentLookup.AddOrReplace(GetComponentCacheKey(mobileInteraction), interactionComponentId); } } } } // // Now wrap the actual interaction creation inside a transaction. We should // probably move this so it uses the InteractionTransaction class for better // performance. This is so we can inform the client that either everything // saved or that nothing saved. No partial saves here. // rockContext.WrapTransaction(() => { foreach (var mobileSession in sessions) { var interactionGuids = mobileSession.Interactions.Select(i => i.Guid).ToList(); var existingInteractionGuids = interactionService.Queryable() .Where(i => interactionGuids.Contains(i.Guid)) .Select(i => i.Guid) .ToList(); // // Loop through all interactions that don't already exist and add each one. // foreach (var mobileInteraction in mobileSession.Interactions.Where(i => !existingInteractionGuids.Contains(i.Guid))) { string cacheKey = GetComponentCacheKey(mobileInteraction); if (!interactionComponentLookup.ContainsKey(cacheKey)) { // Shouldn't happen, but just in case. continue; } var interactionComponentId = interactionComponentLookup[cacheKey]; // // Add the interaction // var interaction = interactionService.CreateInteraction(interactionComponentId, mobileInteraction.EntityId, mobileInteraction.Operation, mobileInteraction.Summary, mobileInteraction.Data, person?.PrimaryAliasId, RockDateTime.ConvertLocalDateTimeToRockDateTime(mobileInteraction.DateTime.LocalDateTime), mobileSession.Application, mobileSession.OperatingSystem, mobileSession.ClientType, null, ipAddress, mobileSession.Guid); interaction.Guid = mobileInteraction.Guid; interaction.PersonalDeviceId = personalDeviceId; interaction.RelatedEntityTypeId = mobileInteraction.RelatedEntityTypeId; interaction.RelatedEntityId = mobileInteraction.RelatedEntityId; interaction.ChannelCustom1 = mobileInteraction.ChannelCustom1; interaction.ChannelCustom2 = mobileInteraction.ChannelCustom2; interaction.ChannelCustomIndexed1 = mobileInteraction.ChannelCustomIndexed1; interactionService.Add(interaction); // Attempt to process this as a communication interaction. ProcessCommunicationInteraction(mobileSession, mobileInteraction, rockContext); } } rockContext.SaveChanges(); }); } return(Ok()); }
/// <summary> /// Processes for recipient. /// </summary> /// <param name="eventType">Type of the event.</param> /// <param name="communicationRecipientGuid">The communication recipient unique identifier.</param> /// <param name="rockContext">The rock context.</param> private void ProcessForReceipent(string eventType, Guid?communicationRecipientGuid, Rock.Data.RockContext rockContext) { if (!communicationRecipientGuid.HasValue) { return; } var communicationRecipient = new CommunicationRecipientService(rockContext).Get(communicationRecipientGuid.Value); if (communicationRecipient != null && communicationRecipient.Communication != null) { int secs = request.Form["timestamp"].AsInteger(); DateTime ts = RockDateTime.ConvertLocalDateTimeToRockDateTime(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(secs).ToLocalTime()); var communicationGuid = Rock.SystemGuid.InteractionChannel.COMMUNICATION.AsGuid(); InteractionComponent interactionComponent = new InteractionComponentService(rockContext) .GetComponentByEntityId(communicationGuid, communicationRecipient.CommunicationId, communicationRecipient.Communication.Subject); rockContext.SaveChanges(); var interactionService = new InteractionService(rockContext); switch (eventType) { case "delivered": communicationRecipient.Status = CommunicationRecipientStatus.Delivered; communicationRecipient.StatusNote = string.Format("Confirmed delivered by Mailgun at {0}", ts.ToString()); break; case "opened": communicationRecipient.Status = CommunicationRecipientStatus.Opened; communicationRecipient.OpenedDateTime = ts; communicationRecipient.OpenedClient = string.Format( "{0} {1} ({2})", request.Form["client-os"] ?? "unknown", request.Form["client-name"] ?? "unknown", request.Form["device-type"] ?? "unknown"); if (interactionComponent != null) { interactionService.AddInteraction( interactionComponent.Id, communicationRecipient.Id, "Opened", string.Empty, communicationRecipient.PersonAliasId, ts, request.Form["client-name"], request.Form["client-os"], request.Form["client-type"], request.Form["device-type"], request.Form["ip"], null); } break; case "clicked": if (interactionComponent != null) { interactionService.AddInteraction( interactionComponent.Id, communicationRecipient.Id, "Click", request.Form["url"], communicationRecipient.PersonAliasId, ts, request.Form["client-name"], request.Form["client-os"], request.Form["client-type"], request.Form["device-type"], request.Form["ip"], null); } break; case "complained": break; case "unsubscribed": break; case "dropped": communicationRecipient.Status = CommunicationRecipientStatus.Failed; communicationRecipient.StatusNote = request.Form["description"]; break; case "bounced": communicationRecipient.Status = CommunicationRecipientStatus.Failed; communicationRecipient.StatusNote = request.Form["notification"]; Rock.Communication.Email.ProcessBounce( request.Form["recipient"], Rock.Communication.BounceType.HardBounce, request.Form["notification"], ts); break; } rockContext.SaveChanges(); } }
/// <summary> /// Processes for recipient. /// </summary> /// <param name="eventType">Type of the event.</param> /// <param name="communicationRecipientGuid">The communication recipient unique identifier.</param> /// <param name="rockContext">The rock context.</param> private void ProcessForRecipient(Guid?communicationRecipientGuid, Rock.Data.RockContext rockContext, SendGridEvent payload) { RockLogger.Log.Debug(RockLogDomains.Communications, "ProcessForRecipient {@payload}", payload); if (!communicationRecipientGuid.HasValue) { return; } var communicationRecipient = new CommunicationRecipientService(rockContext).Get(communicationRecipientGuid.Value); if (communicationRecipient != null && communicationRecipient.Communication != null) { var communicationGuid = Rock.SystemGuid.InteractionChannel.COMMUNICATION.AsGuid(); var interactionComponent = new InteractionComponentService(rockContext) .GetComponentByEntityId(communicationGuid, communicationRecipient.CommunicationId, communicationRecipient.Communication.Subject); rockContext.SaveChanges(); var interactionService = new InteractionService(rockContext); DateTime timeStamp = RockDateTime.ConvertLocalDateTimeToRockDateTime(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(payload.Timestamp).ToLocalTime()); switch (payload.EventType) { case "processed": // Do nothing. break; case "dropped": communicationRecipient.Status = CommunicationRecipientStatus.Failed; communicationRecipient.StatusNote = payload.EventTypeReason; if (payload.EventTypeReason == "Bounced Address") { Rock.Communication.Email.ProcessBounce( payload.Email, Rock.Communication.BounceType.HardBounce, payload.EventTypeReason, timeStamp); } break; case "delivered": communicationRecipient.Status = CommunicationRecipientStatus.Delivered; communicationRecipient.StatusNote = string.Format("Confirmed delivered by SendGrid at {0}", timeStamp.ToString()); break; case "deferred": // TODO: handle deferred. break; case "bounce": communicationRecipient.Status = CommunicationRecipientStatus.Failed; communicationRecipient.StatusNote = payload.EventTypeReason + payload.ServerResponse; Rock.Communication.Email.ProcessBounce( payload.Email, Rock.Communication.BounceType.HardBounce, payload.EventTypeReason, timeStamp); break; case "blocked": // TODO: handle blocked. break; case "open": communicationRecipient.Status = CommunicationRecipientStatus.Opened; communicationRecipient.OpenedDateTime = timeStamp; communicationRecipient.OpenedClient = string.Format( "{0} {1} ({2})", payload.ClientOs ?? "unknown", payload.ClientBrowser ?? "unknown", payload.ClientDeviceType ?? "unknown"); if (interactionComponent != null) { interactionService.AddInteraction( interactionComponent.Id, communicationRecipient.Id, "Opened", payload.SendGridEventId, communicationRecipient.PersonAliasId, timeStamp, payload.ClientBrowser, payload.ClientOs, payload.ClientDeviceType, payload.ClientDeviceBrand, payload.IpAddress, null); } break; case "click": if (interactionComponent != null) { interactionService.AddInteraction( interactionComponent.Id, communicationRecipient.Id, "Click", payload.Url, communicationRecipient.PersonAliasId, timeStamp, payload.ClientBrowser, payload.ClientOs, payload.ClientDeviceType, payload.ClientDeviceBrand, payload.IpAddress, null); } break; case "spamreport": case "unsubscribe": case "group_unsubscribe": case "group_resubscribe": // Do nothing. break; } rockContext.SaveChanges(); } }
/// <summary> /// Processes for workflow. /// </summary> /// <param name="eventType">Type of the event.</param> /// <param name="actionGuid">The action unique identifier.</param> /// <param name="rockContext">The rock context.</param> private void ProcessForWorkflow(Guid?actionGuid, Rock.Data.RockContext rockContext) { string status = string.Empty; switch (mailgunRequestPayload.EventType) { case "complained": case "unsubscribed": case "delivered": status = SendEmailWithEvents.SENT_STATUS; break; case "clicked": status = SendEmailWithEvents.CLICKED_STATUS; break; case "opened": status = SendEmailWithEvents.OPENED_STATUS; break; case "dropped": status = SendEmailWithEvents.FAILED_STATUS; break; case "bounced": status = SendEmailWithEvents.FAILED_STATUS; string message = mailgunRequestPayload.Notification.IsNotNullOrWhiteSpace() ? mailgunRequestPayload.Notification : mailgunRequestPayload.Description; Rock.Communication.Email.ProcessBounce( mailgunRequestPayload.Recipient, Rock.Communication.BounceType.HardBounce, message, RockDateTime.ConvertLocalDateTimeToRockDateTime(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(mailgunRequestPayload.TimeStamp).ToLocalTime())); break; case "failed": // The new mailgun API bundles undeliverable mail into a failed event. The reason (e.g. bounced) is in a seperate property called reason. if (mailgunRequestPayload.EventTypeReason.IsNotNullOrWhiteSpace()) { switch (mailgunRequestPayload.EventTypeReason) { case "bounce": case "suppress-bounce": status = SendEmailWithEvents.FAILED_STATUS; Rock.Communication.Email.ProcessBounce( mailgunRequestPayload.Recipient, Rock.Communication.BounceType.HardBounce, mailgunRequestPayload.Description, RockDateTime.ConvertLocalDateTimeToRockDateTime(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(mailgunRequestPayload.TimeStamp).ToLocalTime())); break; default: status = SendEmailWithEvents.FAILED_STATUS; break; } } break; } if (actionGuid != null && !string.IsNullOrWhiteSpace(status)) { SendEmailWithEvents.UpdateEmailStatus(actionGuid.Value, status, mailgunRequestPayload.EventType, rockContext, true); } }
/// <summary> /// Processes for recipient. /// </summary> /// <param name="eventType">Type of the event.</param> /// <param name="communicationRecipientGuid">The communication recipient unique identifier.</param> /// <param name="rockContext">The rock context.</param> private void ProcessForReceipent(Guid?communicationRecipientGuid, Rock.Data.RockContext rockContext) { if (!communicationRecipientGuid.HasValue) { return; } var communicationRecipient = new CommunicationRecipientService(rockContext).Get(communicationRecipientGuid.Value); if (communicationRecipient != null && communicationRecipient.Communication != null) { var communicationGuid = Rock.SystemGuid.InteractionChannel.COMMUNICATION.AsGuid(); var interactionComponent = new InteractionComponentService(rockContext) .GetComponentByEntityId(communicationGuid, communicationRecipient.CommunicationId, communicationRecipient.Communication.Subject); rockContext.SaveChanges(); var interactionService = new InteractionService(rockContext); DateTime timeStamp = RockDateTime.ConvertLocalDateTimeToRockDateTime(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(mailgunRequestPayload.TimeStamp).ToLocalTime()); switch (mailgunRequestPayload.EventType) { case "delivered": communicationRecipient.Status = CommunicationRecipientStatus.Delivered; communicationRecipient.StatusNote = string.Format("Confirmed delivered by Mailgun at {0}", timeStamp.ToString()); break; case "opened": communicationRecipient.Status = CommunicationRecipientStatus.Opened; communicationRecipient.OpenedDateTime = timeStamp; communicationRecipient.OpenedClient = string.Format( "{0} {1} ({2})", mailgunRequestPayload.ClientOs ?? "unknown", mailgunRequestPayload.ClientName ?? "unknown", mailgunRequestPayload.DeviceType ?? "unknown"); if (interactionComponent != null) { interactionService.AddInteraction( interactionComponent.Id, communicationRecipient.Id, "Opened", string.Empty, communicationRecipient.PersonAliasId, timeStamp, mailgunRequestPayload.ClientName, mailgunRequestPayload.ClientOs, mailgunRequestPayload.ClientType, mailgunRequestPayload.DeviceType, mailgunRequestPayload.Ip, null); } break; case "clicked": if (interactionComponent != null) { interactionService.AddInteraction( interactionComponent.Id, communicationRecipient.Id, "Click", mailgunRequestPayload.Url, communicationRecipient.PersonAliasId, timeStamp, mailgunRequestPayload.ClientName, mailgunRequestPayload.ClientOs, mailgunRequestPayload.ClientType, mailgunRequestPayload.DeviceType, mailgunRequestPayload.Ip, null); } break; case "complained": break; case "unsubscribed": break; case "dropped": communicationRecipient.Status = CommunicationRecipientStatus.Failed; communicationRecipient.StatusNote = mailgunRequestPayload.Description; break; case "bounced": communicationRecipient.Status = CommunicationRecipientStatus.Failed; communicationRecipient.StatusNote = mailgunRequestPayload.Notification; Rock.Communication.Email.ProcessBounce( mailgunRequestPayload.Recipient, Rock.Communication.BounceType.HardBounce, mailgunRequestPayload.Notification, timeStamp); break; case "failed": // The new mailgun API bundles undeliverable mail into a failed event. The reason (e.g. bounced) is in a seperate property called reason. if (mailgunRequestPayload.EventTypeReason.IsNotNullOrWhiteSpace()) { switch (mailgunRequestPayload.EventTypeReason) { case "bounce": case "suppress-bounce": communicationRecipient.Status = CommunicationRecipientStatus.Failed; communicationRecipient.StatusNote = mailgunRequestPayload.Description; Rock.Communication.Email.ProcessBounce( mailgunRequestPayload.Recipient, Rock.Communication.BounceType.HardBounce, mailgunRequestPayload.Description, timeStamp); break; default: communicationRecipient.Status = CommunicationRecipientStatus.Failed; communicationRecipient.StatusNote = mailgunRequestPayload.Description; break; } } break; } rockContext.SaveChanges(); } }
public void Execute(IJobExecutionContext context) { var rockContext = new RockContext(); var dataMap = context.JobDetail.JobDataMap; DateTime?lastRun = null; if (context.PreviousFireTimeUtc.HasValue) { lastRun = RockDateTime.ConvertLocalDateTimeToRockDateTime(context.PreviousFireTimeUtc.Value.LocalDateTime); } var systemEmailGuid = dataMap.GetString("Email").AsGuidOrNull(); string appRoot = Rock.Web.Cache.GlobalAttributesCache.Read().GetValue("PublicApplicationRoot"); if (appRoot.IsNullOrWhiteSpace()) { throw new Exception("Couldn't fetch application root!"); } if (systemEmailGuid == null) { throw new Exception("A system email template needs to be set."); } var systemEmailTemplate = new SystemEmailService(rockContext).Get(systemEmailGuid.Value); if (systemEmailTemplate == null) { throw new Exception("The system email template setting is not a valid system email template."); } var cutOffHours = dataMap.GetString("IncludeHours").AsIntegerOrNull(); if (!cutOffHours.HasValue) { throw new Exception("A cutoff period needs to be set."); } if (lastRun == null) { lastRun = RockDateTime.Now.AddHours(-1 * cutOffHours.Value); } var connectionRequestService = new ConnectionRequestService(rockContext); var openConnectionRequests = connectionRequestService.Queryable() .AsNoTracking() .Where(cr => cr.CreatedDateTime >= lastRun && cr.ConnectionState != ConnectionState.Connected && cr.ConnectorPersonAliasId != null); if (!openConnectionRequests.Any()) { context.Result = "There are no open and assigned connection requests to send alerts for"; return; } var groupedRequests = openConnectionRequests .ToList() .GroupBy(cr => cr.ConnectorPersonAlias); int mailedCount = 0; foreach (var connectionRequestGrouping in groupedRequests) { if (connectionRequestGrouping.Key == null) { continue; } var connectionRequests = connectionRequestGrouping.ToList(); var mergeFields = new Dictionary <string, object> { { "ConnectionRequests", connectionRequests }, { "Person", connectionRequestGrouping.Key.Person } }; var recipients = new List <string> { connectionRequestGrouping.Key.Person.Email }; Email.Send(systemEmailTemplate.From.ResolveMergeFields(mergeFields), systemEmailTemplate.FromName.ResolveMergeFields(mergeFields), systemEmailTemplate.Subject.ResolveMergeFields(mergeFields), recipients, systemEmailTemplate.Body.ResolveMergeFields(mergeFields), appRoot, null); mailedCount++; } context.Result = string.Format("{0} reminders were sent ", mailedCount); }
/// <summary> /// Processes the binary file request. /// </summary> /// <param name="context">The context.</param> private void ProcessBinaryFileRequest(HttpContext context, RockContext rockContext) { int fileId = context.Request.QueryString["id"].AsInteger(); Guid fileGuid = context.Request.QueryString["guid"].AsGuid(); if (fileId == 0 && fileGuid == Guid.Empty) { SendBadRequest(context, "File id key must be a guid or an int."); return; } var binaryFileQuery = new BinaryFileService(rockContext).Queryable(); if (fileGuid != Guid.Empty) { binaryFileQuery = binaryFileQuery.Where(a => a.Guid == fileGuid); } else { binaryFileQuery = binaryFileQuery.Where(a => a.Id == fileId); } //// get just the binaryFileMetaData (not the file content) just in case we can get the filecontent faster from the cache //// a null ModifiedDateTime shouldn't happen, but just in case, set it to DateTime.MaxValue so we error on the side of not getting it from the cache var binaryFileMetaData = binaryFileQuery.Select(a => new { a.Id, BinaryFileType_CacheToServerFileSystem = a.BinaryFileType.CacheToServerFileSystem, BinaryFileType_RequiresViewSecurity = a.BinaryFileType.RequiresViewSecurity, a.BinaryFileTypeId, ModifiedDateTime = a.ModifiedDateTime ?? DateTime.MaxValue, a.MimeType, a.FileName }).FirstOrDefault(); if (binaryFileMetaData == null) { SendNotFound(context); return; } // Add cache validation headers context.Response.AddHeader("Last-Modified", binaryFileMetaData.ModifiedDateTime.ToUniversalTime().ToString("R")); context.Response.AddHeader("ETag", binaryFileMetaData.ModifiedDateTime.ToString().XxHash()); //// if the binaryFile's BinaryFileType requires view security, check security //// note: we put a RequiresViewSecurity flag on BinaryFileType because checking security for every image would be slow (~40ms+ per image request) if (binaryFileMetaData.BinaryFileType_RequiresViewSecurity) { var currentUser = new UserLoginService(rockContext).GetByUserName(UserLogin.GetCurrentUserName()); Person currentPerson = currentUser != null ? currentUser.Person : null; BinaryFile binaryFileAuth = new BinaryFileService(rockContext).Queryable("BinaryFileType").AsNoTracking().First(a => a.Id == binaryFileMetaData.Id); if (!binaryFileAuth.IsAuthorized(Authorization.VIEW, currentPerson)) { SendNotAuthorized(context); return; } } Stream fileContent = null; try { // Is it cached string cacheName = UrlQueryToCachedFileName(context.Request.QueryString, binaryFileMetaData.MimeType); string physCachedFilePath = context.Request.MapPath(string.Format("~/App_Data/Cache/{0}", cacheName)); if (binaryFileMetaData.BinaryFileType_CacheToServerFileSystem && File.Exists(physCachedFilePath)) { //// Compare the File's LastWrite DateTime (which comes from the OS's clock), adjust it for the Rock OrgTimeZone, then compare to BinaryFile's ModifiedDateTime (which is already in OrgTimeZone). //// If the BinaryFile record in the database is less recent than the last time this was cached, it is safe to use the Cached version. //// NOTE: A BinaryFile record is typically just added and never modified (a modify is just creating a new BinaryFile record and deleting the old one), so the cached version will probably always be the correct choice. DateTime cachedFileDateTime = RockDateTime.ConvertLocalDateTimeToRockDateTime(File.GetLastWriteTime(physCachedFilePath)); if (binaryFileMetaData.ModifiedDateTime < cachedFileDateTime) { // NOTE: the cached file has already been resized (the size is part of the cached file's filename), so we don't need to resize it again fileContent = FetchFromCache(physCachedFilePath); } } if (fileContent == null) { // If we didn't get it from the cache, get it from the binaryFileService BinaryFile binaryFile = GetFromBinaryFileService(context, binaryFileMetaData.Id, rockContext); if (binaryFile != null) { fileContent = binaryFile.ContentStream; } // If we got the image from the binaryFileService, it might need to be resized and cached if (fileContent != null) { // If more than 1 query string param is passed in, or the mime type is TIFF, assume resize is needed // Note: we force "image/tiff" to get resized so that it gets converted into a jpg (browsers don't like tiffs) if (context.Request.QueryString.Count > GetQueryCount(context) || binaryFile.MimeType == "image/tiff") { // if it isn't an SVG file, do a Resize if (binaryFile.MimeType != "image/svg+xml") { fileContent = GetResized(context.Request.QueryString, fileContent); } } if (binaryFileMetaData.BinaryFileType_CacheToServerFileSystem) { Cache(fileContent, physCachedFilePath); // Reset stream if (fileContent.CanSeek) { fileContent.Seek(0, SeekOrigin.Begin); } else { fileContent = FetchFromCache(physCachedFilePath); } } } } if (fileContent == null) { // if we couldn't get the file from the binaryFileServie or the cache, respond with NotFound SendNotFound(context); return; } // respond with File if (binaryFileMetaData.BinaryFileTypeId != null) { var binaryFileCache = BinaryFileTypeCache.Get(binaryFileMetaData.BinaryFileTypeId.Value); binaryFileCache.CacheControlHeader.SetupHttpCachePolicy(context.Response.Cache); } // set the mime-type to that of the binary file context.Response.ContentType = binaryFileMetaData.MimeType != "image/tiff" ? binaryFileMetaData.MimeType : "image/jpg"; // check that the format of the image wasn't changed by a format query parm if so adjust the mime-type to reflect the conversion if (context.Request["format"].IsNotNullOrWhiteSpace()) { switch (context.Request["format"]) { case "png": { context.Response.ContentType = "image/png"; break; } case "gif": { context.Response.ContentType = "image/gif"; break; } case "jpg": { context.Response.ContentType = "image/jpeg"; break; } } } using (var responseStream = fileContent) { context.Response.AddHeader("content-disposition", "inline;filename=" + binaryFileMetaData.FileName.MakeValidFileName().UrlEncode()); if (responseStream.CanSeek) { responseStream.Seek(0, SeekOrigin.Begin); } responseStream.CopyTo(context.Response.OutputStream); context.Response.Flush(); } } finally { if (fileContent != null) { fileContent.Dispose(); } } }