public void SendAboutAddRelationshipEventAdd(RelationshipEvent entity, Hashtable fileListInfoHashtable, params Guid[] userID) { if (userID.Length == 0) { return; } NameValueCollection baseEntityData; if (entity.EntityID != 0) { baseEntityData = ExtractBaseDataFrom(entity.EntityType, entity.EntityID); } else { var contact = Global.DaoFactory.GetContactDao().GetByID(entity.ContactID); baseEntityData = new NameValueCollection(); baseEntityData["title"] = contact.GetTitle(); baseEntityData["id"] = contact.ID.ToString(); baseEntityData["entityRelativeURL"] = "default.aspx?id=" + contact.ID; if (contact is Person) { baseEntityData["entityRelativeURL"] += "&type=people"; } baseEntityData["entityRelativeURL"] = String.Concat(PathProvider.BaseAbsolutePath, baseEntityData["entityRelativeURL"]); } client.BeginSingleRecipientEvent("send about add relationship event add"); var interceptor = new InitiatorInterceptor(new DirectRecipient(ASC.Core.SecurityContext.CurrentAccount.ID.ToString(), "")); client.AddInterceptor(interceptor); try { client.SendNoticeToAsync( NotifyConstants.Event_AddRelationshipEvent, null, userID.Select(item => ToRecipient(item)).ToArray(), true, new TagValue(NotifyConstants.Tag_EntityTitle, baseEntityData["title"]), new TagValue(NotifyConstants.Tag_EntityID, baseEntityData["id"]), new TagValue(NotifyConstants.Tag_EntityRelativeURL, baseEntityData["entityRelativeURL"]), new TagValue(NotifyConstants.Tag_AdditionalData, new Hashtable { { "Files", fileListInfoHashtable }, { "EventContent", entity.Content } })); } finally { client.RemoveInterceptor(interceptor.Name); client.EndSingleRecipientEvent("send about add relationship event add"); } }
public static void DemandAccessTo(RelationshipEvent relationshipEvent) { if (!CanAccessTo(relationshipEvent)) { throw CreateSecurityException(); } }
public RelationshipEvent AttachFiles(int contactID, EntityType entityType, int entityID, int[] fileIDs) { if (entityID > 0 && !_supportedEntityType.Contains(entityType)) { throw new ArgumentException(); } var relationshipEvent = new RelationshipEvent { CategoryID = (int)HistoryCategorySystem.FilesUpload, ContactID = contactID, EntityType = entityType, EntityID = entityID, Content = HistoryCategorySystem.FilesUpload.ToLocalizedString() }; var eventID = CreateItem(relationshipEvent).ID; AttachFiles(eventID, fileIDs); relationshipEvent.ID = eventID; return(relationshipEvent); }
public void EditItem(RelationshipEvent item) { if (String.IsNullOrEmpty(item.Content) || item.CategoryID == 0 || (item.ContactID == 0 & item.EntityID == 0)) { throw new ArgumentException(); } if (item.EntityID > 0 && item.EntityType != EntityType.Opportunity && item.EntityType != EntityType.Case) { throw new ArgumentException(); } CRMSecurity.DemandEdit(item); using (var db = GetDb()) { db.ExecuteNonQuery(Update("crm_relationship_event") .Set("contact_id", item.ContactID) .Set("content", item.Content) .Set("entity_type", (int)item.EntityType) .Set("entity_id", item.EntityID) .Set("category_id", item.CategoryID) .Set("last_modifed_on", DateTime.UtcNow) .Set("last_modifed_by", ASC.Core.SecurityContext.CurrentAccount.ID) ); } }
private RelationshipEventWrapper ToRelationshipEventWrapper(RelationshipEvent relationshipEvent) { var result = new RelationshipEventWrapper(relationshipEvent); var historyCategory = DaoFactory.ListItemDao.GetByID(relationshipEvent.CategoryID); if (historyCategory != null) { result.Category = new HistoryCategoryBaseWrapper(historyCategory); } if (relationshipEvent.EntityID > 0) { result.Entity = ToEntityWrapper(relationshipEvent.EntityType, relationshipEvent.EntityID); } result.Files = DaoFactory.RelationshipEventDao.GetFiles(relationshipEvent.ID).ConvertAll(file => new FileWrapper(file)); if (relationshipEvent.ContactID > 0) { var relativeContact = DaoFactory.ContactDao.GetByID(relationshipEvent.ContactID); if (relativeContact != null) { result.Contact = ToContactBaseWrapper(relativeContact); } } result.CanEdit = CRMSecurity.CanAccessTo(relationshipEvent); return(result); }
public void DemandEdit(RelationshipEvent relationshipEvent) { if (!CanEdit(relationshipEvent)) { throw CreateSecurityException(); } }
public static void DemandDelete(RelationshipEvent relationshipEvent) { if (!CanDelete(relationshipEvent)) { throw CreateSecurityException(); } }
public static bool CanEdit(RelationshipEvent relationshipEvent) { var userId = SecurityContext.CurrentAccount.ID; if (IsAdmin) { return(true); } using (var scope = DIHelper.Resolve()) { var daoFactory = scope.Resolve <DaoFactory>(); if (relationshipEvent.ContactID > 0) { var contactObj = daoFactory.ContactDao.GetByID(relationshipEvent.ContactID); if (contactObj != null) { if (CanEdit(contactObj)) { return(true); } return(CanAccessTo(contactObj, userId) && relationshipEvent.CreateBy == userId); } } if (relationshipEvent.EntityType == EntityType.Case) { var caseObj = daoFactory.CasesDao.GetByID(relationshipEvent.EntityID); if (caseObj != null) { if (CanEdit(caseObj)) { return(true); } return(CanAccessTo(caseObj, userId) && relationshipEvent.CreateBy == userId); } } if (relationshipEvent.EntityType == EntityType.Opportunity) { var dealObj = daoFactory.DealDao.GetByID(relationshipEvent.EntityID); if (dealObj != null) { if (CanEdit(dealObj)) { return(true); } return(CanAccessTo(dealObj, userId) && relationshipEvent.CreateBy == userId); } } return(false); } }
public RelationshipEventWrapper(RelationshipEvent relationshipEvent) : base(relationshipEvent.ID) { CreateBy = EmployeeWraper.Get(relationshipEvent.CreateBy); Created = (ApiDateTime)relationshipEvent.CreateOn; Content = relationshipEvent.Content; Files = new List <FileWrapper>(); CanEdit = CRMSecurity.CanEdit(relationshipEvent); }
public bool CanEdit(RelationshipEvent relationshipEvent) { var userId = _securityContext.CurrentAccount.ID; if (IsAdmin) { return(true); } if (relationshipEvent.ContactID > 0) { var contactObj = _daoFactory.GetContactDao().GetByID(relationshipEvent.ContactID); if (contactObj != null) { if (CanEdit(contactObj)) { return(true); } return(CanAccessTo(contactObj, userId) && relationshipEvent.CreateBy == userId); } } if (relationshipEvent.EntityType == EntityType.Case) { var caseObj = _daoFactory.GetCasesDao().GetByID(relationshipEvent.EntityID); if (caseObj != null) { if (CanEdit(caseObj)) { return(true); } return(CanAccessTo(caseObj, userId) && relationshipEvent.CreateBy == userId); } } if (relationshipEvent.EntityType == EntityType.Opportunity) { var dealObj = _daoFactory.GetDealDao().GetByID(relationshipEvent.EntityID); if (dealObj != null) { if (CanEdit(dealObj)) { return(true); } return(CanAccessTo(dealObj, userId) && relationshipEvent.CreateBy == userId); } } return(false); }
private void AddToHistory(int contactID, String content) { if (contactID == 0 || String.IsNullOrEmpty(content)) { return; } var historyEvent = new RelationshipEvent(); historyEvent.ContactID = contactID; historyEvent.Content = content; historyEvent.CreateBy = _currentUserID; historyEvent.CreateOn = TenantUtil.DateTimeNow(); if (historyCategory == 0) { using (var listItemDao = _daoFactory.GetListItemDao()) { // HACK var listItem = listItemDao.GetItems(ListType.HistoryCategory).Find( item => item.AdditionalParams == "event_category_email.png"); if (listItem == null) { listItemDao.CreateItem(ListType.HistoryCategory, new ListItem { AdditionalParams = "event_category_email.png", Title = CRMCommonResource.HistoryCategory_Note }); } // historyCategory = listItem.ID; } } historyEvent.CategoryID = historyCategory; using (var relationshipEventDao = _daoFactory.GetRelationshipEventDao()) { historyEvent = relationshipEventDao.CreateItem(historyEvent); if (historyEvent.ID > 0 && _fileID != null && _fileID.Count > 0) { relationshipEventDao.AttachFiles(historyEvent.ID, _fileID.ToArray()); } } }
public void DeleteItem(RelationshipEvent item) { CRMSecurity.DemandDelete(item); var relativeFiles = GetFiles(item.ID); var nowFilesEditing = relativeFiles.Where(file => (file.FileStatus & FileStatus.IsEditing) == FileStatus.IsEditing); if (nowFilesEditing.Count() != 0) { throw new ArgumentException(); } relativeFiles.ForEach(f => RemoveFile(f)); Db.ExecuteNonQuery(Delete("crm_relationship_event").Where(Exp.Eq("id", item.ID))); }
public static void DemandCreateOrUpdate(RelationshipEvent relationshipEvent) { if (String.IsNullOrEmpty(relationshipEvent.Content) || relationshipEvent.CategoryID == 0 || (relationshipEvent.ContactID == 0 && relationshipEvent.EntityID == 0)) { throw new ArgumentException(); } if (relationshipEvent.EntityID > 0 && relationshipEvent.EntityType != EntityType.Opportunity && relationshipEvent.EntityType != EntityType.Case) { throw new ArgumentException(); } if (!CanAccessTo(relationshipEvent)) { throw CreateSecurityException(); } }
public static bool CanAccessTo(RelationshipEvent relationshipEvent, Guid userId) { if (IsAdministrator(userId)) { return(true); } using (var scope = DIHelper.Resolve()) { var daoFactory = scope.Resolve <DaoFactory>(); if (relationshipEvent.ContactID > 0) { var contactObj = daoFactory.ContactDao.GetByID(relationshipEvent.ContactID); if (contactObj != null) { return(CanAccessTo(contactObj, userId)); } } if (relationshipEvent.EntityType == EntityType.Case) { var caseObj = daoFactory.CasesDao.GetByID(relationshipEvent.EntityID); if (caseObj != null) { return(CanAccessTo(caseObj, userId)); } } if (relationshipEvent.EntityType == EntityType.Opportunity) { var dealObj = daoFactory.DealDao.GetByID(relationshipEvent.EntityID); if (dealObj != null) { return(CanAccessTo(dealObj, userId)); } } return(false); } }
public void AddHistoryToCallContact(VoipCall call, DaoFactory daoFactory) { var listItemDao = daoFactory.GetListItemDao(); if (call == null || call.ContactId == 0) { return; } var category = listItemDao.GetByTitle(ListType.HistoryCategory, CRMCommonResource.HistoryCategory_Call); if (category == null) { category = new ListItem(CRMCommonResource.HistoryCategory_Call, "event_category_call.png"); category.ID = listItemDao.CreateItem(ListType.HistoryCategory, category); } var contact = daoFactory.GetContactDao().GetByID(call.ContactId); if (contact != null && _crmSecurity.CanAccessTo(contact)) { var note = call.Status == VoipCallStatus.Incoming || call.Status == VoipCallStatus.Answered ? CRMContactResource.HistoryVoipIncomingNote : CRMContactResource.HistoryVoipOutcomingNote; var content = string.Format(note, call.DialDuration); var relationshipEvent = new RelationshipEvent { CategoryID = category.ID, EntityType = EntityType.Any, EntityID = 0, Content = content, ContactID = contact.ID, CreateOn = _tenantUtil.DateTimeFromUtc(DateTime.UtcNow), CreateBy = _securityContext.CurrentAccount.ID }; daoFactory.GetRelationshipEventDao().CreateItem(relationshipEvent); } }
public static void DemandCreateOrUpdate(RelationshipEvent relationshipEvent) { if (String.IsNullOrEmpty(relationshipEvent.Content) || relationshipEvent.CategoryID == 0 || (relationshipEvent.ContactID == 0 && relationshipEvent.EntityID == 0)) { throw new ArgumentException(); } if (relationshipEvent.EntityID > 0 && relationshipEvent.EntityType != EntityType.Opportunity && relationshipEvent.EntityType != EntityType.Case) { throw new ArgumentException(); } if (relationshipEvent.Content.Length > Global.MaxHistoryEventCharacters) { throw new ArgumentException("Data too long for column 'content'"); } if (!CanAccessTo(relationshipEvent)) { throw CreateSecurityException(); } }
public RelationshipEvent CreateItem(RelationshipEvent item) { if (String.IsNullOrEmpty(item.Content) || item.CategoryID == 0 || (item.ContactID == 0 && item.EntityID == 0)) { throw new ArgumentException(); } if (item.EntityID > 0 && item.EntityType != EntityType.Opportunity && item.EntityType != EntityType.Case) { throw new ArgumentException(); } if (item.CreateOn == DateTime.MinValue) { item.CreateOn = TenantUtil.DateTimeNow(); } item.ID = DbManager.ExecuteScalar <int>( Insert("crm_relationship_event") .InColumnValue("id", 0) .InColumnValue("contact_id", item.ContactID) .InColumnValue("content", item.Content) .InColumnValue("create_on", TenantUtil.DateTimeToUtc(item.CreateOn)) .InColumnValue("create_by", ASC.Core.SecurityContext.CurrentAccount.ID) .InColumnValue("entity_type", (int)item.EntityType) .InColumnValue("entity_id", item.EntityID) .InColumnValue("category_id", item.CategoryID) .InColumnValue("last_modifed_on", DateTime.UtcNow) .InColumnValue("last_modifed_by", ASC.Core.SecurityContext.CurrentAccount.ID) .InColumnValue("have_files", false) .Identity(1, 0, true)); if (item.CreateOn.Kind == DateTimeKind.Utc) { item.CreateOn = TenantUtil.DateTimeFromUtc(item.CreateOn); } return(item); }
public void DeleteItem(RelationshipEvent item) { _crmSecurity.DemandDelete(item); var relativeFiles = GetFiles(item.ID); var nowFilesEditing = relativeFiles.Where(file => (file.FileStatus & FileStatus.IsEditing) == FileStatus.IsEditing); if (nowFilesEditing.Count() != 0) { throw new ArgumentException(); } relativeFiles.ForEach(f => RemoveFile(f)); var itemToDelete = Query(CrmDbContext.RelationshipEvent).Where(x => x.Id == item.ID).Single(); _factoryIndexer.Delete(itemToDelete); CrmDbContext.RelationshipEvent.Remove(itemToDelete); CrmDbContext.SaveChanges(); }
public static bool CanAccessTo(RelationshipEvent relationshipEvent) { if (IsAdmin) { return(true); } if (relationshipEvent.ContactID > 0) { var contactObj = Global.DaoFactory.GetContactDao().GetByID(relationshipEvent.ContactID); if (contactObj != null) { return(CanAccessTo(contactObj)); } } if (relationshipEvent.EntityType == EntityType.Case) { var caseObj = Global.DaoFactory.GetCasesDao().GetByID(relationshipEvent.EntityID); if (caseObj != null) { return(CanAccessTo(caseObj)); } } if (relationshipEvent.EntityType == EntityType.Opportunity) { var dealObj = Global.DaoFactory.GetDealDao().GetByID(relationshipEvent.EntityID); if (dealObj != null) { return(CanAccessTo(dealObj)); } } return(false); }
public bool CanAccessTo(RelationshipEvent relationshipEvent, Guid userId) { if (IsAdministrator(userId)) { return(true); } if (relationshipEvent.ContactID > 0) { var contactObj = _daoFactory.GetContactDao().GetByID(relationshipEvent.ContactID); if (contactObj != null) { return(CanAccessTo(contactObj, userId)); } } if (relationshipEvent.EntityType == EntityType.Case) { var caseObj = _daoFactory.GetCasesDao().GetByID(relationshipEvent.EntityID); if (caseObj != null) { return(CanAccessTo(caseObj, userId)); } } if (relationshipEvent.EntityType == EntityType.Opportunity) { var dealObj = _daoFactory.GetDealDao().GetByID(relationshipEvent.EntityID); if (dealObj != null) { return(CanAccessTo(dealObj, userId)); } } return(false); }
public static bool CanDelete(RelationshipEvent relationshipEvent) { return(CanEdit(relationshipEvent)); }
public static bool CanEdit(RelationshipEvent relationshipEvent) { return(IsAdmin || relationshipEvent.CreateBy == SecurityContext.CurrentAccount.ID); }
public RelationshipEvent CreateItem(RelationshipEvent item) { _crmSecurity.DemandCreateOrUpdate(item); var htmlBody = String.Empty; if (item.CreateOn == DateTime.MinValue) { item.CreateOn = _tenantUtil.DateTimeNow(); } item.CreateBy = _securityContext.CurrentAccount.ID; item.LastModifedBy = _securityContext.CurrentAccount.ID; if (item.CategoryID == (int)HistoryCategorySystem.MailMessage) { // var jsonObj = JsonDocument.Parse(item.Content).RootElement; // var messageId = jsonObj.GetProperty("message_id").GetInt32(); //var apiServer = new ApiServer(); //var msg = apiServer.GetApiResponse( // String.Format("{0}mail/messages/{1}.json?id={1}&loadImages=true&needSanitize=true", SetupInfo.WebApiBaseUrl, messageId), "GET"); // String msg = null; // if (msg == null) throw new ArgumentException("Mail message cannot be found"); //var msgResponseDto = JObject.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(msg))); //var msgRequestObj = msgResponseDto.Value<JObject>("response"); //string messageUrl; //htmlBody = msgRequestObj.Value<String>("htmlBody"); //using (var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(htmlBody))) //{ // var filePath = String.Format("folder_{0}/message_{1}.html", (messageId / 1000 + 1) * 1000, messageId); // Global.GetStore().Save("mail_messages", filePath, fileStream); // messageUrl = String.Format("{0}HttpHandlers/filehandler.ashx?action=mailmessage&message_id={1}", PathProvider.BaseAbsolutePath, messageId).ToLower(); //} //var msg_date_created = msgRequestObj.Value<String>("date"); //var message_id = msgRequestObj.Value<Int32>("id"); //item.Content = JsonConvert.SerializeObject(new //{ // @from = msgRequestObj.Value<String>("from"), // to = msgRequestObj.Value<String>("to"), // cc = msgRequestObj.Value<String>("cc"), // bcc = msgRequestObj.Value<String>("bcc"), // subject = msgRequestObj.Value<String>("subject"), // important = msgRequestObj.Value<Boolean>("important"), // chain_id = msgRequestObj.Value<String>("chainId"), // is_sended = msgRequestObj.Value<Int32>("folder") != 1, // date_created = msg_date_created, // introduction = msgRequestObj.Value<String>("introduction"), // message_id = message_id, // message_url = messageUrl //}); //item.CreateOn = DateTime.Parse(msg_date_created, CultureInfo.InvariantCulture); //var sqlQueryFindMailsAlready = Query(CRMDbContext.RelationshipEvent) // .Where(x => x.ContactId == item.ContactID) // .Where(x => x.EntityType == item.EntityType) // .Where(x => x.EntityId == item.EntityID) // .Where(x => x.CategoryId == item.CategoryID) // .Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Content, String.Format("\"message_id\":{0},", message_id))); //if (sqlQueryFindMailsAlready.Count() > 0) // throw new Exception("Already exists"); } var itemToInsert = new DbRelationshipEvent { ContactId = item.ContactID, Content = item.Content, CreateOn = _tenantUtil.DateTimeToUtc(item.CreateOn), CreateBy = item.CreateBy, EntityType = item.EntityType, EntityId = item.EntityID, CategoryId = item.CategoryID, LastModifedOn = DateTime.UtcNow, LastModifedBy = item.LastModifedBy, TenantId = TenantID, HaveFiles = false }; CrmDbContext.RelationshipEvent.Add(itemToInsert); CrmDbContext.SaveChanges(); item.ID = itemToInsert.Id; if (item.CreateOn.Kind == DateTimeKind.Utc) { item.CreateOn = _tenantUtil.DateTimeFromUtc(item.CreateOn); } _factoryIndexer.Index(itemToInsert); return(item); }
public static bool CanEdit(RelationshipEvent relationshipEvent) { return(CanAccessTo(relationshipEvent)); }
public static void SetAccessTo(RelationshipEvent relationshipEvent, List <Guid> subjectID) { SetAccessTo((ISecurityObjectId)relationshipEvent, subjectID); }
public static void MakePublic(RelationshipEvent relationshipEvent) { MakePublic((ISecurityObjectId)relationshipEvent); }
public RelationshipEventWrapper AddHistoryTo( string entityType, int entityId, int contactId, string content, int categoryId, ApiDateTime created, IEnumerable <int> fileId, IEnumerable <Guid> notifyUserList) { if (!string.IsNullOrEmpty(entityType) && !( string.Compare(entityType, "opportunity", StringComparison.OrdinalIgnoreCase) == 0 || string.Compare(entityType, "case", StringComparison.OrdinalIgnoreCase) == 0) ) { throw new ArgumentException(); } var entityTypeObj = ToEntityType(entityType); var entityTitle = ""; if (contactId > 0) { var contact = DaoFactory.ContactDao.GetByID(contactId); if (contact == null || !CRMSecurity.CanAccessTo(contact)) { throw new ArgumentException(); } entityTitle = contact.GetTitle(); } if (entityTypeObj == EntityType.Case) { var cases = DaoFactory.CasesDao.GetByID(entityId); if (cases == null || !CRMSecurity.CanAccessTo(cases)) { throw new ArgumentException(); } if (contactId <= 0) { entityTitle = cases.Title; } } if (entityTypeObj == EntityType.Opportunity) { var deal = DaoFactory.DealDao.GetByID(entityId); if (deal == null || !CRMSecurity.CanAccessTo(deal)) { throw new ArgumentException(); } if (contactId <= 0) { entityTitle = deal.Title; } } var relationshipEvent = new RelationshipEvent { CategoryID = categoryId, EntityType = entityTypeObj, EntityID = entityId, Content = content, ContactID = contactId, CreateOn = created, CreateBy = Core.SecurityContext.CurrentAccount.ID }; var category = DaoFactory.ListItemDao.GetByID(categoryId); if (category == null) { throw new ArgumentException(); } var item = DaoFactory.RelationshipEventDao.CreateItem(relationshipEvent); notifyUserList = notifyUserList != null?notifyUserList.ToList() : new List <Guid>(); var needNotify = notifyUserList.Any(); var fileListInfoHashtable = new Hashtable(); if (fileId != null) { var fileIds = fileId.ToList(); var files = FilesDaoFactory.GetFileDao().GetFiles(fileIds.Cast <object>().ToArray()); if (needNotify) { foreach (var file in files) { var extension = Path.GetExtension(file.Title); if (extension == null) { continue; } var fileInfo = string.Format("{0} ({1})", file.Title, extension.ToUpper()); if (!fileListInfoHashtable.ContainsKey(fileInfo)) { fileListInfoHashtable.Add(fileInfo, file.DownloadUrl); } else { fileInfo = string.Format("{0} ({1}, {2})", file.Title, extension.ToUpper(), file.UniqID); fileListInfoHashtable.Add(fileInfo, file.DownloadUrl); } } } DaoFactory.RelationshipEventDao.AttachFiles(item.ID, fileIds.ToArray()); if (files.Any()) { var fileAttachAction = GetFilesAttachAction(entityTypeObj, contactId); MessageService.Send(Request, fileAttachAction, MessageTarget.Create(item.ID), entityTitle, files.Select(x => x.Title)); } } if (needNotify) { NotifyClient.Instance.SendAboutAddRelationshipEventAdd(item, fileListInfoHashtable, DaoFactory, notifyUserList.ToArray()); } var wrapper = ToRelationshipEventWrapper(item); var historyCreatedAction = GetHistoryCreatedAction(entityTypeObj, contactId); MessageService.Send(Request, historyCreatedAction, MessageTarget.Create(item.ID), entityTitle, category.Title); return(wrapper); }
public static bool IsPrivate(RelationshipEvent relationshipEvent) { return(IsPrivate((ISecurityObjectId)relationshipEvent)); }
public static Dictionary <Guid, string> GetAccessSubjectTo(RelationshipEvent relationshipEvent) { return(GetAccessSubjectTo((ISecurityObjectId)relationshipEvent)); }
public static List <Guid> GetAccessSubjectGuidsTo(RelationshipEvent relationshipEvent) { return(GetAccessSubjectGuidsTo((ISecurityObjectId)relationshipEvent)); }