Пример #1
0
        public override async Task OnConnectedAsync()
        {
            var user            = _claimsService.GetUserClaims();
            var userConnections = await _cacheService.Get <List <string> >(CachingHelpers.BuildKey("Notification", user.Id)) ?? new List <string>();

            userConnections.Add(Context.ConnectionId);
            await _cacheService.Set(CachingHelpers.BuildKey("Notification", user.Id), userConnections);

            var numOfNotifications = await _notificationService.GetUserNumberOfNotifications(user.Id);

            var unreadMessages = await _messageService.GetUnreadMessages(user.Id);

            await _pubSub.Publish(Channels.NotififcationMessageChannel, new NewNotificationMessageContract
            {
                ActionType          = NotificationActionType.UnreadMessage,
                ConnectionIds       = userConnections,
                TotalUnreadMessages = unreadMessages
            });

            await _pubSub.Publish(Channels.NotififcationMessageChannel, new NewNotificationMessageContract
            {
                ActionType          = NotificationActionType.NewNoti,
                ConnectionIds       = userConnections,
                TotalUnreadMessages = numOfNotifications
            });

            await base.OnConnectedAsync();
        }
Пример #2
0
        public ActionResult Edit(ProjectEditModel model)
        {
            if (!ModelState.IsValid)
            {
                string messages = string.Join("; ", ModelState.Values
                                              .SelectMany(x => x.Errors)
                                              .Select(x => x.ErrorMessage + x.Exception));
                this.AddNotification(messages, NotificationType.ERROR);
                return(View(model));
            }

            try
            {
                var info      = ExtractEditFormData(model);
                var isSuccess = _mainStore.Update(info);

                CachingHelpers.ClearProjectCache(info.Id);

                if (isSuccess)
                {
                    this.AddNotification(ManagerResource.LB_UPDATE_SUCCESS, NotificationType.SUCCESS);
                }
            }
            catch (Exception ex)
            {
                this.AddNotification(NotifSettings.Error_SystemBusy, NotificationType.ERROR);

                logger.Error("Failed for Edit Product request: " + ex.ToString());

                return(View(model));
            }

            return(RedirectToAction("Edit/" + model.Id));
        }
Пример #3
0
        public ActionResult Delete_Confirm(int id)
        {
            var strError = string.Empty;

            if (id <= 0)
            {
                return(new HttpStatusCodeResult(HttpStatusCode.BadRequest));
            }

            try
            {
                _mainStore.Delete(id);

                //Clear cache
                CachingHelpers.ClearProviderCache();
            }
            catch (Exception ex)
            {
                strError = NotifSettings.Error_SystemBusy;

                logger.Error("Failed to get Delete Provider because: " + ex.ToString());

                return(Json(new { success = true, message = strError }));
            }

            return(Json(new { success = true, message = NotifSettings.Success_Deleted }));
        }
Пример #4
0
        public ActionResult Edit(ProviderEditModel model)
        {
            if (!ModelState.IsValid)
            {
                string messages = string.Join("; ", ModelState.Values
                                              .SelectMany(x => x.Errors)
                                              .Select(x => x.ErrorMessage + x.Exception));
                this.AddNotification(messages, NotificationType.ERROR);
                return(View(model));
            }

            try
            {
                //Begin db transaction
                var shopInfo = ExtractEditFormData(model);

                var isSuccess = _mainStore.Update(shopInfo);
                if (isSuccess)
                {
                    //Clear cache
                    CachingHelpers.ClearProviderCache();

                    this.AddNotification(NotifSettings.Success_Updated, NotificationType.SUCCESS);
                }
            }
            catch (Exception ex)
            {
                this.AddNotification(NotifSettings.Error_SystemBusy, NotificationType.ERROR);

                logger.Error("Failed for Edit Provider POST request: " + ex.ToString());
            }

            return(RedirectToAction("Edit/" + model.Id));
        }
Пример #5
0
        public static Bitmap GetImageFromUrl(string url, bool forceNoCache = false)
        {
            try
            {
                CachingHelpers.CacheStructureBuilder();
                if (string.IsNullOrEmpty(url))
                {
                    return(Resources.unavailable);
                }

                if (!forceNoCache)
                {
                    if (ThumbCaching.ThumbInCache(url))
                    {
                        return(ThumbCaching.ThumbFromCache(url));
                    }
                }
            }
            catch (UnauthorizedAccessException ex)
            {
                LoggingHelpers.RecordException(ex.Message, "ThumbIOAccessError");
            }
            catch (Exception ex)
            {
                LoggingHelpers.RecordException(ex.Message, "ImageFetchError");
                return(Resources.unavailable);
            }

            return(ForceImageFromUrl(url));
        }
