ShellContext IShellContextFactory.CreateSetupContext(ShellSettings settings) { if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("No shell settings available. Creating shell context for setup"); } var descriptor = new ShellDescriptor { SerialNumber = -1, Features = new[] { new ShellFeature { Name = "Orchard.Logging.Console" }, new ShellFeature { Name = "Orchard.Setup" }, }, }; var blueprint = _compositionStrategy.Compose(settings, descriptor); var provider = _shellContainerFactory.CreateContainer(settings, blueprint); return new ShellContext { Settings = settings, Blueprint = blueprint, ServiceProvider = provider }; }
public ShapePlacementParsingStrategy( IExtensionManager extensionManager, ShellDescriptor shellDescriptor, IPlacementFileParser placementFileParser) { _extensionManager = extensionManager; _shellDescriptor = shellDescriptor; _placementFileParser = placementFileParser; }
public void UpdateShellDescriptor(int priorSerialNumber, IEnumerable<ShellFeature> enabledFeatures, IEnumerable<ShellParameter> parameters) { var shellDescriptorRecord = GetShellDescriptor(); var serialNumber = shellDescriptorRecord == null ? 0 : shellDescriptorRecord.SerialNumber; if (priorSerialNumber != serialNumber) { throw new InvalidOperationException("Invalid serial number for shell descriptor"); } if (_logger.IsEnabled(LogLevel.Information)) { _logger.LogInformation("Updating shell descriptor for shell '{0}'...", _shellSettings.Name); } if (shellDescriptorRecord == null) { shellDescriptorRecord = new ShellDescriptor { SerialNumber = 1 }; _session.Save(shellDescriptorRecord); } else { shellDescriptorRecord.SerialNumber++; } shellDescriptorRecord.Features.Clear(); foreach (var feature in enabledFeatures) { shellDescriptorRecord.Features.Add(new ShellFeature { Name = feature.Name }); } if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Enabled features for shell '{0}' set: {1}.", _shellSettings.Name, string.Join(", ", enabledFeatures.Select(feature => feature.Name))); } shellDescriptorRecord.Parameters.Clear(); foreach (var parameter in parameters) { shellDescriptorRecord.Parameters.Add(new ShellParameter { Component = parameter.Component, Name = parameter.Name, Value = parameter.Value }); } if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Parameters for shell '{0}' set: {1}.", _shellSettings.Name, string.Join(", ", parameters.Select(parameter => parameter.Name + "-" + parameter.Value))); } if (_logger.IsEnabled(LogLevel.Information)) { _logger.LogInformation("Shell descriptor updated for shell '{0}'.", _shellSettings.Name); } //_eventNotifier.Notify<IShellDescriptorManagerEventHandler>( // e => e.Changed(GetShellDescriptorFromRecord(shellDescriptorRecord), _shellSettings.Name)); }
public string Setup(SetupContext context) { string executionId = Guid.NewGuid().ToString(); // The vanilla Orchard distibution has the following features enabled. string[] hardcoded = { // Framework "Orchard.Hosting", // Core "Settings", // Test Modules "Orchard.Demo", "Orchard.Test1" }; context.EnabledFeatures = hardcoded.Union(context.EnabledFeatures ?? Enumerable.Empty<string>()).Distinct().ToList(); var shellSettings = new ShellSettings(); shellSettings.Name = context.SiteName; //if (shellSettings.DataProviders.Any()) { // DataProvider provider = new DataProvider(); //shellSettings.DataProvider = context.DatabaseProvider; //shellSettings.DataConnectionString = context.DatabaseConnectionString; //shellSettings.DataTablePrefix = context.DatabaseTablePrefix; //} // TODO: Add Encryption Settings in var shellDescriptor = new ShellDescriptor { Features = context.EnabledFeatures.Select(name => new ShellFeature { Name = name }) }; // creating a standalone environment. // 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 // must mark state as Running - otherwise standalone enviro is created "for setup" shellSettings.State = TenantState.Running; // TODO: Remove and mirror Orchard Setup shellSettings.RequestUrlHost = _httpContextAccessor.HttpContext.Request.Host.Value; shellSettings.RequestUrlPrefix = string.Empty; //shellSettings.DataProvider = "InMemory"; _shellSettingsManager.SaveSettings(shellSettings); return executionId; }
public ShellContext CreateDescribedContext(ShellSettings settings, ShellDescriptor shellDescriptor) { if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Creating described context for tenant {0}", settings.Name); } var blueprint = _compositionStrategy.Compose(settings, shellDescriptor); var provider = _shellContainerFactory.CreateContainer(settings, blueprint); return new ShellContext { Settings = settings, Blueprint = blueprint, ServiceProvider = provider }; }
ShellContext IShellContextFactory.CreateSetupContext(ShellSettings settings) { if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("No shell settings available. Creating shell context for setup"); } var descriptor = new ShellDescriptor { SerialNumber = -1, Features = new[] { new ShellFeature { Name = "Orchard.Logging.Console" }, new ShellFeature { Name = "Orchard.Setup" }, }, }; return CreateDescribedContext(settings, descriptor); }
public ShapeTemplateBindingStrategy( IEnumerable<IShapeTemplateHarvester> harvesters, ShellDescriptor shellDescriptor, IExtensionManager extensionManager, IVirtualPathProvider virtualPathProvider, IEnumerable<IShapeTemplateViewEngine> shapeTemplateViewEngines, IOptions<MvcViewOptions> options, IActionContextAccessor actionContextAccessor, ILogger<DefaultShapeTableManager> logger) { _harvesters = harvesters; _shellDescriptor = shellDescriptor; _extensionManager = extensionManager; _virtualPathProvider = virtualPathProvider; _shapeTemplateViewEngines = shapeTemplateViewEngines; _viewEngine = options; _actionContextAccessor = actionContextAccessor; _logger = logger; }
ShellContext IShellContextFactory.CreateSetupContext(ShellSettings settings) { _logger.LogDebug("No shell settings available. Creating shell context for setup"); var descriptor = new ShellDescriptor { SerialNumber = -1, Features = new[] { new ShellFeature { Name = "Orchard.Logging.Console" }, new ShellFeature { Name = "Orchard.Setup" }, }, }; var blueprint = _compositionStrategy.Compose(settings, descriptor); var provider = _shellContainerFactory.CreateContainer(settings, blueprint); return new ShellContext { Settings = settings, Blueprint = blueprint, LifetimeScope = provider, Shell = provider.GetService<IOrchardShell>() }; }
async Task IShellDescriptorManagerEventHandler.Changed(ShellDescriptor descriptor, string tenant) { // deduce and apply state changes involved var shellState = await _stateManager.GetShellStateAsync(); foreach (var feature in descriptor.Features) { var featureName = feature.Name; var featureState = shellState.Features.SingleOrDefault(f => f.Name == featureName); if (featureState == null) { featureState = new ShellFeatureState { Name = featureName }; } if (!featureState.IsInstalled) { _stateManager.UpdateInstalledState(featureState, ShellFeatureState.State.Rising); } if (!featureState.IsEnabled) { _stateManager.UpdateEnabledState(featureState, ShellFeatureState.State.Rising); } } foreach (var featureState in shellState.Features) { var featureName = featureState.Name; if (descriptor.Features.Any(f => f.Name == featureName)) { continue; } if (!featureState.IsDisabled) { _stateManager.UpdateEnabledState(featureState, ShellFeatureState.State.Falling); } } FireApplyChangesIfNeeded(); }
public string SetupInternal(SetupContext context) { string executionId; _logger.LogInformation("Running setup for tenant '{0}'.", _shellSettings.Name); // The vanilla Orchard distibution has the following features enabled. string[] hardcoded = { // Framework "Orchard.Hosting", // Core "Settings", // Test Modules "Orchard.Demo", "Orchard.Test1" }; 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); shellSettings.DatabaseProvider = context.DatabaseProvider; shellSettings.ConnectionString = context.DatabaseConnectionString; shellSettings.TablePrefix = context.DatabaseTablePrefix; //if (shellSettings.DataProviders.Any()) { // DataProvider provider = new DataProvider(); //shellSettings.DataProvider = context.DatabaseProvider; //shellSettings.DataConnectionString = context.DatabaseConnectionString; //shellSettings.DataTablePrefix = context.DatabaseTablePrefix; //} // TODO: Add Encryption Settings in var shellDescriptor = new ShellDescriptor { Features = context.EnabledFeatures.Select(name => new ShellFeature { Name = name }).ToList() }; // creating a standalone environment. // 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 using (var environment = _orchardHost.CreateShellContext(shellSettings)) { executionId = CreateTenantData(context, environment); using (var store = (IStore)environment.ServiceProvider.GetService(typeof(IStore))) { store.InitializeAsync(); } } shellSettings.State = TenantState.Running; _orchardHost.UpdateShellSettings(shellSettings); return executionId; }
/// <summary> /// A feature is enabled/disabled, the tenant needs to be restarted /// </summary> Task IShellDescriptorManagerEventHandler.Changed(ShellDescriptor descriptor, string tenant) { if (_logger.IsEnabled(LogLevel.Information)) { _logger.LogInformation("A tenant needs to be restarted {0}", tenant); } if (_shellContexts == null) { return Task.CompletedTask; } ShellContext context; if (!_shellContexts.TryGetValue(tenant, out context)) { return Task.CompletedTask; } if (_shellContexts.TryRemove(tenant, out context)) { context.Dispose(); } return Task.CompletedTask; }
public static IEnumerable<FeatureDescriptor> EnabledFeatures(this IExtensionManager extensionManager, ShellDescriptor descriptor) { return EnabledFeatures(extensionManager, descriptor.Features); }
public static IEnumerable<FeatureDescriptor> DisabledFeatures(this IExtensionManager extensionManager, ShellDescriptor descriptor) { return extensionManager.AvailableFeatures().Where(fd => descriptor.Features.All(sf => sf.Name != fd.Id)); }
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 = { "Orchard.Hosting", // shortcut for built-in features "Orchard.Modules", "Orchard.Recipes" }; 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); 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(name => new ShellFeature { Name = name }).ToList() }; using (var shellContext = _shellContextFactory.CreateDescribedContext(shellSettings, shellDescriptor)) { using (var scope = shellContext.CreateServiceScope()) { var store = scope.ServiceProvider.GetRequiredService<IStore>(); try { await store.InitializeAsync(); } catch { // 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 rollbacked if one of the steps is invalid, // unless the recipe is executing? } // Create the "minimum shell descriptor" await scope .ServiceProvider .GetService<IShellDescriptorManager>() .UpdateShellDescriptorAsync(0, shellContext.Blueprint.Descriptor.Features, shellContext.Blueprint.Descriptor.Parameters); // Apply all migrations for the newly initialized tenant var dataMigrationManager = scope.ServiceProvider.GetService<IDataMigrationManager>(); await dataMigrationManager.UpdateAllFeaturesAsync(); var deferredTaskEngine = scope.ServiceProvider.GetService<IDeferredTaskEngine>(); if (deferredTaskEngine != null && deferredTaskEngine.HasPendingTasks) { var taskContext = new DeferredTaskContext(scope.ServiceProvider); await deferredTaskEngine.ExecuteTasksAsync(taskContext); } } _orchardHost.UpdateShellSettings(shellSettings); 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.CreateServiceScope()) { 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); //}); } } // Reloading the shell context as the recipe has probably updated its features using (var shellContext = _orchardHost.CreateShellContext(shellSettings)) { using (var scope = shellContext.CreateServiceScope()) { // Apply all migrations for the newly initialized tenant var dataMigrationManager = scope.ServiceProvider.GetService<IDataMigrationManager>(); await dataMigrationManager.UpdateAllFeaturesAsync(); bool hasErrors = false; Action<string, string> reportError = (key, message) => { hasErrors = true; context.Errors[key] = message; }; // Invoke modules to react to the setup event var eventBus = scope.ServiceProvider.GetService<IEventBus>(); await eventBus.NotifyAsync<ISetupEventHandler>(x => x.Setup( context.SiteName, context.AdminUsername, context.AdminEmail, context.AdminPassword, context.DatabaseProvider, context.DatabaseConnectionString, context.DatabaseTablePrefix, reportError )); if (hasErrors) { // TODO: check why the tables creation is not reverted var session = scope.ServiceProvider.GetService<YesSql.Core.Services.ISession>(); session.Cancel(); 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; }
private void FireApplyChangesIfNeeded() { _deferredTaskEngine.AddTask(async context => { var stateManager = context.ServiceProvider.GetRequiredService<IShellStateManager>(); var shellStateUpdater = context.ServiceProvider.GetRequiredService<IShellStateUpdater>(); var shellState = await stateManager.GetShellStateAsync(); while (shellState.Features.Any(FeatureIsChanging)) { var descriptor = new ShellDescriptor { Features = shellState.Features .Where(FeatureShouldBeLoadedForStateChangeNotifications) .Select(x => new ShellFeature { Name = x.Name }) .ToArray() }; if (Logger.IsEnabled(LogLevel.Information)) { Logger.LogInformation("Adding pending task 'ApplyChanges' for shell '{0}'", _settings.Name); } shellStateUpdater.ApplyChanges(); } }); }
/// <summary> /// A feature is enabled/disabled, the tenant needs to be restarted /// </summary> void IShellDescriptorManagerEventHandler.Changed(ShellDescriptor descriptor, string tenant) { if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Something changed! ARGH! for tenant {0}", tenant); } }