Beispiel #1
0
        protected virtual bool CheckUsingSql(View view, Durados.Rule rule, Durados.TriggerDataAction dataAction, Dictionary <string, object> values, string pk, DataRow prevRow, string connectionString, int currentUserId, string currentUserRole)
        {
            using (IDbConnection connection = GetConnection(view))
            {
                connection.Open();
                string sql;
                if (dataAction == TriggerDataAction.BeforeCreate || dataAction == TriggerDataAction.AfterCreateBeforeCommit || dataAction == TriggerDataAction.AfterCreate || dataAction == TriggerDataAction.AfterDeleteBeforeCommit || dataAction == TriggerDataAction.AfterDelete)
                {
                    sql = GetSql(rule, view, prevRow, null, currentUserId, currentUserRole, values);
                }
                else
                {
                    sql = GetSql(rule, view, prevRow, pk, currentUserId, currentUserRole, values);
                }
                sql = sql.ReplaceWithDollar(view, values);
                sql = sql.ReplaceWithSharp(view, null, prevRow);

                if ((sql.Contains('$') || sql.Contains(Database.DictionaryPrefix + Database.SysPrevPlaceHolder)) && prevRow != null)
                {
                    sql = sql.ReplaceWithDollar(view, values, prevRow);
                }
                if (sql.Contains('$') || sql.Contains('#') || sql.Contains(Database.DictionaryPrefix))
                {
                    return(false);
                }

                IDbCommand command = GetCommand(sql, connection);

                if (!string.IsNullOrEmpty(pk))
                {
                    foreach (SqlParameter parameter in GetWhereParemeters(view, pk))
                    {
                        command.Parameters.Add(GetNewParameter(command, parameter.ParameterName, parameter.Value));
                    }
                }

                object scalar = null;
                try
                {
                    scalar = command.ExecuteScalar();
                }
                catch
                {
                    return(false);
                }

                if (scalar == null || scalar == DBNull.Value)
                {
                    return(false);
                }
                else
                {
                    return(true);
                }
            }
        }
Beispiel #2
0
        public virtual bool Check(Durados.TriggerDataAction dataAction, DataRow row)
        {
            if (row == null)
            {
                return(false);
            }

            List <Rule> rules         = Rules.Values.Where(r => r.DataAction == dataAction).ToList();
            int         currentUserId = Convert.ToInt32(Database.GetUserID());

            foreach (Durados.Rule rule in rules)
            {
                if ((new Durados.Workflow.LogicalParser()).Check(rule.WhereCondition.Replace(currentUserId).Replace(row)))
                {
                    return(true);
                }
            }

            return(false);
        }
Beispiel #3
0
        public virtual bool Check(View view, Durados.Rule rule, Durados.TriggerDataAction dataAction, Dictionary <string, object> values, string pk, DataRow prevRow, bool useSqlPareser, string connectionString, int currentUserId, string currentUserRole)
        {
            if (rule.WhereCondition.Equals("true"))
            {
                return(true);
            }
            if (useSqlPareser && view != view.Database.GetUserView())
            {
                return(CheckUsingSql(view, rule, dataAction, values, pk, prevRow, connectionString, currentUserId, currentUserRole));
            }
            else
            if (string.IsNullOrEmpty(rule.WhereCondition))
            {
                return(true);
            }

            return(parser.Check(rule.WhereCondition.Replace(currentUserId)
                                .Replace(Database.SysUsernamePlaceHolder.AsToken(), GetCurrentUsername(view))
                                .Replace(Database.SysRolePlaceHolder.AsToken(), currentUserRole)
                                .Replace(GetPrevRowAsToken(view, prevRow)).Replace(GetValuesAsToken(values)).Replace(values)
                                .ReplaceConfig(view)));
            //.ReplaceGlobals(view));
        }
Beispiel #4
0
 public bool ShouldTrigger(Durados.TriggerDataAction dataAction)
 {
     return(DataAction == dataAction || (DataAction == TriggerDataAction.AfterCreateOrEdit && (dataAction == TriggerDataAction.AfterCreate || dataAction == TriggerDataAction.AfterEdit)));
 }
