public async Task <IActionResult> Edit(EditTenantViewModel model)
        {
            if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTenants))
            {
                return(Unauthorized());
            }

            if (!IsDefaultShell())
            {
                return(Unauthorized());
            }

            if (ModelState.IsValid)
            {
                ValidateViewModel(model, false);
            }

            var shellContext = GetShells()
                               .Where(x => string.Equals(x.Settings.Name, model.Name, StringComparison.OrdinalIgnoreCase))
                               .FirstOrDefault();

            if (shellContext == null)
            {
                return(NotFound());
            }

            var shellSettings = shellContext.Settings;

            if (ModelState.IsValid)
            {
                shellSettings.RequestUrlPrefix = model.RequestUrlPrefix?.Trim();
                shellSettings.RequestUrlHost   = model.RequestUrlHost;

                // The user can change the 'preset' database information only if the
                // tenant has not been initialized yet
                if (shellSettings.State == TenantState.Uninitialized)
                {
                    shellSettings.DatabaseProvider = model.DatabaseProvider;
                    shellSettings.TablePrefix      = model.TablePrefix;
                    shellSettings.ConnectionString = model.ConnectionString;
                }

                _orchardHost.UpdateShellSettings(shellSettings);

                return(RedirectToAction(nameof(Index)));
            }

            // The user can change the 'preset' database information only if the
            // tenant has not been initialized yet
            if (shellSettings.State == TenantState.Uninitialized)
            {
                model.DatabaseProvider      = shellSettings.DatabaseProvider;
                model.TablePrefix           = shellSettings.TablePrefix;
                model.ConnectionString      = shellSettings.ConnectionString;
                model.CanSetDatabasePresets = true;
            }

            // If we got this far, something failed, redisplay form
            return(View(model));
        }
        public async Task <string> SetupInternalAsync(SetupContext context)
        {
            string executionId;

            if (_logger.IsEnabled(LogLevel.Information))
            {
                _logger.LogInformation("Running setup for tenant '{0}'.", _shellSettings.Name);
            }

            // Features to enable for Setup
            string[] hardcoded =
            {
                "OrchardCore.Commons",
                "OrchardCore.Features",
                "OrchardCore.Recipes",
                "OrchardCore.Scripting"
            };

            context.EnabledFeatures = hardcoded.Union(context.EnabledFeatures ?? Enumerable.Empty <string>()).Distinct().ToList();

            // Set shell state to "Initializing" so that subsequent HTTP requests are responded to with "Service Unavailable" while Orchard is setting up.
            _shellSettings.State = TenantState.Initializing;

            var shellSettings = new ShellSettings(_shellSettings.Configuration);

            if (string.IsNullOrEmpty(shellSettings.DatabaseProvider))
            {
                shellSettings.DatabaseProvider = context.DatabaseProvider;
                shellSettings.ConnectionString = context.DatabaseConnectionString;
                shellSettings.TablePrefix      = context.DatabaseTablePrefix;
            }

            // Creating a standalone environment based on a "minimum shell descriptor".
            // In theory this environment can be used to resolve any normal components by interface, and those
            // components will exist entirely in isolation - no crossover between the safemode container currently in effect
            // It is used to initialize the database before the recipe is run.

            var shellDescriptor = new ShellDescriptor
            {
                Features = context.EnabledFeatures.Select(id => new ShellFeature {
                    Id = id
                }).ToList()
            };

            using (var shellContext = await _shellContextFactory.CreateDescribedContextAsync(shellSettings, shellDescriptor))
            {
                using (var scope = shellContext.EnterServiceScope())
                {
                    var store = scope.ServiceProvider.GetRequiredService <IStore>();

                    try
                    {
                        await store.InitializeAsync();
                    }
                    catch (Exception e)
                    {
                        // Tables already exist or database was not found

                        // The issue is that the user creation needs the tables to be present,
                        // if the user information is not valid, the next POST will try to recreate the
                        // tables. The tables should be rolled back if one of the steps is invalid,
                        // unless the recipe is executing?

                        _logger.LogError("An error occurred while initializing the datastore.", e);
                        context.Errors.Add("DatabaseProvider", T["An error occurred while initializing the datastore: {0}", e.Message]);
                        return(null);
                    }

                    // Create the "minimum shell descriptor"
                    await scope
                    .ServiceProvider
                    .GetService <IShellDescriptorManager>()
                    .UpdateShellDescriptorAsync(0,
                                                shellContext.Blueprint.Descriptor.Features,
                                                shellContext.Blueprint.Descriptor.Parameters);

                    var deferredTaskEngine = scope.ServiceProvider.GetService <IDeferredTaskEngine>();

                    if (deferredTaskEngine != null && deferredTaskEngine.HasPendingTasks)
                    {
                        var taskContext = new DeferredTaskContext(scope.ServiceProvider);
                        await deferredTaskEngine.ExecuteTasksAsync(taskContext);
                    }
                }

                executionId = Guid.NewGuid().ToString("n");

                // Create a new scope for the recipe thread to prevent race issues with other scoped
                // services from the request.
                using (var scope = shellContext.EnterServiceScope())
                {
                    var recipeExecutor = scope.ServiceProvider.GetService <IRecipeExecutor>();

                    // Right now we run the recipe in the same thread, later use polling from the setup screen
                    // to query the current execution.
                    //await Task.Run(async () =>
                    //{
                    await recipeExecutor.ExecuteAsync(executionId, context.Recipe, new
                    {
                        SiteName                 = context.SiteName,
                        AdminUsername            = context.AdminUsername,
                        AdminEmail               = context.AdminEmail,
                        AdminPassword            = context.AdminPassword,
                        DatabaseProvider         = context.DatabaseProvider,
                        DatabaseConnectionString = context.DatabaseConnectionString,
                        DatabaseTablePrefix      = context.DatabaseTablePrefix
                    });

                    //});
                }
            }

            // Reloading the shell context as the recipe  has probably updated its features
            using (var shellContext = await _orchardHost.CreateShellContextAsync(shellSettings))
            {
                using (var scope = shellContext.EnterServiceScope())
                {
                    var hasErrors = false;

                    Action <string, string> reportError = (key, message) => {
                        hasErrors           = true;
                        context.Errors[key] = message;
                    };

                    // Invoke modules to react to the setup event
                    var setupEventHandlers = scope.ServiceProvider.GetServices <ISetupEventHandler>();
                    var logger             = scope.ServiceProvider.GetRequiredService <ILogger <SetupService> >();

                    await setupEventHandlers.InvokeAsync(x => x.Setup(
                                                             context.SiteName,
                                                             context.AdminUsername,
                                                             context.AdminEmail,
                                                             context.AdminPassword,
                                                             context.DatabaseProvider,
                                                             context.DatabaseConnectionString,
                                                             context.DatabaseTablePrefix,
                                                             reportError
                                                             ), logger);

                    if (hasErrors)
                    {
                        return(executionId);
                    }

                    var deferredTaskEngine = scope.ServiceProvider.GetService <IDeferredTaskEngine>();

                    if (deferredTaskEngine != null && deferredTaskEngine.HasPendingTasks)
                    {
                        var taskContext = new DeferredTaskContext(scope.ServiceProvider);
                        await deferredTaskEngine.ExecuteTasksAsync(taskContext);
                    }
                }
            }

            // Update the shell state
            shellSettings.State = TenantState.Running;
            _orchardHost.UpdateShellSettings(shellSettings);

            return(executionId);
        }