Magento: Disable module for any particular store

10,651

Solution 1

This configuration just disables module output in layout for frontend, but module controllers, event observers, admin pages, etc still working.

Also don't forget to specify your module name in layout files definition, otherwise all the layout file content will be loaded for a particular store:

<config>
   <layout>
       <module_alias module="Module_Name">
           <file>yourlayoutfile.xml</file>
       </module_alias>
   </layout>
</config>

If you are developing a module and want to disable full its functionality on the frontent for a particular store, then you should create a configuration field of "Yes/No" type and check its value via Mage::getStoreConfigFlag('config/field/path') in your module code.

Solution 2

To disable a module on the store scope, I've found it's possible to do it like this:

Move app/code/core/Mage/Core/Model/Config.php to app/code/local/Mage/Core/Model/Config.php

Inside Config.php find the method "loadModulesConfiguration" Don't change anything, but add the following code to make the method look like this.

public function loadModulesConfiguration($fileName, $mergeToObject = null, $mergeModel=null)
{
    $disableLocalModules = !$this->_canUseLocalModules();

    if ($mergeToObject === null) {
        $mergeToObject = clone $this->_prototype;
        $mergeToObject->loadString('<config/>');
    }
    if ($mergeModel === null) {
        $mergeModel = clone $this->_prototype;
    }
    $modules = $this->getNode('modules')->children();
    foreach ($modules as $modName=>$module) {
        if ($module->is('active')) {
            // Begin additional code
            if((bool)$module->restricted) {
                $restricted = explode(',', (string)$module->restricted);
                $runCode = (isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : 'default');
                if(in_array($runCode, $restricted)) {
                    continue;
                }
            }
            // End additional code
            if ($disableLocalModules && ('local' === (string)$module->codePool)) {
                continue;
            }
            if (!is_array($fileName)) {
                $fileName = array($fileName);
            }

            foreach ($fileName as $configFile) {
                $configFile = $this->getModuleDir('etc', $modName).DS.$configFile;
                if ($mergeModel->loadFile($configFile)) {
                    $mergeToObject->extend($mergeModel, true);
                }
            }
        }
    }
    return $mergeToObject;
}

The new code will cause the method to also check for a new node in the module xml file, <restricted>. If the node exists, the value would be a comma separated list of store codes that you do NOT want the module to load on. If you have multiple stores, the $_SERVER variable "MAGE_RUN_CODE" should be set with the current store code. If it's not set, the script will fallback to assuming the store code is "default" which is what it is by default unless for some bizarre reason you decide to change that in the backend.

A modules xml file could then look like this:

<?xml version="1.0"?>
<config>
    <modules>
        <MyPackage_MyModule>
            <active>false</active>
            <restricted>mystore1,mystore4,mystore5</restricted>
            <codePool>local</codePool>
        </MyPackage_MyModule>
    </modules>
</config>

With this, the module will not even load while on the stores with a store code of mystore1, mystore4, or mystore5. The <restricted> tag is entirely optional, if you omit it the module will load as it normally would.

Solution 3

I was using Eric solution for a while. In my case I disabled certain module responsible for Layered Navigation in one of my shops - thus returning to default Layered Navigation behaviour.

And it looked like its working, but after a while I've noticed that layered navigation options stopped to appear where they should. Soon I've noticed that in fact the module that should not work on this shop continued to work. Then I realized that when I disable configuration cache Eric's solution works, but after enabling it again it stops.

After a while I realized it had to work that way, with configuration cache enabled, because Eric's solution includes (or not) specified config files in global xml only while this xml is being generated. Then its cached and called from cache only. So when it was generated from site which should use some module it was included, and then used also on site which wasn't suppose to use it.

Anyway I worked out another solution, based on Eric's code (using restricted in modules config). I thought Magento should decide what to load when class is being requested. Then it could check what is current MAGE_RUN_CODE and use it dynamically.

There is a method in Mage_Core_Model_Config which is responsible for getting class name: getGroupedClassName.

Here is the code I used there:

if (strpos($className, 'Pneumatig_') !== false) {
    $var = substr($className, 0, strpos($className, '_', strpos($className, '_') + 1));
    if (isset($this->_xml->modules->$var)) { 
        if ((bool)$this->_xml->modules->$var->restricted === true) {
            $code = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : 'default';
            if (strpos((string)$this->_xml->modules->$var->restricted, $code) !== false) {
                $className = '';
            }
        }
    }
}

This Pneumatig condition is because all my modules start from Company name, so i wanted to avoid not necessary processing, but its optional, code should work without it, or you can change it to anything else.

Then I get actual module name [Company]_[Module], and then check if its enabled in _xml (which is current configuration object). If it is restricted I clear $className so it force Magento to load the default in next line.

And this code is added just before is empty condition:

    // Second - if entity is not rewritten then use class prefix to form class name
    if (empty($className)) {
        if (!empty($config)) {
            $className = $config->getClassName();
        }
        if (empty($className)) {
            $className = 'mage_'.$group.'_'.$groupType;
        }
        if (!empty($class)) {
            $className .= '_'.$class;
        }
        $className = uc_words($className);
    }

    $this->_classNameCache[$groupRootNode][$group][$class] = $className;
    return $className;