Beispiel #5
0
        public virtual void Execute(object controller, Dictionary <string, Parameter> parameters, View view, Dictionary <string, object> values, DataRow prevRow, string pk, string connectionString, int currentUsetId, string currentUserRole, IDbCommand command, IDbCommand sysCommand, string actionName, Durados.TriggerDataAction dataAction)
        {
            if (pk != null && (prevRow == null || dataAction == TriggerDataAction.AfterCreate || dataAction == TriggerDataAction.AfterEdit || dataAction == TriggerDataAction.AfterCreateBeforeCommit || dataAction == TriggerDataAction.AfterEditBeforeCommit) && controller is Durados.Data.IData)
            {
                try
                {
                    if (((Durados.Data.IData)controller).DataHandler != null)
                    {
                        prevRow = ((Durados.Data.IData)controller).DataHandler.GetDataRow(view, pk, command);
                    }
                }
                catch { }
            }

            var guid = Guid.NewGuid();

            //if (System.Web.HttpContext.Current != null && System.Web.HttpContext.Current.Request.QueryString[GuidKey] != null)
            //{
            //    guid = new Guid(System.Web.HttpContext.Current.Request.QueryString[GuidKey]);
            //}
            SetCacheInCurrentRequest("js" + guid, new Dictionary <string, object>()
            {
                { "controller", controller }, { "connectionString", connectionString }, { "currentUsetId", currentUsetId }, { "currentUserRole", currentUserRole }, { "command", command }, { "sysCommand", sysCommand }, { "actionName", actionName }, { "dataAction", dataAction }
            });


            SetCacheInCurrentRequest(ConnectionStringKey, view.Database.SysDbConnectionString);
            SetCacheInCurrentRequest(GuidKey, guid);

            if (!parameters.ContainsKey("code"))
            {
                throw new DuradosException("code was not supplied");
            }

            string code = parameters["code"].Value.Replace(Engine.AsToken(values), ((Durados.Workflow.INotifier)controller).GetTableViewer(), view);

            string currentUsername = view.Database.GetCurrentUsername();

            try
            {
                code = code.Replace(Durados.Database.UserPlaceHolder, currentUsetId.ToString(), false).Replace(Durados.Database.SysUserPlaceHolder.AsToken(), currentUsetId.ToString(), false)
                       .Replace(Durados.Database.UsernamePlaceHolder, currentUsername, false).Replace(Durados.Database.SysUsernamePlaceHolder.AsToken(), currentUsername)
                       .Replace(Durados.Database.RolePlaceHolder, currentUserRole, false).Replace(Durados.Database.SysRolePlaceHolder.AsToken(), currentUserRole)
                       .ReplaceConfig(view);
            }
            catch { }

            Dictionary <string, object> clientParameters = new Dictionary <string, object>();
            Dictionary <string, object> newRow           = new Dictionary <string, object>();
            Dictionary <string, object> oldRow           = new Dictionary <string, object>();
            Dictionary <string, object> userProfile      = new Dictionary <string, object>();

            bool debug = false;

            if (IsDebug())
            {
                debug = true;
            }

            if (values != null)
            {
                foreach (string key in values.Keys)
                {
                    if (key == "$$debug$$" || key == "$$debug$$".AsToken())
                    {
                        debug = true;
                    }
                    else
                    {
                        string keyWithoutToken = key.TrimStart("{{".ToCharArray()).TrimEnd("}}".ToCharArray());

                        if (key.StartsWith("{{"))
                        {
                            if (!clientParameters.ContainsKey(keyWithoutToken))
                            {
                                clientParameters.Add(keyWithoutToken, values[key]);
                            }
                        }

                        if (view.GetFieldsByJsonName(keyWithoutToken) == null || view.GetFieldsByJsonName(keyWithoutToken).Length == 0)
                        {
                            if (view.Fields.ContainsKey(keyWithoutToken))
                            {
                                string jsonName = view.Fields[keyWithoutToken].JsonName;

                                if (!newRow.ContainsKey(jsonName))
                                {
                                    newRow.Add(jsonName, values[key]);
                                }
                            }
                            else
                            {
                                if (!clientParameters.ContainsKey(keyWithoutToken))
                                {
                                    clientParameters.Add(keyWithoutToken, values[key]);
                                }
                            }
                        }
                        else
                        {
                            if (!newRow.ContainsKey(keyWithoutToken))
                            {
                                newRow.Add(keyWithoutToken, values[key]);
                            }
                        }
                    }
                }
            }
            SetCacheInCurrentRequest(Debug, debug);

            if (prevRow != null)
            {
                foreach (Field field in view.Fields.Values)
                {
                    if (!oldRow.ContainsKey(field.JsonName))
                    {
                        if (field.FieldType == FieldType.Column && field.IsDate)
                        {
                            oldRow.Add(field.JsonName, prevRow[((ColumnField)field).DataColumn.ColumnName]);
                        }
                        else
                        {
                            oldRow.Add(field.JsonName, field.GetValue(prevRow));
                        }
                    }
                }
                //var shallowRow = view.RowToShallowDictionary(prevRow, pk);
                //foreach (Field field in view.Fields.Values)
                //{
                //    if (oldRow.ContainsKey(field.JsonName) && shallowRow.ContainsKey(field.JsonName))
                //    {
                //        if (field.FieldType == FieldType.Column && (field.IsBoolean || field.IsPoint || field.IsNumeric))
                //        {
                //            oldRow[field.JsonName] = shallowRow[field.JsonName];
                //        }
                //    }
                //}
            }

            userProfile.Add("username", view.Database.GetCurrentUsername());
            userProfile.Add("role", currentUserRole);
            userProfile.Add("app", view.Database.GetCurrentAppName());
            userProfile.Add("userId", view.Database.GetCurrentUserId());
            userProfile.Add("token", GetUserProfileAuthToken(view));
            userProfile.Add("anonymousToken", view.Database.GetAnonymousToken().ToString());
            userProfile.Add("info", GetUserProfileInfo(view));


            if (!clientParameters.ContainsKey(FILEDATA))
            {
                int parametersLimit = (int)view.Database.GetLimit(Limits.ActionParametersKbSize);

                HandleParametersSizeLimit(parametersLimit, clientParameters);
                userProfile.Add("request", GetRequest());
            }



            var CONSTS = new Dictionary <string, object>()
            {
                { "apiUrl", System.Web.HttpContext.Current.Request.Url.Scheme + "://" + System.Web.HttpContext.Current.Request.Url.Host + ":" + System.Web.HttpContext.Current.Request.Url.Port + System.Web.HttpContext.Current.Request.ApplicationPath }, { "actionGuid", System.Web.HttpContext.Current.Request.QueryString[GuidKey] ?? (System.Web.HttpContext.Current.Items[GuidKey] ?? guid.ToString()) }
            };

            //Newtonsoft.Json.JsonConvert.SerializeObject

            var theJavaScriptSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();

            theJavaScriptSerializer.MaxJsonLength = int.MaxValue;

            var parser = new Jint.Native.Json.JsonParser(call2);
            //var userInput = parser.Parse(theJavaScriptSerializer.Serialize(newRow));
            //Object clientParametersToSend = null;
            //if (!clientParameters.ContainsKey("filedata"))
            //{
            //    clientParametersToSend = parser.Parse(theJavaScriptSerializer.Serialize(clientParameters));
            //}
            //else
            //{
            //    System.Web.HttpContext.Current.Items["file_stream"] = clientParameters["filedata"];
            //    clientParameters["filedata"] = "file_stream";
            //    clientParametersToSend = clientParameters;
            //}
            //var dbRow = parser.Parse(theJavaScriptSerializer.Serialize(oldRow));
            //var userProfile2 = parser.Parse(theJavaScriptSerializer.Serialize(userProfile));
            //var CONSTS2 = parser.Parse(theJavaScriptSerializer.Serialize(CONSTS));


            //var Config = view.Database.GetConfigDictionary();
            //var Config2 = parser.Parse(theJavaScriptSerializer.Serialize(Config));
            var    userInput = parser.Parse(Newtonsoft.Json.JsonConvert.SerializeObject(newRow));
            Object clientParametersToSend = null;

            bool upload = false;

            if (!clientParameters.ContainsKey(FILEDATA))
            {
                clientParametersToSend = parser.Parse(Newtonsoft.Json.JsonConvert.SerializeObject(clientParameters));
            }
            else
            {
                upload = true;
                System.Web.HttpContext.Current.Items["file_stream"] = clientParameters[FILEDATA];
                clientParameters[FILEDATA] = "file_stream";
                clientParametersToSend     = clientParameters;
            }

            if (clientParameters.ContainsKey(FILEDATA) || clientParameters.ContainsKey(FILENAME))
            {
                System.Web.HttpContext.Current.Items[StorageAccountsKey] = view.Database.CloudStorages;
            }

            var dbRow        = parser.Parse(Newtonsoft.Json.JsonConvert.SerializeObject(oldRow));
            var userProfile2 = parser.Parse(Newtonsoft.Json.JsonConvert.SerializeObject(userProfile));
            var CONSTS2      = parser.Parse(Newtonsoft.Json.JsonConvert.SerializeObject(CONSTS));


            var Config  = view.Database.GetConfigDictionary();
            var Config2 = parser.Parse(Newtonsoft.Json.JsonConvert.SerializeObject(Config));

            var Environment  = view.Database.GetEnvironmentDictionary();
            var Environment2 = parser.Parse(Newtonsoft.Json.JsonConvert.SerializeObject(Environment));


            Limits limit = Limits.ActionTimeMSec;

            if (upload)
            {
                limit = Limits.UploadTimeMSec;
            }

            object actionTimeMSecObject = view.Database.GetLimit(limit);
            int    actionTimeMSec       = -1;

            if (!Int32.TryParse(actionTimeMSecObject.ToString(), out actionTimeMSec))
            {
                throw new DuradosException("actionTimeMSecLimit in web.config is not numeric or too long");
            }

            TimeSpan?timeoutInterval = new TimeSpan(0, 0, 0, 0, actionTimeMSec);

            if (actionTimeMSec == 0)
            {
                timeoutInterval = null;
            }

            string actionId = Guid.NewGuid().ToString();

            string startMessage = theJavaScriptSerializer.Serialize(new { objectName = view.JsonName, actionName = actionName, id = actionId, @event = "started", time = GetSequence(view), data = new { userInput = newRow, dbRow = oldRow, parameters = clientParameters, userProfile = userProfile } });

            Backand.Logger.Log(startMessage, 502);

            var call = new Jint.Engine(cfg => cfg.AllowClr(typeof(Backand.XMLHttpRequest).Assembly), timeoutInterval, GetLineStart(), new ActionPath()
            {
                @object = view.JsonName, action = actionName
            });

            try
            {
                call.SetValue("userInput", userInput)
                .SetValue("btoa", new btoaHandler(Backand.Convert.btoa))
                .SetValue("dbRow", dbRow)
                .SetValue("parameters", clientParametersToSend)
                .SetValue("userProfile", userProfile2)
                .SetValue("CONSTS", CONSTS2)
                .SetValue("Config", Config2)
                .SetValue("Environment", Environment2)
                .Execute(GetXhrWrapper() + code + "; function call(){return backandCallback(userInput, dbRow, parameters, userProfile);}");
            }
            catch (TimeoutException exception)
            {
                Handle503(theJavaScriptSerializer, view.JsonName, actionName, actionId, exception.Message, view);
                string errorMessage = "Timeout: The operation took longer than " + actionTimeMSec + " milliseconds limit. at (" + view.JsonName + "/" + actionName + ")";
                if (!IsSubAction())
                {
                    Backand.Logger.Log(exception.Message, 501);
                    if (IsDebug())
                    {
                        throw new MainActionInDebugJavaScriptException(errorMessage, exception);
                    }
                    else
                    {
                        throw new MainActionJavaScriptException(errorMessage, exception);
                    }
                }
                else
                {
                    throw new SubActionJavaScriptException(errorMessage, exception);
                }
            }
            catch (Exception exception)
            {
                Handle503(theJavaScriptSerializer, view.JsonName, actionName, actionId, exception.Message, view);
                string errorMessage = "Syntax error: " + HandleLineCodes(exception.Message, view.JsonName, actionName, view, IsDebug());
                if (!IsSubAction())
                {
                    Backand.Logger.Log(exception.Message, 501);
                    if (IsDebug())
                    {
                        throw new MainActionInDebugJavaScriptException(errorMessage, exception);
                    }
                    else
                    {
                        throw new MainActionJavaScriptException(errorMessage, exception);
                    }
                }
                else
                {
                    throw new SubActionJavaScriptException(errorMessage, exception);
                }
            }
            object r = null;

            try
            {
                var r2 = call.GetValue("call").Invoke();
                if (!r2.IsNull())
                {
                    r = r2.ToObject();
                }

                string endMessage = null;
                try
                {
                    endMessage = theJavaScriptSerializer.Serialize(new { objectName = view.JsonName, actionName = actionName, id = actionId, @event = "ended", time = GetSequence(view), data = r });
                }
                catch (Exception exception)
                {
                    if (exception.Message.StartsWith("A circular reference was detected while serializing an object"))
                    {
                        object oNull = null;
                        endMessage = theJavaScriptSerializer.Serialize(new { objectName = view.JsonName, actionName = actionName, id = actionId, @event = "ended", time = GetSequence(view), data = oNull });
                    }
                    else
                    {
                        endMessage = "Failed to serialize response";
                        if (!IsSubAction())
                        {
                            Backand.Logger.Log(endMessage, 501);
                            //if (IsDebug())
                            //{
                            //    values[ReturnedValueKey] = message;
                            //    return;
                            //}
                            //else
                            if (IsDebug())
                            {
                                throw new MainActionInDebugJavaScriptException(endMessage, exception);
                            }
                            else
                            {
                                throw new MainActionJavaScriptException(endMessage, exception);
                            }
                        }
                        else
                        {
                            throw new SubActionJavaScriptException(endMessage, exception);
                        }
                    }
                }

                Backand.Logger.Log(endMessage, 503);
            }
            catch (TimeoutException exception)
            {
                string message = "Timeout: The operation took longer than " + actionTimeMSec + " milliseconds limit. at (" + view.JsonName + "/" + actionName + ")";
                Handle503(theJavaScriptSerializer, view.JsonName, actionName, actionId, message, view);
                if (!IsSubAction())
                {
                    Backand.Logger.Log(message, 501);
                    if (IsDebug())
                    {
                        throw new MainActionInDebugJavaScriptException(message, exception);
                    }
                    else
                    {
                        throw new MainActionJavaScriptException(message, exception);
                    }
                }
                else
                {
                    throw new SubActionJavaScriptException(message, exception);
                }
            }
            catch (Exception exception)
            {
                string message = (exception.InnerException == null) ? exception.Message : exception.InnerException.Message;
                string trace   = message;
                if (exception is Jint.Runtime.JavaScriptException)
                {
                    trace = ((Jint.Runtime.JavaScriptException)exception).GetTrace();
                }
                trace = HandleLineCodes(trace, view.JsonName, actionName, view, IsDebug());
                Handle503(theJavaScriptSerializer, view.JsonName, actionName, actionId, trace, view);
                if (!IsSubAction())
                {
                    IMainActionJavaScriptException mainActionJavaScriptException;
                    Backand.Logger.Log(trace, 501);
                    if (IsDebug())
                    {
                        mainActionJavaScriptException = new MainActionInDebugJavaScriptException(message, exception);
                    }
                    else
                    {
                        mainActionJavaScriptException = new MainActionJavaScriptException(message, exception);
                    }

                    mainActionJavaScriptException.JintTrace = trace;
                    throw (Exception)mainActionJavaScriptException;
                }
                else
                {
                    throw new SubActionJavaScriptException(message, exception);
                }
            }

            var v = call.GetValue("userInput").ToObject();

            if (v != null && v is System.Dynamic.ExpandoObject)
            {
                IDictionary <string, object> newValues = v as IDictionary <string, object>;
                foreach (string key in newValues.Keys)
                {
                    if (values.ContainsKey(key))
                    {
                        object  val    = newValues[key];
                        Field[] fields = view.GetFieldsByJsonName(key);
                        val         = DateConversion(view, val, fields);
                        values[key] = val;
                    }
                    else
                    {
                        Field[] fields = view.GetFieldsByJsonName(key);
                        if (fields.Length > 0)
                        {
                            string fieldName = fields[0].Name;
                            object val       = newValues[key];
                            val = DateConversion(view, val, fields);
                            if (values.ContainsKey(fieldName))
                            {
                                values[fieldName] = val;
                            }
                            else
                            {
                                values.Add(fieldName, val);
                            }
                        }
                        else
                        {
                            values.Add(key, newValues[key]);
                        }
                    }
                }
            }

            if (r != null && values != null && dataAction == TriggerDataAction.OnDemand)
            {
                if (!values.ContainsKey(ReturnedValueKey))
                {
                    values.Add(ReturnedValueKey, r);
                }
                else
                {
                    values[ReturnedValueKey] = r;
                }
            }
        }
