Multi locale, multi-domain Live Preview failing I have a multi-locale site set up with the different locales in different domains


phoseon.craft.dev - English site
phoseon.it.craft.dev - Italian site


etc.

I am having problems getting Live Preview to work with a locale other than English.

The page I am trying to get this to work on, test-single, is a single, with a template that just displays the title (so no complexity there).

When I try to use Live Preview in a locale other than English, I get this error in the console:


  Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://phoseon.it.craft.dev/. This can be fixed by moving the resource to the same domain or enabling CORS.


And no Live Preview, just a white screen.

Based on this answer I added this to .htaccess for http://phoseon.it.craft.dev

Header add Access-Control-Allow-Origin "http://phoseon.craft.dev"


And now I see these requests, and still a white screen for the Live Preview:

POST http://phoseon.it/craft.dev/test-single 302 Found
  Request Headers:
    Accept  */*
    Accept-Encoding gzip, deflate
    Accept-Language en-US,en;q=0.5
    Content-Length  198
    Content-Type  application/x-www-form-urlencoded; charset=UTF-8
    Host  phoseon.it.craft.dev
    Origin  http://phoseon.craft.dev
    Referer http://phoseon.craft.dev/admin/entries/testSingle/1742-testsingle/it_it
    User-Agent  Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:35.0) Gecko/20100101 Firefox/35.0
  Response Headers:
    Access-Control-Allow-Orig...  http://phoseon.craft.dev
    Cache-Control no-store, no-cache, must-revalidate, post-check=0, pre-check=0
    Connection  Keep-Alive
    Content-Encoding  gzip
    Content-Length  2346
    Content-Type  text/html
    Date  Mon, 16 Feb 2015 20:38:14 GMT
    Expires Thu, 19 Nov 1981 08:52:00 GMT
    Keep-Alive  timeout=5, max=100
    Location  http://phoseon.it.craft.dev/login
    Pragma  no-cache
    Server  Apache/2.2.26 (Unix) DAV/2 PHP/5.4.30 mod_ssl/2.2.26 OpenSSL/0.9.8zc
    Set-Cookie  CraftSessionId=gtm75r2a9it49820uueam9hpe2; path=/; HttpOnly
    Vary  User-Agent,Accept-Encoding
    X-Powered-By  PHP/5.4.30

  And the response is the site's 404 page

GET http://phoseon.it.craft.dev/login 404 Not Found
  Request Headers:
    Accept  */*
    Accept-Encoding gzip, deflate
    Accept-Language en-US,en;q=0.5
    Content-Type  application/x-www-form-urlencoded; charset=UTF-8
    Host  phoseon.it.craft.dev
    Origin  http://phoseon.craft.dev
    Referer http://phoseon.craft.dev/admin/entries/testSingle/1742-testsingle/it_it
    User-Agent  Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:35.0) Gecko/20100101 Firefox/35.0
  Response Headers:
    Access-Control-Allow-Orig...  http://phoseon.craft.dev
    Cache-Control no-store, no-cache, must-revalidate, post-check=0, pre-check=0
    Connection  close
    Content-Encoding  gzip
    Content-Length  42163
    Content-Type  text/html; charset=utf-8
    Date  Mon, 16 Feb 2015 20:38:14 GMT
    Expires Thu, 19 Nov 1981 08:52:00 GMT
    Pragma  no-cache
    Server  Apache/2.2.26 (Unix) DAV/2 PHP/5.4.30 mod_ssl/2.2.26 OpenSSL/0.9.8zc
    Set-Cookie  CraftSessionId=isldd40afpu41mr9as659f6rs7; path=/; HttpOnly
    Vary  User-Agent,Accept-Encoding
    X-Powered-By  PHP/5.4.30
    charset utf-8

  And the response is agin the site's 404 page


Anyone else encountered this? Know what's going on? Recommended fixes?
Only setting headers in .htaccess doesn't fix the problem.
As Marion Newlevant points out in her answer the cross domain works with the headers set. But you still get a white page as a preview.

Looking further into the problem, I've found that the page tries to do a post request to the other domain.
It calls upon the action function actionPreviewEntry() in the EntriesController.php file (Location: /craft/app/controllers/ )

