Multi-Tenancy Import across all tenants

Topics: Administration, Customizing Orchard, General
Sep 19, 2013 at 1:50 AM
Has anyone looked at running an import script across all tenants in one go?

i.e. I want to run an import script from the host site that will Import across all the tenants.

I haven't looked to far into this yet, although its a feature i need. Can someone advise how to get a handle to each tenant to process the import script?
Coordinator
Sep 19, 2013 at 1:58 AM
The command-line has a /Tenant flag for every command.
Sep 19, 2013 at 2:13 AM
I should have said.... I need to be able to do this through the orchard admin. Any ideas?
Coordinator
Sep 19, 2013 at 2:33 AM
I don't think that you can. Or it's going to be extremely difficult. Why not the command-line?
Sep 19, 2013 at 2:38 AM
The administrator who will run the import will not have access to the server.

What happened to web command line? I remember seeing this module although I can't seem to find it anymore.
Sep 19, 2013 at 3:29 AM
Would putting the command in a recipe with /Tenant switch and run the recipe from admin work?


Coordinator
Sep 19, 2013 at 4:38 AM
The web command-line was in the experimental module, that we stopped updating long ago. You should be able to find the source code and extract it from there. It might still work.

Using a recipe might work. Worth trying.
Sep 24, 2013 at 4:59 AM
Its actually quite simple, I've only done a few tests although it all seems to work fine.

Here is the guts of all that's required:
foreach (var tenant in model.Tenants.Where(t => t.IsChecked)) {
                    // Retrieve settings for speficified tenant.
                    var settingsList = _settingsManager.LoadSettings();
                    if (settingsList.Any()) {
                        var settings = settingsList.SingleOrDefault(s => String.Equals(s.Name, tenant.Name, StringComparison.OrdinalIgnoreCase));
                        if (settings == null) {
                            throw new OrchardCoreException(T("Tenant {0} does not exist", tenant));
                        }

                        var recipe = _recipeParser.ParseRecipe(recipeText);
                        using (var env = _orchardHost.CreateStandaloneEnvironment(settings)) {
                            var recipeManager = env.Resolve<IRecipeManager>();
                            var executionId = recipeManager.Execute(recipe);
                        }
                    }
                }
This is the full code minus the views
using Fsi.Update.ViewModels;
using Orchard;
using Orchard.Environment;
using Orchard.Environment.Configuration;
using Orchard.Environment.Descriptor;
using Orchard.Localization;
using Orchard.MultiTenancy.Services;
using Orchard.Recipes.Services;
using Orchard.UI.Admin;
using Orchard.UI.Notify;
using System;
using System.IO;
using System.Linq;
using System.Web.Mvc;

namespace Fsi.Update.Controllers {

    [ValidateInput(false), Admin]
    public class AdminController : Controller {
        private readonly IOrchardServices _services;
        private readonly ITenantService _tenantService;
        private readonly ShellSettings _shellSettings;
        private readonly IOrchardHost _orchardHost;
        private readonly IShellSettingsManager _settingsManager;
        private readonly IShellDescriptorManager _shellDescriptorManager;
        private readonly IRecipeParser _recipeParser;
        private readonly IRecipeJournal _recipeJournal;

        public AdminController(IOrchardServices services,
                               ITenantService tenantService,
                               ShellSettings shellSettings,
                               IOrchardHost orchardHost,
                               IShellSettingsManager settingsManager,
                               IShellDescriptorManager shellDescriptorManager,
                               IRecipeParser recipeParser,
                               IRecipeJournal recipeJournal) {
            _services = services;
            _tenantService = tenantService;
            _shellSettings = shellSettings;
            _orchardHost = orchardHost;
            _settingsManager = settingsManager;
            _shellDescriptorManager = shellDescriptorManager;
            _recipeParser = recipeParser;
            _recipeJournal = recipeJournal;

            T = NullLocalizer.Instance;
        }

        public Localizer T { get; set; }

        public ActionResult Import() {
            var viewModel = new AdminImportViewModel {Tenants = _tenantService.GetTenants().Where(t => t.Name != _shellSettings.Name).Select(t => new TenantEntry {Name = t.Name}).ToList()};

            return View(viewModel);
        }

        [HttpPost, ActionName("Import")]
        public ActionResult ImportPOST(AdminImportViewModel model) {
            if (!_services.Authorizer.Authorize(Permissions.Import, T("Not allowed to import.")))
                return new HttpUnauthorizedResult();

            if (String.IsNullOrEmpty(Request.Files["RecipeFile"].FileName)) {
                ModelState.AddModelError("RecipeFile", T("Please choose a recipe file to import.").Text);
                _services.Notifier.Error(T("Please choose a recipe file to import."));
            }

            if (ModelState.IsValid) {
                var recipeText = new StreamReader(Request.Files["RecipeFile"].InputStream).ReadToEnd();

                foreach (var tenant in model.Tenants.Where(t => t.IsChecked)) {
                    // Retrieve settings for speficified tenant.
                    var settingsList = _settingsManager.LoadSettings();
                    if (settingsList.Any()) {
                        var settings = settingsList.SingleOrDefault(s => String.Equals(s.Name, tenant.Name, StringComparison.OrdinalIgnoreCase));
                        if (settings == null) {
                            throw new OrchardCoreException(T("Tenant {0} does not exist", tenant));
                        }

                        var recipe = _recipeParser.ParseRecipe(recipeText);
                        using (var env = _orchardHost.CreateStandaloneEnvironment(settings)) {
                            var recipeManager = env.Resolve<IRecipeManager>();
                            var executionId = recipeManager.Execute(recipe);
                        }
                    }
                }

                UpdateShell();

                //var executionId = _importExportService.Import(fileText);
                _services.Notifier.Information(T("Your recipe has been imported."));

                //return RedirectToAction("ImportResult", new { ExecutionId = executionId });
            }
            return View(model);
        }

        private void UpdateShell() {
            var descriptor = _shellDescriptorManager.GetShellDescriptor();
            _shellDescriptorManager.UpdateShellDescriptor(descriptor.SerialNumber, descriptor.Features, descriptor.Parameters);
        }

        //public ActionResult ImportResult(string executionId)
        //{
        //    var journal = _recipeJournal.GetRecipeJournal(executionId);
        //    return View(journal);
        //}
    }
}