And for your convenience i paste whole getGroupedClassName code:

public function getGroupedClassName($groupType, $classId, $groupRootNode=null)
{
    if (empty($groupRootNode)) {
        $groupRootNode = 'global/'.$groupType.'s';
    }

    $classArr = explode('/', trim($classId));
    $group = $classArr[0];
    $class = !empty($classArr[1]) ? $classArr[1] : null;

    if (isset($this->_classNameCache[$groupRootNode][$group][$class])) {
        return $this->_classNameCache[$groupRootNode][$group][$class];
    }

    $config = $this->_xml->global->{$groupType.'s'}->{$group};

    // First - check maybe the entity class was rewritten
    $className = null;
    if (isset($config->rewrite->$class)) {
        $className = (string)$config->rewrite->$class;
    } else {
        /**
         * Backwards compatibility for pre-MMDB extensions.
         * In MMDB release resource nodes <..._mysql4> were renamed to <..._resource>. So <deprecatedNode> is left
         * to keep name of previously used nodes, that still may be used by non-updated extensions.
         */
        if (isset($config->deprecatedNode)) {
            $deprecatedNode = $config->deprecatedNode;
            $configOld = $this->_xml->global->{$groupType.'s'}->$deprecatedNode;
            if (isset($configOld->rewrite->$class)) {
                $className = (string) $configOld->rewrite->$class;
            }
        }
    }

    //START CHECKING IF CLASS MODULE IS ENABLED
    if (strpos($className, 'Pneumatig_') !== false) {
        $var = substr($className, 0, strpos($className, '_', strpos($className, '_') + 1));
        if (isset($this->_xml->modules->$var)) { 
            if ((bool)$this->_xml->modules->$var->restricted === true) {
                $code = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : 'default';
                if (strpos((string)$this->_xml->modules->$var->restricted, $code) !== false) {
                    $className = '';
                }
            }
        }
    }
    //END CHECKING IF CLASS MODULE IS ENABLED

    // Second - if entity is not rewritten then use class prefix to form class name
    if (empty($className)) {
        if (!empty($config)) {
            $className = $config->getClassName();
        }
        if (empty($className)) {
            $className = 'mage_'.$group.'_'.$groupType;
        }
        if (!empty($class)) {
            $className .= '_'.$class;
        }
        $className = uc_words($className);
    }

    $this->_classNameCache[$groupRootNode][$group][$class] = $className;
    return $className;
}

Solution 4

My clients install of Magento 1.8.1.0 has a problematic module that breaks another site's menu on a multi-store setup. The solution above posted by Eric Hainer didn't work for this install, so I altered it slightly:

Instead of using $_SERVER['MAGE_RUN_CODE'], I used $_SERVER['SERVER_NAME']. Worked like a charm. :)

So instead of:

$runCode = (isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : 'default');

use:

$runCode = (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'www.site1.com');

and instead of:

<restricted>mystore1,mystore4,mystore5</restricted>

use:

<restricted>www.site2.com,www.site3.com</restricted>

obviously changing "www.site1.com", "www.site2.com", and "www.site3.com" with your own locations.

Thanks for the idea Eric :)

Share:
10,651
Mukesh Chapagain
Author by

Mukesh Chapagain

Blog: PHP Magento jQuery Programming Tip &amp; Tutorials Twitter: chapagain Facebook: mukesh.chapagain LinkedIn: chapagain

Updated on June 15, 2022

Comments

  • Mukesh Chapagain
    Mukesh Chapagain about 2 years

    Suppose, I have 3 stores.

    I want to disable a module in Store 2. I only want it to be enabled in Store 1 and Store 3.

    I see that I can do it by:-

    • Going to System -> Configuration -> Advanced

    • Selecting desired store from Current Configuration Scope dropdown list.

    But this does not work fully.

    And, I also don't want to check store in the module code itself or create system configuration field for the module to check/uncheck store to enable/disable.

    What I am expecting is by adding some code in app/etc/modules/MyNamespace_MyModule.xml. Can we do it this way?

    • 00-BBB
      00-BBB over 4 years
      I run into this all the time... Why is it not easy?! 🤦‍♂️
  • Mukesh Chapagain
    Mukesh Chapagain about 13 years
    I know. I can create system configuration field for store enabling. But, I am seeking answer only for changing module config file in app/etc/modules directory.
  • Ivan Chepurnyi
    Ivan Chepurnyi about 13 years
    @chapagain it is not possible to change app/etc/modules per store, it is global configuration.
  • kleopatra
    kleopatra over 10 years
    Note that link-only answers are discouraged, SO answers should be the end-point of a search for a solution (vs. yet another stopover of references, which tend to get stale over time). Please consider adding a stand-alone synopsis here, keeping the link as a reference.
  • Nits
    Nits over 7 years
    This solution not working when cache is enable. Please let me know if you have any other solution to disable module and modules files like observer and all.
  • Deus777
    Deus777 over 7 years
    @Nits: please check my solution below. Its based on this one, but I solved this cache problem. Works with 1.9.2.3.