public ActionResult ClearLinkFlair(int?submissionID)
        {
            if (submissionID == null)
            {
                return(HybridError(ErrorViewModel.GetErrorViewModel(ErrorType.NotFound)));
                //return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            // get model for selected submission
            var submissionModel = _db.Submission.Find(submissionID);

            if (submissionModel == null || submissionModel.IsDeleted)
            {
                return(HybridError(ErrorViewModel.GetErrorViewModel(ErrorType.NotFound)));
                //return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            // check if caller is subverse moderator, if not, deny posting
            if (!ModeratorPermission.HasPermission(User, submissionModel.Subverse, Domain.Models.ModeratorAction.AssignFlair))
            {
                return(HybridError(ErrorViewModel.GetErrorViewModel(ErrorType.Unauthorized)));
            }

            // clear flair and save submission
            submissionModel.FlairCss   = null;
            submissionModel.FlairLabel = null;
            _db.SaveChanges();
            DataCache.Submission.Remove(submissionID.Value);

            return(JsonResult(CommandResponse.FromStatus(Status.Success)));
            //return new HttpStatusCodeResult(HttpStatusCode.OK);
        }
        public async Task <CommandResponse <string> > RegenerateThumbnail(int submissionID)
        {
            DemandAuthentication();

            // get model for selected submission
            var submission = _db.Submission.Find(submissionID);
            var response   = CommandResponse.FromStatus(Status.Error);

            if (submission == null || submission.IsDeleted)
            {
                return(CommandResponse.FromStatus("", Status.Error, "Submission is missing or deleted"));
            }
            var subverse = submission.Subverse;

            // check if caller is subverse moderator, if not, deny change
            if (!ModeratorPermission.HasPermission(User, subverse, Domain.Models.ModeratorAction.AssignFlair))
            {
                return(CommandResponse.FromStatus("", Status.Denied, "Moderator Permissions are not satisfied"));
            }
            try
            {
                throw new NotImplementedException();

                await _db.SaveChangesAsync();

                return(CommandResponse.FromStatus("", Status.Success));
            }
            catch (Exception ex)
            {
                return(CommandResponse.Error <CommandResponse <string> >(ex));
            }
        }
Example #3
0
        public async Task <CommandResponse <bool?> > SetSubverseListChange(DomainReference setReference, string subverse, SubscriptionAction action)
        {
            DemandAuthentication();
            var set = GetSet(setReference.Name, setReference.OwnerName);

            if (set == null)
            {
                return(CommandResponse.FromStatus <bool?>(null, Status.Denied, "Set cannot be found"));
            }

            //Check Perms
            var perms = SetPermission.GetPermissions(set.Map(), User.Identity);

            if (!perms.EditList)
            {
                return(CommandResponse.FromStatus <bool?>(null, Status.Denied, "User not authorized to modify set"));
            }

            var sub = GetSubverseInfo(subverse);

            if (sub == null)
            {
                return(CommandResponse.FromStatus <bool?>(null, Status.Denied, "Subverse cannot be found"));
            }

            return(await SetSubverseListChange(set, sub, action).ConfigureAwait(CONSTANTS.AWAIT_CAPTURE_CONTEXT));
        }
        public async Task <CommandResponse <bool> > ToggleNSFW(int submissionID)
        {
            DemandAuthentication();

            // get model for selected submission
            var submission = _db.Submission.Find(submissionID);
            var response   = CommandResponse.FromStatus(Status.Error);

            if (submission == null || submission.IsDeleted)
            {
                return(CommandResponse.FromStatus(false, Status.Error, "Submission is missing or deleted"));
            }
            var subverse = submission.Subverse;

            if (!User.Identity.Name.IsEqual(submission.UserName))
            {
                // check if caller is subverse moderator, if not, deny change
                if (!ModeratorPermission.HasPermission(User, subverse, Domain.Models.ModeratorAction.AssignFlair))
                {
                    return(CommandResponse.FromStatus(false, Status.Denied, "Moderator Permissions are not satisfied"));
                }
            }
            try
            {
                submission.IsAdult = !submission.IsAdult;

                await _db.SaveChangesAsync();

                return(CommandResponse.FromStatus(submission.IsAdult, Status.Success));
            }
            catch (Exception ex)
            {
                return(CommandResponse.Error <CommandResponse <bool> >(ex));
            }
        }
        public async Task <ActionResult> Reply(MessageReplyViewModel message)
        {
            if (!ModelState.IsValid)
            {
                PreventSpamAttribute.Reset(HttpContext);

                if (Request.IsAjaxRequest())
                {
                    return(new JsonResult(CommandResponse.FromStatus(Status.Invalid, ModelState.GetFirstErrorMessage())));
                }
                else
                {
                    return(View());
                }
            }

            if (message.ID <= 0)
            {
                return(RedirectToAction("Sent", "Messages"));
            }

            var cmd      = new SendMessageReplyCommand(message.ID, message.Body).SetUserContext(User);
            var response = await cmd.Execute();

            return(JsonResult(response));
        }
        public async Task <CommandResponse <Comment> > DistinguishComment(int commentID)
        {
            DemandAuthentication();
            var response = CommandResponse.FromStatus <Comment>(null, Status.Invalid);
            var comment  = await this.GetComment(commentID);

            if (comment != null)
            {
                // check to see if request came from comment author
                if (User.Identity.Name == comment.UserName)
                {
                    // check to see if comment author is also sub mod or sub admin for comment sub
                    if (ModeratorPermission.HasPermission(User, comment.Subverse, ModeratorAction.DistinguishContent))
                    {
                        var m = new DapperMulti();

                        var u = new DapperUpdate();
                        //u.Update = $"{SqlFormatter.Table("Comment")} SET \"IsDistinguished\" = {SqlFormatter.ToggleBoolean("\"IsDistinguished\"")}";
                        u.Update = SqlFormatter.UpdateSetBlock($"\"IsDistinguished\" = {SqlFormatter.ToggleBoolean("\"IsDistinguished\"")}", SqlFormatter.Table("Comment"));
                        u.Where  = "\"ID\" = @id";
                        u.Parameters.Add("id", commentID);
                        m.Add(u);

                        var s = new DapperQuery();
                        s.Select = $"\"IsDistinguished\" FROM {SqlFormatter.Table("Comment")}";
                        s.Where  = "\"ID\" = @id";
                        m.Add(s);

                        //ProTip: The actual execution of code is important.
                        var result = await _db.Connection.ExecuteScalarAsync <bool>(m.ToCommandDefinition());

                        comment.IsDistinguished = result;

                        response = CommandResponse.FromStatus(comment, Status.Success);
                    }
                    else
                    {
                        response.Message = "User does not have permissions to distinquish content";
                        response.Status  = Status.Denied;
                    }
                }
                else
                {
                    response.Message = "User can only distinquish owned content";
                    response.Status  = Status.Denied;
                }
            }
            else
            {
                response.Message = "Comment can not be found";
                response.Status  = Status.Denied;
            }
            return(response);
        }
Example #7
0
        //These are meant to bridge the gap between the UI and ajax calls.

        protected ActionResult HybridError(ErrorViewModel errorViewModel)
        {
            if (Request.IsAjaxRequest())
            {
                return(JsonResult(CommandResponse.FromStatus(Status.Error, errorViewModel.Description)));
            }
            else
            {
                return(ErrorView(errorViewModel));
            }
        }
Example #8
0
        public async Task <CommandResponse <bool?> > DeleteSet(DomainReference setReference)
        {
            DemandAuthentication();
            var set = GetSet(setReference.Name, setReference.OwnerName);

            if (set == null)
            {
                return(CommandResponse.FromStatus <bool?>(null, Status.Denied, "Set cannot be found"));
            }

            //Check Perms
            var perms = SetPermission.GetPermissions(set.Map(), User.Identity);

            if (!perms.Delete)
            {
                return(CommandResponse.FromStatus <bool?>(null, Status.Denied, "User not authorized to delete set"));
            }

            var param = new DynamicParameters();

            param.Add("ID", set.ID);

            var conn = _db.Connection;

            //var tran = conn.BeginTransaction(System.Data.IsolationLevel.Serializable);
            try
            {
                var d = new DapperDelete();

                d.Delete = SqlFormatter.DeleteBlock(SqlFormatter.Table("SubverseSetSubscription"));
                d.Where  = "\"SubverseSetID\" = @ID";
                await conn.ExecuteAsync(d.ToString(), param);

                d.Delete = SqlFormatter.DeleteBlock(SqlFormatter.Table("SubverseSetList"));
                d.Where  = "\"SubverseSetID\" = @ID";
                await conn.ExecuteAsync(d.ToString(), param);

                d.Delete = SqlFormatter.DeleteBlock(SqlFormatter.Table("SubverseSet"));
                d.Where  = "\"ID\" = @ID";
                await conn.ExecuteAsync(d.ToString(), param);

                //tran.Commit();

                return(CommandResponse.FromStatus <bool?>(true, Status.Success));
            }
            catch (Exception ex)
            {
                //tran.Rollback();
                return(CommandResponse.Error <CommandResponse <bool?> >(ex));
            }
        }
        public override CommandResponse <IVoteRestriction> Evaluate(IPrincipal principal)
        {
            var evaluation = CommandResponse.FromStatus <IVoteRestriction>(this, Status.Success);

            using (var repo = new Repository())
            {
                var count = repo.UserContributionCount(principal.Identity.Name, (Voat.Domain.Models.ContentType)ContentType, Subverse, DateRange);
                if (count < MinimumCount)
                {
                    evaluation = CommandResponse.FromStatus <IVoteRestriction>(this, Status.Denied, $"User only has {count} and needs {MinimumCount}");
                }
            }
            return(evaluation);
        }
Example #10
0
        public async Task <ActionResult> Update(Set set)
        {
            if (ModelState.IsValid)
            {
                var cmd    = new UpdateSetCommand(set).SetUserContext(User);
                var result = await cmd.Execute();

                return(JsonResult(result));
            }
            else
            {
                return(JsonResult(CommandResponse.FromStatus(Status.Invalid, ModelState.GetFirstErrorMessage())));
            }
        }
Example #11
0
        public async Task <ActionResult> ReportContent(ReportContentModel model)
        {
            if (ModelState.IsValid)
            {
                var cmd    = new SaveRuleReportCommand(model.ContentType, model.ID, model.RuleSetID.Value).SetUserContext(User);
                var result = await cmd.Execute();

                return(JsonResult(result));
            }
            else
            {
                PreventSpamAttribute.Reset(HttpContext);
                return(JsonResult(CommandResponse.FromStatus(Status.Error, ModelState.GetFirstErrorMessage())));
            }
        }
Example #12
0
        public async Task <ActionResult> DeleteComment(int id)
        {
            if (ModelState.IsValid)
            {
                var cmd    = new DeleteCommentCommand(id, "This feature is not yet implemented").SetUserContext(User);
                var result = await cmd.Execute();

                return(base.JsonResult(result));
            }
            else
            {
                return(base.JsonResult(CommandResponse.FromStatus(Status.Error, ModelState.GetFirstErrorMessage())));
                //return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
        }
Example #13
0
        public static async Task <CommandResponse <string> > GenerateAvatar(Stream imageStream, string fileName, string mimetype, bool purgeTempFile = true)
        {
            var fileManager = FileManager.Instance;
            var fileCheck   = fileManager.IsUploadPermitted(fileName, FileType.Avatar, mimetype, imageStream.Length);

            if (fileCheck.Success)
            {
                var key = new FileKey();
                key.FileType = FileType.Avatar;
                key.ID       = await GenerateRandomFilename(Path.GetExtension(fileName), FileType.Avatar);

                await GenerateImageThumbnail(fileManager, key, imageStream, VoatSettings.Instance.AvatarSize, false, false);

                return(CommandResponse.Successful(key.ID));
            }

            return(CommandResponse.FromStatus <string>(null, fileCheck.Status, fileCheck.Message));
        }
Example #14
0
        public async Task <ActionResult> EditComment([FromBody()] CommentEditInput commentModel)
        {
            if (ModelState.IsValid)
            {
                var cmd    = new EditCommentCommand(commentModel.ID, commentModel.Content).SetUserContext(User);
                var result = await cmd.Execute();

                if (!result.Success)
                {
                    PreventSpamAttribute.Reset(HttpContext);
                }
                return(JsonResult(result));
            }
            else
            {
                PreventSpamAttribute.Reset(HttpContext);
                return(JsonResult(CommandResponse.FromStatus(Status.Error, ModelState.GetFirstErrorMessage())));
            }
        }
Example #15
0
        public async Task <CommandResponse> BanGlobally(IEnumerable <GenericReference <BanType> > banList, string reason)
        {
            DemandAuthentication();
            var statements = new List <DapperInsert>();

            foreach (var banItem in banList)
            {
                var    insert = new DapperInsert();
                string tablename = "", columnname = "";

                insert.Parameters.Add("CreatedBy", User.Identity.Name);
                insert.Parameters.Add("CreationDate", CurrentDate);
                insert.Parameters.Add("Reason", reason);

                switch (banItem.Type)
                {
                case BanType.Domain:
                    insert.Parameters.Add("Value", banItem.Name.ToNormalized(Normalization.Lower));
                    tablename  = "BannedDomain";
                    columnname = "Domain";

                    break;

                case BanType.User:
                    insert.Parameters.Add("Value", banItem.Name);
                    tablename  = "BannedUser";
                    columnname = "UserName";
                    break;
                }

                insert.Insert  = SqlFormatter.Table(tablename) + $" (\"{columnname}\", \"Reason\", \"CreatedBy\", \"CreationDate\")";
                insert.Insert += $" SELECT @Value, @Reason, @CreatedBy, @CreationDate WHERE NOT EXISTS (SELECT * FROM {SqlFormatter.Table(tablename)} WHERE \"{columnname}\" = @Value)";
                statements.Add(insert);
            }
            //This needs to executed in a single batch, but since a low traffic scenario this is ok
            foreach (var statement in statements)
            {
                await _db.Connection.ExecuteAsync(statement.ToString(), statement.Parameters);
            }

            return(CommandResponse.FromStatus(Status.Success));
        }
        public async Task <ActionResult> EditSubmission([FromBody] EditSubmission model)
        {
            if (ModelState.IsValid)
            {
                var cmd = new EditSubmissionCommand(model.SubmissionId, new Domain.Models.UserSubmission()
                {
                    Content = model.SubmissionContent
                }).SetUserContext(User);
                var response = await cmd.Execute();

                if (response.Success)
                {
                    DataCache.Submission.Remove(model.SubmissionId);
                    CacheHandler.Instance.Remove(CachingKey.Submission(model.SubmissionId));
                    //return Json(new { response = response.Response.FormattedContent });
                }
                return(JsonResult(response));
            }

            return(JsonResult(CommandResponse.FromStatus(Status.Error, ModelState.GetFirstErrorMessage())));
        }
        public ActionResult ApplyLinkFlair(int?submissionID, int?flairId)
        {
            if (submissionID == null || flairId == null)
            {
                return(HybridError(ErrorViewModel.GetErrorViewModel(ErrorType.NotFound)));
                //return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            var submission = _db.Submission.Find(submissionID);

            if (submission == null || submission.IsDeleted)
            {
                return(HybridError(ErrorViewModel.GetErrorViewModel(ErrorType.NotFound)));
                //return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }

            if (!ModeratorPermission.HasPermission(User, submission.Subverse, Domain.Models.ModeratorAction.AssignFlair))
            {
                return(HybridError(ErrorViewModel.GetErrorViewModel(ErrorType.Unauthorized)));
                //return new HttpUnauthorizedResult();
            }

            // find flair by id, apply it to submission
            var flairModel = _db.SubverseFlair.Find(flairId);

            if (flairModel == null || flairModel.Subverse != submission.Subverse)
            {
                return(HybridError(ErrorViewModel.GetErrorViewModel(ErrorType.NotFound)));
            }
            //return new HttpStatusCodeResult(HttpStatusCode.BadRequest);

            // apply flair and save submission
            submission.FlairCss   = flairModel.CssClass;
            submission.FlairLabel = flairModel.Label;
            _db.SaveChanges();
            DataCache.Submission.Remove(submissionID.Value);

            return(JsonResult(CommandResponse.FromStatus(Status.Success)));
            //return new HttpStatusCodeResult(HttpStatusCode.OK);
        }
Example #18
0
        public async Task <ActionResult> SubmitComment(CommentInput commentModel)
        {
            if (!ModelState.IsValid)
            {
                //we want to reset spam filter if the modelstate was not triggered by preventspam
                PreventSpamAttribute.Reset(HttpContext);
                return(JsonResult(CommandResponse.FromStatus(Status.Error, ModelState.GetFirstErrorMessage())));
            }
            else
            {
                var cmd    = new CreateCommentCommand(commentModel.SubmissionID.Value, commentModel.ParentID, commentModel.Content).SetUserContext(User);
                var result = await cmd.Execute();

                if (result.Success)
                {
                    //if good return formatted comment
                    if (Request.IsAjaxRequest())
                    {
                        var comment = result.Response;
                        comment.IsOwner     = true;
                        ViewBag.CommentId   = comment.ID;               //why?
                        ViewBag.rootComment = comment.ParentID == null; //why?
                        return(PartialView("~/Views/Shared/Comments/_SubmissionComment.cshtml", comment));
                    }
                    else
                    {
                        return(new EmptyResult());
                    }
                }
                else
                {
                    PreventSpamAttribute.Reset(HttpContext);
                    return(JsonResult(result));
                }
            }
        }
Example #19
0
        public virtual CommandResponse IsUploadPermitted(string fileName, FileType fileType, string mimeType = null, long?length = null)
        {
            var result = CommandResponse.FromStatus(Status.Success);

            var uploadLimit = VoatSettings.Instance.FileUploadLimits.FirstOrDefault(x => x.Type == fileType);

            if (uploadLimit == null)
            {
                uploadLimit = new FileUploadLimit()
                {
                    Type = fileType
                };
            }

            switch (fileType)
            {
            case FileType.Avatar:
            case FileType.Thumbnail:
            case FileType.Badge:

                if (!uploadLimit.IsExtensionAllowed(fileName))
                {
                    result = CommandResponse.FromStatus(Status.Invalid, "File type is not permitted for upload");
                }
                else if (!uploadLimit.IsMimeTypeAllowed(mimeType))
                {
                    result = CommandResponse.FromStatus(Status.Invalid, "Mime type is not permitted for upload");
                }
                if (length == null || length.Value == 0 || (uploadLimit.ByteLimit > 0 && length > uploadLimit.ByteLimit))
                {
                    result = CommandResponse.FromStatus(Status.Invalid, $"File length is too big or too small but we aren't saying (Hint: {Math.Round(uploadLimit.ByteLimit / (decimal)1000000, 1)} MB max)");
                }
                break;
            }
            return(result);
        }
        public async Task <CommandResponse> ToggleSticky(int submissionID, string subverse = null, bool clearExisting = false, int stickyLimit = 3)
        {
            DemandAuthentication();

            // get model for selected submission
            var submission = _db.Submission.Find(submissionID);
            var response   = CommandResponse.FromStatus(Status.Error);


            if (submission == null || submission.IsDeleted)
            {
                return(CommandResponse.FromStatus(Status.Error, "Submission is missing or deleted"));
            }
            //Eventually we want users to be able to sticky other subs posts, but for now make sure we don't allow this
            subverse = submission.Subverse;

            // check if caller is subverse moderator, if not, deny change
            if (!ModeratorPermission.HasPermission(User, subverse, Domain.Models.ModeratorAction.AssignStickies))
            {
                return(CommandResponse.FromStatus(Status.Denied, "Moderator Permissions are not satisfied"));
            }
            int affectedCount = 0;

            try
            {
                // find and clear current sticky if toggling
                var existingSticky = _db.StickiedSubmission.FirstOrDefault(s => s.SubmissionID == submissionID);
                if (existingSticky != null)
                {
                    _db.StickiedSubmission.Remove(existingSticky);
                    affectedCount += -1;
                }
                else
                {
                    if (clearExisting)
                    {
                        // remove all stickies for subverse matching submission subverse
                        _db.StickiedSubmission.RemoveRange(_db.StickiedSubmission.Where(s => s.Subverse == subverse));
                        affectedCount = 0;
                    }

                    // set new submission as sticky
                    var stickyModel = new Data.Models.StickiedSubmission
                    {
                        SubmissionID = submissionID,
                        CreatedBy    = User.Identity.Name,
                        CreationDate = Repository.CurrentDate,
                        Subverse     = subverse
                    };

                    _db.StickiedSubmission.Add(stickyModel);
                    affectedCount += 1;
                }

                //limit sticky counts
                var currentCount = _db.StickiedSubmission.Count(x => x.Subverse == subverse);
                if ((currentCount + affectedCount) > stickyLimit)
                {
                    return(CommandResponse.FromStatus(Status.Denied, $"Stickies are limited to {stickyLimit}"));
                }

                await _db.SaveChangesAsync();

                StickyHelper.ClearStickyCache(submission.Subverse);

                return(CommandResponse.FromStatus(Status.Success));
            }
            catch (Exception ex)
            {
                return(CommandResponse.Error <CommandResponse>(ex));
            }
        }
Example #21
0
        public static async Task <CommandResponse <string> > GenerateThumbnail(Uri uri, bool purgeTempFile = true)
        {
            if (VoatSettings.Instance.OutgoingTraffic.Enabled)
            {
                var url = uri.ToString();
                if (!String.IsNullOrEmpty(url) && UrlUtility.IsUriValid(url))
                {
                    try
                    {
                        //Ok this all needs to be centralized, we should only make 1 request to a remote resource
                        using (var httpResource = new HttpResource(
                                   new Uri(url),
                                   new HttpResourceOptions()
                        {
                            AllowAutoRedirect = true
                        },
                                   VoatSettings.Instance.OutgoingTraffic.Proxy.ToWebProxy()))
                        {
                            var result = await httpResource.GiddyUp();

                            if (httpResource.IsImage)
                            {
                                var fileManager = FileManager.Instance;
                                var fileCheck   = fileManager.IsUploadPermitted(url, FileType.Thumbnail, httpResource.Response.Content.Headers.ContentType.MediaType, httpResource.Stream.Length);

                                if (fileCheck.Success)
                                {
                                    var key = new FileKey();
                                    key.FileType = FileType.Thumbnail;
                                    key.ID       = await GenerateRandomFilename(".jpg", FileType.Thumbnail);

                                    var stream = httpResource.Stream;

                                    await GenerateImageThumbnail(fileManager, key, stream, VoatSettings.Instance.ThumbnailSize, true);

                                    return(CommandResponse.Successful(key.ID));
                                }
                            }
                            else if (httpResource.Image != null)
                            {
                                //just do it. again.
                                return(await GenerateThumbnail(httpResource.Image));
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        EventLogger.Instance.Log(ex, new { url = url, type = FileType.Thumbnail });
                        var response = CommandResponse.Error <CommandResponse <string> >(ex);
                        response.Response = ""; //Make sure this returns string.empty as other failures do.
                        return(response);
                    }
                }
                EventLogger.Instance.Log(new LogInformation()
                {
                    Type     = LogType.Debug,
                    Category = "Thumbnail Diag",
                    Message  = "Default Response",
                    Data     = new { url = url },
                    Origin   = VoatSettings.Instance.Origin
                });
            }
            return(CommandResponse.FromStatus <string>("", Status.Invalid));
        }
Example #22
0
        public async Task <CommandResponse <Domain.Models.Set> > CreateOrUpdateSet(Set set)
        {
            DemandAuthentication();

            set.Name = set.Name.TrimSafe();
            //Evaulate Rules
            var context = new VoatRuleContext(User);

            context.PropertyBag.Set = set;
            var outcome = VoatRulesEngine.Instance.EvaluateRuleSet(context, RuleScope.CreateSet);

            if (!outcome.IsAllowed)
            {
                return(MapRuleOutCome <Set>(outcome, null));
            }

            var existingSet = _db.SubverseSet.FirstOrDefault(x => x.ID == set.ID);

            if (existingSet != null)
            {
                var perms = SetPermission.GetPermissions(existingSet.Map(), User.Identity);

                if (!perms.EditProperties)
                {
                    return(CommandResponse.FromStatus <Set>(null, Status.Denied, "User does not have permission to edit this set"));
                }

                //HACK: Need to clear this entry out of cache if name changes and check name
                if (!existingSet.Name.IsEqual(set.Name))
                {
                    if (_db.SubverseSet.Any(x => x.Name.ToLower() == set.Name.ToLower() && x.UserName.ToLower() == UserName.ToLower()))
                    {
                        return(CommandResponse.FromStatus <Set>(null, Status.Denied, "A set with this name already exists"));
                    }
                    CacheHandler.Instance.Remove(CachingKey.Set(existingSet.Name, existingSet.UserName));
                }

                existingSet.Name        = set.Name;
                existingSet.Title       = set.Title;
                existingSet.Description = set.Description;
                existingSet.IsPublic    = set.IsPublic;

                await _db.SaveChangesAsync().ConfigureAwait(CONSTANTS.AWAIT_CAPTURE_CONTEXT);

                return(CommandResponse.FromStatus <Set>(existingSet.Map(), Status.Success));
            }
            else
            {
                //Validation - MOVE TO RULES SYSTEM MAYBE
                if (!VoatSettings.Instance.SetCreationEnabled || VoatSettings.Instance.MaximumOwnedSets <= 0)
                {
                    return(CommandResponse.FromStatus <Set>(null, Status.Denied, "Set creation is currently disabled"));
                }

                if (VoatSettings.Instance.MaximumOwnedSets > 0)
                {
                    var d = new DapperQuery();
                    d.Select = $"SELECT COUNT(*) FROM {SqlFormatter.Table("SubverseSet", "subSet")}";
                    d.Where  = "subSet.\"Type\" = @Type AND subSet.\"UserName\" = @UserName";
                    d.Parameters.Add("Type", (int)SetType.Normal);
                    d.Parameters.Add("UserName", UserName);

                    var setCount = _db.Connection.ExecuteScalar <int>(d.ToString(), d.Parameters);
                    if (setCount >= VoatSettings.Instance.MaximumOwnedSets)
                    {
                        return(CommandResponse.FromStatus <Set>(null, Status.Denied, $"Sorry, Users are limited to {VoatSettings.Instance.MaximumOwnedSets} sets and you currently have {setCount}"));
                    }
                }


                //Create new set
                try
                {
                    var setCheck = GetSet(set.Name, UserName);
                    if (setCheck != null)
                    {
                        return(CommandResponse.FromStatus <Set>(null, Status.Denied, "A set with same name and owner already exists"));
                    }

                    var newSet = new SubverseSet
                    {
                        Name            = set.Name,
                        Title           = set.Title,
                        Description     = set.Description,
                        UserName        = UserName,
                        Type            = (int)SetType.Normal,
                        IsPublic        = set.IsPublic,
                        CreationDate    = Repository.CurrentDate,
                        SubscriberCount = 1, //Owner is a subscriber. Reminds me of that hair club commercial: I"m not only the Set Owner, I'm also a subscriber.
                    };

                    _db.SubverseSet.Add(newSet);
                    await _db.SaveChangesAsync().ConfigureAwait(CONSTANTS.AWAIT_CAPTURE_CONTEXT);

                    _db.SubverseSetSubscription.Add(new SubverseSetSubscription()
                    {
                        SubverseSetID = newSet.ID, UserName = UserName, CreationDate = CurrentDate
                    });
                    await _db.SaveChangesAsync().ConfigureAwait(CONSTANTS.AWAIT_CAPTURE_CONTEXT);

                    return(CommandResponse.Successful(newSet.Map()));
                }
                catch (Exception ex)
                {
                    return(CommandResponse.Error <CommandResponse <Set> >(ex));
                }
            }
        }
        public async Task <CommandResponse <Domain.Models.Vote> > SaveVote(Domain.Models.Vote vote)
        {
            DemandAuthentication();

            var newDataModel = VoteDomainMaps.Map(vote);
            var domainModel  = vote;

            //UPDATE
            if (newDataModel.ID > 0)
            {
                var existingDataModel = await GetVoteDataModel(newDataModel.ID);

                //Check some basics
                if (existingDataModel == null)
                {
                    return(CommandResponse.FromStatus <Domain.Models.Vote>(null, Status.Error, "Vote can not be found"));
                }
                if (!existingDataModel.CreatedBy.IsEqual(User.Identity.Name) && !User.IsInAnyRole(new[] { UserRole.GlobalAdmin, UserRole.Admin }))
                {
                    return(CommandResponse.FromStatus <Domain.Models.Vote>(null, Status.Error, "Vote can not be edited by current user"));
                }
                if (existingDataModel.StartDate <= CurrentDate)
                {
                    return(CommandResponse.FromStatus <Domain.Models.Vote>(null, Status.Error, "Vote can not be edited once voting has begun"));
                }

                //TODO: Verify incoming model ids all belong to this vote
                var restrictionsAreBelongToUs = newDataModel.VoteRestrictions.Where(x => x.ID > 0).All(x => existingDataModel.VoteRestrictions.Any(e => e.ID == x.ID));
                if (!restrictionsAreBelongToUs)
                {
                    return(CommandResponse.FromStatus <Domain.Models.Vote>(null, Status.Error, "Message integrity violated (Restrictions)"));
                }
                var optionsAreBelongToUs = newDataModel.VoteOptions.Where(x => x.ID > 0).All(x => existingDataModel.VoteOptions.Any(e => e.ID == x.ID));
                if (!optionsAreBelongToUs)
                {
                    return(CommandResponse.FromStatus <Domain.Models.Vote>(null, Status.Error, "Message integrity violated (Options)"));
                }
                var outcomesAreBelongToUs = newDataModel.VoteOptions.Where(x => x.ID > 0).SelectMany(x => x.VoteOutcomes.Where(o => o.ID > 0)).All(x => existingDataModel.VoteOptions.SelectMany(y => y.VoteOutcomes).Any(e => e.ID == x.ID));
                if (!outcomesAreBelongToUs)
                {
                    return(CommandResponse.FromStatus <Domain.Models.Vote>(null, Status.Error, "Message integrity violated (Outcomes)"));
                }



                existingDataModel.LastEditDate     = CurrentDate;
                existingDataModel.Title            = newDataModel.Title;
                existingDataModel.Content          = newDataModel.Content;
                existingDataModel.FormattedContent = newDataModel.FormattedContent;

                newDataModel.VoteOptions.ForEachIndex((option, index) =>
                {
                    //TODO: Ensure ID belongs to proper vote (aka fuzzy will exploit this)
                    if (option.ID > 0)
                    {
                        //Update Existing
                        var existingOption              = existingDataModel.VoteOptions.FirstOrDefault(x => x.ID == option.ID);
                        existingOption.Title            = option.Title;
                        existingOption.Content          = option.Content;
                        existingOption.FormattedContent = option.FormattedContent;
                        existingOption.SortOrder        = index;

                        option.VoteOutcomes.ForEachIndex((outcome, oIndex) =>
                        {
                            if (outcome.ID > 0)
                            {
                                var existingOutcome  = existingDataModel.VoteOptions[index].VoteOutcomes.FirstOrDefault(x => x.ID == outcome.ID);
                                existingOutcome.Type = outcome.Type;
                                existingOutcome.Data = outcome.Data;
                            }
                            else
                            {
                                var newOutcome  = new VoteOutcome();
                                newOutcome.Type = outcome.Type;
                                newOutcome.Data = outcome.Data;
                                existingOption.VoteOutcomes.Add(newOutcome);
                            }
                        });
                        //Remove deleted outcomes
                        var deletedOutcomes = existingOption.VoteOutcomes.Where(c => !option.VoteOutcomes.Any(n => c.ID == n.ID)).ToList();
                        deletedOutcomes.ForEach(x => existingOption.VoteOutcomes.Remove(x));
                    }
                    else
                    {
                        //Add new
                        var newOption              = new Data.Models.VoteOption();
                        newOption.Title            = option.Title;
                        newOption.Content          = option.Content;
                        newOption.FormattedContent = option.FormattedContent;
                        newOption.SortOrder        = index;
                        existingDataModel.VoteOptions.Add(newOption);

                        option.VoteOutcomes.ForEachIndex((outcome, oIndex) =>
                        {
                            var newOutcome  = new VoteOutcome();
                            newOutcome.Type = outcome.Type;
                            newOutcome.Data = outcome.Data;
                            newOption.VoteOutcomes.Add(newOutcome);
                        });
                    }
                });
                //Remove deleted options
                var deletedOptions = existingDataModel.VoteOptions.Where(c => !newDataModel.VoteOptions.Any(n => c.ID == n.ID)).ToList();
                deletedOptions.ForEach(x => existingDataModel.VoteOptions.Remove(x));

                //handle restrictions
                newDataModel.VoteRestrictions.ForEachIndex((restriction, index) =>
                {
                    if (restriction.ID > 0)
                    {
                        //Update Existing
                        var existingRestriction  = existingDataModel.VoteRestrictions.FirstOrDefault(x => x.ID == restriction.ID);
                        existingRestriction.Type = restriction.Type;
                        existingRestriction.Data = restriction.Data;
                    }
                    else
                    {
                        //Add new
                        var newRestriction  = new Data.Models.VoteRestriction();
                        newRestriction.Type = restriction.Type;
                        newRestriction.Data = restriction.Data;
                        existingDataModel.VoteRestrictions.Add(newRestriction);
                    }
                });
                //Remove deleted options
                var deletedRestrictions = existingDataModel.VoteRestrictions.Where(c => !newDataModel.VoteRestrictions.Any(n => c.ID == n.ID)).ToList();
                deletedRestrictions.ForEach(x => existingDataModel.VoteRestrictions.Remove(x));

                await _db.SaveChangesAsync();

                return(CommandResponse.FromStatus <Domain.Models.Vote>(existingDataModel.Map(), Status.Success));
            }
            //NEW
            else
            {
                newDataModel.CreatedBy    = User.Identity.Name;
                newDataModel.CreationDate = CurrentDate;

                //TODO: Set start end dates according to logic
                newDataModel.StartDate = CurrentDate.AddDays(7);
                newDataModel.EndDate   = CurrentDate.AddDays(14);

                _db.Vote.Add(newDataModel);
                await _db.SaveChangesAsync();

                domainModel = VoteDomainMaps.Map(newDataModel);

                return(CommandResponse.FromStatus <Domain.Models.Vote>(newDataModel.Map(), Status.Success));
            }
        }