Steps to reproduce
- An ajax-enabled form that's accessible for the anonymous user. -- in my case a multi-page webform with webform_ajax enabled.
- Some custom theme is displayed. -- in my case by a module that sets it via hook_custom_theme()
- Either the page cache is enabled or the browser doesn't accept cookies (leading to the session_id() changing between the original form generation and the ajax request).
Result
The ajax request is handled using the default theme which means the styles for the default theme are loaded additionally to the styles of the custom theme (at least with less enabled) - but at the very least use the wrong template files.
What happens exactly?
ajax_base_page_theme() is responsible for setting the same theme that was used in the original form generation. It uses drupal_valid_token() to check whether the the user is allowed to set the theme via $_POST['ajax_page_state']['theme']. drupal_valid_token() uses the session_id() to check the token and thus fails (because the session_id() has changed).
In a broader picture drupal_valid_token() can't work reliably for anonymous in general in this form, because it relies on the session_id().
Proposed solution
Use another token generation/verification method that uses the form_build_id instead of the session_id().
Comments
Comment #1
torotil commentedComment #2
pirog commentedpossibly related #1334818: #ajax does not work in install profiles
Comment #3
torotil commentedIt seems that #1334818: #ajax does not work in install profiles provides a installation specific workaround for the bug mentioned in this report. The other report seems to be another symptom of the same problem.
Comment #4
pirog commentedIt would be great to fix the bug reported here so that #1334818: #ajax does not work in install profiles presumably does not need to.
Comment #5
mkalkbrennerFYI
You can probably use ThemeKey as a workaround by setting up a simple "catch all" rule like
system:dummy = dummy >>> YOUR_THEMEand activating the "Bypass" option at /admin/config/user-interface/themekey/settings/ajaxComment #6
corbacho commentedThis bug also shows when:
* using (webform ajax or custom form with ajax) + domains module (domain_theme submodule)
Because domains_theme uses
domain_theme_custom_themeSomeone reported this bug 3 years ago: https://www.drupal.org/node/1319344
Comment #7
jtwalters commentedI also ran into this issue using hook_custom_theme and there's a couple bugs that prevented it from working for me:
1. Page cache and ajaxPageState['theme_token'] — incompatible
2. session_id() changing on every page load — breaks token check
My workaround solution, at the top of my hook_custom_theme:
Comment #8
geek-merlinComing from #3075068: Make our ajax popups cacheable and #2177975: Page cache breaks nondefault ajax theme on GET.
POST requests should not be cached in the first place and i don't know if this still applies for D7, but it should not for D8.
But if we want to use GET for ajax for better cacheability, this should still be an issue. (Thus adding rescope tag and moving to D8 for now.)
So the additional conditions are
* use GET for ajax
* request a nondefault theme (see ajax_base_page_theme)
I'm not deep in the internals, but if we can add the session key/hash to the get parameters, we should be done. (Just FTR, i think i will not work on it.)
Comment #9
geek-merlin> I'm not deep in the internals, but if we can add the session key/hash to the get parameters, we should be done. (Just FTR, i think i will not work on it.)
Sorry, i was confused. The theme token is of course a session hash so i guess this should not be an issue anymore. Correct me if this manifests differently.
Comment #12
pameeela commented@geek-merlin this is no longer an issue in D8 but probably is in D7, yes? If so we can just update the ticket to be against 7.x rather than closing it.
Comment #13
geek-merlin@pameeela: Yup, thanks!
Comment #14
pameeela commentedComment #15
pamelalies commentedThanks to @jtwalters, your fix in #7 solved my issue as well!
In my case I was using a View with Ajax, and after clicking to the second and subsequent pages of results, the theme reverted from the current correct theme to the default theme for anonymous users. Adding your fix in #7 at the top of my hook_custom_theme() resolved it.
In case seeing the full hook implementation will help anyone else, I'll paste it below. I added a pretty lengthy explanation of the fix to the code since we have multiple developers.