using System; using System.Collections.Generic; using System.Linq; using System.Web; using Coscine.ApiCommons; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Microsoft.AspNetCore.Authorization; using Coscine.Database.Util; using Microsoft.Extensions.Logging; using Coscine.Database.Models; using Coscine.Logging; namespace Coscine.Api.Project.Controllers { /// <summary> /// This controller represents the actions which can be taken with a search object /// </summary> [Authorize] public class SearchController : Controller { private readonly Authenticator _authenticator; private readonly ProjectRoleModel _projectRoleModel; private readonly CoscineLogger _coscineLogger; private readonly AnalyticsLogObject _analyticsLogObject; /// <summary> /// SearchController specifying a ProjectRoleModel and an AnalyticsObject /// </summary> /// <param name="logger">Logger</param> public SearchController(ILogger<SearchController> logger) { _authenticator = new Authenticator(this, Program.Configuration); _projectRoleModel = new ProjectRoleModel(); _coscineLogger = new CoscineLogger(logger); _analyticsLogObject = new AnalyticsLogObject(); } /// <summary> /// Searchs no filter /// </summary> /// <returns>Ok</returns> [HttpGet("[controller]/allNoFilter/")] public IActionResult SearchNoFilter() { var user = _authenticator.GetUser(); return Ok(GetSearchResults(user.Id, "", "")); } /// <summary> /// Returns results for the search word /// </summary> /// <param name="encodedSearchWord">Encoded search word</param> /// <returns>Ok</returns> [HttpGet("[controller]/all/{encodedSearchWord}")] public IActionResult Search(string encodedSearchWord) { var user = _authenticator.GetUser(); return Ok(GetSearchResults(user.Id, encodedSearchWord, "")); } /// <summary> /// Returns searched projects with no filter /// </summary> /// <param name="projectId">Id of the project</param> /// <returns>Ok</returns> [HttpGet("[controller]/projectNoFilter/{projectId}")] public IActionResult SearchProjectNoFilter(string projectId) { var user = _authenticator.GetUser(); return Ok(GetSearchResults(user.Id, "", projectId)); } /// <summary> /// Returns searched projects /// </summary> /// <param name="projectId">Id of the project</param> /// <param name="encodedSearchWord">Encoded search word</param> /// <returns>Ok</returns> [HttpGet("[controller]/project/{projectId}/{encodedSearchWord}")] public IActionResult SearchProject(string projectId, string encodedSearchWord) { var user = _authenticator.GetUser(); return Ok(GetSearchResults(user.Id, encodedSearchWord, projectId)); } /// <summary> /// This method gets the search results /// </summary> /// <param name="userId">Id of the user</param> /// <param name="encodedSearchWord">Encoded search word</param> /// <param name="projectId">Id of the project</param> /// <returns>Search results</returns> private JToken GetSearchResults(Guid userId, string encodedSearchWord, string projectId) { string searchQuery; if (!string.IsNullOrWhiteSpace(encodedSearchWord)) { searchQuery = HttpUtility.UrlDecode(encodedSearchWord); } else { searchQuery = ""; } List<Guid> list; if (projectId.Equals("")) { list = new List<Guid>(); } else { list = GetAllSubProjects(projectId); } // create return object var json = new JObject(); // search and add results for resources json["Resources"] = SearchForResources(userId, searchQuery, projectId, list); // search and add results for projects if (projectId.Equals("")) { json["Projects"] = SearchForProjects(userId, searchQuery, projectId, list, false); } else { json["Projects"] = new JArray(); } // remove the id of the root project since it cann not be a subproject of it self if (list.Count >= 1) { list.RemoveAt(0); } // search and ad results for sub-projects json["SubProjects"] = SearchForProjects(userId, searchQuery, projectId, list, true); LogAnalytics("View Search Results", userId, projectId, json); return json; } /// <summary> /// This method gets all sub projects /// </summary> /// <param name="projectId">Id of the project</param> /// <returns>List of SubProjects</returns> private List<Guid> GetAllSubProjects(string projectId) { var list = new List<Guid>(); if (!projectId.Equals("")) { list.Add(new Guid(projectId)); var counter = 0; DatabaseConnection.ConnectToDatabase((db) => { while (counter != list.Count) { var innerResults = (from sp in db.SubProjects where sp.ProjectId.Equals(list[counter]) select sp.SubProjectId); list.AddRange(innerResults.ToList()); counter++; } }); } return list; } /// <summary> /// This method searchs projects /// </summary> /// <param name="userId">Id of the user</param> /// <param name="searchQuery">Search query</param> /// <param name="projectId">Id of the project</param> /// <param name="listOfSubprojects">List of subprojects</param> /// <param name="showSubProjects">Shows subprojects</param> /// <returns>Found projects</returns> private JToken SearchForProjects(Guid userId, string searchQuery, string projectId, List<Guid> listOfSubprojects, bool showSubProjects) { return DatabaseConnection.ConnectToDatabase((db) => { var allSubProjects = (from sp in db.SubProjects select sp.SubProjectId).ToList(); var allSubProjectsList = new List<Guid>(); allSubProjectsList.AddRange(allSubProjects); var results = (from p in db.Projects join pr in db.ProjectRoles on p.Id equals pr.ProjectId into joinedPr from jpr in joinedPr.DefaultIfEmpty() join v in db.Visibilities on p.VisibilityId equals v.Id into joinedV from jv in joinedV.DefaultIfEmpty() join pd in db.ProjectDisciplines on p.Id equals pd.ProjectId into joinedPd from jpd in joinedPd.DefaultIfEmpty() join d in db.Disciplines on jpd.DisciplineId equals d.Id into joinedD from jd in joinedD.DefaultIfEmpty() join pi in db.ProjectInstitutes on p.Id equals pi.ProjectId into joinedPi from jpi in joinedPi.DefaultIfEmpty() where p.Deleted == false && ((!showSubProjects && !allSubProjectsList.Contains(p.Id)) || (showSubProjects && allSubProjectsList.Contains(p.Id))) && (jpr.UserId == userId || jv.DisplayName == "Public") && (projectId == "" || listOfSubprojects.Contains(p.Id)) && (searchQuery == "" || p.ProjectName.Contains(searchQuery) || p.Description.Contains(searchQuery) || p.Keywords.Contains(searchQuery) || p.DisplayName.Contains(searchQuery) || p.PrincipleInvestigators.Contains(searchQuery) || p.GrantId.Contains(searchQuery) || jv.DisplayName.Contains(searchQuery) || jd.Url.Contains(searchQuery) || jd.DisplayNameDe.Contains(searchQuery) || jd.DisplayNameEn.Contains(searchQuery)) select new { p.Id, p.DisplayName, p.Slug }).OrderBy(element => element.DisplayName).Distinct(); return JToken.Parse(JsonConvert.SerializeObject(results)); }); } /// <summary> /// This method searchs for resources /// </summary> /// <param name="userId">Id of the user</param> /// <param name="searchQuery">Search Query</param> /// <param name="projectId">Id of the project</param> /// <param name="listOfSubprojects">List of subprojects</param> /// <returns>Found resources by a searchQuery</returns> private JToken SearchForResources(Guid userId, string searchQuery, string projectId, List<Guid> listOfSubprojects) { return DatabaseConnection.ConnectToDatabase((db) => { var results = (from r in db.Resources join pres in db.ProjectResources on r.Id equals pres.ResourceId into joinedPres from jpres in joinedPres.DefaultIfEmpty() join p in db.Projects on jpres.ProjectId equals p.Id into joinedP from jp in joinedP.DefaultIfEmpty() join pr in db.ProjectRoles on jp.Id equals pr.ProjectId into joinedPr from jpr in joinedPr.DefaultIfEmpty() join v in db.Visibilities on r.VisibilityId equals v.Id into joinedV from jv in joinedV.DefaultIfEmpty() join rd in db.ResourceDisciplines on r.Id equals rd.ResourceId into joinedRd from jrd in joinedRd.DefaultIfEmpty() join d in db.Disciplines on jrd.DisciplineId equals d.Id into joinedD from jd in joinedD.DefaultIfEmpty() join l in db.Licenses on r.LicenseId equals l.Id into joinedL from jl in joinedL.DefaultIfEmpty() join rt in db.ResourceTypes on r.TypeId equals rt.Id into joinedRt from jrt in joinedRt.DefaultIfEmpty() where jp.Deleted == false && (jpr.UserId == userId || jv.DisplayName == "Public") && (projectId == "" || listOfSubprojects.Contains(jd.Id)) && (searchQuery == "" || r.ResourceName.Contains(searchQuery) || r.DisplayName.Contains(searchQuery) || r.ResourceName.Contains(searchQuery) || r.Keywords.Contains(searchQuery) || r.UsageRights.Contains(searchQuery) || r.Description.Contains(searchQuery) || r.ApplicationProfile.Contains(searchQuery) || jrt.DisplayName.Contains(searchQuery) || jl.DisplayName.Contains(searchQuery) || jd.DisplayNameDe.Contains(searchQuery) || jd.DisplayNameEn.Contains(searchQuery)) select new { r.Id, r.DisplayName, jpr.ProjectId, jp.Slug }).OrderBy(element => element.DisplayName).Distinct(); return JToken.Parse(JsonConvert.SerializeObject(results)); }); } /// <summary> /// Log Analytics /// </summary> /// <param name="operation">Operation</param> /// <param name="userId">Id of the user</param> /// <param name="projectId">Id of the project</param> /// <param name="json">AnalyticsLogObject</param> private void LogAnalytics(string operation, Guid userId, string projectId, JObject json) { if (CoscineLoggerConfiguration.IsLogLevelActivated(LogType.Analytics)) { List<string> projects = new List<string>(); foreach (var entry in json["Projects"]) { projects.Add(entry["Id"].ToString()); } foreach (var entry in json["SubProjects"]) { projects.Add(entry["Id"].ToString()); } List<string> resources = new List<string>(); foreach (var entry in json["Resources"]) { resources.Add(entry["Id"].ToString()); } _analyticsLogObject.Type = "Action"; _analyticsLogObject.Operation = operation; if (!projectId.Equals("")) { _analyticsLogObject.ProjectId = projectId; _analyticsLogObject.RoleId = _projectRoleModel.GetGetUserRoleForProject(new Guid(_analyticsLogObject.ProjectId), userId).ToString(); } _analyticsLogObject.ProjectList = projects; _analyticsLogObject.ResourceList = resources; _coscineLogger.AnalyticsLog(_analyticsLogObject); } } } }