Overriding settings (baseurl / sitename) in certain occasions

Topics: Core, Writing modules
Jul 1, 2013 at 3:13 PM

We're having some trouble overriding the sitename and baseurl.

We created a single tenant site, but we can access it through different URLs. So for example:
http://localhost/ http://tenant/ http://orchard/ They all go to the same Orchard tenant. Now we want to load different site titles according to the domains used.

With the rules engine we can solve part of this; I created a domain(string) RuleProvider. This allows me to create a rule that will match when the domain is for example "tenant".

Now, I need to override the site title and base url when this is the case. Otherwise, of course, the baseurl will be http://localhost instead of http://tenant.

I actually want to use multi tenancy for this, as this covers quite some issues. But the entire site content is 1:1 the same (except for a few small differences). So it's better to not use multi tenancy as it's not designed for this.
We looked in the forums and everybody says to not use tenants in this case. So we're fine with that.

We just want to know how to change some settings (quite as how the tenants do it).

I tried createing a ISiteService and override parts of the ISite (SiteSettingsPart) that is returned on the GetSiteSettings()-call. But it seems these modifications (only overriding the .sitename and .baseurl) persist. So next time the method is called, my changes are already there.
Jul 1, 2013 at 3:25 PM
So far I have this:
namespace X.ThemeSelector.Services
    using Orchard.Caching;
    using Orchard.ContentManagement;
    using Orchard.Core.Settings.Models;
    using Orchard.Core.Settings.Services;
    using Orchard.Data;
    using Orchard.Settings;

    public class XSiteService : ISiteService
        private readonly IRuleManager _ruleManager;
        private readonly SiteService _siteService;
        private readonly IContentManager _contentManager;

        public ObecSiteService(IOrchardServices orchardServices, IRuleManager ruleManager, IRepository<SiteSettingsPartRecord> siteSettingsRepository, IContentManager contentManager, ICacheManager cacheManager)
            _contentManager = contentManager;
            _ruleManager = ruleManager;
            _siteService = new SiteService(siteSettingsRepository, contentManager, cacheManager);
            Logger = NullLogger.Instance;
            T = NullLocalizer.Instance;

        public ILogger Logger { get; set; }
        public Localizer T { get; private set; }

        public ISite GetSiteSettings()
            var settings = _siteService.GetSiteSettings();

            //var overrides = _contentManager.Query<SiteSettingOverridePart, SiteSettingOverridePartRecord>().List().ToArray();
            //if (overrides.Length == 0)
            var tenantPart = new SiteSettingOverridePart();
            tenantPart.Record = new SiteSettingOverridePartRecord();
            tenantPart.Id = 1;
            tenantPart.Name = "Tenant Override";
            tenantPart.Rule = "domain(\"http://tenant:30320\")";
            tenantPart.SiteName = "Tenant";
            tenantPart.BaseUrl = "http://tenant:30320/";
            tenantPart.Description = "Tenant description";

            var kvhPart = new SiteSettingOverridePart();
            kvhPart.Record = new SiteSettingOverridePartRecord();
            kvhPart.Id = 2;
            kvhPart.Name = "KVH Override";
            kvhPart.Rule = "domain(\"http://kvh:30320\")";
            kvhPart.SiteName = "KVH";
            kvhPart.BaseUrl = "http://kvh:30320/";
            kvhPart.Description = "KVH description";

            var overrides = new SiteSettingOverridePart[] { tenantPart, kvhPart };

            foreach (var ovrride in overrides)
                // ignore the rule if it fails to execute
                    if (_ruleManager.Matches(ovrride.Record.Rule))
                        // Override
                        ((SiteSettingsPart)settings).SiteName = ovrride.SiteName;
                        ((SiteSettingsPart)settings).BaseUrl = ovrride.BaseUrl;
                catch (Exception e)
                    Logger.Warning(e, T("An error occured during site setting override evaluation on: {0}", ovrride.Name).Text);

            return settings;
Jul 1, 2013 at 3:27 PM
This actually works. Well, sometimes. I can refresh like 3x, then it dies with numerous NullReferenceExceptions.

And actually, it only works on the "tenants" I override; the main tenant (http://localhost) always gets the settings of the last overridden tenant.

I feel I'm in the wrong part of Orchard, trying to add this "feature". Can anyone hint me into the right direction?
Jul 1, 2013 at 8:52 PM
Out of curiosity, why do you want several sites that only differ by title? Sounds spammy.
Jul 2, 2013 at 8:23 AM
Maybe I have to give a bit more information.

We have one website. It has a few features such as a forum, a chat, a mail component, etc. Users can login and do a few things.

Now we have this "weird" requirement: Customers should be able to buy this website. AND get permissions and rights to customize certain parts of it.
This means we have to provide the website 1 + (number of customers) times. This also means we cannot use multi tenancy. Multi tenancy was designed to create a physical separation between the websites, to let them be full Orchard CMS's next to each other. So that automatically means we cannot reuse any content item or custom type in another tenant.

Now we have set it up like this:
In IIS create a site, host Orchard on it. Create binding for blah.nl. Then also create bindings for tenant.blah.nl, kvh.blah.nl. Later, add more customers to this.
  • When they hit the site, they all get the same Orchard instance.
  • They can use their own theme. This is easily possible thanks to IThemeSelector. We have already tackled this part, by looking at the full request domain name. If it's "tenant.blah.nl", we use theme "TenantTheme", if it's "kvh.blah.nl", we use theme "KvhTheme", etc.
  • They can add stuff to the menu. We could not figure out how to do this. We now created a new Widget Layer, added a rule that included domain("tenant.blah.nl") (we created a DomainRuleProvider for that), so that each website has their own menu and that menu is only displayed if you're actually on that website.
Then we're stuck. We know the menu-part isn't nice, because you now physically have 2 menus. A "tenant" can't say "I don't want this menu item", then we have to remove it from the main menu and add it back on every tenant's menu.

Also, we have a few more tasks:
  • Override the title per "tenant" (and also the base url). Those 2 properties are, I think, the most important. I thought we could do this by using the ISite-stuff. I think I'm trying to use the wrong tool for the "right" job.
  • Have a permissions structure based on the domain-part. Or actually, when users are logged in, we should somehow flag them "tenant" or "kvh" (or any other customer). This also probably means we have to flag the content items too. As we somehow have to check wether or not they have rights for it.
  • Have more than 1 homepage; Each "tenant" should be able to have their own homepage. Right now, in Orchard, you can only have 1 homepage. Somehow we should be able to have a custom homepage per tenant.
  • Let users have permissions to only add items to their own menu. I think we can achieve this easily with the Content Permissions module. Once we have figured out the other tasks, this should be easy to configure without any (or much) coding.
Jul 2, 2013 at 11:34 AM
Hm I looked a bit more. Looks like we should go with IRunningShellTable. How to override it though, I'm not sure.
Looks like we may need 50+% of the multi tenancy code and customize some parts.

Was hoping anyone could shed some light on this, as to whether we're on the right path or not.