diff --git a/lam/lib/pdfstruct.inc b/lam/lib/pdfstruct.inc index f220230a..5cedd21b 100644 --- a/lam/lib/pdfstruct.inc +++ b/lam/lib/pdfstruct.inc @@ -71,62 +71,6 @@ function getPDFStructures($typeId, $profile = null) { return $return; } -/** - * Saves PDF structure to XML file in format: ..xml - * - * @param string $typeId account type - * @param string $name name of structure - * @return string "no perms" if access denied or "ok". - */ -function savePDFStructure($typeId, $name) { - if (!isValidPDFStructureName($name) || !preg_match('/[a-zA-Z]+/', $typeId)) { - return 'no perms'; - } - $struct_file = dirname(__FILE__) . '/../config/pdf/' . $_SESSION['config']->getName() . '/' . $name . '.' . $typeId . '.xml'; - if(!is_writable(dirname(__FILE__) . '/../config/pdf/' . $_SESSION['config']->getName())) { - return 'no perms'; - } - else { - $handle = @fopen($struct_file,'w'); - if (!$handle) return 'no perms'; - $pdf_attributes = ''; - foreach($_SESSION['currentPageDefinitions'] as $key => $value) { - $pdf_attributes .= ' ' . $key . '="' . $value . '"'; - } - $file = '\n"; - foreach($_SESSION['currentPDFStructure'] as $entry) { - $ident = ''; - for($i=0;$i<$entry['level'] -1;$i++) { - $ident .= "\t"; - } - $attributes = ''; - if(isset($entry['attributes']) && is_array($entry['attributes'])) { - foreach($entry['attributes'] as $key => $value) { - $attributes .= ' ' . strtolower($key) . '="' . $value . '"'; - } - } - if($entry['type'] == 'open') { - $file .= $ident . '<' . strtolower($entry['tag']) . $attributes . ">\n"; - } - elseif($entry['type'] == 'close') { - $file .= $ident . '\n"; - } - elseif($entry['type'] == 'complete') { - if(isset($entry['value'])) { - $file .= $ident . '<' . strtolower($entry['tag']) . $attributes . '>' . $entry['value'] . '\n"; - } - else { - $file .= $ident . '<' . strtolower($entry['tag']) . $attributes . " />\n"; - } - } - } - $file .= ""; - fwrite($handle,$file); - fclose($handle); - return 'ok'; - } -} - /** * Deletes XML file with PDF structure definitions. * @@ -291,7 +235,7 @@ function deletePDFLogo($name) { * @return boolean is valid */ function isValidPDFStructureName($name) { - return preg_match('/[a-zA-Z0-9\-\_]+/',$name) === 1; + return preg_match('/^[a-z0-9\-\_]+$/i',$name) === 1; } /** @@ -394,7 +338,7 @@ class PDFStructureReader { $xml->open($file); $structure = new PDFStructure(); // open - $xml->read(); + @$xml->read(); if (!$xml->name == 'pdf') { logNewMessage(LOG_ERR, 'Unknown tag name: ' . $xml->name); throw new \LAMException(_('Unable to read PDF structure.')); @@ -464,6 +408,102 @@ class PDFStructureReader { } +/** + * Writes PDF structures to files. + * + * @author Roland Gruber + */ +class PDFStructureWriter { + + /** + * Writes the PDF structure to disk. + * + * @param string $typeId type ID + * @param string $name structure name + * @param PDFStructure $structure structure + */ + public function write($typeId, $name, $structure) { + $fileName = $this->getFileName($typeId, $name); + $xml = $this->getXML($structure); + $this->writeXML($xml, $fileName); + } + + /** + * Writes the PDF structure to disk. + * + * @param string $typeId type ID + * @param string $name structure name + * @return string file name + */ + protected function getFileName($typeId, $name) { + if (!isValidPDFStructureName($name) || !preg_match('/[a-zA-Z]+/', $typeId)) { + throw new \LAMException(_('PDF structure name not valid'), + _('The name for that PDF-structure you submitted is not valid. A valid name must consist of the following characters: \'a-z\',\'A-Z\',\'0-9\',\'_\',\'-\'.')); + } + if(!is_writable(dirname(__FILE__) . '/../config/pdf/' . $_SESSION['config']->getName())) { + throw new \LAMException(_('Could not save PDF structure, access denied.')); + } + return dirname(__FILE__) . '/../config/pdf/' . $_SESSION['config']->getName() . '/' . $name . '.' . $typeId . '.xml'; + } + + /** + * Returns the generated XML. + * + * @param PDFStructure $structure structure + * @return string XML + */ + public function getXML($structure) { + $writer = new \XMLWriter(); + $writer->openMemory(); + $writer->setIndent(true); + $writer->setIndentString("\t"); + $writer->startElement('pdf'); + $writer->writeAttribute('filename', $structure->getLogo()); + $writer->writeAttribute('headline', $structure->getTitle()); + $writer->writeAttribute('foldingmarks', $structure->getFoldingMarks()); + foreach ($structure->getSections() as $section) { + if ($section instanceof PDFTextSection) { + $writer->startElement('text'); + $writer->text($section->getText()); + $writer->endElement(); + } + else { + $writer->startElement('section'); + if ($section->isAttributeTitle()) { + $writer->writeAttribute('name', '_' . $section->getPdfKey()); + } + else { + $writer->writeAttribute('name', $section->getTitle()); + } + foreach ($section->getEntries() as $entry) { + $writer->startElement('entry'); + $writer->writeAttribute('name', $entry->getKey()); + $writer->endElement(); + } + $writer->endElement(); + } + } + $writer->endElement(); + return $writer->outputMemory(); + } + + /** + * Writes the XML to the given file. + * + * @param string $xml XML + * @param string $file file name + */ + protected function writeXML($xml, $file) { + $handle = @fopen($file,'w'); + if (!$handle) { + throw new \LAMException(_('Could not save PDF structure, access denied.')); + } + fwrite($handle, $xml); + fclose($handle); + } + +} + /** * PDF structure * diff --git a/lam/templates/pdfedit/pdfmain.php b/lam/templates/pdfedit/pdfmain.php index 7b201f71..70cbdc45 100644 --- a/lam/templates/pdfedit/pdfmain.php +++ b/lam/templates/pdfedit/pdfmain.php @@ -222,6 +222,12 @@ include '../main_header.php'; $container->addElement($message, true); } + if (isset($_GET['loadFailed'])) { + $message = new htmlStatusMessage("ERROR", _("Unable to read PDF structure."), htmlspecialchars($_GET['name'])); + $message->colspan = 10; + $container->addElement($message, true); + } + // new template if (!empty($availableTypes)) { $container->addElement(new htmlSubTitle(_('Create a new PDF structure')), true); diff --git a/lam/templates/pdfedit/pdfpage.php b/lam/templates/pdfedit/pdfpage.php index 2869ddc2..b6e77381 100644 --- a/lam/templates/pdfedit/pdfpage.php +++ b/lam/templates/pdfedit/pdfpage.php @@ -19,6 +19,7 @@ use LAM\PDF\PDFTextSection; use LAM\PDF\PDFEntrySection; use LAM\PDF\PDFStructure; use LAM\PDF\PDFSectionEntry; +use LAM\PDF\PDFStructureWriter; /* $Id$ @@ -87,9 +88,6 @@ if (!$_SESSION['ldap'] || !$_SESSION['ldap']->server()) { // Write $_POST variables to $_GET when form was submitted via post if (isset($_POST['type'])) { $_GET = $_POST; - if ($_POST['pdfname'] == '') { - unset($_GET['pdfname']); - } } $typeManager = new \LAM\TYPES\TypeManager(); @@ -106,171 +104,22 @@ if(isset($_GET['abort'])) { exit; } -// Check if pdfname is valid, then save current structure to file and go to -// main pdf structure page -$saveErrors = array(); -if(isset($_GET['submit'])) { - if(!isset($_GET['pdfname']) || !preg_match('/[a-zA-Z0-9\-\_]+/',$_GET['pdfname'])) { - $saveErrors[] = array('ERROR', _('PDF structure name not valid'), _('The name for that PDF-structure you submitted is not valid. A valid name must consist of the following characters: \'a-z\',\'A-Z\',\'0-9\',\'_\',\'-\'.')); - } - else { - $return = \LAM\PDF\savePDFStructure($type->getId(), $_GET['pdfname']); - if($return == 'ok') { - metaRefresh('pdfmain.php?savedSuccessfully=' . $_GET['pdfname']); - exit; - } - elseif($return == 'no perms'){ - $saveErrors[] = array('ERROR', _("Could not save PDF structure, access denied."), $_GET['pdfname']); - } - } -} - -foreach ($_GET as $key => $value) { - // Move a section, static text field or value entry downwards - if (strpos($key, 'down_') === 0) { - $index = substr($key, strlen('down_')); - $tmp = $_SESSION['currentPDFStructure'][$index]; - $next = $_SESSION['currentPDFStructure'][$index + 1]; - // We have a section or static text to move - if($tmp['tag'] == 'SECTION' || $tmp['tag'] == 'TEXT') { - $pos = 0; - $current = current($_SESSION['currentPDFStructure']); - // Find section or static text entry to move - while($pos < $index) { - $current = next($_SESSION['currentPDFStructure']); - $pos++; - } - $borders = array(); - // We have a section to move - if($current['tag'] == 'SECTION'){ - $borders[$current['type']][] = $pos; - $current = next($_SESSION['currentPDFStructure']); - $pos++; - // Find end of section to move - while($current && $current['tag'] != 'SECTION' && $current['type'] != 'close') { - $current = next($_SESSION['currentPDFStructure']); - $pos++; - } - $borders['close'][] = $pos; - } - // We have a static text entry to move - elseif($current['tag'] == 'TEXT') { - $borders['open'][] = $pos; - $borders['close'][] = $pos; - } - $current = next($_SESSION['currentPDFStructure']); - $pos++; - // Find next section or static text entry in structure - if($current) { - // Next is a section - if($current['tag'] == 'SECTION') { - $borders[$current['type']][] = $pos; - $current = next($_SESSION['currentPDFStructure']); - $pos++; - // Find end of this section - while($current && $current['tag'] != 'SECTION' && $current['type'] != 'close') { - if($current['tag'] == 'SECTION') { - $borders[$current['type']][] = $pos; - } - $current = next($_SESSION['currentPDFStructure']); - $pos++; - } - } - // Next is static text entry - elseif($current['tag'] == 'TEXT') { - $borders['open'][] = $pos; - } - $borders['close'][] = $pos; - } - // Move only downwars if not bottenmost element of this structure - if(count($borders['open']) > 1) { - // Calculate entries to move and move them - $cut_start = $borders['open'][count($borders['open']) - 1]; - $cut_count = $borders['close'][count($borders['close']) - 1] - $borders['open'][count($borders['open']) - 1] + 1; - $insert_pos = $borders['open'][count($borders['open']) - 2]; - $tomove = array_splice($_SESSION['currentPDFStructure'],$cut_start,$cut_count); - array_splice($_SESSION['currentPDFStructure'],$insert_pos,0,$tomove); - } - } - // We have a value entry to move; move it only if it is not the bottmmost - // element of this section. - elseif($tmp['tag'] == 'ENTRY' && $next['tag'] == 'ENTRY') { - $_SESSION['currentPDFStructure'][$index] = $_SESSION['currentPDFStructure'][$index + 1]; - $_SESSION['currentPDFStructure'][$index + 1] = $tmp; - } - } - // Move a section, static text or value entry upwards - elseif (strpos($key, 'up_') === 0) { - $index = substr($key, strlen('up_')); - $tmp = $_SESSION['currentPDFStructure'][$index]; - $prev = $_SESSION['currentPDFStructure'][$index - 1]; - // We have a section or static text to move - if($tmp['tag'] == 'SECTION' || $tmp['tag'] == 'TEXT') { - $pos = 0; - $borders = array(); - $current = current($_SESSION['currentPDFStructure']); - // Add borders of sections and static text entry to array - if($current['tag'] == 'SECTION') { - $borders[$current['type']][] = $pos; - } - elseif($current['tag'] == 'TEXT') { - $borders['open'][] = $pos; - $borders['close'][] = $pos; - } - // Find all sections and statci text fields before the section or static - // text entry to move upwards - while($pos < $index) { - $current = next($_SESSION['currentPDFStructure']); - $pos++; - if($current['tag'] == 'SECTION') { - $borders[$current['type']][] = $pos; - } - elseif($current['tag'] == 'TEXT') { - $borders['open'][] = $pos; - $borders['close'][] = $pos; - } - } - // Move only when not topmost element - if(count($borders['close']) > 0) { - // We have a section to move up - if($current['tag'] == 'SECTION') { - $current = next($_SESSION['currentPDFStructure']); - $pos++; - // Find end of section to move - while($current && $current['tag'] != 'SECTION' && $current['type'] != 'close') { - $current = next($_SESSION['currentPDFStructure']); - $pos++; - } - $borders['close'][] = $pos; - } - // Calculate the entries to move and move them - $cut_start = $borders['open'][count($borders['open']) - 1]; - $cut_count = $borders['close'][count($borders['close']) - 1] - $borders['open'][count($borders['open']) - 1] + 1; - $insert_pos = $borders['open'][count($borders['open']) - 2]; - $tomove = array_splice($_SESSION['currentPDFStructure'],$cut_start,$cut_count); - array_splice($_SESSION['currentPDFStructure'],$insert_pos,0,$tomove); - } - } - // We have a value entry to move; move it only if its not the topmost - // entry in this section - elseif($tmp['tag'] == 'ENTRY' && $prev['tag'] == 'ENTRY') { - $_SESSION['currentPDFStructure'][$index] = $prev; - $_SESSION['currentPDFStructure'][$index - 1] = $tmp; - } - } -} - // Load PDF structure from file if it is not defined in session if(!isset($_SESSION['currentPDFStructure'])) { // Load structure file to be edit $reader = new PDFStructureReader(); - if(isset($_GET['edit'])) { - $_SESSION['currentPDFStructure'] = $reader->read($type->getId(), $_GET['edit']); - $_GET['pdfname'] = $_GET['edit']; + try { + if(isset($_GET['edit'])) { + $_SESSION['currentPDFStructure'] = $reader->read($type->getId(), $_GET['edit']); + } + // Load default structure file when creating a new one + else { + $_SESSION['currentPDFStructure'] = $reader->read($type->getId(), 'default'); + } } - // Load default structure file when creating a new one - else { - $_SESSION['currentPDFStructure'] = $reader->read($type->getId(), 'default'); + catch (\LAMException $e) { + metaRefresh('pdfmain.php?loadFailed=1&name=' . $_GET['edit']); + exit; } } @@ -280,6 +129,24 @@ if (!empty($_POST['form_submit'])) { addSection($_SESSION['currentPDFStructure']); addSectionEntry($_SESSION['currentPDFStructure']); removeItem($_SESSION['currentPDFStructure']); + moveUp($_SESSION['currentPDFStructure']); + moveDown($_SESSION['currentPDFStructure']); +} + +// Check if pdfname is valid, then save current structure to file and go to +// main pdf structure page +$saveErrors = array(); +if(isset($_GET['submit'])) { + $writer = new PDFStructureWriter(); + try { + $writer->write($type->getId(), $_POST['pdfname'], $_SESSION['currentPDFStructure']); + unset($_SESSION['currentPDFStructure']); + metaRefresh('pdfmain.php?savedSuccessfully=' . $_POST['pdfname']); + exit; + } + catch (\LAMException $e) { + $saveErrors[] = array('ERROR', $e->getTitle(), $e->getMessage()); + } } $availablePDFFields = getAvailablePDFFields($type->getId()); @@ -344,9 +211,6 @@ $structureName = ''; if (isset($_GET['edit'])) { $structureName = $_GET['edit']; } -elseif (isset($_GET['pdfname'])) { - $structureName = $_GET['pdfname']; -} else if (isset($_POST['pdfname'])) { $structureName = $_POST['pdfname']; } @@ -748,4 +612,69 @@ function removeItem(&$structure) { } } +/** + * Moves up a section or entry if requested. + * + * @param PDFStructure $structure + */ +function moveUp(&$structure) { + $sections = $structure->getSections(); + foreach ($_POST as $key => $value) { + // move section + if (strpos($key, 'up_section_') === 0) { + $pos = substr($key, strlen('up_section_')); + $sectionTmp = $sections[$pos - 1]; + $sections[$pos - 1] = $sections[$pos]; + $sections[$pos] = $sectionTmp; + $structure->setSections($sections); + } + // move section entry + if (strpos($key, 'up_entry_') === 0) { + $parts = substr($key, strlen('up_entry_')); + $parts = explode('_', $parts); + $sectionPos = $parts[0]; + $entryPos = $parts[1]; + $entries = $sections[$sectionPos]->getEntries(); + $entryTmp = $entries[$entryPos - 1]; + $entries[$entryPos - 1] = $entries[$entryPos]; + $entries[$entryPos] = $entryTmp; + $sections[$sectionPos]->setEntries($entries); + $structure->setSections($sections); + } + } +} + +/** + * Moves down a section or entry if requested. + * + * @param PDFStructure $structure + */ +function moveDown(&$structure) { + $sections = $structure->getSections(); + foreach ($_POST as $key => $value) { + // move section + if (strpos($key, 'down_section_') === 0) { + $pos = substr($key, strlen('down_section_')); + $sectionTmp = $sections[$pos + 1]; + $sections[$pos + 1] = $sections[$pos]; + $sections[$pos] = $sectionTmp; + $structure->setSections($sections); + } + // move section entry + if (strpos($key, 'down_entry_') === 0) { + $parts = substr($key, strlen('down_entry_')); + $parts = explode('_', $parts); + $sectionPos = $parts[0]; + $entryPos = $parts[1]; + $entries = $sections[$sectionPos]->getEntries(); + $entries = $sections[$sectionPos]->getEntries(); + $entryTmp = $entries[$entryPos + 1]; + $entries[$entryPos + 1] = $entries[$entryPos]; + $entries[$entryPos] = $entryTmp; + $sections[$sectionPos]->setEntries($entries); + $structure->setSections($sections); + } + } +} + ?> diff --git a/lam/tests/lib/pdfstructTest.php b/lam/tests/lib/pdfstructTest.php index ecd850e9..7e206065 100644 --- a/lam/tests/lib/pdfstructTest.php +++ b/lam/tests/lib/pdfstructTest.php @@ -3,6 +3,7 @@ use LAM\PDF\PDFTextSection; use LAM\PDF\PDFEntrySection; use LAM\PDF\PDFStructureReader; use LAM\PDF\PDFStructure; +use LAM\PDF\PDFStructureWriter; /* This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) @@ -48,7 +49,7 @@ class ReadStructureTest extends PHPUnit_Framework_TestCase { $this->assertEquals('User information', $structure->getTitle()); $this->assertEquals(PDFStructure::FOLDING_STANDARD, $structure->getFoldingMarks()); $sections = $structure->getSections(); - $this->assertEquals(3, sizeof($sections)); + $this->assertEquals(4, sizeof($sections)); // check first section $this->assertInstanceOf(PDFEntrySection::class, $sections[0]); $this->assertFalse($sections[0]->isAttributeTitle()); @@ -69,6 +70,12 @@ class ReadStructureTest extends PHPUnit_Framework_TestCase { $this->assertEquals(2, sizeof($entries)); $this->assertEquals('posixAccount_homeDirectory', $entries[0]->getKey()); $this->assertEquals('posixAccount_loginShell', $entries[1]->getKey()); + // check fourth section + $this->assertInstanceOf(PDFEntrySection::class, $sections[3]); + $this->assertFalse($sections[3]->isAttributeTitle()); + $this->assertEquals('No entries', $sections[3]->getTitle()); + $entries = $sections[3]->getEntries(); + $this->assertEquals(0, sizeof($entries)); } /** @@ -80,6 +87,28 @@ class ReadStructureTest extends PHPUnit_Framework_TestCase { return dirname(dirname(__FILE__)) . '/resources/pdf/' . $file; } + /** + * Tests if the output is the same as the original PDF. + */ + public function testWrite() { + $file = $this->getTestFileName('writer.xml'); + // read input XML + $fileHandle = fopen($file, "r"); + $originalXML = fread($fileHandle, 1000000); + fclose($fileHandle); + // read structure + $reader = $this->getMockBuilder('\LAM\PDF\PDFStructureReader') + ->setMethods(array('getFileName')) + ->getMock(); + $reader->method('getFileName')->willReturn($file); + $structure = $reader->read('type', 'name'); + // create writer and get output XML + $writer = new PDFStructureWriter(); + $xml = $writer->getXML($structure); + // compare + $this->assertEquals($originalXML, $xml); + } + } ?> \ No newline at end of file diff --git a/lam/tests/resources/pdf/test.xml b/lam/tests/resources/pdf/test.xml index ea5621b6..fec76e9c 100644 --- a/lam/tests/resources/pdf/test.xml +++ b/lam/tests/resources/pdf/test.xml @@ -10,4 +10,5 @@ +
\ No newline at end of file diff --git a/lam/tests/resources/pdf/writer.xml b/lam/tests/resources/pdf/writer.xml new file mode 100644 index 00000000..7469e546 --- /dev/null +++ b/lam/tests/resources/pdf/writer.xml @@ -0,0 +1,12 @@ + +
+ + + +
+ test text +
+ + +
+