00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 class Mage_Core_Model_Translate_Inline
00036 {
00037 protected $_tokenRegex = '\{\{\{(.*?)\}\}\{\{(.*?)\}\}\{\{(.*?)\}\}\{\{(.*?)\}\}\}';
00038 protected $_content;
00039 protected $_isAllowed;
00040 protected $_isScriptInserted = false;
00041 protected $_isAjaxRequest = null;
00042
00043 public function isAllowed($storeId=null)
00044 {
00045 if (is_null($this->_isAllowed)) {
00046 if (Mage::getDesign()->getArea()==='adminhtml') {
00047 $active = Mage::getStoreConfigFlag('dev/translate_inline/active_admin', $storeId);
00048 } else {
00049 $active = Mage::getStoreConfigFlag('dev/translate_inline/active', $storeId);
00050 }
00051
00052 $this->_isAllowed = $active && Mage::helper('core')->isDevAllowed($storeId);
00053 }
00054
00055 $translate = Mage::getSingleton('core/translate');
00056
00057
00058 return $translate->getTranslateInline() && $this->_isAllowed;
00059 }
00060
00061 public function processAjaxPost($translate)
00062 {
00063 if (!$this->isAllowed()) {
00064 return;
00065 }
00066
00067 $resource = Mage::getResourceModel('core/translate_string');
00068
00069 foreach ($translate as $t) {
00070 if (Mage::getDesign()->getArea() == 'adminhtml') {
00071 $storeId = 0;
00072 }
00073 elseif (empty($t['perstore'])) {
00074 $resource->deleteTranslate($t['original'], null, false);
00075 $storeId = 0;
00076 }
00077 else {
00078 $storeId = Mage::app()->getStore()->getId();
00079 }
00080
00081 $resource->saveTranslate($t['original'], $t['custom'], null, $storeId);
00082 }
00083 }
00084
00085 public function stripInlineTranslations(&$body)
00086 {
00087 if (is_array($body)) {
00088 foreach ($body as $i=>&$part) {
00089 if (strpos($part,'{{{')!==false) {
00090 $part = preg_replace('#'.$this->_tokenRegex.'#', '$1', $part);
00091 }
00092 }
00093 } elseif (is_string($body)) {
00094 $body = preg_replace('#'.$this->_tokenRegex.'#', '$1', $body);
00095 }
00096 return $this;
00097 }
00098
00099 public function processResponseBody(&$bodyArray)
00100 {
00101 if (!$this->isAllowed()) {
00102
00103 if (Mage::getDesign()->getArea()==='adminhtml') {
00104 $this->stripInlineTranslations($bodyArray);
00105 }
00106 return;
00107 }
00108
00109 foreach ($bodyArray as $i=>$content) {
00110 $this->_content = $content;
00111
00112 $this->_tagAttributes();
00113 $this->_specialTags();
00114 $this->_otherText();
00115 $this->_insertInlineScriptsHtml();
00116
00117 $bodyArray[$i] = $this->_content;
00118 }
00119 }
00120
00121 protected function _insertInlineScriptsHtml()
00122 {
00123 if ($this->_isScriptInserted || stripos($this->_content, '</body>')===false) {
00124 return;
00125 }
00126
00127 $baseJsUrl = Mage::getBaseUrl('js');
00128 $ajaxUrl = Mage::getUrl('core/ajax/translate', array('_secure'=>Mage::app()->getStore()->isCurrentlySecure()));
00129 $trigImg = Mage::getDesign()->getSkinUrl('images/fam_book_open.png');
00130
00131 ob_start();
00132 ?>
00133 <!-- script type="text/javascript" src="<?php echo $baseJsUrl ?>prototype/effects.js"></script -->
00134 <script type="text/javascript" src="<?php echo $baseJsUrl ?>prototype/window.js"></script>
00135 <link rel="stylesheet" type="text/css" href="<?php echo $baseJsUrl ?>prototype/windows/themes/default.css"/>
00136 <link rel="stylesheet" type="text/css" href="<?php echo $baseJsUrl ?>prototype/windows/themes/magento.css"/>
00137
00138 <script type="text/javascript" src="<?php echo $baseJsUrl ?>mage/translate_inline.js"></script>
00139 <link rel="stylesheet" type="text/css" href="<?php echo $baseJsUrl ?>mage/translate_inline.css"/>
00140
00141 <div id="translate-inline-trig"><img src="<?php echo $trigImg ?>" alt="[TR]"/></div>
00142 <script type="text/javascript">
00143 new TranslateInline('translate-inline-trig', '<?php echo $ajaxUrl ?>', '<?php echo Mage::getDesign()->getArea() ?>');
00144 </script>
00145 <?php
00146 $html = ob_get_clean();
00147
00148 $this->_content = str_ireplace('</body>', $html.'</body>', $this->_content);
00149
00150 $this->_isScriptInserted = true;
00151 }
00152
00153 protected function _escape($string)
00154 {
00155 return str_replace("'", "\\'", htmlspecialchars($string));
00156 }
00157
00158 protected function _tagAttributes()
00159 {
00160 #echo __METHOD__;
00161
00162 if ($this->getIsAjaxRequest()) {
00163 $quoteHtml = '\"';
00164 $quotePatern = '\\\\"';
00165 } else {
00166 $quoteHtml = '"';
00167 $quotePatern = '"';
00168 }
00169
00170 $nextTag = 0; $i=0;
00171 while (preg_match('#<([a-z]+)\s*?[^>]+?(('.$this->_tokenRegex.')[^/>]*?)+(/?(>))#i',
00172 $this->_content, $tagMatch, PREG_OFFSET_CAPTURE, $nextTag)) {
00173 #echo '<xmp>'.print_r($tagMatch[0][0],1).'</xmp><hr/>';
00174
00175 $next = 0;
00176 $tagHtml = $tagMatch[0][0];
00177 $trArr = array();
00178
00179 while (preg_match('#'.$this->_tokenRegex.'#i',
00180 $tagHtml, $m, PREG_OFFSET_CAPTURE, $next)) {
00181
00182 $trArr[] = '{shown:\''.$this->_escape($m[1][0]).'\','
00183 .'translated:\''.$this->_escape($m[2][0]).'\','
00184 .'original:\''.$this->_escape($m[3][0]).'\','
00185 .'location:\'Tag attribute (ALT, TITLE, etc.)\','
00186 .'scope:\''.$this->_escape($m[4][0]).'\'}';
00187 $tagHtml = substr_replace($tagHtml, $m[1][0], $m[0][1], strlen($m[0][0]));
00188 $next = $m[0][1];
00189 }
00190
00191 if (preg_match('# translate='.$quotePatern.'\[(.+?)\]'.$quotePatern.'#i', $tagMatch[0][0], $m, PREG_OFFSET_CAPTURE)) {
00192 foreach ($trArr as $i=>$tr) {
00193 if (strpos($m[1][0], $tr)!==false) {
00194 unset($trArr[$i]);
00195 }
00196 }
00197 array_unshift($trArr, $m[1][0]);
00198 $start = $tagMatch[0][1]+$m[0][1];
00199 $len = strlen($m[0][0]);
00200 } else {
00201 $start = $tagMatch[8][1];
00202 $len = 0;
00203 }
00204
00205 $trAttr = ' translate="['.join(',', $trArr).']"';
00206 $tagHtml = preg_replace('#/?>$#', $trAttr.'$0', $tagHtml);
00207
00208
00209 $this->_content = substr_replace($this->_content, $tagHtml,
00210 $tagMatch[0][1], $tagMatch[9][1]+1-$tagMatch[0][1]);
00211 $nextTag = $tagMatch[0][1];
00212 }
00213 }
00214
00215 protected function _specialTags()
00216 {
00217 #echo __METHOD__;
00218
00219 if ($this->getIsAjaxRequest()) {
00220 $quoteHtml = '\"';
00221 $quotePatern = '\\\\"';
00222 } else {
00223 $quoteHtml = '"';
00224 $quotePatern = '"';
00225 }
00226
00227 $nextTag = 0;
00228
00229 $location = array(
00230 'script' => 'String in Javascript',
00231 'title' => 'Page title',
00232 'select' => 'Dropdown option',
00233 'button' => 'Button label',
00234 'a' => 'Link label',
00235 );
00236
00237 while (preg_match('#<(script|title|select|button|a)(\s+[^>]*|)(>)#i',
00238 $this->_content, $tagMatch, PREG_OFFSET_CAPTURE, $nextTag)) {
00239 #echo '<xmp>'.print_r($tagMatch[0][0],1).'</xmp><hr/>';
00240
00241 $tagClosure = '</'.$tagMatch[1][0].'>';
00242 $tagLength = stripos($this->_content, $tagClosure, $tagMatch[0][1])-$tagMatch[0][1]+strlen($tagClosure);
00243
00244 $next = 0;
00245 $tagHtml = substr($this->_content, $tagMatch[0][1], $tagLength);
00246 $trArr = array();
00247
00248 while (preg_match('#'.$this->_tokenRegex.'#i',
00249 $tagHtml, $m, PREG_OFFSET_CAPTURE, $next)) {
00250
00251 $trArr[] = '{shown:\''.$this->_escape($m[1][0]).'\','
00252 .'translated:\''.$this->_escape($m[2][0]).'\','
00253 .'original:\''.$this->_escape($m[3][0]).'\','
00254 .'location:\''.$location[strtolower($tagMatch[1][0])].'\','
00255 .'scope:\''.$this->_escape($m[4][0]).'\'}';
00256
00257 $tagHtml = substr_replace($tagHtml, $m[1][0], $m[0][1], strlen($m[0][0]));
00258
00259 $next = $m[0][1];
00260 }
00261 if (!empty($trArr)) {
00262 $trArr = array_unique($trArr);
00263
00264 $tag = strtolower($tagMatch[1][0]);
00265
00266 switch ($tag) {
00267 case 'script': case 'title':
00268 $tagHtml .= '<span class="translate-inline-'.$tag
00269 .'" translate='.$quoteHtml.'['.join(',',$trArr).']'.$quoteHtml.'>'.strtoupper($tag).'</span>';
00270 break;
00271 }
00272 $this->_content = substr_replace($this->_content, $tagHtml, $tagMatch[0][1], $tagLength);
00273
00274 switch ($tag) {
00275 case 'select': case 'button': case 'a':
00276 if (preg_match('# translate='.$quotePatern.'\[(.+?)\]'.$quotePatern.'#i', $tagMatch[0][0], $m, PREG_OFFSET_CAPTURE)) {
00277 foreach ($trArr as $i=>$tr) {
00278 if (strpos($m[1][0], $tr)!==false) {
00279 unset($trArr[$i]);
00280 }
00281 }
00282 array_unshift($trArr, $m[1][0]);
00283 $start = $tagMatch[0][1]+$m[0][1];
00284 $len = strlen($m[0][0]);
00285 } else {
00286 $start = $tagMatch[3][1];
00287 $len = 0;
00288 }
00289
00290 $this->_content = substr_replace($this->_content,
00291 ' translate='.$quoteHtml.'['.join(',',$trArr).']'.$quoteHtml, $start, $len);
00292 break;
00293 }
00294 }
00295
00296 $nextTag = $tagMatch[0][1]+10;
00297 }
00298
00299 }
00300
00301 protected function _otherText()
00302 {
00303 #return;
00304 #echo __METHOD__;
00305 #echo "<xmp>".$this->_content."</xmp><hr/>";
00306 #exit;
00307
00308 if ($this->getIsAjaxRequest()) {
00309 $quoteHtml = '\"';
00310 } else {
00311 $quoteHtml = '"';
00312 }
00313
00314 $next = 0;
00315 while (preg_match('#('.$this->_tokenRegex.')#',
00316 $this->_content, $m, PREG_OFFSET_CAPTURE, $next)) {
00317 #echo '<xmp>'.print_r($m[0][0],1).'</xmp><hr/>';
00318
00319 $tr = '{shown:\''.$this->_escape($m[2][0]).'\','
00320 .'translated:\''.$this->_escape($m[3][0]).'\','
00321 .'original:\''.$this->_escape($m[4][0]).'\','
00322 .'location:\'Text\','
00323 .'scope:\''.$this->_escape($m[5][0]).'\'}';
00324
00325 $spanHtml = '<span translate='.$quoteHtml.'['.$tr.']'.$quoteHtml.'>'.$m[2][0].'</span>';
00326
00327 $this->_content = substr_replace($this->_content, $spanHtml, $m[0][1], strlen($m[0][0]));
00328 $next = $m[0][1];
00329 }
00330
00331 }
00332
00333 public function getIsAjaxRequest()
00334 {
00335 if (!is_null($this->_isAjaxRequest)) {
00336 return $this->_isAjaxRequest;
00337 } else {
00338 return Mage::app()->getRequest()->getQuery('isAjax');
00339 }
00340 }
00341
00342 public function setIsAjaxRequest($status)
00343 {
00344 $this->_isAjaxRequest = $status;
00345 }
00346 }