public object Put(UpdateTechnologyStack request)
        {
            var techStack = Db.SingleById <TechnologyStack>(request.Id);

            if (techStack == null)
            {
                throw HttpError.NotFound("Tech stack not found");
            }

            var session = SessionAs <AuthUserSession>();

            if (techStack.IsLocked && !(techStack.OwnerId == session.UserAuthId || session.HasRole(RoleNames.Admin)))
            {
                throw HttpError.Unauthorized("This TechStack is locked and can only be modified by its Owner or Admins.");
            }

            var techIds = (request.TechnologyIds ?? new List <long>()).ToHashSet();

            //Only Post an Update if there was no other update today and Stack as TechCount >= 4
            var postUpdate = AppSettings.EnableTwitterUpdates() &&
                             techStack.LastStatusUpdate.GetValueOrDefault(DateTime.MinValue) < DateTime.UtcNow.Date &&
                             techIds.Count >= 4;

            techStack.PopulateWith(request);
            techStack.LastModified   = DateTime.UtcNow;
            techStack.LastModifiedBy = session.UserName;

            if (postUpdate)
            {
                techStack.LastStatusUpdate = techStack.LastModified;
            }

            using (var trans = Db.OpenTransaction())
            {
                Db.Save(techStack);

                var existingTechChoices = Db.Select <TechnologyChoice>(q => q.TechnologyStackId == request.Id);
                var techIdsToAdd        = techIds.Except(existingTechChoices.Select(x => x.TechnologyId)).ToHashSet();
                var techChoices         = techIdsToAdd.Map(x => new TechnologyChoice
                {
                    TechnologyId      = x,
                    TechnologyStackId = request.Id,
                    CreatedBy         = techStack.CreatedBy,
                    LastModifiedBy    = techStack.LastModifiedBy,
                    OwnerId           = techStack.OwnerId,
                });

                var unusedTechChoices = Db.From <TechnologyChoice>().Where(x => x.TechnologyStackId == request.Id);
                if (techIds.Count > 0)
                {
                    unusedTechChoices.And(x => !techIds.Contains(x.TechnologyId));
                }

                Db.Delete(unusedTechChoices);

                Db.InsertAll(techChoices);

                trans.Commit();
            }

            var history = techStack.ConvertTo <TechnologyStackHistory>();

            history.TechnologyStackId = techStack.Id;
            history.Operation         = "UPDATE";
            history.TechnologyIds     = techIds.ToList();
            Db.Insert(history);

            ContentCache.ClearAll();

            var response = new UpdateTechnologyStackResponse
            {
                Result = techStack.ConvertTo <TechStackDetails>()
            };

            if (postUpdate)
            {
                var url = new ClientTechnologyStack {
                    Slug = techStack.Slug
                }.ToAbsoluteUri();
                response.ResponseStatus = new ResponseStatus
                {
                    Message = PostTwitterUpdate(
                        "{0}'s Stack! {1} ".Fmt(techStack.Name, url),
                        request.TechnologyIds,
                        maxLength: 140 - (TweetUrlLength - url.Length))
                };
            }

            return(response);
        }
        public async Task <UpdateTechnologyStackResponse> Put(UpdateTechnologyStack request)
        {
            var techStack = await Db.SingleByIdAsync <TechnologyStack>(request.Id);

            if (techStack == null)
            {
                throw HttpError.NotFound("Tech stack not found");
            }

            if (string.IsNullOrEmpty(request.AppUrl) || request.AppUrl.IndexOf("://", StringComparison.Ordinal) == -1)
            {
                throw new ArgumentException("A valid URL to the Website or App is required");
            }

            if (string.IsNullOrEmpty(request.Description) || request.Description.Length < 100)
            {
                throw new ArgumentException("Summary needs to be a min of 100 chars");
            }

            var session  = SessionAs <AuthUserSession>();
            var authRepo = HostContext.AppHost.GetAuthRepository(Request);

            using (authRepo as IDisposable)
            {
                if (techStack.IsLocked && !(techStack.OwnerId == session.UserAuthId || session.HasRole(RoleNames.Admin, authRepo)))
                {
                    throw HttpError.Unauthorized(
                              "This TechStack is locked and can only be modified by its Owner or Admins.");
                }
            }

            var techIds = (request.TechnologyIds ?? new List <long>()).ToHashSet();

            //Only Post an Update if there was no other update today and Stack as TechCount >= 4
            var postUpdate = AppSettings.EnableTwitterUpdates() &&
                             techStack.LastStatusUpdate.GetValueOrDefault(DateTime.MinValue) < DateTime.UtcNow.Date &&
                             techIds.Count >= 4;

            if (techStack.Details != request.Details)
            {
                techStack.DetailsHtml = await Markdown.TransformAsync(request.Details, session.GetGitHubToken());
            }

            techStack.PopulateWith(request);
            techStack.LastModified   = DateTime.UtcNow;
            techStack.LastModifiedBy = session.UserName;

            if (postUpdate)
            {
                techStack.LastStatusUpdate = techStack.LastModified;
            }

            if (Request.Files.Length > 0)
            {
                techStack.ScreenshotUrl = Request.Files[0].UploadToImgur(AppSettings.GetString("oauth.imgur.ClientId"),
                                                                         nameof(techStack.ScreenshotUrl), minWidth: 858, minHeight: 689, maxWidth: 2600, maxHeight: 2600);
            }

            if (string.IsNullOrEmpty(techStack.ScreenshotUrl))
            {
                throw new ArgumentException("Screenshot is Required", nameof(techStack.ScreenshotUrl));
            }

            using (var trans = Db.OpenTransaction())
            {
                await Db.SaveAsync(techStack);

                var existingTechChoices = await Db.SelectAsync <TechnologyChoice>(q => q.TechnologyStackId == request.Id);

                var techIdsToAdd = techIds.Except(existingTechChoices.Select(x => x.TechnologyId)).ToHashSet();
                var techChoices  = techIdsToAdd.Map(x => new TechnologyChoice
                {
                    TechnologyId      = x,
                    TechnologyStackId = request.Id,
                    CreatedBy         = techStack.CreatedBy,
                    LastModifiedBy    = techStack.LastModifiedBy,
                    OwnerId           = techStack.OwnerId,
                });

                var unusedTechChoices = Db.From <TechnologyChoice>().Where(x => x.TechnologyStackId == request.Id);
                if (techIds.Count > 0)
                {
                    unusedTechChoices.And(x => !techIds.Contains(x.TechnologyId));
                }

                await Db.DeleteAsync(unusedTechChoices);

                await Db.InsertAllAsync(techChoices);

                trans.Commit();
            }

            var history = techStack.ConvertTo <TechnologyStackHistory>();

            history.TechnologyStackId = techStack.Id;
            history.Operation         = "UPDATE";
            history.TechnologyIds     = techIds.ToList();
            await Db.InsertAsync(history);

            Cache.FlushAll();

            var response = new UpdateTechnologyStackResponse
            {
                Result = techStack.ConvertTo <TechStackDetails>()
            };

            if (postUpdate)
            {
                var url = TwitterUpdates.BaseUrl.CombineWith(new ClientTechnologyStack {
                    Slug = techStack.Slug
                }.ToUrl());
                response.ResponseStatus = new ResponseStatus
                {
                    Message = PostTwitterUpdate(
                        $"{techStack.Name}'s Stack! {url} ",
                        request.TechnologyIds,
                        maxLength: 140 - (TweetUrlLength - url.Length))
                };
            }

            return(response);
        }