/// <summary> /// Helper method to register the entity commands. /// </summary> public static void RegisterEntityCommands(ILavaEngine engine) { var entityTypes = EntityTypeCache.All(); // register a business entity engine.RegisterBlock("business", (name) => { return(new RockEntityBlock()); }); // Register the core models, replacing existing blocks of the same name if necessary. foreach (var entityType in entityTypes .Where(e => e.IsEntity && e.Name.StartsWith("Rock.Model") && e.FriendlyName != null && e.FriendlyName != "")) { RegisterEntityCommand(engine, entityType, useQualifiedNameIfExists: true); } // Register plugin models, using fully-qualified namespace if necessary. foreach (var entityType in entityTypes .Where(e => e.IsEntity && !e.Name.StartsWith("Rock.Model") && e.FriendlyName != null && e.FriendlyName != "") .OrderBy(e => e.Id)) { RegisterEntityCommand(engine, entityType, useQualifiedNameIfExists: true); } }
/// <summary> /// Shows the settings. /// </summary> protected override void ShowSettings() { pnlEditModal.Visible = true; upnlContent.Update(); mdEdit.Show(); cblLavaCommands.Items.Clear(); cblLavaCommands.Items.Add("All"); // load the lava commands control foreach (var command in Rock.Lava.LavaHelper.GetLavaCommands()) { cblLavaCommands.Items.Add(command); } List <string> selectedCommands = new List <string>(); if (GetAttributeValue(AttributeKey.CustomResultsCommands) != null) { selectedCommands = GetAttributeValue(AttributeKey.CustomResultsCommands).Split(',').ToList(); } foreach (var command in selectedCommands) { var item = cblLavaCommands.Items.FindByText(command); if (item != null) { item.Selected = true; } } cbShowFilter.Checked = GetAttributeValue(AttributeKey.ShowFilters).AsBoolean(); var enabledModelIds = new List <int>(); if (GetAttributeValue(AttributeKey.EnabledModels).IsNotNullOrWhiteSpace()) { enabledModelIds = GetAttributeValue(AttributeKey.EnabledModels).Split(',').Select(int.Parse).ToList(); } var entities = EntityTypeCache.All(); var indexableEntities = entities.Where(i => i.IsIndexingSupported == true && enabledModelIds.Contains(i.Id)).ToList(); cblEnabledModels.DataValueField = "Id"; cblEnabledModels.DataTextField = "FriendlyName"; cblEnabledModels.DataSource = entities.Where(i => i.IsIndexingSupported == true && i.IsIndexingEnabled == true).ToList(); cblEnabledModels.DataBind(); cblEnabledModels.SetValues(enabledModelIds); cbShowRefinedSearch.Checked = GetAttributeValue(AttributeKey.ShowRefinedSearch).AsBoolean(); cbShowScores.Checked = GetAttributeValue(AttributeKey.ShowScores).AsBoolean(); cbUseCustomResults.Checked = GetAttributeValue(AttributeKey.UseCustomResults).AsBoolean(); ceCustomResultsTemplate.Text = GetAttributeValue(AttributeKey.LavaResultTemplate); cePreHtml.Text = GetAttributeValue(AttributeKey.PreHtml); cePostHtml.Text = GetAttributeValue(AttributeKey.PostHtml); tbBaseFieldFilters.Text = GetAttributeValue(AttributeKey.BaseFieldFilters); tbResultsPerPage.Text = GetAttributeValue(AttributeKey.ResultsPerPage); upnlContent.Update(); }
/// <summary> /// Helper method to register the entity commands. /// </summary> public static void RegisterEntityCommands() { var entityTypes = EntityTypeCache.All(); // register a business entity Template.RegisterTag <RockEntity>("business"); // Register the core models first foreach (var entityType in entityTypes .Where(e => e.IsEntity && e.Name.StartsWith("Rock.Model") && e.FriendlyName != null && e.FriendlyName != "")) { RegisterEntityCommand(entityType); } // Now register plugin models foreach (var entityType in entityTypes .Where(e => e.IsEntity && !e.Name.StartsWith("Rock.Model") && e.FriendlyName != null && e.FriendlyName != "") .OrderBy(e => e.Id)) { RegisterEntityCommand(entityType); } }
/// <summary> /// Formats the search result. /// </summary> /// <param name="person">The person.</param> /// <param name="displayOptions">The display options.</param> /// <param name="mergeFields">The merge fields.</param> /// <returns></returns> public virtual FormattedSearchResult FormatSearchResult(Person person, Dictionary <string, object> displayOptions = null, Dictionary <string, object> mergeFields = null) { string result = string.Empty; // get template from entity type var sourceModelEntity = EntityTypeCache.All().Where(e => e.Name == this.SourceIndexModel).FirstOrDefault(); if (sourceModelEntity != null) { var template = sourceModelEntity.IndexResultTemplate; if (template.IsNotNullOrWhiteSpace()) { if (mergeFields == null) { mergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields(null, person); } mergeFields.AddOrReplace("IndexDocument", this); mergeFields.AddOrReplace("DisplayOptions", displayOptions); return(new FormattedSearchResult() { IsViewAllowed = true, FormattedResult = template.ResolveMergeFields(mergeFields) }); } } // otherwise return not implemented (blank) return(new FormattedSearchResult() { IsViewAllowed = true, FormattedResult = result }); }
/// <summary> /// Loads the custom filters. /// </summary> private void LoadCustomFilters() { var enabledModelIds = new List <int>(); if (GetAttributeValue(AttributeKey.EnabledModels).IsNotNullOrWhiteSpace()) { enabledModelIds = GetAttributeValue(AttributeKey.EnabledModels).Split(',').Select(int.Parse).ToList(); } var entities = EntityTypeCache.All(); var indexableEntities = entities.Where(i => i.IsIndexingEnabled == true).ToList(); // if select entities are configured further filter by them if (enabledModelIds.Count > 0) { indexableEntities = indexableEntities.Where(i => enabledModelIds.Contains(i.Id)).ToList(); } foreach (var entity in indexableEntities) { var entityType = entity.GetEntityType(); if (SupportsIndexFieldFiltering(entityType)) { var filterOptions = GetIndexFilterConfig(entityType); RockCheckBoxList filterConfig = new RockCheckBoxList(); filterConfig.Label = filterOptions.FilterLabel; filterConfig.CssClass = "js-entity-id-" + entity.Id.ToString(); filterConfig.CssClass += " js-entity-filter-field"; filterConfig.RepeatDirection = RepeatDirection.Horizontal; filterConfig.Attributes.Add("entity-id", entity.Id.ToString()); filterConfig.Attributes.Add("entity-filter-field", filterOptions.FilterField); filterConfig.DataSource = filterOptions.FilterValues.Where(i => i != null); filterConfig.DataBind(); // set any selected values from the query string if (!string.IsNullOrWhiteSpace(PageParameter(filterOptions.FilterField))) { List <string> selectedValues = PageParameter(filterOptions.FilterField).Split(',').ToList(); foreach (ListItem item in filterConfig.Items) { if (selectedValues.Contains(item.Value)) { item.Selected = true; } } } if (filterOptions.FilterValues.Count > 0) { HtmlGenericContainer filterWrapper = new HtmlGenericContainer("div", "col-md-6"); filterWrapper.Controls.Add(filterConfig); phFilters.Controls.Add(filterWrapper); } } } }
/// <summary> /// Adds the context entities from headers. /// </summary> protected virtual void AddContextEntitiesFromHeaders() { foreach (var kvp in Headers) { // // Skip any header that isn't an entity context header. // if (!kvp.Key.StartsWith("X-ENTITYCONTEXT-", StringComparison.InvariantCultureIgnoreCase)) { continue; } // // Determine the the entity type in question. // var entityName = kvp.Key.Substring(16); var type = EntityTypeCache.All() .Where(a => a.IsEntity) .FirstOrDefault(a => a.FriendlyName.Equals(entityName, StringComparison.InvariantCultureIgnoreCase)) ?.GetEntityType(); string entityKey = kvp.Value.First(); // // If we got an unknown type or no Id/Guid then skip. // if (type == null || entityKey.IsNullOrWhiteSpace()) { continue; } // // Lazy load the entity so we don't actually load if it is never // accessed. // ContextEntities.AddOrReplace(type, new Lazy <IEntity>(() => { IEntity entity = null; if (int.TryParse(entityKey, out int entityId)) { entity = Reflection.GetIEntityForEntityType(type, entityId); } else if (Guid.TryParse(entityKey, out Guid entityGuid)) { entity = Reflection.GetIEntityForEntityType(type, entityGuid); } if (entity != null && entity is IHasAttributes attributedEntity) { Helper.LoadAttributes(attributedEntity); } return(entity); })); } }
/// <summary> /// Re-indexes the selected entity types in Universal Search /// /// 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; string selectedEntitiesSetting = dataMap.GetString("EntityFilter"); bool allEntities = dataMap.GetBoolean("IndexAllEntities"); RockContext rockContext = new RockContext(); var selectedEntityTypes = EntityTypeCache.All().Where(e => e.IsIndexingSupported && e.IsIndexingEnabled && e.FriendlyName != "Site"); // if 'All' wasn't selected the filter out the ones that weren't selected if (!allEntities) { if (selectedEntitiesSetting.IsNotNullOrWhiteSpace()) { var selectedEntityIds = selectedEntitiesSetting.Split(',').Select(int.Parse).ToList(); selectedEntityTypes = selectedEntityTypes.Where(e => selectedEntityIds.Contains(e.Id)); } } string results = string.Empty; var timerTotal = System.Diagnostics.Stopwatch.StartNew(); foreach (var entityTypeCache in selectedEntityTypes) { EntityTypeService entityTypeService = new EntityTypeService(rockContext); var entityType = entityTypeService.Get(entityTypeCache.Id); IndexContainer.DeleteIndex(entityType.IndexModelType); IndexContainer.CreateIndex(entityType.IndexModelType); Type type = entityTypeCache.GetEntityType(); if (type != null) { object classInstance = Activator.CreateInstance(type, null); MethodInfo bulkItemsMethod = type.GetMethod("BulkIndexDocuments"); if (classInstance != null && bulkItemsMethod != null) { var timer = System.Diagnostics.Stopwatch.StartNew(); bulkItemsMethod.Invoke(classInstance, null); timer.Stop(); results += $"{entityType.FriendlyName}: {timer.ElapsedMilliseconds/1000}s,"; } } } results += $"Total Time: {timerTotal.ElapsedMilliseconds / 1000}s,"; context.Result = "Indexing results: " + results.Trim(','); }
private void LoadCategories() { var entityCategories = new List <MCategory>(); var categoryIcons = new Dictionary <string, string>(); var categoryIconValues = GetAttributeValue("CategoryIcons"); if (!string.IsNullOrWhiteSpace(categoryIconValues)) { categoryIconValues = categoryIconValues.TrimEnd('|'); foreach (var keyVal in categoryIconValues.Split('|') .Select(s => s.Split('^')) .Where(s => s.Length == 2)) { categoryIcons.AddOrIgnore(keyVal[0], keyVal[1]); } } foreach (var entity in EntityTypeCache.All().Where(t => t.IsEntity)) { var type = entity.GetEntityType(); if (type != null && type.InheritsOrImplements(typeof(Rock.Data.Entity <>))) { string category = "Other"; var domainAttr = type.GetCustomAttribute <RockDomainAttribute>(false); if (domainAttr != null && domainAttr.Name.IsNotNullOrWhitespace()) { category = domainAttr.Name; } var entityCategory = entityCategories .Where(c => c.Name == category) .FirstOrDefault(); if (entityCategory == null) { entityCategory = new MCategory { Guid = Guid.NewGuid(), Name = category, RockEntities = new List <MEntity>() }; entityCategory.IconCssClass = categoryIcons.ContainsKey(category) ? categoryIcons[category] : string.Empty; entityCategories.Add(entityCategory); } entityCategory.RockEntities.Add(new MEntity { Id = entity.Id, AssemblyName = entity.AssemblyName, FriendlyName = entity.FriendlyName }); } } EntityCategories = new List <MCategory>(entityCategories.Where(c => c.Name != "Other").OrderBy(c => c.Name)); EntityCategories.AddRange(entityCategories.Where(c => c.Name == "Other")); }
/// <summary> /// Shows the smart search view. /// </summary> private void ShowSmartSearchView() { var entitySetting = Rock.Web.SystemSettings.GetValue("core_SmartSearchUniversalSearchEntities"); if (!string.IsNullOrWhiteSpace(entitySetting)) { List <int> entityIds = entitySetting.Split(',').Select(int.Parse).ToList(); var selected = string.Join(", ", EntityTypeCache.All().Where(e => entityIds.Contains(e.Id)).Select(e => e.FriendlyName).ToList()); lSmartSearchEntities.Text = string.Join(",", EntityTypeCache.All().Where(e => entityIds.Contains(e.Id)).Select(e => e.FriendlyName).ToList()); } lSmartSearchFilterCriteria.Text = Rock.Web.SystemSettings.GetValue("core_SmartSearchUniversalSearchFieldCriteria"); var searchType = Rock.Web.SystemSettings.GetValue("core_SmartSearchUniversalSearchSearchType").ConvertToEnumOrNull <SearchType>() ?? SearchType.Wildcard; lSearchType.Text = searchType.ToString(); }
/// <summary> /// Gets the document URL. /// </summary> /// <returns></returns> public virtual string GetDocumentUrl(Dictionary <string, object> displayOptions = null) { // get template from entity type var sourceModelEntity = EntityTypeCache.All().Where(e => e.Name == this.SourceIndexModel).FirstOrDefault(); if (sourceModelEntity != null) { var template = sourceModelEntity.IndexDocumentUrl; if (template.IsNotNullOrWhiteSpace()) { var mergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields(null, null); mergeFields.Add("IndexDocument", this); mergeFields.Add("DisplayOptions", displayOptions); return(template.ResolveMergeFields(mergeFields).Trim()); } } return(string.Empty);; }
/// <summary> /// Gets a list of the entity types that can be selected in the picker. /// </summary> /// <returns>A collection of ListItemViewModel objects that represent the entity types.</returns> private List <ListItemViewModel> GetEntityTypes() { var entityTypes = EntityTypeCache.All() .Where(t => t.IsEntity) .OrderByDescending(t => t.IsCommon) .ThenBy(t => t.FriendlyName) .Select(t => new ListItemViewModel { Value = t.Guid.ToString(), Text = t.FriendlyName, Category = t.IsCommon ? "Common" : "All Entities" }) .ToList(); entityTypes.Insert(0, new ListItemViewModel { Value = Guid.Empty.ToString(), Text = "None (Global Attributes)" }); return(entityTypes); }
/// <summary> /// Handles the Click event of the lbSmartSearchEdit 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 lbSmartSearchEdit_Click(object sender, EventArgs e) { var entities = EntityTypeCache.All(); var indexableEntities = entities.Where(i => i.IsIndexingSupported == true).ToList(); cblSmartSearchEntities.DataTextField = "FriendlyName"; cblSmartSearchEntities.DataValueField = "Id"; cblSmartSearchEntities.DataSource = indexableEntities; cblSmartSearchEntities.DataBind(); var entitySetting = Rock.Web.SystemSettings.GetValue("core_SmartSearchUniversalSearchEntities"); List <string> entityList = entitySetting.Split(',').ToList(); foreach (ListItem checkbox in cblSmartSearchEntities.Items) { if (entityList.Contains(checkbox.Value)) { checkbox.Selected = true; } } tbSmartSearchFieldCrieria.Text = Rock.Web.SystemSettings.GetValue("core_SmartSearchUniversalSearchFieldCriteria"); var searchType = ((int)SearchType.Wildcard).ToString(); if (!string.IsNullOrWhiteSpace(Rock.Web.SystemSettings.GetValue("core_SmartSearchUniversalSearchSearchType"))) { searchType = Rock.Web.SystemSettings.GetValue("core_SmartSearchUniversalSearchSearchType"); } ddlSearchType.SelectedValue = searchType; pnlSmartSearchEdit.Visible = true; pnlSmartSearchView.Visible = false; }
/// <summary> /// Renders the specified context. /// </summary> /// <param name="context">The context.</param> /// <param name="result">The result.</param> public override void Render(Context context, TextWriter result) { // first ensure that search commands are allowed in the context if (!this.IsAuthorized(context)) { result.Write(string.Format(RockLavaBlockBase.NotAuthorizedMessage, this.Name)); base.Render(context, result); return; } var parms = ParseMarkup(_markup, context); SearchFieldCriteria fieldCriteria = new SearchFieldCriteria(); SearchType searchType = SearchType.Wildcard; List <int> entityIds = new List <int>(); string query = string.Empty; int limit = 50; int offset = 0; if (parms.Any(p => p.Key == "query")) { query = parms["query"]; } if (parms.Any(p => p.Key == "limit")) { Int32.TryParse(parms["limit"], out limit); } if (parms.Any(p => p.Key == "offset")) { Int32.TryParse(parms["offset"], out offset); } if (parms.Any(p => p.Key == "fieldcriteria")) { foreach (var queryString in parms["fieldcriteria"].ToKeyValuePairList()) { // check that multiple values were not passed var values = queryString.Value.ToString().Split(','); foreach (var value in values) { // the first letter of the field name should be lowercase string fieldName = Char.ToLowerInvariant(queryString.Key[0]) + queryString.Key.Substring(1); fieldCriteria.FieldValues.Add(new FieldValue { Field = fieldName, Value = value }); } } } if (parms.Any(p => p.Key == "searchtype")) { switch (parms["searchtype"]) { case "exactmatch": { searchType = SearchType.ExactMatch; break; } case "fuzzy": { searchType = SearchType.Fuzzy; break; } case "wildcard": { searchType = SearchType.Wildcard; break; } } } if (parms.Any(p => p.Key == "criteriasearchtype")) { if (parms["criteriasearchtype"].ToLower() == "and") { fieldCriteria.SearchType = CriteriaSearchType.And; } } if (parms.Any(p => p.Key == "entities")) { var entities = parms["entities"].Split(','); foreach (var entity in entities) { foreach (var entityType in EntityTypeCache.All()) { if (entityType.FriendlyName?.ToLower() == entity) { entityIds.Add(entityType.Id); } } } } var client = IndexContainer.GetActiveComponent(); var results = client.Search(query, searchType, entityIds, fieldCriteria, limit, offset); context.Scopes.Last()[parms["iterator"]] = results; base.Render(context, result); }
/// <summary> /// Loads the Entity Attribute data. /// </summary> /// <param name="csvData">The CSV data.</param> private int LoadEntityAttributes(CSVInstance csvData) { var lookupContext = new RockContext(); var importedAttributes = new AttributeService(lookupContext).Queryable().Count(a => a.ForeignKey != null); var entityTypes = EntityTypeCache.All().Where(e => e.IsEntity && e.IsSecured).ToList(); var completedItems = 0; var addedItems = 0; ReportProgress(0, string.Format("Verifying attribute import ({0:N0} already imported).", importedAttributes)); string[] row; // Uses a look-ahead enumerator: this call will move to the next record immediately while ((row = csvData.Database.FirstOrDefault()) != null) { var entityTypeName = row[AttributeEntityTypeName]; var attributeForeignKey = row[AttributeId]; var rockKey = row[AttributeRockKey]; var attributeName = row[AttributeName]; var categoryName = row[AttributeCategoryName]; var attributeTypeString = row[AttributeType]; var definedValueForeignKey = row[AttributeDefinedTypeId]; var entityTypeQualifierName = row[AttributeEntityTypeQualifierName]; var entityTypeQualifierValue = row[AttributeEntityTypeQualifierValue]; if (!string.IsNullOrWhiteSpace(entityTypeQualifierName) && !string.IsNullOrWhiteSpace(entityTypeQualifierValue)) { entityTypeQualifierValue = GetEntityTypeQualifierValue(entityTypeQualifierName, entityTypeQualifierValue, lookupContext); } if (string.IsNullOrEmpty(attributeName)) { LogException("Attribute", string.Format("Entity Attribute Name cannot be blank for {0} {1}", entityTypeName, attributeForeignKey)); } else { var entityTypeId = entityTypes.FirstOrDefault(et => et.Name.Equals(entityTypeName)).Id; var definedValueForeignId = definedValueForeignKey.AsType <int?>(); var fieldTypeId = TextFieldTypeId; fieldTypeId = GetAttributeFieldType(attributeTypeString); var fk = string.Empty; if (string.IsNullOrWhiteSpace(attributeForeignKey)) { fk = string.Format("Bulldozer_{0}_{1}", categoryName.RemoveWhitespace(), attributeName.RemoveWhitespace()).Left(100); } else { fk = attributeForeignKey; } var attribute = FindEntityAttribute(lookupContext, categoryName, attributeName, entityTypeId, attributeForeignKey, rockKey); if (attribute == null) { attribute = AddEntityAttribute(lookupContext, entityTypeId, entityTypeQualifierName, entityTypeQualifierValue, fk, categoryName, attributeName, rockKey, fieldTypeId, true, definedValueForeignId, definedValueForeignKey, attributeTypeString: attributeTypeString); addedItems++; } else if (string.IsNullOrWhiteSpace(attribute.ForeignKey)) { attribute = AddEntityAttribute(lookupContext, entityTypeId, entityTypeQualifierName, entityTypeQualifierValue, fk, categoryName, attributeName, rockKey, fieldTypeId, true, definedValueForeignId, definedValueForeignKey, attributeTypeString: attributeTypeString); addedItems++; } completedItems++; if (completedItems % (ReportingNumber * 10) < 1) { ReportProgress(0, string.Format("{0:N0} attributes processed.", completedItems)); } if (completedItems % ReportingNumber < 1) { ReportPartialProgress(); } } } ReportProgress(100, string.Format("Finished attribute import: {0:N0} attributes imported.", addedItems)); return(completedItems); }
/// <summary> /// Cleanups the orphaned attributes. /// </summary> /// <param name="dataMap">The data map.</param> /// <returns></returns> private int CleanupOrphanedAttributes(JobDataMap dataMap) { int recordsDeleted = 0; // Cleanup AttributeMatrix records that are no longer associated with an attribute value using (RockContext rockContext = new RockContext()) { AttributeMatrixService attributeMatrixService = new AttributeMatrixService(rockContext); AttributeMatrixItemService attributeMatrixItemService = new AttributeMatrixItemService(rockContext); var matrixFieldTypeId = FieldTypeCache.Read <MatrixFieldType>().Id; // get a list of attribute Matrix Guids that are actually in use var usedAttributeMatrices = new AttributeValueService(rockContext).Queryable().Where(a => a.Attribute.FieldTypeId == matrixFieldTypeId).Select(a => a.Value).ToList().AsGuidList(); // clean up any orphaned attribute matrices var dayAgo = RockDateTime.Now.AddDays(-1); var orphanedAttributeMatrices = attributeMatrixService.Queryable().Where(a => (a.CreatedDateTime < dayAgo) && !usedAttributeMatrices.Contains(a.Guid)).ToList(); if (orphanedAttributeMatrices.Any()) { recordsDeleted += orphanedAttributeMatrices.Count; attributeMatrixItemService.DeleteRange(orphanedAttributeMatrices.SelectMany(a => a.AttributeMatrixItems)); attributeMatrixService.DeleteRange(orphanedAttributeMatrices); rockContext.SaveChanges(); } } // clean up other orphaned entity attributes Type rockContextType = typeof(Rock.Data.RockContext); foreach (var cachedType in EntityTypeCache.All().Where(e => e.IsEntity)) { Type entityType = cachedType.GetEntityType(); if (entityType != null && typeof(IEntity).IsAssignableFrom(entityType) && typeof(IHasAttributes).IsAssignableFrom(entityType) && !entityType.Namespace.Equals("Rock.Rest.Controllers")) { try { bool ignore = false; if (entityType.Assembly != rockContextType.Assembly) { // If the model is from a custom project, verify that it is using RockContext, if not, ignore it since an // exception will occur due to the AttributeValue query using RockContext. var entityContextType = Reflection.SearchAssembly(entityType.Assembly, typeof(System.Data.Entity.DbContext)); ignore = (entityContextType.Any() && !entityContextType.First().Value.Equals(rockContextType)); } if (!ignore) { var classMethod = this.GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic) .First(m => m.Name == "CleanupOrphanedAttributeValuesForEntityType"); var genericMethod = classMethod.MakeGenericMethod(entityType); var result = genericMethod.Invoke(this, null) as int?; if (result.HasValue) { recordsDeleted += (int)result; } } } catch { } } } return(recordsDeleted); }
/// <summary> /// Loads the Entity Attribute data. /// </summary> /// <param name="csvData">The CSV data.</param> private int LoadEntityAttributeValues(CSVInstance csvData) { var lookupContext = new RockContext(); var importedAttributeValues = new AttributeValueService(lookupContext).Queryable().Count(a => a.ForeignKey != null); var entityTypes = EntityTypeCache.All().Where(e => e.IsEntity && e.IsSecured).ToList(); var attributeService = new AttributeService(lookupContext); var attributeValues = new List <AttributeValue>(); var completedItems = 0; var addedItems = 0; int? entityTypeId = null; var prevEntityTypeName = string.Empty; var prevAttributeForeignKey = string.Empty; var prevRockKey = string.Empty; Attribute attribute = null; Type contextModelType = null; System.Data.Entity.DbContext contextDbContext = null; IService contextService = null; IHasAttributes entity = null; var prevAttributeValueEntityId = string.Empty; ReportProgress(0, string.Format("Verifying attribute value import ({0:N0} already imported).", importedAttributeValues)); string[] row; // Uses a look-ahead enumerator: this call will move to the next record immediately while ((row = csvData.Database.FirstOrDefault()) != null) { var entityTypeName = row[AttributeEntityTypeName]; var attributeForeignKey = row[AttributeId]; var rockKey = row[AttributeRockKey]; var attributeValueForeignKey = row[AttributeValueId]; var attributeValueEntityId = row[AttributeValueEntityId]; var attributeValue = row[AttributeValue]; if (!string.IsNullOrWhiteSpace(entityTypeName) && !string.IsNullOrWhiteSpace(attributeValueEntityId) && !string.IsNullOrWhiteSpace(attributeValue) && (!string.IsNullOrEmpty(attributeForeignKey) || !string.IsNullOrEmpty(rockKey))) { var findNewEntity = false; if (!entityTypeId.HasValue || !prevEntityTypeName.Equals(entityTypeName, StringComparison.OrdinalIgnoreCase)) { entityTypeId = entityTypes.FirstOrDefault(et => et.Name.Equals(entityTypeName)).Id; prevEntityTypeName = entityTypeName; findNewEntity = true; contextModelType = entityTypes.FirstOrDefault(et => et.Name.Equals(entityTypeName)).GetEntityType(); contextDbContext = Reflection.GetDbContextForEntityType(contextModelType); if (contextDbContext != null) { contextService = Reflection.GetServiceForEntityType(contextModelType, contextDbContext); } } if (entityTypeId.HasValue && contextService != null) { if (!string.IsNullOrWhiteSpace(attributeForeignKey) && !prevAttributeForeignKey.Equals(attributeForeignKey, StringComparison.OrdinalIgnoreCase)) { attribute = attributeService.GetByEntityTypeId(entityTypeId).FirstOrDefault(a => a.ForeignKey == attributeForeignKey); prevAttributeForeignKey = attributeForeignKey; prevRockKey = string.Empty; } else if (string.IsNullOrWhiteSpace(attributeForeignKey)) { // if no FK provided force attribute to null so the rockKey is tested attribute = null; } if (attribute == null && !string.IsNullOrWhiteSpace(rockKey) && !prevRockKey.Equals(rockKey, StringComparison.OrdinalIgnoreCase)) { attribute = attributeService.GetByEntityTypeId(entityTypeId).FirstOrDefault(a => a.Key == rockKey); prevRockKey = rockKey; prevAttributeForeignKey = string.Empty; } if (attribute != null) { // set the fk if it wasn't for some reason if (string.IsNullOrWhiteSpace(attribute.ForeignKey) && !string.IsNullOrWhiteSpace(attributeForeignKey)) { var updatedAttributeRockContext = new RockContext(); var updatedAttributeService = new AttributeService(updatedAttributeRockContext); var updatedAttribute = updatedAttributeService.GetByEntityTypeId(entityTypeId).FirstOrDefault(a => a.Id == attribute.Id); updatedAttribute.ForeignKey = attributeForeignKey; updatedAttribute.ForeignId = attributeForeignKey.AsIntegerOrNull(); updatedAttributeRockContext.SaveChanges(DisableAuditing); } if (entity == null || (findNewEntity || !prevAttributeValueEntityId.Equals(attributeValueEntityId, StringComparison.OrdinalIgnoreCase))) { MethodInfo qryMethod = contextService.GetType().GetMethod("Queryable", new Type[] { }); var entityQry = qryMethod.Invoke(contextService, new object[] { }) as IQueryable <IEntity>; var entityResult = entityQry.Where(e => e.ForeignKey.Equals(attributeValueEntityId, StringComparison.OrdinalIgnoreCase)); entity = entityResult.FirstOrDefault() as IHasAttributes; prevAttributeValueEntityId = attributeValueEntityId; } if (entity != null) { var av = CreateEntityAttributeValue(lookupContext, attribute, entity, attributeValue, attributeValueForeignKey); if (av != null && !attributeValues.Where(e => e.EntityId == av.EntityId).Where(a => a.AttributeId == av.AttributeId).Any()) { attributeValues.Add(av); addedItems++; } } } } } completedItems++; if (completedItems % (ReportingNumber * 10) < 1) { ReportProgress(0, string.Format("{0:N0} attribute values processed.", completedItems)); } if (completedItems % ReportingNumber < 1) { SaveAttributeValues(lookupContext, attributeValues); attributeValues.Clear(); lookupContext.Dispose(); lookupContext = new RockContext(); attributeService = new AttributeService(lookupContext); ReportPartialProgress(); } } if (attributeValues.Any()) { SaveAttributeValues(lookupContext, attributeValues); } ReportProgress(100, string.Format("Finished attribute value import: {0:N0} attribute values imported.", addedItems)); return(completedItems); }
/// <summary> /// Loads the Notes data. /// </summary> /// <param name="csvData">The CSV data.</param> private int LoadNote(CSVInstance csvData) { var lookupContext = new RockContext(); var importedNotes = new NoteService(lookupContext).Queryable().Count(n => n.ForeignKey != null); var entityTypes = EntityTypeCache.All().Where(e => e.IsEntity && e.IsSecured).ToList(); var noteList = new List <Note>(); var skippedNotes = new Dictionary <string, string>(); var completedItems = 0; ReportProgress(0, string.Format("Verifying note import ({0:N0} already imported).", importedNotes)); string[] row; // Uses a look-ahead enumerator: this call will move to the next record immediately while ((row = csvData.Database.FirstOrDefault()) != null) { var noteType = row[NoteType] as string; var entityTypeName = row[EntityTypeName] as string; var entityForeignKey = row[EntityForeignId]; var noteCaption = row[NoteCaption] as string; var noteText = row[NoteText] as string; var createdDate = row[NoteDate].AsDateTime(); var createdByKey = row[CreatedById]; var rowIsAlert = row[IsAlert]; var rowIsPrivate = row[IsPrivate]; var isAlert = ( bool )ParseBoolOrDefault(rowIsAlert, false); var isPrivate = ( bool )ParseBoolOrDefault(rowIsPrivate, false); int?noteTypeId = null; int?noteEntityId = null; var entityType = entityTypes.FirstOrDefault(et => et.Name.Equals(entityTypeName)); if (entityType != null) { var entityTypeInstance = entityType.GetEntityType(); if (entityTypeInstance == typeof(Person)) { // this is a person, reference the local keys that are already cached var personKeys = GetPersonKeys(entityForeignKey); if (personKeys != null) { noteEntityId = personKeys.PersonId; } noteTypeId = noteType.StartsWith("General", StringComparison.OrdinalIgnoreCase) ? ( int? )PersonalNoteTypeId : null; } else { // activate service type and query the foreign id for this entity type var entityService = Reflection.GetServiceForEntityType(entityTypeInstance, lookupContext); var entityQueryable = entityService.GetType().GetMethod("Queryable", new Type[] { }); // Note: reflection-invoked service can only return IEnumerable or primitive object types noteEntityId = ((IQueryable <IEntity>)entityQueryable.Invoke(entityService, new object[] { })) .Where(q => entityForeignKey == q.ForeignKey) .Select(q => q.Id) .FirstOrDefault(); } if (noteEntityId > 0 && !string.IsNullOrWhiteSpace(noteText)) { var creatorKeys = GetPersonKeys(createdByKey); var creatorAliasId = creatorKeys != null ? ( int? )creatorKeys.PersonAliasId : null; var note = AddEntityNote(lookupContext, entityType.Id, ( int )noteEntityId, noteCaption, noteText, isAlert, isPrivate, noteType, noteTypeId, false, createdDate, string.Format("Note imported {0}", ImportDateTime), creatorAliasId); noteList.Add(note); completedItems++; if (completedItems % (ReportingNumber * 10) < 1) { ReportProgress(0, string.Format("{0:N0} notes processed.", completedItems)); } if (completedItems % ReportingNumber < 1) { SaveNotes(noteList); ReportPartialProgress(); noteList.Clear(); } } } else { skippedNotes.Add(entityForeignKey, noteType); } } if (noteList.Any()) { SaveNotes(noteList); } if (skippedNotes.Any()) { ReportProgress(0, "The following notes could not be imported and were skipped:"); foreach (var key in skippedNotes) { ReportProgress(0, string.Format("{0} note for Foreign ID {1}.", key.Value, key)); } } ReportProgress(100, string.Format("Finished note import: {0:N0} notes imported.", completedItems)); return(completedItems); }
/// <summary> /// Configures the settings. /// </summary> private void ConfigureSettings() { // toggle refine search view toggle button lbRefineSearch.Visible = GetAttributeValue("ShowRefinedSearch").AsBoolean(); // model selector var enabledModelIds = new List <int>(); if (GetAttributeValue("EnabledModels").IsNotNullOrWhiteSpace()) { enabledModelIds = GetAttributeValue("EnabledModels").Split(',').Select(int.Parse).ToList(); } var entities = EntityTypeCache.All(); var indexableEntities = entities.Where(i => i.IsIndexingEnabled == true).ToList(); // if enabled entities setting is set further filter by those if (enabledModelIds.Count > 0) { indexableEntities = indexableEntities.Where(i => enabledModelIds.Contains(i.Id)).ToList(); } cblModelFilter.DataTextField = "FriendlyName"; cblModelFilter.DataValueField = "Id"; cblModelFilter.DataSource = indexableEntities; cblModelFilter.DataBind(); cblModelFilter.Visible = GetAttributeValue("ShowFilters").AsBoolean(); // if only one model is selected then hide the type checkbox if (cblModelFilter.Items.Count == 1) { cblModelFilter.Visible = false; } hrSeparator.Visible = cblModelFilter.Visible; ddlSearchType.BindToEnum <SearchType>(); ddlSearchType.SelectedValue = GetAttributeValue("SearchType"); // override the block setting if passed in the query string if (!string.IsNullOrWhiteSpace(PageParameter("SearchType"))) { ddlSearchType.SelectedValue = PageParameter("SearchType"); } // set setting values from query string if (!string.IsNullOrWhiteSpace(PageParameter("Models"))) { var queryStringModels = PageParameter("Models").Split(',').Select(s => s.Trim()).ToList(); foreach (ListItem item in cblModelFilter.Items) { if (queryStringModels.Contains(item.Value)) { item.Selected = true; } else { item.Selected = false; } } } if (!string.IsNullOrWhiteSpace(PageParameter("ItemsPerPage"))) { _itemsPerPage = PageParameter("ItemsPerPage").AsInteger(); } if (!string.IsNullOrWhiteSpace(PageParameter("CurrentPage"))) { _currentPageNum = PageParameter("CurrentPage").AsInteger() - 1; } if (!string.IsNullOrWhiteSpace(PageParameter("RefinedSearch"))) { pnlRefineSearch.Visible = PageParameter("RefinedSearch").AsBoolean(); if (pnlRefineSearch.Visible) { lbRefineSearch.Text = "Hide Refined Search"; } } _itemsPerPage = GetAttributeValue("ResultsPerPage").AsInteger(); }
/// <summary> /// Renders the specified context. /// </summary> /// <param name="context">The context.</param> /// <param name="result">The result.</param> /// <exception cref="System.Exception">Your Lava command must contain at least one valid filter. If you configured a filter it's possible that the property or attribute you provided does not exist.</exception> public override void Render(Context context, TextWriter result) { // first ensure that entity commands are allowed in the context if (!this.IsAuthorized(context)) { result.Write(string.Format(RockLavaBlockBase.NotAuthorizedMessage, this.Name)); base.Render(context, result); return; } bool hasFilter = false; var modelName = string.Empty; // get a service for the entity based off it's friendly name if (_entityName == "business") { modelName = "Rock.Model.Person"; } else { modelName = "Rock.Model." + _entityName; } // Check first to see if this is a core model. use the createIfNotFound = false option var entityTypeCache = EntityTypeCache.Get(modelName, false); if (entityTypeCache == null) { var entityTypes = EntityTypeCache.All(); // If not, look for first plug-in model that has same friendly name entityTypeCache = entityTypes .Where(e => e.IsEntity && !e.Name.StartsWith("Rock.Model") && e.FriendlyName != null && e.FriendlyName.RemoveSpaces().ToLower() == _entityName) .OrderBy(e => e.Id) .FirstOrDefault(); // If still null check to see if this was a duplicate class and full class name was used as entity name if (entityTypeCache == null) { modelName = _entityName.Replace('_', '.'); entityTypeCache = entityTypes.Where(e => String.Equals(e.Name, modelName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); } } if (entityTypeCache != null) { Type entityType = entityTypeCache.GetEntityType(); if (entityType != null) { // Get the appropriate database context for this entity type. // Note that this may be different from the standard RockContext if the entity is sourced from a plug-in. var dbContext = Reflection.GetDbContextForEntityType(entityType); // Check if there is a RockContext in the Lava context. If so then use the RockContext passed in. if (dbContext is RockContext) { var lavaContext = context.Registers["RockContext"]; if (lavaContext.IsNotNull()) { dbContext = (DbContext)lavaContext; } } // Disable change-tracking for this data context to improve performance - objects supplied to a Lava context are read-only. dbContext.Configuration.AutoDetectChangesEnabled = false; // Create an instance of the entity's service IService serviceInstance = Reflection.GetServiceForEntityType(entityType, dbContext); ParameterExpression paramExpression = Expression.Parameter(entityType, "x"); Expression queryExpression = null; // the base expression we'll use to build our query from // Parse markup var parms = ParseMarkup(_markup, context); if (parms.Any(p => p.Key == "id")) { string propertyName = "Id"; List <string> selectionParms = new List <string>(); selectionParms.Add(PropertyComparisonConversion("==").ToString()); selectionParms.Add(parms["id"].ToString()); selectionParms.Add(propertyName); var entityProperty = entityType.GetProperty(propertyName); queryExpression = ExpressionHelper.PropertyFilterExpression(selectionParms, paramExpression, propertyName, entityProperty.PropertyType); hasFilter = true; } else { // where clause expression if (parms.Any(p => p.Key == "where")) { queryExpression = ParseWhere(parms["where"], entityType, serviceInstance, paramExpression, entityType, entityTypeCache); if (queryExpression != null) { hasFilter = true; } } // DataView expression if (parms.Any(p => p.Key == "dataview")) { var dataViewId = parms["dataview"].AsIntegerOrNull(); if (dataViewId.HasValue) { var dataViewExpression = GetDataViewExpression(dataViewId.Value, serviceInstance, paramExpression, entityTypeCache); if (queryExpression == null) { queryExpression = dataViewExpression; hasFilter = true; } else { queryExpression = Expression.AndAlso(queryExpression, dataViewExpression); } } } // process dynamic filter expressions (from the query string) if (parms.Any(p => p.Key == "dynamicparameters")) { var dynamicFilters = parms["dynamicparameters"].Split(',') .Select(x => x.Trim()) .Where(x => !string.IsNullOrWhiteSpace(x)) .ToList(); foreach (var dynamicFilter in dynamicFilters) { var dynamicFilterValue = HttpContext.Current.Request[dynamicFilter]; var dynamicFilterExpression = GetDynamicFilterExpression(dynamicFilter, dynamicFilterValue, entityType, serviceInstance, paramExpression); if (dynamicFilterExpression != null) { if (queryExpression == null) { queryExpression = dynamicFilterExpression; hasFilter = true; } else { queryExpression = Expression.AndAlso(queryExpression, dynamicFilterExpression); } } } } } // Make the query from the expression. /* [2020-10-08] DL * "Get" is intentionally used here rather than "GetNoTracking" to allow lazy-loading of navigation properties from the Lava context. * (Refer https://github.com/SparkDevNetwork/Rock/issues/4293) */ MethodInfo getMethod = serviceInstance.GetType().GetMethod("Get", new Type[] { typeof(ParameterExpression), typeof(Expression), typeof(Rock.Web.UI.Controls.SortProperty), typeof(int?) }); if (getMethod != null) { // get a listing of ids and build it into the query expression if (parms.Any(p => p.Key == "ids")) { List <int> value = parms["ids"].ToString().Split(',').Select(int.Parse).ToList(); MemberExpression propertyExpression = Expression.Property(paramExpression, "Id"); ConstantExpression constantExpression = Expression.Constant(value, typeof(List <int>)); Expression containsExpression = Expression.Call(constantExpression, typeof(List <int>).GetMethod("Contains", new Type[] { typeof(int) }), propertyExpression); if (queryExpression != null) { queryExpression = Expression.AndAlso(queryExpression, containsExpression); } else { queryExpression = containsExpression; } hasFilter = true; } var getResult = getMethod.Invoke(serviceInstance, new object[] { paramExpression, queryExpression, null, null }); var queryResult = getResult as IQueryable <IEntity>; // process entity specific filters switch (_entityName) { case "person": { queryResult = PersonFilters((IQueryable <Person>)queryResult, parms); break; } case "business": { queryResult = BusinessFilters((IQueryable <Person>)queryResult, parms); break; } } // if there was a dynamic expression add it now if (parms.Any(p => p.Key == "expression")) { queryResult = queryResult.Where(parms["expression"]); hasFilter = true; } var queryResultExpression = queryResult.Expression; // add sort expressions if (parms.Any(p => p.Key == "sort")) { string orderByMethod = "OrderBy"; foreach (var column in parms["sort"].Split(',').Select(x => x.Trim()).Where(x => !string.IsNullOrWhiteSpace(x)).ToList()) { string propertyName; var direction = SortDirection.Ascending; if (column.EndsWith(" desc", StringComparison.OrdinalIgnoreCase)) { direction = SortDirection.Descending; propertyName = column.Left(column.Length - 5); } else { propertyName = column; } string methodName = direction == SortDirection.Descending ? orderByMethod + "Descending" : orderByMethod; if (entityType.GetProperty(propertyName) != null) { // sorting a entity property var memberExpression = Expression.Property(paramExpression, propertyName); LambdaExpression sortSelector = Expression.Lambda(memberExpression, paramExpression); queryResultExpression = Expression.Call(typeof(Queryable), methodName, new Type[] { queryResult.ElementType, sortSelector.ReturnType }, queryResultExpression, sortSelector); } else { // sorting on an attribute // get attribute id int?attributeId = null; foreach (var attribute in AttributeCache.GetByEntityType(entityTypeCache.Id)) { if (attribute.Key == propertyName) { attributeId = attribute.Id; break; } } if (attributeId.HasValue) { // get AttributeValue queryable and parameter if (dbContext is RockContext) { var attributeValues = new AttributeValueService(dbContext as RockContext).Queryable(); ParameterExpression attributeValueParameter = Expression.Parameter(typeof(AttributeValue), "v"); MemberExpression idExpression = Expression.Property(paramExpression, "Id"); var attributeExpression = Attribute.Helper.GetAttributeValueExpression(attributeValues, attributeValueParameter, idExpression, attributeId.Value); LambdaExpression sortSelector = Expression.Lambda(attributeExpression, paramExpression); queryResultExpression = Expression.Call(typeof(Queryable), methodName, new Type[] { queryResult.ElementType, sortSelector.ReturnType }, queryResultExpression, sortSelector); } else { throw new Exception(string.Format("The database context for type {0} does not support RockContext attribute value queries.", entityTypeCache.FriendlyName)); } } } orderByMethod = "ThenBy"; } } // check to ensure we had some form of filter (otherwise we'll return all results in the table) if (!hasFilter) { throw new Exception("Your Lava command must contain at least one valid filter. If you configured a filter it's possible that the property or attribute you provided does not exist."); } // reassemble the queryable with the sort expressions queryResult = queryResult.Provider.CreateQuery(queryResultExpression) as IQueryable <IEntity>; if (parms.GetValueOrNull("count").AsBoolean()) { int countResult = queryResult.Count(); context.Scopes.Last()["count"] = countResult; } else { // Run security check on each result if enabled and entity is not a person (we do not check security on people) if (parms["securityenabled"].AsBoolean() && _entityName != "person") { var items = queryResult.ToList(); var itemsSecured = new List <IEntity>(); Person person = GetCurrentPerson(context); foreach (IEntity item in items) { ISecured itemSecured = item as ISecured; if (itemSecured == null || itemSecured.IsAuthorized(Authorization.VIEW, person)) { itemsSecured.Add(item); /* * 8/13/2020 - JME * It might seem logical to break out of the loop if there is limit parameter provided once the * limit is reached. This though has two issues. * * FIRST * Depending how it was implemented it can have the effect of breaking when an offset is * provided. * {% contentchannelitem where:'ContentChannelId == 1' limit:'3' %} * {% for item in contentchannelitemItems %} * {{ item.Id }} - {{ item.Title }}<br> * {% endfor %} * {% endcontentchannelitem %} * Returns 3 items (correct) * * {% contentchannelitem where:'ContentChannelId == 1' limit:'3' offset:'1' %} * {% for item in contentchannelitemItems %} * {{ item.Id }} - {{ item.Title }}<br> * {% endfor %} * {% endcontentchannelitem %} * Returns only 2 items (incorrect) - because of the offset * * SECOND * If the limit is moved before the security check it's possible that the security checks * will remove items and will therefore not give you the amount of items that you asked for. * * Unfortunately this has to be an inefficent process to ensure pagination works. I will also * add a detailed note to the documentation to encourage people to disable security checks, * especially when used with pagination, in the Lava docs. */ } } queryResult = itemsSecured.AsQueryable(); } // offset if (parms.Any(p => p.Key == "offset")) { queryResult = queryResult.Skip(parms["offset"].AsInteger()); } // limit, default to 1000 if (parms.Any(p => p.Key == "limit")) { queryResult = queryResult.Take(parms["limit"].AsInteger()); } else { queryResult = queryResult.Take(1000); } var resultList = queryResult.ToList(); // if there is only one item to return set an alternative non-array based variable if (resultList.Count == 1) { context.Scopes.Last()[_entityName] = resultList.FirstOrDefault(); } context.Scopes.Last()[parms["iterator"]] = resultList; } } } } else { result.Write(string.Format("Could not find a model for {0}.", _entityName)); base.Render(context, result); } base.Render(context, result); }
/// <summary> /// Renders the specified context. /// </summary> /// <param name="context">The context.</param> /// <param name="result">The result.</param> /// <exception cref="System.Exception">Your Lava command must contain at least one valid filter. If you configured a filter it's possible that the property or attribute you provided does not exist.</exception> public override void Render(Context context, TextWriter result) { // first ensure that entity commands are allowed in the context if (!this.IsAuthorized(context)) { result.Write(string.Format(RockLavaBlockBase.NotAuthorizedMessage, this.Name)); base.Render(context, result); return; } bool hasFilter = false; // get a service for the entity based off it's friendly name var entityTypes = EntityTypeCache.All(); var model = string.Empty; if (_entityName == "business") { model = "Rock.Model.Person"; } else { model = "Rock.Model." + _entityName; } // Check first to see if this is a core model var entityTypeCache = entityTypes.Where(e => String.Equals(e.Name, model, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); // If not, look for first plugin model that has same friendly name if (entityTypeCache == null) { entityTypeCache = entityTypes .Where(e => e.IsEntity && !e.Name.StartsWith("Rock.Model") && e.FriendlyName != null && e.FriendlyName.RemoveSpaces().ToLower() == _entityName) .OrderBy(e => e.Id) .FirstOrDefault(); } // If still null check to see if this was a duplicate class and full class name was used as entity name if (entityTypeCache == null) { model = _entityName.Replace('_', '.'); entityTypeCache = entityTypes.Where(e => String.Equals(e.Name, model, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); } if (entityTypeCache != null) { Type entityType = entityTypeCache.GetEntityType(); if (entityType != null) { // Get the database context Type contextType = null; Rock.Data.DbContext dbContext = null; var contexts = Rock.Reflection.SearchAssembly(entityType.Assembly, typeof(Rock.Data.DbContext)); if (contexts.Any()) { contextType = contexts.First().Value; dbContext = Activator.CreateInstance(contextType) as Rock.Data.DbContext; } if (dbContext == null) { dbContext = _rockContext; } // create an instance of the entity's service Type[] modelType = { entityType }; Type genericServiceType = typeof(Rock.Data.Service <>); Type modelServiceType = genericServiceType.MakeGenericType(modelType); Rock.Data.IService serviceInstance = Activator.CreateInstance(modelServiceType, new object[] { dbContext }) as IService; ParameterExpression paramExpression = Expression.Parameter(entityType, "x"); Expression queryExpression = null; // the base expression we'll use to build our query from // parse markup var parms = ParseMarkup(_markup, context); if (parms.Any(p => p.Key == "id")) { string propertyName = "Id"; List <string> selectionParms = new List <string>(); selectionParms.Add(PropertyComparisonConverstion("==").ToString()); selectionParms.Add(parms["id"].ToString()); selectionParms.Add(propertyName); var entityProperty = entityType.GetProperty(propertyName); queryExpression = ExpressionHelper.PropertyFilterExpression(selectionParms, paramExpression, propertyName, entityProperty.PropertyType); hasFilter = true; } else { // where clause expression if (parms.Any(p => p.Key == "where")) { queryExpression = ParseWhere(parms["where"], entityType, serviceInstance, paramExpression, entityType, entityTypeCache); if (queryExpression != null) { hasFilter = true; } } // dataview expression if (parms.Any(p => p.Key == "dataview")) { var dataViewId = parms["dataview"].AsIntegerOrNull(); if (dataViewId.HasValue) { var dataViewExpression = GetDataViewExpression(dataViewId.Value, serviceInstance, paramExpression, entityTypeCache); if (queryExpression == null) { queryExpression = dataViewExpression; hasFilter = true; } else { queryExpression = Expression.AndAlso(queryExpression, dataViewExpression); } } } // process dynamic filter expressions (from the query string) if (parms.Any(p => p.Key == "dynamicparameters")) { var dynamicFilters = parms["dynamicparameters"].Split(',') .Select(x => x.Trim()) .Where(x => !string.IsNullOrWhiteSpace(x)) .ToList(); foreach (var dynamicFilter in dynamicFilters) { var dynamicFilterValue = HttpContext.Current.Request[dynamicFilter]; var dynamicFilterExpression = GetDynamicFilterExpression(dynamicFilter, dynamicFilterValue, entityType, serviceInstance, paramExpression); if (dynamicFilterExpression != null) { if (queryExpression == null) { queryExpression = dynamicFilterExpression; hasFilter = true; } else { queryExpression = Expression.AndAlso(queryExpression, dynamicFilterExpression); } } } } } // make the query from the expression MethodInfo getMethod = serviceInstance.GetType().GetMethod("Get", new Type[] { typeof(ParameterExpression), typeof(Expression), typeof(Rock.Web.UI.Controls.SortProperty), typeof(int?) }); if (getMethod != null) { var queryResult = getMethod.Invoke(serviceInstance, new object[] { paramExpression, queryExpression, null, null }) as IQueryable <IEntity>; // process entity specific filters switch (_entityName) { case "person": { queryResult = PersonFilters((IQueryable <Person>)queryResult, parms); break; } case "business": { queryResult = BusinessFilters((IQueryable <Person>)queryResult, parms); break; } } // if there was a dynamic expression add it now if (parms.Any(p => p.Key == "expression")) { queryResult = queryResult.Where(parms["expression"]); hasFilter = true; } // get a listing of ids if (parms.Any(p => p.Key == "ids")) { var value = parms["ids"].ToString().Split(',').Select(int.Parse).ToList(); queryResult = queryResult.Where(x => value.Contains(x.Id)); hasFilter = true; } var queryResultExpression = queryResult.Expression; // add sort expressions if (parms.Any(p => p.Key == "sort")) { string orderByMethod = "OrderBy"; foreach (var column in parms["sort"].Split(',').Select(x => x.Trim()).Where(x => !string.IsNullOrWhiteSpace(x)).ToList()) { string propertyName; var direction = SortDirection.Ascending; if (column.EndsWith(" desc", StringComparison.OrdinalIgnoreCase)) { direction = SortDirection.Descending; propertyName = column.Left(column.Length - 5); } else { propertyName = column; } string methodName = direction == SortDirection.Descending ? orderByMethod + "Descending" : orderByMethod; if (entityType.GetProperty(propertyName) != null) { // sorting a entity property var memberExpression = Expression.Property(paramExpression, propertyName); LambdaExpression sortSelector = Expression.Lambda(memberExpression, paramExpression); queryResultExpression = Expression.Call(typeof(Queryable), methodName, new Type[] { queryResult.ElementType, sortSelector.ReturnType }, queryResultExpression, sortSelector); } else { // sorting on an attribute // get attribute id int?attributeId = null; foreach (var id in AttributeCache.GetByEntity(entityTypeCache.Id).SelectMany(a => a.AttributeIds)) { var attribute = AttributeCache.Get(id); if (attribute.Key == propertyName) { attributeId = id; } } if (attributeId.HasValue) { // get AttributeValue queryable and parameter var attributeValues = _rockContext.Set <AttributeValue>(); ParameterExpression attributeValueParameter = Expression.Parameter(typeof(AttributeValue), "v"); MemberExpression idExpression = Expression.Property(paramExpression, "Id"); var attributeExpression = Attribute.Helper.GetAttributeValueExpression(attributeValues, attributeValueParameter, idExpression, attributeId.Value); LambdaExpression sortSelector = Expression.Lambda(attributeExpression, paramExpression); queryResultExpression = Expression.Call(typeof(Queryable), methodName, new Type[] { queryResult.ElementType, sortSelector.ReturnType }, queryResultExpression, sortSelector); } } orderByMethod = "ThenBy"; } } // reassemble the queryable with the sort expressions queryResult = queryResult.Provider.CreateQuery(queryResultExpression) as IQueryable <IEntity>; if (parms.GetValueOrNull("count").AsBoolean()) { int countResult = queryResult.Count(); context.Scopes.Last()["count"] = countResult; } else { // run security check on each result var items = queryResult.ToList(); var itemsSecured = new List <IEntity>(); Person person = GetCurrentPerson(context); foreach (IEntity item in items) { ISecured itemSecured = item as ISecured; if (itemSecured == null || itemSecured.IsAuthorized(Authorization.VIEW, person)) { itemsSecured.Add(item); } } queryResult = itemsSecured.AsQueryable(); // offset if (parms.Any(p => p.Key == "offset")) { queryResult = queryResult.Skip(parms["offset"].AsInteger()); } // limit, default to 1000 if (parms.Any(p => p.Key == "limit")) { queryResult = queryResult.Take(parms["limit"].AsInteger()); } else { queryResult = queryResult.Take(1000); } // check to ensure we had some form of filter (otherwise we'll return all results in the table) if (!hasFilter) { throw new Exception("Your Lava command must contain at least one valid filter. If you configured a filter it's possible that the property or attribute you provided does not exist."); } var resultList = queryResult.ToList(); // if there is only one item to return set an alternative non-array based variable if (resultList.Count == 1) { context.Scopes.Last()[_entityName] = resultList.FirstOrDefault(); } context.Scopes.Last()[parms["iterator"]] = resultList; } } } } else { result.Write(string.Format("Could not find a model for {0}.", _entityName)); base.Render(context, result); } base.Render(context, result); }
/// <summary> /// Searches the specified query. /// </summary> /// <param name="query">The query.</param> /// <param name="searchType">Type of the search.</param> /// <param name="entities">The entities.</param> /// <param name="fieldCriteria">The field criteria.</param> /// <param name="size">The size.</param> /// <param name="from">From.</param> /// <param name="totalResultsAvailable">The total results available.</param> /// <returns></returns> public override List <IndexModelBase> Search(string query, SearchType searchType, List <int> entities, SearchFieldCriteria fieldCriteria, int?size, int?from, out long totalResultsAvailable) { List <IndexModelBase> documents = new List <IndexModelBase>(); totalResultsAvailable = 0; bool allEntities = false; BooleanQuery queryContainer = new BooleanQuery(); List <string> combinedFields = new List <string>(); List <Type> indexModelTypes = new List <Type>(); Dictionary <string, Analyzer> combinedFieldAnalyzers = new Dictionary <string, Analyzer>(); using (RockContext rockContext = new RockContext()) { var entityTypeService = new EntityTypeService(rockContext); if (entities == null || entities.Count == 0) { //add all entities allEntities = true; var selectedEntityTypes = EntityTypeCache.All().Where(e => e.IsIndexingSupported && e.IsIndexingEnabled && e.FriendlyName != "Site"); foreach (var entityTypeCache in selectedEntityTypes) { entities.Add(entityTypeCache.Id); } } foreach (var entityId in entities) { // get entities search model name var entityType = entityTypeService.GetNoTracking(entityId); indexModelTypes.Add(entityType.IndexModelType); // check if this is a person model, if so we need to add two model types one for person and the other for businesses // wish there was a cleaner way to do this if (entityType.Guid == SystemGuid.EntityType.PERSON.AsGuid()) { indexModelTypes.Add(typeof(BusinessIndex)); } } indexModelTypes = indexModelTypes.Distinct().ToList(); CombineIndexTypes(indexModelTypes, out combinedFields, out combinedFieldAnalyzers); if (entities != null && entities.Count != 0 && !allEntities) { var indexModelTypesQuery = new BooleanQuery(); indexModelTypes.ForEach(f => indexModelTypesQuery.Add(new TermQuery(new Term("type", f.Name.ToLower())), Occur.SHOULD)); queryContainer.Add(indexModelTypesQuery, Occur.MUST); } } TopDocs topDocs = null; // Use the analyzer in fieldAnalyzers if that field is in that dictionary, otherwise use StandardAnalyzer. PerFieldAnalyzerWrapper analyzer = new PerFieldAnalyzerWrapper(defaultAnalyzer: new StandardAnalyzer(_matchVersion), fieldAnalyzers: combinedFieldAnalyzers); if (fieldCriteria != null && fieldCriteria.FieldValues?.Count > 0) { Occur occur = fieldCriteria.SearchType == CriteriaSearchType.And ? Occur.MUST : Occur.SHOULD; foreach (var match in fieldCriteria.FieldValues) { BooleanClause booleanClause = new BooleanClause(new TermQuery(new Term(match.Field, match.Value)), occur); booleanClause.Query.Boost = match.Boost; queryContainer.Add(booleanClause); } } switch (searchType) { case SearchType.ExactMatch: { var wordQuery = new BooleanQuery(); if (!string.IsNullOrWhiteSpace(query)) { var words = query.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (var word in words) { var innerQuery = new BooleanQuery(); combinedFields.ForEach(f => innerQuery.Add(new PrefixQuery(new Term(f, word.ToLower())), Occur.SHOULD)); wordQuery.Add(innerQuery, Occur.SHOULD); } } if (wordQuery.Count() != 0) { queryContainer.Add(wordQuery, Occur.MUST); } // special logic to support emails if (query.Contains("@")) { queryContainer.Add(new BooleanClause(new TermQuery(new Term("Email", query)), Occur.SHOULD)); } // special logic to support phone search if (query.IsDigitsOnly()) { queryContainer.Add(new BooleanClause(new WildcardQuery(new Term("PhoneNumbers", "*" + query + "*")), Occur.SHOULD)); } // add a search for all the words as one single search term foreach (var field in combinedFields) { var phraseQuery = new PhraseQuery(); phraseQuery.Add(new Term(field, query.ToLower())); queryContainer.Add(phraseQuery, Occur.SHOULD); } break; } case SearchType.Fuzzy: { foreach (var field in combinedFields) { queryContainer.Add(new FuzzyQuery(new Term(field, query.ToLower())), Occur.SHOULD); } break; } case SearchType.Wildcard: { bool enablePhraseSearch = true; if (!string.IsNullOrWhiteSpace(query)) { BooleanQuery wildcardQuery = new BooleanQuery(); // break each search term into a separate query and add the * to the end of each var queryTerms = query.Split(' ').Select(p => p.Trim()).ToList(); // special logic to support emails if (queryTerms.Count == 1 && query.Contains("@")) { wildcardQuery.Add(new WildcardQuery(new Term("Email", "*" + query.ToLower() + "*")), Occur.SHOULD); enablePhraseSearch = false; } else { foreach (var queryTerm in queryTerms) { if (!string.IsNullOrWhiteSpace(queryTerm)) { var innerQuery = new BooleanQuery(); combinedFields.ForEach(f => innerQuery.Add(new PrefixQuery(new Term(f, queryTerm.ToLower())), Occur.SHOULD)); wildcardQuery.Add(innerQuery, Occur.MUST); } } // add special logic to help boost last names if (queryTerms.Count() > 1 && (indexModelTypes.Contains(typeof(PersonIndex)) || indexModelTypes.Contains(typeof(BusinessIndex)))) { BooleanQuery nameQuery = new BooleanQuery { { new PrefixQuery(new Term("FirstName", queryTerms.First().ToLower())), Occur.MUST }, { new PrefixQuery(new Term("LastName", queryTerms.Last().ToLower())) { Boost = 30 }, Occur.MUST } }; wildcardQuery.Add(nameQuery, Occur.SHOULD); nameQuery = new BooleanQuery { { new PrefixQuery(new Term("NickName", queryTerms.First().ToLower())), Occur.MUST }, { new PrefixQuery(new Term("LastName", queryTerms.Last().ToLower())) { Boost = 30 }, Occur.MUST } }; wildcardQuery.Add(nameQuery, Occur.SHOULD); } // special logic to support phone search if (query.IsDigitsOnly()) { wildcardQuery.Add(new PrefixQuery(new Term("PhoneNumbers", queryTerms.First().ToLower())), Occur.SHOULD); } } queryContainer.Add(wildcardQuery, Occur.MUST); } // add a search for all the words as one single search term if (enablePhraseSearch) { // add a search for all the words as one single search term foreach (var field in combinedFields) { var phraseQuery = new PhraseQuery(); phraseQuery.Add(new Term(field, query.ToLower())); queryContainer.Add(phraseQuery, Occur.SHOULD); } } break; } } int returnSize = 10; if (size.HasValue) { returnSize = size.Value; } OpenReader(); if (from.HasValue) { TopScoreDocCollector collector = TopScoreDocCollector.Create(returnSize * 10, true); // Search for 10 pages with returnSize entries in each page _indexSearcher.Search(queryContainer, collector); topDocs = collector.GetTopDocs(from.Value, returnSize); } else { topDocs = _indexSearcher.Search(queryContainer, returnSize); } totalResultsAvailable = topDocs.TotalHits; if (topDocs != null) { foreach (var hit in topDocs.ScoreDocs) { var document = LuceneDocToIndexModel(queryContainer, hit); if (document != null) { documents.Add(document); } } } return(documents); }