Commit 15e8168d authored by SergeevaAA's avatar SergeevaAA

Реализован поиск файлов по описанию

parent bafa9d6f
namespace FileDesk.Domain.ViewModels
{
using System.ComponentModel;
/// <summary>
/// Вью-модель поиска
/// </summary>
public class SearchViewModel
{
/// <summary>
/// Строка поиска - проверяются вхождения каждого слова
/// </summary>
[DisplayName("Поиск по описанию")]
public virtual string SearchString { get; set; }
/// <summary>
/// Чувствителен ли поиск к регистру
/// </summary>
[DisplayName("Учитывать регистр")]
public virtual bool IsCaseSensitive { get; set; }
}
}
\ No newline at end of file
......@@ -6,6 +6,7 @@
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using FileDesk.DataAccess.Repository;
using FileDesk.Domain.Enums;
......@@ -14,9 +15,8 @@
using Action = Domain.Models.Action;
using File = Domain.Models.File;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
using FileDesk.Services.Helpers;
/// <summary>
/// Сервис для работы с файлами
......@@ -40,10 +40,52 @@
}
/// <summary>
/// Получение списка файлов
/// Поиск файлов
/// </summary>
/// <param name="searchString">Строка поиска</param>
/// <param name="isCaseSensitive">Чувствителен ли к регистру</param>
/// <returns>Список файлов</returns>
public List<FileViewModel> GetAll()
public List<FileViewModel> Search(string searchString, bool isCaseSensitive)
{
var words = Regex.Replace(searchString, @"\s+", " ").Split(" ");
var whereClause = PredicateBuilder.True<FileViewModel>();
if (isCaseSensitive)
{
foreach (var word in words)
{
whereClause = whereClause.And(file => file.Description.Contains(word, StringComparison.InvariantCulture));
}
}
else
{
foreach (var word in words)
{
whereClause = whereClause.And(file => file.Description.Contains(word, StringComparison.InvariantCultureIgnoreCase));
}
}
return _db.Files.Include(f => f.User).OrderByDescending(f => f.UploadDateTime)
.Select(f => new FileViewModel
{
Id = f.Id,
Name = f.Name,
Description = f.Description,
UploadDateTime = f.UploadDateTime,
Size = GetSize(f.Size),
Format = f.Format.ToString(),
FormatDisplayName = f.Format.GetDisplayName(),
User = f.User.Login
})
.Where(whereClause.Compile())
.ToList();
}
/// <summary>
/// Получение списка файлов
/// </summary>
/// <returns>Запрос на получение списка файлов</returns>
public IQueryable<FileViewModel> GetAll()
{
return _db.Files.Include(f => f.User).OrderByDescending(f => f.UploadDateTime)
.Select(f => new FileViewModel
......@@ -56,7 +98,7 @@
Format = f.Format.ToString(),
FormatDisplayName = f.Format.GetDisplayName(),
User = f.User.Login
}).ToList();
});
}
/// <summary>
......@@ -102,7 +144,7 @@
Name = $"{file.Name}{Path.GetExtension(file.File.FileName)}",
Description = file.Description,
Content = GetContent(file.File),
ContentType=file.File.ContentType,
ContentType = file.File.ContentType,
Format = GetFormat(file.File.FileName),
Size = file.File.Length,
Actions = new List<Action>
......
namespace FileDesk.Services.Helpers
{
using System;
using System.Linq;
using System.Linq.Expressions;
/// <summary>
/// Построитель предикатов для LINQ-выражения Where
/// </summary>
public static class PredicateBuilder
{
public static Expression<Func<T, bool>> True<T>() { return f => true; }
public static Expression<Func<T, bool>> False<T>() { return f => false; }
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
}
}
}
\ No newline at end of file
namespace FileDesk.Services.Interfaces
{
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using FileDesk.Domain.ViewModels;
......@@ -10,10 +12,12 @@
public interface IFileService
{
/// <summary>
/// Получение списка файлов
/// Поиск файлов
/// </summary>
/// <param name="searchString">Строка поиска</param>
/// <param name="isCaseSensitive">Чувствителен ли к регистру</param>
/// <returns>Список файлов</returns>
List<FileViewModel> GetAll();
List<FileViewModel> Search(string searchString, bool isCaseSensitive);
/// <summary>
/// Загрузка файла
......
......@@ -8,6 +8,7 @@
using FileDesk.Services.Interfaces;
using FileDesk.Domain.ViewModels;
using System;
/// <summary>
/// Контроллер для работы с файлами
......@@ -38,16 +39,38 @@
#region Методы
/// <summary>
/// Список файлов
/// Главная
/// </summary>
/// <returns></returns>
[Authorize(Roles = "admin, user")]
[HttpGet]
public IActionResult Index()
{
var model = _fileService.GetAll();
return View();
}
return View(model);
/// <summary>
/// Список файлов
/// </summary>
/// <param name="searchString">Строка поиска</param>
/// <param name="isCaseSensitive">Чувствителен ли поиск к регистру</param>
/// <returns>Список файлов</returns>
[Authorize(Roles = "admin, user")]
[HttpGet]
public ActionResult Search(string searchString, bool isCaseSensitive)
{
try
{
var model = _fileService.Search(searchString ?? "", isCaseSensitive);
return PartialView("_Search", model);
}
catch (Exception)
{
// handle exception
}
return PartialView("Error");
}
/// <summary>
......@@ -75,7 +98,7 @@
{
var userId = _userService.GetId(HttpContext.User.Identity.Name);
_fileService.Upload(userId, model);
_fileService.Upload(userId, model);
return RedirectToAction("Index", "File");
}
......
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.IO;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Mvc.Rendering
{
public static class HtmlHelperViewExtensions
{
public static IHtmlContent Action(this IHtmlHelper helper, string action, object parameters = null)
{
var controller = (string)helper.ViewContext.RouteData.Values["controller"];
return Action(helper, action, controller, parameters);
}
public static IHtmlContent Action(this IHtmlHelper helper, string action, string controller, object parameters = null)
{
var area = (string)helper.ViewContext.RouteData.Values["area"];
return Action(helper, action, controller, area, parameters);
}
public static IHtmlContent Action(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)
{
if (action == null)
throw new ArgumentNullException("action");
if (controller == null)
throw new ArgumentNullException("controller");
var task = RenderActionAsync(helper, action, controller, area, parameters);
return task.Result;
}
private static async Task<IHtmlContent> RenderActionAsync(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)
{
// fetching required services for invocation
var serviceProvider = helper.ViewContext.HttpContext.RequestServices;
var actionContextAccessor = helper.ViewContext.HttpContext.RequestServices.GetRequiredService<IActionContextAccessor>();
var httpContextAccessor = helper.ViewContext.HttpContext.RequestServices.GetRequiredService<IHttpContextAccessor>();
var actionSelector = serviceProvider.GetRequiredService<IActionSelector>();
// creating new action invocation context
var routeData = new RouteData();
foreach (var router in helper.ViewContext.RouteData.Routers)
{
routeData.PushState(router, null, null);
}
routeData.PushState(null, new RouteValueDictionary(new { controller = controller, action = action, area = area }), null);
routeData.PushState(null, new RouteValueDictionary(parameters ?? new { }), null);
//get the actiondescriptor
RouteContext routeContext = new RouteContext(helper.ViewContext.HttpContext) { RouteData = routeData };
var candidates = actionSelector.SelectCandidates(routeContext);
var actionDescriptor = actionSelector.SelectBestCandidate(routeContext, candidates);
var originalActionContext = actionContextAccessor.ActionContext;
var originalhttpContext = httpContextAccessor.HttpContext;
try
{
var newHttpContext = serviceProvider.GetRequiredService<IHttpContextFactory>().Create(helper.ViewContext.HttpContext.Features);
if (newHttpContext.Items.ContainsKey(typeof(IUrlHelper)))
{
newHttpContext.Items.Remove(typeof(IUrlHelper));
}
newHttpContext.Response.Body = new MemoryStream();
var actionContext = new ActionContext(newHttpContext, routeData, actionDescriptor);
actionContextAccessor.ActionContext = actionContext;
var invoker = serviceProvider.GetRequiredService<IActionInvokerFactory>().CreateInvoker(actionContext);
await invoker.InvokeAsync();
newHttpContext.Response.Body.Position = 0;
using (var reader = new StreamReader(newHttpContext.Response.Body))
{
return new HtmlString(reader.ReadToEnd());
}
}
catch (Exception ex)
{
return new HtmlString(ex.Message);
}
finally
{
actionContextAccessor.ActionContext = originalActionContext;
httpContextAccessor.HttpContext = originalhttpContext;
if (helper.ViewContext.HttpContext.Items.ContainsKey(typeof(IUrlHelper)))
{
helper.ViewContext.HttpContext.Items.Remove(typeof(IUrlHelper));
}
}
}
}
}
\ No newline at end of file
......@@ -36,6 +36,9 @@ namespace FileDesk.Web
services.AddTransient<IUserService, UserService>();
services.AddTransient<IFileService, FileService>();
services.AddTransient<Microsoft.AspNetCore.Mvc.Infrastructure.IActionContextAccessor, Microsoft.AspNetCore.Mvc.Infrastructure.ActionContextAccessor>();
services.AddTransient<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();
services.AddControllersWithViews();
}
......
@model List<FileDesk.Domain.ViewModels.FileViewModel>
<h2>Все файлы</h2>
<h2>Все файлы</h2>
<a asp-action="Upload" asp-controller="File">Добавить файл</a>
<table class="table">
<tr>
<th></th>
<th>Название</th>
<th>Размер</th>
<th>Дата добавления</th>
<th>Пользователь</th>
<th>Описание</th>
<th>Действия</th>
</tr>
@foreach (var file in Model)
{
<tr>
<td>
<input hidden asp-for="@file.Id" />
<img src="~/images/@String.Format("{0}.png", file.Format)" title="@file.FormatDisplayName" />
</td>
<td><a asp-area="" asp-controller="File" asp-action="Download" asp-route-id="@file.Id" target="_blank">@file.Name</a></td>
<td><input asp-for="@file.Size" /></td>
<td><input asp-for="@file.UploadDateTime" /></td>
<td><input asp-for="@file.User" /></td>
<td><input asp-for="@file.Description" /></td>
<td><button type="submit" class="download" onclick="window.location.href='@Url.Action("Download", "File", new { id = file.Id }, Context.Request.Scheme)'" title="Скачать файл" /></td>
</tr>
}
</table>
<br />
Поиск по описанию:
<input type="search" id="searchString" placeholder="Введите слова для поиска через пробел и нажмите Enter" onsearch="search()" />
<br />
<input type="checkbox" id="isCaseSensitive" /> Учитывать регистр
<div id="search-results">
@Html.Action("Search", "File")
</div>
<style>
img {
height: 30px;
width: 30px;
}
.download {
height: 30px;
width: 30px;
background-image: url(images/Download.png);
background-size: cover;
outline: 0px #ffffff;
}
</style>
\ No newline at end of file
<script>
function search() {
$.get('@Url.Action("Search", "File")', { searchString: $('#searchString').val(), isCaseSensitive: $('#isCaseSensitive')[0].checked }, function (data) {
$('#search-results').replaceWith(data);
});
}
</script>
\ No newline at end of file
@model List<FileDesk.Domain.ViewModels.FileViewModel>
<div id="search-results">
@if (Model != null && Model.Any())
{
<table class="table">
<tr>
<th></th>
<th>Название</th>
<th>Размер</th>
<th>Дата добавления</th>
<th>Пользователь</th>
<th>Описание</th>
<th>Действия</th>
</tr>
@foreach (var file in Model)
{
<tr>
<td>
<input hidden asp-for="@file.Id" />
<img src="~/images/@String.Format("{0}.png", file.Format)" title="@file.FormatDisplayName" />
</td>
<td><a asp-area="" asp-controller="File" asp-action="Download" asp-route-id="@file.Id" target="_blank">@file.Name</a></td>
<td><input asp-for="@file.Size" /></td>
<td><input asp-for="@file.UploadDateTime" /></td>
<td><input asp-for="@file.User" /></td>
<td><input asp-for="@file.Description" /></td>
<td><button type="submit" class="download" onclick="window.location.href='@Url.Action("Download", "File", new { id = file.Id }, Context.Request.Scheme)'" title="Скачать файл" /></td>
</tr>
}
</table>
}
else
{
<br/><h3>Не найдено файлов по запросу.</h3>
}
</div>
<style>
img {
height: 30px;
width: 30px;
}
.download {
height: 30px;
width: 30px;
background-image: url(~/images/Download.png);
background-size: cover;
}
</style>
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment