Hi,
I'm using this module code as a reference to code my own domain submodule for other module. And I'm experimenting something very strange on save. When I change the domains enable from one to another the database finish with one row, 'domain_site 0', and one row 'domain_id id'. I suppose the domain_site should not be present if only one domian is checked.
Maybe I did something wrong on the adaptation of code to my case, I would have to review, but... before doing that I have this idea on my mind, Is really necesary to do so many things on save?
How I see the save function, maybe I'm wrong, is as follows (I would explain with domain blocks as example):
- The save function parameters. Two exclusively identify the block, $module, $delta. The third one contains the domains checked on the form domain visibility settings for that block.
- The logic needed. If no domains are checked, database should contain realm:domain_site domain_id:0 for block $module, $delta. If some domains are checked, database should contain instead one row for each domain, realm: domain_id domain_id: id for block $module, $delta.
The logic you are using is first look at what was previously on database and delete what is not more necesary, add what is needed new...
Wouldn't it be simpler?:
- Delete all rows $module, $delta.
- If domains checked ($domain_arr) is empty, insert a $module, $delta, 'domain_site', 0 row.
- If domains checked is not empty add one row for each domain, $module, $delta, 'domain_id', domain_id.
If I look at the logic you are using carefully I think the bug is there. Look what you do when there are previously domains on database and $domains_arr is not empty:
- First you remove domain_site if $domains_arr is not empty. OK
- Then if there were previously domains on db, you look which ones of $domains_arr were just there to do nothing with them. OK
- Then those previously on db but not on $domain_arr are remove. OK
- Then if there is any one new to include still present on $domain_arr you include then. OK
- Finally if $domain_count_saved == 0 you add the domain_site grant all. WRONG
Why that is wrong, because $domain_count_saved is 0 when all previously domain in db are removed, but that is not a sufficient condition to add a domain_site grant, if $domain_arr has added domains then you shouldnt add the domain_site grant. So, if you want to do all that logic, you should change that final condition to: $domain_count_saved == 0 && empty($domain_arr).
But as I wrote before, In my opinion all that logic is really confusing and you could do it easier just with the three steps I mentioned.
Maybe I'm missing something but I think there is no more to do than that.
| Comment | File | Size | Author |
|---|---|---|---|
| #22 | domain_blocks-704066-22.patch | 1.6 KB | manfer |
| #18 | domain_blocks-704066-DRUPAL-6--1.patch | 13.67 KB | manfer |
| #16 | 704066-domain_blocks-DRUPAL-6--1.patch | 13.89 KB | nonsie |
| #15 | domain_blocks-704066.patch | 10.55 KB | manfer |
Comments
Comment #1
manfer commentedAs I'm not sure I prefer that you consider if you should move this to a bug report. I think the bug is there.
Comment #2
manfer commentedHere is the simple logic I mean:
And the helper function _domain_blocks_with_realm_load would not be needed at all.
Those would be the two changes.
Comment #3
manfer commentedAnd if you think, but that way when the domain visibility settings are not changed I'm deleting the db values and writing them again. Just do the check on form submit:
You carry previous values on form:
And check if there were changes on submit:
And just to avoid that case and save some queries. As these queries only take place on administration changes that are not so often, I think are not something to worry about. Is not like they were queries on site display to visitors.
Comment #4
nonsieMy personal preference is to avoid going to the database for queries if it is not absolutely necessary. True, your approach works fine for sites with limited number of blocks and domains. Now imagine you have 100+ domains on your site.
About the $domain_count_saved == 0 bit - can you provide an example with domain grants before and after where it does not work?
Comment #5
manfer commentedJust configure a domain visibility for a block, assigning it to only one domain. The block should only be shown on that domain. OK that works.
Then reconfigure that block domain visibility settings, just uncheck the previously assigned domain, check another different one and submit. The bug should appear, the new assigned domain is going to be added to domain_blocks table but as $domain_count_saved is 0 (the previously assigned domains, in this case only one, have been unchecked) then domain_site is going to be added too. So the domain_blocks table for that block on that moment has two entries, one for the selected domain, and one with domain_site realm. The block, though assigned only to one domain, will be shown on all domains.
If you prefer all that logic, you should change that codition $domain_count_saved == 0 to ($domain_count_saved == 0 && empty($domain_arr)), that means all previously assigned domains have been unchecked and, besides, and important too to decide to asign a domain_site, no new ones have been checked (just means no domains are checked, but in the logic you are coding you have to check it that way).
But even if you think is better doing all that logic I would review it anyway. In my opinion you should proceed checking the new values, not doing conditions based on previously values:
And there is no more that those two cases, or there is nothing selected and domain_site is needed or there is some domains selected and you have to update the database entries.
But the logic you are following based on previous values insteed of new values is really strange, though it would work with that change on the condition $domain_count_saved == 0.
Comment #6
manfer commentedThis is how I would do it:
How it is coded even this:
is being avoided and if now none of the domains are selected and previously none were selected, nothing is done, no queries.
Comment #7
manfer commentedAnd that is because you are changing the value of the parameter $domain_arr in your algorithm inside your save function. If you declare another variable to that purpose, $domains_to_be_added, really the only needed to assign a domain_site is that the parameter $domain_arr is empty (none of the domains are checked). So it would be enough to change the condition from:
to:
Anyway, in my modest opinion, I think the algorithm I describe and the code in previous message is clearer than the one you are using testing first previous selected domains and then new selected ones.
Comment #8
manfer commentedA minor change on the code I posted. On:
the array_values() is not needed as implode just use the values of the array itself, the keys are not important at all, there is no need to regenerate them as 0, 1, 2... So that line could be just simplified to:
Comment #9
manfer commentedI move this to a bug report because the bug is present there. You can reproduce it as mentioned before:
The reason why is on #5.
And this is the code I suggest with a modification because I was not taking into account the first time a block configuration form is submited for new blocks created by modules installed after domain blocks, when still there are no rows on {domain_blocks} table for that block and the user has not checked any domain on domain specific visibility settings for that block, domain_site grant must be added.
Comment #10
manfer commentedComment #11
manfer commentedComment #12
manfer commentedSorry but still was an error on the code I posted. So here it is finally doing what I explain in the algorith and taking into account when still there is no record in {domain_blocks} database table about that block:
A bracket was missing in the if operator so NULL value was not being assigned to $previous_block_domains as intended when there where no records for that block in database. A check is needed to prevent the foreach (NULL as..) in the stament that generate $block_domains variable when $previous_block_domains is NULL. And I changed the check $previous_block_domains == NULL (that is true too if $previous_block_domains = array()) to !isset($previous_block_domains) that will be true only if $previous_block_domains = NULL.
With those changes now it is correct code for the algorithm I described before and I think it is doing just the same you pretended with all your save logic to avoid as many queries as possible.
Sorry for so many post. This is the last one before you check it again and give some opinion.
Comment #13
nonsieCould you please include sample records from your domain_blocks table before and after? I have not been able to replicate your problem.
Comment #14
manfer commentedFresh drupal installation, fresh domain access installation, fresh domain blocks installation:
------------------------------------------------------------------
After configuring block "Powered by Drupal" to only first domain:
Now block Powered by Drupal is only shown on that first domain.
------------------------------------------------------------------
After reconfiguring block "Powered by Drupal", unchecking first domain and checking a second one:
Now the block is shown on all domains though it should only be shown on second domain because of the present of that domain_site row on {domain_blocks} table. In this moment you must check going to the different domains to see that the block is there on all domains.
I'm sorry if my english is poor to explain this but I think it is easy to debug manually your save algorithm to see what is happening. I repeat again step by step:
------------------------------------------------------------------
If you enter the configuration form of block "Power by Drupal" it shows correctly only second domain checked but that is because when it reads the records it reads the record with domain_id. If you at that point save again the block configuration, the domain_site record disappears, because in your save logic in this second save with same only second domain checked, what is going to happened is:
At this point with a second save, the block will be shown only on second domain.
------------------------------------------------------------------
The problem is there, so if you don't see it, or you are not looking at the database (in block configuration form the problem is not going to appear if you think it will) or you are not visiting the domains after the reconfiguration as I explained to reproduce the problem and you are visiting the configuration form again and doing the second save that removes domain_site from the database table.
But though the second save eliminates the wrong row that's not mean the problem is not there and I don't think users are going to do a double save of a form. They save once and if they don't visit domains where the block shouldn't appear they even won't notice the bug.
What's more, the bug only appears, let see if I have not explained it correctly, when you uncheck all, all, all, all, previously configured domains, and check some new one(s) (domains not previously checked).
If still you can't reproduce the problem, I can't understand it, I must be very very bad explaining myself. But you only need to follow your logic. A manual debug reveals the problem.
Or if you want I include debug code on 6.x-1.1 version to reveal it.
------------------------------------------------------------------
Comment #15
manfer commentedComment #16
nonsieI took the patch from #15, renamed a few variables, did some coder cleanup and added a submit handler for user defined block deletion that was missing before.
Anyone willing to test it?
Comment #17
manfer commentedFirst of all thanks for looking into it.
I tested and I'm sorry to have to tell I bug was introduced on the changes to patch in #15.
First I'm going to show the warning that appears with 6.x-1.x-dev with patch in #16:
and when it appears:
When you have a module with its domain visibility settings as domain_site (no domains checked) and you enter the configuration form for that block and you save without making changes (that means, the visibility settings intact, still no domains checked).
and why:
The code in patch #15 related to $previous_block_domains is as follow:
You can see on that code that it is using domains with realm. And that function in #15 is as follows:
This function is going to return an empty array if the block is not present at all in the database, otherwise it is going to return $block['domain_site'][0]=0 if domain_site grant is present for that block or $block['domain_id'] = array(0=>0, 1=>1) as an example, or in words, an array of domains ids. And this is what is used to compute $previous_block_domains which would be NULL if this function returns an empty array, it will be array() if this function returns the domain_site array or it would contain the array of domains if this function returns that array.
But you changed it on #16 to:
And now that function is not returning the same, it returns NULL if the block is not present in the database, it returns the array of domains if some domains are configured for that block but it does not return the domain_site if that is present, if domain site is present it returns NULL too, so $previous_block_domains is going to be NULL too when domain_site is present on the database and the save function is going to try to insert that domain_site again in the database which is a violation of the primary key for domain_blocks table.
That was probably the most confusing part of the code in #15. Maybe to prevent that confusion is better to do all inside the _domain_blocks_load function, making that function responsible of directly returning the $previous_block_domains (as you almost did with that function documentation but not with its code). I mean something like this:
Besides, and I'm very sorry about that, there is a bug in my save code, exactly in this piece of the save code:
The if is going to be FALSE when the only domain to be removed is the default domain that has an id of 0, because the variable $domains_to_be_removed is going to be the string "0" that is an equivalent of FALSE. So to solve that, that piece of code should be:
That way even in the case the only domain to be removed were the default domain with id 0, the check is being done before imploding the array so $domains_to_be_removed is going to be array(0=>"0") which is not an equivalent of FALSE.
I would do a patch tomorrow starting with version 6.x-1.x-dev with patch #16 applied as an start to make as less changes as possible to your code.
Comment #18
manfer commentedI realize the bug I mention is present in the save code in #15 was just solved in #16.
So the only problem in #16 is the warning when saving a block with domain_site, without making changes.
The function to solve that in #17 was not very good one, because is checking for each row in the result if it has realm 'domain_site'. So it would be better:
I attached the patch.
Comment #19
nonsieThanks so much for handing in there for this issue. The patch works as expected.
Comment #20
nonsieCommitted to 6.x-1.x-dev
Comment #21
nonsieReleased as part of 6.x-1.2
Comment #22
manfer commentedThere is an error on the delete query that uses IN, sorry. The query that deletes unchecked domains. As it is now if the admin unchecks more than one domain only the first one is going to be deleted.
needs to be changed to:
To use IN at least as possible the query that deletes domain_site can be changed too, cause it is not needed there to check the domain_id column. So it can be changed from:
to:
I attach the patch.
Comment #23
nonsieBug confirmed. Committed and rolling out a new bug fix release