Jump to content

Abort module Uninstall on condition


Recommended Posts

Hi all,

I have a module which creates numerous fields, pages etc. I would like to disable the ability to uninstall the module if any of the generated pages have children, as a mistaken uninstall could really mess up any website which is using it.

Im curious about what is the best way to achieve this, currently I have some like:

 

    public function ___uninstall(){

        error_log('---- UNINSTALL!! ----');

        //check to see if any config pages have been added and abort uninstall if true
        //to avoid uninstalling and losing any work which has been done up to this point.

        if($this->canWeUninstall()) {
            //remove pages
            $this->deletePages($this->cPages->getPages());

            //remove templates and field groups
            $this->deleteTemplates($this->cTemplates->getTemplates());

            //remove repeaters
            $this->deleteRepeaters($this->cRepeaters->getRepeaters());

            //delete fields
            $this->deleteFields($this->cFields->getFields(), $this->cRepeaters->getRepeaters());
        } else {
            error_log('Uninstall aborted because config settings exist!');
            $this->error('Whoops! Module cant be uninstalled as it appears to be in use.');
            return;
        }
    }

This works in the sense that it doesn't remove any of the generated content, which is good as it wont break the website. However it still shows the module as uninstalled,  I can achieve what i'm looking for by replacing 'return' with 'die' but that results in a white page and looks rather rubbish so its not really a great solution.

I would imagine there must be a better way to achieve this, any thoughts?

Would it be possible to do this check via a hook before uninstallation and abort before we get to the point of running the uninstall() method, as I imagine that is what updates the DB to turn the module off so to speak? 

Link to comment
Share on other sites

I think you just need to make sure you don't return from ___uninstall. Throwing an exception should be sufficient, though I haven't tested it.

if(! $this->canWeUninstall()) {
	throw new WireException("Whoops! Module cant be uninstalled as it appears to be in use.");
}

 

  • Like 2
Link to comment
Share on other sites

13 hours ago, BitPoet said:

I think you just need to make sure you don't return from ___uninstall. Throwing an exception should be sufficient, though I haven't tested it.


if(! $this->canWeUninstall()) {
	throw new WireException("Whoops! Module cant be uninstalled as it appears to be in use.");
}

 

That works a treat, thanks man!

  • Like 1
Link to comment
Share on other sites

  • 2 years later...

This is what I came up with today: I have a main module that installs 3 other modules (via module info "installs" => ["mod1", "mod2", "mod3"]). Uninstall was a bit of a problem, because it tried to uninstall a fieldtype module and this was not possible when I had an existing field of that type. Throwing an exception did also not work, because all submodules where uninstalled even if the main module threw an exception. I then tried to throw an exception in all of the submodules as well, but that was a mess and did not work reliably.

Hooks to the rescue:

  public function init() {
    $this->addHookBefore("Modules::uninstall", $this, "customUninstall");
  }


  /**
   * Custom uninstall routine
   * 
   * @param HookEvent $event
   */
  public function customUninstall($event) {
    $class = $event->arguments(0);
    if($class != 'yourmodulename') return;

    // set flag
    $abort = false;
    
    // check1
    if($this->check1() == false) {
      $this->error('check1 failed');
      $abort = true;
    }

    // check2
    if($this->check2() == false) {
      $this->error('check2 failed');
      $abort = true;
    }

    // uninstall?
    if($abort) {
      // there where some errors
      $event->replace = true; // prevents original uninstall
      $this->session->redirect("./edit?name=$class"); // prevent "module uninstalled" message
    }
  }

Real life example that also checks if the uninstalled module is the main module or a sub-module. If it is a sub-module it shows an error and redirects to the config screen of the main module:

GUS739t.png

otoP2Mi.png

  public function init() {
    $this->addHookBefore("Modules::uninstall", $this, "customUninstall");
  }


  /**
   * Custom uninstall routine
   * 
   * @param HookEvent $event
   */
  public function customUninstall($event) {
    $installs = $this->getModuleInfo();
    $class = $event->arguments(0);
    $url = "./edit?name=$class";

    // exit when class does not match
    if(!in_array($class, $installs)) return;
    
    // intercept uninstall
    $abort = false;

    // if it is not the main module we redirect to the main module's config
    if($class != 'RockMarkup') {
      $abort = true;
      $url = "./edit?name=RockMarkup";
      $this->error('Please uninstall the main module');
    }
    
    // check if any fields exist
    $fields = $this->wire->fields->find('type=FieldtypeRockMarkup')->count();
    if($fields > 0) {
      $this->error('Remove all fields of type RockMarkup before uninstall!');
      $abort = true;
    }

    // on uninstall of the main module we remove this hook so that it does
    // not interfere with the auto-uninstall submodules
    if($class == 'RockMarkup') $event->removeHook(null);

    // uninstall?
    if($abort) {
      // there where some errors
      $event->replace = true; // prevents original uninstall
      $this->session->redirect($url); // prevent "module uninstalled" message
    }
  }

 

  • Like 1
Link to comment
Share on other sites

  • 7 months later...
On 7/12/2019 at 5:37 PM, bernhard said:

Real life example that also checks if the uninstalled module is the main module or a sub-module. If it is a sub-module it shows an error and redirects to the config screen of the main module:

Hi @bernhard, I have the same situation and just stumbled across your post here! Great solution!

Where do I setup this hook? In main module or the submodule?

Link to comment
Share on other sites

11 minutes ago, bernhard said:

I see you placed the hook into the main modules init method. But how is this hook triggered when someone tries to uninstall the submodule?

Link to comment
Share on other sites

It's triggered by all uninstalls of modules that are based on the main module (like RockTabulator is based on RockMarkup2). And those modules extend the main module and therefore the init method is called on every load of the submodule.

Link to comment
Share on other sites

6 minutes ago, bernhard said:

It's triggered by all uninstalls of modules that are based on the main module (like RockTabulator is based on RockMarkup2). And those modules extend the main module and therefore the init method is called on every load of the submodule.

Hmm.. my FieldType module (and the two other submodules) isn't based on the main module so it seems I need to place the hook into the FieldType module?

Link to comment
Share on other sites

I'm developing a module which comes with 3 other integrated (and required) modules. On of them is a custom selector FieldType which gets its data from a helper class within the main module. The problem is if one wants to uninstall the main module but wants to preserve the pages which uses the custom selector FieldType, the uninstallation process is interrupted as this FieldType is still in use.

I first thought to simply let the FieldType installed (standalone), but it has dependencies in a helper class which won't work when main module is uninstalled.

Link to comment
Share on other sites

Yeah, I guess PW's module dependency features are too limited for such cases. I think you'd need to remove the dependencies in the module's info array and implement them on your own somewhere in the modules' install() and uninstall() methods.

  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...