Beispiel #6
0
 protected virtual object ExecuteJs(object controller, Dictionary <string, Parameter> parameters, View view, DataRow prevRow, Dictionary <string, object> values, string pk, string connectionString, int currentUserId, string currentUserRole, IDbCommand command, IDbCommand sysCommand, string actionName, Durados.TriggerDataAction dataAction)
 {
     javaScript.Execute(controller, parameters, view, values, prevRow, pk, connectionString, currentUserId, currentUserRole, command, sysCommand, actionName, dataAction);
     return(null);
 }
Beispiel #7
0
        protected virtual object PerformAction(object controller, Durados.WorkflowAction action, Dictionary <string, Parameter> parameters, View view, Dictionary <string, object> values, DataRow prevRow, string pk, string connectionString, int currentUserId, string currentUserRole, IDbCommand command, IDbCommand sysCommand, string actionName, Durados.TriggerDataAction dataAction, Rule rule)
        {
            switch (action)
            {
            case WorkflowAction.Notify:
                return(Notify(controller, parameters, view, prevRow, values, pk, connectionString, currentUserId, currentUserRole, command));

            //case WorkflowAction.Task:
            //    return Task(controller, parameters, view, values, pk, connectionString);
            case WorkflowAction.Validate:
                return(Validate(controller, parameters, view, values, prevRow, pk, connectionString, currentUserId, currentUserRole));

            case WorkflowAction.Execute:
                return(Execute(controller, parameters, view, prevRow, values, pk, connectionString, currentUserId, currentUserRole, command));

            case WorkflowAction.JavaScript:
                return(ExecuteJs(controller, parameters, view, prevRow, values, pk, connectionString, currentUserId, currentUserRole, command, sysCommand, actionName, dataAction));

            case WorkflowAction.NodeJS:
                return(ExecuteNodeJS(controller, parameters, view, prevRow, values, pk, connectionString, currentUserId, currentUserRole, command, sysCommand, actionName));

            case WorkflowAction.Lambda:
                return(ExecuteLambda(controller, parameters, view, prevRow, values, pk, connectionString, currentUserId, currentUserRole, command, sysCommand, actionName, rule));

            case WorkflowAction.WebService:
                return(CallWebService(controller, parameters, view, prevRow, values, pk, connectionString, currentUserId, currentUserRole, command));

            case WorkflowAction.CompleteStep:
                return(CompleteStep(controller, parameters, view, prevRow, values, pk, connectionString, currentUserId, currentUserRole, command));

            case WorkflowAction.Approval:
                return(Approval(controller, parameters, view, prevRow, values, pk, connectionString, command));

            case WorkflowAction.Document:
                return(Document(controller, parameters, view, prevRow, values, pk, connectionString, command));

            case WorkflowAction.Xml:
                return(ExportToXml(controller, parameters, view, prevRow, values, pk, connectionString, currentUserId, currentUserRole, command));

            case WorkflowAction.Custom:
                return(Custom(controller, parameters, values, pk, connectionString));

            default:
                return(null);
            }
        }
