Platform  3.1
PHP API documentation
 All Data Structures Namespaces Files Functions Variables Pages
Class.OOoLayout.php
Go to the documentation of this file.
1 <?php
2 /*
3  * @author Anakeen
4  * @license http://creativecommons.org/licenses/by-nc-sa/2.0/fr/ Anakeen - licence CC
5  * @package FDL
6 */
7 /**
8  * Layout Class for OOo files
9  *
10  * @author Anakeen 2000
11  * @version $Id: Class.OOoLayout.php,v 1.16 2008/10/31 17:01:18 eric Exp $
12  * @license http://creativecommons.org/licenses/by-nc-sa/2.0/fr/ Anakeen - licence CC
13  * @package FDL
14  * @subpackage CORE
15  */
16 /**
17  */
18 
19 include_once ('Class.Layout.php');
20 include_once ('Lib.FileMime.php');
21 
22 class OOoLayout extends Layout
23 {
24  //############################################
25  //#
26  private $strip = 'Y';
27  public $encoding = "utf-8";
28 
29  private $saved_sections = array();
30  private $added_images = array();
31  private $removed_images = array();
32  //########################################################################
33  //# Public methods
34  //#
35  //#
36  public $content_template = '';
37  public $style_template = '';
38  public $meta_template = '';
39  public $template = '';
40  public $manifest = '';
41 
42  protected $arrayKeys = array();
43  protected $arrayMainKeys = array();
44  /**
45  * construct layout for view card containt
46  *
47  * @param string $caneva file of the template
48  * @param Action $action current action
49  * @param Doc $doc document
50  */
51  function __construct($caneva = "", Action & $action = null, Doc & $doc = null)
52  {
53  $this->LOG = new Log("", "Layout");
54  $this->doc = $doc;
55  $this->template = "";
56  $this->action = & $action;
57  $this->generation = "";
58  $file = $caneva;
59  $this->file = "";
60  if ($caneva != "") {
61  if ((!file_exists($file)) && ($file[0] != '/')) {
62  $file = GetParam("CORE_PUBDIR") . "/$file"; // try absolute
63 
64  }
65  if (file_exists($file)) {
66  if (filesize($file) > 0) {
67  $this->odf2content($file);
68  $this->file = $file;
69  }
70  } else {
71  $this->template = "file [$caneva] not exists";
72  }
73  }
74  }
75 
76  function innerXML(DOMnode & $node)
77  {
78  if (!$node) return false;
79  $document = $node->ownerDocument;
80  $nodeAsString = $document->saveXML($node);
81  preg_match('!<.*?>(.*)</.*?>!s', $nodeAsString, $match);
82  return $match[1];
83  }
84  /**
85  * @deprecated
86  * Enter description here ...
87  * @param unknown_type $block
88  * @param unknown_type $aid
89  * @param unknown_type $vkey
90  */
91  function parseListInBlock($block, $aid, $vkey)
92  {
94  $head = '<?xml version="1.0" encoding="UTF-8"?>
95 <office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" office:version="1.0">';
96  $foot = '</office:document-content>';
97  $domblock = new DOMDocument();
98 
99  $block = trim($block);
100  if (substr($block, 0, 2) == '</') {
101  // fragment of block
102  $firsttag = strpos($block, '>');
103  $lasttag = strrpos($block, '<');
104  $frag1 = substr($block, 0, $firsttag + 1);
105  $frag2 = substr($block, $lasttag);
106  $block = substr($block, $firsttag + 1, $lasttag - strlen($block));
107  // print("\nfrag1:$frag1 $lasttag rag2:$frag2\n");
108  // print("\n====================\n");
109  //print("\nNB:[$block]\n");
110 
111  }
112 
113  if (!$domblock->loadXML($head . $block . $foot)) {
114  print "\n=============\n";
115  print $head . trim($block) . $foot;
116  return $block;
117  }
118 
119  $lists = $domblock->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "list");
120  foreach ($lists as $list) {
121  $items = $list->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "list-item");
122  if ($items->length > 0) {
123  $item = $items->item(0);
124 
125  if (preg_match('/\[V_[A-Z0-9_-]+\]/', $item->textContent, $reg)) {
126  $skey = $reg[0];
127  // print "serack key : [$skey] [$aid] [$vkey]";
128  if ($skey == $aid) {
129  // $vkey=$this->rkey[$key];
130  $tvkey = explode('<text:tab/>', $vkey);
131 
132  foreach ($tvkey as $key) {
133  $clone = $item->cloneNode(true);
134  $item->parentNode->appendChild($clone);
135  $this->replaceNodeText($clone, $reg[0], $key);
136  }
137  $item->parentNode->removeChild($item);
138  }
139  }
140  }
141  }
142  return $frag1 . $this->innerXML($domblock->firstChild) . $frag2;
143  return $frag1 . $domblock->saveXML($domblock->firstChild->firstChild) . $frag2;
144  }
145 
146  function getAncestor(&$node, $type)
147  {
148  $mynode = $node;
149  while (!empty($mynode->parentNode)) {
150  $mynode = $mynode->parentNode;
151  if ($mynode->tagName == $type) {
152  return $mynode;
153  }
154  }
155  return false;
156  }
157  /**
158  * get depth in dom tree
159  * @param DOMNode $node
160  */
161  private function getNodeDepth(DOMNode & $node)
162  {
163  $mynode = $node;
164  $depth = 0;
165  while (!empty($mynode->parentNode)) {
166  $depth++;
167  $mynode = $mynode->parentNode;
168  }
169  return $depth;
170  }
171  function ParseBlock()
172  {
173  $this->template = preg_replace("/(?m)\[BLOCK\s*([^\]]*)\](.*?)\[ENDBLOCK\s*\\1\]/se", "\$this->SetBlock('\\1','\\2')", $this->template);
174  }
175  /**
176  *
177  * @param string $name name of the IF
178  * @param string $block xml string which containt the condition
179  * @param boolean $not negative condition
180  * @param array $levelPath Path use to retrieve condition value in recursive repeatable mode
181  */
182  function TestIf($name, $block, $not = false, $levelPath = null)
183  {
184  $out = "";
185  $cond = null;
186  if ($levelPath) {
187  $val = $this->getArrayKeyValue($name, $levelPath);
188  if (is_array($val)) $val = null; // it is not the good level
189  if ($val !== null) $cond = ($val == true);
190  } else {
191  if ($this->rif[$name] !== null) $cond = ($this->rif[$name] == true);
192  elseif ($this->rkey[$name] !== null) $cond = ($this->rkey[$name] == true);
193  }
194  if ($cond !== null) {
195  if ($cond xor $not) {
196  $out = $block;
197  }
198  } else {
199  // return condition
200  if ($not) $out = "[IFNOT $name]" . $block . "[ENDIF $name]";
201  else $out = "[IF $name]" . $block . "[ENDIF $name]";
202  }
203  if ($this->strip == 'Y') $out = str_replace("\\\"", "\"", $out);
204  return ($out);
205  }
206  /**
207  * Top level parse condition
208  */
209  function ParseIf()
210  {
211  $templateori = '';
212  $level = 0;
213  //header('Content-type: text/xml; charset=utf-8');print $this->template;exit;
214  while ($templateori != $this->template && ($level < 10)) {
215  $templateori = $this->template;
216  $this->template = preg_replace("/(?m)\[IF(NOT)?\s*([^\]]*)\](.*?)\[ENDIF\s*\\2\]/se", "\$this->TestIf('\\2','\\3','\\1')", $this->template);
217  $level++; // to prevent infinite loop
218 
219  }
220  $this->fixSpanIf($this->template);
221  // header('Content-type: text/xml; charset=utf-8');print $this->template;exit;
222  // restore user fields
223  if (!$this->dom->loadXML($this->template)) {
224  print $this->template;
225  throw new Exception("Error in parse condition");
226  }
227  //header('Content-type: text/xml; charset=utf-8');print $this->dom->saveXML();exit;
228  $lists = $this->dom->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "user-field-get");
229 
230  $domElemsToRemove = array();
231  $domElemsToClean = array();
232 
233  foreach ($lists as $list) {
234  if (!$list->getAttribute('office:string-value')) {
235  if ($list->textContent == '') {
236  $domElemsToRemove[] = $list;
237  } else {
238  //$list->setAttribute("text:name",'');
239  $domElemsToClean[] = $list;
240  }
241  }
242  }
243  foreach ($domElemsToClean as $domElement) {
244  //$domElement->parentNode->nodeValue=$domElement->nodeValue;
245 
246  }
247  foreach ($domElemsToRemove as $domElement) {
248  $domElement->parentNode->removeChild($domElement);
249  }
250 
251  $this->template = $this->dom->saveXML();
252  }
253  /**
254  * to not parse user fields set
255  */
256  protected function hideUserFieldSet()
257  {
258  //$this->dom->loadXML($this->template);
259  $lists = $this->dom->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "user-field-decl");
260  foreach ($lists as $list) {
261  $list->setAttribute('office:string-value', str_replace('[', '-CROCHET-', $list->getAttribute('office:string-value')));
262  $list->setAttribute('text:name', str_replace('[', '-CROCHET-', $list->getAttribute('text:name')));
263  }
264  // detect user field to force it into a span
265  $lists = $this->dom->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "user-field-get");
266  foreach ($lists as $list) {
267  if ($list->parentNode->tagName != 'text:span') {
268  $nt = $this->dom->createElement("text:span");
269  $list->parentNode->insertBefore($nt, $list);
270  $nt->appendChild($list);
271  }
272  }
273  // header('Content-type: text/xml; charset=utf-8');print $this->dom->saveXML();exit;
274  $lists = $this->dom->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "user-field-get");
275  $userFields = array();
276  // set the key of fields to up
277  foreach ($lists as $list) {
278  $textContent = $list->nodeValue;
279  if (substr($textContent, 0, 1) == '[') {
280  $userFields[] = $list;
281  $nt = $this->dom->createTextNode($textContent);
282  $list->parentNode->insertBefore($nt, $list);
283  }
284  }
285  foreach ($userFields as $list) {
286  $list->parentNode->removeChild($list);
287  }
288 
289  $this->template = $this->dom->saveXML();
290  }
291  /**
292  * replace brackets
293  */
294  protected function restoreUserFieldSet()
295  {
296  // header('Content-type: text/xml; charset=utf-8');print $this->template;exit;
297  $this->dom->loadXML($this->template);
298  $lists = $this->dom->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "user-field-decl");
299  foreach ($lists as $list) {
300  $list->setAttribute('office:string-value', str_replace('-CROCHET-', '[', $list->getAttribute('office:string-value')));
301  $list->setAttribute('text:name', str_replace('-CROCHET-', '[', $list->getAttribute('text:name')));
302  }
303  $this->template = $this->dom->saveXML();
304  }
305  /**
306  * not use for the moment
307  * @deprecated
308  * @param $name
309  * @param $block
310  */
311  function SetBlock($name, $block)
312  {
314  if ($this->strip == 'Y') {
315  // $block = StripSlashes($block);
316  $block = str_replace("\\\"", "\"", $block);
317  }
318  $out = "";
319  if (isset($this->data) && isset($this->data["$name"]) && is_array($this->data["$name"])) {
320  foreach ($this->data["$name"] as $k => $v) {
321  $loc = $block;
322 
323  foreach ($this->corresp["$name"] as $k2 => $v2) {
324 
325  if (strstr($v[$v2], '<text:tab/>')) {
326  $loc = $this->parseListInBlock($loc, $k2, $v[$v2]);
327  } elseif ((!is_object($v[$v2])) && (!is_array($v[$v2]))) $loc = str_replace($k2, $v[$v2], $loc);
328  }
329  $this->rif = & $v;
330  // $this->ParseIf($loc);
331  $out.= $loc;
332  }
333  }
334  // $this->ParseBlock($out);
335  return ($out);
336  }
337  /**
338  * not use for the moment
339  * @deprecated
340  * @param $out
341  */
342  function ParseZone(&$out)
343  {
345 
346  $out = preg_replace("/\[ZONE\s*([^:]*):([^\]]*)\]/e", "\$this->execute('\\1','\\2')", $out);
347  }
348  /**
349  * replace simple key in xml string
350  */
351  function ParseKey()
352  {
353  if (isset($this->rkey)) {
354  $this->template = preg_replace($this->pkey, $this->rkey, $this->template);
355  }
356  }
357  private function ParseKeyXML()
358  {
359  if (isset($this->rkeyxml)) {
360 
361  $lists = $this->dom->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "p");
362  foreach ($this->rkeyxml as $k => $xmlkey) {
363  print "\n\nserach [$k]\n";
364  foreach ($lists as $list) {
365  $pstyle = $list->getAttribute("text:style-name");
366  $content = $this->dom->saveXML($list);
367 
368  if (strstr($content, "[$k]")) {
369  print "\n----------------\nfind C:$xmlkey $k:\n$content";
370  }
371  }
372  }
373  }
374  }
375  /**
376  * read odt file and insert xmls in object
377  * @param string $odsfile path to the odt file
378  */
379  function odf2content($odtfile)
380  {
381  if (!file_exists($odtfile)) return "file $odtfile not found";
382  $this->cibledir = uniqid(getTmpDir() . "/odf");
383 
384  $cmd = sprintf("unzip %s -d %s >/dev/null", escapeshellarg($odtfile) , escapeshellarg($this->cibledir));
385  system($cmd);
386 
387  $contentxml = $this->cibledir . "/content.xml";
388  if (file_exists($contentxml)) {
389  $this->content_template = file_get_contents($contentxml);
390  unlink($contentxml);
391  }
392  $contentxml = $this->cibledir . "/META-INF/manifest.xml";
393  if (file_exists($contentxml)) {
394  $this->manifest = file_get_contents($contentxml);
395  unlink($contentxml);
396  }
397  $contentxml = $this->cibledir . "/styles.xml";
398  if (file_exists($contentxml)) {
399  $this->style_template = file_get_contents($contentxml);
400  unlink($contentxml);
401  }
402  $contentxml = $this->cibledir . "/meta.xml";
403  if (file_exists($contentxml)) {
404  $this->meta_template = file_get_contents($contentxml);
405  unlink($contentxml);
406  }
407  }
408  /**
409  * recompose odt file
410  * @param string $odsfile output file path
411  */
413  {
414  if (file_exists($odsfile)) return "file $odsfile must not be present";
415 
416  $contentxml = $this->cibledir . "/content.xml";
417 
418  $this->content_template = preg_replace("!</?text:bookmark-(start|end)([^>]*)>!s", "", $this->content_template);
419  //$this->content_template=preg_replace("!<text:section>(\s*<text:p/>)+!s","<text:section>",$this->content_template);
420  //$this->content_template=preg_replace("!(<text:p/>\s*)+</text:section>!s","</text:section>",$this->content_template);
421  //$this->content_template=preg_replace("/<text:span([^>]*)>\s*<text:section>/s","<text:section>",$this->content_template);
422  //$this->content_template=preg_replace("/<\/text:section>\s*<\/text:span>/s","</text:section>",$this->content_template);
423  //$this->content_template=preg_replace("/<text:p([^>]*)>\s*<text:section([^>]*)>/s","<text:section\\2>",$this->content_template);
424  //$this->content_template=preg_replace("/<\/text:section>\s*<\/text:p>/s","</text:section>",$this->content_template);
425  //$this->content_template=preg_replace("/<text:p ([^>]*)>\s*<text:([^\/]*)\/>\s*<text:section[^>]*>/s","<text:section><text:\\2/>",$this->content_template);
426  //$this->content_template=preg_replace("/<\/text:section>\s*<text:([^\/]*)\/>\s*<\/text:p>/s","</text:section><text:\\1/>",$this->content_template);
427  //$this->content_template=preg_replace("/<table:table-cell ([^>]*)>\s*<text:section>/s","<table:table-cell \\1>",$this->content_template);
428  //$this->content_template=preg_replace("/<\/text:section>\s*<\/table:table-cell>/s","</table:table-cell>",$this->content_template);
429  $this->content_template = str_replace("&lt;text:line-break/&gt;", "<text:line-break/>", $this->content_template);
430  // header('Content-type: text/xml; charset=utf-8');print($this->content_template);exit;
431  file_put_contents($contentxml, $this->content_template);
432 
433  $contentxml = $this->cibledir . "/META-INF/manifest.xml";
434  file_put_contents($contentxml, $this->manifest);
435 
436  $contentxml = $this->cibledir . "/styles.xml";
437  file_put_contents($contentxml, $this->style_template);
438 
439  $contentxml = $this->cibledir . "/meta.xml";
440  file_put_contents($contentxml, $this->meta_template);
441 
442  $cmd = sprintf("cd %s;zip -q -Z store -X %s mimetype ;zip -q -r -X -u %s * && /bin/rm -fr %s", escapeshellarg($this->cibledir) , escapeshellarg($odsfile) , escapeshellarg($odsfile) , escapeshellarg($this->cibledir));
443 
444  system($cmd);
445  //rmdir($this->cibledir);
446 
447  }
448 
449  function execute($appname, $actionargn)
450  {
451 
452  if ($this->action == "") return ("Layout not used in a core environment");
453  // analyse action & its args
454  $actionargn = str_replace(":", "--", $actionargn); //For buggy function parse_url in PHP 4.3.1
455  $acturl = parse_url($actionargn);
456  $actionname = $acturl["path"];
457 
458  global $ZONE_ARGS;
459  $OLD_ZONE_ARGS = $ZONE_ARGS;
460  if (isset($acturl["query"])) {
461  $acturl["query"] = str_replace("--", ":", $acturl["query"]); //For buggy function parse_url in PHP 4.3.1
462  $zargs = explode("&", $acturl["query"]);
463  foreach ($zargs as $k => $v) {
464  if (preg_match("/([^=]*)=(.*)/", $v, $regs)) {
465  // memo zone args for next action execute
466  $ZONE_ARGS[$regs[1]] = urldecode($regs[2]);
467  }
468  }
469  }
470 
471  if ($appname != $this->action->parent->name) {
472  $appl = new Application();
473  $appl->Set($appname, $this->action->parent);
474  } else {
475  $appl = & $this->action->parent;
476  }
477 
478  if (($actionname != $this->action->name) || ($OLD_ZONE_ARGS != $ZONE_ARGS)) {
479  $act = new Action();
480 
481  if ($act->Exists($actionname, $appl->id)) {
482 
483  $res = $act->Set($actionname, $appl);
484  } else {
485  // it's a no-action zone (no ACL, cannot be call directly by URL)
486  $act->name = $actionname;
487 
488  $res = $act->CompleteSet($appl);
489  }
490  if ($res == "") {
491  $res = $act->execute();
492  }
493  $ZONE_ARGS = $OLD_ZONE_ARGS; // restore old zone args
494  return ($res);
495  } else {
496  return ("Fatal loop : $actionname is called in $actionname");
497  }
498  }
499  /**
500  * set key/value pair
501  * @param string $tag the key to replace
502  * @param string $val the value for the key
503  */
504  public function set($tag, $val)
505  {
506  if (!isUTF8($val)) $val = utf8_encode($val);
507  if (!$this->isXml($val)) {
508  $this->pkey[$tag] = "/\[$tag\]/";
509  if (is_array($val)) $val = implode('<text:tab/>', $val);
510  $this->rkey[$tag] = $val;
511  } else {
512 
513  $this->rkeyxml[$tag] = $val;
514  }
515  }
516  /**
517  * set key/value pair and XML entity encode
518  * @param string $tag the key to replace
519  * @param string $val the value for the key
520  */
521  public function eSet($tag, $val)
522  {
523  $this->set($tag, $this->xmlEntities($val));
524  }
525  /**
526  * replace entities & < >
527  * @param string $s text to encode
528  */
529  static public function xmlEntities($s)
530  {
531  return str_replace(array(
532  "&",
533  '<',
534  '>'
535  ) , array(
536  "&amp;",
537  '&lt;',
538  '&gt;'
539  ) , $s);
540  }
541  /**
542  *
543  * @param string $val
544  */
545  function isXML($val)
546  {
547  return false;
548  return preg_match("/<text:/", $val);
549  }
550  /**
551  * get value of $tag key
552  * @param string $tag
553  */
554  public function get($tag)
555  {
556  if (isset($this->rkey)) return $this->rkey[$tag];
557  return "";
558  }
559  /**
560  * parse text
561  */
562  protected function ParseText()
563  {
564  if ($this->encoding == "utf-8") bind_textdomain_codeset("what", 'UTF-8');
565  $this->template = preg_replace('/\[TEXT:([^\]]*)\]/e', "\$this->Text('\\1')", $this->template);
566  if ($this->encoding == "utf-8") bind_textdomain_codeset("what", 'ISO-8859-15'); // restore
567 
568  }
569  /**
570  *
571  */
572  protected function updateManifest()
573  {
574  $manifest = new DomDocument();
575  $manifest->loadXML($this->manifest);
576 
577  $manifest_root = null;
578  $items = $manifest->childNodes;
579  foreach ($items as $item) {
580  if ($item->nodeName == 'manifest:manifest') {
581  $manifest_root = $item;
582  break;
583  }
584  }
585  if ($manifest_root === null) {
586  return false;
587  }
588 
589  $items = $manifest->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0", "file-entry");
590  foreach ($items as $item) {
591  $type = $item->getAttribute("manifest:media-type");
592  if (substr($type, 0, 6) == "image/") {
593  $file = $item->getAttribute("manifest:full-path");
594  if (in_array($file, $this->removed_images)) {
595  $item->parentNode->removeChild($item);
596  }
597  }
598  }
599  foreach ($this->added_images as $image) {
600  $mime = getSysMimeFile($this->cibledir . '/' . $image);
601  $new = $manifest->createElement("manifest:file-entry");
602  $new->setAttribute("manifest:media-type", $mime);
603  $new->setAttribute("manifest:full-path", $image);
604  $manifest_root->appendChild($new);
605  }
606 
607  $this->manifest = $manifest->saveXML();
608  }
609  /**
610  * set image
611  * @param DomNode $node
612  * @param string $name
613  * @param string $value
614  */
615  protected function setDraw(DOMNode & $draw, $name, $file)
616  {
617  if (strpos($file, '<text:tab') !== false) {
618  return 'muliple values : fail';
619  }
620  $imgs = $draw->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:drawing:1.0", "image");
621  $err = "";
622  if ($imgs->length > 0) {
623  $img = $imgs->item(0);
624  if (file_exists($file)) {
625  $draw->setAttribute('draw:name', substr($name, 2) . ' ' . uniqid() . mt_rand(1000, 9999));
626  $href = 'Pictures/dcp' . uniqid() . mt_rand(1000000, 9999999) . substr($img->getAttribute('xlink:href') , -9);
627  $img->setAttribute('xlink:href', $href);
628  $this->added_images[] = $href;
629  if (!copy($file, $this->cibledir . '/' . $href)) {
630  $err = "copy fail";
631  }
632 
633  if ($err == "") { // need to respect image proportion
634  $width = $draw->getAttribute('svg:width');
635  $size = getimagesize($file);
636  $unit = "";
637  if (preg_match('/[0-9\.]+(.*)$/', $width, $reg)) $unit = $reg[1];
638  $height = sprintf("%.03f%s", (doubleval($width) / $size[0]) * $size[1], $unit);
639  $draw->setAttribute('svg:height', $height);
640  }
641  }
642  }
643  return $err;
644  }
645  /**
646  * parse images
647  */
648  protected function parseDraw()
649  {
650  $draws = $this->dom->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:drawing:1.0", "frame");
651  foreach ($draws as $draw) {
652  $name = trim($draw->getAttribute('draw:name'));
653  if (preg_match('/\[(V_[A-Z0-9_-]+)\]/', $name, $reg)) {
654  $key = $reg[1];
655  if (isset($this->rkey[$key])) {
656  $this->setDraw($draw, $key, $this->rkey[$key]);
657  }
658  }
659  }
660  }
661  /**
662  * remove all xml:id attributes in children nodes
663  * @param DomNode $objNode
664  */
665  protected function removeXmlId(&$objNode)
666  {
667  $objNodeListNested = $objNode->childNodes;
668  foreach ($objNodeListNested as $objNodeNested) {
669  if ($objNodeNested->nodeType == XML_ELEMENT_NODE) {
670  $objNodeNested->removeAttribute("xml:id");
671  $this->removeXmlId($objNodeNested);
672  }
673  }
674  }
675  /**
676  * This function replaces a node's string content with strNewContent
677  * @param DomNode $objNode
678  * @param string $strOldContent
679  * @param string $strNewContent
680  */
681  protected function replaceNodeText(DOMNode & $objNode, $strOldContent, $strNewContent)
682  {
683  if ($strNewContent === null) return;
684  if (is_array($strNewContent)) {
685  throw new Exception("node replacement must be a string : array found");
686  }
687  $objNodeListNested = & $objNode->childNodes;
688  foreach ($objNodeListNested as $objNodeNested) {
689  if ($objNodeNested->nodeType == XML_TEXT_NODE) {
690  if ($objNodeNested->nodeValue != "") {
691  if (strpos($strNewContent, '<text:p>') !== false) {
692  $strNewContent = str_replace('<', '--Lower.Than--', $strNewContent);
693  $strNewContent = str_replace('>', '--Greater.Than--', $strNewContent);
694  $strNewContent = htmlspecialchars_decode($strNewContent);
695  }
696  $objNodeNested->nodeValue = str_replace($strOldContent, $strNewContent, $objNodeNested->nodeValue);
697  }
698  } elseif ($objNodeNested->nodeType == XML_ELEMENT_NODE) {
699  if ($objNodeNested->nodeName == 'text:text-input') {
700  $name = $objNodeNested->getAttribute('text:description');
701  if ($name == $strOldContent) {
702  $this->setInputField($objNodeNested, substr($name, 1, -1) , $strNewContent);
703  }
704  } elseif ($objNodeNested->nodeName == 'text:drop-down') {
705  $name = $objNodeNested->getAttribute('text:name');
706  if ($name == $strOldContent) {
707  $this->setDropDownField($objNodeNested, substr($name, 1, -1) , $strNewContent);
708  }
709  } elseif ($objNodeNested->nodeName == 'draw:frame') {
710  $name = $objNodeNested->getAttribute('draw:name');
711  if (substr($name, 0, strlen($strOldContent)) == $strOldContent) {
712  $this->setDraw($objNodeNested, substr($strOldContent, 1, -1) , $strNewContent);
713  }
714  } else {
715  $this->replaceNodeText($objNodeNested, $strOldContent, $strNewContent);
716  }
717  }
718  }
719  }
720  /**
721  * parse bullet lists
722  */
723  protected function parseListItem()
724  {
725  $err = '';
726  $lists = $this->dom->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "list");
727  foreach ($lists as $list) {
728  $items = $list->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "list-item");
729  if ($items->length > 0) {
730  $item = $items->item(0);
731  $skey = implode('|', array_keys($this->arrayMainKeys));
732  if (preg_match_all("/\[($skey)\]/", $this->innerXML($list) , $reg)) {
733  $reg0 = $reg[0];
734  $tvkey = array();
735  $maxk = 0;
736  foreach ($reg0 as $k => $v) {
737  $key = substr(trim($v) , 1, -1);
738  $tvkey[$key] = $this->arrayMainKeys[$key];
739  $maxk = max(count($tvkey[$key]) , $maxk);
740  }
741  if ($maxk > 0) {
742  for ($i = 0; $i < $maxk; $i++) {
743  $clone = $item->cloneNode(true);
744  $item->parentNode->appendChild($clone);
745  foreach ($tvkey as $kk => $key) {
746  $this->replaceNodeText($clone, "[$kk]", $key[$i]);
747  }
748  }
749  }
750  $item->parentNode->removeChild($item);
751  }
752  }
753  }
754  return $err;
755  }
756 
757  private function _section_cmp($k1, $k2)
758  {
759  if ($k2 > $k1) return 1;
760  else if ($k2 < $k1) return -1;
761  return 0;
762  }
763  /**
764  * parse section repeat
765  */
766  protected function parseSection()
767  {
768 
769  $lists = $this->dom->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "section");
770  $ks = 0;
771  $section = array();
772  // need to inpect section with max depth before to avoid et repeat top level section
773  foreach ($lists as $item) {
774  $depth = $this->getNodeDepth($item);
775  $section[($depth * 200) + $ks] = $item;
776  $ks++;
777  }
778  //reorder by depth
779  uksort($section, array(
780  $this,
781  "_section_cmp"
782  ));
783 
784  foreach ($section as $item) {
785 
786  $skey = implode('|', array_keys($this->arrayMainKeys));
787  if (preg_match_all("/\[($skey)\]/", $this->innerXML($item) , $reg)) {
788  $reg0 = $reg[0];
789  $tvkey = array();
790  $maxk = 0;
791  foreach ($reg0 as $k => $v) {
792  $key = substr(trim($v) , 1, -1);
793  $tvkey[$key] = $this->arrayMainKeys[$key];
794  $maxk = max(count($tvkey[$key]) , $maxk);
795  }
796  if ($maxk > 0) {
797  for ($i = 0; $i < $maxk; $i++) {
798  $clone = $item->cloneNode(true);
799  $item->parentNode->insertBefore($clone, $item);
800  foreach ($tvkey as $kk => $key) {
801  $this->replaceNodeText($clone, "[$kk]", $key[$i]);
802  }
803  $this->replaceRowIf($clone, array(
804  $i
805  )); // main level
806  $this->replaceRowNode($clone, array(
807  $i
808  )); // inspect sub levels
809 
810  }
811  }
812  $item->parentNode->removeChild($item);
813  }
814  }
815  }
816  /**
817  * modify a text:input field value
818  *
819  * @param DomNode $node
820  * @param string $name
821  * @param string $value
822  */
823  protected function setInputField(DOMNode & $node, $name, $value)
824  {
825  if (strpos($file, '<text:tab') !== false) {
826  return 'muliple values : fail';
827  }
828  $node->nodeValue = $value;
829  $node->setAttribute("text:description", '[PP' . $name . 'PP]');
830  }
831  /**
832  * modify a text:drop-down list
833  *
834  * @param DomNode$node
835  * @param string $name
836  * @param string $value
837  */
838  protected function setDropDownField(DOMNode & $node, $name, $value)
839  {
840  if (strpos($file, '<text:tab') !== false) {
841  return 'muliple values : fail';
842  }
843  $this->removeAllChilds($node);
844  $node->setAttribute("text:name", '[PP' . $name . 'PP]');
845  $value = str_replace(array(
846  "&lt;",
847  "&gt;",
848  "&amp;"
849  ) , array(
850  "<",
851  ">",
852  '&'
853  ) , $value);
854  $item = new DOMElement('text:label', '', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0');
855  $item = $node->appendChild($item);
856  $item->setAttribute("text:current-selected", 'true');
857  $item->setAttribute("text:value", $value);
858  $node->appendChild(new DOMText($value));
859  }
860  /**
861  * remove all child nodes
862  *
863  * @param DomNode $objNode
864  */
865  protected function removeAllChilds(DOMNode & $objNode)
866  {
867  $objNodeListNested = $objNode->childNodes;
868  $objNode->nodeValue = '';
869  if (!empty($objNodeListNested) && $objNodeListNested->length > 0) {
870  foreach ($objNodeListNested as $objNodeNested) {
871  $objNode->removeChild($objNodeNested);
872  }
873  }
874  }
875  /**
876  * parse tables
877  */
878  protected function parseTableRow()
879  {
880  $err = '';
881  $lists = $this->dom->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:table:1.0", "table-row");
882  $validRow = array();
883 
884  $skey = implode('|', array_keys($this->arrayMainKeys));
885  foreach ($lists as $rowItem) {
886  if (preg_match("/\[($skey)\]/", $this->innerXML($rowItem) , $reg)) {
887  $validRow[] = $rowItem;
888  }
889  }
890  foreach ($validRow as $rowItem) {
891  if (preg_match_all("/\[($skey)\]/", $this->innerXML($rowItem) , $reg)) {
892  $reg0 = $reg[0];
893  $tvkey = array();
894  $maxk = 0; // search values which has the greatest number of values
895  foreach ($reg0 as $k => $v) {
896  $key = substr(trim($v) , 1, -1);
897  $tvkey[$key] = $this->arrayMainKeys[$key];
898  $maxk = max(count($tvkey[$key]) , $maxk);
899  }
900  if ($maxk > 0) {
901  for ($i = 0; $i < $maxk; $i++) {
902  $clone = $rowItem->cloneNode(true);
903  $rowItem->parentNode->insertBefore($clone, $rowItem);
904  foreach ($tvkey as $kk => $key) {
905  $this->replaceNodeText($clone, "[$kk]", $key[$i]);
906  }
907  $this->replaceRowIf($clone, array(
908  $i
909  )); // main level
910  $this->replaceRowNode($clone, array(
911  $i
912  )); // inspect sub levels
913 
914  }
915  }
916 
917  $rowItem->parentNode->removeChild($rowItem);
918  }
919  }
920  return $err;
921  }
922  /**
923  * return the number of array in arrays
924  * @param array $v
925  */
926  private static function getArrayDepth($v)
927  {
928  $depth = - 1;
929  while (is_array($v)) {
930  $depth++;
931  $v = current($v);
932  }
933  return $depth;
934  }
935  /**
936  *
937  * Retrieve one of values for a multi value key
938  * @param string $key the key name (multiple values)
939  * @param array $levelPath path to access of a particular value
940  */
941  protected function getArrayKeyValue($key, array $levelPath)
942  {
943  $value = null;
944 
945  if (count($levelPath) == 1) {
946  if (isset($this->arrayMainKeys[$key])) {
947  $index = current($levelPath);
948  return $this->arrayMainKeys[$key][$index];
949  }
950  }
951  if (!isset($this->arrayKeys[$key])) return null;
952 
953  $value = $this->arrayKeys[$key];
954  foreach ($levelPath as $index) {
955  $value = $value[$index];
956  }
957 
958  return $value;
959  }
960  /**
961  * fix span cause when IF/ENDIF are not on the same depth
962  * @param $s
963  */
964  private function fixSpanIf(&$s)
965  {
966  $s = preg_replace('/<text:span ([^>]*)>\s*<\/text:p>/s', "</text:p>", $s);
967  $s = preg_replace('/<text:p ([^>]*)>\s*<\/text:span>/s', "<text:p \\1>", $s);
968  }
969  /**
970  *
971  * Inspect conditions in cells
972  * @param string_type $row
973  * @param array $levelPath
974  */
975  protected function replaceRowIf(DOMNode & $row, array $levelPath)
976  {
977 
978  $this->removeXmlId($row);
979  $inner = $row->ownerDocument->saveXML($row);
980 
981  $head = '<?xml version="1.0" encoding="UTF-8"?><office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" office:version="1.0">';
982  $foot = '</office:document-content>';
983 
984  $level = 0;
985  while ($level < 10) {
986  $replacement = preg_replace("/(?m)\[IF(NOT)?\s*([^\]]*)\](.*?)\[ENDIF\s*\\2\]/se", "\$this->TestIf('\\2','\\3','\\1',\$levelPath)", $inner);
987  if ($inner == $replacement) break;
988  else $inner = $replacement;
989  $level++;
990  }
991  $this->fixSpanIf($replacement);
992 
993  $dxml = new DomDocument();
994 
995  $dxml->loadXML($head . $replacement . $foot);
996  $ot = $dxml->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0", "document-content");
997  if ($ot->length > 0) {
998  $newnode = $this->dom->importNode($ot->item(0)->firstChild, true);
999  // copy inside
1000  // first delete inside
1001  while ($row->firstChild) {
1002  $row->removeChild($row->firstChild);
1003  }
1004  // move to
1005  while ($newnode->firstChild) {
1006  $row->appendChild($newnode->firstChild);
1007  }
1008  }
1009  }
1010  /**
1011  *
1012  * Inspect sub tables
1013  * @param string_type $row
1014  * @param array $levelPath
1015  */
1016  protected function replaceRowNode(DOMNode & $row, array $levelPath)
1017  {
1018  // Inspect sub tables, rows
1019  $this->replaceRowSomething($row, $levelPath, "table", "table-row", true);
1020  // Inspect list in sub tables
1021  $this->replaceRowSomething($row, $levelPath, "text", "list-item", false);
1022  // Inspect list in subsection
1023  $this->replaceRowSomething($row, $levelPath, "text", "section", true);
1024  }
1025  /**
1026  *
1027  * Inspect list in sub tables
1028  * @param DOMNode $row
1029  * @param array $levelPath
1030  * @param string $ns namespace for filter items (like text or table)
1031  * @param string $tag tag for filter (like table-row or list-item)
1032  * @param boolean $recursive recursive mode
1033  */
1034  protected function replaceRowSomething(DOMNode & $row, array $levelPath, $ns, $tag, $recursive)
1035  {
1036  if (count($this->arrayKeys) == 0) return; // nothing to do
1037  $keys = array();
1038  $subIndex = count($levelPath);
1039  foreach ($this->arrayKeys as $k => $v) {
1040  if ($this->getArrayDepth($v) == $subIndex) $keys[] = $k;
1041  }
1042 
1043  $rowList = $row->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:$ns:1.0", $tag);
1044  if ($rowList->length > 0) {
1045  $skey = implode('|', $keys);
1046  /* print "<h1>$tag</h1>";
1047  print_r2($levelPath);
1048  print_r2($keys);*/
1049  foreach ($rowList as $item) {
1050  if (preg_match_all("/\[($skey)\]/", $this->innerXML($item) , $reg)) {
1051 
1052  $maxk = 0;
1053  foreach ($reg[1] as $k => $v) {
1054  $vkey = $this->getArrayKeyValue($v, $levelPath);
1055  $tvkey[$v] = $vkey;
1056  $maxk = max(count($tvkey[$v]) , $maxk);
1057  }
1058 
1059  if ($maxk > 0) {
1060  for ($i = 0; $i < $maxk; $i++) {
1061  $clone = $item->cloneNode(true);
1062  $item->parentNode->appendChild($clone);
1063  foreach ($tvkey as $kk => $key) {
1064  $this->replaceNodeText($clone, "[$kk]", $key[$i]);
1065  }
1066  $newPath = array_merge($levelPath, array(
1067  $i
1068  ));
1069  $this->replaceRowIf($clone, $newPath);
1070  //if ($recursive) $this->replaceRowSomething($clone,$newPath,$ns,$tag,$recursive);
1071  if ($recursive) $this->replaceRowNode($clone, $newPath);
1072  }
1073  }
1074  $item->parentNode->removeChild($item);
1075  }
1076  }
1077  }
1078  }
1079  /**
1080  * parse text:input
1081  */
1082  protected function parseInput()
1083  {
1084  $lists = $this->dom->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "text-input");
1085  foreach ($lists as $list) {
1086  $name = $list->getAttribute("text:description");
1087  if (preg_match('/\[(V_[A-Z0-9_-]+)\]/', $name, $reg)) {
1088  $key = $reg[1];
1089  if (isset($this->rkey[$key])) {
1090  $this->setInputField($list, $key, $this->rkey[$key]);
1091  }
1092  }
1093  }
1094  }
1095  /**
1096  * parse text:drop-down
1097  */
1098  protected function parseDropDown()
1099  {
1100  $lists = $this->dom->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "drop-down");
1101  foreach ($lists as $list) {
1102  $name = $list->getAttribute("text:name");
1103  if (preg_match('/\[(V_[A-Z0-9_-]+)\]/', $name, $reg)) {
1104  $key = $reg[1];
1105  if (isset($this->rkey[$key])) {
1106  $this->setDropDownField($list, $key, $this->rkey[$key]);
1107  }
1108  }
1109  }
1110  }
1111  /**
1112  * restore protected values
1113  */
1114  protected function restoreProtectedValues()
1115  {
1116  $this->template = preg_replace('/\[PP(V_[A-Z0-9_]+)PP\]/s', '[$1]', $this->template);
1117  $this->template = str_replace('--Lower.Than--', '<', $this->template);
1118  $this->template = str_replace('--Greater.Than--', '>', $this->template);
1119  }
1120  /**
1121  * parse section and clone "tpl_xxx" sections into saved_sections
1122  */
1123  protected function parseTplSection()
1124  {
1125  $this->saved_sections = array();
1126  // remove old generated sections
1127  $lists = $this->dom->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "section");
1128  foreach ($lists as $list) {
1129  $name = $list->getAttribute("text:name");
1130  if (substr($name, 0, 5) == '_tpl_') {
1131  $list->parentNode->removeChild($list);
1132  }
1133  }
1134  // clone sections and generate them again
1135  $lists = $this->dom->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "section");
1136  foreach ($lists as $list) {
1137  $name = $list->getAttribute("text:name");
1138  if (substr($name, 0, 4) == 'tpl_') {
1139  $this->removeXmlId($list);
1140  // restore original style name of first head
1141  $heads = $list->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "h");
1142  if ($heads->length > 0) {
1143  $firsthead = $heads->item(0);
1144  $firsthead->setAttribute("text:style-name", trim($firsthead->getAttribute('text:style-name') , '_'));
1145  }
1146  $originSection = $this->saved_sections[$name] = $list->cloneNode(true);
1147  $list->setAttribute("text:name", '_' . $name);
1148  $list->setAttribute("text:protected", 'true');
1149  $list->setAttribute("text:display", 'true');
1150  // // special treatment to have correct chapter numeration search first header
1151  $heads = $originSection->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "h");
1152  if ($heads->length > 0) {
1153  $firsthead = $heads->item(0);
1154  $styleName = $firsthead->getAttribute("text:style-name");
1155  if ($styleName) {
1156  $styleName = trim($styleName, '_');
1157  $styles = $this->dom->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:style:1.0", "style");
1158  //addLogMsg(array("style"=>$styleName,"length"=>$styles->length));
1159  $tStyleName = array();
1160  foreach ($styles as $style) {
1161 
1162  $aStyleName = $style->getAttribute("style:name");
1163  $tStyleName[] = $aStyleName;
1164  if ($aStyleName == $styleName) {
1165  $copyName = '_' . $styleName . '_';
1166  if (!(in_array($copyName, $tStyleName))) {
1167  $cloneStyle = $style->cloneNode(true);
1168  $cloneStyle->setAttribute("style:name", $copyName);
1169  $cloneStyle->setAttribute("style:list-style-name", ""); // unset numeration chapter
1170  $style->parentNode->insertBefore($cloneStyle, $style);
1171  }
1172  $firsthead->setAttribute("text:style-name", $copyName);
1173  break;
1174  }
1175  }
1176  }
1177  }
1178  }
1179  }
1180  }
1181  /**
1182  * restore cloned and saved sections at the end
1183  */
1184  protected function restoreSection()
1185  {
1186 
1187  $inserts_to_do = array();
1188 
1189  $lists = $this->dom->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "section");
1190  foreach ($lists as $list) {
1191  $name = $list->getAttribute("text:name");
1192  if (substr($name, 0, 5) == '_tpl_' && isset($this->saved_sections[substr($name, 1) ])) {
1193  $node = $this->dom->importNode($this->saved_sections[substr($name, 1) ], true);
1194  $inserts_to_do[] = array(
1195  $node,
1196  $list
1197  );
1198  }
1199  }
1200  foreach ($inserts_to_do as $insert_to_do) {
1201  //$node = $insert_to_do[1]->parentNode->insertBefore($insert_to_do[0], $insert_to_do[1]);
1202  // insert after
1203  if ($insert_to_do[1]->nextSibling) {
1204  $node = $insert_to_do[1]->parentNode->insertBefore($insert_to_do[0], $insert_to_do[1]->nextSibling);
1205  } else {
1206  $node = $insert_to_do[1]->parentNode->appenChild($insert_to_do[0]);
1207  }
1208  $node->setAttribute("text:protected", 'true');
1209  $node->setAttribute("text:display", 'none');
1210  }
1211  }
1212  /**
1213  * Initialize of list
1214  * @param string $key the key variable
1215  * @param array $t the values of the key
1216  */
1217  public function setColumn($key, array $t)
1218  {
1219  $ti = array();
1220  $k = 0;
1221  foreach ($t as $v) $ti[$k++] = $v; // delete associative keys
1222  if (is_array(current($t))) $this->setArray($key, $ti);
1223  else $this->arrayMainKeys[$key] = $ti;
1224  //else $this->set($key,implode('<text:tab/>',$t));
1225 
1226  }
1227 
1228  protected function setArray($key, array $t)
1229  {
1230  if (!$key) throw new Exception('Key must not be empty');
1231  $this->arrayKeys[$key] = $t;
1232  }
1233  /**
1234  * @deprecated
1235  * @param unknown_type $p_nom_block
1236  * @param unknown_type $data
1237  */
1238  public function SetBlockData($p_nom_block, $data)
1239  {
1241  if ($p_nom_block != "") {
1242  if ($data != null && $this->encoding == "utf-8") {
1243  if (is_array($data)) {
1244  foreach ($data as $k => $v) {
1245  foreach ($v as $kk => $vk) {
1246  if (!isUTF8($vk)) {
1247  $data[$k][$kk] = utf8_encode($vk);
1248  }
1249  }
1250  }
1251  } else {
1252  if (!isUTF8($data)) $data = utf8_encode($data);
1253  }
1254  }
1255  $this->data[$p_nom_block] = $data;
1256  if (is_array($data)) {
1257  reset($data);
1258  $elem = pos($data);
1259  if (isset($elem) && is_array($elem)) {
1260  reset($elem);
1261  while (list($k, $v) = each($elem)) {
1262  if (!isset($this->corresp["$p_nom_block"]["[$k]"])) {
1263  $this->SetBlockCorresp($p_nom_block, $k);
1264  }
1265  }
1266  }
1267  }
1268  } else {
1269  $this->setrepeatable($data);
1270  }
1271  }
1272  /*
1273  * set array to be use in repeat set like table, list-item or section
1274  * @param array $data the arry to set
1275  */
1276  public function setrepeatable(array $data)
1277  {
1278 
1279  $t = array();
1280  if (is_array($data)) {
1281  foreach ($data as $k => $v) {
1282  foreach ($v as $ki => $vi) {
1283  $t[$ki][$k] = $vi;
1284  }
1285  }
1286  }
1287  // fill array
1288  $max = 0;
1289  foreach ($t as $k => $v) {
1290  if (count($v) > $max) $max = count($v);
1291  }
1292  foreach ($t as $k => $v) {
1293  if (count($v) < $max) {
1294  $fill = array_fill(0, $max, '');
1295  foreach ($v as $idx => $vi) {
1296  $fill[$idx] = $vi;
1297  }
1298  $t[$k] = $fill;
1299  }
1300  }
1301  // affect completed columns
1302  foreach ($t as $k => $v) {
1303  $this->setColumn($k, $v);
1304  }
1305  }
1306 
1307  protected function addHTMLStyle()
1308  {
1309  $xmldata = '<xhtml:html xmlns:xhtml="http://www.w3.org/1999/xhtml">' . "</xhtml:html>";
1310 
1311  $xslt = new xsltProcessor;
1312  $xslt->importStyleSheet(DomDocument::load(DEFAULT_PUBDIR . "/CORE/Layout/html2odt.xsl"));
1313  $xmlout = $xslt->transformToXML(DomDocument::loadXML($xmldata));
1314 
1315  $dxml = new DomDocument();
1316  $dxml->loadXML($xmlout);
1317  $ot = $dxml->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0", "automatic-styles");
1318  $ot1 = $ot->item(0);
1319  $ass = $this->dom->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0", "automatic-styles");
1320 
1321  $ass0 = $ass->item(0);
1322  foreach ($ot1->childNodes as $ots) {
1323  $c = $this->dom->importNode($ots, true);
1324  $ass0->appendChild($c);
1325  }
1326  }
1327  /**
1328  * Delete not used images (need when reuse template where repeat section)
1329  */
1330  protected function removeOrphanImages()
1331  {
1332  $imgs = $this->dom->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:drawing:1.0", "image");
1333  $used_images = array();
1334  foreach ($imgs as $img) {
1335  $href = basename($img->getAttribute('xlink:href'));
1336  if (substr($href, 0, 7) == 'dcp') {
1337  $used_images[] = $href;
1338  }
1339  }
1340  $files = glob($this->cibledir . '/Pictures/dcp*');
1341  if (is_array($files)) {
1342  foreach ($files as $file) {
1343  if (!in_array(basename($file) , $used_images)) {
1344  $this->removed_images[] = 'Pictures/' . basename($file);
1345  @unlink($file);
1346  }
1347  }
1348  }
1349  }
1350 
1351  function GenJsRef()
1352  {
1353  return "";
1354  }
1355  function GenJsCode($showlog)
1356  {
1357  return ("");
1358  }
1359  function ParseJs(&$out)
1360  {
1361  }
1362  function GenCssRef()
1363  {
1364  return "";
1365  }
1366  function GenCssCode()
1367  {
1368  return ("");
1369  }
1370  function ParseCss(&$out)
1371  {
1372  }
1373  /**
1374  * generate OOo document style part
1375  */
1376  protected function genStyle()
1377  {
1378 
1379  $this->dom = new DOMDocument();
1380 
1381  $this->dom->loadXML($this->style_template);
1382  if ($this->dom) {
1383  $this->template = $this->style_template;
1384 
1385  $this->parseDraw();
1386  $this->template = $this->dom->saveXML();
1387 
1388  $this->hideUserFieldSet();
1389  $this->ParseIf();
1390  $this->ParseKey();
1391  $this->ParseText();
1392  $this->restoreUserFieldSet();
1393  $this->style_template = $this->template;
1394  }
1395  }
1396  /**
1397  * generate OOo document meta part
1398  */
1399  protected function genMeta()
1400  {
1401  $this->dom = new DOMDocument();
1402 
1403  $this->dom->loadXML($this->meta_template);
1404  if ($this->dom) {
1405  $this->template = $this->meta_template;
1406 
1407  $this->ParseIf();
1408  $this->ParseKey();
1409  $this->ParseText();
1410 
1411  $this->meta_template = $this->template;
1412  }
1413  }
1414  /**
1415  * clean section done by htmltext values
1416  * delete unecessary span or p
1417  * delete section tag if needed (not in cell or text:section or text:body
1418  */
1419  public function parseHtmlText()
1420  {
1421  $this->dom->loadXML($this->template);
1422  $lists = $this->dom->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "section");
1423  $htmlSections = array();
1424  $htmlCleanSections = array();
1425 
1426  foreach ($lists as $list) {
1427  $aid = $list->getAttribute("aid");
1428  if ($aid) {
1429  if ($list->parentNode->childNodes->length == 1) {
1430  $htmlSections[] = $list;
1431  } else {
1432  // remove section
1433  $htmlCleanSections[] = $list;
1434  }
1435  $list->removeAttribute("aid");
1436  }
1437  }
1438 
1439  foreach ($htmlSections as $htmlSection) {
1440  $pParentHtml = $htmlSection->parentNode->parentNode;
1441  $parentHtml = $htmlSection->parentNode;
1442 
1443  if ($parentHtml->nextSibling) {
1444  $pParentHtml->insertBefore($htmlSection, $parentHtml->nextSibling);
1445  } else {
1446  $pParentHtml->appendChild($htmlSection);
1447  }
1448  $pParentHtml->removeChild($parentHtml);
1449  // double up
1450  $pParentHtml = $htmlSection->parentNode->parentNode;
1451  $parentHtml = $htmlSection->parentNode;
1452 
1453  if (($parentHtml->nodeName == "text:p") && ($parentHtml->childNodes->length == 1)) {
1454 
1455  if ($parentHtml->nextSibling) {
1456  $pParentHtml->insertBefore($htmlSection, $parentHtml->nextSibling);
1457  } else {
1458  $pParentHtml->appendChild($htmlSection);
1459  }
1460  $pParentHtml->removeChild($parentHtml);
1461  } else {
1462  $htmlCleanSections[] = $htmlSection;
1463  }
1464  if (in_array($htmlSection->parentNode->nodeName, array(
1465  "text:list-item",
1466  "draw:text-box",
1467  "text:p"
1468  ))) {
1469  $htmlCleanSections[] = $htmlSection;
1470  }
1471  //print "Parent Node is ".$htmlSection->parentNode->nodeName."\n";
1472 
1473  }
1474  foreach ($htmlCleanSections as $htmlSection) {
1475  if ($htmlSection->parentNode->nodeName == "text:p") {
1476  $nbp = $htmlSection->getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "p")->length;
1477  }
1478  while ($htmlSection->childNodes->length > 0) {
1479  if (($htmlSection->parentNode->nodeName == "text:p") && ($nbp > 1)) {
1480  if ($htmlSection->firstChild->nodeName == "text:p") {
1481  $htmlSection->firstChild->appendChild($this->dom->createElement("text:line-break"));
1482  $nbp--;
1483  }
1484  }
1485  $htmlSection->parentNode->insertBefore($htmlSection->firstChild, $htmlSection);
1486  }
1487  }
1488  $this->template = $this->dom->saveXML();
1489  }
1490  /**
1491  * generate OOo document content
1492  */
1493  protected function genContent()
1494  {
1495 
1496  $this->dom = new DOMDocument();
1497 
1498  $this->dom->loadXML($this->content_template);
1499  if ($this->dom) {
1500  $this->template = $this->content_template;
1501  $this->parseTplSection();
1502  //header('Content-type: text/xml; charset=utf-8');print $this->dom->saveXML();exit;
1503  $this->hideUserFieldSet();
1504  $this->parseTableRow();
1505 
1506  $this->parseSection();
1507  $this->parseListItem();
1508  $this->parseDraw();
1509 
1510  $this->parseInput();
1511  $this->parseDropDown();
1512 
1513  $this->addHTMLStyle();
1514  $this->template = $this->dom->saveXML();
1515  // Parse i18n text
1516  $this->ParseBlock();
1517  $this->ParseIf();
1518  //$this->ParseKeyXml();
1519  //$this->template=$this->dom->saveXML();
1520  // print $this->template;exit;
1521  $this->ParseKey();
1522  $this->ParseText();
1523  $this->restoreUserFieldSet();
1524 
1525  $this->restoreProtectedValues();
1526 
1527  $this->ParseHtmlText();
1528  $this->dom = new DOMDocument();
1529  if ($this->dom->loadXML($this->template)) {
1530  $this->restoreSection();
1531  // not remove images because delete images defined in style.xml
1532  //$this->removeOrphanImages();
1533  $this->template = $this->dom->saveXML();
1534 
1535  $this->content_template = $this->template;
1536  } else {
1537  print $this->template;
1538  throw new Exception("cannot product ooo template");
1539  }
1540  } else {
1541  throw new Exception(sprintf("not openDocument file %s", $this->file));
1542  }
1543  }
1544  /**
1545  * generate OOo document
1546  */
1547  public function gen()
1548  {
1549  // if used in an app , set the app params
1550  if (is_object($this->action)) {
1551  $list = $this->action->parent->GetAllParam();
1552  while (list($k, $v) = each($list)) {
1553  $v = str_replace(array(
1554  '<BR>',
1555  '<br>',
1556  '<br/>',
1557  '<br />'
1558  ) , '<text:line-break/>', $v);
1559  $this->set($k, $v);
1560  }
1561  }
1562  // $this->rif=&$this->rkey;
1563  // $this->ParseIf($out);
1564  // Parse IMG: and LAY: tags
1565  $this->genContent();
1566 
1567  $this->genStyle();
1568  $this->genMeta();
1569  $this->updateManifest();
1570  $outfile = uniqid(getTmpDir() . "/odf") . '.odt';
1571  $this->content2odf($outfile);
1572  //print_r2($this->content_template);exit;
1573  return ($outfile);
1574  }
1575 }
1576 ?>
← centre documentaire © anakeen - published under CC License - Dynacase