We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 22851
    • 805 Posts
    Does anyone know if it is possible to alter the document output after any caching has taken place. From the wiki, it would seem that there is one event that occurs after caching, [tt]OnWebPageComplete[/tt], but [tt]$modx->documentOutput[/tt] didn’t appear to be available when I tried it.

    Thank you.
      YAMS: Yet Another Multilingual Solution for MODx
      YAMS Forums | Latest: YAMS 1.1.9 | YAMS Documentation
      Please consider donating if you appreciate the time and effort spent developing and supporting YAMS.
      • 3749
      • 24,544 Posts
      I’m having a hard time imagining the point of doing that. MODx would show the unchanged, cached document to any visitors and they wouldn’t see the changes you made until some arbitrary time later when the cache was cleared.

      Maybe if you explain clearly what you’re trying to accomplish, somone could come up with a way to do it.
        Did I help you? Buy me a beer
        Get my Book: MODX:The Official Guide
        MODX info for everyone: http://bobsguides.com/modx.html
        My MODX Extras
        Bob's Guides is now hosted at A2 MODX Hosting
        • 22851
        • 805 Posts
        I’d like to be able to take the (potentially) cached version of the document output and alter it before it is served.

        The motivation for this is as follows: Doing this would allow me to store multiple versions of content within a single document (normal and handheld formatted versions of the same page, or versions of the content in multiple languages) and have the multiple versions subject to the normal caching behaviour of MODx. Then after the cached version has been retrieved, content negotiation can be used to strip out unwanted content based on the headers sent along with the request for the page. If I stripped out the content before the caching took place, then only one version of the content would be cached and anyone wanting a different version of the same document content would get served the wrong one.

        So, I ’d like access to an event that occurs after any caching and that gives me access to output (cached or otherwise) before it is served.

        Does that make sense?
          YAMS: Yet Another Multilingual Solution for MODx
          YAMS Forums | Latest: YAMS 1.1.9 | YAMS Documentation
          Please consider donating if you appreciate the time and effort spent developing and supporting YAMS.
          • 29774
          • 386 Posts
          @PMS I’ve recently considered doing the very same thing, but for storing multilingual content, where the various language versions of a page would be cached but only the selected language would be output.

          The event you need to attach your plugin to is OnWebPagePrerender; the document output is available as $modx->documentOutput.

          Edit: this regex should match content between two delimiters str1 and str2 when parsing the output:

          /str1(?.*?)str2/
            Snippets: GoogleMap | FileDetails | Related Plugin: SSL
            • 22851
            • 805 Posts
            That’s where I currently have my language selection regexp - and it does appear to be working - but according to the wiki, caching occurs after the OnWebPagePrerender event has had a chance to modify the content, not before. Is that wrong?

            I’ll do some additional tests to be sure.
              YAMS: Yet Another Multilingual Solution for MODx
              YAMS Forums | Latest: YAMS 1.1.9 | YAMS Documentation
              Please consider donating if you appreciate the time and effort spent developing and supporting YAMS.
              • 29774
              • 386 Posts
              The wiki is wrong about that, the parser has made all it’s passes by that point and $modx->documentOutput is about to be sent to the browser. If you open up one of the cached pages in /assets/cache/ you should see your cached content.
                Snippets: GoogleMap | FileDetails | Related Plugin: SSL
                • 22851
                • 805 Posts
                Thanks therebechips. That brings me one step closer to releasing my multilingual module into the community for testing.
                  YAMS: Yet Another Multilingual Solution for MODx
                  YAMS Forums | Latest: YAMS 1.1.9 | YAMS Documentation
                  Please consider donating if you appreciate the time and effort spent developing and supporting YAMS.
                  • 29774
                  • 386 Posts
                  Cool smiley

                  I may as well share this with you then since I seem to be working on a similar thing to you.

                  I have found that it is ideal to have a different base url for each language in use; it means your content gets indexed separately by the search engines, and each language can have pages that can be bookmarked. This could be done with separate document trees (mydomain.com/en/ etc) but what if only parts of your page are going to be translated, and you want to fall back to a default language if a translation is not available? I prefer to use TVs for elements of a page that is to be translated.

                  One easy way to achieve this without separate document trees is to set up subdomains of your domain or register your domain in other tlds and make them aliases of your main domain, eg:
                  fr.mydomain.com
                  or
                  mydomain.fr

                  I have two plugins, one that establishes the page language based on the server and registers some global variables that can be used by snippets, and one that replaces custom placeholders within the documentOutput which you use in your templates like this: ((lang.switcher)) - the language switcher, the base href ((base.href)), language direction ((dir)) language 2-letter code ((lang)) and language ((language)).

                  Language:
                  <?php
                  /*
                  Title:      Language
                  Author:     Mark Croxton, [email protected]
                  Desc:		Plugin triggered OnParseDocument
                  Updated:    May 15 2009
                  */
                  global $modx;
                  $domains  = array(
                  	'de' => array('Deutsch','de.mydomain.com','ltr'),
                  	'en' => array('English','mydomain.com','ltr'),
                  	'es' => array('Español','es.mydomain.com','ltr'),
                  	'fr' => array('Français','fr.mydomain.com','ltr'),
                  	'zh' => array('中文','cn.mydomain.com','ltr')
                  	);
                  
                  // what's the URI of the current page?
                  $url = $_SERVER['REQUEST_URI'];
                  
                  # what's the name of the server?
                  $server = $domains['en'][1]; // English by default
                  $language = 'en';
                  
                  # check that the server name is in the predefined array before assigning it 
                  # to prevent injection via headers
                  foreach ($domains as $key => $value) {
                  	if ($value[1] == $_SERVER['SERVER_NAME']) {
                  		$server = $value[1];
                  		$language = $key;
                  	}
                  }
                  
                  if ($server != $_SERVER['SERVER_NAME']) {
                  	// domain not recognised, redirect to default
                  	$modx->sendRedirect('http://'.$server.$url);
                  }	
                  $GLOBALS['domains'] = $domains;
                  $GLOBALS['language'] = $language;
                  
                  ?>
                  


                  Language switcher:
                  <?php
                  /*
                  Title:      Language switcher
                  Author:     Mark Croxton, [email protected]
                  Desc:		Plugin triggered OnWebPagePrerender
                  			Sets language placeholders based on site url
                  Updated:    May 15 2009
                  */
                  
                  global $modx, $domains, $language;
                  
                  # server
                  $server = 'http://'.$domains[$language][1];
                  # text direction
                  $dir = $domains[$language][2];
                  # what's the URI of the current page?
                  $url = $_SERVER['REQUEST_URI'];
                  
                  $tplRow = '
                  <li[+active+]><a href="[+server+][+url+]"><em>[+lang+] ([+lang.code+])</em></a></li>
                  ';
                  $tplOuter ='
                  <ul>
                  	[+rows+]
                  </ul>
                  ';
                  
                  $rows ='';
                  foreach ($domains as $key => $value) {
                  	$active='';
                  	if ($key == $language) {
                  		$active =' class="active"';
                  	}
                  	$f	= array('[+active+]','[+server+]','[+url+]','[+lang+]','[+lang.code+]');
                  	$v	= array($active,'http://'.$domains[$key][1],$url,$domains[$key][0],$key);
                  	$rows.= str_replace($f,$v,$tplRow);
                  }
                  
                  $switcher = str_replace('[+rows+]',$rows,$tplOuter);
                  
                  # replace placeholders using a custom syntax to ensure these values are never cached ((placeholder))
                  $fields = array('((base.href))','((dir))','((lang))','((language))','((lang.switcher))');
                  $values = array($server, $dir, $language, $domains[$language][0], $switcher);
                  $modx->documentOutput = str_replace($fields,$values,$modx->documentOutput);
                  
                  ?>
                  


                  I use a snippet to translate on the fly - either a document field, a chunk or hardcoded word:

                  <?php
                  
                  /*
                  Title:      Translate
                  Author:     Mark Croxton, [email protected]
                  Desc:		Translates a document field, tv, or chunk, into the global language
                  Updated:    May 18 2009
                  */
                  
                  global $modx, $language;
                  
                  $field = isset($field)? $field : 'pagetitle';
                  $tpl = isset($tpl)? $tpl : '';
                  $word = isset($word)? $word : '';
                  $docid = isset($id)? $id : $modx->documentIdentifier;
                  
                  if ($word != '') {
                  	$lexicon = array(
                  		'Members' => array(
                  			'en' => 'Members',
                  			'de' => 'Mitglieder',
                  			'es' => 'Miembros',
                  			'fr' => 'Membres',
                  			'zh' => '会员'
                  		),
                  		'Map' => array(
                  			'en' => 'Map',
                  			'de' => 'Karte',
                  			'es' => 'Mapa',
                  			'fr' => 'Cartes',
                  			'zh' => '地图'
                  		),
                  		'Search' => array(
                  			'en' => 'Search',
                  			'de' => 'Suche',
                  			'es' => 'Buscar',
                  			'fr' => 'Trouver',
                  			'zh' => '搜索'
                  		),
                  		'News' => array(
                  			'en' => 'News',
                  			'de' => 'News',
                  			'es' => 'Noticias',
                  			'fr' => 'Nouvelle',
                  			'zh' => '新闻'
                  		),
                  		'Press releases' => array(
                  			'en' => 'Press releases',
                  			'de' => 'Artikel',
                  			'es' => 'Noticias',
                  			'fr' => 'Articles',
                  			'zh' => '新闻稿件'
                  		),
                  		'Find press releases' => array(
                  			'en' => 'Press releases',
                  			'de' => 'Pressemitteilungen finden',
                  			'es' => 'Buscador de comunicados de prensa',
                  			'fr' => 'Trouver des communiqués',
                  			'zh' => '查找新闻稿件'
                  		),
                  		'Events' => array(
                  			'en' => 'Events',
                  			'de' => 'Events',
                  			'es' => 'Eventos',
                  			'fr' => 'Événements',
                  			'zh' => '活动'
                  		),
                  		'Login' => array(
                  			'en' => 'Login',
                  			'de' => '',
                  			'es' => '',
                  			'fr' => '',
                  			'zh' => ''
                  		),
                  		'Contact' => array(
                  			'en' => 'Contact',
                  			'de' => '',
                  			'es' => '',
                  			'fr' => '',
                  			'zh' => ''
                  		),
                  		'Profile' => array(
                  			'en' => 'Profile',
                  			'de' => 'Profil',
                  			'es' => 'Perfil',
                  			'fr' => 'Profil',
                  			'zh' => '外形'
                  		),
                  		'Other offices' => array(
                  			'en' => 'Other offices',
                  			'de' => 'Andere Büros',
                  			'es' => 'Otras oficinas',
                  			'fr' => 'D\'autres bureaux',
                  			'zh' => '其他办公室'
                  		),
                  		'Key contact' => array(
                  			'en' => 'Key contact',
                  			'de' => 'Hauptkontakt',
                  			'es' => 'Contacto principal',
                  			'fr' => 'Contact principal',
                  			'zh' => '主要联络'
                  		),
                  		'Firm name' => array(
                  			'en' => 'Firm name',
                  			'de' => 'Firmenname',
                  			'es' => 'Nombre de la empresa',
                  			'fr' => 'Nom de la firme',
                  			'zh' => '事务所名称'
                  		),
                  		'City' => array(
                  			'en' => 'City',
                  			'de' => 'Stadt',
                  			'es' => 'Ciudad',
                  			'fr' => 'Ville',
                  			'zh' => '城市'
                  		),
                  		'Country' => array(
                  			'en' => 'Country',
                  			'de' => 'Land',
                  			'es' => 'País',
                  			'fr' => 'Pays',
                  			'zh' => '国家'
                  		),
                  		'A-Z' => array(
                  			'en' => 'A-Z',
                  			'de' => 'A-Z',
                  			'es' => 'A-Z',
                  			'fr' => 'A-Z',
                  			'zh' => '字母'
                  		)
                  	);
                  	return $lexicon[$word][$language];
                  } else if ($tpl=='') {
                  	$lang_field = $field;
                  	if ($language != 'en') { // ignore english
                  		$lang_field = $field.$language;
                  	}
                  
                  	$docfield = $modx->getTemplateVarOutput(array($lang_field), $docid, 1);
                  
                  	if ($docfield[$lang_field] == '') { // no content for the selected language, so use english
                  		$docfield = $modx->getTemplateVarOutput(array($field), $docid, 1);
                  		return $docfield[$field];
                  	} else {
                  		return $docfield[$lang_field];
                  	}
                  } else {
                  	// eg myChunk_en, myChunk_de etc
                  	return $modx->getChunk($tpl.'_'.$language);
                  }
                  
                  ?>
                  


                  Hope this helps in some way.
                    Snippets: GoogleMap | FileDetails | Related Plugin: SSL
                    • 3749
                    • 24,544 Posts
                    Ah, I thought you wanted to modify the doc in the DB.

                    onWebPagePrerender should work fine. I think the Wiki is correct. Step 9 is where the decision is made to pull the content from the cache. The cache decision after onWebPagePrerender (step 35) is about whether to cache the document in the first place. If there is a cached version of it already, that step shouldn’t occur.

                    The only issue would be what happens when the cache is cleared manually. It’s possible that your altered version would be cached on the first page access. You’d have to check.

                    Another route would be to use javascript to hide/display the stuff you want.
                      Did I help you? Buy me a beer
                      Get my Book: MODX:The Official Guide
                      MODX info for everyone: http://bobsguides.com/modx.html
                      My MODX Extras
                      Bob's Guides is now hosted at A2 MODX Hosting
                      • 22851
                      • 805 Posts
                      BobRay.

                      I hadn’t considered using javascript to achieve what I want before. The main problem with this is that the way I have it structured now, the output isn’t necessarily valid (X)HTML when all versions of the content are embedded in a single document. Also, serving multiple versions of the same document and get the client to hide the ones they don’t want doesn’t seem right (although it might be less of a burden on the server.) Thanks for your suggestion though. Certainly worth the thought.

                      therebechips.

                      Yes. Parts of my solution work in a similar way to yours. I also have placeholders that provide access to the name of the current language etc. With my solution It is also not necessary to maintain multiple document trees within the manager. There’s a module interface where you can choose which templates are multilingual and configure other parameters. The multiple languages are stored as template variables within the document, which (and I am working on this at the moment) are created automatically from within a module interface. The standard document variables can be hidden. The clever thing is that you don’t have to alter your main document templates. If you include [*pagetitle*] in your template then the correct language will be displayed. I have written an extension for ditto which allows you to use your existing templates and for it to work smoothly. Likewise, I have written templates for wayfinder to replace the default ones which automatically include the correct language version. I’m nearly there now. Just this automatic creation of template variables and making sure there is good documentation to go. It’ll probably take me a couple of weekends.

                      I currently don’t have built in the ability to switch the domain dependent on the language - only the name of the root directory. I like your solution however and it’s given me an idea of how I could do the same within my multilingual framework. So if you don’t mind I’ll try implementing my own version of it.
                        YAMS: Yet Another Multilingual Solution for MODx
                        YAMS Forums | Latest: YAMS 1.1.9 | YAMS Documentation
                        Please consider donating if you appreciate the time and effort spent developing and supporting YAMS.