From 861f45e9a8595503d9960a41b4d4ca42ab728a9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laurent=20L=C3=A9cluse?= <laurent.lecluse@unicaen.fr> Date: Wed, 30 Jan 2019 14:12:58 +0100 Subject: [PATCH] Pb colier/coller de fichier!! --- src/Document.php | 878 +++++++++++++++++++++++++++++----------------- src/Publisher.php | 3 +- 2 files changed, 559 insertions(+), 322 deletions(-) diff --git a/src/Document.php b/src/Document.php index 286e6ad..a6f8e15 100644 --- a/src/Document.php +++ b/src/Document.php @@ -2,83 +2,196 @@ namespace Unicaen\OpenDocument; -use Exception; use DOMDocument; use DOMElement; use DOMNode; use DOMNodeList; +use Exception; use ZipArchive; -class Publisher + +class Document { - const PAGE_BREAK_NAME = 'UNICAEN_PAGE_BREAK'; - const PARAGRAPH = 'text:p'; - const TABLE_ROW = 'table:table-row'; + /** + * @var ZipArchive + */ + private $archive; /** - * Lecteur de fichier OpenDocument - * - * @var Document + * @var bool */ - private $document; + private $pdfOutput = false; + + /** + * @var Publisher + */ + private $publisher; + + /** + * @var Stylist + */ + private $stylist; + + /** + * @var DomDocument; + */ + private $meta; + + /** + * @var DOMDocument + */ + private $styles; /** - * Contenu XML du corps de texte - * * @var DOMDocument */ private $content; /** - * @var DOMElement + * @var array */ - private $body; + private $namespaces = []; /** - * Ajoute un saut de page automatiquement entre deux instances de document lors du publipostage - * - * @var boolean + * @var bool */ - private $autoBreak = true; + private $metaChanged = false; /** - * @var array + * @var bool */ - private $values = []; + private $stylesChanged = false; + + /** + * @var bool + */ + private $contentChanged = false; /** - * Variable contenant le résultat final du content.xml - * * @var string */ - private $outContent; + private $convCommand = 'unoconv -f pdf -o :outputFile :inputFile'; /** - * @var bool + * @var string */ - private $published = false; + private $tmpDir; + /** + * @var array + */ + private $formatters = []; /** - * @return Document + * @var array + */ + private $tmpFiles = []; + + private $defaultNamespaces = [ + 'office' => "urn:oasis:names:tc:opendocument:xmlns:office:1.0", + 'style' => "urn:oasis:names:tc:opendocument:xmlns:style:1.0", + 'text' => "urn:oasis:names:tc:opendocument:xmlns:text:1.0", + 'table' => "urn:oasis:names:tc:opendocument:xmlns:table:1.0", + 'draw' => "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0", + 'fo' => "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0", + 'xlink' => "http://www.w3.org/1999/xlink", + 'dc' => "http://purl.org/dc/elements/1.1/", + 'meta' => "urn:oasis:names:tc:opendocument:xmlns:meta:1.0", + 'number' => "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0", + 'svg' => "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0", + 'chart' => "urn:oasis:names:tc:opendocument:xmlns:chart:1.0", + 'dr3d' => "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0", + 'math' => "http://www.w3.org/1998/Math/MathML", + 'form' => "urn:oasis:names:tc:opendocument:xmlns:form:1.0", + 'script' => "urn:oasis:names:tc:opendocument:xmlns:script:1.0", + 'ooo' => "http://openoffice.org/2004/office", + 'ooow' => "http://openoffice.org/2004/writer", + 'oooc' => "http://openoffice.org/2004/calc", + 'dom' => "http://www.w3.org/2001/xml-events", + 'rpt' => "http://openoffice.org/2005/report", + 'of' => "urn:oasis:names:tc:opendocument:xmlns:of:1.2", + 'xhtml' => "http://www.w3.org/1999/xhtml", + 'grddl' => "http://www.w3.org/2003/g/data-view#", + 'officeooo' => "http://openoffice.org/2009/office", + 'tableooo' => "http://openoffice.org/2009/table", + 'drawooo' => "http://openoffice.org/2010/draw", + 'calcext' => "urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0", + 'css3t' => "http://www.w3.org/TR/css3-text/", + 'xforms' => 'http://www.w3.org/2002/xforms', + 'xsd' => 'http://www.w3.org/2001/XMLSchema', + 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance', + 'loext' => 'urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0', + 'field' => 'urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0', + 'formx' => 'urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0', + ]; + + + + /** + * @return ZipArchive + * @throws Exception + */ + public function getArchive(): ZipArchive + { + if (!$this->archive) { + throw new \Exception("Aucun document n\'est chargé"); + } + + return $this->archive; + } + + + + /** + * @return DOMDocument + */ + public function getContent(): DOMDocument + { + return $this->content; + } + + + + /** + * @return DOMDocument */ - public function getDocument(): Document + public function getStyles(): DOMDocument { - return $this->document; + return $this->styles; } /** - * @param Document $document + * @return DOMDocument + */ + public function getMeta(): DOMDocument + { + return $this->meta; + } + + + + /** + * @return bool + */ + public function isPdfOutput(): bool + { + return $this->pdfOutput; + } + + + + /** + * @param bool $pdfOutput * - * @return Publisher + * @return Document */ - public function setDocument(Document $document): Publisher + public function setPdfOutput(bool $pdfOutput): Document { - $this->document = $document; + $this->pdfOutput = $pdfOutput; return $this; } @@ -86,23 +199,23 @@ class Publisher /** - * @return DOMElement + * @return bool */ - public function getBody(): DOMElement + public function isMetaChanged(): bool { - return $this->body; + return $this->metaChanged; } /** - * Peuple l'instance courante du document et l'ajoute à la suite du fichier + * @param bool $metaChanged * - * @param array $values + * @return Document */ - public function add(array $values): Publisher + public function setMetaChanged(bool $metaChanged): Document { - $this->values[] = $values; + $this->metaChanged = $metaChanged; return $this; } @@ -110,21 +223,23 @@ class Publisher /** - * @return array + * @return bool */ - public function getValues(): array + public function isStylesChanged(): bool { - return $this->values; + return $this->stylesChanged; } /** - * @param array $values + * @param bool $stylesChanged + * + * @return Document */ - public function setValues(array $values): Publisher + public function setStylesChanged(bool $stylesChanged): Document { - $this->values = $values; + $this->stylesChanged = $stylesChanged; return $this; } @@ -134,21 +249,21 @@ class Publisher /** * @return bool */ - public function isAutoBreak(): bool + public function isContentChanged(): bool { - return $this->autoBreak; + return $this->contentChanged; } /** - * @param bool $autoBreak + * @param bool $contentChanged * - * @return Publisher + * @return Document */ - public function setAutoBreak(bool $autoBreak): Publisher + public function setContentChanged(bool $contentChanged): Document { - $this->autoBreak = $autoBreak; + $this->contentChanged = $contentChanged; return $this; } @@ -156,50 +271,167 @@ class Publisher /** - * @param DOMElement $element + * @return string + */ + public function getConvCommand(): string + { + return $this->convCommand; + } + + + + /** + * @param string $convCommand * - * @return Publisher - * @throws Exception + * @return Document */ - public function getVariables(DOMElement $element): array + public function setConvCommand(string $convCommand): Document { - $textNs = $this->getDocument()->getNamespaceUrl('text'); + $this->convCommand = $convCommand; + + return $this; + } - $variables = []; - $vElements = $element->getElementsByTagNameNS($textNs, 'variable-set'); - foreach ($vElements as $vElement) { - $name = $vElement->getAttributeNS($textNs, 'name'); - if (!isset($variables[$name])) $variables[$name] = []; - $variables[$name][] = $vElement; + + /** + * Retourne les méta-données du fichier OpenDocument + * + * @return array + */ + public function getMetaArray(): array + { + $m = []; + + $nodes = $this->getMeta()->documentElement->childNodes->item(0)->childNodes; + foreach ($nodes as $node) { + if (isset($node->tagName)) { + switch ($node->tagName) { + case 'meta:generator': + case 'meta:initial-creator': + case 'meta:editing-cycles': + case 'meta:editing-duration': + case 'meta:printed-by': + case 'dc:title': + case 'dc:description': + case 'dc:subject': + case 'dc:creator': + case 'dc:language': + list($ns, $tag) = explode(':', $node->tagName); + $m[$tag] = $node->textContent; + break; + case 'meta:creation-date': + $m['creation-date'] = substr($node->textContent, 0, 10); + break; + case 'meta:print_date': + $m['print_date'] = substr($node->textContent, 0, 10); + break; + case 'dc:date': + $m['date'] = substr($node->textContent, 0, 10); + break; + case 'meta:document-statistic': + $m['document-statistic'] = []; + foreach ($node->attributes as $attribute) { + $m['document-statistic'][$attribute->name] = $attribute->value; + } + break; + case 'meta:user-defined': + if (!isset($m['user-defined'])) $m['user-defined'] = []; + foreach ($node->attributes as $attribute) { + $m['user-defined'][$attribute->name] = $attribute->value; + } + break; + case 'meta:keywords': + $m['keywords'] = []; + foreach ($node->childNodes as $knode) { + $m['keywords'][] = $knode->textContent; + } + break; + } + } } - return $variables; + return $m; } - public function getSections(DOMElement $element): array + /** + * @param string $variable + * @param mixed $value + * + * @return string + */ + public function formatValue(string $variable, $value): string { - $textNs = $this->getDocument()->getNamespaceUrl('text'); + if (isset($this->formatters[$variable]) && $format = $this->formatters[$variable]){ + if (is_callable($format)){ + $value = $format($value); + } + } + + if (is_float($value)){ + return number_format($value, 2, ',', ' '); + } + + return (string)$value; + } - $sections = []; - $vElements = $this->findFrom($element, 'text:section'); - foreach ($vElements as $vElement) { - $name = $vElement->getAttributeNS($textNs, 'name'); - if (!isset($sections[$name])) $sections[$name] = []; - $sections[$name][] = $vElement; + + /** + * Retourne les espaces de nom associés à leurs URI respectives sous forme de tableau associatif + * + * @return array + */ + public function getNamespaces(): array + { + if (empty($this->namespaces)) { + $content = $this->getArchive()->getFromName('content.xml'); + + $begin = strpos($content, '<', 1) + 2 + strlen('office:document-content'); + $end = strpos($content, '>', 50) - $begin; + $content = explode(' ', substr($content, $begin, $end)); + $this->namespaces = $this->defaultNamespaces; + foreach ($content as $str) { + if (0 === strpos($str, 'xmlns:')) { + $namespace = substr($str, 6, strpos($str, '"') - 7); + $url = substr($str, strpos($str, '"') + 1, -1); + $this->namespaces[$namespace] = $url; + } + } } - return $sections; + return $this->namespaces; } - public function hideSection(DOMElement $section): Publisher + /** + * @param string $name + * + * @return bool + */ + public function hasNamespace(string $name): bool { - $section->parentNode->removeChild($section); + $this->getNamespaces(); + + return isset($this->namespaces[$name]); + } + + + + /** + * @param string $name + * @param string $url + * + * @return Document + */ + public function addNamespace(string $name, string $url): Document + { + if (!$this->hasNamespace($name)) { + $this->namespaces[$name] = $url; + } return $this; } @@ -207,70 +439,73 @@ class Publisher /** - * @param DOMElement $element - * @param mixed $value + * Retourne l'url associé à un espace de nom * - * @return Publisher + * @param string $namespace + * + * @return string */ - protected function setVariable(DOMElement $element, $value): Publisher + public function getNamespaceUrl(string $namespace): string { - $textNs = $this->getDocument()->getNamespaceUrl('text'); - $document = $element->ownerDocument; + $ns = $this->getNamespaces(); - if (is_string($value) && false !== strpos($value, "\n")){ - $value = explode("\n", $value); - }else{ - $value = (array)$value; - } - for ($i = 0; $i < count($value); $i++) { - if ($i > 0) { - $returnVNode = $document->createElementNS($textNs, 'text:line-break'); - $element->parentNode->insertBefore($returnVNode, $element); - } - $vText = $document->createTextNode($this->getDocument()->formatValue($element->nodeValue,$value[$i])); - $element->parentNode->insertBefore($vText, $element); + if (!isset($ns[$namespace])) { + throw new Exception('L\'espace de nom ' . $namespace . ' n\'a pas été trouvé.'); } - $element->parentNode->removeChild($element); - return $this; + return $ns[$namespace]; } - private function addPageBreakStyle(): Publisher + /** + * Retourne un champ d'information + * + * @param integer $index + * + * @return string + */ + public function getInfo($index) { - /* get office:automatic-styles node */ - $styles = $this->content->getElementsByTagNameNS( - $this->getDocument()->getNamespaceUrl('office'), - 'automatic-styles' - )->item(0); + $infonodes = $this->getMeta()->getElementsByTagNameNS($this->getNamespaceUrl('meta'), 'user-defined'); - $stylePageBreak = $this->newElement('style:style', [ - 'style:name' => self::PAGE_BREAK_NAME, - 'style:family' => 'paragraph', - 'style:parent-style-name' => 'Standard', - 'style:master-page-name' => 'Standard', - ]); - $stylePageBreakProperties = $this->newElement('style:paragraph-properties', [ - 'style:writing-mode' => 'page', - 'style:page-number' => '1', - ]); + return $infonodes->item($index)->nodeValue; + } - $stylePageBreak->appendChild($stylePageBreakProperties); - $styles->appendChild($stylePageBreak); + + + /** + * Modifie un champ d'information + * + * @todo à finir d'implémenter, car ça ne marche pas!! + * + * @param integer $index + * @param string $value + */ + public function setInfo($index, $value): Document + { + throw new \Exception('Implémentation à corriger : ne fonctionne pas'); + + $infonodes = $this->getMeta()->getElementsByTagNameNS($this->getNamespaceUrl('meta'), 'user-defined'); + if ($infonodes->length > 0) { + $infonodes->item($index)->nodeValue = $value; + $this->setMetaChanged(true); + } return $this; } - private function addPageBreak(DOMElement $element): Publisher + /** + * @param array $values + * + * @return Document + */ + public function publish(array $values): Document { - $textNs = $this->getDocument()->getNamespaceUrl('text'); - - $pageBreak = $this->content->createElementNS($textNs, 'text:p'); - $pageBreak->setAttributeNS($textNs, 'text:style-name', self::PAGE_BREAK_NAME); - $element->insertBefore($pageBreak, $element->firstChild); + $this->getPublisher()->setValues($values); + $this->getPublisher()->publish(); return $this; } @@ -279,161 +514,153 @@ class Publisher /** * @return Publisher - * @throws Exception */ - public function publishBegin(): Publisher + public function getPublisher(): Publisher { - /* On récupère le content du document */ - $this->content = new DOMDocument(); - $this->content->loadXML($this->getDocument()->getContent()->saveXML()); - - if ($this->isAutoBreak()) { - $this->addPageBreakStyle(); + if (!$this->publisher) { + $this->publisher = new Publisher(); + $this->publisher->setDocument($this); } - $contentText = $this->content->saveXML(); - $officeDocumentContentPos = strpos($contentText, '<office:document-content'); - $length = strpos($contentText, '>', $officeDocumentContentPos) + 1; - $this->out(substr($contentText, 0, $length)); + return $this->publisher; + } - /* wtite all nodes, except body */ - foreach ($this->content->documentElement->childNodes as $node) { - if ($node->nodeName != 'office:body') { - $this->out($this->content->saveXML($node)); - } - } - $this->out("<office:body>"); - /* declaration tags */ - $declTags = [ - 'variable-decls', 'sequence-decls', 'user-field-decls', 'dde-connexion-decls', - ]; - foreach ($declTags as $tagName) { - $node = $this->content->getElementsByTagNameNS($this->getDocument()->getNamespaceUrl('text'), $tagName); - if ($node->length > 0) { - $this->out($this->content->saveXML($node->item(0))); - $node->item(0)->parentNode->removeChild($node->item(0)); - } - } - $this->body = $this->content->getElementsByTagNameNS($this->getDocument()->getNamespaceUrl('office'), 'text')[0]; + /** + * @return Stylist + */ + public function getStylist(): Stylist + { + if (!$this->stylist) { + $this->stylist = new Stylist(); + $this->stylist->setDocument($this); + } - return $this; + return $this->stylist; } /** - * @return Publisher + * @param $data + * + * @return Document + * @throws Exception */ - public function publishEnd(): Publisher + public function loadFromData($data): Document { - $this->out("</office:body></office:document-content>"); + if (!class_exists('ZipArchive')) { + throw new Exception('Zip extension not loaded'); + } - /* On renvoie le content dans le document */ - $this->getDocument()->getArchive()->addFromString('content.xml', $this->outContent); - $this->published = true; + $odtFile = $this->tempFileName('odtfile_', 'odt'); + file_put_contents($odtFile, $data); - return $this; + return $this->loadFromFile($odtFile, false); } /** - * @param DOMElement $element - * @param array $values + * @param string $fileName + * @param bool $duplicate * - * @return Publisher + * @return Document * @throws Exception */ - public function publishValues(DOMElement $element, array $values): Publisher + public function loadFromFile(string $fileName, bool $duplicate = true): Document { - if ($element === $this->body) { - $this->getDocument()->getStylist()->setVariables($values); + if (!class_exists('ZipArchive')) { + throw new Exception('Zip extension not loaded'); } - $variables = $this->getVariables($element); - $sections = $this->getSections($element); - - foreach ($values as $name => $val) { - if (is_array($val)) { - /* On traite les données filles... */ - list($vname, $vparent) = explode("@", $name); - if (isset($variables[$vname])) { - foreach ($variables[$vname] as $elVar) { - $this->publishSubData($elVar, $vparent, $val); - } - } - if (isset($sections[$vname])) { - foreach ($sections[$vname] as $elSec) { - $this->publishSubData($elSec, $vparent, $val); - } - } - } else { - if (isset($variables[$name])) { - foreach ($variables[$name] as $vElement) { - $this->setVariable($vElement, $val ? $val : ''); - } - } - if (false !== strpos($name, '@')) { - list($sName, $sType) = explode("@", $name); - if ($sType == 'text:section') { - $name = $sName; - } - } - if (isset($sections[$name])) { - foreach ($sections[$name] as $sElement) { - if ($val === null - || $val === 0 - || $val === '0' - || $val === false - || $val === '' - || strtolower($val) === 'false' - ) { - $this->hideSection($sElement); - } - } - } - } + if (!file_exists($fileName)) { + throw new Exception('OpenDocument file "' . $fileName . '" doesn\'t exists.'); } - if ($element === $this->body) { - $this->out($this->content->saveXML($element)); + if ($duplicate) { + $odtFile = $this->tempFileName('odtFile_', 'odt'); + copy($fileName, $odtFile); + } else { + $odtFile = $fileName; } + $this->archive = new ZipArchive(); + if (!true === $this->archive->open($odtFile, ZIPARCHIVE::CREATE)) { + throw new Exception('OpenDocument file "' . $fileName . '" don\'t readable.'); + } + + $this->meta = new DOMDocument; + $this->meta->loadXML($this->archive->getFromName('meta.xml')); + + $this->styles = new DOMDocument; + $this->styles->loadXML($this->archive->getFromName('styles.xml')); + + $this->content = new DOMDocument; + $this->content->loadXML($this->archive->getFromName('content.xml')); + + $this->namespaces = []; + return $this; } /** - * @param DOMElement $element - * @param string $parent - * @param string $variable - * - * @return DOMElement + * @return string * @throws Exception */ - public function getSubDoc(DOMElement $element, string $parent, string $variable): DOMElement + private function prepareSaving($filename = null): string { - $variables = $this->getVariables($element); - if (!isset($variables[$variable][0])) { - throw new \Exception('La variable "' . $variable . '"" n\'a pas été trouvée dans le document'); + if ($this->isMetaChanged()) { + $this->getArchive()->addFromString('meta.xml', $this->getMeta()->saveXML()); } - return $this->getSubDocWithVariable($variables[$variable][0], $parent); + if ($this->isStylesChanged()) { + $this->getArchive()->addFromString('styles.xml', $this->getStyles()->saveXML()); + } + + if ($this->isContentChanged()) { + $this->getArchive()->addFromString('content.xml', $this->getContent()->saveXML()); + } + + $actualFile = $this->getArchive()->filename; + $this->getArchive()->close(); + $this->archive = null; + + if ($this->isPdfOutput()) { + if (!$filename) { + $filename = $this->tempFileName('odt2pdf_', 'pdf'); + } + + $this->odtToPdf($actualFile, $filename); + $actualFile = $filename; + } + + return $actualFile; } /** - * @param DOMElement $element + * @param $origine + * @param $destination * - * @return Publisher + * @return Document + * @throws Exception */ - public function remove(DOMElement $element): Publisher + public function odtToPdf($origine, $destination): Document { - $element->parentNode->removeChild($element); + $command = $this->getConvCommand(); + + $command = str_replace(':inputFile', $origine, $this->getConvCommand()); + $command = str_replace(':outputFile', $destination, $command); + exec($command, $output, $return); + + if (0 != $return) { + throw new \Exception('La conversion de document en PDF a échoué'); + } return $this; } @@ -441,17 +668,14 @@ class Publisher /** - * @param DOMElement $subDoc - * @param array $variables - * @param DOMElement $refNode + * @param string $fileName * - * @return Publisher + * @return Document + * @throws Exception */ - public function publishBefore(DOMElement $subDoc, array $variables, DOMElement $refNode): Publisher + public function saveToFile(string $fileName): Document { - $node = $subDoc->cloneNode(true); - $this->publishValues($node, $variables); - $refNode->parentNode->insertBefore($node, $refNode); + $actualFile = $this->prepareSaving($fileName); return $this; } @@ -459,174 +683,186 @@ class Publisher /** - * @param DOMElement $element - * @param string $parent - * @param DOMElement $variable - * - * @return DOMElement + * @return string * @throws Exception */ - private function getSubDocWithVariable(DOMElement $element, string $parent): DOMElement + public function saveToData(): string { - if ($element->tagName == 'text:section') { - return $element; // C'est la section qui est dupliquée - } else { - $i = 10; - $found = false; - for ($i = 0; $i < 10; $i++) { - $parentNode = isset($parentNode) ? $parentNode->parentNode : $element->parentNode; - if ($parentNode->nodeName == $parent) { - $found = true; - break; - } - } + $actualFile = $this->prepareSaving(); - if (!$found) { - throw new \Exception('Le noeud parent de type ' . $parent . ' n\'a pas été trouvé'); - } - - return $parentNode; - } + return file_get_contents($actualFile); } /** - * @param DOMElement $element - * @param string $parent - * @param array $values - * - * @return Publisher + * @return string */ - private function publishSubData(DOMElement $element, string $parent, array $values): Publisher + public function getTmpDir(): string { - $parentNode = $this->getSubDocWithVariable($element, $parent); - - foreach ($values as $vals) { - $clone = $parentNode->cloneNode(true); - $this->publishValues($clone, $vals); - $parentNode->parentNode->insertBefore($clone, $parentNode); + if (!$this->tmpDir) { + return sys_get_temp_dir(); } - return $this->remove($parentNode); + return $this->tmpDir; } /** - * Construit le fichier final à partir des données + * @param string $tmpDir + * + * @return Document */ - public function publish() + public function setTmpDir(string $tmpDir): Document { - $this->publishBegin(); - - $first = true; - foreach ($this->values as $values) { - $bodyNode = $this->body->cloneNode(true); - if ($first) { - $first = false; - } elseif ($this->isAutoBreak()) { - $this->addPageBreak($bodyNode); - } - - $this->publishValues($bodyNode, $values); - $this->getDocument()->getStylist()->setVariables($values); - $this->out($this->content->saveXML($bodyNode)); - $first = false; + $this->tmpDir = $tmpDir; + if (!file_exists($tmpDir)) { + mkdir($tmpDir); } - $this->publishEnd(); + return $this; } /** - * @param string $name + * @param null $prefix * - * @return DOMNode[] - * @throws Exception + * @return string */ - private function find(string $name): DOMNodeList + private function tempFileName($prefix = null, $ext = 'odt'): string { - $document = $this->getDocument(); + $tmpDir = $this->getTmpDir(); + if ('/' != substr($tmpDir, -1)) { + $tmpDir .= '/'; + } + + $tempFileName = uniqid($tmpDir . $prefix) . '.' . $ext; + $this->tmpFiles[] = $tempFileName; - return $document->find($this->content, $name); + return $tempFileName; } /** - * @param DOMNode $node - * @param $name - * - * @return DOMNodeList - * @throws Exception + * @return array */ - private function findFrom(DOMNode $node, $name): DOMNodeList + public function getFormatters(): array { - return $this->getDocument()->find($node, $name); + return $this->formatters; } /** - * @param string $name - * @param array $attrs + * @param string $variable + * @param mixed $format * - * @return DOMElement + * @return Document */ - private function newElement(string $name, array $attrs = []): DOMElement + public function addFormatter(string $variable, $format): Document { - $document = $this->getDocument(); + $this->formatters[$variable] = $format; - return $document->newElement($this->content, $name, $attrs); + return $this; } /** - * Ajoute du contenu au fichier content.xml - * Méthode à ne pas exploiter + * PHP 5 introduces a destructor concept similar to that of other object-oriented languages, such as C++. + * The destructor method will be called as soon as all references to a particular object are removed or + * when the object is explicitly destroyed or in any order in shutdown sequence. + * + * Like constructors, parent destructors will not be called implicitly by the engine. + * In order to run a parent destructor, one would have to explicitly call parent::__destruct() in the destructor body. * - * @param string $xml + * Note: Destructors called during the script shutdown have HTTP headers already sent. + * The working directory in the script shutdown phase can be different with some SAPIs (e.g. Apache). + * + * Note: Attempting to throw an exception from a destructor (called in the time of script termination) causes a fatal + * error. + * + * @return void + * @link https://php.net/manual/en/language.oop5.decon.php */ - public function out($xml) + public function __destruct() { - $this->outContent .= $xml; + /* On supprime les éventuels fichiers temporaires pour libérer l'espace */ + foreach ($this->tmpFiles as $tmpFile) { + if (file_exists($tmpFile)) { + unlink($tmpFile); + } + } } /** - * @return string + * @param $fileName + * + * @return Document + * @throws Exception */ - public function getOutContent(): string + public function download($fileName): Document { - return $this->outContent; + $actualFile = $this->prepareSaving(); + + if (headers_sent()) { + throw new Exception('Headers Allready Sent'); + } + + $contentType = $this->isPdfOutput() ? 'application/pdf' : 'application/vnd.oasis.opendocument.text'; + + header('Content-type: ' . $contentType); + header('Content-Disposition: attachment; filename="' . $fileName . '"'); + header('Content-Transfer-Encoding: binary'); + header('Pragma: no-cache'); + header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0'); + header('Expires: 0'); + readfile($actualFile); + + return $this; } /** - * @return bool + * @param DOMNode $node + * @param string $name + * + * @return DOMNode[] + * @throws Exception */ - public function isPublished(): bool + public function find(DOMNode $node, string $name): DOMNodeList { - return $this->published; + list($namespace, $name) = explode(':', $name); + + return $node->getElementsByTagNameNS($this->getNamespaceUrl($namespace), $name); } /** - * @param bool $published + * @param DOMDocument $document + * @param string $name + * @param array $attrs * - * @return Publisher + * @return DOMElement + * @throws Exception */ - public function setPublished(bool $published): Publisher + public function newElement(DOMDocument $document, string $name, array $attrs = []): DOMElement { - $this->published = $published; + list($namespace) = explode(':', $name); - return $this; - } + $newNode = $document->createElementNS($this->getNamespaceUrl($namespace), $name); + foreach ($attrs as $attrName => $attrValue) { + list($attrNS) = explode(':', $attrName); + $newNode->setAttributeNS($this->getNamespaceUrl($attrNS), $attrName, $attrValue); + } -} \ No newline at end of file + return $newNode; + } +} diff --git a/src/Publisher.php b/src/Publisher.php index 22fd950..286e6ad 100644 --- a/src/Publisher.php +++ b/src/Publisher.php @@ -505,7 +505,7 @@ class Publisher foreach ($values as $vals) { $clone = $parentNode->cloneNode(true); $this->publishValues($clone, $vals); - $this->insertBefore($clone, $parentNode); + $parentNode->parentNode->insertBefore($clone, $parentNode); } return $this->remove($parentNode); @@ -530,6 +530,7 @@ class Publisher } $this->publishValues($bodyNode, $values); + $this->getDocument()->getStylist()->setVariables($values); $this->out($this->content->saveXML($bodyNode)); $first = false; } -- GitLab