Custom block delete() not being called

Permalink 1 user found helpful
Hi all,

I have a custom block setup for a site I am working on. This custom block can have content tags attached to it, for a tag-cloud like functionality.

Now, when deleting a block what needs to happen is that any extra tag data related to this block needs to be deleted. This sounds like a perfect use of overriding the block controller's delete() function.

So, I put together this:

class myCustomBlockBlockController extends BlockController {
  /* snip */
  function delete() {
    Loader::model("Custom_Model");
    $t = new Custom_Model;
    $t->doSomething($this->bID);
    parent::delete();
  }
}


According to the documentation, my custom delete() code should be executed when the block is deleted - but it is not. I added in some tracing code to see if execution was ever reaching this point, but it was not.

I tried this as well. A custom delete function which does not call parent::delete():

function delete() {
  // do nothing
}


Seeing as this does not execute parent::delete(), the block should not ever be deleted properly. Yet, in this case, the block gets deleted fine.

Does anyone have any insight into why my yCustomBlockBlockController::delete() function is not being called as it is supposed to be?

Version: 5.4.0.5
Platform: xampp 1.7.3 running on Windows 7

 
Mnkras replied on at Permalink Reply
Mnkras
just so you know, 5.4.1 has the tag cloud functionality if you want to try it out,
jeneaux replied on at Permalink Reply
Yes, I am aware of that, thank you :-)

Unfortunately in this case the functionality I require isn't covered by what Concrete5 provides, and as such we have rolled our own content tagging system.
jordanlev replied on at Permalink Reply
jordanlev
That's really weird -- it should definitely be calling your delete method.
Perhaps explicitly making the method public would help?
public function delete() {
  //do stuff
}


If that doesn't work, can you post your block's controller.php file here so we can take a look?
jeneaux replied on at Permalink Reply
Thank you for your post. Unfortunately, explicitly defining this function as public has had no effect.

Since my original post I have done some more research into this issue. I found the code within Block::deleteBlock() that calls the custom delete() function, which is inserted below for reference (concrete/models/block.php:708-714)

$bt = BlockType::getByID($this->getBlockTypeID());
if( $bt && method_exists($bt,'getBlockTypeClass') ){
   $class = $bt->getBlockTypeClass();
   $bc = new $class($this);
   $bc->delete();
}


I copied this code into my own test script, and modified it as below, in an attempt to isolate the issue.

$bt = BlockType::getByID(38);
if( $bt && method_exists($bt,'getBlockTypeClass') ){
   $class = $bt->getBlockTypeClass();
   echo "class = $class" // this appears correctly, and matches expectations
   $bc = new $class($this);
   $bc->delete();
}


where "38" is the btID of my custom block. When run, this code executes as expected, passing execution through to $bc->delete(), custom delete handling code is then executed with no issues.

Below is my custom block's controller. Perhaps I have missed something important here?

It is also worth noting that I have also implemented a custom save() function, which has been working perfectly with no issues since implementation.

class CtaImageBlockController extends BlockController {
  protected $btDescription = "A CTA image block.";
  protected $btName = "(Type 17) CTA Image";
  protected $btTable = 'btCtaImage';
  protected $btInterfaceWidth = "550";
  protected $btInterfaceHeight = "500";
  function on_start() {
    $this->mb = Loader::helper('mylg_block');
  }
  function save($args) {
    // Handle checkbox values properly otherwise they cant be unchecked
    $args['share']        = (isset($args['share'])) ? 1 : 0;
    $args['display_icon'] = (isset($args['display_icon'])) ? 1 : 0;
    // Handle tags
    Loader::model("Content_Tags_Model");
jordanlev replied on at Permalink Reply
jordanlev
Yeah, I don't see anything out of the ordinary. You should try clearing your cache (Dashboard -> Sitewide Settings -> Debug). If that has no effect, I would uninstall and then reinstall the block and see if it works then.

I can't think of any reason this wouldn't be working other than it's storing an old version of your controller code in a cache somewhere. (I mean you're just extending a class -- this is basic php functionality that doesn't even have anything to do with concrete5 specifically).
jeneaux replied on at Permalink Reply
OK, I have now tried 1) emptying the cache and 2) turning the cache off completely. Neither of these options have rectified my issue.

RE: Uninstalling/reinstalling the block; this block is used many times on many different pages, and it would be quite a bit of work to uninstall and recreate them all. I will leave this option for later on.

I do have some more information on this strange behaviour. I have managed to get the block's custom delete function to execute by following the steps below:

1) Open a page for editing
2) Create a block on this page
3) Delete the block

In this case, delete() will execute. If I "Exit Edit Mode", or reload the page before deleting the block, delete() will not be executed.

It appears that the lines of code in question are /concrete/models/block.php:703-705;

$q = "select count(*) as total from CollectionVersionBlocks where bID = '$bID'";
$totalBlocks = $db->getOne($q);
if ($totalBlocks < 1) {
  /* snip */
  $bt = BlockType::getByID($this->getBlockTypeID());
  if( $bt && method_exists($bt,'getBlockTypeClass') ){
    $class = $bt->getBlockTypeClass();
    $bc = new $class($this);
    $bc->delete();
  }
  /* snip */
}


After a block has been saved, $totalBlocks gets assigned the value of 1, which means execution skips that whole code section.

I assume this is something to do with aliased blocks, and not overzealously deleting block content that may still be required - the inline comment states " we see whether or not this block is aliased to anything else". This tells me that Concrete5 is thinking that my block is aliased to something else, so is not doing a full delete.

Can anyone shed some light on how to fix this issue?
jordanlev replied on at Permalink Best Answer Reply
jordanlev
Aahh... yes I see -- there is definitely something weird going on there. After going through some of the core code, I think the problem is that it's hard to tell what exactly "delete" means -- because when you delete the block from the page, it's not actually getting deleted yet -- it doesn't officially happen until you APPROVE that edit. And even then, it still doesn't get deleted altogether, because there's a version history (you could roll back a version of the page to before the block was deleted and it would come back). So the actual delete() method isn't called unless you're deleting the block and there's no prior versions at all (which is why it works when you just add it to a page but immediately delete it -- because no prior versions exist at that point).

So... I'm not sure where that leaves you. Based on what this exact functionality that you're trying to achieve is, could you say whether it would be best to do your custom deletion code when the block is first removed from the page (before approval), or to wait until it's approved? And what if someone rolls back and the block re-appears -- what would you want to happen to this other data then?
If you could think about how you want this to work and what should happen under which circumstances, we might be able to figure out an alternative method for you to achieve your goal.

-Jordan
jeneaux replied on at Permalink Reply
Thanks Jordan - your explanation makes perfect sense. It looks like this strange behaviour was more caused by my incomplete understanding of Concrete5's blocks and versioning mechanics.

I also believe I may have found a solution; the $btIncludeAll optional block controller property, which is documented athttp://www.concrete5.org/documentation/developers/blocks/mvc-approa...

By the sounds of it, this would ensure that when adding a block, there will only ever be one existing version of it across all pages - therefore when deleting, the delete() override should be successfully called.

Thank you very much for your assistance in helping me reach this point, Jordan!
jordanlev replied on at Permalink Reply
jordanlev
You're welcome -- I learned a lot in this thread!

Glad you found a workable solution.