public HttpResponseMessage ProcessMobileCheckin(string param) { try { var session = HttpContext.Current.Session; var currentCheckInState = session["CheckInState"] as CheckInState; if (currentCheckInState.CheckIn.SearchType.Guid != Constants.CHECKIN_SEARCH_TYPE_USERLOGIN.AsGuid()) { throw new Exception(); //We'll catch this later and return a forbidden } var localDeviceConfigCookie = HttpContext.Current.Request.Cookies[CheckInCookieKey.LocalDeviceConfig].Value; var localDevice = localDeviceConfigCookie.FromJsonOrNull <LocalDeviceConfiguration>(); var rockContext = new Rock.Data.RockContext(); UserLoginService userLoginService = new UserLoginService(rockContext); var family = userLoginService.Queryable().AsNoTracking() .Where(u => u.UserName == currentCheckInState.CheckIn.SearchValue) .Select(u => u.Person.PrimaryFamily) .FirstOrDefault(); var checkinFamily = new CheckInFamily { Group = family.Clone(false), Caption = family.ToString(), Selected = true }; currentCheckInState.CheckIn.Families.Add(checkinFamily); SaveState(session, currentCheckInState); Guid blockGuid = ( Guid )session["BlockGuid"]; var block = BlockCache.Get(blockGuid); Guid? workflowGuid = block.GetAttributeValue("WorkflowType").AsGuidOrNull(); string workflowActivity = block.GetAttributeValue("WorkflowActivity"); List <string> errors; var workflowService = new WorkflowService(rockContext); var workflowType = WorkflowTypeCache.Get(workflowGuid.Value); var CurrentWorkflow = Rock.Model.Workflow.Activate(workflowType, currentCheckInState.Kiosk.Device.Name, rockContext); var activityType = workflowType.ActivityTypes.Where(a => a.Name == workflowActivity).FirstOrDefault(); if (activityType != null) { WorkflowActivity.Activate(activityType, CurrentWorkflow, rockContext); if (workflowService.Process(CurrentWorkflow, currentCheckInState, out errors)) { if (errors.Any()) { var innerException = new Exception(string.Join(" -- ", errors)); ExceptionLogService.LogException(new Exception("Process Mobile Checkin failed initial workflow. See inner exception for details.", innerException)); } // Keep workflow active for continued processing CurrentWorkflow.CompletedDateTime = null; SaveState(session, currentCheckInState); List <CheckInFamily> families = currentCheckInState.CheckIn.Families; families = families.OrderBy(f => f.Caption).ToList(); return(ControllerContext.Request.CreateResponse(HttpStatusCode.OK, param)); } else { if (errors.Any()) { var innerException = new Exception(string.Join(" -- ", errors)); ExceptionLogService.LogException(new Exception("Process Mobile Checkin failed initial workflow. See inner exception for details.", innerException)); } else { ExceptionLogService.LogException(new Exception("Process Mobile Checkin failed initial workflow. See inner exception for details.")); } } } else { return(ControllerContext.Request.CreateResponse(HttpStatusCode.InternalServerError, string.Format("Workflow type does not have a '{0}' activity type", workflowActivity))); } return(ControllerContext.Request.CreateResponse(HttpStatusCode.InternalServerError, String.Join("\n", errors))); } catch (Exception ex) { ExceptionLogService.LogException(ex, HttpContext.Current); return(ControllerContext.Request.CreateResponse(HttpStatusCode.Forbidden, "Forbidden")); } }
/// <summary> /// Executes the specified context. /// </summary> /// <param name="context">The context.</param> public void Execute(IJobExecutionContext context) { JobDataMap dataMap = context.JobDetail.JobDataMap; Guid? groupGuid = dataMap.GetString("EligibleFollowers").AsGuidOrNull(); Guid? systemEmailGuid = dataMap.GetString("EmailTemplate").AsGuidOrNull(); int followingEventsSent = 0; if (groupGuid.HasValue && systemEmailGuid.HasValue) { var exceptionMsgs = new List <string>(); using (var rockContext = new RockContext()) { var followingService = new FollowingService(rockContext); var followingEventTypeService = new FollowingEventTypeService(rockContext); var followingEventNotificationService = new FollowingEventNotificationService(rockContext); // Get all the active event types var eventTypes = followingEventTypeService .Queryable().AsNoTracking() .Where(e => e.EntityTypeId.HasValue && e.IsActive) .OrderBy(e => e.Order) .ToList(); // Get the required event types var requiredEventTypes = eventTypes .Where(e => e.IsNoticeRequired) .ToList(); // The people who are eligible to get following event notices based on the group type setting for this job var eligiblePersonIds = new GroupMemberService(rockContext) .Queryable().AsNoTracking() .Where(m => m.Group != null && m.Group.Guid.Equals(groupGuid.Value) && m.GroupMemberStatus == GroupMemberStatus.Active && m.Person != null && m.Person.Email != null && m.Person.Email != "") .Select(m => m.PersonId) .Distinct() .ToList(); // Get all the subscriptions for the eligible people var eventSubscriptions = new FollowingEventSubscriptionService(rockContext) .Queryable("PersonAlias").AsNoTracking() .Where(f => eligiblePersonIds.Contains(f.PersonAlias.PersonId)) .ToList(); // Dictionaries used to store information that will be used to create notification var personSubscriptions = new Dictionary <int, List <int> >(); // Key: personId, Value: list of event type ids that person subscribes to var personFollowings = new Dictionary <int, List <int> >(); // Key: personId, Value: list of following ids that person follows var eventsThatHappened = new Dictionary <int, Dictionary <int, string> >(); // Key: event type id Value: Dictionary of entity id and formatted event notice for the entity //Get the subscriptions for each person foreach (int personId in eligiblePersonIds) { var personEventTypes = eventSubscriptions .Where(s => s.PersonAlias.PersonId == personId) .Select(s => s.EventType) .ToList(); personEventTypes.AddRange(requiredEventTypes); if (personEventTypes.Any()) { personSubscriptions.AddOrIgnore(personId, personEventTypes .OrderBy(e => e.Order) .ThenBy(e => e.Name) .Select(e => e.Id) .Distinct() .ToList()); } } // Get a distinct list of each entitytype/entity that is being followed by anyone that subscribes to events var followings = followingService .Queryable("PersonAlias").AsNoTracking() .Where(f => personSubscriptions.Keys.Contains(f.PersonAlias.PersonId)) .ToList(); // group the followings by their type var followedEntityIds = new Dictionary <int, List <int> >(); foreach (var followedEntity in followings .Select(f => new { f.EntityTypeId, f.EntityId }) .Distinct()) { followedEntityIds.AddOrIgnore(followedEntity.EntityTypeId, new List <int>()); followedEntityIds[followedEntity.EntityTypeId].Add(followedEntity.EntityId); } // group the followings by the follower foreach (int personId in personSubscriptions.Select(s => s.Key)) { var personFollowing = followings .Where(f => f.PersonAlias.PersonId == personId) .Select(f => f.Id) .ToList(); personFollowings.Add(personId, personFollowing); } var timestamp = RockDateTime.Now; // foreach followed entitytype foreach (var keyVal in followedEntityIds) { // Get the entitytype EntityTypeCache itemEntityType = EntityTypeCache.Read(keyVal.Key); if (itemEntityType.AssemblyName != null) { // get the actual type of what is being followed Type entityType = itemEntityType.GetEntityType(); if (entityType != null) { var dbContext = Reflection.GetDbContextForEntityType(entityType); if (dbContext != null) { var serviceInstance = Reflection.GetServiceForEntityType(entityType, dbContext); if (serviceInstance != null) { MethodInfo qryMethod = serviceInstance.GetType().GetMethod("Queryable", new Type[] { }); var entityQry = qryMethod.Invoke(serviceInstance, new object[] { }) as IQueryable <IEntity>; // If looking at person alias following, make sure to exclude deceased people if (entityType == typeof(Rock.Model.PersonAlias)) { var personAliasQry = entityQry as IQueryable <PersonAlias>; if (personAliasQry != null) { entityQry = personAliasQry.Where(p => !p.Person.IsDeceased); } } var entityList = entityQry.Where(q => keyVal.Value.Contains(q.Id)).ToList(); // If there are any followed entities of this type if (entityList.Any()) { // Get the active event types for this entity type foreach (var eventType in eventTypes.Where(e => e.FollowedEntityTypeId == keyVal.Key)) { try { // Get the component var eventComponent = eventType.GetEventComponent(); if (eventComponent != null) { // Get the previous notificatoins for this event type var previousNotifications = followingEventNotificationService .Queryable() .Where(n => n.FollowingEventTypeId == eventType.Id) .ToList(); // check each entity that is followed (by anyone) foreach (IEntity entity in entityList) { var previousNotification = previousNotifications .Where(n => n.EntityId == entity.Id) .FirstOrDefault(); DateTime?lastNotification = previousNotification != null ? previousNotification.LastNotified : (DateTime?)null; // if the event happened if (eventComponent.HasEventHappened(eventType, entity, lastNotification)) { // Store the event type id and the entity for later processing of notifications eventsThatHappened.AddOrIgnore(eventType.Id, new Dictionary <int, string>()); eventsThatHappened[eventType.Id].Add(entity.Id, eventComponent.FormatEntityNotification(eventType, entity)); if (previousNotification == null) { previousNotification = new FollowingEventNotification(); previousNotification.FollowingEventTypeId = eventType.Id; previousNotification.EntityId = entity.Id; followingEventNotificationService.Add(previousNotification); } previousNotification.LastNotified = timestamp; } } rockContext.SaveChanges(); } eventType.LastCheckDateTime = RockDateTime.Now; } catch (Exception ex) { exceptionMsgs.Add(string.Format("An exception occurred calculating events for the '{0}' suggestion type:{1} {2}", eventType.Name, Environment.NewLine, ex.Messages().AsDelimited(Environment.NewLine + " "))); ExceptionLogService.LogException(ex, System.Web.HttpContext.Current); } } } } } } } } // send notificatons var appRoot = Rock.Web.Cache.GlobalAttributesCache.Read(rockContext).GetValue("ExternalApplicationRoot"); var possibleRecipients = new PersonService(rockContext) .Queryable().AsNoTracking() .Where(p => personSubscriptions.Keys.Contains(p.Id)) .ToList(); // Loop through the possible recipients that actually subscribe to events foreach (var personSubscription in personSubscriptions) { // Get the recipient person int personId = personSubscription.Key; var person = possibleRecipients.Where(p => p.Id == personId).FirstOrDefault(); if (person != null) { try { // Make sure person is actually following anything if (personFollowings.ContainsKey(personId)) { // Dictionary to store the entities that had an event for each event type var personEventTypeNotices = new List <FollowingEventTypeNotices>(); // Get the event types that person subscribes to foreach (var eventType in eventsThatHappened.Where(e => personSubscription.Value.Contains(e.Key))) { // Get the EntityTypeId for this event type int entityTypeId = eventTypes .Where(e => e.Id == eventType.Key) .Select(e => e.FollowedEntityTypeId.Value) .FirstOrDefault(); // Find all the entities with this event type that the person follows var personFollowedEntityIds = followings .Where(f => personFollowings[personId].Contains(f.Id) && f.EntityTypeId == entityTypeId) .Select(f => f.EntityId) .ToList(); // Get any of those entities that had an event happen var personFollowedEntities = eventType.Value .Where(e => personFollowedEntityIds.Contains(e.Key)) .ToList(); // If any were found if (personFollowedEntities.Any()) { // Add the entry var eventTypeObj = eventTypes.Where(e => e.Id == eventType.Key).FirstOrDefault(); if (eventTypeObj != null) { personEventTypeNotices.Add(new FollowingEventTypeNotices(eventTypeObj, personFollowedEntities.Select(e => e.Value).ToList())); } } } // If there are any events for any of the entities that this person follows, send a notification if (personEventTypeNotices.Any()) { // Send the notice var recipients = new List <RecipientData>(); var mergeFields = new Dictionary <string, object>(); mergeFields.Add("Person", person); mergeFields.Add("EventTypes", personEventTypeNotices.OrderBy(e => e.EventType.Order).ToList()); recipients.Add(new RecipientData(person.Email, mergeFields)); Email.Send(systemEmailGuid.Value, recipients, appRoot); followingEventsSent++; } } } catch (Exception ex) { exceptionMsgs.Add(string.Format("An exception occurred sending event notice to '{0}':{1} {2}", person.FullName, Environment.NewLine, ex.Messages().AsDelimited(Environment.NewLine + " "))); ExceptionLogService.LogException(ex, System.Web.HttpContext.Current); } } } } context.Result = string.Format("{0} following events emails sent", followingEventsSent); if (exceptionMsgs.Any()) { throw new Exception("One or more exceptions occurred calculating following events..." + Environment.NewLine + exceptionMsgs.AsDelimited(Environment.NewLine)); } } }
/// <summary> /// Executes the specified workflow. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="action">The action.</param> /// <param name="entity">The entity.</param> /// <param name="errorMessages">The error messages.</param> /// <returns></returns> public override bool Execute(RockContext rockContext, WorkflowAction action, Object entity, out List <string> errorMessages) { errorMessages = new List <string>(); var mergeFields = GetMergeFields(action); // Validate the person exists var personGuid = GetAttributeValue(action, AttributeKey.Person, true).AsGuidOrNull(); if (!personGuid.HasValue) { errorMessages.Add("The person guid is required but was missing"); return(LogMessagesForExit(action, errorMessages)); } var personService = new PersonService(rockContext); var person = personService.Queryable("Aliases").AsNoTracking() .FirstOrDefault(p => p.Guid == personGuid.Value || p.Aliases.Any(pa => pa.Guid == personGuid.Value)); if (person == null) { errorMessages.Add($"The person with the guid '{personGuid.Value}' was not found"); return(LogMessagesForExit(action, errorMessages)); } if (!person.PrimaryAliasId.HasValue) { errorMessages.Add($"{person.FullName} does not have a primary alias identifier"); return(LogMessagesForExit(action, errorMessages)); } // Validate the step type exists. Could be a step type id or a guid var stepType = GetStepType(rockContext, action, out var errorMessage); if (!errorMessage.IsNullOrWhiteSpace()) { errorMessages.Add(errorMessage); return(LogMessagesForExit(action, errorMessages)); } if (stepType == null) { errorMessages.Add("The step type could not be found"); return(LogMessagesForExit(action, errorMessages)); } // Validate the step status exists and is in the same program as the step type var stepStatus = GetStepStatus(stepType, action, out errorMessage); if (!errorMessage.IsNullOrWhiteSpace()) { errorMessages.Add(errorMessage); return(LogMessagesForExit(action, errorMessages)); } if (stepStatus == null) { errorMessages.Add("The step status could not be found"); return(LogMessagesForExit(action, errorMessages)); } // Get the start and end dates var startDate = GetLavaAttributeValue(action, AttributeKey.StartDate).AsDateTime() ?? RockDateTime.Now; var endDate = GetLavaAttributeValue(action, AttributeKey.EndDate).AsDateTime(); var campusAttributeValue = GetLavaAttributeValue(action, AttributeKey.Campus); var campusId = campusAttributeValue.AsIntegerOrNull(); var campusGuid = campusAttributeValue.AsGuidOrNull(); if (campusGuid != null) { var campus = CampusCache.Get(campusGuid.Value); if (campus != null) { campusId = campus.Id; } } // The completed date is today or the end date if the status is a completed status var completedDate = stepStatus.IsCompleteStatus ? (endDate ?? RockDateTime.Now) : ( DateTime? )null; // Create the step object var step = new Step { StepTypeId = stepType.Id, PersonAliasId = person.PrimaryAliasId.Value, StartDateTime = startDate, EndDateTime = endDate, CompletedDateTime = completedDate, StepStatusId = stepStatus.Id, CampusId = campusId }; // Validate the step if (!step.IsValid) { errorMessages.AddRange(step.ValidationResults.Select(a => a.ErrorMessage)); return(LogMessagesForExit(action, errorMessages)); } // Check if the step can be created because of Allow Multiple rules on the step type and also prerequisite requirements var stepService = new StepService(rockContext); var canAdd = stepService.CanAdd(step, out errorMessage); if (!errorMessage.IsNullOrWhiteSpace()) { errorMessages.Add(errorMessage); } else if (!canAdd) { errorMessages.Add("Cannot add the step for an unspecified reason"); } else { try { stepService.Add(step); rockContext.SaveChanges(); SetCreatedItemAttribute(action, AttributeKey.StepAttribute, step, rockContext); } catch (Exception exception) { errorMessages.Add($"Exception thrown: {exception.Message}"); ExceptionLogService.LogException(exception); } } return(LogMessagesForExit(action, errorMessages)); }
/// <summary> /// Performs the final step of a three-step charge. /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <param name="resultQueryString">The result query string from step 2.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public override FinancialTransaction ChargeStep3(FinancialGateway financialGateway, string resultQueryString, out string errorMessage) { errorMessage = string.Empty; try { var rootElement = GetRoot(financialGateway, "complete-action"); rootElement.Add(new XElement("token-id", resultQueryString.Substring(10))); XDocument xdoc = new XDocument(new XDeclaration("1.0", "UTF-8", "yes"), rootElement); var result = PostToGateway(financialGateway, xdoc); if (result == null) { errorMessage = "Invalid Response from NMI!"; return(null); } if (result.GetValueOrNull("result") != "1") { errorMessage = result.GetValueOrNull("result-text"); string resultCodeMessage = GetResultCodeMessage(result); if (resultCodeMessage.IsNotNullOrWhitespace()) { errorMessage += string.Format(" ({0})", resultCodeMessage); } // write result error as an exception ExceptionLogService.LogException(new Exception($"Error processing NMI transaction. Result Code: {result.GetValueOrNull( "result-code" )} ({resultCodeMessage}). Result text: {result.GetValueOrNull( "result-text" )}. Card Holder Name: {result.GetValueOrNull( "first-name" )} {result.GetValueOrNull( "last-name" )}. Amount: {result.GetValueOrNull( "total-amount" )}. Transaction id: {result.GetValueOrNull( "transaction-id" )}. Descriptor: {result.GetValueOrNull( "descriptor" )}. Order description: {result.GetValueOrNull( "order-description" )}.")); return(null); } var transaction = new FinancialTransaction(); transaction.TransactionCode = result.GetValueOrNull("transaction-id"); transaction.ForeignKey = result.GetValueOrNull("customer-vault-id"); transaction.FinancialPaymentDetail = new FinancialPaymentDetail(); string ccNumber = result.GetValueOrNull("billing_cc-number"); if (!string.IsNullOrWhiteSpace(ccNumber)) { // cc payment var curType = DefinedValueCache.Read(Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_CREDIT_CARD); transaction.FinancialPaymentDetail.NameOnCardEncrypted = Encryption.EncryptString($"{result.GetValueOrNull( "billing_first-name" )} {result.GetValueOrNull( "billing_last-name" )}"); transaction.FinancialPaymentDetail.CurrencyTypeValueId = curType != null ? curType.Id : (int?)null; transaction.FinancialPaymentDetail.CreditCardTypeValueId = CreditCardPaymentInfo.GetCreditCardType(ccNumber.Replace('*', '1').AsNumeric())?.Id; transaction.FinancialPaymentDetail.AccountNumberMasked = ccNumber.Masked(true); string mmyy = result.GetValueOrNull("billing_cc-exp"); if (!string.IsNullOrWhiteSpace(mmyy) && mmyy.Length == 4) { transaction.FinancialPaymentDetail.ExpirationMonthEncrypted = Encryption.EncryptString(mmyy.Substring(0, 2)); transaction.FinancialPaymentDetail.ExpirationYearEncrypted = Encryption.EncryptString(mmyy.Substring(2, 2)); } } else { // ach payment var curType = DefinedValueCache.Read(Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_ACH); transaction.FinancialPaymentDetail.CurrencyTypeValueId = curType != null ? curType.Id : (int?)null; transaction.FinancialPaymentDetail.AccountNumberMasked = result.GetValueOrNull("billing_account-number").Masked(true); } transaction.AdditionalLavaFields = new Dictionary <string, object>(); foreach (var keyVal in result) { transaction.AdditionalLavaFields.Add(keyVal.Key, keyVal.Value); } return(transaction); } catch (WebException webException) { string message = GetResponseMessage(webException.Response.GetResponseStream()); errorMessage = webException.Message + " - " + message; return(null); } catch (Exception ex) { errorMessage = ex.Message; return(null); } }
public MobilePage GetPage(int id, string parameter = "") { var person = GetPerson(); if (!HttpContext.Current.Items.Contains("CurrentPerson")) { HttpContext.Current.Items.Add("CurrentPerson", person); } var pageCache = PageCache.Get(id); if (pageCache == null || !pageCache.IsAuthorized(Authorization.VIEW, person)) { return(new MobilePage()); } SavePageViewInteraction(pageCache, person); string theme = pageCache.Layout.Site.Theme; string layout = pageCache.Layout.FileName; string layoutPath = PageCache.FormatPath(theme, layout); Rock.Web.UI.RockPage cmsPage = (Rock.Web.UI.RockPage)BuildManager.CreateInstanceFromVirtualPath(layoutPath, typeof(Rock.Web.UI.RockPage)); MobilePage mobilePage = new MobilePage(); mobilePage.Layout = AvalancheUtilities.GetLayout(pageCache.Layout.Name); mobilePage.Title = pageCache.PageTitle; mobilePage.ShowTitle = pageCache.PageDisplayTitle; foreach (var attribute in pageCache.Attributes) { mobilePage.Attributes.Add(attribute.Key, pageCache.GetAttributeValue(attribute.Key)); } foreach (var block in pageCache.Blocks) { if (block.IsAuthorized(Authorization.VIEW, person)) { var blockCache = BlockCache.Get(block.Id); try { var control = ( RockBlock )cmsPage.TemplateControl.LoadControl(blockCache.BlockType.Path); if (control is RockBlock && control is IMobileResource) { control.SetBlock(pageCache, blockCache); var mobileResource = control as IMobileResource; var mobileBlock = mobileResource.GetMobile(parameter); mobileBlock.BlockId = blockCache.Id; mobileBlock.Zone = blockCache.Zone; mobilePage.Blocks.Add(mobileBlock); } } catch (Exception e) { ExceptionLogService.LogException(e, HttpContext.Current); } } } HttpContext.Current.Response.Headers.Set("TTL", pageCache.OutputCacheDuration.ToString()); return(mobilePage); }
/// <summary> /// Create new attempt records and return them in a list. All new attempts should be after the most recent successful attempt. /// </summary> /// <param name="streakTypeAchievementTypeCache">The streak type achievement type cache.</param> /// <param name="streak">The streak.</param> /// <param name="mostRecentSuccess">The most recent successful attempt.</param> /// <returns></returns> protected override List <StreakAchievementAttempt> CreateNewAttempts(StreakTypeAchievementTypeCache streakTypeAchievementTypeCache, Streak streak, StreakAchievementAttempt mostRecentSuccess) { var rockContext = new RockContext(); var streakTypeService = new StreakTypeService(rockContext); var streakTypeCache = streakTypeAchievementTypeCache.StreakTypeCache; // Validate the attribute values var numberToAccumulate = GetAttributeValue(streakTypeAchievementTypeCache, AttributeKey.NumberToAccumulate).AsInteger(); if (numberToAccumulate <= 0) { ExceptionLogService.LogException($"AccumulativeAchievement.CreateNewAttempts cannot process because the NumberToAccumulate attribute is less than 1"); return(null); } var attributeTimespanDays = GetAttributeValue(streakTypeAchievementTypeCache, AttributeKey.TimespanInDays).AsIntegerOrNull(); if (attributeTimespanDays.HasValue && attributeTimespanDays.Value <= 0) { ExceptionLogService.LogException($"AccumulativeAchievement.CreateNewAttempts cannot process because the TimespanInDays attribute is less than 1"); return(null); } // Calculate the date range where new achievements can be validly found var attributeMinDate = GetAttributeValue(streakTypeAchievementTypeCache, AttributeKey.StartDateTime).AsDateTime(); var attributeMaxDate = GetAttributeValue(streakTypeAchievementTypeCache, AttributeKey.EndDateTime).AsDateTime(); var minDate = CalculateMinDateForAchievementAttempt(streak.EnrollmentDate, mostRecentSuccess, attributeMinDate, numberToAccumulate); var maxDate = CalculateMaxDateForAchievementAttempt(minDate, attributeMaxDate); // Track the attempts in a list that will be returned. The int is the streak count for that attempt var attempts = new List <StreakAchievementAttempt>(); var accumulations = new List <ComputedStreak>(); // Define what happens for each bit in the date range bool iterationAction(int currentUnit, DateTime currentDate, bool hasOccurrence, bool hasEngagement, bool hasExclusion) { // If there is an engagement and a timespan, then this is a possible attempt. If there is no timespan then only one // attempt needs to be tracked at a time if (hasOccurrence && hasEngagement && (attributeTimespanDays.HasValue || !accumulations.Any())) { accumulations.Add(new ComputedStreak(currentDate)); } for (var i = accumulations.Count - 1; i >= 0; i--) { var accumulation = accumulations[i]; if (hasOccurrence && hasEngagement) { // Increment the accumulation accumulation.Count++; accumulation.EndDate = currentDate; // Check for a fulfilled attempt if (accumulation.Count >= numberToAccumulate) { accumulations.Clear(); if (streakTypeAchievementTypeCache.AllowOverAchievement) { accumulations.Add(accumulation); i = 0; } else { attempts.Add(GetAttempt(accumulation, numberToAccumulate, true)); break; } } } // If there is a timespan and this accumulation is too old, then the attempt is closed if (attributeTimespanDays.HasValue) { var inclusiveAge = (currentDate - accumulation.StartDate).Days + 1; if (inclusiveAge >= attributeTimespanDays.Value) { var timedOutAttempt = GetAttempt(accumulation, numberToAccumulate, true); attempts.Add(timedOutAttempt); accumulations.RemoveAt(i); // Remove more recently started accumulations that started before the next valid start date (based // on the deficiency of this timed out attempt) var nextValidStartDate = CalculateMinDateForAchievementAttempt(streak.EnrollmentDate, timedOutAttempt, attributeMinDate, numberToAccumulate); for (var j = accumulations.Count - 1; j >= i; j--) { var moreRecentAccumulation = accumulations[j]; if (moreRecentAccumulation.StartDate < nextValidStartDate) { accumulations.RemoveAt(j); } } } } } return(false); } // Iterate through the streak date for the date range specified streakTypeService.IterateStreakMap(streakTypeCache, streak.PersonAliasId, minDate, maxDate, iterationAction, out var errorMessage); if (!errorMessage.IsNullOrWhiteSpace()) { ExceptionLogService.LogException($"AccumulativeAchievement.CreateNewAttempts got an error calling StreakTypeService.IterateStreakMap: {errorMessage}"); return(null); } // The longest leftover accumulation is an open attempt if (accumulations.Any()) { var longestStreak = accumulations.First(); attempts.Add(GetAttempt(longestStreak, numberToAccumulate, false)); } return(attempts); }
/// <summary> /// Handles the Click event of the btnMigrateScheduledTransactions control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> protected void btnMigrateScheduledTransactions_Click(object sender, EventArgs e) { var rockContext = new RockContext(); var binaryFileService = new BinaryFileService(rockContext); var binaryFileId = fuScheduleImportFile.BinaryFileId; BinaryFile binaryFile = null; if (binaryFileId.HasValue) { binaryFile = binaryFileService.Get(binaryFileId.Value); } Dictionary <string, string> subscriptionImportRecordLookup = null; var importData = binaryFile.ContentsToString(); StringReader stringReader = new StringReader(importData); CsvReader csvReader = new CsvReader(stringReader); csvReader.Configuration.HasHeaderRecord = false; subscriptionImportRecordLookup = csvReader.GetRecords <SubscriptionCustomerImportRecord>().ToDictionary(k => k.NMISubscriptionId, v => v.PiCustomerId); var financialGatewayService = new FinancialGatewayService(rockContext); var nmiFinancialGatewayId = ddlNMIGateway.SelectedValue.AsInteger(); var nmiFinancialGateway = financialGatewayService.Get(nmiFinancialGatewayId); var nmiGatewayComponent = nmiFinancialGateway.GetGatewayComponent(); var piFinancialGatewayId = ddlPiGateway.SelectedValue.AsInteger(); var piFinancialGateway = financialGatewayService.Get(piFinancialGatewayId); var piGatewayComponent = piFinancialGateway.GetGatewayComponent() as IHostedGatewayComponent; var financialScheduledTransactionService = new FinancialScheduledTransactionService(rockContext); // Get the ScheduledTransaction with NoTracking. If we need to update it, we'll track it with a different rockContext then save it. // Limit to active subscriptions that have a NextPaymentDate (onetime or canceled schedules might not have a NextPaymentDate) var scheduledTransactions = financialScheduledTransactionService.Queryable().Where(a => a.FinancialGatewayId == nmiFinancialGatewayId & a.IsActive && a.NextPaymentDate.HasValue).AsNoTracking().ToList(); var earliestPiStartDate = piGatewayComponent.GetEarliestScheduledStartDate(piFinancialGateway); var oneTimeFrequencyId = DefinedValueCache.GetId(Rock.SystemGuid.DefinedValue.TRANSACTION_FREQUENCY_ONE_TIME.AsGuid()); string errorMessage; var scheduledTransactionResultsBuilder = new StringBuilder(); var scheduledTransactionCount = scheduledTransactions.Count(); var scheduledTransactionProgress = 0; // Migrating Scheduled Transactions might take a while. Each migrated Scheduled Payment may take a half second or so to create on the Pi Gateway. var importTask = new Task(() => { // wait a little so the browser can render and start listening to events Task.Delay(1000).Wait(); _hubContext.Clients.All.setButtonVisibilty(this.SignalRNotificationKey, false); foreach (var scheduledTransaction in scheduledTransactions) { System.Threading.Thread.Sleep(1000); UpdateProgressMessage(string.Format("Migrating Scheduled Transactions: {0} of {1}", scheduledTransactionProgress, scheduledTransactionCount), " "); scheduledTransactionProgress++; var nmiSubscriptionId = scheduledTransaction.GatewayScheduleId; var nmiCustomerId = scheduledTransaction.ForeignKey; var piCustomerId = subscriptionImportRecordLookup.GetValueOrNull(nmiSubscriptionId); if (piCustomerId == null) { scheduledTransactionResultsBuilder.AppendFormat( "WARNING: No Pi CustomerId found for Financial Scheduled Transaction with Id: {0} which is associated NMI SubscriptionId: '{1}'" + Environment.NewLine, scheduledTransaction.Id, nmiSubscriptionId ); continue; } // Pi requires that NextPaymentDate is in the Future (using UTC). That math is done in the gateway implementation... // if the NextPayment null or earlier than whatever Pi considers the earliest start date, see if we can fix that up by calling GetStatus if (scheduledTransaction.NextPaymentDate == null || scheduledTransaction.NextPaymentDate < earliestPiStartDate) { financialScheduledTransactionService.GetStatus(scheduledTransaction, out errorMessage); } if (scheduledTransaction.NextPaymentDate == null) { // Shouldn't happen, but just in case scheduledTransactionResultsBuilder.AppendFormat( "WARNING: Unknown NextPaymentDate for FinancialScheduledTransaction.Id: {0} NMI SubscriptionId: '{1}'" + Environment.NewLine, scheduledTransaction.Id, nmiSubscriptionId ); continue; } if (scheduledTransaction.NextPaymentDate < earliestPiStartDate) { if ((scheduledTransaction.NextPaymentDate > RockDateTime.Today) && earliestPiStartDate.Subtract(scheduledTransaction.NextPaymentDate.Value).TotalDays <= 2) { // if the NextPaymentDate is after Today but before the Earliest Pi Start Date, it'll be off by less than 24 hrs, so just reschedule it for the Earliest Pi Start Date scheduledTransaction.NextPaymentDate = earliestPiStartDate; } else { // if the NextPaymentDate is still too early AFTER getting the most recent status, then we can't safely figure it out, so report it scheduledTransactionResultsBuilder.AppendFormat( "WARNING: NextPaymentDate of {0} for FinancialScheduledTransaction.Id: {1} and NMI SubscriptionId: '{2}' must have a NextPaymentDate of at least {3}." + Environment.NewLine, scheduledTransaction.NextPaymentDate, scheduledTransaction.Id, nmiSubscriptionId, earliestPiStartDate ); } } // create a subscription in the Pi System, then cancel the one on the NMI system PaymentSchedule paymentSchedule = new PaymentSchedule { TransactionFrequencyValue = DefinedValueCache.Get(scheduledTransaction.TransactionFrequencyValueId), StartDate = scheduledTransaction.NextPaymentDate.Value, PersonId = scheduledTransaction.AuthorizedPersonAlias.PersonId }; ReferencePaymentInfo referencePaymentInfo = new ReferencePaymentInfo { GatewayPersonIdentifier = piCustomerId, Description = string.Format("Migrated from NMI SubscriptionID:{0}", nmiSubscriptionId) }; var piGateway = (piGatewayComponent as PiGateway); string alreadyMigratedPiSubscriptionId = null; if (piGateway != null) { var customerPiSubscriptions = piGateway.SearchCustomerSubscriptions(piFinancialGateway, piCustomerId); alreadyMigratedPiSubscriptionId = customerPiSubscriptions.Data.Where(a => a.Description.Contains(referencePaymentInfo.Description)).Select(a => a.Customer.Id).FirstOrDefault(); } if (string.IsNullOrEmpty(alreadyMigratedPiSubscriptionId)) { // hasn't already been migrated, so go ahead and migrate it var tempFinancialScheduledTransaction = piGatewayComponent.AddScheduledPayment(piFinancialGateway, paymentSchedule, referencePaymentInfo, out errorMessage); if (tempFinancialScheduledTransaction != null) { ////////////#### DISABLE this when debugger ##### nmiGatewayComponent.CancelScheduledPayment(scheduledTransaction, out errorMessage); // update the scheduled transaction to point to the Pi scheduled transaction using (var updateRockContext = new RockContext()) { // Attach the person to the updateRockContext so that it'll be tracked/saved using updateRockContext updateRockContext.FinancialScheduledTransactions.Attach(scheduledTransaction); scheduledTransaction.TransactionCode = tempFinancialScheduledTransaction.TransactionCode; scheduledTransaction.GatewayScheduleId = tempFinancialScheduledTransaction.GatewayScheduleId; scheduledTransaction.FinancialGatewayId = tempFinancialScheduledTransaction.FinancialGatewayId; updateRockContext.SaveChanges(); } scheduledTransactionResultsBuilder.AppendFormat( "SUCCESS: Scheduled Transaction migration succeeded. (FinancialScheduledTransaction.Id: {0}, NMI SubscriptionId: '{1}', Pi CustomerId: {2}, Pi SubscriptionId: {3})" + Environment.NewLine, scheduledTransaction.Id, nmiSubscriptionId, piCustomerId, scheduledTransaction.GatewayScheduleId ); } else { scheduledTransactionResultsBuilder.AppendFormat( "ERROR: Scheduled Transaction migration failed. ErrorMessage: {0}, FinancialScheduledTransaction.Id: {1}, NMI SubscriptionId: '{2}', Pi CustomerId: {3}" + Environment.NewLine, errorMessage, scheduledTransaction.Id, nmiSubscriptionId, piCustomerId ); } } else { scheduledTransactionResultsBuilder.AppendFormat( "INFO: Scheduled Transaction already migrated to PI. FinancialScheduledTransaction.Id: {0}, NMI SubscriptionId: '{1}', Pi SubscriptionId: '{2}', Pi CustomerId: {3}" + Environment.NewLine, scheduledTransaction.Id, nmiSubscriptionId, alreadyMigratedPiSubscriptionId, piCustomerId ); } } }); string importResult = string.Empty; importTask.ContinueWith((c) => { if (c.Exception != null) { ExceptionLogService.LogException(c.Exception); scheduledTransactionResultsBuilder.AppendLine(string.Format("EXCEPTION: {0}", c.Exception.Flatten().Message)); importResult = "EXCEPTION"; UpdateProgressMessage(importResult, scheduledTransactionResultsBuilder.ToString()); } else { importResult = "Migrate Scheduled Transactions Completed Successfully"; UpdateProgressMessage(importResult, scheduledTransactionResultsBuilder.ToString()); } this.SetBlockUserPreference("MigrateScheduledTransactionsResultSummary", importResult); this.SetBlockUserPreference("MigrateScheduledTransactionsResultDetails", scheduledTransactionResultsBuilder.ToString()); }); importTask.Start(); nbMigrateScheduledTransactions.Visible = false; // wait for 5 seconds to see if this happens fast enough to do without Signal R. Otherwise, let the importTask continue and send progress to Signal R. var waitResult = importTask.Wait(5000); if (waitResult) { // wait just a little bit to make sure the importResult gets set System.Threading.Thread.Sleep(1000); nbMigrateScheduledTransactions.Visible = true; nbMigrateScheduledTransactions.Title = "Success"; nbMigrateScheduledTransactions.NotificationBoxType = Rock.Web.UI.Controls.NotificationBoxType.Success; var resultDetails = scheduledTransactionResultsBuilder.ToString(); if (resultDetails.Contains("ERROR") || resultDetails.Contains("WARNING")) { nbMigrateScheduledTransactions.Title = "Completed with Warnings"; nbMigrateScheduledTransactions.NotificationBoxType = Rock.Web.UI.Controls.NotificationBoxType.Info; } nbMigrateScheduledTransactions.Text = importResult; nbMigrateScheduledTransactions.Details = resultDetails.ConvertCrLfToHtmlBr(); } }
/// <summary> /// Sends the specified rock message. /// </summary> /// <param name="rockMessage">The rock message.</param> /// <param name="mediumEntityTypeId">The medium entity type identifier.</param> /// <param name="mediumAttributes">The medium attributes.</param> /// <param name="errorMessages">The error messages.</param> /// <returns></returns> public override bool Send(RockMessage rockMessage, int mediumEntityTypeId, Dictionary <string, string> mediumAttributes, out List <string> errorMessages) { errorMessages = new List <string>(); var emailMessage = rockMessage as RockEmailMessage; if (emailMessage != null) { // Common Merge Field var mergeFields = Lava.LavaHelper.GetCommonMergeFields(null, rockMessage.CurrentPerson); foreach (var mergeField in rockMessage.AdditionalMergeFields) { mergeFields.AddOrReplace(mergeField.Key, mergeField.Value); } string fromAddress = emailMessage.FromEmail; string fromName = emailMessage.FromName; // Resolve any possible merge fields in the from address fromAddress = fromAddress.ResolveMergeFields(mergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands); fromName = fromName.ResolveMergeFields(mergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands); // From - if none is set, use the one in the Organization's GlobalAttributes. var globalAttributes = GlobalAttributesCache.Get(); if (string.IsNullOrWhiteSpace(fromAddress)) { fromAddress = globalAttributes.GetValue("OrganizationEmail"); } if (string.IsNullOrWhiteSpace(fromName)) { fromName = globalAttributes.GetValue("OrganizationName"); } if (fromAddress.IsNullOrWhiteSpace()) { errorMessages.Add("A From address was not provided and no Organization email address is configured."); return(false); } MailMessage message = new MailMessage(); // Reply To try { if (emailMessage.ReplyToEmail.IsNotNullOrWhiteSpace()) { // Resolve any possible merge fields in the replyTo address message.ReplyToList.Add(new MailAddress(emailMessage.ReplyToEmail.ResolveMergeFields(mergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands))); } } catch { } message.IsBodyHtml = true; message.Priority = MailPriority.Normal; using (var smtpClient = GetSmtpClient()) { foreach (var recipientData in rockMessage.GetRecipientData()) { try { foreach (var mergeField in mergeFields) { recipientData.MergeFields.AddOrIgnore(mergeField.Key, mergeField.Value); } message.To.Clear(); message.CC.Clear(); message.Bcc.Clear(); message.Headers.Clear(); // Set From/To and check safe sender message.From = new MailAddress(fromAddress, fromName); message.To.Add(new MailAddress( recipientData.To.ResolveMergeFields(recipientData.MergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands), recipientData.Name.ResolveMergeFields(recipientData.MergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands))); CheckSafeSender(message, globalAttributes); // cc foreach (string cc in emailMessage.CCEmails.Where(e => e != "")) { // Resolve any possible merge fields in the cc address string ccRecipient = cc.ResolveMergeFields(recipientData.MergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands); message.CC.Add(new MailAddress(ccRecipient)); } // bcc foreach (string bcc in emailMessage.BCCEmails.Where(e => e != "")) { // Resolve any possible merge fields in the cc address string bccRecipient = bcc.ResolveMergeFields(recipientData.MergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands); message.Bcc.Add(new MailAddress(bccRecipient)); } // Subject string subject = ResolveText(emailMessage.Subject, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands, recipientData.MergeFields, emailMessage.AppRoot, emailMessage.ThemeRoot); // Body string body = ResolveText(emailMessage.Message, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands, recipientData.MergeFields, emailMessage.AppRoot, emailMessage.ThemeRoot); body = Regex.Replace(body, @"\[\[\s*UnsubscribeOption\s*\]\]", string.Empty); message.Subject = subject.Left(998); message.Body = body; var metaData = new Dictionary <string, string>(emailMessage.MessageMetaData); // If a communication is going to get created, create a guid for tracking the opens/clicks Guid?recipientGuid = null; if (emailMessage.CreateCommunicationRecord) { recipientGuid = Guid.NewGuid(); metaData.Add("communication_recipient_guid", recipientGuid.Value.ToString()); } using (var rockContext = new RockContext()) { // Recreate the attachments message.Attachments.Clear(); if (emailMessage.Attachments.Any()) { var binaryFileService = new BinaryFileService(rockContext); foreach (var binaryFileId in emailMessage.Attachments.Where(a => a != null).Select(a => a.Id)) { var attachment = binaryFileService.Get(binaryFileId); if (attachment != null) { message.Attachments.Add(new Attachment(attachment.ContentStream, attachment.FileName)); } } } AddAdditionalHeaders(message, metaData); smtpClient.Send(message); } if (emailMessage.CreateCommunicationRecord) { var transaction = new SaveCommunicationTransaction(recipientData.To, emailMessage.FromName, emailMessage.FromName, subject, body); transaction.RecipientGuid = recipientGuid; RockQueue.TransactionQueue.Enqueue(transaction); } } catch (Exception ex) { errorMessages.Add(ex.Message); ExceptionLogService.LogException(ex); } } } } return(!errorMessages.Any()); }
/// <summary> /// Sends the specified communication. /// </summary>Medi /// <param name="communication">The communication.</param> /// <param name="mediumEntityTypeId">The medium entity type identifier.</param> /// <param name="mediumAttributes">The medium attributes.</param> public override void Send(Rock.Model.Communication communication, int mediumEntityTypeId, Dictionary <string, string> mediumAttributes) { using (var communicationRockContext = new RockContext()) { // Requery the Communication communication = new CommunicationService(communicationRockContext) .Queryable() .Include(a => a.CreatedByPersonAlias.Person) .Include(a => a.CommunicationTemplate) .FirstOrDefault(c => c.Id == communication.Id); bool hasPendingRecipients; if (communication != null && communication.Status == Model.CommunicationStatus.Approved && (!communication.FutureSendDateTime.HasValue || communication.FutureSendDateTime.Value.CompareTo(RockDateTime.Now) <= 0)) { var qryRecipients = new CommunicationRecipientService(communicationRockContext).Queryable(); hasPendingRecipients = qryRecipients .Where(r => r.CommunicationId == communication.Id && r.Status == Model.CommunicationRecipientStatus.Pending && r.MediumEntityTypeId.HasValue && r.MediumEntityTypeId.Value == mediumEntityTypeId) .Any(); } else { hasPendingRecipients = false; } if (!hasPendingRecipients) { return; } var currentPerson = communication.CreatedByPersonAlias?.Person; var globalAttributes = GlobalAttributesCache.Get(); string publicAppRoot = globalAttributes.GetValue("PublicApplicationRoot").EnsureTrailingForwardslash(); var mergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields(null, currentPerson); var cssInliningEnabled = communication.CommunicationTemplate?.CssInliningEnabled ?? false; string fromAddress = communication.FromEmail; string fromName = communication.FromName; // Resolve any possible merge fields in the from address fromAddress = fromAddress.ResolveMergeFields(mergeFields, currentPerson, communication.EnabledLavaCommands); fromName = fromName.ResolveMergeFields(mergeFields, currentPerson, communication.EnabledLavaCommands); // From - if none is set, use the one in the Organization's GlobalAttributes. if (string.IsNullOrWhiteSpace(fromAddress)) { fromAddress = globalAttributes.GetValue("OrganizationEmail"); } if (string.IsNullOrWhiteSpace(fromName)) { fromName = globalAttributes.GetValue("OrganizationName"); } MailMessage message = new MailMessage(); // Reply To try { string replyTo = communication.ReplyToEmail; if (!string.IsNullOrWhiteSpace(replyTo)) { // Resolve any possible merge fields in the replyTo address message.ReplyToList.Add(new MailAddress(replyTo.ResolveMergeFields(mergeFields, currentPerson))); } } catch { } message.IsBodyHtml = true; message.Priority = MailPriority.Normal; using (var smtpClient = GetSmtpClient()) { var personEntityTypeId = EntityTypeCache.Get("Rock.Model.Person").Id; var communicationEntityTypeId = EntityTypeCache.Get("Rock.Model.Communication").Id; var communicationCategoryGuid = Rock.SystemGuid.Category.HISTORY_PERSON_COMMUNICATIONS.AsGuid(); bool recipientFound = true; while (recipientFound) { // make a new rockContext per recipient var recipientRockContext = new RockContext(); var recipient = Rock.Model.Communication.GetNextPending(communication.Id, mediumEntityTypeId, recipientRockContext); if (recipient != null) { if (ValidRecipient(recipient, communication.IsBulkCommunication)) { try { message.To.Clear(); message.CC.Clear(); message.Bcc.Clear(); message.Headers.Clear(); message.AlternateViews.Clear(); // Set From/To and check safe sender message.From = new MailAddress(fromAddress, fromName); message.To.Add(new MailAddress(recipient.PersonAlias.Person.Email, recipient.PersonAlias.Person.FullName)); CheckSafeSender(message, globalAttributes); // Create merge field dictionary var mergeObjects = recipient.CommunicationMergeValues(mergeFields); // CC string cc = communication.CCEmails; if (!string.IsNullOrWhiteSpace(cc)) { // Resolve any possible merge fields in the cc address cc = cc.ResolveMergeFields(mergeObjects, currentPerson); foreach (string ccRecipient in cc.SplitDelimitedValues()) { message.CC.Add(new MailAddress(ccRecipient)); } } // BCC string bcc = communication.BCCEmails; if (!string.IsNullOrWhiteSpace(bcc)) { bcc = bcc.ResolveMergeFields(mergeObjects, currentPerson); foreach (string bccRecipient in bcc.SplitDelimitedValues()) { // Resolve any possible merge fields in the bcc address message.Bcc.Add(new MailAddress(bccRecipient)); } } // Subject message.Subject = ResolveText(communication.Subject, currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); // Plain text if (mediumAttributes.ContainsKey("DefaultPlainText")) { string plainText = ResolveText(mediumAttributes["DefaultPlainText"], currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); if (!string.IsNullOrWhiteSpace(plainText)) { AlternateView plainTextView = AlternateView.CreateAlternateViewFromString(plainText, new System.Net.Mime.ContentType(MediaTypeNames.Text.Plain)); message.AlternateViews.Add(plainTextView); } } // Add Html view // Get the unsubscribe content and add a merge field for it string htmlBody = communication.Message; if (communication.IsBulkCommunication && mediumAttributes.ContainsKey("UnsubscribeHTML")) { string unsubscribeHtml = ResolveText(mediumAttributes["UnsubscribeHTML"], currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); mergeObjects.AddOrReplace("UnsubscribeOption", unsubscribeHtml); htmlBody = ResolveText(htmlBody, currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); // Resolve special syntax needed if option was included in global attribute if (Regex.IsMatch(htmlBody, @"\[\[\s*UnsubscribeOption\s*\]\]")) { htmlBody = Regex.Replace(htmlBody, @"\[\[\s*UnsubscribeOption\s*\]\]", unsubscribeHtml); } // Add the unsubscribe option at end if it wasn't included in content if (!htmlBody.Contains(unsubscribeHtml)) { htmlBody += unsubscribeHtml; } } else { htmlBody = ResolveText(htmlBody, currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); htmlBody = Regex.Replace(htmlBody, @"\[\[\s*UnsubscribeOption\s*\]\]", string.Empty); } if (!string.IsNullOrWhiteSpace(htmlBody)) { if (cssInliningEnabled) { // move styles inline to help it be compatible with more email clients htmlBody = htmlBody.ConvertHtmlStylesToInlineAttributes(); } // add the main Html content to the email AlternateView htmlView = AlternateView.CreateAlternateViewFromString(htmlBody, new System.Net.Mime.ContentType(MediaTypeNames.Text.Html)); message.AlternateViews.Add(htmlView); } // Add any additional headers that specific SMTP provider needs var metaData = new Dictionary <string, string>(); metaData.Add("communication_recipient_guid", recipient.Guid.ToString()); AddAdditionalHeaders(message, metaData); // Recreate the attachments message.Attachments.Clear(); foreach (var binaryFile in communication.GetAttachments(CommunicationType.Email).Select(a => a.BinaryFile)) { message.Attachments.Add(new Attachment(binaryFile.ContentStream, binaryFile.FileName)); } smtpClient.Send(message); recipient.Status = CommunicationRecipientStatus.Delivered; string statusNote = StatusNote; if (!string.IsNullOrWhiteSpace(statusNote)) { recipient.StatusNote = statusNote; } recipient.TransportEntityTypeName = this.GetType().FullName; try { var historyChangeList = new History.HistoryChangeList(); historyChangeList.AddChange( History.HistoryVerb.Sent, History.HistoryChangeType.Record, $"Communication") .SetRelatedData(message.From.DisplayName, communicationEntityTypeId, communication.Id) .SetCaption(message.Subject); HistoryService.SaveChanges(recipientRockContext, typeof(Rock.Model.Person), communicationCategoryGuid, recipient.PersonAlias.PersonId, historyChangeList, false, communication.SenderPersonAliasId); } catch (Exception ex) { ExceptionLogService.LogException(ex, null); } } catch (Exception ex) { ExceptionLogService.LogException(ex); recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "Exception: " + ex.Messages().AsDelimited(" => "); } } recipientRockContext.SaveChanges(); } else { recipientFound = false; } } } } }
/// <summary> /// Sends the specified communication. /// </summary> /// <param name="communication">The communication.</param> /// <param name="mediumEntityTypeId">The medium entity type identifier.</param> /// <param name="mediumAttributes">The medium attributes.</param> /// <exception cref="System.NotImplementedException"></exception> public override void Send(Model.Communication communication, int mediumEntityTypeId, Dictionary <string, string> mediumAttributes) { using (var communicationRockContext = new RockContext()) { // Requery the Communication communication = new CommunicationService(communicationRockContext) .Queryable("CreatedByPersonAlias.Person") .FirstOrDefault(c => c.Id == communication.Id); bool hasPendingRecipients; if (communication != null && communication.Status == Model.CommunicationStatus.Approved && (!communication.FutureSendDateTime.HasValue || communication.FutureSendDateTime.Value.CompareTo(RockDateTime.Now) <= 0)) { var qryRecipients = new CommunicationRecipientService(communicationRockContext).Queryable(); hasPendingRecipients = qryRecipients .Where(r => r.CommunicationId == communication.Id && r.Status == Model.CommunicationRecipientStatus.Pending && r.MediumEntityTypeId.HasValue && r.MediumEntityTypeId.Value == mediumEntityTypeId) .Any(); } else { hasPendingRecipients = false; } if (hasPendingRecipients) { var currentPerson = communication.CreatedByPersonAlias?.Person; var globalAttributes = GlobalAttributesCache.Get(); string publicAppRoot = globalAttributes.GetValue("PublicApplicationRoot").EnsureTrailingForwardslash(); var mergeFields = Lava.LavaHelper.GetCommonMergeFields(null, currentPerson); string serverKey = GetAttributeValue("ServerKey"); var sender = new Sender(serverKey); var personEntityTypeId = EntityTypeCache.Get("Rock.Model.Person").Id; var communicationEntityTypeId = EntityTypeCache.Get("Rock.Model.Communication").Id; var communicationCategoryId = CategoryCache.Get(Rock.SystemGuid.Category.HISTORY_PERSON_COMMUNICATIONS.AsGuid(), communicationRockContext).Id; bool recipientFound = true; while (recipientFound) { // make a new rockContext per recipient var recipientRockContext = new RockContext(); var recipient = Model.Communication.GetNextPending(communication.Id, mediumEntityTypeId, recipientRockContext); if (recipient != null) { if (ValidRecipient(recipient, communication.IsBulkCommunication)) { try { int personAlias = recipient.PersonAliasId; var service = new PersonalDeviceService(recipientRockContext); List <string> devices = service.Queryable() .Where(p => p.PersonAliasId.HasValue && p.PersonAliasId.Value == personAlias && p.NotificationsEnabled) .Select(p => p.DeviceRegistrationId) .ToList(); if (devices != null) { // Create merge field dictionary var mergeObjects = recipient.CommunicationMergeValues(mergeFields); var message = ResolveText(communication.PushMessage, currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); var title = ResolveText(communication.PushTitle, currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); var sound = ResolveText(communication.PushSound, currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); var notification = new Message { RegistrationIds = devices.Distinct().ToList(), Notification = new FCM.Net.Notification { Title = title, Body = message, Sound = sound, } }; ResponseContent response = Utility.AsyncHelpers.RunSync(() => sender.SendAsync(notification)); bool failed = response.MessageResponse.Failure == devices.Count; var status = failed ? CommunicationRecipientStatus.Failed : CommunicationRecipientStatus.Delivered; if (failed) { recipient.StatusNote = "Firebase failed to notify devices"; } else { recipient.SendDateTime = RockDateTime.Now; } recipient.Status = status; recipient.TransportEntityTypeName = this.GetType().FullName; recipient.UniqueMessageId = response.MessageResponse.MulticastId; try { var historyService = new HistoryService(recipientRockContext); historyService.Add(new History { CreatedByPersonAliasId = communication.SenderPersonAliasId, EntityTypeId = personEntityTypeId, CategoryId = communicationCategoryId, EntityId = recipient.PersonAlias.PersonId, Verb = History.HistoryVerb.Sent.ConvertToString().ToUpper(), ChangeType = History.HistoryChangeType.Record.ToString(), ValueName = "Push Notification", Caption = message.Truncate(200), RelatedEntityTypeId = communicationEntityTypeId, RelatedEntityId = communication.Id }); } catch (Exception ex) { ExceptionLogService.LogException(ex, null); } } else { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "No Personal Devices with Messaging Enabled"; } } catch (Exception ex) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "Firebase Exception: " + ex.Message; } } recipientRockContext.SaveChanges(); } else { recipientFound = false; } } } } }
/// <summary> /// Gets the payments that have been processed for any scheduled transactions /// </summary> /// <param name="gateway">The gateway.</param> /// <param name="startDate">The start date.</param> /// <param name="endDate">The end date.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public override List <Payment> GetPayments(FinancialGateway gateway, DateTime startDate, DateTime endDate, out string errorMessage) { var today = RockDateTime.Now; var lookupContext = new RockContext(); var accountLookup = new FinancialAccountService(lookupContext); var transactionLookup = new FinancialTransactionService(lookupContext); var donationUrl = GetBaseUrl(gateway, "donations", out errorMessage); var supporterUrl = GetBaseUrl(gateway, "sponsorship_supporters", out errorMessage); var categoryUrl = GetBaseUrl(gateway, "donation_categories", out errorMessage); var projectsUrl = GetBaseUrl(gateway, "projects", out errorMessage); if (donationUrl.IsNullOrWhiteSpace() || supporterUrl.IsNullOrWhiteSpace()) { // errorMessage already set return(null); } var authenticator = GetAuthenticator(gateway, out errorMessage); if (authenticator == null) { // errorMessage already set return(null); } var reachAccountMaps = DefinedTypeCache.Get(GetAttributeValue(gateway, "AccountMap")).DefinedValues; var connectionStatus = DefinedValueCache.Get(GetAttributeValue(gateway, "PersonStatus")); var reachSourceType = DefinedValueCache.Get(GetAttributeValue(gateway, "SourceType")); var defaultAccount = accountLookup.Get(GetAttributeValue(gateway, "DefaultAccount").AsGuid()); var updatePrimaryEmail = GetAttributeValue(gateway, "UpdatePrimaryEmail").AsBoolean(); if (connectionStatus == null || reachAccountMaps == null || reachSourceType == null || defaultAccount == null) { errorMessage = "The Reach Account Map, Person Status, Source Type, or Default Account is not configured correctly in gateway settings."; return(null); } var currentPage = 1; var queryHasResults = true; var skippedTransactionCount = 0; var errorMessages = new List <string>(); var newTransactions = new List <FinancialTransaction>(); var categoryResult = Api.PostRequest(categoryUrl, authenticator, null, out errorMessage); var categories = JsonConvert.DeserializeObject <List <Reporting.Category> >(categoryResult.ToStringSafe()); var projectResult = Api.PostRequest(projectsUrl, authenticator, null, out errorMessage); var projects = JsonConvert.DeserializeObject <List <Reporting.Project> >(projectResult.ToStringSafe()); if (categories == null) { // errorMessage already set return(null); } while (queryHasResults) { var parameters = new Dictionary <string, string> { { "from_date", startDate.ToString("yyyy-MM-dd") }, { "to_date", endDate.ToString("yyyy-MM-dd") }, { "per_page", "50" }, { "page", currentPage.ToString() } }; // to_date doesn't have a timestamp, so it includes transactions posted after the cutoff var donationResult = Api.PostRequest(donationUrl, authenticator, parameters, out errorMessage); var donations = JsonConvert.DeserializeObject <List <Donation> >(donationResult.ToStringSafe()); if (donations != null && donations.Any() && errorMessage.IsNullOrWhiteSpace()) { // only process completed transactions with confirmation codes and within the date range foreach (var donation in donations.Where(d => d.updated_at >= startDate && d.updated_at < endDate && d.status.Equals("complete") && d.confirmation.IsNotNullOrWhiteSpace())) { var transaction = transactionLookup.Queryable() .FirstOrDefault(t => t.FinancialGatewayId.HasValue && t.FinancialGatewayId.Value == gateway.Id && t.TransactionCode == donation.confirmation); if (transaction == null) { // find or create this person asynchronously for performance var personAlias = Api.FindPersonAsync(lookupContext, donation, connectionStatus.Id, updatePrimaryEmail); var reachAccountName = string.Empty; var donationItem = donation.line_items.FirstOrDefault(); if (donationItem != null && donationItem.referral_type.Equals("DonationOption", StringComparison.InvariantCultureIgnoreCase)) { // one-time gift, should match a known category var category = categories.FirstOrDefault(c => c.id == donationItem.referral_id); if (category != null) { reachAccountName = category.title.Trim(); } } else if (donationItem != null && donationItem.referral_type.Equals("Project", StringComparison.InvariantCultureIgnoreCase)) { // one-time gift, should match a known project var project = projects.FirstOrDefault(c => c.id == donationItem.referral_id); if (project != null) { reachAccountName = string.Format("PROJECT {0}", project.title.Trim()); } } else { // recurring gift, lookup the sponsor info var referralId = donation.referral_id ?? donationItem.referral_id; var supporterResults = Api.PostRequest(string.Format("{0}/{1}", supporterUrl, referralId), authenticator, null, out errorMessage); var supporter = JsonConvert.DeserializeObject <Supporter>(supporterResults.ToStringSafe()); if (supporter != null) { var place = supporter.sponsorship?.place?.title; var sponsorshipType = supporter.sponsorship?.sponsorship_type_title; var shareType = supporter.share_type_id; string shareTypeName; switch (shareType) { case "668": shareTypeName = "Primary"; break; case "669": shareTypeName = "Secondary"; break; default: shareTypeName = string.Empty; break; } reachAccountName = string.Format("{0} {1} {2}", place, sponsorshipType, shareTypeName).Trim(); } } int?rockAccountId = defaultAccount.Id; var accountMapping = reachAccountMaps.FirstOrDefault(v => v.Value.Equals(reachAccountName, StringComparison.CurrentCultureIgnoreCase)); if (accountMapping != null) { var accountGuid = accountMapping.GetAttributeValue("RockAccount").AsGuidOrNull(); if (accountGuid.HasValue) { rockAccountId = accountLookup.Get(( Guid )accountGuid).Id; } } // verify person alias was found or created personAlias.Wait(); if (!personAlias.Result.HasValue) { var infoMessage = string.Format("{0} Reach import skipped {1} {2}'s donation {3} for {4} because their record could not be found or created", endDate.ToString("d"), donation.first_name, donation.last_name, donation.confirmation, reachAccountName); ExceptionLogService.LogException(new Exception(infoMessage), null); continue; } // create the transaction var summary = string.Format("Reach Donation for {0} from {1} using {2} on {3} ({4})", reachAccountName, donation.name, donation.payment_method, donation.updated_at, donation.token); transaction = new FinancialTransaction { TransactionDateTime = donation.updated_at, ProcessedDateTime = donation.updated_at, TransactionCode = donation.confirmation, Summary = summary, SourceTypeValueId = reachSourceType.Id, TransactionTypeValueId = contributionTypeId, Guid = Guid.NewGuid(), CreatedDateTime = today, ModifiedDateTime = today, AuthorizedPersonAliasId = personAlias.Result.Value, FinancialGatewayId = gateway.Id, ForeignId = donation.id, FinancialPaymentDetail = new FinancialPaymentDetail(), TransactionDetails = new List <FinancialTransactionDetail> { new FinancialTransactionDetail { AccountId = (int)rockAccountId, Amount = (decimal)donation.amount, Summary = summary, Guid = Guid.NewGuid(), CreatedDateTime = today, ModifiedDateTime = today } } }; newTransactions.Add(transaction); } else if (transaction != null) { skippedTransactionCount++; } } } else { queryHasResults = false; } currentPage++; } if (skippedTransactionCount > 0) { ExceptionLogService.LogException(new Exception(string.Format("{0} Reach import skipped downloading {1} transactions because they already exist", endDate.ToString("d"), skippedTransactionCount)), null); } if (newTransactions.Any()) { using (var rockContext = new RockContext()) { // create batch and add transactions var batchPrefix = GetAttributeValue(gateway, "BatchPrefix"); var batchDate = newTransactions.GroupBy(t => t.TransactionDateTime.Value.Date).OrderByDescending(t => t.Count()) .Select(g => g.Key).FirstOrDefault(); var batch = new FinancialBatchService(rockContext).GetByNameAndDate(string.Format("{0} {1}", batchPrefix, batchDate.ToString("d")), endDate, gateway.GetBatchTimeOffset()); batch.BatchStartDateTime = batchDate; batch.BatchEndDateTime = endDate; batch.Note = string.Format("{0} transactions downloaded starting {1} to {2}", batchPrefix, startDate, endDate); batch.ControlAmount += newTransactions.Select(t => t.TotalAmount).Sum(); var currentChanges = 0; foreach (var transaction in newTransactions) { // save in large batches so it doesn't overload context batch.Transactions.Add(transaction); if (currentChanges++ > 100) { rockContext.SaveChanges(disablePrePostProcessing: true); currentChanges = 0; } } // by default Rock associates with the current person rockContext.SaveChanges(disablePrePostProcessing: true); } } if (errorMessages.Any()) { errorMessage = string.Join("<br>", errorMessages); } return(new List <Payment>()); }
/// <summary> /// Binds the grid. /// </summary> private void BindGrid() { var rockContext = new RockContext(); var communications = new CommunicationService(rockContext) .Queryable().AsNoTracking() .Where(c => c.Status != CommunicationStatus.Transient); string subject = tbSubject.Text; if (!string.IsNullOrWhiteSpace(subject)) { communications = communications.Where(c => (string.IsNullOrEmpty(c.Subject) && c.Name.Contains(subject)) || c.Subject.Contains(subject)); } var communicationType = ddlType.SelectedValueAsEnumOrNull <CommunicationType>(); if (communicationType != null) { communications = communications.Where(c => c.CommunicationType == communicationType); } string status = ddlStatus.SelectedValue; if (!string.IsNullOrWhiteSpace(status)) { var communicationStatus = ( CommunicationStatus )System.Enum.Parse(typeof(CommunicationStatus), status); communications = communications.Where(c => c.Status == communicationStatus); } if (canApprove) { if (ppSender.PersonId.HasValue) { communications = communications .Where(c => c.SenderPersonAlias != null && c.SenderPersonAlias.PersonId == ppSender.PersonId.Value); } } else { // If can't approve, only show current person's communications communications = communications .Where(c => c.SenderPersonAlias != null && c.SenderPersonAlias.PersonId == CurrentPersonId); } if (drpCreatedDates.LowerValue.HasValue) { communications = communications.Where(a => a.CreatedDateTime.HasValue && a.CreatedDateTime.Value >= drpCreatedDates.LowerValue.Value); } if (drpCreatedDates.UpperValue.HasValue) { DateTime upperDate = drpCreatedDates.UpperValue.Value.Date.AddDays(1); communications = communications.Where(a => a.CreatedDateTime.HasValue && a.CreatedDateTime.Value < upperDate); } if (drpSentDates.LowerValue.HasValue) { communications = communications.Where(a => (a.SendDateTime ?? a.FutureSendDateTime) >= drpSentDates.LowerValue.Value); } if (drpSentDates.UpperValue.HasValue) { DateTime upperDate = drpSentDates.UpperValue.Value.Date.AddDays(1); communications = communications.Where(a => (a.SendDateTime ?? a.FutureSendDateTime) < upperDate); } string content = tbContent.Text; if (!string.IsNullOrWhiteSpace(content)) { communications = communications.Where(c => c.Message.Contains(content) || c.SMSMessage.Contains(content) || c.PushMessage.Contains(content)); } var recipients = new CommunicationRecipientService(rockContext).Queryable(); // We want to limit to only communications that they are authorized to view, but if there are a large number of communications, that could be very slow. // So, since communication security is based on CommunicationTemplate, take a shortcut and just limit based on authorized communication templates var authorizedCommunicationTemplateIds = new CommunicationTemplateService(rockContext).Queryable() .Where(a => communications.Where(x => x.CommunicationTemplateId.HasValue).Select(x => x.CommunicationTemplateId.Value).Distinct().Contains(a.Id)) .ToList().Where(a => a.IsAuthorized(Rock.Security.Authorization.VIEW, this.CurrentPerson)).Select(a => a.Id).ToList(); var queryable = communications.Where(a => a.CommunicationTemplateId == null || authorizedCommunicationTemplateIds.Contains(a.CommunicationTemplateId.Value)) .Select(c => new CommunicationItem { Id = c.Id, CommunicationType = c.CommunicationType, // Subject = string.IsNullOrEmpty( c.Subject ) ? c.Name : c.Subject, Subject = string.IsNullOrEmpty(c.Name) ? (string.IsNullOrEmpty(c.Subject) ? c.PushTitle : c.Subject) : c.Name, CreatedDateTime = c.CreatedDateTime, SendDateTime = c.SendDateTime ?? c.FutureSendDateTime, SendDateTimePrefix = c.SendDateTime == null && c.FutureSendDateTime != null ? "<span class='label label-info'>Future</span> " : "", Sender = c.SenderPersonAlias != null ? c.SenderPersonAlias.Person : null, ReviewedDateTime = c.ReviewedDateTime, Reviewer = c.ReviewerPersonAlias != null ? c.ReviewerPersonAlias.Person : null, Status = c.Status, Recipients = recipients.Where(r => r.CommunicationId == c.Id).Count(), PendingRecipients = recipients.Where(r => r.CommunicationId == c.Id && r.Status == CommunicationRecipientStatus.Pending).Count(), CancelledRecipients = recipients.Where(r => r.CommunicationId == c.Id && r.Status == CommunicationRecipientStatus.Cancelled).Count(), FailedRecipients = recipients.Where(r => r.CommunicationId == c.Id && r.Status == CommunicationRecipientStatus.Failed).Count(), DeliveredRecipients = recipients.Where(r => r.CommunicationId == c.Id && (r.Status == CommunicationRecipientStatus.Delivered || r.Status == CommunicationRecipientStatus.Opened)).Count(), OpenedRecipients = recipients.Where(r => r.CommunicationId == c.Id && r.Status == CommunicationRecipientStatus.Opened).Count() }); var sortProperty = gCommunication.SortProperty; if (sortProperty != null) { queryable = queryable.Sort(sortProperty); } else { queryable = queryable.OrderByDescending(c => c.SendDateTime); } gCommunication.EntityTypeId = EntityTypeCache.Get <Rock.Model.Communication>().Id; nbBindError.Text = string.Empty; try { gCommunication.SetLinqDataSource(queryable); gCommunication.DataBind(); } catch (Exception e) { ExceptionLogService.LogException(e); Exception sqlException = e; while (sqlException != null && !(sqlException is System.Data.SqlClient.SqlException)) { sqlException = sqlException.InnerException; } nbBindError.Text = string.Format("<p>An error occurred trying to retrieve the communication history. Please try adjusting your filter settings and try again.</p><p>Error: {0}</p>", sqlException != null ? sqlException.Message : e.Message); gCommunication.DataSource = new List <object>(); gCommunication.DataBind(); } }
/// <summary> /// Enables processing of HTTP Web requests by a custom <see langword="HttpHandler" /> that implements the <see cref="T:System.Web.IHttpHandler" /> interface. /// </summary> /// <param name="context">An <see cref="T:System.Web.HttpContext" /> object that provides references to the intrinsic server objects (for example, <see langword="Request" />, <see langword="Response" />, <see langword="Session" />, and <see langword="Server" />) used to service HTTP requests.</param> public void ProcessRequest(HttpContext context) { // see https://sandbox.gotnpgateway.com/docs/webhooks/#core-webhook-response-format for example payload HttpRequest request = context.Request; var response = context.Response; response.ContentType = "text/plain"; // Signature https://sandbox.gotnpgateway.com/docs/webhooks/#security // see signature @ https://sandbox.fluidpay.com/merchant/settings/webhooks/search var postedSignature = request.Headers["Signature"]; string postedData = string.Empty; using (var reader = new StreamReader(request.InputStream)) { postedData = reader.ReadToEnd(); } var cardSyncWebhookResponse = postedData.FromJsonOrNull <CardSyncWebhookResponse>(); if (cardSyncWebhookResponse == null) { response.StatusCode = ( int )HttpStatusCode.BadRequest; response.StatusDescription = "Unable to determine response format."; return; } var paymentMethodData = cardSyncWebhookResponse.PaymentMethodData; if (paymentMethodData == null) { response.StatusCode = ( int )HttpStatusCode.BadRequest; response.StatusDescription = "Unable to determine payment method 'data'."; return; } var rockContext = new RockContext(); FinancialPersonSavedAccountService financialPersonSavedAccountService = new FinancialPersonSavedAccountService(rockContext); var financialPersonSavedAccountQuery = financialPersonSavedAccountService.Queryable() .Where(a => a.GatewayPersonIdentifier == paymentMethodData.RecordId || a.FinancialPaymentDetail.GatewayPersonIdentifier == paymentMethodData.RecordId); var savedAccounts = financialPersonSavedAccountQuery.Include(a => a.FinancialPaymentDetail).Include(a => a.FinancialGateway).ToList(); // There probably is only one saved account for the GatewayPersonIdentifier, but just in case, we'll loop thru. foreach (var savedAccount in savedAccounts) { var financialGateway = savedAccount.FinancialGateway; var myWellGateway = savedAccount.FinancialGateway?.GetGatewayComponent() as MyWellGateway; if (myWellGateway == null) { ExceptionLogService.LogException(new MyWellGatewayException($"Unable to determine Gateway for CardSync GatewayPersonIdentifier: {paymentMethodData.RecordId} and FinancialGatewayId: {savedAccount.FinancialGatewayId}")); response.StatusCode = ( int )HttpStatusCode.BadRequest; response.StatusDescription = $"Unable to find matching financial gateway record for recordId: {paymentMethodData.RecordId}"; return; } financialGateway.LoadAttributes(); var validSignature = myWellGateway.VerifySignature(financialGateway, postedSignature, postedData); if (!validSignature) { ExceptionLogService.LogException(new MyWellGatewayException($"Invalid WebHook signature included in header. (PostedData for RecordId: {paymentMethodData.RecordId} and FinancialGatewayId: {savedAccount.FinancialGatewayId})")); response.StatusCode = ( int )HttpStatusCode.Forbidden; response.StatusDescription = "Invalid WebHook signature included in header"; return; } var financialPaymentDetail = savedAccount.FinancialPaymentDetail; if (financialPaymentDetail == null) { // shouldn't happen continue; } if (paymentMethodData.ExpirationDate.IsNotNullOrWhiteSpace() && paymentMethodData.ExpirationDate.Length == 5) { // now that we validated this is a 5 char string (MM/YY), extract MM and YY as integers financialPaymentDetail.ExpirationMonth = paymentMethodData.ExpirationDate.Substring(0, 2).AsIntegerOrNull(); financialPaymentDetail.ExpirationYear = paymentMethodData.ExpirationDate.Substring(3, 2).AsIntegerOrNull(); // ToDo: See if they send us the CreditCardType (visa, mastercard) // ?? financialPaymentDetail.CreditCardTypeValueId } financialPaymentDetail.AccountNumberMasked = paymentMethodData.MaskedNumber; rockContext.SaveChanges(); } // NOTE: If it takes us more than 5 seconds to respond, they'll retry. // Otherwise, if we respond with a 200, they'll assume we processed it. // See https://sandbox.gotnpgateway.com/docs/webhooks/#acknowledge-and-retry for additional details response.StatusCode = ( int )HttpStatusCode.OK; }
public HttpResponseMessage Family(string param) { try { var session = HttpContext.Current.Session; var localDeviceConfigCookie = HttpContext.Current.Request.Cookies[CheckInCookieKey.LocalDeviceConfig].Value; var localDevice = localDeviceConfigCookie.FromJsonOrNull <LocalDeviceConfiguration>(); var currentKioskId = localDevice.CurrentKioskId.Value; Guid blockGuid = ( Guid )session["BlockGuid"]; var currentCheckInState = new CheckInState(currentKioskId, localDevice.CurrentCheckinTypeId, localDevice.CurrentGroupTypeIds); currentCheckInState.CheckIn.UserEnteredSearch = true; currentCheckInState.CheckIn.ConfirmSingleFamily = true; currentCheckInState.CheckIn.SearchType = DefinedValueCache.Get(Rock.SystemGuid.DefinedValue.CHECKIN_SEARCH_TYPE_PHONE_NUMBER); currentCheckInState.CheckIn.SearchValue = param; var rockContext = new Rock.Data.RockContext(); var block = BlockCache.Get(blockGuid); string workflowActivity = block.GetAttributeValue("WorkflowActivity"); Guid? workflowGuid = block.GetAttributeValue("WorkflowType").AsGuidOrNull(); List <string> errors; var workflowService = new WorkflowService(rockContext); var workflowType = WorkflowTypeCache.Get(workflowGuid.Value); var CurrentWorkflow = Rock.Model.Workflow.Activate(workflowType, currentCheckInState.Kiosk.Device.Name, rockContext); var activityType = workflowType.ActivityTypes.Where(a => a.Name == workflowActivity).FirstOrDefault(); if (activityType != null) { WorkflowActivity.Activate(activityType, CurrentWorkflow, rockContext); if (workflowService.Process(CurrentWorkflow, currentCheckInState, out errors)) { if (errors.Any()) { var innerException = new Exception(string.Join(" -- ", errors)); ExceptionLogService.LogException(new Exception("Process Mobile Checkin failed initial workflow. See inner exception for details.", innerException)); } // Keep workflow active for continued processing CurrentWorkflow.CompletedDateTime = null; SaveState(session, currentCheckInState); List <CheckInFamily> families = currentCheckInState.CheckIn.Families; families = families.OrderBy(f => f.Caption).ToList(); return(ControllerContext.Request.CreateResponse(HttpStatusCode.OK, families)); } else { if (errors.Any()) { var innerException = new Exception(string.Join(" -- ", errors)); ExceptionLogService.LogException(new Exception("Process Mobile Checkin failed initial workflow. See inner exception for details.", innerException)); } else { ExceptionLogService.LogException(new Exception("Process Mobile Checkin failed initial workflow. See inner exception for details.")); } } } else { return(ControllerContext.Request.CreateResponse(HttpStatusCode.InternalServerError, string.Format("Workflow type does not have a '{0}' activity type", workflowActivity))); } return(ControllerContext.Request.CreateResponse(HttpStatusCode.InternalServerError, String.Join("\n", errors))); } catch (Exception ex) { ExceptionLogService.LogException(ex, HttpContext.Current); return(ControllerContext.Request.CreateResponse(HttpStatusCode.Forbidden, "Forbidden")); } }
/// <summary> /// Creates the cache type mapping and analyzer. /// </summary> /// <param name="documentType">Type of the document.</param> /// <param name="deleteIfExists">if set to <c>true</c> [delete if exists].</param> public override void CreateIndex(Type documentType, bool deleteIfExists = true) { try { var indexName = documentType.Name.ToLower(); object instance = Activator.CreateInstance(documentType); // Check if index already exists. If it exists, no need to create it again if (_indexes.ContainsKey(indexName)) { return; } Index index = new Index(); // make sure this is an index document if (instance is IndexModelBase) { Dictionary <string, TypeMappingProperties> typeMapping = new Dictionary <string, TypeMappingProperties>(); Dictionary <string, Analyzer> fieldAnalyzers = new Dictionary <string, Analyzer>(); // get properties from the model and add them to the index (hint: attributes will be added dynamically as the documents are loaded) var modelProperties = documentType.GetProperties(); foreach (var property in modelProperties) { var indexAttribute = property.GetCustomAttributes(typeof(RockIndexField), false); if (indexAttribute.Length > 0) { var attribute = ( RockIndexField )indexAttribute[0]; var propertyName = property.Name; // rewrite non-string index option (would be nice if they made the enums match up...) if (attribute.Type != IndexFieldType.String) { if (attribute.Index == IndexType.NotIndexed) { continue; } } var typeMappingProperty = new TypeMappingProperties(); typeMappingProperty.Name = propertyName; typeMappingProperty.Boost = ( float )attribute.Boost; switch (attribute.Type) { case IndexFieldType.Boolean: case IndexFieldType.Date: case IndexFieldType.Number: { typeMappingProperty.IndexType = IndexType.NotAnalyzed; typeMappingProperty.Analyzer = string.Empty; break; } default: { typeMappingProperty.IndexType = attribute.Index; if (!string.IsNullOrWhiteSpace(attribute.Analyzer)) { typeMappingProperty.Analyzer = attribute.Analyzer; } break; } } typeMapping.Add(propertyName, typeMappingProperty); if (typeMappingProperty.Analyzer?.ToLowerInvariant() == "snowball") { fieldAnalyzers[typeMappingProperty.Name] = Analyzer.NewAnonymous(createComponents: (fieldName, reader) => { var tokenizer = new StandardTokenizer(_matchVersion, reader); var sbpff = new SnowballPorterFilterFactory(new Dictionary <string, string>() { { "language", "English" } }); sbpff.Inform(new ClasspathResourceLoader(documentType)); TokenStream result = sbpff.Create(new StandardTokenizer(_matchVersion, reader)); return(new TokenStreamComponents(tokenizer, result)); // https://github.com/apache/lucenenet/blob/master/src/Lucene.Net.Analysis.Common/Analysis/Snowball/SnowballAnalyzer.cs }); } else if (typeMappingProperty.Analyzer?.ToLowerInvariant() == "whitespace") { fieldAnalyzers[propertyName] = Analyzer.NewAnonymous(createComponents: (fieldName, reader) => { var tokenizer = new WhitespaceTokenizer(_matchVersion, reader); TokenStream result = new StandardFilter(_matchVersion, tokenizer); return(new TokenStreamComponents(tokenizer, result)); }); } } } index.MappingProperties = typeMapping; index.FieldAnalyzers = fieldAnalyzers; _indexes[indexName] = index; } } catch (Exception ex) { HttpContext context2 = HttpContext.Current; ExceptionLogService.LogException(ex, context2); } }
/// <summary> /// Shows the error message. /// </summary> /// <param name="ex">The ex.</param> /// <param name="friendlyMessage">The friendly message.</param> private void ShowErrorMessage(Exception ex, string friendlyMessage) { nbErrorMessage.Text = friendlyMessage; nbErrorMessage.Visible = true; ExceptionLogService.LogException(ex, this.Context); }
/// <summary> /// Update the open attempt record if there are changes. /// </summary> /// <param name="openAttempt"></param> /// <param name="streakTypeAchievementTypeCache">The streak type achievement type cache.</param> /// <param name="streak">The streak.</param> protected override void UpdateOpenAttempt(StreakAchievementAttempt openAttempt, StreakTypeAchievementTypeCache streakTypeAchievementTypeCache, Streak streak) { var rockContext = new RockContext(); var streakTypeService = new StreakTypeService(rockContext); var streakTypeCache = streakTypeAchievementTypeCache.StreakTypeCache; // Validate the attribute values var numberToAccumulate = GetAttributeValue(streakTypeAchievementTypeCache, AttributeKey.NumberToAccumulate).AsInteger(); if (numberToAccumulate <= 0) { ExceptionLogService.LogException($"AccumulativeAchievement.UpdateOpenAttempt cannot process because the NumberToAccumulate attribute is less than 1"); return; } var attributeTimespanDays = GetAttributeValue(streakTypeAchievementTypeCache, AttributeKey.TimespanInDays).AsIntegerOrNull(); if (attributeTimespanDays.HasValue && attributeTimespanDays.Value <= 0) { ExceptionLogService.LogException($"AccumulativeAchievement.UpdateOpenAttempt cannot process because the TimespanInDays attribute is less than 1"); return; } // Calculate the date range where the open attempt can be validly fulfilled var attributeMaxDate = GetAttributeValue(streakTypeAchievementTypeCache, AttributeKey.EndDateTime).AsDateTime(); var minDate = openAttempt.AchievementAttemptStartDateTime; var maxDate = CalculateMaxDateForAchievementAttempt(minDate, attributeMaxDate); // Track the accumulation var accumulation = new ComputedStreak(minDate) { EndDate = minDate }; // Define what happens for each bit in the date range bool iterationAction(int currentUnit, DateTime currentDate, bool hasOccurrence, bool hasEngagement, bool hasExclusion) { // If there is an engagement, then increment the accumulation if (hasOccurrence && hasEngagement) { accumulation.Count++; accumulation.EndDate = currentDate; // Check for a fulfilled attempt if (accumulation.Count >= numberToAccumulate) { var progress = CalculateProgress(accumulation.Count, numberToAccumulate); openAttempt.AchievementAttemptEndDateTime = accumulation.EndDate; openAttempt.Progress = progress; openAttempt.IsClosed = !streakTypeAchievementTypeCache.AllowOverAchievement; openAttempt.IsSuccessful = progress >= 1m; } } // If there is a timespan and this accumulation is too old, then the attempt is closed if (attributeTimespanDays.HasValue) { var inclusiveAge = (currentDate - accumulation.StartDate).Days + 1; if (inclusiveAge >= attributeTimespanDays.Value) { var progress = CalculateProgress(accumulation.Count, numberToAccumulate); openAttempt.AchievementAttemptEndDateTime = accumulation.EndDate; openAttempt.Progress = progress; openAttempt.IsClosed = true; openAttempt.IsSuccessful = progress >= 1m; } } return(openAttempt.IsClosed); } // Iterate through the streak date for the date range specified streakTypeService.IterateStreakMap(streakTypeCache, streak.PersonAliasId, minDate, maxDate, iterationAction, out var errorMessage); if (!errorMessage.IsNullOrWhiteSpace()) { ExceptionLogService.LogException($"AccumulativeAchievement.UpdateOpenAttempt got an error calling StreakTypeService.IterateStreakMap: {errorMessage}"); return; } // If the attempt wasn't closed in the iteration, then it will remain open if (!openAttempt.IsClosed) { var progress = CalculateProgress(accumulation.Count, numberToAccumulate); openAttempt.Progress = progress; openAttempt.IsSuccessful = progress >= 1m; } }
/// <summary> /// Executes the specified context. /// </summary> /// <param name="context">The context.</param> public void Execute(IJobExecutionContext context) { JobDataMap dataMap = context.JobDetail.JobDataMap; StringBuilder results = new StringBuilder(); int updatedDatasetCount = 0; int updatedDatasetTotalCount; var errors = new List <string>(); List <Exception> exceptions = new List <Exception>(); using (var rockContext = new RockContext()) { var currentDateTime = RockDateTime.Now; var persistedDatasetQuery = new PersistedDatasetService(rockContext).Queryable(); updatedDatasetTotalCount = persistedDatasetQuery.Count(); // exclude datasets that are no longer active persistedDatasetQuery = persistedDatasetQuery.Where(a => a.IsActive && (a.ExpireDateTime == null || a.ExpireDateTime > currentDateTime)); // exclude datasets that are already up-to-date based on the Refresh Interval and LastRefreshTime persistedDatasetQuery = persistedDatasetQuery .Where(a => a.LastRefreshDateTime == null || (System.Data.Entity.SqlServer.SqlFunctions.DateAdd("mi", a.RefreshIntervalMinutes.Value, a.LastRefreshDateTime.Value) < currentDateTime)); var expiredPersistedDatasetsList = persistedDatasetQuery.ToList(); foreach (var persistedDataset in expiredPersistedDatasetsList) { var name = persistedDataset.Name; try { context.UpdateLastStatusMessage($"Updating {persistedDataset.Name}"); persistedDataset.UpdateResultData(); rockContext.SaveChanges(); updatedDatasetCount++; } catch (Exception ex) { // Capture and log the exception because we're not going to fail this job // unless all the data views fail. var errorMessage = $"An error occurred while trying to update persisted dataset '{name}' so it was skipped. Error: {ex.Message}"; errors.Add(errorMessage); var ex2 = new Exception(errorMessage, ex); exceptions.Add(ex2); ExceptionLogService.LogException(ex2, null); continue; } } } int notUpdatedCount = updatedDatasetTotalCount - updatedDatasetCount; // Format the result message results.AppendLine($"Updated {updatedDatasetCount} {"persisted dataset".PluralizeIf( updatedDatasetCount != 1 )}."); if (notUpdatedCount > 0) { results.AppendLine($"Skipped {notUpdatedCount} {"persisted dataset".PluralizeIf( updatedDatasetCount != 1 )} that are already up-to-date or inactive."); } context.Result = results.ToString(); if (errors.Any()) { StringBuilder sb = new StringBuilder(); sb.AppendLine(); sb.Append("Errors: "); errors.ForEach(e => { sb.AppendLine(); sb.Append(e); }); string errorMessage = sb.ToString(); context.Result += errorMessage; // We're not going to throw an aggregate exception unless there were no successes. // Otherwise the status message does not show any of the success messages in // the last status message. if (updatedDatasetCount == 0) { throw new AggregateException(exceptions.ToArray()); } } }
/// <summary> /// Job that will sync groups. /// /// Called by the <see cref="IScheduler" /> when a /// <see cref="ITrigger" /> fires that is associated with /// the <see cref="IJob" />. /// </summary> public virtual void Execute(IJobExecutionContext context) { // Get the job setting(s) JobDataMap dataMap = context.JobDetail.JobDataMap; bool requirePasswordReset = dataMap.GetBoolean("RequirePasswordReset"); // Counters for displaying results int groupsSynced = 0; int groupsChanged = 0; try { // get groups set to sync var groupIdsThatSync = new List <int>(); using (var rockContext = new RockContext()) { groupIdsThatSync = new GroupService(rockContext) .Queryable().AsNoTracking() .Where(g => g.SyncDataViewId != null) .Select(a => a.Id) .ToList(); } foreach (var syncGroupId in groupIdsThatSync) { bool hasGroupChanged = false; // Use a fresh rockContext per group so that ChangeTracker doesn't get bogged down using (var rockContext = new RockContext()) { // increase the timeout just in case the dataview source is slow rockContext.Database.CommandTimeout = 180; // Get the Group var syncGroup = new GroupService(rockContext) .Queryable().AsNoTracking() .FirstOrDefault(t => t.Id == syncGroupId); // Ensure that the group's Sync Data View is a person dataview if (syncGroup.SyncDataView.EntityTypeId == EntityTypeCache.Read(typeof(Person)).Id) { List <string> errorMessages = new List <string>(); // Get the person id's from the dataview (source) var personService = new PersonService(rockContext); var parameterExpression = personService.ParameterExpression; var whereExpression = syncGroup.SyncDataView.GetExpression(personService, parameterExpression, out errorMessages); var sourcePersonIds = new PersonService(rockContext) .Get(parameterExpression, whereExpression) .Select(q => q.Id) .ToList(); // Get the person id's in the group (target) var targetPersonIds = new GroupMemberService(rockContext) .Queryable().AsNoTracking() .Where(gm => gm.GroupId == syncGroup.Id) .Select(gm => gm.PersonId) .ToList(); // Delete people from the group that are no longer in the dataview foreach (var personId in targetPersonIds.Where(t => !sourcePersonIds.Contains(t))) { // Use a new context to limit the amount of change-tracking required using (var groupMemberContext = new RockContext()) { // Delete any group members with the person id var groupMemberService = new GroupMemberService(groupMemberContext); foreach (var groupMember in groupMemberService .Queryable() .Where(m => m.GroupId == syncGroupId && m.PersonId == personId) .ToList()) { groupMemberService.Delete(groupMember); } groupMemberContext.SaveChanges(); // If the Group has an exit email, and person has an email address, send them the exit email if (syncGroup.ExitSystemEmail != null) { var person = new PersonService(groupMemberContext).Get(personId); if (person.Email.IsNotNullOrWhitespace()) { // Send the exit email var mergeFields = new Dictionary <string, object>(); mergeFields.Add("Group", syncGroup); mergeFields.Add("Person", person); var emailMessage = new RockEmailMessage(syncGroup.ExitSystemEmail); emailMessage.AddRecipient(new RecipientData(person.Email, mergeFields)); emailMessage.Send(); } } } hasGroupChanged = true; } // Add people to the group that are in the dataview and not currently in the group int groupRoleId = syncGroup.GroupType.DefaultGroupRoleId ?? syncGroup.GroupType.Roles.FirstOrDefault().Id; foreach (var personId in sourcePersonIds.Where(s => !targetPersonIds.Contains(s))) { // Use a new context to limit the amount of change-tracking required using (var groupMemberContext = new RockContext()) { // Add new person to the group var groupMemberService = new GroupMemberService(groupMemberContext); var newGroupMember = new GroupMember { Id = 0 }; newGroupMember.PersonId = personId; newGroupMember.GroupId = syncGroup.Id; newGroupMember.GroupMemberStatus = GroupMemberStatus.Active; newGroupMember.GroupRoleId = groupRoleId; groupMemberService.Add(newGroupMember); groupMemberContext.SaveChanges(); // If the Group has a welcome email, and person has an email address, send them the welcome email and possibly create a login if (syncGroup.WelcomeSystemEmail != null) { var person = new PersonService(groupMemberContext).Get(personId); if (person.Email.IsNotNullOrWhitespace()) { // If the group is configured to add a user account for anyone added to the group, and person does not yet have an // account, add one for them. string newPassword = string.Empty; bool createLogin = syncGroup.AddUserAccountsDuringSync ?? false; if (createLogin && !person.Users.Any()) { newPassword = System.Web.Security.Membership.GeneratePassword(9, 1); string username = Rock.Security.Authentication.Database.GenerateUsername(person.NickName, person.LastName); UserLogin login = UserLoginService.Create( groupMemberContext, person, AuthenticationServiceType.Internal, EntityTypeCache.Read(Rock.SystemGuid.EntityType.AUTHENTICATION_DATABASE.AsGuid()).Id, username, newPassword, true, requirePasswordReset); } // Send the welcome email var mergeFields = new Dictionary <string, object>(); mergeFields.Add("Group", syncGroup); mergeFields.Add("Person", person); mergeFields.Add("NewPassword", newPassword); mergeFields.Add("CreateLogin", createLogin); var emailMessage = new RockEmailMessage(syncGroup.WelcomeSystemEmail); emailMessage.AddRecipient(new RecipientData(person.Email, mergeFields)); emailMessage.Send(); } } } hasGroupChanged = true; } // Increment Groups Changed Counter (if people were deleted or added to the group) if (hasGroupChanged) { groupsChanged++; } // Increment the Groups Synced Counter groupsSynced++; // If the group changed, and it was a security group, flush the security for the group if (hasGroupChanged && (syncGroup.IsSecurityRole || syncGroup.GroupType.Guid.Equals(Rock.SystemGuid.GroupType.GROUPTYPE_SECURITY_ROLE.AsGuid()))) { Rock.Security.Role.Flush(syncGroup.Id); } } } } // Format the result message var resultMessage = string.Empty; if (groupsSynced == 0) { resultMessage = "No groups to sync"; } else if (groupsSynced == 1) { resultMessage = "1 group was sync'ed"; } else { resultMessage = string.Format("{0} groups were sync'ed", groupsSynced); } resultMessage += string.Format(" and {0} groups were changed", groupsChanged); context.Result = resultMessage; } catch (System.Exception ex) { HttpContext context2 = HttpContext.Current; ExceptionLogService.LogException(ex, context2); throw; } }
/// <summary> /// Provides an end method for an asynchronous process. /// </summary> /// <param name="result">An IAsyncResult that contains information about the status of the process.</param> public void EndProcessRequest(IAsyncResult result) { // restore the context from the asyncResult.AsyncState HttpContext context = (HttpContext)result.AsyncState; try { context.Response.Clear(); bool isBinaryFile = (bool)context.Items["isBinaryFile"]; if (isBinaryFile) { var rockContext = new RockContext(); bool requiresViewSecurity = false; BinaryFile binaryFile = new BinaryFileService(rockContext).EndGet(result, context, out requiresViewSecurity); if (binaryFile != null) { //// if the binaryFile's BinaryFileType requires security, check security //// note: we put a RequiresViewSecurity flag on BinaryFileType because checking security for every file would be slow (~40ms+ per request) if (requiresViewSecurity) { var currentUser = new UserLoginService(rockContext).GetByUserName(UserLogin.GetCurrentUserName()); Person currentPerson = currentUser != null ? currentUser.Person : null; binaryFile.BinaryFileType = binaryFile.BinaryFileType ?? new BinaryFileTypeService(rockContext).Get(binaryFile.BinaryFileTypeId.Value); if (!binaryFile.IsAuthorized(Authorization.VIEW, currentPerson)) { SendNotAuthorized(context); return; } } SendFile(context, binaryFile.ContentStream, binaryFile.MimeType, binaryFile.FileName, binaryFile.Guid.ToString("N")); return; } } else { Stream fileContents = (Stream)context.Items["fileContents"]; string physicalContentFileName = context.Items["physicalContentFileName"] as string; if (fileContents != null) { string mimeType = System.Web.MimeMapping.GetMimeMapping(physicalContentFileName); string fileName = Path.GetFileName(physicalContentFileName); SendFile(context, fileContents, mimeType, fileName, ""); return; } } context.Response.StatusCode = 404; context.Response.StatusDescription = "Unable to find the requested file."; } catch (Exception ex) { ExceptionLogService.LogException(ex, context); try { context.Response.StatusCode = 500; context.Response.StatusDescription = ex.Message; context.Response.Flush(); context.ApplicationInstance.CompleteRequest(); } catch (Exception ex2) { ExceptionLogService.LogException(ex2, context); } } }
/// <summary> /// Executes the specified context. /// </summary> /// <param name="context">The context.</param> public virtual void Execute(IJobExecutionContext context) { JobDataMap dataMap = context.JobDetail.JobDataMap; var expireDays = dataMap.GetString("ExpireDate").AsIntegerOrNull() ?? 1; int remindersSent = 0; var errors = new List <string>(); using (var rockContext = new RockContext()) { DateTime now = RockDateTime.Now; DateTime expireDate = now.AddDays(expireDays * -1); foreach (var instance in new RegistrationInstanceService(rockContext) .Queryable("RegistrationTemplate,Registrations") .Where(i => i.IsActive && i.RegistrationTemplate.IsActive && i.RegistrationTemplate.ReminderEmailTemplate != string.Empty && !i.ReminderSent && i.SendReminderDateTime.HasValue && i.SendReminderDateTime <= now && i.SendReminderDateTime >= expireDate) .ToList()) { var template = instance.RegistrationTemplate; foreach (var registration in instance.Registrations .Where(r => !r.IsTemporary && r.ConfirmationEmail != null && r.ConfirmationEmail != string.Empty)) { try { var mergeFields = new Dictionary <string, object>(); mergeFields.Add("RegistrationInstance", registration.RegistrationInstance); mergeFields.Add("Registration", registration); var emailMessage = new RockEmailMessage(); emailMessage.AdditionalMergeFields = mergeFields; emailMessage.AddRecipient(registration.GetConfirmationRecipient(mergeFields)); emailMessage.FromEmail = template.ReminderFromEmail; emailMessage.FromName = template.ReminderFromName; emailMessage.Subject = template.ReminderSubject; emailMessage.Message = template.ReminderEmailTemplate; var emailErrors = new List <string>(); emailMessage.Send(out emailErrors); errors.AddRange(emailErrors); } catch (Exception exception) { ExceptionLogService.LogException(exception); continue; } } // Even if an error occurs, still mark as completed to prevent _everyone_ being sent the reminder multiple times due to a single failing address instance.SendReminderDateTime = now; instance.ReminderSent = true; remindersSent++; rockContext.SaveChanges(); } if (remindersSent == 0) { context.Result = "No reminders to send"; } else if (remindersSent == 1) { context.Result = "1 reminder was sent"; } else { context.Result = string.Format("{0} reminders were sent", remindersSent); } if (errors.Any()) { StringBuilder sb = new StringBuilder(); sb.AppendLine(); sb.Append(string.Format("{0} Errors: ", errors.Count())); errors.ForEach(e => { sb.AppendLine(); sb.Append(e); }); string errorMessage = sb.ToString(); context.Result += errorMessage; var exception = new Exception(errorMessage); HttpContext context2 = HttpContext.Current; ExceptionLogService.LogException(exception, context2); throw exception; } } }
/// <summary> /// Updates the Created/Modified data for any model being created or modified /// </summary> /// <param name="dbContext">The database context.</param> /// <param name="personAlias">The person alias.</param> /// <param name="enableAuditing">if set to <c>true</c> [enable auditing].</param> /// <returns></returns> protected virtual List <ContextItem> RockPreSave(DbContext dbContext, PersonAlias personAlias, bool enableAuditing = false) { int?personAliasId = null; if (personAlias != null) { personAliasId = personAlias.Id; } var preSavedEntities = new HashSet <Guid>(); // First loop through all models calling the PreSaveChanges foreach (var entry in dbContext.ChangeTracker.Entries() .Where(c => c.Entity is IEntity && (c.State == EntityState.Added || c.State == EntityState.Modified || c.State == EntityState.Deleted))) { if (entry.Entity is IModel) { var model = entry.Entity as IModel; model.PreSaveChanges(this, entry, entry.State); if (!preSavedEntities.Contains(model.Guid)) { preSavedEntities.Add(model.Guid); } } } // Then loop again, as new models may have been added by PreSaveChanges events var updatedItems = new List <ContextItem>(); foreach (var entry in dbContext.ChangeTracker.Entries() .Where(c => c.Entity is IEntity && (c.State == EntityState.Added || c.State == EntityState.Modified || c.State == EntityState.Deleted))) { // Cast entry as IEntity var entity = entry.Entity as IEntity; // Get the context item to track audits var contextItem = new ContextItem(entity, entry, enableAuditing); // If entity was added or modified, update the Created/Modified fields if (entry.State == EntityState.Added || entry.State == EntityState.Modified) { // instead of passing "true" the trigger model and UI would support a // on-value-changed checkbox (or perhaps it should be the default/only behavior) // and its value would be passed in to the onValueChange if (!TriggerWorkflows(contextItem, WorkflowTriggerType.PreSave, personAlias)) { return(null); } if (entry.Entity is IModel) { var model = entry.Entity as IModel; if (!preSavedEntities.Contains(model.Guid)) { model.PreSaveChanges(this, entry); } // Update Guid/Created/Modified person and times if (entry.State == EntityState.Added) { if (!model.CreatedDateTime.HasValue) { model.CreatedDateTime = RockDateTime.Now; } if (!model.CreatedByPersonAliasId.HasValue) { model.CreatedByPersonAliasId = personAliasId; } if (model.Guid == Guid.Empty) { model.Guid = Guid.NewGuid(); } model.ModifiedDateTime = RockDateTime.Now; if (!model.ModifiedAuditValuesAlreadyUpdated || model.ModifiedByPersonAliasId == null) { model.ModifiedByPersonAliasId = personAliasId; } } else if (entry.State == EntityState.Modified) { model.ModifiedDateTime = RockDateTime.Now; if (!model.ModifiedAuditValuesAlreadyUpdated || model.ModifiedByPersonAliasId == null) { model.ModifiedByPersonAliasId = personAliasId; } } } } else if (entry.State == EntityState.Deleted) { if (!TriggerWorkflows(contextItem, WorkflowTriggerType.PreDelete, personAlias)) { return(null); } } if (enableAuditing) { try { GetAuditDetails(dbContext, contextItem, personAliasId); } catch (SystemException ex) { contextItem.Audit = null; System.Diagnostics.Debug.WriteLine($"Exception when getting Audit details for {contextItem?.GetType().Name} - {ex}"); ExceptionLogService.LogException(ex, null); } } updatedItems.Add(contextItem); } return(updatedItems); }
/// <summary> /// Tries the delete. /// </summary> /// <remarks> /// This method will always log any exception that occurs even if the exception isn't thrown. /// </remarks> /// <param name="filePath">The file path.</param> /// <param name="shouldBubbleException">if set to <c>true</c> [should bubble exception].</param> public static void TryDelete(string filePath, bool shouldBubbleException) { TryDelete(filePath, (ex) => ExceptionLogService.LogException(ex), shouldBubbleException); }
/// <summary> /// Creates audit logs and/or triggers workflows for items that were changed /// </summary> /// <param name="updatedItems">The updated items.</param> /// <param name="personAlias">The person alias.</param> /// <param name="enableAuditing">if set to <c>true</c> [enable auditing].</param> protected virtual void RockPostSave(List <ContextItem> updatedItems, PersonAlias personAlias, bool enableAuditing = false) { if (enableAuditing) { try { var audits = updatedItems.Where(a => a.Audit != null).Select(i => i.Audit).ToList(); if (audits.Any(a => a.Details.Any())) { var transaction = new Rock.Transactions.AuditTransaction(); transaction.Audits = audits.Where(a => a.Details.Any() == true).ToList(); Rock.Transactions.RockQueue.TransactionQueue.Enqueue(transaction); } } catch (SystemException ex) { ExceptionLogService.LogException(ex, null); } } List <ITransaction> indexTransactions = new List <ITransaction>(); foreach (var item in updatedItems) { if (item.State == EntityState.Detached || item.State == EntityState.Deleted) { TriggerWorkflows(item, WorkflowTriggerType.PostDelete, personAlias); } else { if (item.PreSaveState == EntityState.Added) { TriggerWorkflows(item, WorkflowTriggerType.PostAdd, personAlias); } TriggerWorkflows(item, WorkflowTriggerType.ImmediatePostSave, personAlias); TriggerWorkflows(item, WorkflowTriggerType.PostSave, personAlias); } if (item.Entity is IModel) { var model = item.Entity as IModel; model.PostSaveChanges(this); } // check if this entity should be passed on for indexing if (item.Entity is IRockIndexable) { if (item.State == EntityState.Detached || item.State == EntityState.Deleted) { DeleteIndexEntityTransaction transaction = new DeleteIndexEntityTransaction(); transaction.EntityTypeId = item.Entity.TypeId; transaction.EntityId = item.Entity.Id; indexTransactions.Add(transaction); } else { IndexEntityTransaction transaction = new IndexEntityTransaction(); transaction.EntityTypeId = item.Entity.TypeId; transaction.EntityId = item.Entity.Id; indexTransactions.Add(transaction); } } if (item.Entity is ICacheable) { (item.Entity as ICacheable).UpdateCache(item.PreSaveState, this); } } // check if Indexing is enabled in another thread to avoid deadlock when Snapshot Isolation is turned off when the Index components upload/load attributes if (indexTransactions.Any()) { System.Threading.Tasks.Task.Run(() => { var indexingEnabled = IndexContainer.GetActiveComponent() == null ? false : true; if (indexingEnabled) { indexTransactions.ForEach(t => RockQueue.TransactionQueue.Enqueue(t)); } }); } }
/// <summary> /// Called by the <see cref="IScheduler"/> after a <see cref="IJobDetail"/> /// has been executed, and before the associated <see cref="Quartz.Spi.IOperableTrigger"/>'s /// <see cref="Quartz.Spi.IOperableTrigger.Triggered"/> method has been called. /// </summary> /// <param name="context"></param> /// <param name="jobException"></param> public void JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException) { RockJobResultSpecifier result; IRockJobResult resultInfo; Exception exceptionToLog = null; ServiceJob job = null; // If the Job threw an Exception, create a corresponding RockJobResult object and find the appropriate Exception to log. if (jobException != null) { exceptionToLog = jobException; resultInfo = new RockJobResult(); resultInfo.Result = RockJobResultSpecifier.Failed; // Unpack the Scheduler Exceptions to get the Exception thrown by the Task itself. while (exceptionToLog is SchedulerException && exceptionToLog.InnerException != null) { exceptionToLog = exceptionToLog.InnerException; } var summaryException = exceptionToLog; if (summaryException is AggregateException) { var aggregateException = (AggregateException)summaryException; if (aggregateException.InnerExceptions != null) { if (aggregateException.InnerExceptions.Count == 1) { // if it's an aggregate, but there is only one, convert it to a single exception summaryException = aggregateException.InnerExceptions[0]; } else { summaryException = aggregateException.Flatten(); } } } resultInfo.ResultDescription = summaryException.Message; var ex = summaryException.InnerException; string details = string.Empty;; while (ex != null) { details += "\n--> " + ex.Message; ex = ex.InnerException; } resultInfo.ResultDetails = details.Trim('\n'); } else { resultInfo = context.Result as IRockJobResult; // If the Job did not return a result object and did not throw an Exception, assume success. if (resultInfo == null) { resultInfo = new RockJobResult(); resultInfo.Result = RockJobResultSpecifier.Succeeded; } else { // If the Job returned a failure in the result object, create a corresponding Exception for logging purposes. if (resultInfo.Result.HasValue && resultInfo.Result.Value == RockJobResultSpecifier.Failed) { exceptionToLog = new Exception(resultInfo.ResultDescription); } } } // Update the Job with the most recent result. result = resultInfo.Result.GetValueOrDefault(RockJobResultSpecifier.Succeeded); // Retrieve the Job details. int jobId = Convert.ToInt16(context.JobDetail.Description); using (var rockContext = new RockContext()) { var jobService = new ServiceJobService(rockContext); job = jobService.Get(jobId); // set last run date job.LastRunDateTime = RockDateTime.Now; // set run time job.LastRunDurationSeconds = Convert.ToInt32(context.JobRunTime.TotalSeconds); // set the scheduler name job.LastRunSchedulerName = context.Scheduler.SchedulerName; switch (result) { case RockJobResultSpecifier.Succeeded: job.LastStatus = "Success"; job.LastSuccessfulRunDateTime = job.LastRunDateTime; break; case RockJobResultSpecifier.CompletedWithWarnings: job.LastStatus = "Warning"; job.LastSuccessfulRunDateTime = job.LastRunDateTime; break; case RockJobResultSpecifier.Failed: job.LastStatus = "Exception"; break; } job.LastStatusMessage = resultInfo.ResultDescription; if (!string.IsNullOrEmpty(resultInfo.ResultDetails)) { job.LastStatusMessage += "\n" + resultInfo.ResultDetails; } // Save changes to the Job. rockContext.SaveChanges(); } if (result == RockJobResultSpecifier.Failed) { // log the exception to the database ExceptionLogService.LogException(exceptionToLog, null); } this.ProcessNotificationMessage(context, job, resultInfo); }
/// <summary> /// Determines whether the entity matches the current and/or previous qualifier values. /// If /// </summary> /// <param name="item">The item.</param> /// <param name="properties">The properties.</param> /// <param name="trigger">The trigger.</param> /// <returns>true if matches; false otherwise</returns> private static bool IsQualifierMatch(ContextItem item, Dictionary <string, PropertyInfo> properties, WorkflowTrigger trigger) { bool match = false; try { var dbEntity = item.DbEntityEntry; // Now attempt to find a match taking into account the EntityTypeQualifierValue and/or EntityTypeQualifierValuePrevious if (properties.ContainsKey(trigger.EntityTypeQualifierColumn.ToLower())) { var propertyInfo = properties[trigger.EntityTypeQualifierColumn.ToLower()]; bool hasPrevious = !string.IsNullOrEmpty(trigger.EntityTypeQualifierValuePrevious); bool hasCurrent = !string.IsNullOrEmpty(trigger.EntityTypeQualifierValue); var currentProperty = propertyInfo.GetValue(item.Entity, null); var currentValue = currentProperty != null?currentProperty.ToString() : string.Empty; var previousValue = string.Empty; if (item.OriginalValues != null && item.OriginalValues.ContainsKey(propertyInfo.Name)) { previousValue = item.OriginalValues[propertyInfo.Name].ToStringSafe(); } else { var dbPropertyEntry = dbEntity.Property(propertyInfo.Name); if (dbPropertyEntry != null) { previousValue = item.PreSaveState == EntityState.Added ? string.Empty : dbPropertyEntry.OriginalValue.ToStringSafe(); } } if (trigger.WorkflowTriggerType == WorkflowTriggerType.PreDelete || trigger.WorkflowTriggerType == WorkflowTriggerType.PostDelete) { match = (previousValue == trigger.EntityTypeQualifierValue); } if (trigger.WorkflowTriggerType == WorkflowTriggerType.PostAdd) { match = (currentValue == trigger.EntityTypeQualifierValue); } if (trigger.WorkflowTriggerType == WorkflowTriggerType.ImmediatePostSave || trigger.WorkflowTriggerType == WorkflowTriggerType.PostSave || trigger.WorkflowTriggerType == WorkflowTriggerType.PreSave) { if (trigger.WorkflowTriggerValueChangeType == WorkflowTriggerValueChangeType.ValueEqual) { match = trigger.EntityTypeQualifierValue == currentValue; } else { if (hasCurrent && !hasPrevious) { // ...and previous cannot be the same as the current (must be a change) match = (currentValue == trigger.EntityTypeQualifierValue && currentValue != previousValue); } else if (!hasCurrent && hasPrevious) { // ...and previous cannot be the same as the current (must be a change) match = (previousValue == trigger.EntityTypeQualifierValuePrevious && previousValue != currentValue); } else if (hasCurrent && hasPrevious) { match = (currentValue == trigger.EntityTypeQualifierValue && previousValue == trigger.EntityTypeQualifierValuePrevious); } else if (!hasCurrent && !hasPrevious) { match = previousValue != currentValue; } } } } } catch (Exception ex) { ExceptionLogService.LogException(ex, null); } return(match); }
protected override void ExtractPackageFilesToProject(IPackage package) { List <IPackageFile> contentFiles = package.GetContentFiles().ToList(); Dictionary <string, string> transformedFiles = new Dictionary <string, string>(); string packageRestorePath = Path.Combine(Project.Root, "App_Data", "PackageRestore"); // go through each *.rock.xdt file and apply the transform first, foreach (var xdtFile in contentFiles.Where(f => f.Path.EndsWith(TRANSFORM_FILE_PREFIX, StringComparison.OrdinalIgnoreCase))) { using (Stream stream = xdtFile.GetStream()) { var fileName = xdtFile.EffectivePath; // write the transform file out to the PackageRestore/xdt folder var transformFilefullPath = Path.Combine(packageRestorePath, "xdt", fileName); Directory.CreateDirectory(Path.GetDirectoryName(transformFilefullPath)); using (var fileStream = File.Create(transformFilefullPath)) { stream.CopyTo(fileStream); } var sourceFile = fileName.Remove(fileName.Length - TRANSFORM_FILE_PREFIX.Length); var sourceFileFullPath = Path.Combine(Project.Root, sourceFile); var tempPathOfTransformedFile = Path.Combine(packageRestorePath, "xdt", sourceFile); // now transform the Rock file using the xdt file, but write it to the PackageRestore\xdt folder and we'll // move it into place after the update is finished. // If the transform fails, then we have to quit and inform the user. if (!ProcessXmlDocumentTransformation(transformFilefullPath, sourceFileFullPath, tempPathOfTransformedFile)) { throw new System.Xml.XmlException(sourceFile); } transformedFiles.Add(tempPathOfTransformedFile, sourceFile); } } // now let the package installation proceed as normal base.ExtractPackageFilesToProject(package); // lastly, move the transformed xml files into place MoveTransformedFiles(transformedFiles); try { try { Directory.Delete(packageRestorePath, recursive: true); } catch (IOException) { // try one more time System.Threading.Tasks.Task.Delay(10).Wait(); if (Directory.Exists(packageRestorePath)) { Directory.Delete(packageRestorePath, recursive: true); } } } catch (Exception ex) { ExceptionLogService.LogException(new Exception(string.Format("Note: Unable to delete the temporary package restore folder ({0}) after a successful update.", packageRestorePath), ex), System.Web.HttpContext.Current); } }
/// <summary> /// Job that will send scheduled group emails. /// /// Called by the <see cref="IScheduler" /> when a /// <see cref="ITrigger" /> fires that is associated with /// the <see cref="IJob" />. /// </summary> public virtual void Execute(IJobExecutionContext context) { var dataMap = context.JobDetail.JobDataMap; int?commandTimeout = dataMap.GetString("CommandTimeout").AsIntegerOrNull(); int?lastRunBuffer = dataMap.GetString("LastRunBuffer").AsIntegerOrNull(); var enabledLavaCommands = dataMap.GetString("EnabledLavaCommands"); var JobStartDateTime = RockDateTime.Now; var dateAttributes = new List <AttributeValue>(); var dAttributeMatrixItemAndGroupIds = new Dictionary <int, int>(); // Key: AttributeMatrixItemId Value: GroupId int communicationsSent = 0; var emailMediumType = EntityTypeCache.Get("Rock.Communication.Medium.Email"); var dateAttributeId = Rock.Web.Cache.AttributeCache.Get(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_SEND_DATE.AsGuid()).Id; var recurrenceAttributeId = Rock.Web.Cache.AttributeCache.Get(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_SEND_RECURRENCE.AsGuid()).Id; var fromEmailAttributeId = Rock.Web.Cache.AttributeCache.Get(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_FROM_EMAIL.AsGuid()).Id; var fromNameAttributeId = Rock.Web.Cache.AttributeCache.Get(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_FROM_NAME.AsGuid()).Id; var subjectAttributeId = Rock.Web.Cache.AttributeCache.Get(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_SUBJECT.AsGuid()).Id; var messageAttributeId = Rock.Web.Cache.AttributeCache.Get(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_MESSAGE.AsGuid()).Id; try { using (var rockContext = new RockContext()) { // get the last run date or yesterday DateTime?lastStartDateTime = null; // get job type id int jobId = context.JobDetail.Description.AsInteger(); // load job var job = new ServiceJobService(rockContext) .GetNoTracking(jobId); if (job != null && job.Guid != Rock.SystemGuid.ServiceJob.JOB_PULSE.AsGuid()) { lastStartDateTime = job.LastRunDateTime?.AddSeconds(0.0d - ( double )(job.LastRunDurationSeconds + lastRunBuffer)); } var beginDateTime = lastStartDateTime ?? JobStartDateTime.AddDays(-1); // get the date attributes dateAttributes = new AttributeValueService(rockContext) .Queryable().AsNoTracking() .Where(d => d.AttributeId == dateAttributeId && d.EntityId.HasValue && d.ValueAsDateTime >= beginDateTime && d.ValueAsDateTime <= JobStartDateTime) .ToList(); } foreach (var d in dateAttributes) { // Use a new context to limit the amount of change-tracking required using (var rockContext = new RockContext()) { var attributeMatrixId = new AttributeMatrixItemService(rockContext) .GetNoTracking(d.EntityId.Value) .AttributeMatrixId; var attributeMatrixGuid = new AttributeMatrixService(rockContext) .GetNoTracking(attributeMatrixId) .Guid .ToString(); var attributeValue = new AttributeValueService(rockContext) .Queryable().AsNoTracking() .FirstOrDefault(a => a.Value.Equals(attributeMatrixGuid, StringComparison.CurrentCultureIgnoreCase)); if (attributeValue != null && attributeValue.EntityId.HasValue) { dAttributeMatrixItemAndGroupIds.Add(d.EntityId.Value, attributeValue.EntityId.Value); } } } foreach (var attributeMatrixItemAndGroupId in dAttributeMatrixItemAndGroupIds) { // Use a new context to limit the amount of change-tracking required using (var rockContext = new RockContext()) { rockContext.Database.CommandTimeout = commandTimeout; var fromEmail = new AttributeValueService(rockContext) .GetByAttributeIdAndEntityId(fromEmailAttributeId, attributeMatrixItemAndGroupId.Key) .Value; var fromName = new AttributeValueService(rockContext) .GetByAttributeIdAndEntityId(fromNameAttributeId, attributeMatrixItemAndGroupId.Key) .Value; var subject = new AttributeValueService(rockContext) .GetByAttributeIdAndEntityId(subjectAttributeId, attributeMatrixItemAndGroupId.Key) .Value; var message = new AttributeValueService(rockContext) .GetByAttributeIdAndEntityId(messageAttributeId, attributeMatrixItemAndGroupId.Key) .Value; var attachments = new List <BinaryFile>(); var group = new GroupService(rockContext) .GetNoTracking(attributeMatrixItemAndGroupId.Value); if (!message.IsNullOrWhiteSpace() && emailMediumType != null) { var groupMembers = group.Members.Where(m => m.Person != null && m.Person.Email != null && m.Person.Email != string.Empty && m.GroupMemberStatus == GroupMemberStatus.Active); if (!groupMembers.Any()) { continue; } var communicationService = new CommunicationService(rockContext); var communication = new Rock.Model.Communication(); communication.Status = CommunicationStatus.Transient; communication.ReviewedDateTime = JobStartDateTime; communication.ReviewerPersonAliasId = group.ModifiedByPersonAliasId; communication.SenderPersonAliasId = group.ModifiedByPersonAliasId; communication.CreatedByPersonAliasId = group.ModifiedByPersonAliasId; communicationService.Add(communication); communication.EnabledLavaCommands = enabledLavaCommands; var personIdHash = new HashSet <int>(); foreach (var member in groupMembers) { if (!personIdHash.Contains(member.PersonId)) { personIdHash.Add(member.PersonId); var communicationRecipient = new CommunicationRecipient(); communicationRecipient.PersonAliasId = member.Person.PrimaryAliasId; communicationRecipient.AdditionalMergeValues = new Dictionary <string, object>(); communicationRecipient.AdditionalMergeValues.Add("GroupMember", member); //communicationRecipient.AdditionalMergeValues.Add( "Group", group ); communication.Recipients.Add(communicationRecipient); } } communication.IsBulkCommunication = false; communication.CommunicationType = CommunicationType.Email; communication.CommunicationTemplateId = null; foreach (var recipient in communication.Recipients) { recipient.MediumEntityTypeId = emailMediumType.Id; } communication.FromEmail = fromEmail; communication.FromName = fromName; communication.Subject = subject; communication.Message = message; communication.Status = CommunicationStatus.Approved; rockContext.SaveChanges(); Rock.Model.Communication.Send(communication); communicationsSent += personIdHash.Count; var recurrence = new AttributeValueService(rockContext) .GetByAttributeIdAndEntityId(recurrenceAttributeId, attributeMatrixItemAndGroupId.Key); if (recurrence != null && !string.IsNullOrWhiteSpace(recurrence.Value)) { var sendDate = new AttributeValueService(rockContext) .GetByAttributeIdAndEntityId(dateAttributeId, attributeMatrixItemAndGroupId.Key); switch (recurrence.Value) { case "1": sendDate.Value = sendDate.ValueAsDateTime.Value.AddDays(7).ToString(); break; case "2": sendDate.Value = sendDate.ValueAsDateTime.Value.AddDays(14).ToString(); break; case "3": sendDate.Value = sendDate.ValueAsDateTime.Value.AddMonths(1).ToString(); break; case "4": sendDate.Value = sendDate.ValueAsDateTime.Value.AddDays(1).ToString(); break; default: break; } rockContext.SaveChanges(); } } } } if (communicationsSent > 0) { context.Result = string.Format("Sent {0} {1}", communicationsSent, "communication".PluralizeIf(communicationsSent > 1)); } else { context.Result = "No communications to send"; } } catch (System.Exception ex) { HttpContext context2 = HttpContext.Current; ExceptionLogService.LogException(ex, context2); throw; } }
/// <summary> /// Job that will run quick SQL queries on a schedule. /// /// Called by the <see cref="IScheduler" /> when a /// <see cref="ITrigger" /> fires that is associated with /// the <see cref="IJob" />. /// </summary> public virtual void Execute(IJobExecutionContext context) { JobDataMap dataMap = context.JobDetail.JobDataMap; // get job parms bool runIntegrityCheck = dataMap.GetBoolean("RunIntegrityCheck"); bool runIndexRebuild = dataMap.GetBoolean("RunIndexRebuild"); bool runStatisticsUpdate = dataMap.GetBoolean("RunStatisticsUpdate"); int commandTimeout = dataMap.GetString("CommandTimeout").AsInteger(); StringBuilder resultsMessage = new StringBuilder(); bool integrityCheckPassed = false; bool integrityCheckIgnored = false; /* * DJL 2020-04-08 * For Microsoft Azure, disable the Integrity Check and Statistics Update tasks. * Refer: https://azure.microsoft.com/en-us/blog/data-integrity-in-azure-sql-database/ */ if (RockInstanceConfig.Database.Platform == RockInstanceDatabaseConfiguration.PlatformSpecifier.AzureSql) { runIntegrityCheck = false; runStatisticsUpdate = false; } // run integrity check if (runIntegrityCheck) { try { string alertEmail = dataMap.GetString("AlertEmail"); integrityCheckPassed = IntegrityCheck(commandTimeout, alertEmail, resultsMessage); } catch (Exception ex) { ExceptionLogService.LogException(ex, HttpContext.Current); ExceptionLogService.LogException(resultsMessage.ToString()); throw; } } else { integrityCheckIgnored = true; } if (integrityCheckPassed || integrityCheckIgnored) { // rebuild fragmented indexes if (runIndexRebuild) { try { RebuildFragmentedIndexes(dataMap, commandTimeout, resultsMessage); } catch (Exception ex) { ExceptionLogService.LogException(ex, HttpContext.Current); ExceptionLogService.LogException(resultsMessage.ToString()); throw; } } // update statistics if (runStatisticsUpdate) { try { UpdateStatistics(commandTimeout, resultsMessage); } catch (Exception ex) { ExceptionLogService.LogException(ex, HttpContext.Current); ExceptionLogService.LogException(resultsMessage.ToString()); throw; } } } context.Result = resultsMessage.ToString().TrimStart(','); }
/// <summary> /// Gets the parent page references. /// </summary> /// <returns></returns> public static List <PageReference> GetParentPageReferences(RockPage rockPage, PageCache currentPage, PageReference currentPageReference) { // Get previous page references in nav history var pageReferenceHistory = HttpContext.Current.Session["RockPageReferenceHistory"] as List <PageReference>; // Current page heirarchy references var pageReferences = new List <PageReference>(); if (currentPage != null) { var parentPage = currentPage.ParentPage; if (parentPage != null) { var currentParentPages = parentPage.GetPageHierarchy(); if (currentParentPages != null && currentParentPages.Count > 0) { currentParentPages.Reverse(); foreach (PageCache page in currentParentPages) { PageReference parentPageReference = null; if (pageReferenceHistory != null) { parentPageReference = pageReferenceHistory.Where(p => p.PageId == page.Id).FirstOrDefault(); } if (parentPageReference == null) { parentPageReference = new PageReference( ); parentPageReference.PageId = page.Id; parentPageReference.BreadCrumbs = new List <BreadCrumb>(); parentPageReference.QueryString = new NameValueCollection(); parentPageReference.Parameters = new Dictionary <string, string>(); string bcName = page.BreadCrumbText; if (bcName != string.Empty) { parentPageReference.BreadCrumbs.Add(new BreadCrumb(bcName, parentPageReference.BuildUrl())); } foreach (var block in page.Blocks.Where(b => b.BlockLocation == Model.BlockLocation.Page)) { try { System.Web.UI.Control control = rockPage.TemplateControl.LoadControl(block.BlockType.Path); if (control is RockBlock) { RockBlock rockBlock = control as RockBlock; rockBlock.SetBlock(page, block); rockBlock.GetBreadCrumbs(parentPageReference).ForEach(c => parentPageReference.BreadCrumbs.Add(c)); } control = null; } catch (Exception ex) { ExceptionLogService.LogException(ex, HttpContext.Current, currentPage.Id, currentPage.Layout.SiteId); } } } parentPageReference.BreadCrumbs.ForEach(c => c.Active = false); pageReferences.Add(parentPageReference); } } } } return(pageReferences); }