Пример #6
0
        public async Task ReadMessage(Guid messageId, Guid contactUserId)
        {
            var identity             = _claimsService.GetUserClaims();
            var contactConnectionIds = await _cacheService.Get <List <string> >(CachingHelpers.BuildKey("Chat", contactUserId)) ?? new List <string>();

            var userConnectionIds = await _cacheService.Get <List <string> >(CachingHelpers.BuildKey("Notification", identity.Id)) ?? new List <string>();

            var messageRes = await _messageService.ReadMessage(messageId, identity.Id);

            var unreadMessages = await _messageService.GetUnreadMessages(identity.Id);

            await _pubSub.Publish(Channels.PrivateMessageChannel, new PrivateMessageContract
            {
                ActionType    = PrivateMessageActionType.SeenMessage,
                ConnectionIds = contactConnectionIds,
                SeenMessage   = messageRes
            });

            await _pubSub.Publish(Channels.NotififcationMessageChannel, new NewNotificationMessageContract
            {
                ActionType          = NotificationActionType.UnreadMessage,
                ConnectionIds       = userConnectionIds,
                TotalUnreadMessages = unreadMessages
            });
        }
Пример #7
0
        public async Task <ActionResult> Delete(string id)
        {
            if (id == null)
            {
                return(new HttpStatusCodeResult(HttpStatusCode.BadRequest));
            }

            var user = await UserManager.FindByIdAsync(id);

            if (user == null)
            {
                return(HttpNotFound());
            }
            var result = await UserManager.DeleteAsync(user);

            if (result.Succeeded)
            {
                //Clear cache
                CachingHelpers.ClearUserCache();
                CachingHelpers.ClearUserCache(id);

                return(Json(new { success = true }));
            }
            else
            {
                throw new Exception("Failed to delete the user");
            }
        }
Пример #8
0
        public ActionResult Delete_Confirm(int id)
        {
            var strError = string.Empty;

            if (id <= 0)
            {
                return(new HttpStatusCodeResult(HttpStatusCode.BadRequest));
            }

            try
            {
                _mainStore.Delete(id);

                //Clear cache
                CachingHelpers.ClearProductCategoryCache();
            }
            catch (Exception ex)
            {
                strError = ManagerResource.LB_SYSTEM_BUSY;

                logger.Error("Failed to get Delete ProductCategory because: " + ex.ToString());

                return(Json(new { success = false, message = strError }));
            }

            return(Json(new { success = true, message = ManagerResource.LB_DELETE_SUCCESS, title = ManagerResource.LB_NOTIFICATION }));
        }
Пример #9
0
        public UsersAdminController(ApplicationUserManager userManager, ApplicationRoleManager roleManager, IIdentityStore identityStore)
        {
            UserManager = userManager;
            RoleManager = roleManager;

            _identityStore = identityStore;
            //Clear cache
            CachingHelpers.ClearUserCache();
        }
Пример #10
0
        public override async Task OnDisconnectedAsync(Exception exception)
        {
            var user          = _claimsService.GetUserClaims();
            var connectionIds = await _cacheService.Get <List <string> >(CachingHelpers.BuildKey("Notification", user.Id)) ?? new List <string>();

            var ids = connectionIds.Where(x => x != Context.ConnectionId).ToList();
            await _cacheService.Set(CachingHelpers.BuildKey("Notification", user.Id), ids);

            await base.OnDisconnectedAsync(exception);
        }
Пример #11
0
        public override async Task OnConnectedAsync()
        {
            var user = _claimsService.GetUserClaims();
            var key  = CachingHelpers.BuildKey("Chat", user.Id);

            var userConnections = await _cacheService.Get <List <string> >(key) ?? new List <string>();

            userConnections.Add(Context.ConnectionId);
            await _cacheService.Set(key, userConnections);

            await base.OnConnectedAsync();
        }
Пример #12
0
        protected async Task TryGetRevalidatedResult(IHandlerContextWithResult handlerContext, HttpRequestMessage request, HttpResponseMessage response)
        {
            if (!Enabled)
            {
                return;
            }

            var context = GetContext(handlerContext);

            var result = (CacheEntry)context.Items["CacheHandlerCachedItem"];

            var meta = CachingHelpers.CreateResponseMetadata(result, request, response, context);

            if (result == null || !context.RevalidateValidator(context, meta))
            {
                return;
            }

            if (!result.IsEmpty && result.Value != null)
            {
                result.UpdateResponseMetadata(request, response, context);

                context.ResultInspector?.Invoke(result);
            }
            else
            {
                result = CacheEntry.Empty;
            }

            if (result.IsEmpty)
            {
                var missedResult = await context.HandlerRegister.OnMiss(context);

                if (missedResult.IsDirty)
                {
                    result = new CacheEntry(missedResult.Value, null);
                }
            }

            if (!result.IsEmpty)
            {
                var hitResult = await context.HandlerRegister.OnHit(context, result.Value);

                if (hitResult.IsDirty && !(bool)hitResult.Value)
                {
                    handlerContext.Result = result.Value;
                }
            }

            context.Items["CacheHandlerCachedItem"] = result;
        }
Пример #13
0
        public async Task <ActionResult> Edit([Bind(Include = "Email,Id,PhoneNumber,ProviderId")] EditUserViewModel editUser, params string[] selectedRole)
        {
            if (ModelState.IsValid)
            {
                var user = await UserManager.FindByIdAsync(editUser.Id);

                if (user == null)
                {
                    return(HttpNotFound());
                }

                user.Email       = editUser.Email;
                user.PhoneNumber = editUser.PhoneNumber;
                //user.ProviderId = editUser.ProviderId;

                var userRoles = await UserManager.GetRolesAsync(user.Id);

                await UserManager.UpdateAsync(user);

                selectedRole = selectedRole ?? new string[] { };

                var result = await UserManager.AddUserToRolesAsync(user.Id, selectedRole.Except(userRoles).ToList <string>());

                if (!result.Succeeded)
                {
                    ModelState.AddModelError("", result.Errors.First());
                    return(View());
                }
                result = await UserManager.RemoveUserFromRolesAsync(user.Id, userRoles.Except(selectedRole).ToList <string>());

                if (!result.Succeeded)
                {
                    this.AddNotification("Update user profiles faied: " + result.Errors.First(), NotificationType.ERROR);
                    return(View());
                }

                MenuHelper.ClearUserMenuCache(editUser.Id);

                //Clear cache
                CachingHelpers.ClearUserCache();
                CachingHelpers.ClearUserCache(editUser.Id);

                this.AddNotification(ManagerResource.LB_UPDATE_SUCCESS, NotificationType.SUCCESS);
                return(RedirectToAction("Edit/" + editUser.Id));
            }

            this.AddNotificationModelStateErrors(ModelState);
            return(RedirectToAction("Edit/" + editUser.Id));
        }
Пример #14
0
        public async Task <IActionResult> Logout()
        {
            var userId = GetClaimsUserId(User.Claims);
            var device = GetClaimsDevice(User.Claims);

            var keyAccessToken = CachingHelpers.GetKeyAccessToken(userId, device);

            _cache.Remove(keyAccessToken);

            var keyRefressToken = CachingHelpers.GetKeyRefreshToken(userId, device);

            _cache.Remove(keyRefressToken);

            return(Ok());
        }
Пример #15
0
        public async Task MessageStopTyping(Guid conversationId, Guid contactUserId)
        {
            var identity             = _claimsService.GetUserClaims();
            var contactConnectionIds = await _cacheService.Get <List <string> >(CachingHelpers.BuildKey("Chat", contactUserId)) ?? new List <string>();

            await _pubSub.Publish(Channels.PrivateMessageChannel, new PrivateMessageContract
            {
                ActionType           = PrivateMessageActionType.StopTyping,
                ConnectionIds        = contactConnectionIds,
                TypingOnConversation = new TypingOnConversationContract
                {
                    ConversationId = conversationId,
                    ContactUserId  = identity.Id
                }
            });
        }
Пример #16
0
 private static void GetServerListWorker(object sender, WaitWindowEventArgs e)
 {
     CachingHelpers.CacheStructureBuilder();
     if (ServerCaching.ServerInCache(ObjectProvider.Settings.ConnectionInfo.PlexAccountToken) &&
         ObjectProvider.Settings.CacheSettings.Mode.EnableServerCaching)
     {
         e.Result = ServerCaching.ServerFromCache(ObjectProvider.Settings.ConnectionInfo.PlexAccountToken);
     }
     else
     {
         var result = ObjectProvider.PlexProvider.GetServers(ObjectProvider.User);
         if (ObjectProvider.Settings.CacheSettings.Mode.EnableServerCaching)
         {
             ServerCaching.ServerToCache(result, ObjectProvider.User.authenticationToken);
         }
         e.Result = result;
     }
 }
Пример #17
0
        public ActionResult Create(ProductCategoryCreateModel model)
        {
            var newId = 0;

            if (!ModelState.IsValid)
            {
                string messages = string.Join("; ", ModelState.Values
                                              .SelectMany(x => x.Errors)
                                              .Select(x => x.ErrorMessage + x.Exception));
                this.AddNotification(messages, NotificationType.ERROR);
                return(View(model));
            }

            try
            {
                //Extract info
                var info = ExtractCreateFormData(model);

                newId = _mainStore.Insert(info);

                //Clear cache
                CachingHelpers.ClearProductCategoryCache();

                if (newId > 0)
                {
                    this.AddNotification(ManagerResource.LB_INSERT_SUCCESS, NotificationType.SUCCESS);
                }
                else
                {
                    this.AddNotification(NotifSettings.Error_SystemBusy, NotificationType.ERROR);
                }
            }
            catch (Exception ex)
            {
                this.AddNotification(NotifSettings.Error_SystemBusy, NotificationType.ERROR);

                logger.Error("Failed for Create ProductCategory request: " + ex.ToString());

                return(View(model));
            }

            return(RedirectToAction("Edit/" + newId));
        }
Пример #18
0
        public async Task <ActionResult> Lock(string id)
        {
            if (id == null)
            {
                return(new HttpStatusCodeResult(HttpStatusCode.BadRequest));
            }

            var user = await UserManager.FindByIdAsync(id);

            if (user == null)
            {
                return(HttpNotFound());
            }

            //var helper = new IdentityModelHelper(UserManager, RoleManager);
            //var model = await helper.GetUserDetailsViewModel(id);
            try
            {
                var result = await UserManager.LockUserAccount(id, 1000 * 1000);

                if (!result.Succeeded)
                {
                    AddErrors(result.Errors);
                }
                else
                {
                    //Clear cache
                    CachingHelpers.ClearUserCache();
                    CachingHelpers.ClearUserCache(id);

                    //model = await helper.GetUserDetailsViewModel(id);
                    return(RedirectToAction("Details/" + id, "UsersAdmin"));
                }
            }
            catch (Exception ex)
            {
                AddError(ex);
            }
            //ViewBag.RoleNames = await UserManager.GetRolesAsync(user.Id);
            return(View());
        }
Пример #19
0
 public CacheSettings()
 {
     HandlerRegister = new CacheHandlerRegister();
     MustRevalidate  = false;
     SuppressTypeMismatchExceptions = false;
     CacheableHttpMethods           = new HashSet <HttpMethod> {
         HttpMethod.Get
     };
     CacheableHttpStatusCodes = new HashSet <HttpStatusCode> {
         HttpStatusCode.OK, HttpStatusCode.NotModified
     };
     DefaultVaryByHeaders = new HashSet <string> {
         "Accept", "Accept-Encoding"
     };
     DefaultDurationForCacheableResults = TimeSpan.FromMinutes(15);
     ResponseValidator         = (ctx, res) => CachingHelpers.ValidateResponse(res, ctx.CacheableHttpStatusCodes);
     RequestValidator          = CachingHelpers.CanCacheRequest;
     RevalidateValidator       = (ctx, res) => CachingHelpers.ShouldRevalidate(ctx.Request, res, ctx.CacheableHttpMethods);
     AllowStaleResultValidator = (ctx, res) => CachingHelpers.AllowStale(ctx.Request, res);
     DependentUris             = new HashSet <Uri>();
 }
