public ActionResult Search(PagerParametersWithSortFields pagerParameters, PostedTicketSearchViewModel searchModel) { if (!this.IsDisplayAuthorized()) { return(new HttpUnauthorizedResult()); } // A simple solution for the bug of sending page paraemter via querystring, if searchModel has value, with unknown reason, the page will not be set if (pagerParameters != null && pagerParameters.Page == null && !string.IsNullOrEmpty(Request.QueryString["page"])) { int page; if (int.TryParse(Request.QueryString["page"], out page)) { pagerParameters.Page = page; } } if (this.crmContentOwnershipService.IsCurrentUserCustomer()) { searchModel.Unassigned = false; searchModel.Users = new int[] { }; searchModel.IncludeAllVisibleItemsBySelectedGroupsAndUsers = false; } // add default sort field, if it is not provided if (string.IsNullOrEmpty(pagerParameters.SortField)) { pagerParameters.SortField = TicketPart.IdentityFieldName; pagerParameters.Descending = true; } if (searchModel != null) { searchModel.Users = searchModel.Users ?? new int[] { }; searchModel.BusinessUnits = searchModel.BusinessUnits ?? new int[] { }; } if (!string.IsNullOrEmpty(searchModel.Status) && !this.basicDataService.GetStatusRecords().Any(c => c.Id.ToString() == searchModel.Status)) { searchModel.Status = string.Empty; } // full text search will be done by lucene if (string.IsNullOrEmpty(searchModel.Term)) { return(this.SearchByHqlQuery(pagerParameters, searchModel)); } else { return(this.SearchByLucene(pagerParameters, searchModel)); } }
public int CountByIndexProvider(PostedTicketSearchViewModel searchModel) { var builder = this.CreateLuceneBuilder(searchModel); builder.ExactMatch(); try { builder.ExactMatch(); return(builder.Count()); } catch (Exception exception) { Logger.Error(T("Invalid search query: {0}", exception.Message).Text); throw exception; } }
/// <summary> /// Fills the model with the businessUnits that the user has granted to view them /// </summary> private void FillBusinessUnitsAndUsers(SearchTicketsViewModel model, PostedTicketSearchViewModel postedSearchModel) { bool restrictToUserPermissions = !this.services.Authorizer.Authorize(Permissions.AdvancedOperatorPermission); this.businessUnitService.Fill(model.BusinessUnits, restrictToUserPermissions); var selectedBusinessUnits = postedSearchModel.BusinessUnits.ToList(); // TeamIds of the search var teams = new List <int>(); // set checkes of businessUnits model.BusinessUnits .ToList() .ForEach(c => c.Checked = selectedBusinessUnits.Count(d => d == c.BusinessUnitId) > 0); // set checks of teams model.BusinessUnits .SelectMany(c => c.Teams) .ToList() .ForEach(c => c.Checked = teams.Count(d => d == c.TeamId) > 0); IEnumerable <IUser> users = null; if (restrictToUserPermissions) { users = new[] { this.services.WorkContext.CurrentUser }; } else { users = this.basicDataService.GetOperators().ToList(); } foreach (var user in users) { SearchTicketsViewModel.UserViewModel userViewModel = new SearchTicketsViewModel.UserViewModel { Id = user.Id, Username = CRMHelper.GetFullNameOfUser(user.As <UserPart>()), Checked = postedSearchModel.Users.Count(c => c == user.Id) > 0, IsAdminOrOperator = true }; model.Users.Add(userViewModel); } }
public int CountByDatabase(PostedTicketSearchViewModel searchModel) { var query = this.CreateQuery(searchModel); return(query.Count()); }
private ISearchBuilder CreateLuceneBuilder(PostedTicketSearchViewModel searchModel) { List <string> queries = new List <string>(); List <string> fields = new List <string>(); var builder = indexManager.GetSearchIndexProvider().CreateSearchBuilder(TicketController.SearchIndexName); // Ticket type queries.Add("type:\"ticket\""); fields.Add("type"); if (this.crmContentOwnershipService.IsCurrentUserCustomer()) { // filter by items created by current user string userId = this.services.WorkContext.CurrentUser.Id.ToString(CultureInfo.InvariantCulture); string query = string.Format(CultureInfo.InvariantCulture, "{0}:\"{1}\"", TicketPart.RequestingUserFieldName, userId); queries.Add(query); fields.Add(TicketPart.RequestingUserFieldName); } // Term if (!String.IsNullOrWhiteSpace(searchModel.Term)) { string searchText = searchModel.Term.Replace("\"", " "); foreach (var character in searchText.ToArray()) { if (!char.IsLetterOrDigit(character)) { searchText = searchText.Replace(character, ' '); } } searchText = searchText.Trim(); searchModel.Term = searchText; if (!string.IsNullOrEmpty(searchText)) { string query = string.Format(CultureInfo.InvariantCulture, "{0}:\"{1}*\" OR {2}:\"{1}*\" OR {3}:\"{1}*\" OR {4}:{1}", TicketPart.DescriptionFieldName, searchText, TicketPart.TitleFieldName, CRMCommentsPart.CommentsFieldName, TicketPart.IdentityFieldName); queries.Add(query); fields.AddRange(new string[] { TicketPart.DescriptionFieldName, TicketPart.TitleFieldName }); } } // Due date if (!string.IsNullOrEmpty(searchModel.DueDate)) { string maxDueDate = string.Empty; string minDueDate = string.Empty; if (searchModel.DueDate != PostedTicketSearchViewModel.OverDueDate) { minDueDate = "19000101000000"; DateTime maxDateTime; if (DateTime.TryParse(searchModel.DueDate, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out maxDateTime)) { maxDueDate = maxDateTime.ToString("yyyyMMdd", CultureInfo.InvariantCulture) + "235959"; } if (maxDateTime > DateTime.UtcNow.Date) { minDueDate = DateTime.UtcNow.Date.ToString("yyyyMMdd", CultureInfo.InvariantCulture) + "000000"; } } else { maxDueDate = DateTime.UtcNow.Date.ToString("yyyyMMdd", CultureInfo.InvariantCulture) + "235959";; minDueDate = "19000101000000"; } string query = string.Format(CultureInfo.InvariantCulture, "{0}:[{1} TO {2}]", TicketPart.DueDateFieldName, minDueDate, maxDueDate); queries.Add(query); fields.Add(TicketPart.DueDateFieldName); } // Ticket status if (!string.IsNullOrEmpty(searchModel.Status)) { string status = searchModel.Status.Replace("\"", " ").Trim(); if (!string.IsNullOrEmpty(status)) { string query = string.Format(CultureInfo.InvariantCulture, "{0}:{1}", TicketPart.StatusFieldName, status); queries.Add(query); fields.Add(TicketPart.StatusFieldName); } } else if (searchModel.UnStatus) { string query = string.Format(CultureInfo.InvariantCulture, "{0}:{1}", TicketPart.StatusFieldName, TicketPart.NullValueForIntegers); queries.Add(query); fields.Add(TicketPart.StatusFieldName); } // restrict based on the project if (this.projectService.IsTicketsRelatedToProjects()) { var projectIds = this.projectService .GetProjects(null) .AsPart <ProjectPart>() .Select(c => c.Record.Id) .ToList(); if (searchModel.ProjectId.HasValue && projectIds.Any(c => c == searchModel.ProjectId.Value)) { string query = string.Format(CultureInfo.InvariantCulture, "{0}:{1}", AttachToProjectPart.ProjectIdFieldName, searchModel.ProjectId.Value.ToString(CultureInfo.InvariantCulture)); queries.Add(query); fields.Add(AttachToProjectPart.ProjectIdFieldName); } } // Permissions Query if (searchModel.Unassigned) { queries.Add(string.Format( CultureInfo.InvariantCulture, "{0}:\"{1}\"", ContentItemPermissionPart.PermissionsSearchFieldName, ContentItemPermissionPart.EmptyPermissionSearchFieldName )); fields.Add(ContentItemPermissionPart.PermissionsSearchFieldName); } else { string permissionsQuery = this.CreateLucenePermissionQuery(searchModel); if (!string.IsNullOrEmpty(permissionsQuery)) { queries.Add(permissionsQuery); fields.Add(ContentItemPermissionPart.PermissionsSearchFieldName); fields.Add(ContentItemPermissionPart.OwnerSearchFieldName); } } string mainQuery = string.Join(" AND ", queries.Select(c => "(" + c + ")").ToArray()); builder.Parse(fields.ToArray(), mainQuery, false); return(builder); }
private void RestrictListsBasedOnCurrentUserAccess(ref List <int> teams, ref List <int> users, ref List <int> businessUnits, out bool allItemsUserCanSee, PostedTicketSearchViewModel searchModel) { int userId = this.services.WorkContext.CurrentUser.Id; allItemsUserCanSee = false; if (searchModel.Unassigned) { searchModel.Users = new int[] { }; searchModel.BusinessUnits = new int[] { }; users.Clear(); teams.Clear(); businessUnits.Clear(); return; } if (!this.services.Authorizer.Authorize(Permissions.AdvancedOperatorPermission)) { var userBusinessUnits = this.basicDataService .GetBusinessUnitMembers() .Where(c => c.UserPartRecord.Id == userId); var userTeams = this.basicDataService .GetTeamMembers() .Where(c => c.UserPartRecord.Id == userId); // Restrict to teams and businessUnits of the user teams = teams.Where(c => userTeams.Count(d => d.TeamPartRecord.Id == c) > 0).ToList(); businessUnits = businessUnits.Where(c => userBusinessUnits.Count(d => d.BusinessUnitPartRecord.Id == c) > 0).ToList(); users = users.Where(c => c == userId).ToList(); if (teams.Count == 0 && businessUnits.Count == 0 && users.Count == 0) { allItemsUserCanSee = true; users.Add(userId); users = users.Distinct().ToList(); var userTeamIds = userTeams.Select(c => c.TeamPartRecord.Id); teams.AddRange(userTeamIds); teams = teams.Distinct().ToList(); var userBusinessUnitIds = userBusinessUnits.Select(c => c.BusinessUnitPartRecord.Id); businessUnits.AddRange(userBusinessUnitIds); businessUnits = businessUnits.Distinct().ToList(); } } }
public string CreateLucenePermissionQuery(PostedTicketSearchViewModel searchModel) { // TeamIds of the search var teams = new List <int>(); // BusinessUnitIds of the search List <int> businessUnits = searchModel.BusinessUnits != null?searchModel.BusinessUnits.ToList() : new List <int>(); // Ids of the searched users List <int> users = searchModel.Users != null?searchModel.Users.ToList() : new List <int>(); bool allItemsUserCanSee; this.RestrictListsBasedOnCurrentUserAccess(ref teams, ref users, ref businessUnits, out allItemsUserCanSee, searchModel); string searchField = ContentItemPermissionPart.OwnerSearchFieldName; if (!this.services.Authorizer.Authorize(Permissions.AdvancedOperatorPermission) && allItemsUserCanSee) { searchField = ContentItemPermissionPart.PermissionsSearchFieldName; } else { if (teams.Count == 0 && users.Count == 0 && businessUnits.Count == 0) { return(string.Empty); } if (searchModel.IncludeAllVisibleItemsBySelectedGroupsAndUsers) { searchField = ContentItemPermissionPart.PermissionsSearchFieldName; } } // users List <string> searchItems = users.Select(c => string.Format( CultureInfo.InvariantCulture, "{0}:\"U{1}\"", searchField, c.ToString(CultureInfo.InvariantCulture))).ToList(); // business Units if (businessUnits.Count > 0) { var businessUnitSearchTerms = businessUnits.Select(c => string.Format( CultureInfo.InvariantCulture, "{0}:\"B{1}\"", searchField, c.ToString(CultureInfo.InvariantCulture))).ToList(); searchItems = searchItems.Union(businessUnitSearchTerms).ToList(); } // Teams if (teams.Count > 0) { var teamSearchTerms = teams.Select(c => string.Format( CultureInfo.InvariantCulture, "{0}:\"T{1}\"", searchField, c.ToString(CultureInfo.InvariantCulture))).ToList(); searchItems = searchItems.Union(teamSearchTerms).ToList(); } return(string.Join(" OR ", searchItems)); }
public IHqlQuery CreateQuery(PostedTicketSearchViewModel searchModel) { var contentQuery = this.services.ContentManager.HqlQuery().ForVersion(VersionOptions.Published); dynamic state = new JObject(); if (this.crmContentOwnershipService.IsCurrentUserCustomer()) { // filter by items created by current user state.RequestingUser_Id = this.services.WorkContext.CurrentUser.Id.ToString(CultureInfo.InvariantCulture); contentQuery = this.projectionManagerWithDynamicSort.ApplyFilter(contentQuery, "Ticket", "RequestingUser", state); } // Ticket contentType state.ContentTypes = "Ticket"; contentQuery = this.projectionManagerWithDynamicSort.ApplyFilter(contentQuery, "Content", "ContentTypes", state); // RelatedContentItem if (searchModel.RelatedContentItemId.HasValue) { state.RelatedContentItemId = searchModel.RelatedContentItemId.Value.ToString(CultureInfo.InvariantCulture); contentQuery = this.projectionManagerWithDynamicSort.ApplyFilter(contentQuery, TicketFieldsFilter.CategoryName, TicketFieldsFilter.RelatedContentItem, state); } // restrict based on the project if (this.projectService.IsTicketsRelatedToProjects()) { var projectIds = this.projectService .GetProjects(null) .AsPart <ProjectPart>() .Select(c => c.Record.Id) .ToList(); if (searchModel.ProjectId.HasValue && projectIds.Any(c => c == searchModel.ProjectId.Value)) { state.Project_Id = searchModel.ProjectId.Value.ToString(CultureInfo.InvariantCulture); contentQuery = this.projectionManagerWithDynamicSort.ApplyFilter(contentQuery, AttachToProjectFilter.CategoryName, AttachToProjectFilter.IdFilterType, state); } } // Ticket status if (!string.IsNullOrEmpty(searchModel.Status)) { state.Status_Id = searchModel.Status; contentQuery = this.projectionManagerWithDynamicSort.ApplyFilter(contentQuery, "Ticket", "Status", state); } else if (searchModel.UnStatus) { state.UnStatus = true; contentQuery = this.projectionManagerWithDynamicSort.ApplyFilter(contentQuery, "Ticket", "Status", state); } // Due Date if (!string.IsNullOrEmpty(searchModel.DueDate)) { state = new JObject(); if (searchModel.DueDate == PostedTicketSearchViewModel.OverDueDate) { state.MaxDueDate = DateTime.UtcNow.Date; // we are not consider closed overdue items var statusRecords = this.basicDataService.GetStatusRecords().ToList(); var closedStatus = statusRecords.FirstOrDefault(c => c.StatusTypeId == StatusRecord.ClosedStatus); if (searchModel.Status != closedStatus.Id.ToString(CultureInfo.InvariantCulture)) { state.State_Id = closedStatus.Id; contentQuery = this.projectionManagerWithDynamicSort.ApplyFilter(contentQuery, "Ticket", "NotEqualStatus", state); } } else { DateTime maxDateTime; if (DateTime.TryParse(searchModel.DueDate, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out maxDateTime)) { state.MaxDueDate = maxDateTime; } if (maxDateTime > DateTime.UtcNow.Date) { state.MinDueDate = DateTime.UtcNow.Date; } } contentQuery = this.projectionManagerWithDynamicSort.ApplyFilter(contentQuery, "Ticket", "TicketDueDate", state); } // TeamIds of the search var teams = new List <int>(); // BusinessUnitIds of the search List <int> businessUnits = searchModel.BusinessUnits != null?searchModel.BusinessUnits.ToList() : new List <int>(); // Ids of the searched users List <int> users = searchModel.Users != null?searchModel.Users.ToList() : new List <int>(); if (searchModel.Unassigned) { dynamic temp = new JObject(); contentQuery = this.projectionManagerWithDynamicSort.ApplyFilter(contentQuery, ContentItemPermissionFilter.CategoryName, ContentItemPermissionFilter.UnassignedItems, temp); } else { // restrict the list for none admin users if (!this.services.Authorizer.Authorize(Permissions.AdvancedOperatorPermission)) { int? accessType = ContentItemPermissionAccessTypes.Assignee; bool allItemsUserCanSee; this.RestrictListsBasedOnCurrentUserAccess(ref teams, ref users, ref businessUnits, out allItemsUserCanSee, searchModel); // if allItemsUserCanSee then the accessType is useless and the result must include all of the items that user can see if (allItemsUserCanSee) { accessType = null; } dynamic permissionsState = new { Users = users, Teams = teams, BusinessUnits = businessUnits, AccessType = accessType }; contentQuery = this.projectionManagerWithDynamicSort.ApplyFilter(contentQuery, ContentItemPermissionFilter.CategoryName, ContentItemPermissionFilter.AnySelectedUserTeamBusinessUnit, permissionsState); } else if (teams.Count > 0 || businessUnits.Count > 0 || users.Count > 0) { int? accessType = searchModel.IncludeAllVisibleItemsBySelectedGroupsAndUsers ? null : (int?)ContentItemPermissionAccessTypes.Assignee; dynamic permissionsState = new { Users = users, Teams = teams, BusinessUnits = businessUnits, AccessType = accessType }; contentQuery = this.projectionManagerWithDynamicSort.ApplyFilter(contentQuery, ContentItemPermissionFilter.CategoryName, ContentItemPermissionFilter.AnySelectedUserTeamBusinessUnit, permissionsState); } } return(contentQuery); }
public IContent[] SearchByIndexProvider(PagerParametersWithSortFields pagerParameters, PostedTicketSearchViewModel searchModel) { Pager pager = new Pager(siteService.GetSiteSettings(), pagerParameters); var builder = this.CreateLuceneBuilder(searchModel); // apply sort if (!string.IsNullOrEmpty(pagerParameters.SortField)) { string[] integerFields = new string[] { TicketPart.IdentityFieldName, TicketPart.PriorityFieldName, TicketPart.ServiceFieldName, TicketPart.StatusFieldName, TicketPart.TypeFieldName }; switch (pagerParameters.SortField) { case TicketPart.IdentityFieldName: case TicketPart.ServiceFieldName: case TicketPart.TypeFieldName: builder.SortByInteger(pagerParameters.SortField); break; case TicketPart.DueDateFieldName: builder.SortByDateTime(pagerParameters.SortField); break; case TicketPart.PriorityFieldName: case TicketPart.PriorityOrderFieldName: builder.SortByInteger(TicketPart.PriorityOrderFieldName); break; case TicketPart.StatusOrderFieldName: case TicketPart.StatusFieldName: builder.SortByInteger(TicketPart.StatusOrderFieldName); break; default: builder.SortBy(pagerParameters.SortField); break; } if (!pagerParameters.Descending) { builder.Ascending(); } } int[] foundIds = new int[0]; try { builder.ExactMatch(); builder = builder.Slice((pager.Page > 0 ? pager.Page - 1 : 0) * pager.PageSize, pager.PageSize); var searchResults = builder.Search(); foundIds = searchResults.Select(searchHit => searchHit.ContentItemId).ToArray(); } catch (Exception exception) { Logger.Error(T("Invalid search query: {0}", exception.Message).Text); } var includedPartRecords = new[] { "TicketPartRecord", "ContentItemPermissionPartRecord" }; var contentItems = this.services.ContentManager.GetMany <IContent>(foundIds, VersionOptions.Published, new QueryHints().ExpandRecords(includedPartRecords)); return(contentItems.ToArray()); }
public IContent[] SearchByDatabase(PagerParametersWithSortFields pagerParameters, PostedTicketSearchViewModel searchModel) { Pager pager = new Pager(siteService.GetSiteSettings(), pagerParameters); var contentQuery = this.CreateQuery(searchModel); // apply sort if (!string.IsNullOrEmpty(pagerParameters.SortField)) { string type = pagerParameters.SortField ?? TicketPart.IdentityFieldName; string category = "Ticket"; dynamic sortState = new { Sort = !pagerParameters.Descending }; contentQuery = this.projectionManagerWithDynamicSort.AddSortCriterion(category, type, sortState, contentQuery); } contentQuery = contentQuery.Include(new[] { "TicketPartRecord", "ContentItemPermissionPartRecord" }); IEnumerable <ContentItem> contentItems = pager.PageSize == 0 ? contentQuery.List() : contentQuery.Slice((pager.Page > 0 ? pager.Page - 1 : 0) * pager.PageSize, pager.PageSize); return(contentItems.ToArray()); }
private SearchTicketsViewModel CreateSearchModel(IEnumerable <IContent> contentItems, Pager pager, PostedTicketSearchViewModel postedSearchModel, PagerParametersWithSortFields pagerParameters, int totalCount) { SearchTicketsViewModel model = new SearchTicketsViewModel(); model.Term = postedSearchModel.Term; model.IsAdminUser = this.services.Authorizer.Authorize(Permissions.AdvancedOperatorPermission); if (postedSearchModel.DueDate == PostedTicketSearchViewModel.OverDueDate) { model.Overdue = true; } else { model.Overdue = false; DateTime value; if (DateTime.TryParse(postedSearchModel.DueDate, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out value)) { model.DueDate = value; } } model.StatusId = postedSearchModel.Status; model.UnStatus = postedSearchModel.UnStatus; model.PagerParameters = pagerParameters; model.RelatedContentItemId = postedSearchModel.RelatedContentItemId; // related contentItem if (postedSearchModel.RelatedContentItemId.HasValue) { var relatedContentItem = this.contentManager.Get(postedSearchModel.RelatedContentItemId.Value); var titlePart = relatedContentItem.As <TitlePart>(); if (titlePart != null) { model.RelatedContentItemTitle = titlePart.Title; } } model.Pager = this.services.New.Pager(pager).TotalItemCount(totalCount); model.Items = new List <dynamic>(); foreach (var contentItem in contentItems) { // ignore search results which content item has been removed or unpublished if (contentItem == null) { totalCount--; continue; } var itemModel = this.contentManager.BuildDisplay(contentItem, "TableRow"); itemModel.Metadata.Type = "Ticket_TableRow_Container"; model.Items.Add(itemModel); itemModel.IsEditable = this.crmContentOwnershipService.CurrentUserCanEditContent(contentItem); } model.Unassigned = postedSearchModel.Unassigned; this.FillBusinessUnitsAndUsers(model, postedSearchModel); // Projects if (this.projectService.IsTicketsRelatedToProjects()) { model.IsProjectForTicketsSupported = true; model.ProjectId = postedSearchModel.ProjectId; var projects = this.projectService.GetProjects(null); Converter.Fill(model.Projects, projects.AsPart <ProjectPart>()); } if (this.crmContentOwnershipService.IsCurrentUserCustomer()) { model.IsCustomerUser = true; model.Users.Clear(); model.BusinessUnits.ToList().ForEach(c => c.Teams.Clear()); } var statusRecords = this.basicDataService.GetStatusRecords().ToList(); model.ClosedStatusId = statusRecords.First(c => c.StatusTypeId == StatusRecord.ClosedStatus).Id; model.OpenStatusId = statusRecords.First(c => c.StatusTypeId == StatusRecord.OpenStatus).Id; // IncludeAllVisibleItemsBySelectedGroupsAndUsers is meaningful, if there is a selected user or business unit model.IncludeAllVisibleItemsBySelectedGroupsAndUsers = postedSearchModel.IncludeAllVisibleItemsBySelectedGroupsAndUsers && (model.Users.Any(c => c.Checked) || model.BusinessUnits.Any(c => c.Checked) || model.BusinessUnits.SelectMany(c => c.Teams).Any(c => c.Checked)); model.SearchDescription = this.GetSearchDescription(model, statusRecords); return(model); }
private ActionResult SearchByHqlQuery(PagerParametersWithSortFields pagerParameters, PostedTicketSearchViewModel searchModel) { Pager pager = new Pager(siteService.GetSiteSettings(), pagerParameters); int totalCount = this.searchTicketService.CountByDatabase(searchModel); var contentItems = this.searchTicketService.SearchByDatabase(pagerParameters, searchModel); SearchTicketsViewModel model = this.CreateSearchModel(contentItems, pager, searchModel, pagerParameters, totalCount); return(this.View(model)); }
private ActionResult SearchByLucene(PagerParametersWithSortFields pagerParameters, PostedTicketSearchViewModel searchModel) { Pager pager = new Pager(siteService.GetSiteSettings(), pagerParameters); if (!indexManager.HasIndexProvider()) { return(View("NoIndex")); } // Status contentType int totalCount = this.searchTicketService.CountByIndexProvider(searchModel); var contentItems = this.searchTicketService.SearchByIndexProvider(pagerParameters, searchModel); SearchTicketsViewModel model = this.CreateSearchModel(contentItems, pager, searchModel, pagerParameters, totalCount); return(this.View(model)); }