Beispiel #8
0
        public virtual void PerformActions(object controller, Durados.View view, Durados.TriggerDataAction dataAction, Dictionary <string, object> values, string pk, DataRow prevRow, string connectionString, int currentUserId, string currentUserRole, IDbCommand command, IDbCommand sysCommand, string ruleName = null)
        {
            SetCurrentDatabase(view);

            IEnumerable <Rule> rules = view.GetRules().Where(r => r.ShouldTrigger(dataAction)).OrderBy(r => r.Name).ToList();

            if (ruleName != null)
            {
                rules = rules.Where(r => r.Name.Equals(ruleName)).ToList();
            }

            List <Rule>      failedRules = new List <Rule>();
            List <Exception> exceptions  = new List <Exception>();

            Results = new Dictionary <string, object>();

            foreach (Durados.Rule rule in rules)
            {
                bool isChecked = Check(view, rule, dataAction, values, pk, prevRow, rule.UseSqlParser, connectionString, currentUserId, currentUserRole);
                if (isChecked)
                {
                    try
                    {
                        view.Database.Logger.Log(view.Name, "Start", "PerformAction " + rule.Name, "Engine", "", 14, view.Database.Logger.NowWithMilliseconds(), DateTime.Now);
                        object result = PerformAction(controller, rule.WorkflowAction, rule.GetParameters(), view, values, prevRow, pk, connectionString, currentUserId, currentUserRole, command, sysCommand, rule.Name, dataAction, rule);
                        view.Database.Logger.Log(view.Name, "End", "PerformAction " + rule.Name, "Engine", "", 14, view.Database.Logger.NowWithMilliseconds(), DateTime.Now);
                        if (result != null)
                        {
                            Results.Add(rule.Name, result);
                            if (result is Step.Result)
                            {
                                if (StepResult != null)
                                {
                                    throw new WorkflowEngineException("Must be only one Complete Step rule");
                                }

                                StepResult = (Step.Result)result;
                            }
                        }
                    }
                    catch (SqlExecuteException sqlExecuteException)
                    {
                        throw new WorkflowEngineException(sqlExecuteException.Message + "\nat (" + view.JsonName + "/" + rule.Name + ")");
                    }
                    catch (WorkflowEngineException workflowEngineException)
                    {
                        if ((rule.Name == "Create My App User" || rule.Name == "Update My App User" || rule.Name == "Delete My App User") && !view.Database.Views.ContainsKey("users"))
                        {
                        }
                        else
                        {
                            throw workflowEngineException;
                        }
                    }
                    catch (Exception exception)
                    {
                        failedRules.Add(rule);

                        exceptions.Add(exception);
                    }
                }
            }

            if (exceptions.Count > 0)
            {
                if (exceptions[0] is NodeJsException && ((NodeJsException)exceptions[0]).JsonFormat)
                {
                    throw exceptions[0];
                }

                //Backand.Logger.Log(exceptions[0].Message + "\n" + exceptions[0].StackTrace, 501);
                if (IsDebug())
                {
                    throw new WorkflowEngineException(exceptions.ToArray(), failedRules.ToArray());
                }
                else
                {
                    throw exceptions[0];
                }
            }
        }