Пример #20
0
        public async Task <ActionResult> Create(RegisterViewModel userViewModel, params string[] selectedRoles)
        {
            if (ModelState.IsValid)
            {
                userViewModel.Password = Utility.Md5HashingData(userViewModel.Password);
                var user = new ApplicationUser {
                    UserName = userViewModel.UserName, Email = userViewModel.Email, PhoneNumber = userViewModel.PhoneNumber, EmailConfirmed = true
                };

                var adminresult = await UserManager.CreateAsync(user, userViewModel.Password);

                //Clear cache
                CachingHelpers.ClearUserCache();

                //Add User to the selected Roles
                if (adminresult.Succeeded)
                {
                    if (selectedRoles != null)
                    {
                        var result = await UserManager.AddUserToRolesAsync(user.Id, selectedRoles);

                        if (!result.Succeeded)
                        {
                            ModelState.AddModelError("", result.Errors.First());
                            ViewBag.RoleId = new SelectList(RoleManager.Roles, "Name", "Name");
                            return(View());
                        }
                    }
                }
                else
                {
                    ModelState.AddModelError("", adminresult.Errors.First());
                    ViewBag.RoleId = new SelectList(RoleManager.Roles, "Name", "Name");
                    return(View());
                }
                return(RedirectToAction("Edit/" + user.Id));
            }
            //ViewBag.RoleId = new SelectList(RoleManager.Roles, "Name", "Name");
            return(View());
        }
Пример #21
0
        public async Task SendMessage(string message, string fileUrl, Guid contactUserId, Guid conversationId)
        {
            var identity = _claimsService.GetUserClaims();
            var payload  = new SendMessageRequestContract
            {
                SenderId       = identity.Id,
                ContactUserId  = contactUserId,
                Message        = message,
                MessageType    = string.IsNullOrEmpty(fileUrl) ? 0 : 1,
                AttachmentUrl  = fileUrl,
                ConversationId = conversationId
            };

            var messageId = await _messageService.CreateMessageAsync(payload);

            payload.MessageId = messageId;
            var userConnectionIds = await _cacheService.Get <List <string> >(CachingHelpers.BuildKey("Chat", identity.Id)) ?? new List <string>();

            var contactConnectionIds = await _cacheService.Get <List <string> >(CachingHelpers.BuildKey("Chat", contactUserId)) ?? new List <string>();

            var connectionIds = userConnectionIds.Union(contactConnectionIds).ToList();

            await _pubSub.Publish(Channels.PrivateMessageChannel,
                                  new
                                  PrivateMessageContract
            {
                ConnectionIds = connectionIds,
                NewMessage    = payload
            });

            var unreadMessages = await _messageService.GetUnreadMessages(contactUserId);

            await _pubSub.Publish(Channels.NotififcationMessageChannel, new NewNotificationMessageContract
            {
                ActionType          = NotificationActionType.UnreadMessage,
                ConnectionIds       = await _cacheService.Get <List <string> >(CachingHelpers.BuildKey("Notification", contactUserId)) ?? new List <string>(),
                TotalUnreadMessages = unreadMessages
            });
        }
Пример #22
0
 public CacheEntry(object result, HttpRequestMessage request, HttpResponseMessage response, ICacheMetadata metadata)
     : this(result, CachingHelpers.CreateResponseMetadata(result, request, response, metadata))
 {
 }
Пример #23
0
 public UsersAdminController(IIdentityStore identityStore)
 {
     _identityStore = identityStore;
     //Clear cache
     CachingHelpers.ClearUserCache();
 }
