/* * * (c) Copyright Ascensio System Limited 2010-2018 * * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. * * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html * * You can contact Ascensio System SIA by email at sales@onlyoffice.com * * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. * * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains * relevant author attributions when distributing the software. If the display of the logo in its graphic * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" * in every copy of the program you distribute. * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. * */ using System; using System.Collections.Generic; using System.Linq; using ASC.Api.Core; using ASC.Api.CRM; using ASC.Common.Web; using ASC.Core; using ASC.Core.Users; using ASC.CRM.ApiModels; using ASC.CRM.Core; using ASC.CRM.Core.Dao; using ASC.CRM.Core.Entities; using ASC.CRM.Core.Enums; using ASC.ElasticSearch; using ASC.MessagingSystem; using ASC.Web.Api.Routing; using ASC.Web.Core.Users; using ASC.Web.CRM.Services.NotifyService; using ASC.Web.Files.Services.WCFService; using AutoMapper; using Microsoft.AspNetCore.Mvc; namespace ASC.CRM.Api { public class CasesController : BaseApiController { private readonly DisplayUserSettingsHelper _displayUserSettingsHelper; private readonly SecurityContext _securityContext; private readonly NotifyClient _notifyClient; private readonly ApiContext _apiContext; private readonly MessageService _messageService; private readonly MessageTarget _messageTarget; private readonly UserManager _userManager; public CasesController(CrmSecurity crmSecurity, DaoFactory daoFactory, ApiContext apiContext, MessageTarget messageTarget, MessageService messageService, NotifyClient notifyClient, SecurityContext securityContext, DisplayUserSettingsHelper displayUserSettingsHelper, UserManager userManager, IMapper mapper) : base(daoFactory, crmSecurity, mapper) { _apiContext = apiContext; _messageTarget = messageTarget; _messageService = messageService; _notifyClient = notifyClient; _securityContext = securityContext; _displayUserSettingsHelper = displayUserSettingsHelper; _userManager = userManager; _mapper = mapper; } /// /// Close the case with the ID specified in the request /// /// Close case /// Cases /// Case ID /// /// /// /// Case /// [Update(@"case/{caseid:int}/close")] public CasesDto CloseCases(int caseid) { if (caseid <= 0) throw new ArgumentException(); var cases = _daoFactory.GetCasesDao().CloseCases(caseid); if (cases == null) throw new ItemNotFoundException(); _messageService.Send(MessageAction.CaseClosed, _messageTarget.Create(cases.ID), cases.Title); return _mapper.Map(cases); } /// /// Resume the case with the ID specified in the request /// /// Resume case /// Cases /// Case ID /// /// /// /// Case /// [Update(@"case/{caseid:int}/reopen")] public CasesDto ReOpenCases(int caseid) { if (caseid <= 0) throw new ArgumentException(); var cases = _daoFactory.GetCasesDao().ReOpenCases(caseid); if (cases == null) throw new ItemNotFoundException(); _messageService.Send(MessageAction.CaseOpened, _messageTarget.Create(cases.ID), cases.Title); return _mapper.Map(cases); } /// /// Creates the case with the parameters specified in the request /// /// Create case /// Case title /// Participants /// User field list /// Case privacy: private or not /// List of users with access to the case /// Notify users in accessList about the case /// Case /// Cases /// /// /// /// [Create(@"case")] public CasesDto CreateCases([FromForm] CreateOrUpdateCasesRequestDto inDto) { var title = inDto.Title; var customFieldList = inDto.CustomFieldList; var isPrivate = inDto.isPrivate; var isNotify = inDto.isNotify; var members = inDto.Members; var accessList = inDto.accessList; if (string.IsNullOrEmpty(title)) throw new ArgumentException(); var casesID = _daoFactory.GetCasesDao().CreateCases(title); var cases = new Cases { ID = casesID, Title = title, CreateBy = _securityContext.CurrentAccount.ID, CreateOn = DateTime.UtcNow }; SetAccessToCases(cases, isPrivate, accessList, isNotify, false); var membersList = members != null ? members.ToList() : new List(); if (membersList.Any()) { var contacts = _daoFactory.GetContactDao().GetContacts(membersList.ToArray()).Where(_crmSecurity.CanAccessTo).ToList(); membersList = contacts.Select(m => m.ID).ToList(); _daoFactory.GetCasesDao().SetMembers(cases.ID, membersList.ToArray()); } if (customFieldList != null) { var existingCustomFieldList = _daoFactory.GetCustomFieldDao().GetFieldsDescription(EntityType.Case).Select(fd => fd.ID).ToList(); foreach (var field in customFieldList) { if (string.IsNullOrEmpty(field.Value) || !existingCustomFieldList.Contains(field.Key)) continue; _daoFactory.GetCustomFieldDao().SetFieldValue(EntityType.Case, cases.ID, field.Key, field.Value); } } var casesToResult = _daoFactory.GetCasesDao().GetByID(casesID); return _mapper.Map(casesToResult); } /// /// Updates the selected case with the parameters specified in the request /// /// Update case /// Case ID /// Case title /// Participants /// User field list /// Case privacy: private or not /// List of users with access to the case /// Notify users in accessList about the case /// Cases /// Case /// /// /// /// /// [Update(@"case/{caseid:int}")] public CasesDto UpdateCases(int caseid, [FromForm] CreateOrUpdateCasesRequestDto inDto) { var title = inDto.Title; var isPrivate = inDto.isPrivate; var isNotify = inDto.isNotify; var accessList = inDto.accessList; var members = inDto.Members; var customFieldList = inDto.CustomFieldList; if ((caseid <= 0) || (string.IsNullOrEmpty(title))) throw new ArgumentException(); var cases = _daoFactory.GetCasesDao().GetByID(caseid); if (cases == null) throw new ItemNotFoundException(); cases.Title = title; _daoFactory.GetCasesDao().UpdateCases(cases); if (_crmSecurity.IsAdmin || cases.CreateBy == _securityContext.CurrentAccount.ID) { SetAccessToCases(cases, isPrivate, accessList, isNotify, false); } var membersList = members != null ? members.ToList() : new List(); if (membersList.Any()) { var contacts = _daoFactory.GetContactDao().GetContacts(membersList.ToArray()).Where(_crmSecurity.CanAccessTo).ToList(); membersList = contacts.Select(m => m.ID).ToList(); _daoFactory.GetCasesDao().SetMembers(cases.ID, membersList.ToArray()); } if (customFieldList != null) { var existingCustomFieldList = _daoFactory.GetCustomFieldDao().GetFieldsDescription(EntityType.Case).Select(fd => fd.ID).ToList(); foreach (var field in customFieldList) { if (string.IsNullOrEmpty(field.Value) || !existingCustomFieldList.Contains(field.Key)) continue; _daoFactory.GetCustomFieldDao().SetFieldValue(EntityType.Case, cases.ID, field.Key, field.Value); } } return _mapper.Map(cases); } /// /// Sets access rights for the selected case with the parameters specified in the request /// /// Case ID /// Case privacy: private or not /// List of users with access to the case /// Set rights to case /// Cases /// /// /// /// Case /// [Update(@"case/{caseid:int}/access")] public CasesDto SetAccessToCases(int caseid, bool isPrivate, IEnumerable accessList) { if (caseid <= 0) throw new ArgumentException(); var cases = _daoFactory.GetCasesDao().GetByID(caseid); if (cases == null) throw new ItemNotFoundException(); if (!(_crmSecurity.IsAdmin || cases.CreateBy == _securityContext.CurrentAccount.ID)) throw _crmSecurity.CreateSecurityException(); return SetAccessToCases(cases, isPrivate, accessList, false, true); } private CasesDto SetAccessToCases(Cases cases, bool isPrivate, IEnumerable accessList, bool isNotify, bool isMessageServicSende) { var accessListLocal = accessList != null ? accessList.Distinct().ToList() : new List(); if (isPrivate && accessListLocal.Any()) { if (isNotify) { accessListLocal = accessListLocal.Where(u => u != _securityContext.CurrentAccount.ID).ToList(); _notifyClient.SendAboutSetAccess(EntityType.Case, cases.ID, _daoFactory, accessListLocal.ToArray()); } if (!accessListLocal.Contains(_securityContext.CurrentAccount.ID)) { accessListLocal.Add(_securityContext.CurrentAccount.ID); } _crmSecurity.SetAccessTo(cases, accessListLocal); if (isMessageServicSende) { var users = GetUsersByIdList(accessListLocal); _messageService.Send(MessageAction.CaseRestrictedAccess, _messageTarget.Create(cases.ID), cases.Title, users.Select(x => x.DisplayUserName(false, _displayUserSettingsHelper))); } } else { _crmSecurity.MakePublic(cases); if (isMessageServicSende) { _messageService.Send(MessageAction.CaseOpenedAccess, _messageTarget.Create(cases.ID), cases.Title); } } return _mapper.Map(cases); } /// /// Sets access rights for other users to the list of cases with the IDs specified in the request /// /// Case ID list /// Case privacy: private or not /// List of users with access /// Set case access rights /// Cases /// /// /// /// Case list /// [Update(@"case/access")] public IEnumerable SetAccessToBatchCases([FromForm] SetAccessToBatchCasesRequestDto inDto) { var casesid = inDto.CasesId; var isPrivate = inDto.isPrivate; var accessList = inDto.AccessList; var result = new List(); var cases = _daoFactory.GetCasesDao().GetCases(casesid); if (!cases.Any()) return new List(); foreach (var c in cases) { if (c == null) throw new ItemNotFoundException(); if (!(_crmSecurity.IsAdmin || c.CreateBy == _securityContext.CurrentAccount.ID)) continue; SetAccessToCases(c, isPrivate, accessList, false, true); result.Add(c); } return ToListCasesDtos(result); } /// /// Sets access rights for other users to the list of all cases matching the parameters specified in the request /// /// Contact ID /// Case status /// Tags /// Case privacy: private or not /// List of users with access /// Set case access rights /// Cases /// /// /// /// Case list /// [Update(@"case/filter/access")] public IEnumerable SetAccessToBatchCases([FromForm] SetAccessToBatchCasesByFilterInDto inDto) { int contactid = inDto.Contactid; bool? isClosed = inDto.isClosed; IEnumerable tags = inDto.Tags; bool isPrivate = inDto.isPrivate; IEnumerable accessList = inDto.AccessList; var result = new List(); var caseses = _daoFactory.GetCasesDao().GetCases(_apiContext.FilterValue, contactid, isClosed, tags, 0, 0, null); if (!caseses.Any()) return new List(); foreach (var casese in caseses) { if (casese == null) throw new ItemNotFoundException(); if (!(_crmSecurity.IsAdmin || casese.CreateBy == _securityContext.CurrentAccount.ID)) continue; SetAccessToCases(casese, isPrivate, accessList, false, true); result.Add(casese); } return ToListCasesDtos(result); } /// /// Returns the detailed information about the case with the ID specified in the request /// /// Get case by ID /// Cases /// Case ID /// /// [Read(@"case/{caseid:int}")] public CasesDto GetCaseByID(int caseid) { if (caseid <= 0) throw new ItemNotFoundException(); var cases = _daoFactory.GetCasesDao().GetByID(caseid); if (cases == null || !_crmSecurity.CanAccessTo(cases)) throw new ItemNotFoundException(); return _mapper.Map(cases); } /// /// Returns the list of all cases matching the parameters specified in the request /// /// Get case list /// Contact ID /// Case status /// Tags /// Cases /// /// Case list /// [Read(@"case/filter")] public IEnumerable GetCases(int contactid, bool? isClosed, IEnumerable tags) { IEnumerable result; SortedByType sortBy; OrderBy casesOrderBy; var searchString = _apiContext.FilterValue; if (ASC.CRM.Classes.EnumExtension.TryParse(_apiContext.SortBy, true, out sortBy)) { casesOrderBy = new OrderBy(sortBy, !_apiContext.SortDescending); } else if (string.IsNullOrEmpty(_apiContext.SortBy)) { casesOrderBy = new OrderBy(SortedByType.Title, true); } else { casesOrderBy = null; } var fromIndex = (int)_apiContext.StartIndex; var count = (int)_apiContext.Count; if (casesOrderBy != null) { result = ToListCasesDtos( _daoFactory .GetCasesDao() .GetCases( searchString, contactid, isClosed, tags, fromIndex, count, casesOrderBy)).ToList(); _apiContext.SetDataPaginated(); _apiContext.SetDataFiltered(); _apiContext.SetDataSorted(); } else { result = ToListCasesDtos( _daoFactory .GetCasesDao() .GetCases( searchString, contactid, isClosed, tags, 0, 0, null)).ToList(); } int totalCount; if (result.Count() < count) { totalCount = fromIndex + result.Count(); } else { totalCount = _daoFactory.GetCasesDao().GetCasesCount(searchString, contactid, isClosed, tags); } _apiContext.SetTotalCount(totalCount); return result; } /// /// Deletes the case with the ID specified in the request /// /// Delete case /// Case ID /// Cases /// /// /// /// Case /// [Delete(@"case/{caseid:int}")] public CasesDto DeleteCase(int caseid) { if (caseid <= 0) throw new ArgumentException(); var cases = _daoFactory.GetCasesDao().DeleteCases(caseid); if (cases == null) throw new ItemNotFoundException(); _messageService.Send(MessageAction.CaseDeleted, _messageTarget.Create(cases.ID), cases.Title); return _mapper.Map(cases); } /// /// Deletes the group of cases with the IDs specified in the request /// /// Case ID list /// /// /// Delete case group /// Cases /// /// Case list /// [Update(@"case")] public IEnumerable DeleteBatchCases([FromForm] IEnumerable casesids) { if (casesids == null) throw new ArgumentException(); casesids = casesids.Distinct(); var caseses = _daoFactory.GetCasesDao().DeleteBatchCases(casesids.ToArray()); if (caseses == null || !caseses.Any()) return new List(); _messageService.Send(MessageAction.CasesDeleted, _messageTarget.Create(casesids), caseses.Select(c => c.Title)); return ToListCasesDtos(caseses); } /// /// Deletes the list of all cases matching the parameters specified in the request /// /// Contact ID /// Case status /// Tags /// /// /// Delete case group /// Cases /// /// Case list /// [Delete(@"case/filter")] public IEnumerable DeleteBatchCases(int contactid, bool? isClosed, IEnumerable tags) { var caseses = _daoFactory.GetCasesDao().GetCases(_apiContext.FilterValue, contactid, isClosed, tags, 0, 0, null); if (!caseses.Any()) return new List(); caseses = _daoFactory.GetCasesDao().DeleteBatchCases(caseses); _messageService.Send(MessageAction.CasesDeleted, _messageTarget.Create(caseses.Select(c => c.ID)), caseses.Select(c => c.Title)); return ToListCasesDtos(caseses); } /// /// Returns the list of all contacts associated with the case with the ID specified in the request /// /// Get all case contacts /// Case ID /// Cases /// Contact list /// [Read(@"case/{caseid:int}/contact")] public IEnumerable GetCasesMembers(int caseid) { var contactIDs = _daoFactory.GetCasesDao().GetMembers(caseid); var contacts = _daoFactory.GetContactDao().GetContacts(contactIDs); return contactIDs == null ? new ItemList() : _mapper.Map, List>(contacts); } /// /// Adds the selected contact to the case with the ID specified in the request /// /// Add case contact /// Cases /// Case ID /// Contact ID /// /// /// /// Participant /// [Create(@"case/{caseid:int}/contact")] public ContactDto AddMemberToCases([FromRoute] int caseid,[FromForm] int contactid) { if ((caseid <= 0) || (contactid <= 0)) throw new ArgumentException(); var cases = _daoFactory.GetCasesDao().GetByID(caseid); if (cases == null || !_crmSecurity.CanAccessTo(cases)) throw new ItemNotFoundException(); var contact = _daoFactory.GetContactDao().GetByID(contactid); if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException(); _daoFactory.GetCasesDao().AddMember(caseid, contactid); var messageAction = contact is Company ? MessageAction.CaseLinkedCompany : MessageAction.CaseLinkedPerson; _messageService.Send(messageAction, _messageTarget.Create(cases.ID), cases.Title, contact.GetTitle()); return _mapper.Map(contact); } /// /// Delete the selected contact from the case with the ID specified in the request /// /// Delete case contact /// Cases /// Case ID /// Contact ID /// /// /// /// Participant /// [Delete(@"case/{caseid:int}/contact/{contactid:int}")] public ContactDto DeleteMemberFromCases(int caseid, int contactid) { if ((caseid <= 0) || (contactid <= 0)) throw new ArgumentException(); var cases = _daoFactory.GetCasesDao().GetByID(caseid); if (cases == null || !_crmSecurity.CanAccessTo(cases)) throw new ItemNotFoundException(); var contact = _daoFactory.GetContactDao().GetByID(contactid); if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException(); var result = _mapper.Map(contact); _daoFactory.GetCasesDao().RemoveMember(caseid, contactid); var messageAction = contact is Company ? MessageAction.CaseUnlinkedCompany : MessageAction.CaseUnlinkedPerson; _messageService.Send(messageAction, _messageTarget.Create(cases.ID), cases.Title, contact.GetTitle()); return result; } /// /// Returns the list of 30 cases in the CRM module with prefix /// /// /// /// Cases /// /// Cases list /// /// false [Read(@"case/byprefix")] public IEnumerable GetCasesByPrefix(string prefix, int contactID) { var result = new List(); if (contactID > 0) { var findedCases = _daoFactory.GetCasesDao().GetCases(string.Empty, contactID, null, null, 0, 0, null); foreach (var item in findedCases) { if (item.Title.IndexOf(prefix, StringComparison.Ordinal) != -1) { result.Add(_mapper.Map(item)); } } _apiContext.SetTotalCount(findedCases.Count); } else { const int maxItemCount = 30; var findedCases = _daoFactory.GetCasesDao().GetCasesByPrefix(prefix, 0, maxItemCount); foreach (var item in findedCases) { result.Add(_mapper.Map(item)); } } return result; } private IEnumerable ToListCasesDtos(ICollection items) { if (items == null || items.Count == 0) return new List(); var result = new List(); var contactIDs = new List(); var casesIDs = items.Select(item => item.ID).ToArray(); var customFields = _daoFactory.GetCustomFieldDao() .GetEnityFields(EntityType.Case, casesIDs) .GroupBy(item => item.EntityID) .ToDictionary(item => item.Key, item => item.Select(x => _mapper.Map(x))); var casesMembers = _daoFactory.GetCasesDao().GetMembers(casesIDs); foreach (var value in casesMembers.Values) { contactIDs.AddRange(value); } var contacts = _daoFactory .GetContactDao() .GetContacts(contactIDs.Distinct().ToArray()) .ToDictionary(item => item.ID, x => _mapper.Map(x)); foreach (var cases in items) { var casesDto = _mapper.Map(cases); casesDto.CustomFields = customFields.ContainsKey(cases.ID) ? customFields[cases.ID] : new List(); casesDto.Members = casesMembers.ContainsKey(cases.ID) ? casesMembers[cases.ID].Where(contacts.ContainsKey).Select(item => contacts[item]) : new List(); result.Add(casesDto); } return result; } private IEnumerable GetUsersByIdList(IEnumerable ids) { return _userManager.GetUsers().Where(x => ids.Contains(x.ID)); } } }