/// <summary> /// Gets the root matrix attribute value that ties the matrix values to a person or other entity. /// </summary> /// <returns></returns> private AttributeValue GetRootMatrixAttributeValue() { var rockContext = new RockContext(); var attributeMatrixService = new AttributeMatrixService(rockContext); var attributeValueService = new AttributeValueService(rockContext); var matrixGuidQuery = attributeMatrixService.Queryable().AsNoTracking().Where(am => am.AttributeMatrixItems.Any(ami => ami.Id == EntityId) ).Select(am => am.Guid.ToString()); var attributeValue = attributeValueService.Queryable().AsNoTracking().FirstOrDefault(av => matrixGuidQuery.Contains(av.Value) ); return(attributeValue); }
/// <summary> /// Gets the root matrix attribute value that ties the matrix values to a person or other entity. /// </summary> /// <returns></returns> private AttributeValue GetRootMatrixAttributeValue() { var rockContext = new RockContext(); var attributeMatrixService = new AttributeMatrixService(rockContext); var attributeService = new AttributeService(rockContext); var attributeValueService = new AttributeValueService(rockContext); var matrixGuidQuery = attributeMatrixService.Queryable().AsNoTracking().Where(am => am.AttributeMatrixItems.Any(ami => ami.Id == EntityId)) .Select(am => am.Guid.ToString()); var matrixFieldType = FieldTypeCache.Get(SystemGuid.FieldType.MATRIX); var attributeIdQuery = attributeService.Queryable().AsNoTracking().Where(a => a.FieldTypeId == matrixFieldType.Id) .Select(a => a.Id); var attributeValue = attributeValueService.Queryable().AsNoTracking().FirstOrDefault(av => attributeIdQuery.Contains(av.AttributeId) && matrixGuidQuery.Contains(av.Value)); return(attributeValue); }
/// <summary> /// This method is called in the /// <see cref="M:Rock.Data.Model`1.PreSaveChanges(Rock.Data.DbContext,System.Data.Entity.Infrastructure.DbEntityEntry,System.Data.Entity.EntityState)" /> /// method. Use it to populate <see cref="P:Rock.Data.Model`1.HistoryItems" /> if needed. /// These history items are queued to be written into the database post save (so that they /// are only written if the save actually occurs). /// </summary> /// <param name="dbContext">The database context.</param> /// <param name="entry">The entry.</param> /// <param name="state">The state.</param> protected override void BuildHistoryItems(Data.DbContext dbContext, DbEntityEntry entry, EntityState state) { /* * 12/18/2019 BJW * * We want to log the history of attribute values within a person matrix. Most of this logging occurs from * the attribute value model. However, when a matrix item (row in the table) is deleted, the pre-save event * of the attribute values is not called. Therefore, the delete event needs to be logged here. Additionally, * when the matrix item is added, the history is much cleaner when added here so that all the values of the * row are consolidated to one history item. Modified state is not possible to log here because the * matrix item is not actually modified when its attributes change. * * Task: https://app.asana.com/0/1120115219297347/1136643182208595/f */ if (state != EntityState.Deleted && state != EntityState.Added) { return; } var rockContext = new RockContext(); var matrixId = AttributeMatrixId != default ? AttributeMatrixId : entry.OriginalValues["AttributeMatrixId"].ToStringSafe().AsIntegerOrNull(); var matrix = AttributeMatrix; if (matrix == null && matrixId.HasValue) { var matrixService = new AttributeMatrixService(rockContext); matrix = matrixService.Queryable().AsNoTracking().FirstOrDefault(am => am.Id == matrixId); } if (matrix == null) { return; } // The root attribute matrix attribute value is linked to the matrix by the guid as the attribute value var matrixGuidString = matrix.Guid.ToString(); var personEntityTypeId = EntityTypeCache.Get(typeof(Person)).Id; var attributeValueService = new AttributeValueService(rockContext); var rootAttributeValue = attributeValueService.Queryable().AsNoTracking().FirstOrDefault(av => av.Value.Equals(matrixGuidString, System.StringComparison.OrdinalIgnoreCase) && av.Attribute.EntityTypeId == personEntityTypeId ); if (rootAttributeValue?.EntityId == null) { return; } var rootAttributeCache = AttributeCache.Get(rootAttributeValue.AttributeId); if (rootAttributeCache == null) { return; } // Evaluate the history changes var historyChangeList = new History.HistoryChangeList(); if (AttributeValues == null || !AttributeValues.Any()) { this.LoadAttributes(); } var isDelete = state == EntityState.Deleted; foreach (var attributeValue in AttributeValues.Values) { var attributeCache = AttributeCache.Get(attributeValue.AttributeId); var formattedOldValue = isDelete ? GetHistoryFormattedValue(attributeValue.Value, attributeCache) : string.Empty; var formattedNewValue = isDelete ? string.Empty : GetHistoryFormattedValue(attributeValue.Value, attributeCache); History.EvaluateChange(historyChangeList, attributeCache.Name, formattedOldValue, formattedNewValue, attributeCache.FieldType.Field.IsSensitive()); } if (!historyChangeList.Any()) { historyChangeList.AddChange( isDelete ? History.HistoryVerb.Delete : History.HistoryVerb.Add, History.HistoryChangeType.Record, $"{rootAttributeCache.Name} Item"); } HistoryItems = HistoryService.GetChanges( typeof(Person), SystemGuid.Category.HISTORY_PERSON_DEMOGRAPHIC_CHANGES.AsGuid(), rootAttributeValue.EntityId.Value, historyChangeList, rootAttributeCache.Name, typeof(Attribute), rootAttributeCache.Id, dbContext.GetCurrentPersonAlias()?.Id, dbContext.SourceOfChange); }