Пример #24
0
        public ActionResult UpdateProperty(PropertyEditModel model)
        {
            var msg       = ManagerResource.LB_OPERATION_SUCCESS;
            var isSuccess = false;

            if (!ModelState.IsValid)
            {
                string messages = string.Join("; ", ModelState.Values
                                              .SelectMany(x => x.Errors)
                                              .Select(x => x.ErrorMessage + x.Exception));
                this.AddNotification(messages, NotificationType.ERROR);

                return(Json(new { success = isSuccess, title = ManagerResource.LB_NOTIFICATION, message = messages }));
            }

            try
            {
                //Begin db transaction
                var data = new IdentityProperty();
                data.PropertyCategoryId = model.PropertyCategoryId;
                data.Id     = model.Id;
                data.Code   = model.Code;
                data.Name   = model.Name;
                data.Status = (int)EnumStatus.Activated;

                var storeProperty = GlobalContainer.IocContainer.Resolve <IStoreProperty>();

                if (model.Id > 0)
                {
                    //Update
                    storeProperty.Update(data);

                    //Clear cache
                    CachingHelpers.ClearPropertyCategoryCache();
                }
                else
                {
                    //Add new
                    var newId = storeProperty.Insert(data);

                    if (newId > 0)
                    {
                        isSuccess = true;

                        //Clear cache
                        CachingHelpers.ClearPropertyCategoryCache();

                        return(Json(new { success = isSuccess, title = ManagerResource.LB_NOTIFICATION, message = ManagerResource.LB_OPERATION_SUCCESS, clientcallback = " location.reload()" }));
                    }
                }

                isSuccess = true;
            }
            catch (Exception ex)
            {
                this.AddNotification(NotifSettings.Error_SystemBusy, NotificationType.ERROR);

                logger.Error("Failed for UpdateProperty request: " + ex.ToString());

                return(Json(new { success = isSuccess, title = ManagerResource.LB_NOTIFICATION, message = NotifSettings.Error_SystemBusy }));
            }

            return(Json(new { success = isSuccess, title = ManagerResource.LB_NOTIFICATION, message = msg, clientcallback = " location.reload()" }));
        }
Пример #25
0
 public void UpdateResponseMetadata(HttpRequestMessage request, HttpResponseMessage response, ICacheMetadata metadata)
 {
     Metadata = CachingHelpers.CreateResponseMetadata(Value, request, response, metadata);
 }
Пример #26
0
        public static XmlDocument GetXmlTransaction(string uri, bool forceNoCache = false,
                                                    bool silent = false, bool waitWindow = true)
        {
            //allows multi-threaded operations
            if (waitWindow)
            {
                //offload to another thread if specified
                return((XmlDocument)WaitWindow.WaitWindow.Show(GetXmlTransaction, @"Fetching from API", uri,
                                                               forceNoCache, silent));
            }

            //Create the cache folder structure
            CachingHelpers.CacheStructureBuilder();

            //check if it's already cached
            if (XmlCaching.XmlInCache(uri) && !forceNoCache)
            {
                try
                {
                    //load from the cache
                    var xmlCache = XmlCaching.XmlFromCache(uri);

                    //return the cached XML if not null, otherwise force a re-download
                    return(xmlCache ?? GetXmlTransaction(uri, true, silent, false));
                }
                catch (Exception ex)
                {
                    //record the error
                    LoggingHelpers.RecordException(ex.Message, "CacheLoadError");

                    //force a re-download
                    return(GetXmlTransaction(uri, true, silent, false));
                }
            }

            //default secret account token
            var secret = !string.IsNullOrWhiteSpace(ObjectProvider.Settings.ConnectionInfo.PlexAccountToken)
                ? ObjectProvider.Settings.ConnectionInfo.PlexAccountToken
                : ObjectProvider.User.authenticationToken;

            //allows specific server connection matching for the correct token
            var uriToken = string.IsNullOrEmpty(secret)
                ? Methods.MatchUriToToken(uri, ObjectProvider.PlexServers)
                : secret;

            //the API URI is combined with the token to yield the fully-qualified URI
            var fullUri = $@"{uri}{uriToken}";

            try
            {
                //get the resource
                var xmlString = ResourceGrab.GrabString(fullUri);

                //validation
                if (!string.IsNullOrWhiteSpace(xmlString))
                {
                    //conversion
                    var xmlResponse = xmlString.ToXmlDocument();

                    //log outcome
                    LoggingHelpers.RecordTransaction(fullUri, ResourceGrab.LastStatusCode);

                    //ensure file is cached
                    XmlCaching.XmlToCache(xmlResponse, uri);

                    //return XML document
                    return(xmlResponse);
                }
            }
            catch (ThreadAbortException)
            {
                //literally nothing; this gets raised when a cancellation happens.
            }
            catch (Exception ex)
            {
                LoggingHelpers.RecordException(ex.Message, "XMLTransactionError");
                LoggingHelpers.RecordTransaction(fullUri, "Undetermined");
                if (!silent)
                {
                    UIMessages.Error("Error Occurred in XML Transaction\n\n" + ex, @"Network Error");
                }
            }

            //default
            return(new XmlDocument());
        }