private ActionResult ShowCommon(Revision revision, string slug, bool latest)
        {
            if (revision == null)
            {
                return PageNotFound();
            }

            var title = revision.Metadata.Title;
            int rootId = revision.OwnerId != null ? revision.RootId.Value : revision.Id;
            int ownerId = revision.OwnerId ?? 0;

            title = title.URLFriendly();

            // if this user has a display name, and the title is missing or does not match, permanently redirect to it
            if (title.HasValue() && (string.IsNullOrEmpty(slug) || slug != title))
            {
                string url = "/{0}/query/{2}/{3}";

                if (latest)
                {
                    url = "/{0}/query/{1}/{2}/{3}";
                }

                return PageMovedPermanentlyTo(string.Format(url, new object[] {
                    Site.Name.ToLower(),
                    ownerId,
                    latest ? rootId : revision.Id,
                    title
                }) + Request.Url.Query);
            }

            title = revision.Metadata.Title;

            if (title.IsNullOrEmpty())
            {
                title = revision.Query.AsTitle();
            }

            SetHeader(title);
            SelectMenuItem("Queries");

            ViewData["GuessedUserId"] = Site.GuessUserId(CurrentUser);

            // Need to revamp voting process
            int totalVotes = Current.DB.Query<int>(@"
                SELECT
                    COUNT(*)
                FROM
                    Votes
                WHERE
                    RootId = @root AND
                    VoteTypeId = @voteType AND
                    OwnerId " + (ownerId > 0 ? "= @owner" : "IS NULL"),
                new
                {
                    root = rootId,
                    owner = ownerId,
                    voteType = (int)VoteType.Favorite
                }
            ).FirstOrDefault();

            var voting = new QueryVoting
            {
                TotalVotes = totalVotes,
                RevisionId = revision.Id,
                ReadOnly = CurrentUser.IsAnonymous
            };

            if (!Current.User.IsAnonymous)
            {
                voting.HasVoted = Current.DB.Query<Vote>(@"
                    SELECT
                        *
                    FROM
                        Votes
                    WHERE
                        RootId = @root AND
                        VoteTypeId = @voteType AND
                        UserId = @user AND
                        OwnerId " + (ownerId > 0 ? "= @owner" : "IS NULL"),
                   new
                   {
                       root = rootId,
                       owner = ownerId,
                       voteType = (int)VoteType.Favorite,
                       user = Current.User.Id
                   }
               ).FirstOrDefault() != null;
            }

            CachedResult cachedResults = QueryUtil.GetCachedResults(
                new ParsedQuery(revision.Query.QueryBody, Request.Params),
                Site.Id
            );

            ViewData["QueryVoting"] = voting;
            ViewData["query_action"] = "run/" + Site.Id + "/" + revision.Id;

            if (cachedResults != null)
            {
                ViewData["cached_results"] = new QueryResults
                {
                    RevisionId = revision.Id,
                    SiteId = Site.Id,
                    SiteName = Site.Name,
                    Slug = revision.Metadata.Title.URLFriendly()
                }.WithCache(cachedResults);
            }

            if (!IsSearchEngine())
            {
                QueryViewTracker.TrackQueryView(GetRemoteIP(), rootId, ownerId);
            }

            var viewmodel = new QueryExecutionViewData
            {
                QueryVoting = voting,
                Id = revision.Id,
                Name = revision.Metadata.Title,
                Description = revision.Metadata.Description,
                FavoriteCount = revision.Metadata.Votes,
                Views = revision.Metadata.Views,
                LastRun = revision.Metadata.LastActivity,
                CreatorId = revision.Owner != null ? revision.Owner.Id : (int?)null,
                CreatorLogin = revision.Owner != null ? revision.Owner.Login : null,
                SiteName = Site.Name.ToLower(),
                SQL = revision.Query.QueryBody,
                UseLatestLink = false
            };

            return View("Viewer", viewmodel);
        }
        private void SaveMetadata(Revision revision, string title, string description, bool updateWithoutChange)
        {
            QuerySet querySet = null;

            if (title.IsNullOrEmpty())
            {
                title = null;
            }

            if (description.IsNullOrEmpty())
            {
                description = null;
            }

            if (!CurrentUser.IsAnonymous)
            {
                querySet = Current.DB.Query<QuerySet>(@"
                    select * from QuerySets
                        *
                    FROM
                        QuerySets
                    WHERE
                        InitialRevisionId = @revision AND
                        OwnerId = @owner",
                    new
                    {
                        owner = CurrentUser.Id
                    }
                ).FirstOrDefault();
            }

            // We always save a querys set for anonymous users since they don't have an
            // actual revision history that we're associating the query set with
            if (CurrentUser.IsAnonymous || querySet == null)
            {
                Current.DB.QuerySets.Insert(
                    new
                    {
                        InitialRevisionId = revision.Id,
                        CurrentRevisionId = revision.Id,
                        OwnerId = CurrentUser.IsAnonymous ? (int?)null : CurrentUser.Id,
                        Title = title,
                        Description = description,
                        LastActivity = DateTime.UtcNow,
                        Votes = 0, 
                        Views = 0
                    }
                );
            }
            else if (querySet.Title != title || querySet.Description != description)
            {
                Current.DB.QuerySets.Update(querySet.Id,
                    new
                    {
                        Title = title,
                        Description = description,
                        CurrentRevisionId = revision.Id,
                        LastActivity = DateTime.UtcNow
                    }
                );
            }
        }
        private void SaveMetadata(Revision revision, string title, string description, bool updateWithoutChange)
        {
            Metadata metadata = null;

            if (title.IsNullOrEmpty())
            {
                title = null;
            }

            if (description.IsNullOrEmpty())
            {
                description = null;
            }

            if (!CurrentUser.IsAnonymous)
            {
                metadata = Current.DB.Query<Metadata>(@"
                    SELECT
                        *
                    FROM
                        Metadata
                    WHERE
                        RevisionId = @revision AND
                        OwnerId = @owner",
                    new
                    {
                        revision = revision.RootId,
                        owner = CurrentUser.Id
                    }
                ).FirstOrDefault();
            }

            // We always save a metadata for anonymous users since they don't have an
            // actual revision history that we're associating the metadata with
            if (CurrentUser.IsAnonymous || metadata == null)
            {
                Current.DB.Execute(@"
                    INSERT INTO Metadata(
                        RevisionId, OwnerId, Title, Description,
                        LastQueryId, LastActivity, Votes, Views
                    ) VALUES(
                        @revision, @owner, @title, @description,
                        @query, @activity, 0, 0
                    )",
                    new
                    {
                        revision = CurrentUser.IsAnonymous ? revision.Id : revision.RootId,
                        owner = CurrentUser.IsAnonymous ? (int?)null : CurrentUser.Id,
                        title = title,
                        description = description,
                        query = revision.QueryId,
                        activity = DateTime.UtcNow
                    }
                );
            }
            else if (updateWithoutChange || metadata.Title != title || metadata.Description != description)
            {
                Current.DB.Execute(@"
                    UPDATE
                        Metadata
                    SET
                        Title = @title, Description = @description,
                        LastQueryId = @query, LastActivity = @activity
                    WHERE
                        Id = @id",
                    new
                    {
                        id = metadata.Id,
                        title = title,
                        description = description,
                        query = revision.QueryId,
                        activity = DateTime.UtcNow
                    }
                );
            }
        }
        private ActionResult ShowCommon(Revision revision, string slug, bool latest)
        {
            if (revision == null)
            {
                return PageNotFound();
            }

            var title = revision.QuerySet.Title;
            int ownerId = revision.OwnerId ?? 0;

            title = title.URLFriendly();

            // if this query has a title, and the title is missing or does not match, permanently redirect to it
            if (title.HasValue() && (string.IsNullOrEmpty(slug) || slug != title))
            {
                string url = latest ? "/{0}/query/{1}/{3}" : "/{0}/revision/{1}/{2}/{3}";

                return PageMovedPermanentlyTo(string.Format(url,
                    Site.TinyName.ToLower(),
                    revision.QuerySet.Id,
                    revision.Id,
                    title
                ) + Request.Url.Query);
            }

            title = revision.QuerySet.Title;

            if (title.IsNullOrEmpty())
            {
                title = revision.Query.AsTitle();
            }

            SetHeader(title);
            SelectMenuItem("Queries");

            ViewData["GuessedUserId"] = Site.GuessUserId(CurrentUser);

            // Need to revamp voting process
            int totalVotes = Current.DB.Query<int>(@"
                SELECT
                    COUNT(*)
                FROM
                    Votes
                WHERE
                    QuerySetId = @querySetId AND
                    VoteTypeId = @voteType",
                new
                {
                    querySetId = revision.QuerySet.Id,
                    voteType = (int)VoteType.Favorite
                }
            ).FirstOrDefault();

            var voting = new QuerySetVoting
            {
                TotalVotes = totalVotes,
                QuerySetId = revision.QuerySet.Id,
                ReadOnly = CurrentUser.IsAnonymous
            };

            if (!Current.User.IsAnonymous)
            {
                voting.HasVoted = Current.DB.Query<Vote>(@"
                    SELECT
                        *
                    FROM
                        Votes
                    WHERE
                        QuerySetId = @querySetId AND
                        VoteTypeId = @voteType AND
                        UserId = @user",
                   new
                   {
                       querySetId = revision.QuerySet.Id,
                       voteType = (int)VoteType.Favorite,
                       user = Current.User.Id
                   }
               ).FirstOrDefault() != null;
            }

            CachedResult cachedResults = QueryUtil.GetCachedResults(
                new ParsedQuery(revision.Query.QueryBody, Request.Params),
                Site.Id
            );

            ViewData["QueryVoting"] = voting;
            ViewData["query_action"] = "run/" + Site.Id + "/" + revision.QuerySet.Id + "/" + revision.Id;

            if (cachedResults != null)
            {
                // Don't show cached execution plan, since the user didn't ask for it...
                cachedResults.ExecutionPlan = null;

                ViewData["cached_results"] = new QueryResults
                {
                    RevisionId = revision.Id,
                    QuerySetId = revision.QuerySet.Id,
                    SiteId = Site.Id,
                    SiteName = Site.TinyName.ToLower(),
                    Slug = revision.QuerySet.Title.URLFriendly(),
                    Url = Site.Url
                }.WithCache(cachedResults);
            }

            if (!IsSearchEngine())
            {
                QueryViewTracker.TrackQueryView(GetRemoteIP(), revision.QuerySet.Id);
            }

            var initialRevision = revision.QuerySet.InitialRevision; 

            var viewmodel = new QueryViewerData
            {
                QuerySetVoting = voting,
                Revision = revision
            };

            return View("Viewer", viewmodel);
        }
        public ActionResult Create(string sql, string title, string description, int siteId, int? parentId, bool? textResults, bool? withExecutionPlan, bool? crossSite, bool? excludeMetas)
        {
            ActionResult response = null;

            try
            {
                Revision parent = null;

                if (parentId.HasValue)
                {
                    parent = QueryUtil.GetBasicRevision(parentId.Value);

                    if (parent == null)
                    {
                        throw new ApplicationException("Invalid revision ID");
                    }
                }

                var parsedQuery = new ParsedQuery(
                    sql,
                    Request.Params,
                    withExecutionPlan == true,
                    crossSite == true,
                    excludeMetas == true
                );
                var results = ExecuteWithResults(parsedQuery, siteId, textResults == true);
                var query = Current.DB.Query<Query>(
                    "SELECT * FROM Queries WHERE QueryHash = @hash",
                    new
                    {
                        hash = parsedQuery.Hash
                    }
                ).FirstOrDefault();

                int revisionId = 0, queryId;
                DateTime saveTime;

                // We only create revisions if something actually changed.
                // We'll log it as an execution anyway if applicable, so the user will
                // still get a link in their profile, just not their own revision.
                if (!(parent != null && query != null && query.Id == parent.QueryId))
                {
                    if (query == null)
                    {
                        queryId = (int)Current.DB.Query<decimal>(@"
                            INSERT INTO Queries(
                                QueryHash, QueryBody
                            ) VALUES(
                                @hash, @body
                            )

                            SELECT SCOPE_IDENTITY()",
                            new
                            {
                                hash = parsedQuery.Hash,
                                body = parsedQuery.Sql
                            }
                        ).First();
                    }
                    else
                    {
                        queryId = query.Id;
                    }

                    revisionId = (int)Current.DB.Query<decimal>(@"
                        INSERT INTO Revisions(
                            QueryId, RootId, ParentId, OwnerId, OwnerIP, CreationDate
                        ) VALUES(
                            @query, @root, @parent, @owner, @ip, @creation
                        )

                        SELECT SCOPE_IDENTITY()",
                        new
                        {
                            query = queryId,
                            root = parent != null ? (int?)parent.RootId : null,
                            parent = parentId,
                            owner = CurrentUser.IsAnonymous ? null : (int?)CurrentUser.Id,
                            ip = GetRemoteIP(),
                            creation = saveTime = DateTime.UtcNow
                        }
                    ).First();

                    var revision = new Revision
                    {
                        Id = revisionId,
                        RootId = parent != null ? (int?)parent.RootId : null,
                        QueryId = queryId
                    };

                    SaveMetadata(revision, title, description, true);

                    results.RevisionId = revisionId;
                    results.Created = saveTime;
                }
                else
                {
                    queryId = query.Id;
                    results.RevisionId = parentId.Value;
                }

                if (parent != null)
                {
                    results.ParentId = parent.Id;
                }

                if (title != null)
                {
                    results.Slug = title.URLFriendly();
                }

                QueryRunner.LogQueryExecution(CurrentUser, siteId, results.RevisionId, queryId);

                // Consider handling this XSS condition (?) in the ToJson() method instead, if possible
                response = Content(results.ToJson().Replace("/", "\\/"), "application/json");
            }
            catch (Exception ex)
            {
                response = TransformExecutionException(ex);
            }

            return response;
        }