If you pass an invalid delta in the URL for a block’s configuration form, the form is still displayed. When the user submits the form, the entries are stored in the block table which can cause some unwanted issues.
To replicate, as a user who can administer blocks enter “admin/structure/block/manage/menu/no-delta/configure” in the URL. The block doesn’t exist, but you still have the ability to edit and submit the form. After you submit the form, an entry gets added to the block table (see entries-in-table.png).
When you visit another page, the menu module will throw an undefined index error since the table contains a block that the menu module doesn’t know about (see undefined-index.png).
Ideally the form should throw a 404 if the block is not valid, which would prevent this from happening. This issue was discovered through #1150662: Facet blocks not remembering overridden settings raised against Facet API.
Symptoms
From #866972: Fatal error when attempting to edit non-existent block, which is now closed as duplicate of this one:
If a wrong Block URL is called (for example http://localhost/d7test/admin/structure/block/manage/block/1000/configure) then a fatal PHP Error appear on Line 519 in the Block Module.
Fatal error: Unsupported operand types in /var/www/html/foobar/modules/block/block.module on line 519
Or you may see this message:
Error: Unsupported operand types in block_custom_block_form() (line 520 of [..]/modules/block/block.module).
| Comment | File | Size | Author |
|---|---|---|---|
| #31 | D7-1154742-31-block_admin_configure.patch | 3.07 KB | donquixote |
| #16 | drupal-1154742-invalid-block-delta-fix-16.patch | 4.39 KB | mark.labrecque |
| #3 | block-1154742-3.patch | 4.06 KB | cpliakas |
| #1 | block-1154742-1.patch | 4.06 KB | cpliakas |
| undefined-index.png | 122.2 KB | cpliakas |
Comments
Comment #1
cpliakas commentedThe attached patch issues a 404 if the block is not valid. The patch adds a block_object_load() function which is used in the block configuration menu item. block_load() cannot be used since it returns an initialized object if the block doesn’t exist. Modifying block_load() to return FALSE is too much of an API change that would probably introduce new bugs.
Comment #2
cpliakas commentedMarking as "needs review".
Comment #3
cpliakas commentedFixed typo in docblock of the test method.
Comment #4
davidhernandezI can replicate the original issue, but the patch seems to not create a 404. I'm running WAMP, with latest 7.x install, clean install. Once applied, the form still appears but I get this error message on the form:
Notice: Trying to get property of non-object in block_admin_configure() (line 257 of C:\wamp\www\drupal\modules\block\block.admin.inc).
Notice: Trying to get property of non-object in block_admin_configure() (line 261 of C:\wamp\www\drupal\modules\block\block.admin.inc).
Notice: Trying to get property of non-object in block_admin_configure() (line 265 of C:\wamp\www\drupal\modules\block\block.admin.inc).
Notice: Trying to get property of non-object in block_admin_configure() (line 266 of C:\wamp\www\drupal\modules\block\block.admin.inc).
Notice: Trying to get property of non-object in block_admin_configure() (line 274 of C:\wamp\www\drupal\modules\block\block.admin.inc).
Notice: Trying to get property of non-object in block_admin_configure() (line 280 of C:\wamp\www\drupal\modules\block\block.admin.inc).
Notice: Trying to get property of non-object in block_admin_configure() (line 280 of C:\wamp\www\drupal\modules\block\block.admin.inc).
Notice: Trying to get property of non-object in block_admin_configure() (line 301 of C:\wamp\www\drupal\modules\block\block.admin.inc).
Notice: Trying to get property of non-object in block_admin_configure() (line 302 of C:\wamp\www\drupal\modules\block\block.admin.inc).
Notice: Trying to get property of non-object in block_admin_configure() (line 301 of C:\wamp\www\drupal\modules\block\block.admin.inc).
Notice: Trying to get property of non-object in block_admin_configure() (line 302 of C:\wamp\www\drupal\modules\block\block.admin.inc).
Notice: Trying to get property of non-object in block_admin_configure() (line 390 of C:\wamp\www\drupal\modules\block\block.admin.inc).
Notice: Trying to get property of non-object in block_admin_configure() (line 391 of C:\wamp\www\drupal\modules\block\block.admin.inc).
The form is still editable, and when submitted results in this:
PDOException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'delta' cannot be null: INSERT INTO {block} (theme, delta, module, region, pages, status) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5); Array ( [:db_insert_placeholder_0] => bartik [:db_insert_placeholder_1] => [:db_insert_placeholder_2] => [:db_insert_placeholder_3] => [:db_insert_placeholder_4] => [:db_insert_placeholder_5] => 0 ) in block_admin_configure_submit() (line 502 of C:\wamp\www\drupal\modules\block\block.admin.inc).
Comment #5
cpliakas commenteddavidhernandez,
Did you clear cache after applying the patch? The patch adds menu items which will not get picked up unless cache is cleared.
Thanks,
Chris
Comment #6
davidhernandezYou are correct. I neglected to do so. After rechecking/patching I do get the 404 for all instances of an invalid delta I tried. My mistake.
Comment #7
cpliakas commentedNot a problem. Thanks for posting back!
Comment #8
dave reidDoes this callback also need 'load arguments' => array(5)?
Powered by Dreditor.
Comment #9
David_Rothstein commentedIs this the same issue as #866972: Fatal error when attempting to edit non-existent block? (Only difference seems to be in which module is used in the URL, "block" vs "menu" - they lead to very different symptoms.)
Comment #10
dcam commented@David_Rothstein
Yeah, the problem is the same. They are both a result of the fact that the menu system isn't calling the load function for the block. In fact nothing is checking to see whether or not the block really exists. The only reason we get an error for custom blocks is because block_custom_block_form() tries to add some defaults to the $edit array, but with a non-existent block an operation exception is thrown. If it didn't try to add those defaults, it would probably pop up with its form just like you see with non-existent Menu blocks. Something different might happen for every module you try.
I closed the other issue as a duplicate.
#3 needs a reroll.
Comment #11
dcam commentedTo answer #8, the change to the callback in #3 does include
'load arguments' => array(5).Comment #12
mark.labrecqueComment #13
mark.labrecqueThis patch is far too out date for a reroll, but I can look into how this is working now and try to create a new patch.
Comment #14
mark.labrecqueComment #15
mark.labrecqueI actually made a mistake here. I thought this issue was against 8.0.x. I will re-evaluate this based on 7.x
Comment #16
mark.labrecqueLet's try this one.
Comment #17
mark.labrecqueComment #18
epophoto commentedAfter recreating the issue, I installed The patch in #16 and it gave me the 404 as expected.
Comment #19
travis-bradbury commentedRTBC++
Comment #20
mark.labrecqueComment #21
scotwith1tI'm getting a 403 (Forbidden) instead of a 404. Any reason that would be? This at least alleviates #866972 which originally led me here, so that's a big plus. This works for me, just wanted to clarify my experience was a different end result than a 404 as others have reported.
Comment #22
scotwith1tIt's also giving me a 403 on blocks that ARE valid as well...any ideas? Should it matter that I ran the patch against 7.38 (it applied cleanly)?
Comment #23
David_Rothstein commentedThis patch has a couple API changes (i.e. to the function signature of the block_admin_configure form, and to the URL patterns in hook_menu()) and I'm not sure why we need any of them?
Couldn't we just change the existing menu item to use a custom page callback instead (rather than "drupal_get_form") and then in the page callback return MENU_NOT_FOUND if the block doesn't exist, or return
drupal_get_form('block_admin_configure', ....)if it does?Comment #24
donquixote commentedAdding relevant error messages to issue summary, so people can find this issue.
Comment #25
donquixote commentedThe other issue, #866972: Fatal error when attempting to edit non-existent block, which is now closed as duplicate, has a much simpler solution.
It simply checks, in the form, whether the block exists, and calls
drupal_not_found()+drupal_exit()if not.I propose we either do that, or we let the form display a "Block does not exist" text, without even bothering with a 404 header.
@David_Rothstein (#23)
That would be another option, yes.
For my taste, let's aim for a minimum diff solution.
If we avoid signature changes, it means the form callback will keep as parameters $module, $delta, and have to call block_load().
Any function which does this should not rely on the block existing, but should have its own fallback logic.
Comment #26
donquixote commentedActually, if
$delta === NULL, or if the block for the given delta does not exist, thenblock_load()returns a stub block object, which is used for the form to add new blocks.On the other hand,
block_custom_block_get($delta)returns FALSE if the delta does not exist.I think we have to implement fallback behavior in all places where a delta can be NULL, 0, '' or it points to a non-existing block.
E.g. even if we implement fallback behavior in block_admin_configure(), the function block_block_configure() should not rely on that.
Comment #27
donquixote commentedWith all the different ways that these form builder callbacks are called, I think it is safer to show a form element saying "Block does not exist", than calling drupal_not_found() + drupal_exit().
How could such a form / element look like?
Comment #28
donquixote commentedMore problems.
block_load() attempts to load information about the block from the
blockdatabase table.But this table contains one entry per theme, for each block.
Or in some cases it might not contain any entry, even if the block does exist via hook_block_info(), because it was not written to the database yet.
In
block_admin_configure_submit(), the new values for title, pages etc are stored for all entries inblocktable with the given module and delta.This means we should assume that most of the columns in
blocktable are synced between themes. Only theregionandweightare not synced.The reliable way to determine if a block exists or not is this:
Currently, this is being called from block_admin_configure(), but the result is only used to set the page title.
If the block does not exist, the form is still shown.
I think this is the place where we need to add the fallback logic.
Comment #29
donquixote commentedIf we return a crippled form, this will confuse implementations of hook_form_alter() which attempt to alter this form.
The proposal by @David_Rothstein in #23 would prevent this problem.
The drupal_not_found() + drupal_exit() would also prevent this problem, however I am not sure if it has any undesired side effects.
Comment #30
donquixote commented@mark.labrecque (#16)
Regarding the patch in #16.
Aside of what @David_Rothstein in already said in #23 about API changes.
This is the wrong approach. Looking into the database is not the reliable way to check if a module + delta is valid or not.
Instead, we should call
module_invoke($module, 'block_info'), and check if the delta exists (unless the delta is NULL for creating a new block).Comment #31
donquixote commentedI made a branch / PR on github which also contains code style changes.
https://github.com/donquixote/drupal/compare/7.x...7.x-1154742-block-edit
https://github.com/donquixote/drupal/compare/7.x...1154742-31-PR
(the tag will remain the same, the branch might change in the future)
I am going to upload a patch here, but this will only contain the first of those commits.
https://github.com/donquixote/drupal/commit/4e224f3528cd8327897da9e63e70...
The patch does not contain the change to block_block_configure
https://github.com/donquixote/drupal/commit/72b790c26895d7d15d90cea46e19...
(or any of the code style changes)
I do not post an interdiff to #16, because this is a completely new approach.
The combination drupal_set_message() + drupal_not_found() + drupal_exit() works nicely, it seems.
I added an additional
exit;just to make the IDE happy.Static code validation is important!
The code looks a bit complex, but this is simply because it takes care of various cases that were previously not reliably taken care of.