XarayaTranslationMemory  1.6
TranslationExportXaraya.php
Go to the documentation of this file.
1 <?php
2 
3 /***************************************************************************
4  * Copyright (C) 2005-2020 by Ferenc Veres *
5  * lion@netngine.hu *
6  * *
7  * This program is free software; you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation; either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19  ***************************************************************************/
20 
35 {
36  protected $ProjectName = "Xaraya";
37  protected $XmlNameSpace = "http://xaraya.com/2002/ns/translations";
38 
40  private $RootTimeStamp;
41 
43  private $SilentMode;
44 
46  private $ErrorLogFile;
47 
49  private $XarayaRoot;
50 
51  function __construct()
52  {
53  }
54 
64  private function &ExportProject(TranslationProject $project)
65  {
66  $langXml = new DOMDocument();
67  //$langXml->substituteEntities = true;
68  $langXml->LoadXML($project->GetSkeleton());
69 
70  // Loop on all strings of this project and put in a hash for later replace
71  $strings = new StringIterator($project->GetID());
72  foreach($strings as $str)
73  {
74  $projStrings["###".$str->GetStringID()."###"] = $this->FixEntity($str->GetOriginal());
75 
76  $projStrings["###".$str->GetStringID()."t###"] = $this->FixEntity($str->GetTranslation());
77 /* if(preg_match('/\&/', $str->GetOriginal()))
78  {
79  echo( $str->GetOriginal()."\n");
80  echo(FixQuot($str->GetOriginal()."\n"));
81  }
82 */ }
83 
84  // XPath to all translation entries
85  $xpath = new DOMXPath($langXml);
86  $xpath->registerNamespace("m",$this->XmlNameSpace);
87  $entryNodes = $xpath->query("//m:translations/m:entry");
88 
89  // Loop on each string in theXML and replace variables
90  foreach ($entryNodes as $entry)
91  {
92  $origNodes = $xpath->query("m:string", $entry);
93  $transNodes = $xpath->query("m:translation", $entry);
94 
95  if($origNodes->item(0)->nodeValue == "")
96  {
97  echo "Empty Original node in the file:".$project->GetName().".\n";
98  }
99  else
100  {
101  // Replace ### node with the same strings hash key value
102  $origNodes->item(0)->nodeValue = $projStrings[$origNodes->item(0)->nodeValue];
103 
104  $transNodes->item(0)->nodeValue = $projStrings[$transNodes->item(0)->nodeValue];
105 
106  }
107  }
108  $xmlContent = $langXml->saveXML();
109  return $xmlContent;
110  }
111 
120  function FixEntity($text)
121  {
122  $s = str_replace("&", "&amp;", $text);
123  $s = str_replace("\"", "&quot;", $s); // !!!
124  $s = str_replace("<", "&lt;", $s);
125  $s = str_replace(">", "&gt;", $s);
126  return $s;
127  }
128 
138  private function ProcessRecursive($dir, ProjectIterator $projectIterator)
139  {
140  // Process each project in the iterator
141  foreach($projectIterator as $project)
142  {
143  // Completely ignore dead projects and their subprojects (older than root)
144  if($this->RootTimeStamp <= $project->GetLastSeen())
145  {
146  // Empty document means direcrory only.
147  if($project->GetIsFolder())
148  {
149  // Create a new dir and process the connected projects recursively.
150 
151  $newDir = $dir."/".$project->GetName();
152  if(!is_dir($newDir))
153  {
154  $ret = mkdir($newDir);
155  if(!$ret)
156  {
157  throw new Exception("Failed to create directory $newDir, no permission or disk full.");
158  }
159  }
160  if($projectIterator->hasChildren())
161  {
162  $this->ProcessRecursive($newDir, $projectIterator->getChildren());
163  }
164  }
165  else
166  {
167  // Save one project XML file.
168 
169  // Process XML files with exporter.
170  if(preg_match("/\.xml$/", $project->GetName()))
171  {
172  $export = $this->ExportProject($project);
173  }
174  else
175  {
176  // NonXML files, just resave original content.
177  $export = $project->GetDocument();
178  }
179 
180  $bytes = file_put_contents($dir."/".$project->GetName(), $export);
181  if($bytes != strlen($export))
182  {
183  throw new Exception("Failed to save ".$dir."/".$project->GetName().", no permission or disk full.");
184  }
185  if(!$this->SilentMode)
186  {
187  echo("Saved: ".$dir."/".$project->GetName()."\n");
188  }
189 
190  // Check if corresponding Xaraya file still exists
191  if($this->XarayaRoot != "")
192  {
193  $this->CheckCodeFiles($project);
194  }
195  }
196  }
197  }
198 
199  }
200 
209  private function CheckCodeFiles(TranslationProject $project)
210  {
211  // Only XML files need a corresponding code file
212  if(!preg_match("/\.xml$/", $project->GetName()))
213  {
214  return;
215  }
216 
217  // Loop back to root to find if this is a module,theme or core.
218  unset($parents);
219 
220  $pid = $project->GetID();
221  $localeID = $project->GetLocaleID();
222 
223  // Loop to parent
224  while($pid != 0)
225  {
226  $proj = ProjectIterator::$ProjectList[$localeID][$pid];
227  $parents[] = $proj;
228  $pid = $proj->GetParentID();
229  }
230 
231  // Not a module or theme file (too few dirs, e.g. core, which has no code file.)
232  if(count($parents) < 4)
233  {
234  return;
235  }
236 
237  // Build an array of directory path
238  unset($path);
239  $xmlPath = "";
240  for($i = 0; $i < count($parents); $i++)
241  {
242  $path[] = $parents[count($parents) - $i - 1]->GetName();
243 
244  // Path to XML locale file, for error message.
245  if($i > 0)
246  {
247  $xmlPath .= "/".$path[count($path)-1];
248  }
249  }
250 
251  // Skip "commmon.xml" and "fuzzy.xml" in module/theme root.
252  if(count($path) == 4 &&
253  ($path[count($path)-1] == "common.xml" || $path[count($path)-1] == "fuzzy.xml"))
254  {
255  return;
256  }
257 
258  $mainFolder = $path[1]; // "modules" or "themes"
259  $entityName = $path[2]; // module name or theme name.
260 
261  $codeFile = "";
262 
263  switch($mainFolder)
264  {
265  case "modules":
266 
267  // In the module folder almost everything is "xar".
268  $codeFile = "modules/".$entityName."/xar".$path[3];
269 
270  // Extension is xd in "xartemplates", otherwise php.
271  $fileExt = ".php";
272  if($path[3] == "templates")
273  {
274  $fileExt = ".xd";
275  }
276 
277  // Add subfolder/file names
278  for($i = 4; $i < count($path); $i++)
279  {
280  $codeFile .= "/".$path[$i];
281  }
282 
283  // Replace xml extension to php/xd
284  $codeFile = preg_replace("/\.xml$/", $fileExt, $codeFile);
285  break;
286 
287  case "themes":
288 
289  $codeFile = "themes/".$entityName;
290 
291  // Simply add subfolder/file names
292  for($i = 3; $i < count($path); $i++)
293  {
294  $codeFile .= "/".$path[$i];
295  }
296 
297  // Replace xml extension to xt
298  $codeFile = preg_replace("/\.xml$/", ".xt", $codeFile);
299  break;
300 
301  }
302 
303  if(!file_exists($this->XarayaRoot."/".$codeFile))
304  {
305  $this->LogError("No corresponding code file: $xmlPath [$codeFile]\n");
306  }
307  }
308 
313  private function LogError($msg)
314  {
315  $handle = fopen($this->ErrorLogFile, "a+");
316  fwrite($handle, $msg);
317  fclose($handle);
318  }
328  public function Export($localeName, $silentMode = false)
329  {
330  $this->SilentMode = $silentMode;
331 
332  $localeID = TranslationProject::$LocaleIDCache[$localeName];
333  if(empty($localeID))
334  {
335  throw new Exception("Export called with unknown locale name: $localeName");
336  }
337 
338  // Check export path and create locale dir
339  if(!is_dir(GetConfigVar('export_path')))
340  {
341  throw new Exception("Export path points to non-existing directory: ".GetConfigVar('export_path'));
342  }
343  $dir = GetConfigVar('export_path')."/".$localeName;
344  if(!is_dir($dir))
345  {
346  $ret = mkdir($dir, 0755);
347  if(!$ret)
348  {
349  throw new Exception("Failed to create directory $dir, no permission or disk full.");
350  }
351  }
352  $dir = GetConfigVar('export_path')."/".$localeName."/xml/";
353  if(!is_dir($dir))
354  {
355  $ret = mkdir($dir, 0755);
356  if(!$ret)
357  {
358  throw new Exception("Failed to create directory $dir, no permission or disk full.");
359  }
360  }
361 
362  // Set error logs file location
363  $this->ErrorLogFile = GetConfigVar('export_path')."/".$localeName."/xartm_errors.txt";
364  if(file_exists($this->ErrorLogFile))
365  {
366  // "Truncate" file
367  unlink($this->ErrorLogFile);
368  }
369  echo("Errors will be written to:".$this->ErrorLogFile."\n");
370 
371  // Xaraya root to find code files in it to check if a locale file is still valid
372  $this->XarayaRoot = GetConfigVar('export_path')."/../..";
373  if(!file_exists($this->XarayaRoot."/index.php"))
374  {
375  echo("No ".$this->ProjectName." root found:".$this->XarayaRoot."/index.php. Won't detect obsolete files into errorlog.\n");
376  $this->XarayaRoot = ""; // Not a xaraya, do not check for files.
377  }
378 
379  // Go exporting
380  $rootProject = new TranslationProject();
381  $rootProject->LoadRoot($this->ProjectName, $localeID);
382  $this->RootTimeStamp = $rootProject->GetLastSeen();
383 
384  // Work from database (no cache)
385  ProjectIterator::ClearCache($localeID);
386 
387  $allProjects = new ProjectIterator($rootProject->GetID(), $localeID, false);
388 
389  $this->ProcessRecursive($dir, $allProjects);
390  }
391 }
392 ?>
ProcessRecursive($dir, ProjectIterator $projectIterator)
CheckCodeFiles(TranslationProject $project)
& ExportProject(TranslationProject $project)
static ClearCache($localeID)
GetConfigVar($varName)
Definition: Common.php:84
Export($localeName, $silentMode=false)