But because the user isn't logged in on the other domain and anonymous access isn't allowed for this action. You get redirected to /login to login on that domain.

I came up with a quick solution for the problem. It's a bit dirty for now, but the authors of Craft aren't coming with a solution either.

Solution


Delete (If you have set them) the headers for Access-Control-Allow in your .htaccess file. We don't need them.
Open the file EntriesController.php in the folder /craft/app/controllers/
Replace the code.

protected $allowAnonymous = array('actionViewSharedEntry');


With:

protected $allowAnonymous = array('actionViewSharedEntry','actionPreviewEntry');



Add the following code in the public function actionPreviewEntry():

// Set a boolean for logout admin user if cross-domain access
$crossDomainAccess = FALSE;

// Check if the user isn't logged in. Likely meaning that the action is called from another domain.
if ( !craft()-&gt;userSession-&gt;isLoggedIn() ) {

  // Get allowed origins
  $locales = array('en','nl');
  $allowedOrigins = array();
  foreach ( $locales as $locale ) {
    $allowedOrigins[] = craft()-&gt;config-&gt;getLocalized('siteUrl', $locale);
  }

  // Check origin and give access if allowed
  $httpOrigin = $_SERVER['HTTP_ORIGIN'];
  if ( in_array($httpOrigin, $allowedOrigins) ) {
    header('Access-Control-Allow-Origin: ' . $httpOrigin);
    header('Access-Control-Allow-Credentials: true');
    craft()-&gt;userSession-&gt;loginByUserId(1);
    $crossDomainAccess = TRUE;
  }
}



Explanation of the code:

As you can see I set the locales to en and nl. Because that's the only locales for my website.

$locales = array('en','nl');


The domains that are allowed to communicate with each other are set in the general config file under siteUrl. See step 4 of the localization guide for more information.
We will fetch them here and put them in an array for checking.

$allowedOrigins = array();
foreach ( $locales as $locale ) {
    $allowedOrigins[] = craft()-&gt;config-&gt;getLocalized('siteUrl', $locale);
}


We check the origin of the request and if it's allowed to communicate with this domain. If so, then set the header to allow cross-domain access.

And automatically login as the admin user to get full access for the preview page to load as if you are logged in on the other domain.

$httpOrigin = $_SERVER['HTTP_ORIGIN'];
if ( in_array($httpOrigin, $allowedOrigins) ) {
    header('Access-Control-Allow-Origin: ' . $httpOrigin);
    header('Access-Control-Allow-Credentials: true');
    craft()-&gt;userSession-&gt;loginByUserId(1);
    $crossDomainAccess = TRUE;
}



Next we need to logout the admin user on the other domain. Add the following code:

if ( $crossDomainAccess == TRUE ) {
  craft()->userSession->logout();
}


Before the line (In the same function, public function actionPreviewEntry()):

$this-&gt;_showEntry($entry);


And that's it. A quickfix for anyone that has come across this problem and wants to fix it quick but dirty for now.

Note: If you update your craft cms, then this code will get deleted, so you have to add it again after you updated.

If you're using baseCpUrl

Slight modification if you're using the baseCpUrl setting and/or have paths in your settings file:

if ( !craft()-&gt;userSession-&gt;isLoggedIn() ) {

  // Get allowed origins
  $allowedOrigins = array(
          parse_url(craft()-&gt;config-&gt;get('baseCpUrl'), PHP_URL_HOST),
          parse_url(craft()-&gt;config-&gt;getLocalized('siteUrl', 'en_us'), PHP_URL_HOST)
  );

  // Check origin and give access if allowed
  $httpOrigin = parse_url($_SERVER['HTTP_ORIGIN'], PHP_URL_HOST);
  if ( in_array($httpOrigin, $allowedOrigins) ) {
          header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']);
          header('Access-Control-Allow-Credentials: true');
          craft()-&gt;userSession-&gt;loginByUserId(1);
          $crossDomainAccess = TRUE;
  }
}


Here I've hard-coded the language in, but it can be combined with the answer above instead.