diff --git a/.circleci/config.yml b/.circleci/config.yml
index e2411ed..6baad65 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -2,13 +2,13 @@ version: 2
jobs:
build:
docker:
- - image: mcr.microsoft.com/dotnet/sdk:6.0
+ - image: mcr.microsoft.com/dotnet/sdk:8.0
steps:
- checkout
- run: dotnet build ./Web/QueryTree.csproj -v n
test:
docker:
- - image: mcr.microsoft.com/dotnet/sdk:6.0
+ - image: mcr.microsoft.com/dotnet/sdk:8.0
steps:
- checkout
- run: dotnet test ./Tests/Tests.csproj -v n
diff --git a/Dockerfile b/Dockerfile
index f840c42..54880ac 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,10 +1,10 @@
-FROM mcr.microsoft.com/dotnet/sdk:6.0 as builder
+FROM mcr.microsoft.com/dotnet/sdk:8.0 as builder
WORKDIR /build
COPY . .
RUN dotnet restore
RUN dotnet publish --no-restore -c Release ./Web/QueryTree.csproj -o /dist
-FROM mcr.microsoft.com/dotnet/aspnet:6.0 as runtime
+FROM mcr.microsoft.com/dotnet/aspnet:8.0 as runtime
WORKDIR /app
COPY --from=builder /dist .
COPY --from=builder /build/Web/EmailTemplates ./EmailTemplates
diff --git a/Engine/DataProcessorNode.cs b/Engine/DataProcessorNode.cs
index baffa53..b944830 100644
--- a/Engine/DataProcessorNode.cs
+++ b/Engine/DataProcessorNode.cs
@@ -32,8 +32,8 @@ internal override void FetchOrderedDependencies(IList dependencies)
}
///
- /// Based on this node's inputs and settings, what columns will it
- /// return, default implementation returns all the columns from its
+ /// Based on this node's inputs and settings, what columns will it
+ /// return, default implementation returns all the columns from its
/// first input
///
/// The columns.
@@ -46,7 +46,7 @@ public override IList GetColumns()
}
///
- /// Based on this node's inputs and settings, what will the types of
+ /// Based on this node's inputs and settings, what will the types of
/// its columns be
///
/// The column types.
diff --git a/Engine/DatabaseTableNode.cs b/Engine/DatabaseTableNode.cs
index 33f54fb..3ca21a2 100644
--- a/Engine/DatabaseTableNode.cs
+++ b/Engine/DatabaseTableNode.cs
@@ -54,8 +54,8 @@ public override void UpdateSettings(Dictionary settings)
}
///
- /// Special version of the GetColumnName for database tables, which
- /// selects the actual table_name.column_name rather than the aliased
+ /// Special version of the GetColumnName for database tables, which
+ /// selects the actual table_name.column_name rather than the aliased
/// name(e.g.node_xxx.Column_x).
///
/// Col number.
@@ -124,7 +124,7 @@ public string GetTableFrom()
public string GetDatabaseFrom()
{
- return GetDatabaseTable(); // In most cases, bit after the FROM clause will just be 'FROM XXX', but joins may return 'FROM XXX JOIN YYY ON XXX.A = YYY.B';
+ return GetDatabaseTable(); // In most cases, bit after the FROM clause will just be 'FROM XXX', but joins may return 'FROM XXX JOIN YYY ON XXX.A = YYY.B';
}
public override string GetQuerySql()
diff --git a/Engine/Engine.csproj b/Engine/Engine.csproj
index cfb111a..351bf5e 100644
--- a/Engine/Engine.csproj
+++ b/Engine/Engine.csproj
@@ -1,13 +1,13 @@
-
-
-
- net6.0
-
-
-
-
-
-
-
-
-
+
+
+
+ net8.0
+
+
+
+
+
+
+
+
+
diff --git a/Engine/ExtractNode.cs b/Engine/ExtractNode.cs
index e511d0d..0541727 100644
--- a/Engine/ExtractNode.cs
+++ b/Engine/ExtractNode.cs
@@ -41,7 +41,7 @@ private string DatabaseLengthFunction()
else
{
return "LENGTH";
- }
+ }
}
public override bool IsConfigured()
@@ -52,7 +52,7 @@ public override bool IsConfigured()
public override void UpdateSettings(Dictionary settings)
{
base.UpdateSettings(settings);
-
+
if (settings.ContainsKey("InputColumnIndex"))
{
InputColumnIndex = Convert.ToInt32(settings["InputColumnIndex"]);
@@ -62,7 +62,7 @@ public override void UpdateSettings(Dictionary settings)
{
StartType = (ExtractStartTypes)Enum.Parse(typeof(ExtractStartTypes), settings["StartType"].ToString());
}
-
+
if (settings.ContainsKey("StartPosition"))
{
StartPosition = Convert.ToInt32(settings["StartPosition"]);
@@ -93,7 +93,7 @@ public override void UpdateSettings(Dictionary settings)
ResultColumnName = (string)settings["ResultColumnName"];
}
}
-
+
public override IList GetColumns()
{
var baseCols = new List();
@@ -279,7 +279,7 @@ public override string GetQuerySql()
newColumnDefinition,
GetColumns().Count - 1,
input1.GetDependencySql());
-
+
return sql;
}
}
diff --git a/Engine/FilterNode.cs b/Engine/FilterNode.cs
index 7f722cd..1df4607 100644
--- a/Engine/FilterNode.cs
+++ b/Engine/FilterNode.cs
@@ -3,7 +3,7 @@
namespace QueryTree.Engine
{
- public enum FilterOperator
+ public enum FilterOperator
{
EqualTo,
DoesNotEqual,
@@ -337,13 +337,13 @@ public override string GetQuerySql()
{
compareValue = "LOWER(" + compareValue + ")";
}
-
+
if (DatabaseType == DatabaseType.PostgreSQL && IsBoolType(columnTypes[FilterColumnIndex.Value]))
{
compareValue = (new List() { "1", "YES", "TRUE" }).Contains(compareValue.ToUpper()) ? "TRUE" : "FALSE";
}
}
-
+
switch (Operator)
{
case FilterOperator.EqualTo:
diff --git a/Engine/GraphNode.cs b/Engine/GraphNode.cs
index a83078d..de536cd 100644
--- a/Engine/GraphNode.cs
+++ b/Engine/GraphNode.cs
@@ -10,7 +10,7 @@ public class GraphNode : DataProcessorNode
private string Values1;
private IList DataSeriesColumnIndexes;
private string NodeType;
-
+
public override void UpdateSettings(Dictionary settings)
{
base.UpdateSettings(settings);
@@ -19,7 +19,7 @@ public override void UpdateSettings(Dictionary settings)
{
HorizontalAxis = (string)settings["HorizontalAxis"];
}
-
+
if (settings.ContainsKey("Values1"))
{
Values1 = (string)settings["Values1"];
@@ -63,7 +63,7 @@ public override IList GetColumns()
}
}
}
-
+
return newCols;
}
@@ -105,7 +105,7 @@ public override string GetQuerySql()
{
SortColumnIndexes = new List() { GetColumns().IndexOf(HorizontalAxis) };
}
-
+
var input1 = InputDict[Inputs[0]];
var input1Cols = input1.GetColumns();
var input1ColTypes = input1.GetColumnTypes();
diff --git a/Engine/JoinNode.cs b/Engine/JoinNode.cs
index da307d2..0c1750a 100644
--- a/Engine/JoinNode.cs
+++ b/Engine/JoinNode.cs
@@ -12,7 +12,7 @@ public enum JoinType
FullOuter,
Cross
}
-
+
public class JoinNode : DataProcessorNode, ICollapsibleQueryNode
{
private JoinType JoinType;
@@ -163,15 +163,15 @@ public string GetRawColumnName(int colNumber)
else
{
return input2.GetColumnName(colNumber - input1.GetColumns().Count);
- }
+ }
}
}
///
- /// Build the FROM xxx JOIN yyy ON xxx.col = yyy.col part of the JOIN
+ /// Build the FROM xxx JOIN yyy ON xxx.col = yyy.col part of the JOIN
/// query.
- ///
- /// If the inputs are themselves data tables or other joins, collapses
+ ///
+ /// If the inputs are themselves data tables or other joins, collapses
/// the query without using CTEs or inline views(in MySQL).
///
/// The table from.
diff --git a/Engine/NodeBase.cs b/Engine/NodeBase.cs
index 6eed410..07ef034 100644
--- a/Engine/NodeBase.cs
+++ b/Engine/NodeBase.cs
@@ -1,302 +1,302 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Linq;
-
-namespace QueryTree.Engine
-{
- public enum DatabaseType
- {
- MySQL = 0,
- PostgreSQL = 3,
- SQLServer = 1
- }
-
- public abstract class NodeBase
- {
- public DatabaseType DatabaseType = DatabaseType.MySQL;
-
- public string ClientId { get; set; }
-
- protected List SortColumnIndexes = new List() { 0 };
- protected List SortDirections = new List() { true }; // True == ascending
-
- ///
- /// Returns a SQL query which will be used to define a view. This view
- /// will be selected from by the NodeBase.get_select_sql code.The
- /// results of that will be passed via the process_data method before
- /// being returned to the client
- ///
- /// The query string.
- public abstract string GetQuerySql();
-
- public abstract bool IsConfigured();
-
- public virtual void UpdateSettings(Dictionary settings)
- {
-
- }
-
- protected string GetSortColumns()
- {
- return String.Join(",", Enumerable.Range(0, SortColumnIndexes.Count).Select(i => string.Format("Column_{0:D} {1}", SortColumnIndexes[i], GetSortDirection(i))));
- }
-
- protected string QuoteName(string name)
- {
- switch (DatabaseType)
- {
- case DatabaseType.SQLServer:
- return "[" + name + "]";
- case DatabaseType.PostgreSQL:
- return "\"" + name + "\"";
- default:
- return "`" + name + "`";
- }
- }
-
- protected virtual string GetSelectColumns()
- {
- var i = 0;
- return String.Join(",", GetColumns().Select(c => string.Format("Column_{0:D} AS {1}", i++, QuoteName(c))));
- }
-
- protected string GetSortDirection(int i)
- {
- if (SortDirections.Count > i && SortDirections[i])
- {
- return "ASC";
- }
- else
- {
- return "DESC";
- }
- }
-
- protected bool IsNumberType(string colType)
- {
- var numericDataTypes = new List() { "INTEGER", "INT", "SMALLINT", "TINYINT", "MEDIUMINT", "BIGINT", "DECIMAL", "NUMERIC", "FLOAT", "DOUBLE", "REAL", "MONEY", "SMALLMONEY", "DOUBLE PRECISION", "SMALLSERIAL", "SERIAL", "BIGSERIAL" };
-
- switch (DatabaseType) {
- case DatabaseType.PostgreSQL:
- // POSTGRES TYPES = ["SMALLINT", "INTEGER", "BIGINT", "DECIMAL", "NUMERIC", "REAL", "DOUBLE PRECISION", "SMALLSERIAL", "SERIAL", "BIGSERIAL", "MONEY"]
-
- case DatabaseType.SQLServer:
- case DatabaseType.MySQL:
- return numericDataTypes.Contains(colType.ToUpper());
- }
-
- return false;
- }
-
- protected bool IsTextType(string colType)
- {
- var quotedTypes = new List() { "VARCHAR", "NVARCHAR", "CHAR", "NCHAR", "ENUM", "XML", "CHARACTER VARYING", "CHARACTER", "TEXT", "USER-DEFINED", "LONGTEXT" };
-
- switch (DatabaseType)
- {
- case DatabaseType.PostgreSQL:
- // # POSTGRES TYPES = ["CHARACTER VARYING", "VARCHAR", "CHARACTER", "CHAR", "TEXT", "TIMESTAMP WITHOUT TIME ZONE", "TIMESTAMP WITH TIME ZONE", "DATE", "TIME WITHOUT TIME ZONE", "TIME WITH TIME ZONE", "INTERVAL", "USER-DEFINED"]
-
- case DatabaseType.SQLServer:
- case DatabaseType.MySQL:
- return quotedTypes.Contains(colType.ToUpper());
- }
-
- return false;
- }
-
- protected bool IsBoolType(string colType)
- {
- var quotedTypes = new List() { "BIT", "BOOL", "BOOLEAN" };
-
- switch (DatabaseType)
- {
- case DatabaseType.PostgreSQL:
- case DatabaseType.SQLServer:
- case DatabaseType.MySQL:
- return quotedTypes.Contains(colType.ToUpper());
- }
-
- return false;
- }
-
- protected bool IsQuotedType(string colType)
- {
- return IsTextType(colType) || IsDateType(colType);
- }
-
- protected bool IsDateType(string colType)
- {
- var dateTypes = new List() { "DATE", "DATETIME", "TIMESTAMP WITHOUT TIME ZONE", "TIMESTAMP", "TIMESTAMP WITH TIME ZONE", "TIME WITHOUT TIME ZONE", "TIME WITH TIME ZONE", "INTERVAL" };
-
- switch (DatabaseType)
- {
- case DatabaseType.PostgreSQL:
- // # POSTGRES TYPES = ["TIMESTAMP WITHOUT TIME ZONE", "TIMESTAMP WITH TIME ZONE", "DATE", "TIME WITHOUT TIME ZONE", "TIME WITH TIME ZONE", "INTERVAL"]
-
- case DatabaseType.SQLServer:
- case DatabaseType.MySQL:
- return dateTypes.Contains(colType.ToUpper());
- }
-
- return false;
- }
-
- protected bool IsCaseTyoStringType(string colType)
- {
- var castToStringTypes = new List() { "XML", "USER-DEFINED" };
-
- switch (DatabaseType)
- {
- case DatabaseType.PostgreSQL:
- case DatabaseType.SQLServer:
- case DatabaseType.MySQL:
- return castToStringTypes.Contains(colType.ToUpper());
- }
-
- return false;
- }
-
- protected virtual string GetSelectSql(int? count = null, int? skip = null)
- {
- var sql = string.Format("SELECT * FROM {0}", GetNodeAlias());
-
- if (count.HasValue && skip.HasValue)
- {
- sql += string.Format(" LIMIT {0:D},{1:D}", skip, count);
- }
-
- return sql;
- }
-
- internal virtual string GetColumnName(int colNumber)
- {
- return string.Format("{0}.Column_{1:D}", GetNodeAlias(), colNumber);
- }
-
- internal virtual string GetNodeAlias()
- {
- return string.Format("node_{0}", ClientId.Replace("-", "_"));
- }
-
- internal virtual string GetTableAlias()
- {
- return string.Format("table_{0}", ClientId.Replace("-", "_"));
- }
-
- public virtual IList GetColumns()
- {
- throw new NotImplementedException("Derived classes should implement the GetColumns method");
- }
-
- public virtual IList GetColumnTypes()
- {
- throw new NotImplementedException("Derived classes should implement the GetColumnTypes method");
- }
-
- private static string BuildWithSql(IEnumerable orderedUpstreamDependencies)
- {
- var query = new StringBuilder("WITH ");
- var separator = "";
-
- foreach (var dependency in orderedUpstreamDependencies)
- {
- query.AppendFormat("{0}{1} AS ({2})", separator, dependency.GetNodeAlias(), dependency.GetQuerySql());
- separator = ",";
- }
- return query.ToString();
- }
-
- internal virtual void FetchOrderedDependencies(IList dependencies)
- {
- if (dependencies.Contains(this))
- {
- dependencies.Remove(this);
- }
-
- dependencies.Insert(0, this);
- }
-
- public string GetFetchDataQuery(int? startRow = null, int? rowCount = null)
- {
- var query = new StringBuilder();
-
- if (DatabaseType == DatabaseType.MySQL)
- {
- query.AppendFormat("SELECT {0} FROM ({1} ORDER BY {2}) AS results",
- GetSelectColumns(),
- GetQuerySql(),
- GetSortColumns());
-
- if (startRow.HasValue && rowCount.HasValue)
- {
- query.AppendFormat(" LIMIT {0:D}, {1:D}", startRow, rowCount);
- }
- }
- else if (DatabaseType == DatabaseType.SQLServer || DatabaseType == DatabaseType.PostgreSQL)
- {
- IList nodes = new List();
- FetchOrderedDependencies(nodes);
- query.Append(BuildWithSql(nodes));
-
- if (startRow.HasValue && rowCount.HasValue)
- {
- // In order for this to work, we will need a ROW_NUMBER column in the CTE, which we
- // don"t select in the following query but which we use in a "WHERE row_num BETWEEN x AND y"
- // clause. To make this work we"ll need all nodes to have a default sort column which
- // we put into the ROW_NUMBER"s ORDER BY clause. The Sort node would then override that sort
- // column with the user selected column
-
- query.AppendFormat(",{0}_with_row_num AS (SELECT *, ROW_NUMBER() OVER (ORDER BY {1}) AS ROW_NUM FROM {2})",
- GetNodeAlias(),
- GetSortColumns(),
- GetNodeAlias());
-
- query.AppendFormat("SELECT {0} FROM {1}_with_row_num WHERE row_num BETWEEN {2:D} AND {3:D}",
- GetSelectColumns(),
- GetNodeAlias(),
- startRow.Value + 1, startRow.Value + rowCount.Value);
- }
- else
- {
- query.AppendFormat("SELECT {0} FROM {1}",
- GetSelectColumns(),
- GetNodeAlias());
- }
-
- query.AppendFormat(" ORDER BY {0}", GetSortColumns());
- }
-
- return query.ToString();
- }
-
- public string GetCountQuerySql()
- {
- var query = new StringBuilder();
-
- if (DatabaseType == DatabaseType.SQLServer || DatabaseType == DatabaseType.PostgreSQL)
- {
- IList nodes = new List();
- FetchOrderedDependencies(nodes);
- query.Append(BuildWithSql(nodes));
- }
-
- query.AppendFormat("SELECT COUNT(*) FROM ({0}) as {1}", GetQuerySql(), GetNodeAlias());
-
- return query.ToString();
- }
-
- internal string GetDependencySql()
- {
- if (DatabaseType == DatabaseType.SQLServer || DatabaseType == DatabaseType.PostgreSQL)
- {
- return GetNodeAlias();
- }
- else
- {
- return string.Format("({0}) AS {1}", GetQuerySql(), GetNodeAlias());
- }
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Linq;
+
+namespace QueryTree.Engine
+{
+ public enum DatabaseType
+ {
+ MySQL = 0,
+ PostgreSQL = 3,
+ SQLServer = 1
+ }
+
+ public abstract class NodeBase
+ {
+ public DatabaseType DatabaseType = DatabaseType.MySQL;
+
+ public string ClientId { get; set; }
+
+ protected List SortColumnIndexes = new List() { 0 };
+ protected List SortDirections = new List() { true }; // True == ascending
+
+ ///
+ /// Returns a SQL query which will be used to define a view. This view
+ /// will be selected from by the NodeBase.get_select_sql code.The
+ /// results of that will be passed via the process_data method before
+ /// being returned to the client
+ ///
+ /// The query string.
+ public abstract string GetQuerySql();
+
+ public abstract bool IsConfigured();
+
+ public virtual void UpdateSettings(Dictionary settings)
+ {
+
+ }
+
+ protected string GetSortColumns()
+ {
+ return String.Join(",", Enumerable.Range(0, SortColumnIndexes.Count).Select(i => string.Format("Column_{0:D} {1}", SortColumnIndexes[i], GetSortDirection(i))));
+ }
+
+ protected string QuoteName(string name)
+ {
+ switch (DatabaseType)
+ {
+ case DatabaseType.SQLServer:
+ return "[" + name + "]";
+ case DatabaseType.PostgreSQL:
+ return "\"" + name + "\"";
+ default:
+ return "`" + name + "`";
+ }
+ }
+
+ protected virtual string GetSelectColumns()
+ {
+ var i = 0;
+ return String.Join(",", GetColumns().Select(c => string.Format("Column_{0:D} AS {1}", i++, QuoteName(c))));
+ }
+
+ protected string GetSortDirection(int i)
+ {
+ if (SortDirections.Count > i && SortDirections[i])
+ {
+ return "ASC";
+ }
+ else
+ {
+ return "DESC";
+ }
+ }
+
+ protected bool IsNumberType(string colType)
+ {
+ var numericDataTypes = new List() { "INTEGER", "INT", "SMALLINT", "TINYINT", "MEDIUMINT", "BIGINT", "DECIMAL", "NUMERIC", "FLOAT", "DOUBLE", "REAL", "MONEY", "SMALLMONEY", "DOUBLE PRECISION", "SMALLSERIAL", "SERIAL", "BIGSERIAL" };
+
+ switch (DatabaseType) {
+ case DatabaseType.PostgreSQL:
+ // POSTGRES TYPES = ["SMALLINT", "INTEGER", "BIGINT", "DECIMAL", "NUMERIC", "REAL", "DOUBLE PRECISION", "SMALLSERIAL", "SERIAL", "BIGSERIAL", "MONEY"]
+
+ case DatabaseType.SQLServer:
+ case DatabaseType.MySQL:
+ return numericDataTypes.Contains(colType.ToUpper());
+ }
+
+ return false;
+ }
+
+ protected bool IsTextType(string colType)
+ {
+ var quotedTypes = new List() { "VARCHAR", "NVARCHAR", "CHAR", "NCHAR", "ENUM", "XML", "CHARACTER VARYING", "CHARACTER", "TEXT", "USER-DEFINED", "LONGTEXT" };
+
+ switch (DatabaseType)
+ {
+ case DatabaseType.PostgreSQL:
+ // # POSTGRES TYPES = ["CHARACTER VARYING", "VARCHAR", "CHARACTER", "CHAR", "TEXT", "TIMESTAMP WITHOUT TIME ZONE", "TIMESTAMP WITH TIME ZONE", "DATE", "TIME WITHOUT TIME ZONE", "TIME WITH TIME ZONE", "INTERVAL", "USER-DEFINED"]
+
+ case DatabaseType.SQLServer:
+ case DatabaseType.MySQL:
+ return quotedTypes.Contains(colType.ToUpper());
+ }
+
+ return false;
+ }
+
+ protected bool IsBoolType(string colType)
+ {
+ var quotedTypes = new List() { "BIT", "BOOL", "BOOLEAN" };
+
+ switch (DatabaseType)
+ {
+ case DatabaseType.PostgreSQL:
+ case DatabaseType.SQLServer:
+ case DatabaseType.MySQL:
+ return quotedTypes.Contains(colType.ToUpper());
+ }
+
+ return false;
+ }
+
+ protected bool IsQuotedType(string colType)
+ {
+ return IsTextType(colType) || IsDateType(colType);
+ }
+
+ protected bool IsDateType(string colType)
+ {
+ var dateTypes = new List() { "DATE", "DATETIME", "TIMESTAMP WITHOUT TIME ZONE", "TIMESTAMP", "TIMESTAMP WITH TIME ZONE", "TIME WITHOUT TIME ZONE", "TIME WITH TIME ZONE", "INTERVAL" };
+
+ switch (DatabaseType)
+ {
+ case DatabaseType.PostgreSQL:
+ // # POSTGRES TYPES = ["TIMESTAMP WITHOUT TIME ZONE", "TIMESTAMP WITH TIME ZONE", "DATE", "TIME WITHOUT TIME ZONE", "TIME WITH TIME ZONE", "INTERVAL"]
+
+ case DatabaseType.SQLServer:
+ case DatabaseType.MySQL:
+ return dateTypes.Contains(colType.ToUpper());
+ }
+
+ return false;
+ }
+
+ protected bool IsCaseTyoStringType(string colType)
+ {
+ var castToStringTypes = new List() { "XML", "USER-DEFINED" };
+
+ switch (DatabaseType)
+ {
+ case DatabaseType.PostgreSQL:
+ case DatabaseType.SQLServer:
+ case DatabaseType.MySQL:
+ return castToStringTypes.Contains(colType.ToUpper());
+ }
+
+ return false;
+ }
+
+ protected virtual string GetSelectSql(int? count = null, int? skip = null)
+ {
+ var sql = string.Format("SELECT * FROM {0}", GetNodeAlias());
+
+ if (count.HasValue && skip.HasValue)
+ {
+ sql += string.Format(" LIMIT {0:D},{1:D}", skip, count);
+ }
+
+ return sql;
+ }
+
+ internal virtual string GetColumnName(int colNumber)
+ {
+ return string.Format("{0}.Column_{1:D}", GetNodeAlias(), colNumber);
+ }
+
+ internal virtual string GetNodeAlias()
+ {
+ return string.Format("node_{0}", ClientId.Replace("-", "_"));
+ }
+
+ internal virtual string GetTableAlias()
+ {
+ return string.Format("table_{0}", ClientId.Replace("-", "_"));
+ }
+
+ public virtual IList GetColumns()
+ {
+ throw new NotImplementedException("Derived classes should implement the GetColumns method");
+ }
+
+ public virtual IList GetColumnTypes()
+ {
+ throw new NotImplementedException("Derived classes should implement the GetColumnTypes method");
+ }
+
+ private static string BuildWithSql(IEnumerable orderedUpstreamDependencies)
+ {
+ var query = new StringBuilder("WITH ");
+ var separator = "";
+
+ foreach (var dependency in orderedUpstreamDependencies)
+ {
+ query.AppendFormat("{0}{1} AS ({2})", separator, dependency.GetNodeAlias(), dependency.GetQuerySql());
+ separator = ",";
+ }
+ return query.ToString();
+ }
+
+ internal virtual void FetchOrderedDependencies(IList dependencies)
+ {
+ if (dependencies.Contains(this))
+ {
+ dependencies.Remove(this);
+ }
+
+ dependencies.Insert(0, this);
+ }
+
+ public string GetFetchDataQuery(int? startRow = null, int? rowCount = null)
+ {
+ var query = new StringBuilder();
+
+ if (DatabaseType == DatabaseType.MySQL)
+ {
+ query.AppendFormat("SELECT {0} FROM ({1} ORDER BY {2}) AS results",
+ GetSelectColumns(),
+ GetQuerySql(),
+ GetSortColumns());
+
+ if (startRow.HasValue && rowCount.HasValue)
+ {
+ query.AppendFormat(" LIMIT {0:D}, {1:D}", startRow, rowCount);
+ }
+ }
+ else if (DatabaseType == DatabaseType.SQLServer || DatabaseType == DatabaseType.PostgreSQL)
+ {
+ IList nodes = new List();
+ FetchOrderedDependencies(nodes);
+ query.Append(BuildWithSql(nodes));
+
+ if (startRow.HasValue && rowCount.HasValue)
+ {
+ // In order for this to work, we will need a ROW_NUMBER column in the CTE, which we
+ // don"t select in the following query but which we use in a "WHERE row_num BETWEEN x AND y"
+ // clause. To make this work we"ll need all nodes to have a default sort column which
+ // we put into the ROW_NUMBER"s ORDER BY clause. The Sort node would then override that sort
+ // column with the user selected column
+
+ query.AppendFormat(",{0}_with_row_num AS (SELECT *, ROW_NUMBER() OVER (ORDER BY {1}) AS ROW_NUM FROM {2})",
+ GetNodeAlias(),
+ GetSortColumns(),
+ GetNodeAlias());
+
+ query.AppendFormat("SELECT {0} FROM {1}_with_row_num WHERE row_num BETWEEN {2:D} AND {3:D}",
+ GetSelectColumns(),
+ GetNodeAlias(),
+ startRow.Value + 1, startRow.Value + rowCount.Value);
+ }
+ else
+ {
+ query.AppendFormat("SELECT {0} FROM {1}",
+ GetSelectColumns(),
+ GetNodeAlias());
+ }
+
+ query.AppendFormat(" ORDER BY {0}", GetSortColumns());
+ }
+
+ return query.ToString();
+ }
+
+ public string GetCountQuerySql()
+ {
+ var query = new StringBuilder();
+
+ if (DatabaseType == DatabaseType.SQLServer || DatabaseType == DatabaseType.PostgreSQL)
+ {
+ IList nodes = new List();
+ FetchOrderedDependencies(nodes);
+ query.Append(BuildWithSql(nodes));
+ }
+
+ query.AppendFormat("SELECT COUNT(*) FROM ({0}) as {1}", GetQuerySql(), GetNodeAlias());
+
+ return query.ToString();
+ }
+
+ internal string GetDependencySql()
+ {
+ if (DatabaseType == DatabaseType.SQLServer || DatabaseType == DatabaseType.PostgreSQL)
+ {
+ return GetNodeAlias();
+ }
+ else
+ {
+ return string.Format("({0}) AS {1}", GetQuerySql(), GetNodeAlias());
+ }
+ }
+ }
+}
diff --git a/Engine/Query.cs b/Engine/Query.cs
index 8acf69a..fa8bdbc 100644
--- a/Engine/Query.cs
+++ b/Engine/Query.cs
@@ -8,7 +8,7 @@ namespace QueryTree.Engine
public class Query
{
private List Nodes;
-
+
public Query(DatabaseType type, string queryJson, IList tables)
{
var nodeSettings = JsonConvert.DeserializeObject>>(queryJson);
diff --git a/Engine/SelectNode.cs b/Engine/SelectNode.cs
index 94f157b..dac8203 100644
--- a/Engine/SelectNode.cs
+++ b/Engine/SelectNode.cs
@@ -30,7 +30,7 @@ public override void UpdateSettings(Dictionary settings)
public override bool IsConfigured()
{
return Inputs.Count == 1
- //&& len(self.columns) > 0
+ //&& len(self.columns) > 0
&& IncludedColumnIndexes.Count > 0;
}
diff --git a/Engine/SummarizeNode.cs b/Engine/SummarizeNode.cs
index 00d9cfc..abaf575 100644
--- a/Engine/SummarizeNode.cs
+++ b/Engine/SummarizeNode.cs
@@ -188,7 +188,7 @@ public override IList GetColumns()
return columns;
}
-
+
public override IList GetColumnTypes()
{
var colTypes = new List();
@@ -254,7 +254,7 @@ public override string GetQuerySql()
}
var aggStr = GetAggStr(
- aggFunction != AggregationFunction.Count ? AggColumnIndexes[i] : 0,
+ aggFunction != AggregationFunction.Count ? AggColumnIndexes[i] : 0,
aggFunction);
selectCols.Add(string.Format("{0} AS Column_{1:D}", aggStr, selectCols.Count));
diff --git a/QueryTree.sln b/QueryTree.sln
index 2701538..ba002b1 100644
--- a/QueryTree.sln
+++ b/QueryTree.sln
@@ -1,29 +1,29 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2012
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine", "Engine\Engine.csproj", "{4687C4BB-B4D5-4549-A0E1-E2AEA17CF3CB}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{61DC1986-95A7-4B95-A6B6-F3DBA74CACCB}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QueryTree", "Web\QueryTree.csproj", "{D9B429A4-CF3C-446D-B884-A7E6058514FB}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {4687C4BB-B4D5-4549-A0E1-E2AEA17CF3CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4687C4BB-B4D5-4549-A0E1-E2AEA17CF3CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4687C4BB-B4D5-4549-A0E1-E2AEA17CF3CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4687C4BB-B4D5-4549-A0E1-E2AEA17CF3CB}.Release|Any CPU.Build.0 = Release|Any CPU
- {61DC1986-95A7-4B95-A6B6-F3DBA74CACCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {61DC1986-95A7-4B95-A6B6-F3DBA74CACCB}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {61DC1986-95A7-4B95-A6B6-F3DBA74CACCB}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {61DC1986-95A7-4B95-A6B6-F3DBA74CACCB}.Release|Any CPU.Build.0 = Release|Any CPU
- {D9B429A4-CF3C-446D-B884-A7E6058514FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D9B429A4-CF3C-446D-B884-A7E6058514FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D9B429A4-CF3C-446D-B884-A7E6058514FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D9B429A4-CF3C-446D-B884-A7E6058514FB}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine", "Engine\Engine.csproj", "{4687C4BB-B4D5-4549-A0E1-E2AEA17CF3CB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{61DC1986-95A7-4B95-A6B6-F3DBA74CACCB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QueryTree", "Web\QueryTree.csproj", "{D9B429A4-CF3C-446D-B884-A7E6058514FB}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {4687C4BB-B4D5-4549-A0E1-E2AEA17CF3CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4687C4BB-B4D5-4549-A0E1-E2AEA17CF3CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4687C4BB-B4D5-4549-A0E1-E2AEA17CF3CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4687C4BB-B4D5-4549-A0E1-E2AEA17CF3CB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {61DC1986-95A7-4B95-A6B6-F3DBA74CACCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {61DC1986-95A7-4B95-A6B6-F3DBA74CACCB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {61DC1986-95A7-4B95-A6B6-F3DBA74CACCB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {61DC1986-95A7-4B95-A6B6-F3DBA74CACCB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D9B429A4-CF3C-446D-B884-A7E6058514FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D9B429A4-CF3C-446D-B884-A7E6058514FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D9B429A4-CF3C-446D-B884-A7E6058514FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D9B429A4-CF3C-446D-B884-A7E6058514FB}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/README.md b/README.md
index 3b0c3d7..b017cdc 100644
--- a/README.md
+++ b/README.md
@@ -3,8 +3,8 @@
# QueryTree
-QueryTree is an ad-hoc reporting tool that works with any Microsoft
-SQL Server, PostgreSQL or MySQL database. It allows users to query
+QueryTree is an ad-hoc reporting tool that works with any Microsoft
+SQL Server, PostgreSQL or MySQL database. It allows users to query
databases, build reports and schedule those reports to email distribution
lists, without needing to write any code.
@@ -12,7 +12,7 @@ For more information see the [QueryTree website](http://querytreeapp.com)
## Features
-QueryTree can connect to MySQL and PostgreSQL databases using SSH tunnels,
+QueryTree can connect to MySQL and PostgreSQL databases using SSH tunnels,
secured with passwords or key files.
Supports customization of the logo image, system name and CSS used
@@ -21,14 +21,14 @@ within the app.
Can use either Sqlite or Microsoft SQL Server database for it's own user
and reports data storage.
-Database and SSH passwords are stored in its database in encryped form,
+Database and SSH passwords are stored in its database in encryped form,
using AES encryption. Users may provide their own key file, or let the
system generate one on first run. Keys can be shared between mutliple
web servers in a load balancing scenario.
-Users may choose to build their own plugins to store database/SSH
-passwords in an external key vault. This is achieved by implementing
-a .NET interface and registering the class in the appSettings.config
+Users may choose to build their own plugins to store database/SSH
+passwords in an external key vault. This is achieved by implementing
+a .NET interface and registering the class in the appSettings.config
file. See [Building a password manager](/docs/password-manager.md)
for more information.
@@ -112,7 +112,7 @@ Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.
```
-Visit the URL shown in your browser. You should see the QueryTree application.
+Visit the URL shown in your browser. You should see the QueryTree application.
5. For use in production environments, QueryTree should be run behind a reverse proxy such as nginx. For more information on hosting QueryTree using nginx see: https://docs.microsoft.com/en-us/aspnet/core/publishing/linuxproduction
diff --git a/Tests/AppendTests.cs b/Tests/AppendTests.cs
index 7e7a17c..f578e95 100644
--- a/Tests/AppendTests.cs
+++ b/Tests/AppendTests.cs
@@ -100,7 +100,7 @@ public void TestMultipleRoutesToDataTablesDoesntDefineThemTwice()
int i = -1;
while ((i = sql.IndexOf("node_1 AS", i+1)) >= 0)
c++;
-
+
// The "node_1" datatable should only be defined once in the query
Assert.Equal(1, c);
}
diff --git a/Tests/DataTableTests.cs b/Tests/DataTableTests.cs
index fae1813..735b94a 100644
--- a/Tests/DataTableTests.cs
+++ b/Tests/DataTableTests.cs
@@ -1,70 +1,70 @@
-using System;
-using Xunit;
-using QueryTree.Engine;
-using System.Collections.Generic;
-
-namespace QueryTree.Engine.Tests
-{
- public class DataTableTests
- {
- private string NodesJson
- {
- get
- {
- return @"[
- {
- ""Id"": ""1"",
- ""Type"": ""Data Table"",
- ""Table"": ""employees""
- }
- ]";
- }
- }
-
- private List DatabaseInfo
- {
- get
- {
- return new List()
- {
- new MockTableInfo()
- {
- DisplayName = "employees",
- Columns = new List()
- {
- new MockColumnInfo() { DataType = "int", Name = "ID" },
- new MockColumnInfo() { DataType = "varchar", Name = "Name" }
- }
- }
- };
- }
- }
-
- [Fact]
- public void TestFromClause()
- {
- var query = new Query(
- DatabaseType.MySQL,
- NodesJson,
- DatabaseInfo);
-
- var sql = query.GetSql("1");
-
- Assert.True(sql.Contains("FROM `employees` AS"), "SQL Value was: " + sql);
- }
-
- [Fact]
- public void TestColumns()
- {
- var query = new Query(
- DatabaseType.MySQL,
- NodesJson,
- DatabaseInfo);
-
- var sql = query.GetSql("1");
-
- Assert.True(sql.Contains("`ID` AS "), "SQL Value was: " + sql);
- Assert.True(sql.Contains("`Name` AS "), "SQL Value was: " + sql);
- }
- }
-}
+using System;
+using Xunit;
+using QueryTree.Engine;
+using System.Collections.Generic;
+
+namespace QueryTree.Engine.Tests
+{
+ public class DataTableTests
+ {
+ private string NodesJson
+ {
+ get
+ {
+ return @"[
+ {
+ ""Id"": ""1"",
+ ""Type"": ""Data Table"",
+ ""Table"": ""employees""
+ }
+ ]";
+ }
+ }
+
+ private List DatabaseInfo
+ {
+ get
+ {
+ return new List()
+ {
+ new MockTableInfo()
+ {
+ DisplayName = "employees",
+ Columns = new List()
+ {
+ new MockColumnInfo() { DataType = "int", Name = "ID" },
+ new MockColumnInfo() { DataType = "varchar", Name = "Name" }
+ }
+ }
+ };
+ }
+ }
+
+ [Fact]
+ public void TestFromClause()
+ {
+ var query = new Query(
+ DatabaseType.MySQL,
+ NodesJson,
+ DatabaseInfo);
+
+ var sql = query.GetSql("1");
+
+ Assert.True(sql.Contains("FROM `employees` AS"), "SQL Value was: " + sql);
+ }
+
+ [Fact]
+ public void TestColumns()
+ {
+ var query = new Query(
+ DatabaseType.MySQL,
+ NodesJson,
+ DatabaseInfo);
+
+ var sql = query.GetSql("1");
+
+ Assert.True(sql.Contains("`ID` AS "), "SQL Value was: " + sql);
+ Assert.True(sql.Contains("`Name` AS "), "SQL Value was: " + sql);
+ }
+ }
+}
diff --git a/Tests/FilterTests.cs b/Tests/FilterTests.cs
index 89ef384..fc6e071 100644
--- a/Tests/FilterTests.cs
+++ b/Tests/FilterTests.cs
@@ -94,7 +94,7 @@ private string NodesJsonBoolNotEqual
]";
}
}
-
+
private string NodesJsonGreaterThanDate
{
get
diff --git a/Tests/SortTests.cs b/Tests/SortTests.cs
index 1a201f1..6580722 100644
--- a/Tests/SortTests.cs
+++ b/Tests/SortTests.cs
@@ -9,7 +9,7 @@ public class SortTests
{
private string NodesJson
{
- get
+ get
{
return @"[
{
@@ -30,7 +30,7 @@ private string NodesJson
private List DatabaseInfo
{
- get
+ get
{
return new List()
{
diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj
index 6073115..ccd49aa 100644
--- a/Tests/Tests.csproj
+++ b/Tests/Tests.csproj
@@ -1,19 +1,19 @@
-
-
-
- net6.0
-
-
-
-
-
-
+
+
+
+ net8.0
+
+
+
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/Web/Controllers/AccountController.cs b/Web/Controllers/AccountController.cs
index fd412a6..a81511d 100644
--- a/Web/Controllers/AccountController.cs
+++ b/Web/Controllers/AccountController.cs
@@ -121,7 +121,7 @@ public async Task Login(LoginViewModel model, string returnUrl =
// If we got this far, something failed, redisplay form
return View(model);
}
-
+
[HttpGet]
[AllowAnonymous]
public IActionResult Register(string email)
@@ -173,7 +173,7 @@ public async Task Register(RegisterViewModel model)
var user = await CreateUser(model.FirstName, model.LastName, model.OrganisationName, model.Email, model.Password);
if (user != null)
- {
+ {
await _signInManager.SignInAsync(user, isPersistent:false);
return RedirectToAction("Index", "Home");
@@ -335,12 +335,12 @@ public ActionResult Index(string message = null)
}
vm.OtherConnections = otherConnections;
-
+
ViewBag.InfoAlert = message;
return View(vm);
}
-
+
[HttpPost]
[ValidateAntiForgeryToken]
public async Task Index(SettingsViewModel settings)
@@ -348,7 +348,7 @@ public async Task Index(SettingsViewModel settings)
db.Entry(CurrentUser).Reference(u => u.Organisation).Load();
CurrentUser.Organisation.OrganisationName = settings.OrganisationName;
-
+
await db.SaveChangesAsync();
return Index();
diff --git a/Web/Controllers/ApiController.cs b/Web/Controllers/ApiController.cs
index 7f3395e..b355af1 100644
--- a/Web/Controllers/ApiController.cs
+++ b/Web/Controllers/ApiController.cs
@@ -36,11 +36,11 @@ public class ApiController : Controller
protected UserManager _userManager;
- public ApiController(UserManager userManager,
+ public ApiController(UserManager userManager,
ApplicationDbContext dbContext,
- IMemoryCache cache,
- IPasswordManager passwordManager,
- IConfiguration config,
+ IMemoryCache cache,
+ IPasswordManager passwordManager,
+ IConfiguration config,
IScheduledEmailManager scheduledEmailManager,
IWebHostEnvironment env,
ILoggerFactory loggerFactory)
@@ -109,7 +109,7 @@ public ActionResult TestConnection(int type, string server, int port, string use
credentials = new SshProxyCredentials(_passwordManager, sshUsername, sshPassword);
}
}
-
+
string error = null;
if (_dbMgr.TryUseDbConnection(dbType, server, port, useSsh, sshServer, sshPort, credentials, username, password, databaseName, out error))
{
@@ -142,12 +142,12 @@ public ActionResult GetConnectionStatus(int databaseId)
var connection = GetConnection(databaseId);
if (CanUserAccessDatabase(connection))
- {
+ {
var statusText = _dbMgr.CheckConnection(connection) ? "ok" : "error";
return Json(new { status = statusText });
}
-
+
return NotFound();
}
@@ -235,7 +235,7 @@ public ActionResult RunQuery(string id, string nodeId, int? startRow = null, int
return Json(data);
}
- else
+ else
{
return NotFound();
}
@@ -261,7 +261,7 @@ public ActionResult ExportQuery(string id, string nodeId, int? startRow = null,
return File(result, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "export.xlsx");
}
-
+
return NotFound();
}
@@ -418,7 +418,7 @@ public async Task Schedule(int id)
.Include(s => s.Query)
.Include(s => s.Query.DatabaseConnection)
.FirstOrDefaultAsync(m => m.ScheduleID == id);
-
+
ScheduledReportViewModel model = null;
if (result == null)
@@ -430,7 +430,7 @@ public async Task Schedule(int id)
model = new ScheduledReportViewModel(result);
return Json(model);
}
-
+
return NotFound();
}
@@ -460,7 +460,7 @@ public ActionResult GetData(int queryId, GetDataRequest req)
return Json(dataTable);
}
-
+
return NotFound();
}
@@ -471,7 +471,7 @@ public ActionResult QueryColumnsName(int queryId)
.Include(q => q.DatabaseConnection)
.ThenInclude(c => c.SshKeyFile)
.FirstOrDefault(q => q.QueryID == queryId);
-
+
if (query != null && CanUserAccessDatabase(query.DatabaseConnection))
{
var queryDefinition = JsonConvert.DeserializeObject(query.QueryDefinition);
@@ -482,7 +482,7 @@ public ActionResult QueryColumnsName(int queryId)
return Json(data.Columns);
}
-
+
return NotFound();
}
diff --git a/Web/Controllers/HomeController.cs b/Web/Controllers/HomeController.cs
index e8c6e3c..8d7e6b3 100644
--- a/Web/Controllers/HomeController.cs
+++ b/Web/Controllers/HomeController.cs
@@ -39,7 +39,7 @@ public HomeController(
public ActionResult Index()
{
string Email = User.Identity.Name;
-
+
if (CurrentUser == null)
{
return RedirectToAction("Login", "Account");
@@ -58,7 +58,7 @@ public ActionResult Index()
.Include(d => d.Queries)
.ThenInclude(q => q.ScheduledReport)
.ToList();
-
+
foreach (var dbConn in dbConns)
{
dbConnView.Add(new DatabaseConnectionIndexViewModel
@@ -86,7 +86,7 @@ public ActionResult Index()
ReportsCount = db.Queries.Count(q => q.DatabaseConnectionID == conn.Db.DatabaseConnectionID)
});
}
-
+
return View(dbConnView);
}
@@ -94,7 +94,7 @@ public ActionResult Index()
public ActionResult Details(int id)
{
var database = db.DatabaseConnections.FirstOrDefault(dc => dc.DatabaseConnectionID == id);
-
+
if (database == null)
{
return NotFound();
@@ -108,7 +108,7 @@ public ActionResult Details(int id)
{
return NotFound();
}
-
+
ViewBag.UserIsOrganisationAdmin = database.OrganisationId == CurrentUser.OrganisationId;
ViewBag.UserCanModifyQueries = PermissionMgr.UserCanModifyQuery(userPermissions, database) || database.OrganisationId == CurrentUser.OrganisationId;
ViewBag.UserCanModifyDatabase = PermissionMgr.UserCanModifyDatabase(userPermissions, database) || database.OrganisationId == CurrentUser.OrganisationId;
@@ -132,12 +132,12 @@ public ActionResult Details(int id)
AccessUsers = new List()
};
List viewQueries = new List();
-
+
var queries = db.Queries
.Include(q => q.CreatedBy)
.Include(q => q.LastEditedBy)
.Where(q => q.DatabaseConnectionID == database.DatabaseConnectionID);
-
+
foreach(var query in queries)
{
DatabaseConnectionQueriesDetailsViewModel queryView = new DatabaseConnectionQueriesDetailsViewModel();
@@ -153,7 +153,7 @@ public ActionResult Details(int id)
}
viewModel.SavedQueries = viewQueries.OrderByDescending(q => q.LastEditedOn);
-
+
if (database.Organisation != null)
{
viewModel.OrganisationName = database.Organisation.OrganisationName;
@@ -192,7 +192,7 @@ public ActionResult Details(int id)
public ActionResult Create()
{
ViewBag.TestMessage = "";
-
+
return View(new DatabaseConnectionViewModel
{
SshPort = 22
@@ -200,7 +200,7 @@ public ActionResult Create()
}
// POST: DatabaseConnections/Create
- // To protect from overposting attacks, please enable the specific properties you want to bind to, for
+ // To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
@@ -210,7 +210,7 @@ public ActionResult Create(DatabaseConnectionViewModel viewModel)
ViewBag.WillUpgrade = false;
if (ModelState.IsValid)
- {
+ {
DatabaseConnection connection = new DatabaseConnection
{
CreatedOn = DateTime.Now,
@@ -246,7 +246,7 @@ private void DeleteSecureInformation(DatabaseConnection connection)
_passwordManager.DeleteSecret(SecretType.SSHPassword.ToString() + "_" + connection.DatabaseConnectionID);
}
}
-
+
// GET: DatabaseConnections/Edit/5
public ActionResult Edit(int? id)
{
@@ -254,7 +254,7 @@ public ActionResult Edit(int? id)
{
return BadRequest();
}
-
+
var database = db.DatabaseConnections.FirstOrDefault(dc => dc.DatabaseConnectionID == id.Value);
if (database == null)
@@ -265,16 +265,16 @@ public ActionResult Edit(int? id)
var userPermissions = db.UserDatabaseConnections
.Where(uc => uc.ApplicationUserID == CurrentUser.Id)
.ToList();
-
+
if (PermissionMgr.UserCanModifyDatabase(userPermissions, database) == false && database.OrganisationId != CurrentUser.OrganisationId)
{
return NotFound();
}
ViewBag.hasExistingKeyFile = database.UseSshKey;
-
+
var viewModel = new DatabaseConnectionViewModel(database);
-
+
if (viewModel.SshPort == null)
{
viewModel.SshPort = 22;
@@ -349,7 +349,7 @@ private bool TryMapViewModel(DatabaseConnectionViewModel viewModel, ref Database
}
// POST: DatabaseConnections/Edit/5
- // To protect from overposting attacks, please enable the specific properties you want to bind to, for
+ // To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
@@ -360,7 +360,7 @@ public ActionResult Edit(DatabaseConnectionViewModel viewModel)
{
return NotFound();
}
-
+
if (ModelState.IsValid)
{
string error;
@@ -376,7 +376,7 @@ public ActionResult Edit(DatabaseConnectionViewModel viewModel)
db.SaveChanges();
SaveSecureInformation(existingConnection, viewModel);
-
+
return RedirectToAction("Details", new { id = existingConnection.DatabaseConnectionID });
}
@@ -392,16 +392,16 @@ public ActionResult Delete(int? id)
}
DatabaseConnection database = db.DatabaseConnections.Find(id);
-
+
if (database == null)
{
return NotFound();
}
-
+
var userPermissions = db.UserDatabaseConnections
.Where(uc => uc.ApplicationUserID == CurrentUser.Id)
.ToList();
-
+
if (PermissionMgr.UserCanDeleteDatabase(userPermissions, database) == false && database.OrganisationId != CurrentUser.OrganisationId)
{
return NotFound();
@@ -441,13 +441,13 @@ public ActionResult DeleteConfirmed(int id)
return RedirectToAction("Index");
}
-
+
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
-
+
}
base.Dispose(disposing);
}
diff --git a/Web/Controllers/InvitationsController.cs b/Web/Controllers/InvitationsController.cs
index 186270c..b6b5aa4 100644
--- a/Web/Controllers/InvitationsController.cs
+++ b/Web/Controllers/InvitationsController.cs
@@ -68,7 +68,7 @@ public ActionResult Index()
{
OrganisationInviteId = orgGrp.First().OrganisationInviteId
};
-
+
Organisation organisation = db.Organisations.First(ba => ba.OrganisationId == orgGrp.Key);
viewModel.OrganisationId = organisation.OrganisationId;
@@ -77,9 +77,9 @@ public ActionResult Index()
.OrderBy(_ => _)
.Distinct()
.ToList();
-
+
viewModel.OrganisationName = organisation.OrganisationName;
-
+
viewModels.Add(viewModel);
}
@@ -87,7 +87,7 @@ public ActionResult Index()
{
List databasesMerged = new List();
List databasesLost = new List();
-
+
List databases = db.DatabaseConnections
.Where(d => d.OrganisationId == CurrentUser.OrganisationId)
.ToList()
@@ -107,7 +107,7 @@ public ActionResult Index()
{
databasesLost = databases;
}
-
+
foreach(var viewModel in viewModels)
{
viewModel.DatabasesMerged = databasesMerged;
@@ -141,7 +141,7 @@ public ActionResult Accept(int id)
List leave = new List();
List migrate = new List();
-
+
// Migrate or cut connections with databases as necessary.
List databases = db.DatabaseConnections
.Where(d => d.OrganisationId == CurrentUser.OrganisationId)
@@ -160,7 +160,7 @@ public ActionResult Accept(int id)
{
leave = databases;
}
-
+
foreach(var database in leave)
{
// there shouldn't be any of these, but do it just in case
@@ -171,17 +171,17 @@ public ActionResult Accept(int id)
{
// there shouldn't be any of these, but do it just in case
db.UserDatabaseConnections.RemoveWhere(uc => uc.DatabaseConnectionID == database.DatabaseConnectionID && uc.ApplicationUserID == CurrentUser.Id);
-
+
database.Organisation = organisation;
}
-
+
CurrentUser.OrganisationId = organisation.OrganisationId;
invite.AcceptedOn = DateTime.Now;
-
+
// reject other invitations to other organisations
var invitesToReject = db.OrganisationInvites.Where(uc => uc.InviteEmail.ToLower() == CurrentUser.Email.ToLower() && uc.OrganisationInviteId != invite.OrganisationInviteId);
-
+
foreach (var inviteToReject in invitesToReject)
{
inviteToReject.RejectedOn = DateTime.Now;
@@ -191,14 +191,14 @@ public ActionResult Accept(int id)
return RedirectToAction("Index", "Home");
}
-
+
public ActionResult Reject(int id)
{
if (CurrentUser == null)
{
return NotFound("Could not find user");
}
-
+
var invite = db.OrganisationInvites.FirstOrDefault(uc => uc.InviteEmail.ToLower() == CurrentUser.Email.ToLower() && uc.AcceptedOn == null && uc.RejectedOn == null && uc.OrganisationInviteId == id);
if (invite == null)
diff --git a/Web/Controllers/ManageController.cs b/Web/Controllers/ManageController.cs
index f5c4c59..31af303 100644
--- a/Web/Controllers/ManageController.cs
+++ b/Web/Controllers/ManageController.cs
@@ -58,10 +58,10 @@ public async Task ChangePassword(ChangePasswordViewModel model)
}
AddErrors(result);
}
-
+
return View(model);
}
-
+
#region Helpers
private void AddErrors(IdentityResult result)
diff --git a/Web/Controllers/QueriesController.cs b/Web/Controllers/QueriesController.cs
index 81afeb3..8c01841 100644
--- a/Web/Controllers/QueriesController.cs
+++ b/Web/Controllers/QueriesController.cs
@@ -15,7 +15,7 @@ public class QueriesController : IdentityController
{
public QueriesController(
ApplicationDbContext dbContext,
- UserManager userManager)
+ UserManager userManager)
: base(userManager, dbContext)
{
}
@@ -43,7 +43,7 @@ public ActionResult Details(int? id)
var userPermissions = db.UserDatabaseConnections
.Where(uc => uc.ApplicationUserID == CurrentUser.Id)
.ToList();
-
+
if (PermissionMgr.UserCanViewQuery(userPermissions, database) == false && database.OrganisationId != CurrentUser.OrganisationId)
{
return NotFound();
@@ -67,7 +67,7 @@ public ActionResult Create(int connectionId = 0)
var userPermissions = db.UserDatabaseConnections
.Where(uc => uc.ApplicationUserID == CurrentUser.Id)
.ToList();
-
+
if (PermissionMgr.UserCanModifyQuery(userPermissions, database) == false && database.OrganisationId != CurrentUser.OrganisationId)
{
return NotFound();
@@ -79,7 +79,7 @@ public ActionResult Create(int connectionId = 0)
}
// POST: Queries/Create
- // To protect from overposting attacks, please enable the specific properties you want to bind to, for
+ // To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
@@ -95,7 +95,7 @@ public ActionResult Create(Query query)
var userPermissions = db.UserDatabaseConnections
.Where(uc => uc.ApplicationUserID == CurrentUser.Id)
.ToList();
-
+
if (PermissionMgr.UserCanModifyQuery(userPermissions, database) == false && database.OrganisationId != CurrentUser.OrganisationId)
{
return NotFound();
@@ -140,7 +140,7 @@ public ActionResult Edit(int id)
var userPermissions = db.UserDatabaseConnections
.Where(uc => uc.ApplicationUserID == CurrentUser.Id)
.ToList();
-
+
if (PermissionMgr.UserCanModifyQuery(userPermissions, database) == false && database.OrganisationId != CurrentUser.OrganisationId)
{
return NotFound();
@@ -150,14 +150,14 @@ public ActionResult Edit(int id)
}
// POST: Queries/Edit/5
- // To protect from overposting attacks, please enable the specific properties you want to bind to, for
+ // To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Query query)
{
Query dbQuery = db.Queries.Find(query.QueryID);
-
+
if (dbQuery == null)
{
return NotFound();
@@ -204,7 +204,7 @@ public ActionResult Delete(int? id)
{
return BadRequest();
}
-
+
Query query = db.Queries.Find(id.Value);
if (query == null)
{
@@ -221,7 +221,7 @@ public ActionResult Delete(int? id)
var userPermissions = db.UserDatabaseConnections
.Where(uc => uc.ApplicationUserID == CurrentUser.Id)
.ToList();
-
+
if (PermissionMgr.UserCanModifyQuery(userPermissions, database) == false && database.OrganisationId != CurrentUser.OrganisationId)
{
return NotFound();
@@ -251,7 +251,7 @@ public ActionResult DeleteConfirmed(int id)
var userPermissions = db.UserDatabaseConnections
.Where(uc => uc.ApplicationUserID == CurrentUser.Id)
.ToList();
-
+
if (PermissionMgr.UserCanModifyQuery(userPermissions, database) == false && database.OrganisationId != CurrentUser.OrganisationId)
{
return NotFound();
diff --git a/Web/Controllers/SimpleController.cs b/Web/Controllers/SimpleController.cs
index c48a354..c92a6b0 100644
--- a/Web/Controllers/SimpleController.cs
+++ b/Web/Controllers/SimpleController.cs
@@ -31,7 +31,7 @@ public ActionResult Create(int connectionId)
var userPermissions = db.UserDatabaseConnections
.Where(uc => uc.ApplicationUserID == CurrentUser.Id)
.ToList();
-
+
if (PermissionMgr.UserCanModifyQuery(userPermissions, database) == false && database.OrganisationId != CurrentUser.OrganisationId)
{
return NotFound();
@@ -58,7 +58,7 @@ public ActionResult Create(int connectionId)
}
// POST: Simple/Create
- // To protect from overposting attacks, please enable the specific properties you want to bind to, for
+ // To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
@@ -74,7 +74,7 @@ public ActionResult Create(Query query)
var userPermissions = db.UserDatabaseConnections
.Where(uc => uc.ApplicationUserID == CurrentUser.Id)
.ToList();
-
+
if (PermissionMgr.UserCanModifyQuery(userPermissions, database) == false && database.OrganisationId != CurrentUser.OrganisationId)
{
return NotFound();
@@ -133,7 +133,7 @@ public ActionResult Edit(int id)
// POST: Simple/Create
- // To protect from overposting attacks, please enable the specific properties you want to bind to, for
+ // To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
diff --git a/Web/Controllers/SshKeyFileController.cs b/Web/Controllers/SshKeyFileController.cs
index f822a50..dd03214 100644
--- a/Web/Controllers/SshKeyFileController.cs
+++ b/Web/Controllers/SshKeyFileController.cs
@@ -67,7 +67,7 @@ public ActionResult Upload()
return Json(new { Status = "error" , Message = "Error uploading file, please try again" });
}
}
-
+
private bool ValidateSshKeyFile(byte[] data)
{
try
diff --git a/Web/Controllers/TeamController.cs b/Web/Controllers/TeamController.cs
index 2ed43d4..8a7e2af 100644
--- a/Web/Controllers/TeamController.cs
+++ b/Web/Controllers/TeamController.cs
@@ -81,7 +81,7 @@ public ActionResult Index()
}
viewModel.DatabasePermissions = viewModel.DatabasePermissions.OrderBy(p => p.DatabaseName).ToList();
-
+
viewModels.Add(viewModel);
}
@@ -124,7 +124,7 @@ public ActionResult Index()
return View(viewModels);
}
-
+
// GET: Team/Invite
public ActionResult Invite()
{
@@ -143,11 +143,11 @@ public ActionResult Invite()
viewModel.OrganisationName = CurrentUser.Organisation.OrganisationName;
viewModel.OrganisationDatabaseCount = db.DatabaseConnections.Count(dc => dc.OrganisationId == CurrentUser.OrganisationId);
-
+
List databases = new List();
-
+
databases = db.DatabaseConnections.Where(d => d.OrganisationId == CurrentUser.OrganisationId).ToList();
-
+
foreach (var database in databases)
{
viewModel.DatabasePermissions.Add(new PermissionViewModel.DatabasePermission
@@ -157,7 +157,7 @@ public ActionResult Invite()
AccessType = "None",
});
}
-
+
return View(viewModel);
}
@@ -326,9 +326,9 @@ public ActionResult Edit(PermissionViewModel viewModel)
{
viewModel.DatabasePermissions = new List();
}
-
+
var currentConnections = db.UserDatabaseConnections.Where(uc => uc.ApplicationUserID == user.Id).ToList();
-
+
if (viewModel.IsOrganisationAdmin)
{
// Remove any DB specific permissions
@@ -392,7 +392,7 @@ public ActionResult Remove(string email)
{
return BadRequest();
}
-
+
if (CurrentUser == null)
{
return NotFound("Could not find user");
@@ -408,7 +408,7 @@ public ActionResult Remove(string email)
}
ViewBag.Email = email;
-
+
return View();
}
@@ -480,7 +480,7 @@ public void SendOrganisationInviteEmail(string fromEmail, string inviteEmail, bo
{
var email = new MimeMessage();
email.From.Add(new MailboxAddress("QueryTree", _config.GetValue("Email:SenderAddress")));
- email.To.Add(new MailboxAddress(inviteEmail));
+ email.To.Add(new MailboxAddress(inviteEmail, inviteEmail));
email.Subject = string.Format("You have been invited to use QueryTree by {0}", fromEmail);
var templatePath = _env.ContentRootPath.TrimEnd('/') + '/';
diff --git a/Web/Controllers/UserDatabaseConnectionsController.cs b/Web/Controllers/UserDatabaseConnectionsController.cs
index 9e0113c..f6bcfec 100644
--- a/Web/Controllers/UserDatabaseConnectionsController.cs
+++ b/Web/Controllers/UserDatabaseConnectionsController.cs
@@ -22,7 +22,7 @@ public class UserDatabaseConnectionsController : IdentityController
private IEmailSender _emailSender;
private IWebHostEnvironment _env;
private IConfiguration _config;
-
+
public UserDatabaseConnectionsController(
ApplicationDbContext dbContext,
UserManager userManager,
@@ -43,7 +43,7 @@ private bool HasAccess(ApplicationUser currentUser, DatabaseConnection databaseC
var userConnections = db.UserDatabaseConnections
.Where(uc => uc.ApplicationUserID == CurrentUser.Id);
-
+
if (userConnections.Any(uc => uc.DatabaseConnectionID == databaseConnection.DatabaseConnectionID))
{
hasAccess = true;
@@ -79,7 +79,7 @@ public ActionResult Create(int id)
}
// POST: UserDatabaseConnections/Create
- // To protect from overposting attacks, please enable the specific properties you want to bind to, for
+ // To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
@@ -105,7 +105,7 @@ public ActionResult Create(UserDatabaseConnection userDatabaseConnection)
var lowercaseEmail = userDatabaseConnection.InviteEmail.ToLower();
var invitedUser = db.ApplicationUsers.FirstOrDefault(u => u.Email.ToLower() == lowercaseEmail);
-
+
if (invitedUser != null && db.UserDatabaseConnections.Any(u => u.DatabaseConnectionID == userDatabaseConnection.DatabaseConnectionID && u.ApplicationUserID == invitedUser.Id))
{
ModelState.AddModelError("Error", "This User already has an access to this Database Connection");
@@ -119,7 +119,7 @@ public ActionResult Create(UserDatabaseConnection userDatabaseConnection)
ModelState.AddModelError("Error", "This User has already been invited to be an Organisation Admin for this Database Connection");
}
else
- {
+ {
userDatabaseConnection.CreatedOn = DateTime.Now;
userDatabaseConnection.CreatedBy = CurrentUser;
@@ -135,9 +135,9 @@ public ActionResult Create(UserDatabaseConnection userDatabaseConnection)
SendDatabaseInviteMail(userDatabaseConnection);
return RedirectToAction("Details", "Home", new { id = userDatabaseConnection.DatabaseConnectionID });
- }
+ }
}
-
+
ViewBag.types = new[] { UserDatabaseTypes.Admin, UserDatabaseTypes.ReportBuilder, UserDatabaseTypes.ReportViewer }.Select(e => new { Id = (int)e, Value = e.ToString() });
return View(userDatabaseConnection);
@@ -147,7 +147,7 @@ public void SendDatabaseInviteMail(UserDatabaseConnection userLink)
{
var email = new MimeMessage();
email.From.Add(new MailboxAddress(UTF8Encoding.UTF8, "QueryTree", _config.GetValue("Email:SenderAddress")));
- email.To.Add(new MailboxAddress(userLink.InviteEmail ?? userLink.ApplicationUser.Email));
+ email.To.Add(new MailboxAddress(userLink.ApplicationUser.FirstName + " " + userLink.ApplicationUser.LastName, userLink.InviteEmail ?? userLink.ApplicationUser.Email));
email.Subject = string.Format("You have been invited to use QueryTree by {0}", userLink.CreatedBy.Email);
var webRoot = _env.ContentRootPath.TrimEnd('/') + '/';
@@ -223,7 +223,7 @@ public ActionResult Edit(int? id)
}
// POST: UserDatabaseConnections/Edit/5
- // To protect from overposting attacks, please enable the specific properties you want to bind to, for
+ // To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
@@ -248,7 +248,7 @@ public ActionResult Edit(UserDatabaseConnection userDatabaseConnection)
if (ModelState.IsValid)
{
existingUserDatabaseConnection.Type = userDatabaseConnection.Type;
-
+
db.SaveChanges();
return RedirectToAction("Details", "Home", new { id = existingUserDatabaseConnection.DatabaseConnectionID });
@@ -309,9 +309,9 @@ public ActionResult DeleteConfirmed(int id)
}
int? dbId = existingUserDatabaseConnection.DatabaseConnectionID;
-
+
db.UserDatabaseConnections.Remove(existingUserDatabaseConnection);
-
+
db.SaveChanges();
if (dbId.HasValue)
diff --git a/Web/Enums/DatabaseType.cs b/Web/Enums/DatabaseType.cs
index 43c2cd3..5b01b31 100644
--- a/Web/Enums/DatabaseType.cs
+++ b/Web/Enums/DatabaseType.cs
@@ -3,9 +3,9 @@
namespace QueryTree.Enums
{
- public enum DatabaseType
- {
- MySQL = 0,
+ public enum DatabaseType
+ {
+ MySQL = 0,
[Display(Name = "SQL Server")]
SQLServer = 1,
PostgreSQL = 3
diff --git a/Web/Enums/SecretType.cs b/Web/Enums/SecretType.cs
index 22db6be..1c04937 100644
--- a/Web/Enums/SecretType.cs
+++ b/Web/Enums/SecretType.cs
@@ -2,10 +2,10 @@
namespace QueryTree.Enums
{
- public enum SecretType : int
- {
- DatabasePassword = 0,
- SSHPassword = 1,
- SshKeyFile = 3
+ public enum SecretType : int
+ {
+ DatabasePassword = 0,
+ SSHPassword = 1,
+ SshKeyFile = 3
}
}
diff --git a/Web/Managers/ConvertManager.cs b/Web/Managers/ConvertManager.cs
index e522815..39f463e 100644
--- a/Web/Managers/ConvertManager.cs
+++ b/Web/Managers/ConvertManager.cs
@@ -10,7 +10,7 @@ namespace QueryTree.Managers
{
public class ConvertManager
{
-
+
private void PopulateData(QueryResponse data, SpreadsheetDocument spreadsheet)
{
Excel.AddWorksheet(spreadsheet, "Data");
@@ -21,7 +21,7 @@ private void PopulateData(QueryResponse data, SpreadsheetDocument spreadsheet)
foreach (string column in data.Columns)
{
Excel.SetCellValue(spreadsheet, worksheet, col, row, column, false, false);
-
+
//if (columnType == "datetime")
//{
// using (ExcelRange range = ws.Cells[2, col, 1 + data.Rows.Count, col])
@@ -29,7 +29,7 @@ private void PopulateData(QueryResponse data, SpreadsheetDocument spreadsheet)
// range.Style.Numberformat.Format = "dd/mm/yyyy hh:mm";
// }
// }
-
+
col++;
}
@@ -49,18 +49,18 @@ private void PopulateData(QueryResponse data, SpreadsheetDocument spreadsheet)
{
if (cellData is DateTime)
{
- Excel.SetCellValue(spreadsheet, worksheet, col, row, (DateTime)cellData, 1, true);
+ Excel.SetCellValue(spreadsheet, worksheet, col, row, (DateTime)cellData, 1, true);
}
else if (cellData is Int32 || cellData is Double)
{
Excel.SetCellValue(spreadsheet, worksheet, col, row, Convert.ToDouble(cellData), null, true);
}
- else
+ else
{
Excel.SetCellValue(spreadsheet, worksheet, col, row, cellData.ToString(), false, false);
}
}
-
+
col++;
}
@@ -73,17 +73,15 @@ private void PopulateData(QueryResponse data, SpreadsheetDocument spreadsheet)
public byte[] ToExcel(QueryResponse data)
{
var stream = new MemoryStream();
-
+
DocumentFormat.OpenXml.Packaging.SpreadsheetDocument spreadsheet;
-
+
spreadsheet = Excel.CreateWorkbook(stream);
-
+
Excel.AddBasicStyles(spreadsheet);
-
+
PopulateData(data, spreadsheet);
- spreadsheet.Close();
-
stream.Flush();
return stream.ToArray();
diff --git a/Web/Managers/DbManager.cs b/Web/Managers/DbManager.cs
index 758b3df..615b70b 100644
--- a/Web/Managers/DbManager.cs
+++ b/Web/Managers/DbManager.cs
@@ -7,7 +7,7 @@
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using System.Data.Common;
-using System.Data.SqlClient;
+using Microsoft.Data.SqlClient;
using System.Linq;
using Microsoft.Extensions.Caching.Memory;
using QueryTree.Enums;
@@ -175,7 +175,7 @@ private DbModel GetDbModel(DatabaseType type, DbConnection conn, int connectionI
+ " AND C.TABLE_NAME = PK.TABLE_NAME "
+ " AND C.COLUMN_NAME = PK.COLUMN_NAME "
+ "WHERE C.TABLE_SCHEMA = @schema "
- + "ORDER BY C.TABLE_NAME, COLUMN_NAME;";
+ + "ORDER BY C.TABLE_NAME, C.ORDINAL_POSITION;";
cmd = CreateCommand(type, conn, sql);
cmd.Parameters.Add(new MySqlParameter("@schema", databaseName));
}
@@ -196,7 +196,7 @@ private DbModel GetDbModel(DatabaseType type, DbConnection conn, int connectionI
+ " AND C.TABLE_NAME = PK.TABLE_NAME "
+ " AND C.COLUMN_NAME = PK.COLUMN_NAME "
+ "WHERE C.TABLE_SCHEMA <> 'information_schema' AND C.TABLE_SCHEMA <> 'pg_catalog' "
- + "ORDER BY C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME;";
+ + "ORDER BY C.TABLE_SCHEMA, C.TABLE_NAME, C.ORDINAL_POSITION;";
cmd = CreateCommand(type, conn, sql);
}
break;
@@ -216,7 +216,7 @@ private DbModel GetDbModel(DatabaseType type, DbConnection conn, int connectionI
+ " AND C.TABLE_NAME = PK.TABLE_NAME "
+ " AND C.COLUMN_NAME = PK.COLUMN_NAME "
+ "WHERE C.TABLE_SCHEMA <> 'sys' AND C.TABLE_NAME <> '__MigrationHistory' "
- + "ORDER BY C.TABLE_SCHEMA, C.TABLE_NAME, PK.COLUMN_NAME DESC, C.COLUMN_NAME ";
+ + "ORDER BY C.TABLE_SCHEMA, C.TABLE_NAME, PK.COLUMN_NAME DESC, C.ORDINAL_POSITION";
cmd = CreateCommand(type, conn, sql);
}
break;
@@ -279,7 +279,7 @@ private DbModel GetDbModel(DatabaseType type, DbConnection conn, int connectionI
result.Tables.Add(currentTable);
}
- if (tableName == currentTable.Name)
+ if (tableName == currentTable.Name)
{
currentTable.Columns.Add(new QueryTree.Models.DbColumn
{
@@ -357,7 +357,7 @@ private static void SetDbForeignKeys(DbModel dbModel, DatabaseType type, DbConne
break;
case DatabaseType.SQLServer:
{
- string sql = "SELECT C.TABLE_SCHEMA AS CHILD_TABLE_SCHEMA, "
+ string sql = "SELECT C.TABLE_SCHEMA AS CHILD_TABLE_SCHEMA, "
+ " C.TABLE_NAME AS CHILD_TABLE_NAME, "
+ " C.COLUMN_NAME AS CHILD_COLUMN_NAME, "
+ " P.TABLE_SCHEMA AS PARENT_TABLE_SCHEMA, "
@@ -408,11 +408,11 @@ private static void SetDbForeignKeys(DbModel dbModel, DatabaseType type, DbConne
DbTable dbTable = null;
if (column.IsPrimaryKey == false && column.Parent == null)
{
- dbTable = dbModel.Tables.FirstOrDefault(t =>
- t.Schema == table.Schema &&
+ dbTable = dbModel.Tables.FirstOrDefault(t =>
+ t.Schema == table.Schema &&
((t.Name.ToLower() + "_id") == column.Name.ToLower()) ||
((t.Name.ToLower() + "id") == column.Name.ToLower()));
-
+
if (dbTable != null)
{
column.Parent = dbTable.Columns[0] as Models.DbColumn;
@@ -428,7 +428,7 @@ public static List GetTableParents(DbTable table)
queue.Push(table);
HashSet parents = new HashSet();
-
+
while (queue.Any())
{
var curr = queue.Pop();
@@ -618,7 +618,7 @@ private static DbConnection GetDbConnection(DatabaseType type, string server, in
switch (type)
{
case DatabaseType.MySQL:
- conn = new MySqlConnection(string.Format("server={0};port={1};uid={2};pwd={3};database={4};Convert Zero Datetime=True;SslMode=Preferred", server, port, username, password, databaseName));
+ conn = new MySqlConnection(string.Format("server={0};port={1};uid={2};pwd={3};database={4};Convert Zero Datetime=True;SslMode=Preferred;", server, port, username, password, databaseName));
conn.Open();
break;
case DatabaseType.PostgreSQL:
diff --git a/Web/Managers/Excel.cs b/Web/Managers/Excel.cs
index 792fafa..a84a755 100644
--- a/Web/Managers/Excel.cs
+++ b/Web/Managers/Excel.cs
@@ -41,7 +41,7 @@ public static DocumentFormat.OpenXml.Packaging.SpreadsheetDocument CreateWorkboo
workbookStylesPart = spreadSheet.WorkbookPart.AddNewPart();
workbookStylesPart.Stylesheet = new DocumentFormat.OpenXml.Spreadsheet.Stylesheet();
workbookStylesPart.Stylesheet.Save();
-
+
return spreadSheet;
}
diff --git a/Web/Managers/PasswordManager.cs b/Web/Managers/PasswordManager.cs
index a7c3f62..3e8df29 100644
--- a/Web/Managers/PasswordManager.cs
+++ b/Web/Managers/PasswordManager.cs
@@ -84,7 +84,7 @@ private string Decrypt(string val)
Buffer.BlockCopy(fullCipher, 0, iv, 0, iv.Length);
Buffer.BlockCopy(fullCipher, iv.Length, cipher, 0, cipher.Length);
-
+
using (var aes = Aes.Create())
{
aes.Key = key;
@@ -111,7 +111,7 @@ private byte[] GetKey()
var keyFilePath = Path.IsPathRooted(_config.Value.Keyfile) ?
_config.Value.Keyfile :
Path.Combine(_env.ContentRootPath, _config.Value.Keyfile);
-
+
if (File.Exists(keyFilePath))
{
using (var keyFile = File.OpenRead(keyFilePath))
@@ -121,7 +121,7 @@ private byte[] GetKey()
return bytes;
}
}
- else
+ else
{
var key = GetRandomBytes(16);
using (var keyFile = File.OpenWrite(keyFilePath))
@@ -158,7 +158,7 @@ public void DeleteSecret(string entityId)
public string GetSecret(string entityId)
{
var dbSecret = _db.Secrets.FirstOrDefault(s => s.SecretID == entityId);
- if (dbSecret != null)
+ if (dbSecret != null)
{
return Decrypt(dbSecret.SecretData);
}
diff --git a/Web/Managers/PermissionMgr.cs b/Web/Managers/PermissionMgr.cs
index 39ec9af..b160492 100644
--- a/Web/Managers/PermissionMgr.cs
+++ b/Web/Managers/PermissionMgr.cs
@@ -11,12 +11,12 @@ public class PermissionMgr
public static List GetDatabasePermissions(DatabaseConnection database, IEnumerable userPermissions)
{
List results = new List();
-
+
results = userPermissions
.Where(p => p.DatabaseConnectionID == database.DatabaseConnectionID)
.Select(p => p.Type)
.ToList();
-
+
return results;
}
@@ -64,7 +64,7 @@ public static bool UserCanViewDatabase(IEnumerable userP
var permissions = GetDatabasePermissions(database, userPermissions);
return permissions.Any(p => ViewDatabaseTypes.Contains(p));
- }
+ }
public static bool UserCanModifyDatabase(IEnumerable userPermissions, DatabaseConnection database)
{
diff --git a/Web/Managers/SSHProxy.cs b/Web/Managers/SSHProxy.cs
index 29eefd9..e3837fb 100644
--- a/Web/Managers/SSHProxy.cs
+++ b/Web/Managers/SSHProxy.cs
@@ -80,7 +80,7 @@ private SshClient GetSshClient()
{
return new SshClient(SshServer, SshPort, SshCredentials.Username, SshCredentials.PrivateKeyFile);
}
- else
+ else
{
return new SshClient(SshServer, SshPort, SshCredentials.Username, SshCredentials.Password);
}
@@ -212,7 +212,7 @@ public void Run()
}
catch
{
-
+
Error = "An unexpected error occurred.";
}
finally
diff --git a/Web/Managers/SSHProxyCredentials.cs b/Web/Managers/SSHProxyCredentials.cs
index 975c352..e7d7fde 100644
--- a/Web/Managers/SSHProxyCredentials.cs
+++ b/Web/Managers/SSHProxyCredentials.cs
@@ -76,5 +76,5 @@ public SshProxyCredentials(IPasswordManager passwordManager, DatabaseConnection
}
}
}
- }
+ }
}
diff --git a/Web/Managers/SSHProxyManager.cs b/Web/Managers/SSHProxyManager.cs
index 9fa6bda..70a80c8 100644
--- a/Web/Managers/SSHProxyManager.cs
+++ b/Web/Managers/SSHProxyManager.cs
@@ -67,7 +67,7 @@ private static bool TryAcquireExistingProxy(string server, int port, out SSHProx
return false;
}
-
+
public static bool TryUseProxy(string server, int port, string sshServer, int sshPort, SshProxyCredentials credentials, DatabaseConnection connection, Action action, out string error)
{
return TryUseProxy(server, port, sshServer, sshPort, credentials, action, out error);
@@ -92,7 +92,7 @@ public static bool TryUseProxy(string server, int port, string sshServer, int ss
UseProxy(proxy, action);
return true;
}
- else
+ else
{
proxy = new SSHProxy(server, port, sshServer, sshPort, credentials);
diff --git a/Web/Managers/ScheduledEmailManager.cs b/Web/Managers/ScheduledEmailManager.cs
index 148fcb1..f77f833 100644
--- a/Web/Managers/ScheduledEmailManager.cs
+++ b/Web/Managers/ScheduledEmailManager.cs
@@ -33,7 +33,7 @@ public class ScheduledEmailManager : IScheduledEmailManager
public ScheduledEmailManager(
IEmailSenderService emailSenderService,
IEmailSender emailSender,
- IConfiguration config,
+ IConfiguration config,
ApplicationDbContext db,
IWebHostEnvironment env,
IMemoryCache cache,
@@ -56,7 +56,7 @@ public void BuildScheduledEmail(string queryName, string editUrl, string recipie
return;
}
- if(!_emailSenderService.TrySetDelivered(queryId))
+ if(!_emailSenderService.TrySetDelivered(queryId))
{
return;
}
@@ -64,7 +64,7 @@ public void BuildScheduledEmail(string queryName, string editUrl, string recipie
var query = _db.Queries
.Include(q => q.DatabaseConnection)
.FirstOrDefault(q => q.QueryID == queryId);
-
+
if (query != null && query.QueryDefinition != null)
{
var queryDefinition = JsonConvert.DeserializeObject(query.QueryDefinition);
@@ -78,7 +78,7 @@ public void BuildScheduledEmail(string queryName, string editUrl, string recipie
var message = new MimeKit.MimeMessage();
foreach (var email in recipients.Split(','))
- message.To.Add(new MailboxAddress(email));
+ message.To.Add(new MailboxAddress(email, email));
message.From.Add(new MailboxAddress("QueryTree", _config.GetValue("Email:SenderAddress")));
message.Subject = string.Format("Here's your scheduled report {0}", queryName);
diff --git a/Web/Migrations/20170906223136_Initial Version.Designer.cs b/Web/Migrations/20170906223136_Initial Version.Designer.cs
index 34fe89f..6c30fcc 100644
--- a/Web/Migrations/20170906223136_Initial Version.Designer.cs
+++ b/Web/Migrations/20170906223136_Initial Version.Designer.cs
@@ -35,7 +35,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder)
b.HasIndex("NormalizedName")
.IsUnique()
- .HasName("RoleNameIndex");
+ .HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
@@ -103,11 +103,11 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder)
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
- .HasName("EmailIndex");
+ .HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
- .HasName("UserNameIndex");
+ .HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers");
diff --git a/Web/Migrations/20170915175020_Added Secrets table.Designer.cs b/Web/Migrations/20170915175020_Added Secrets table.Designer.cs
index 815a452..126fd31 100755
--- a/Web/Migrations/20170915175020_Added Secrets table.Designer.cs
+++ b/Web/Migrations/20170915175020_Added Secrets table.Designer.cs
@@ -35,7 +35,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder)
b.HasIndex("NormalizedName")
.IsUnique()
- .HasName("RoleNameIndex");
+ .HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
@@ -103,11 +103,11 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder)
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
- .HasName("EmailIndex");
+ .HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
- .HasName("UserNameIndex");
+ .HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers");
diff --git a/Web/Migrations/20180307104120_Added SSH Server.Designer.cs b/Web/Migrations/20180307104120_Added SSH Server.Designer.cs
index 323b69e..cb734be 100644
--- a/Web/Migrations/20180307104120_Added SSH Server.Designer.cs
+++ b/Web/Migrations/20180307104120_Added SSH Server.Designer.cs
@@ -40,7 +40,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder)
b.HasIndex("NormalizedName")
.IsUnique()
- .HasName("RoleNameIndex");
+ .HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
@@ -108,11 +108,11 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder)
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
- .HasName("EmailIndex");
+ .HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
- .HasName("UserNameIndex");
+ .HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers");
diff --git a/Web/Migrations/20180307104120_Added SSH Server.cs b/Web/Migrations/20180307104120_Added SSH Server.cs
index 7c78bb9..56ed6a4 100644
--- a/Web/Migrations/20180307104120_Added SSH Server.cs
+++ b/Web/Migrations/20180307104120_Added SSH Server.cs
@@ -12,7 +12,7 @@ protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("ALTER TABLE DatabaseConnections ADD COLUMN SshServer TEXT;");
}
- else
+ else
{
migrationBuilder.AddColumn(
table: "DatabaseConnections",
@@ -30,9 +30,9 @@ protected override void Down(MigrationBuilder migrationBuilder)
migrationBuilder.Sql(@"
PRAGMA foreign_keys=off;
-
+
ALTER TABLE DatabaseConnections RENAME TO temp_DatabaseConnections;
-
+
CREATE TABLE DatabaseConnections
(
DatabaseConnectionID INTEGER NOT NULL
@@ -60,7 +60,7 @@ REFERENCES SshKeyFiles
UseSshKey INTEGER NOT NULL,
Username TEXT NOT NULL
);
-
+
INSERT INTO DatabaseConnections (
DatabaseConnectionID,
CreatedOn,
@@ -95,14 +95,14 @@ INSERT INTO DatabaseConnections (
UseSshKey,
Username
FROM temp_DatabaseConnections;
-
+
DROP TABLE temp_DatabaseConnections;
-
+
PRAGMA foreign_keys=on;
-
+
");
}
- else
+ else
{
migrationBuilder.DropColumn(
name: "SshServer",
diff --git a/Web/Migrations/ApplicationDbContextModelSnapshot.cs b/Web/Migrations/ApplicationDbContextModelSnapshot.cs
index ef8c7e1..e198574 100644
--- a/Web/Migrations/ApplicationDbContextModelSnapshot.cs
+++ b/Web/Migrations/ApplicationDbContextModelSnapshot.cs
@@ -39,7 +39,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.HasIndex("NormalizedName")
.IsUnique()
- .HasName("RoleNameIndex");
+ .HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
@@ -107,11 +107,11 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
- .HasName("EmailIndex");
+ .HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
- .HasName("UserNameIndex");
+ .HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers");
diff --git a/Web/Models/UserDatabaseConnection.cs b/Web/Models/UserDatabaseConnection.cs
index 39c2036..63902a0 100644
--- a/Web/Models/UserDatabaseConnection.cs
+++ b/Web/Models/UserDatabaseConnection.cs
@@ -13,13 +13,13 @@ public class UserDatabaseConnection
[Display(Name = "Database")]
public int DatabaseConnectionID { get; set; }
-
+
public virtual DatabaseConnection DatabaseConnection { get; set; }
public string ApplicationUserID{ get; set; }
[ForeignKey("ApplicationUserID")]
public virtual ApplicationUser ApplicationUser { get; set; }
-
+
[Display(Name="Access Level")]
public UserDatabaseTypes Type { get; set; }
public string CreatedByID { get; set; }
diff --git a/Web/Program.cs b/Web/Program.cs
index c887a62..25282aa 100644
--- a/Web/Program.cs
+++ b/Web/Program.cs
@@ -1,5 +1,4 @@
-using Google.Protobuf.WellKnownTypes;
-using Hangfire;
+using Hangfire;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
@@ -9,12 +8,10 @@
using QueryTree.Services;
using QueryTree;
using System;
-using System.Configuration;
using Hangfire.SQLite;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Hosting;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton(builder.Configuration);
@@ -75,6 +72,7 @@
builder.Services.AddTransient(); // Allows controllers to set/get/delete database credentials
builder.Services.AddTransient();
builder.Services.AddMemoryCache();
+builder.Services.AddHangfireServer();
var app = builder.Build();
using (var scope = app.Services.CreateScope())
@@ -98,8 +96,6 @@
app.UseAuthorization();
if (builder.Configuration["RunHangfire"] == "true")
{
- app.UseHangfireServer();
-
var dashboardOptions = new DashboardOptions
{
Authorization = new[] { new HangfireAuthorizationFilter() }
diff --git a/Web/QueryTree.csproj b/Web/QueryTree.csproj
index 49ca022..1907367 100644
--- a/Web/QueryTree.csproj
+++ b/Web/QueryTree.csproj
@@ -1,6 +1,6 @@
- net6.0
+ net8.0
aspnet-QueryTree-46E61BB7-4239-4527-BA0C-1B66D664CC58
0.0.0
D4 Software Ltd
@@ -25,29 +25,28 @@
-
-
+
-
-
+
+
-
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
diff --git a/Web/Scripts/lib/bootstrap/.bower.json b/Web/Scripts/lib/bootstrap/.bower.json
index e774ec9..1e99b62 100644
--- a/Web/Scripts/lib/bootstrap/.bower.json
+++ b/Web/Scripts/lib/bootstrap/.bower.json
@@ -1,45 +1,45 @@
-{
- "name": "bootstrap",
- "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.",
- "keywords": [
- "css",
- "js",
- "less",
- "mobile-first",
- "responsive",
- "front-end",
- "framework",
- "web"
- ],
- "homepage": "http://getbootstrap.com",
- "license": "MIT",
- "moduleType": "globals",
- "main": [
- "less/bootstrap.less",
- "dist/js/bootstrap.js"
- ],
- "ignore": [
- "/.*",
- "_config.yml",
- "CNAME",
- "composer.json",
- "CONTRIBUTING.md",
- "docs",
- "js/tests",
- "test-infra"
- ],
- "dependencies": {
- "jquery": "1.9.1 - 3"
- },
- "version": "3.3.7",
- "_release": "3.3.7",
- "_resolution": {
- "type": "version",
- "tag": "v3.3.7",
- "commit": "0b9c4a4007c44201dce9a6cc1a38407005c26c86"
- },
- "_source": "https://github.com/twbs/bootstrap.git",
- "_target": "v3.3.7",
- "_originalSource": "bootstrap",
- "_direct": true
+{
+ "name": "bootstrap",
+ "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.",
+ "keywords": [
+ "css",
+ "js",
+ "less",
+ "mobile-first",
+ "responsive",
+ "front-end",
+ "framework",
+ "web"
+ ],
+ "homepage": "http://getbootstrap.com",
+ "license": "MIT",
+ "moduleType": "globals",
+ "main": [
+ "less/bootstrap.less",
+ "dist/js/bootstrap.js"
+ ],
+ "ignore": [
+ "/.*",
+ "_config.yml",
+ "CNAME",
+ "composer.json",
+ "CONTRIBUTING.md",
+ "docs",
+ "js/tests",
+ "test-infra"
+ ],
+ "dependencies": {
+ "jquery": "1.9.1 - 3"
+ },
+ "version": "3.3.7",
+ "_release": "3.3.7",
+ "_resolution": {
+ "type": "version",
+ "tag": "v3.3.7",
+ "commit": "0b9c4a4007c44201dce9a6cc1a38407005c26c86"
+ },
+ "_source": "https://github.com/twbs/bootstrap.git",
+ "_target": "v3.3.7",
+ "_originalSource": "bootstrap",
+ "_direct": true
}
\ No newline at end of file
diff --git a/Web/Scripts/lib/jquery-validation-unobtrusive/.bower.json b/Web/Scripts/lib/jquery-validation-unobtrusive/.bower.json
index c7a594f..ccf4812 100644
--- a/Web/Scripts/lib/jquery-validation-unobtrusive/.bower.json
+++ b/Web/Scripts/lib/jquery-validation-unobtrusive/.bower.json
@@ -1,44 +1,44 @@
-{
- "name": "jquery-validation-unobtrusive",
- "version": "3.2.6",
- "homepage": "https://github.com/aspnet/jquery-validation-unobtrusive",
- "description": "Add-on to jQuery Validation to enable unobtrusive validation options in data-* attributes.",
- "main": [
- "jquery.validate.unobtrusive.js"
- ],
- "ignore": [
- "**/.*",
- "*.json",
- "*.md",
- "*.txt",
- "gulpfile.js"
- ],
- "keywords": [
- "jquery",
- "asp.net",
- "mvc",
- "validation",
- "unobtrusive"
- ],
- "authors": [
- "Microsoft"
- ],
- "license": "http://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm",
- "repository": {
- "type": "git",
- "url": "git://github.com/aspnet/jquery-validation-unobtrusive.git"
- },
- "dependencies": {
- "jquery-validation": ">=1.8",
- "jquery": ">=1.8"
- },
- "_release": "3.2.6",
- "_resolution": {
- "type": "version",
- "tag": "v3.2.6",
- "commit": "13386cd1b5947d8a5d23a12b531ce3960be1eba7"
- },
- "_source": "git://github.com/aspnet/jquery-validation-unobtrusive.git",
- "_target": "3.2.6",
- "_originalSource": "jquery-validation-unobtrusive"
+{
+ "name": "jquery-validation-unobtrusive",
+ "version": "3.2.6",
+ "homepage": "https://github.com/aspnet/jquery-validation-unobtrusive",
+ "description": "Add-on to jQuery Validation to enable unobtrusive validation options in data-* attributes.",
+ "main": [
+ "jquery.validate.unobtrusive.js"
+ ],
+ "ignore": [
+ "**/.*",
+ "*.json",
+ "*.md",
+ "*.txt",
+ "gulpfile.js"
+ ],
+ "keywords": [
+ "jquery",
+ "asp.net",
+ "mvc",
+ "validation",
+ "unobtrusive"
+ ],
+ "authors": [
+ "Microsoft"
+ ],
+ "license": "http://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/aspnet/jquery-validation-unobtrusive.git"
+ },
+ "dependencies": {
+ "jquery-validation": ">=1.8",
+ "jquery": ">=1.8"
+ },
+ "_release": "3.2.6",
+ "_resolution": {
+ "type": "version",
+ "tag": "v3.2.6",
+ "commit": "13386cd1b5947d8a5d23a12b531ce3960be1eba7"
+ },
+ "_source": "git://github.com/aspnet/jquery-validation-unobtrusive.git",
+ "_target": "3.2.6",
+ "_originalSource": "jquery-validation-unobtrusive"
}
\ No newline at end of file
diff --git a/Web/Scripts/lib/jquery-validation/.bower.json b/Web/Scripts/lib/jquery-validation/.bower.json
index 2a8c3c8..cab34a4 100644
--- a/Web/Scripts/lib/jquery-validation/.bower.json
+++ b/Web/Scripts/lib/jquery-validation/.bower.json
@@ -1,40 +1,40 @@
-{
- "name": "jquery-validation",
- "homepage": "http://jqueryvalidation.org/",
- "repository": {
- "type": "git",
- "url": "git://github.com/jzaefferer/jquery-validation.git"
- },
- "authors": [
- "Jörn Zaefferer "
- ],
- "description": "Form validation made easy",
- "main": "dist/jquery.validate.js",
- "keywords": [
- "forms",
- "validation",
- "validate"
- ],
- "license": "MIT",
- "ignore": [
- "**/.*",
- "node_modules",
- "bower_components",
- "test",
- "demo",
- "lib"
- ],
- "dependencies": {
- "jquery": ">= 1.7.2"
- },
- "version": "1.14.0",
- "_release": "1.14.0",
- "_resolution": {
- "type": "version",
- "tag": "1.14.0",
- "commit": "c1343fb9823392aa9acbe1c3ffd337b8c92fed48"
- },
- "_source": "git://github.com/jzaefferer/jquery-validation.git",
- "_target": ">=1.8",
- "_originalSource": "jquery-validation"
+{
+ "name": "jquery-validation",
+ "homepage": "http://jqueryvalidation.org/",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/jzaefferer/jquery-validation.git"
+ },
+ "authors": [
+ "Jörn Zaefferer "
+ ],
+ "description": "Form validation made easy",
+ "main": "dist/jquery.validate.js",
+ "keywords": [
+ "forms",
+ "validation",
+ "validate"
+ ],
+ "license": "MIT",
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "demo",
+ "lib"
+ ],
+ "dependencies": {
+ "jquery": ">= 1.7.2"
+ },
+ "version": "1.14.0",
+ "_release": "1.14.0",
+ "_resolution": {
+ "type": "version",
+ "tag": "1.14.0",
+ "commit": "c1343fb9823392aa9acbe1c3ffd337b8c92fed48"
+ },
+ "_source": "git://github.com/jzaefferer/jquery-validation.git",
+ "_target": ">=1.8",
+ "_originalSource": "jquery-validation"
}
\ No newline at end of file
diff --git a/Web/Scripts/lib/jquery/.bower.json b/Web/Scripts/lib/jquery/.bower.json
index 9c17945..419488b 100644
--- a/Web/Scripts/lib/jquery/.bower.json
+++ b/Web/Scripts/lib/jquery/.bower.json
@@ -1,25 +1,25 @@
-{
- "name": "jquery",
- "main": "dist/jquery.js",
- "license": "MIT",
- "ignore": [
- "package.json"
- ],
- "keywords": [
- "jquery",
- "javascript",
- "browser",
- "library"
- ],
- "homepage": "https://github.com/jquery/jquery-dist",
- "version": "2.2.0",
- "_release": "2.2.0",
- "_resolution": {
- "type": "version",
- "tag": "2.2.0",
- "commit": "6fc01e29bdad0964f62ef56d01297039cdcadbe5"
- },
- "_source": "git://github.com/jquery/jquery-dist.git",
- "_target": "2.2.0",
- "_originalSource": "jquery"
+{
+ "name": "jquery",
+ "main": "dist/jquery.js",
+ "license": "MIT",
+ "ignore": [
+ "package.json"
+ ],
+ "keywords": [
+ "jquery",
+ "javascript",
+ "browser",
+ "library"
+ ],
+ "homepage": "https://github.com/jquery/jquery-dist",
+ "version": "2.2.0",
+ "_release": "2.2.0",
+ "_resolution": {
+ "type": "version",
+ "tag": "2.2.0",
+ "commit": "6fc01e29bdad0964f62ef56d01297039cdcadbe5"
+ },
+ "_source": "git://github.com/jquery/jquery-dist.git",
+ "_target": "2.2.0",
+ "_originalSource": "jquery"
}
\ No newline at end of file
diff --git a/Web/Services/EmailSenderService.cs b/Web/Services/EmailSenderService.cs
index 159a7df..b682f32 100644
--- a/Web/Services/EmailSenderService.cs
+++ b/Web/Services/EmailSenderService.cs
@@ -1,23 +1,23 @@
using System;
using System.Collections.Generic;
-namespace QueryTree.Services
+namespace QueryTree.Services
{
- public sealed class EmailSenderService : IEmailSenderService
+ public sealed class EmailSenderService : IEmailSenderService
{
private const int TimeoutInSeconds = 60;
private readonly object _locker = new object();
private readonly Dictionary _messageDeliveryTime = new Dictionary();
- public bool TrySetDelivered(int messageId)
+ public bool TrySetDelivered(int messageId)
{
DateTime deliveredTime;
- if (!_messageDeliveryTime.TryGetValue(messageId, out deliveredTime) || deliveredTime.AddSeconds(TimeoutInSeconds) <= DateTime.UtcNow)
+ if (!_messageDeliveryTime.TryGetValue(messageId, out deliveredTime) || deliveredTime.AddSeconds(TimeoutInSeconds) <= DateTime.UtcNow)
{
- lock(_locker)
+ lock(_locker)
{
- if (!_messageDeliveryTime.TryGetValue(messageId, out deliveredTime) || deliveredTime.AddSeconds(TimeoutInSeconds) <= DateTime.UtcNow)
+ if (!_messageDeliveryTime.TryGetValue(messageId, out deliveredTime) || deliveredTime.AddSeconds(TimeoutInSeconds) <= DateTime.UtcNow)
{
_messageDeliveryTime[messageId] = DateTime.UtcNow;
return true;
diff --git a/Web/Services/IEmailSenderService.cs b/Web/Services/IEmailSenderService.cs
index 3858c5a..92e78ef 100644
--- a/Web/Services/IEmailSenderService.cs
+++ b/Web/Services/IEmailSenderService.cs
@@ -1,6 +1,6 @@
-namespace QueryTree.Services
+namespace QueryTree.Services
{
- public interface IEmailSenderService
+ public interface IEmailSenderService
{
bool TrySetDelivered(int messageId);
}
diff --git a/Web/Services/MessageServices.cs b/Web/Services/MessageServices.cs
index 750b9c6..ffdf040 100755
--- a/Web/Services/MessageServices.cs
+++ b/Web/Services/MessageServices.cs
@@ -21,16 +21,16 @@ public class EmailSender : IEmailSender
public EmailSender(IConfiguration config)
{
- _config = config;
+ _config = config;
}
-
+
public void SendMail(MimeMessage message)
{
using (var client = new SmtpClient())
{
// For demo-purposes, accept all SSL certificates (in case the server supports STARTTLS)
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
-
+
client.Connect(_config.GetValue("Email:SmtpHost"), _config.GetValue("Email:SmtpPort"), _config.GetValue("Email:UseSSL"));
// Note: only needed if the SMTP server requires authentication
@@ -42,12 +42,12 @@ public void SendMail(MimeMessage message)
client.Disconnect(true);
}
}
-
+
public void SendMail(string to, string subject, string body)
{
var email = new MimeMessage();
email.From.Add(new MailboxAddress("QueryTree", _config.GetValue("Email:SenderAddress")));
- email.To.Add(new MailboxAddress(to));
+ email.To.Add(new MailboxAddress(to, to));
email.Subject = subject;
email.Body = new TextPart("plain") { Text = body };
diff --git a/Web/ViewModels/InvitationViewModel.cs b/Web/ViewModels/InvitationViewModel.cs
index 7c969e6..1ef98ff 100644
--- a/Web/ViewModels/InvitationViewModel.cs
+++ b/Web/ViewModels/InvitationViewModel.cs
@@ -9,7 +9,7 @@ namespace QueryTree.ViewModels
{
public class InvitationViewModel
{
- public InvitationViewModel()
+ public InvitationViewModel()
{
DatabasesMerged = new List();
DatabasesLost = new List();
diff --git a/Web/ViewModels/PermissionViewModel.cs b/Web/ViewModels/PermissionViewModel.cs
index fb972ab..961c8c3 100644
--- a/Web/ViewModels/PermissionViewModel.cs
+++ b/Web/ViewModels/PermissionViewModel.cs
@@ -16,7 +16,7 @@ public class PermissionViewModel
public string Email { get; set; }
public bool IsOrganisationAdmin { get; set; }
-
+
[Display(Name ="Organisation Name")]
public string OrganisationName { get; set; }
diff --git a/Web/Views/Account/ForgotPasswordConfirmation.cshtml b/Web/Views/Account/ForgotPasswordConfirmation.cshtml
index dd7af8c..1e115b1 100644
--- a/Web/Views/Account/ForgotPasswordConfirmation.cshtml
+++ b/Web/Views/Account/ForgotPasswordConfirmation.cshtml
@@ -10,4 +10,4 @@
Please check your email to reset your password.
-
+
diff --git a/Web/Views/Account/Index.cshtml b/Web/Views/Account/Index.cshtml
index 12fc5d7..aea5092 100644
--- a/Web/Views/Account/Index.cshtml
+++ b/Web/Views/Account/Index.cshtml
@@ -25,11 +25,11 @@
@Html.ActionLink("Edit Team Members", "Index", "Team", null, new { @class = "btn btn-lg btn-default" })
-
+
}
-
+
@Html.ActionLink("Change your password", "ChangePassword", "Manage", null, new { @class = "btn btn-lg btn-primary" })
@if (Model.OtherConnections.Any())
{
@@ -44,7 +44,7 @@
}
These connections will not count towards your plan
- }
+ }
@section Scripts {
diff --git a/Web/Views/Account/Register.cshtml b/Web/Views/Account/Register.cshtml
index 86b7c8d..219d5fd 100644
--- a/Web/Views/Account/Register.cshtml
+++ b/Web/Views/Account/Register.cshtml
@@ -12,7 +12,7 @@
@ViewBag.Title.
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
-
+
}
-
+
diff --git a/Web/Views/Home/Create.cshtml b/Web/Views/Home/Create.cshtml
index 36e9421..ebf3921 100644
--- a/Web/Views/Home/Create.cshtml
+++ b/Web/Views/Home/Create.cshtml
@@ -148,7 +148,7 @@
- @Html.Partial("_ConnectionHelpers")
+
diff --git a/Web/Views/Home/Delete.cshtml b/Web/Views/Home/Delete.cshtml
index 1fea2a4..0122cf9 100644
--- a/Web/Views/Home/Delete.cshtml
+++ b/Web/Views/Home/Delete.cshtml
@@ -68,7 +68,7 @@
@using (Html.BeginForm()) {
@Html.AntiForgeryToken()
-
+
-
+
@@ -143,7 +143,7 @@
@Html.DisplayFor(model => model.SshServer)
-
+
@Html.DisplayNameFor(model => model.SshPort)
diff --git a/Web/Views/Home/Edit.cshtml b/Web/Views/Home/Edit.cshtml
index a8326d0..471ea45 100644
--- a/Web/Views/Home/Edit.cshtml
+++ b/Web/Views/Home/Edit.cshtml
@@ -10,7 +10,7 @@
@using (Html.BeginForm(null, null, FormMethod.Post, new { id = "mainForm" }))
{
@Html.AntiForgeryToken()
-
+
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@@ -112,7 +112,7 @@
@Html.ValidationMessage("keyFile", new { @class = "text-danger" })
-
+
@{string filename = Model.SshKeyFile != null ? Model.SshKeyFile.Filename : null; }
@filename
@@ -148,7 +148,7 @@
- @Html.Partial("_ConnectionHelpers")
+
-
+
}
diff --git a/Web/Views/Queries/Create.cshtml b/Web/Views/Queries/Create.cshtml
index 996d5f7..e30275f 100644
--- a/Web/Views/Queries/Create.cshtml
+++ b/Web/Views/Queries/Create.cshtml
@@ -22,7 +22,7 @@
- @Html.Partial("_Qt_Advanced")
-
+
+
@@ -62,7 +62,7 @@
}
-
+
@section Scripts {
diff --git a/Web/Views/Shared/_Layout.cshtml b/Web/Views/Shared/_Layout.cshtml
index 5dabb33..c8e79d1 100644
--- a/Web/Views/Shared/_Layout.cshtml
+++ b/Web/Views/Shared/_Layout.cshtml
@@ -40,7 +40,7 @@
- @Html.Partial("_LoginPartial")
+
@@ -50,7 +50,7 @@
@RenderBody()
- @Html.Partial("_FooterPartial")
+
@RenderSection("scripts", required: false)
@if (!String.IsNullOrEmpty(CustomizationConfiguration.Value.BaseUri))
diff --git a/Web/Views/Shared/_Qt_simple.cshtml b/Web/Views/Shared/_Qt_simple.cshtml
index b2a89c0..6ce91b5 100644
--- a/Web/Views/Shared/_Qt_simple.cshtml
+++ b/Web/Views/Shared/_Qt_simple.cshtml
@@ -2,7 +2,7 @@
Connecting to your database...
-
+
diff --git a/Web/Views/Simple/Create.cshtml b/Web/Views/Simple/Create.cshtml
index 448f378..c493261 100644
--- a/Web/Views/Simple/Create.cshtml
+++ b/Web/Views/Simple/Create.cshtml
@@ -6,7 +6,7 @@
@{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
-}
+}
Build your report
@@ -35,7 +35,7 @@
-
+
-
+
@@ -100,12 +100,12 @@
@@ -316,7 +316,7 @@
}
-@section Scripts {
+@section Scripts {
diff --git a/Web/Views/Simple/Edit.cshtml b/Web/Views/Simple/Edit.cshtml
index f1511da..03eb13c 100644
--- a/Web/Views/Simple/Edit.cshtml
+++ b/Web/Views/Simple/Edit.cshtml
@@ -6,14 +6,14 @@
@{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
-}
+}
@Html.ActionLink("Cancel", "Details", "Home", new { id = Model.DatabaseConnectionID }, new { @class = "btn btn-default" })
@@ -35,7 +35,7 @@
-
+
-
+
@@ -149,9 +149,9 @@
-
-

+
+
@@ -199,14 +199,14 @@
-
-
+
+
@@ -340,7 +340,7 @@
-@section Scripts {
+@section Scripts {
diff --git a/Web/Views/Team/Edit.cshtml b/Web/Views/Team/Edit.cshtml
index 7c66628..a9bad37 100644
--- a/Web/Views/Team/Edit.cshtml
+++ b/Web/Views/Team/Edit.cshtml
@@ -26,7 +26,7 @@
@Html.HiddenFor(model => model.ApplicationUserID)
-
+
@if (Model.OganisationHasDatabases)
{
@@ -71,7 +71,7 @@
}
- @Html.Partial("_AccessTypeHelp")
+
}
else
diff --git a/Web/Views/Team/Invite.cshtml b/Web/Views/Team/Invite.cshtml
index 0104103..298519d 100644
--- a/Web/Views/Team/Invite.cshtml
+++ b/Web/Views/Team/Invite.cshtml
@@ -4,8 +4,8 @@
ViewBag.Title = "QueryTree";
Layout = "~/Views/Shared/_Layout.cshtml";
- var teamNameInBrackets = string.IsNullOrWhiteSpace(Model.OrganisationName) == false
- ? $"(${Model.OrganisationName})"
+ var teamNameInBrackets = string.IsNullOrWhiteSpace(Model.OrganisationName) == false
+ ? $"(${Model.OrganisationName})"
: "";
}
@@ -61,7 +61,7 @@
{
}
-
+
@if (Model.DatabasePermissions.Count > 0)
{
@@ -92,7 +92,7 @@
}
-
+
- @Html.Partial("_AccessTypeHelp")
+
\ No newline at end of file
diff --git a/Web/Views/_ViewImports.cshtml b/Web/Views/_ViewImports.cshtml
index 70b6fe3..98f687f 100644
--- a/Web/Views/_ViewImports.cshtml
+++ b/Web/Views/_ViewImports.cshtml
@@ -1,2 +1,2 @@
-@using QueryTree
-@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
+@using QueryTree
+@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
diff --git a/Web/appsettings.Development.json b/Web/appsettings.Development.json
index f334029..fa8ce71 100644
--- a/Web/appsettings.Development.json
+++ b/Web/appsettings.Development.json
@@ -1,10 +1,10 @@
-{
- "Logging": {
- "IncludeScopes": false,
- "LogLevel": {
- "Default": "Debug",
- "System": "Information",
- "Microsoft": "Information"
- }
- }
-}
+{
+ "Logging": {
+ "IncludeScopes": false,
+ "LogLevel": {
+ "Default": "Debug",
+ "System": "Information",
+ "Microsoft": "Information"
+ }
+ }
+}
diff --git a/Web/appsettings.json b/Web/appsettings.json
index a9e3153..0f14c69 100644
--- a/Web/appsettings.json
+++ b/Web/appsettings.json
@@ -1,31 +1,31 @@
-{
- "Email": {
- "SmtpHost": "",
- "SmtpPort": 587,
- "SmtpUser": "",
- "SmtpPassword": "",
- "SenderAddress": "",
- "UseSSL": false,
- "UseAuthentication": true
- },
- "Logging": {
- "IncludeScopes": false,
- "LogLevel": {
- "Default": "Warning"
- }
- },
- "ConnectionStrings": {
- "DefaultConnection": "Filename=querytree.db;" /* It's important for hangfire that this connection string contains a ';' */
- },
- "Passwords": {
- "Keyfile": "querytree.key"
- },
- "Customization": {
- "SystemName": "QueryTree",
- "SystemLogo": "/images/querytree_logo.png",
- "ExtraCSS": "",
- "AllowAdvancedQuery": false,
- "DataStore": "Sqlite",
- "BaseUri": ""
- }
-}
+{
+ "Email": {
+ "SmtpHost": "",
+ "SmtpPort": 587,
+ "SmtpUser": "",
+ "SmtpPassword": "",
+ "SenderAddress": "",
+ "UseSSL": false,
+ "UseAuthentication": true
+ },
+ "Logging": {
+ "IncludeScopes": false,
+ "LogLevel": {
+ "Default": "Warning"
+ }
+ },
+ "ConnectionStrings": {
+ "DefaultConnection": "Filename=querytree.db;" /* It's important for hangfire that this connection string contains a ';' */
+ },
+ "Passwords": {
+ "Keyfile": "querytree.key"
+ },
+ "Customization": {
+ "SystemName": "QueryTree",
+ "SystemLogo": "/images/querytree_logo.png",
+ "ExtraCSS": "",
+ "AllowAdvancedQuery": false,
+ "DataStore": "Sqlite",
+ "BaseUri": ""
+ }
+}
diff --git a/Web/bundleconfig.json b/Web/bundleconfig.json
index becd3de..d6b585a 100644
--- a/Web/bundleconfig.json
+++ b/Web/bundleconfig.json
@@ -1,106 +1,106 @@
-// Configure bundling and minification for the project.
-// More info at https://go.microsoft.com/fwlink/?LinkId=808241
-[
- {
- "outputFileName": "wwwroot/css/site.css",
- // An array of relative input file paths. Globbing patterns supported
- "inputFiles": [
- "Styles/qt-bootstrap.css",
- "Styles/pickatime.css",
- "Styles/site.css"
- ]
- },
- {
- "outputFileName": "wwwroot/css/aqb.css",
- "inputFiles": [
- "Styles/jquery-ui.datepicker.css",
- "Styles/aqb/base.css",
- "Styles/aqb/app.css"
- ]
- },
- {
- "outputFileName": "wwwroot/css/jquery.css",
- "inputFiles": [
- "Styles/jquery-ui.datepicker.css"
- ]
- },
- {
- "outputFileName": "wwwroot/js/connection.js",
- "inputFiles": [
- "Scripts/connection.js"
- ]
- },
- {
- "outputFileName": "wwwroot/js/schedule.js",
- "inputFiles": [
- "Scripts/lib/knockout-pickatime/knockout-pickatime.min.js",
- "Scripts/lib/knockout-pickatime/pickatime.min.js",
- "Scripts/tools.js",
- "Scripts/utils.js",
- "Scripts/backend.js"
- ]
- },
- {
- "outputFileName": "wwwroot/js/sqb.js",
- "inputFiles": [
- "Scripts/lib/jquery.maskedinput/jquery.maskedinput.min.js",
- "Scripts/lib/jquery.numeric/jquery.numeric.js",
- "Scripts/utils.js",
- "Scripts/backend.js",
- "Scripts/nodes.js",
- "Scripts/tools.js",
- "Scripts/simple.js"
- ],
- "minify": {
- "enabled": true,
- "renameLocals": true
- }
- },
- {
- "outputFileName": "wwwroot/js/fabric.js",
- "inputFiles": [
- "Scripts/lib/fabric/fabric.js",
- "Scripts/lib/fabric/cufon.js",
- "Scripts/lib/fabric/Delicious_500.font.js"
- ]
- },
- {
- "outputFileName": "wwwroot/js/aqb.js",
- "inputFiles": [
- "Scripts/lib/jquery.maskedinput/jquery.maskedinput.min.js",
- "Scripts/lib/jquery.numeric/jquery.numeric.js",
- "Scripts/jquery.querystring.js",
- "Scripts/polyfills.js",
- "Scripts/utils.js",
- "Scripts/backend.js",
- "Scripts/nodes.js",
- "Scripts/tools.js",
- "Scripts/models.js",
- "Scripts/events.js",
- "Scripts/views.js",
- "Scripts/main.js"
- ],
- "minify": {
- "enabled": true,
- "renameLocals": true
- },
- "sourceMap": true
- },
- {
- "outputFileName": "wwwroot/js/base.js",
- "inputFiles": [
- "Scripts/lib/jquery/dist/jquery.js",
- "Scripts/lib/jquery-validation/dist/jquery.validate.js",
- "Scripts/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js",
- "Scripts/lib/jquery-validation/dist/additional-methods.js",
- "Scripts/lib/bootstrap/dist/js/bootstrap.js",
- "Scripts/respond.js",
- "Scripts/ui-utils.js"
- ],
- "minify": {
- "enabled": true,
- "renameLocals": true
- },
- "sourceMap": false
- }
-]
+// Configure bundling and minification for the project.
+// More info at https://go.microsoft.com/fwlink/?LinkId=808241
+[
+ {
+ "outputFileName": "wwwroot/css/site.css",
+ // An array of relative input file paths. Globbing patterns supported
+ "inputFiles": [
+ "Styles/qt-bootstrap.css",
+ "Styles/pickatime.css",
+ "Styles/site.css"
+ ]
+ },
+ {
+ "outputFileName": "wwwroot/css/aqb.css",
+ "inputFiles": [
+ "Styles/jquery-ui.datepicker.css",
+ "Styles/aqb/base.css",
+ "Styles/aqb/app.css"
+ ]
+ },
+ {
+ "outputFileName": "wwwroot/css/jquery.css",
+ "inputFiles": [
+ "Styles/jquery-ui.datepicker.css"
+ ]
+ },
+ {
+ "outputFileName": "wwwroot/js/connection.js",
+ "inputFiles": [
+ "Scripts/connection.js"
+ ]
+ },
+ {
+ "outputFileName": "wwwroot/js/schedule.js",
+ "inputFiles": [
+ "Scripts/lib/knockout-pickatime/knockout-pickatime.min.js",
+ "Scripts/lib/knockout-pickatime/pickatime.min.js",
+ "Scripts/tools.js",
+ "Scripts/utils.js",
+ "Scripts/backend.js"
+ ]
+ },
+ {
+ "outputFileName": "wwwroot/js/sqb.js",
+ "inputFiles": [
+ "Scripts/lib/jquery.maskedinput/jquery.maskedinput.min.js",
+ "Scripts/lib/jquery.numeric/jquery.numeric.js",
+ "Scripts/utils.js",
+ "Scripts/backend.js",
+ "Scripts/nodes.js",
+ "Scripts/tools.js",
+ "Scripts/simple.js"
+ ],
+ "minify": {
+ "enabled": true,
+ "renameLocals": true
+ }
+ },
+ {
+ "outputFileName": "wwwroot/js/fabric.js",
+ "inputFiles": [
+ "Scripts/lib/fabric/fabric.js",
+ "Scripts/lib/fabric/cufon.js",
+ "Scripts/lib/fabric/Delicious_500.font.js"
+ ]
+ },
+ {
+ "outputFileName": "wwwroot/js/aqb.js",
+ "inputFiles": [
+ "Scripts/lib/jquery.maskedinput/jquery.maskedinput.min.js",
+ "Scripts/lib/jquery.numeric/jquery.numeric.js",
+ "Scripts/jquery.querystring.js",
+ "Scripts/polyfills.js",
+ "Scripts/utils.js",
+ "Scripts/backend.js",
+ "Scripts/nodes.js",
+ "Scripts/tools.js",
+ "Scripts/models.js",
+ "Scripts/events.js",
+ "Scripts/views.js",
+ "Scripts/main.js"
+ ],
+ "minify": {
+ "enabled": true,
+ "renameLocals": true
+ },
+ "sourceMap": true
+ },
+ {
+ "outputFileName": "wwwroot/js/base.js",
+ "inputFiles": [
+ "Scripts/lib/jquery/dist/jquery.js",
+ "Scripts/lib/jquery-validation/dist/jquery.validate.js",
+ "Scripts/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js",
+ "Scripts/lib/jquery-validation/dist/additional-methods.js",
+ "Scripts/lib/bootstrap/dist/js/bootstrap.js",
+ "Scripts/respond.js",
+ "Scripts/ui-utils.js"
+ ],
+ "minify": {
+ "enabled": true,
+ "renameLocals": true
+ },
+ "sourceMap": false
+ }
+]
diff --git a/docs/autojoin.md b/docs/autojoin.md
index 435db00..3991674 100644
--- a/docs/autojoin.md
+++ b/docs/autojoin.md
@@ -10,7 +10,7 @@ There are two ways to tell QueryTree how to join between tables.
## Foreign Keys
-The most robust way to tell QueryTree how to join between tables is to create a foreign key relationship between the tables.
+The most robust way to tell QueryTree how to join between tables is to create a foreign key relationship between the tables.
For example, given the following two tables, QueryTree will prompt to join from "orders" to "users".
diff --git a/docs/docker.md b/docs/docker.md
index 87b690f..364ea77 100644
--- a/docs/docker.md
+++ b/docs/docker.md
@@ -10,7 +10,7 @@ docker run -p 8080:80 --name querytree -d d4software/querytree:latest
The image runs QueryTree Web on port 80 so you'll need to proxy through to port 80.
-`--name` Sets a name for the container, this can be anything you want. It makes it easier to re-run this container with your saved configurations later. If this is not set docker will assign a random name to the container.
+`--name` Sets a name for the container, this can be anything you want. It makes it easier to re-run this container with your saved configurations later. If this is not set docker will assign a random name to the container.
### Confirm the container is running
@@ -41,7 +41,7 @@ To override these settings in docker you can provide an enviroment variable that
The format for these enviroment variables match the JSON structure of the file but instead of using dot notation you need to replace the DOT (.) with two underscores(__).
-Take the `Customization.SystemName` configuration as an example.
+Take the `Customization.SystemName` configuration as an example.
```json
{
diff --git a/update-packages.sh b/update-packages.sh
new file mode 100755
index 0000000..57f9306
--- /dev/null
+++ b/update-packages.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+regex='PackageReference Include="([^"]*)" Version="([^"]*)"'
+find . -name "*.*proj" | while read proj
+do
+ while read line
+ do
+ if [[ $line =~ $regex ]]
+ then
+ name="${BASH_REMATCH[1]}"
+ version="${BASH_REMATCH[2]}"
+ if [[ $version != *-* ]]
+ then
+ dotnet add $proj package $name
+ fi
+ fi
+ done < $proj
+done