diff --git a/composer.json b/composer.json index f2015f0d..7c61b448 100644 --- a/composer.json +++ b/composer.json @@ -4,6 +4,7 @@ "squizlabs/php_codesniffer" : "3.4.0" }, "require": { - "ext-ldap": "*" + "ext-ldap": "*", + "ext-json": "*" } } diff --git a/lam/graphics/confImportExport.png b/lam/graphics/confImportExport.png new file mode 100644 index 00000000..460d384c Binary files /dev/null and b/lam/graphics/confImportExport.png differ diff --git a/lam/lib/account.inc b/lam/lib/account.inc index 2f6ee42b..1d53eb3a 100644 --- a/lam/lib/account.inc +++ b/lam/lib/account.inc @@ -1752,6 +1752,16 @@ function getLAMVersionText() { return $text . ' - ' . LAMVersion(); } +/** + * Returns if the given release is a developer version. + * + * @param string version + * @return bool is developer version + */ +function isDeveloperVersion($version) { + return strpos($version, 'DEV') !== false; +} + /** * LAM exception with title and message. * diff --git a/lam/lib/config.inc b/lam/lib/config.inc index 3e0a21fd..4743d72f 100644 --- a/lam/lib/config.inc +++ b/lam/lib/config.inc @@ -649,6 +649,78 @@ class LAMConfig { $this->reload(); } + /** + * Returns the server profile data. + * + * @return array data + */ + public function exportData() { + $data = array(); + $settingsToIgnore = array('modules', 'types', 'tools', 'jobs'); + foreach ($this->settings as $setting) { + if (in_array($setting, $settingsToIgnore)) { + continue; + } + $data[$setting] = $this->$setting; + } + $data['typeSettings'] = $this->typeSettings; + $data['moduleSettings'] = $this->moduleSettings; + $data['toolSettings'] = $this->toolSettings; + $data['jobSettings'] = $this->jobSettings; + if ($this->jobsDatabase === 'SQLite') { + $dbFileName = __DIR__ . '/../config/' . $this->getName() . '.sqlite'; + if (is_file($dbFileName) && is_readable($dbFileName)) { + $file = @fopen($dbFileName, "r"); + if ($file) { + $dbData = fread($file, 100000000); + fclose($file); + $data['jobSQLite'] = base64_encode($dbData); + } + } + } + return $data; + } + + /** + * Imports server profile data. + * + * @param array $data config data + * @throws LAMException import error + */ + public function importData($data) { + $settingsToIgnore = array('modules', 'types', 'tools', 'jobs', 'typeSettings', + 'moduleSettings', 'toolSettings', 'jobSettings', 'jobSQLite'); + foreach ($data as $dataKey => $dataValue) { + if (in_array($dataKey, $settingsToIgnore)) { + continue; + } + if (!in_array($dataKey, $this->settings)) { + logNewMessage(LOG_WARNING, 'Ignored setting during import: ' . $dataKey); + continue; + } + if (!(($dataValue === null) || is_array($dataValue) || is_string($dataValue) || is_int($dataValue) || is_bool($dataValue))) { + throw new LAMException('Invalid import data type for ' . htmlspecialchars($dataKey) . ': ' . gettype($dataValue)); + } + $this->$dataKey = $dataValue; + } + $typeSettingsData = !empty($data['typeSettings']) && is_array($data['typeSettings']) ? $data['typeSettings'] : array(); + $this->typeSettings = $typeSettingsData; + $moduleSettingsData = !empty($data['moduleSettings']) && is_array($data['moduleSettings']) ? $data['moduleSettings'] : array(); + $this->moduleSettings = $moduleSettingsData; + $toolSettingsData = !empty($data['toolSettings']) && is_array($data['toolSettings']) ? $data['toolSettings'] : array(); + $this->toolSettings = $toolSettingsData; + $jobSettingsData = !empty($data['jobSettings']) && is_array($data['jobSettings']) ? $data['jobSettings'] : array(); + $this->jobSettings = $jobSettingsData; + if (!empty($data['jobSQLite'])) { + $dbFileName = __DIR__ . '/../config/' . $this->getName() . '.sqlite'; + $file = @fopen($dbFileName, "wb"); + if ($file) { + fputs($file, base64_decode($data['jobSQLite'])); + fclose($file); + } + } + } + /** * Reloads preferences from config file * @@ -759,6 +831,10 @@ class LAMConfig { /** Saves preferences to config file */ public function save() { $conffile = $this->getPath(); + if (!file_exists($conffile)) { + $newFile = fopen($conffile, 'wb'); + fclose($newFile); + } if (is_file($conffile) && is_readable($conffile)) { $file = fopen($conffile, "r"); $file_array = array(); @@ -1775,7 +1851,7 @@ class LAMConfig { /** * Returns a list of active account types. * - * @return array list of types + * @return string[] list of types */ public function get_ActiveTypes() { if (($this->activeTypes == '') || !isset($this->activeTypes)) { @@ -1789,7 +1865,7 @@ class LAMConfig { /** * Sets the list of active types. * - * @param array list of types + * @param string[] list of types */ public function set_ActiveTypes($types) { $this->activeTypes = implode(",", $types); @@ -2681,6 +2757,83 @@ class LAMCfgMain { $this->reload(); } + /** + * Exports the configuration data. + * + * @return array config data + */ + public function exportData() { + $data = array(); + foreach ($this->settings as $setting) { + $data[$setting] = $this->$setting; + } + return $data; + } + + /** + * Imports configuration data. + * + * @param array $data config data + * @throws LAMException import error + */ + public function importData($data) { + foreach ($data as $dataKey => $dataValue) { + if (!in_array($dataKey, $this->settings)) { + logNewMessage(LOG_WARNING, 'Ignored setting during import: ' . $dataKey); + continue; + } + if (!(($dataValue === null) || is_array($dataValue) || is_string($dataValue) || is_int($dataValue) || is_bool($dataValue))) { + throw new LAMException('Invalid import data type for ' . htmlspecialchars($dataKey) . ': ' . gettype($dataValue)); + } + $this->$dataKey = $dataValue; + } + } + + /** + * Returns the content of the server certificates file + * + * @return null|string certificates + */ + public function exportCertificates() { + $fileName = $this->getSSLCaCertPath(); + if ($fileName === null) { + return null; + } + $content = null; + $handle = @fopen($fileName, "r"); + if ($handle) { + $content = fread($handle, 10000000); + fclose($handle); + } + return $content; + } + + /** + * Imports the server certificates. + * + * @param null|string $certsContent certificates + * @throws LAMException write to file failed + */ + public function importCertificates($certsContent) { + $fileName = $this->getSSLCaCertPath(); + if (empty($certsContent)) { + if ($fileName !== null) { + unlink($fileName); + } + return; + } + $fileName = $this->getInternalSSLCaCertFileName(); + $handle = @fopen($fileName, "wb"); + if ($handle) { + fputs($handle, $certsContent); + fclose($handle); + @chmod($fileName, 0600); + } + else { + throw new LAMException(printf(_('Unable to write file %s.'), $fileName)); + } + } + /** * Reloads preferences from config file config.cfg * diff --git a/lam/lib/html.inc b/lam/lib/html.inc index 7b077aaf..21fb7b7d 100644 --- a/lam/lib/html.inc +++ b/lam/lib/html.inc @@ -3082,6 +3082,15 @@ class htmlStatusMessage extends htmlElement { return array(); } + /** + * Returns the message type. + * + * @return String type + */ + public function getType() { + return $this->type; + } + } /** @@ -3644,6 +3653,8 @@ class htmlSpan extends htmlElement { /** htmlElement that generates inner content */ private $content = null; + /** onclick handler */ + private $onclick = null; /** * Constructor. @@ -3674,13 +3685,27 @@ class htmlSpan extends htmlElement { if (($this->cssClasses != null) && (sizeof($this->cssClasses) > 0)) { $classesValue = ' class="' . implode(' ', $this->cssClasses) . '"'; } - echo ''; + $onclickHandler = ''; + if (!empty($this->onclick)) { + $onclickHandler = ' onclick="' . $this->onclick . '"'; + } + echo ''; if ($this->content != null) { $return = $this->content->generateHTML($module, $input, $values, $restricted, $tabindex, $scope); } echo ''; return $return; } + + /** + * Sets the onclick event. + * + * @param string $event event handler code + */ + public function setOnclick($event) { + $this->onclick = $event; + } + } /** @@ -4715,6 +4740,8 @@ class htmlResponsiveInputCheckbox extends htmlInputCheckbox { private $renderParentHtml = false; /** long label */ private $longLabel = false; + /** label after checkbox */ + private $labelAfterCheckbox = false; /** * Constructor. @@ -4751,14 +4778,16 @@ class htmlResponsiveInputCheckbox extends htmlInputCheckbox { $row = new htmlResponsiveRow(); $tabletColumnsLabel = 6; $tabletColumnsBox = 6; + $mobileColumnsLabel = 10; + $mobileColumnsBox = 2; if ($this->longLabel) { $tabletColumnsLabel = 10; $tabletColumnsBox = 2; } // label text - $labelGroup = new htmlGroup(); - $labelGroup->addElement(new htmlOutputText($this->label)); - $row->add($labelGroup, 10, $tabletColumnsLabel, $tabletColumnsLabel, 'responsiveLabel'); + $text = new htmlSpan(new htmlOutputText($this->label)); + $text->setCSSClasses($this->cssClasses); + $text->setOnclick('jQuery(\'#' . $this->name . '\').prop(\'checked\',!jQuery(\'#' . $this->name . '\').prop(\'checked\')); jQuery(\'#' . $this->name . '\').change();'); // input field $fieldGroup = new htmlGroup(); $fieldGroup->addElement($this); @@ -4767,7 +4796,14 @@ class htmlResponsiveInputCheckbox extends htmlInputCheckbox { $helpLink->setCSSClasses(array('margin-left5 align-unset-img')); $fieldGroup->addElement($helpLink); } - $row->add($fieldGroup, 2, $tabletColumnsBox, $tabletColumnsBox, 'responsiveField nowrap'); + if ($this->labelAfterCheckbox) { + $row->add($fieldGroup, $mobileColumnsBox, $tabletColumnsBox, $tabletColumnsBox, 'responsiveLabel nowrap'); + $row->add($text, $mobileColumnsLabel, $tabletColumnsLabel, $tabletColumnsLabel, 'responsiveField'); + } + else { + $row->add($text, $mobileColumnsLabel, $tabletColumnsLabel, $tabletColumnsLabel, 'responsiveLabel'); + $row->add($fieldGroup, $mobileColumnsBox, $tabletColumnsBox, $tabletColumnsBox, 'responsiveField nowrap'); + } return $row->generateHTML($module, $input, $values, $restricted, $tabindex, $scope); } @@ -4779,6 +4815,15 @@ class htmlResponsiveInputCheckbox extends htmlInputCheckbox { return '.row'; } + /** + * Sets if the label should be shown after the checkbox instead before it. + * + * @param bool $labelAfterCheckbox show label after box + */ + public function setLabelAfterCheckbox($labelAfterCheckbox = true) { + $this->labelAfterCheckbox = $labelAfterCheckbox; + } + } /** diff --git a/lam/lib/lists.inc b/lam/lib/lists.inc index 7ad71fc6..d8aa0feb 100644 --- a/lam/lib/lists.inc +++ b/lam/lib/lists.inc @@ -759,7 +759,7 @@ class lamList { $selAccounts[] = $id; } // get possible PDF structures - $pdf_structures = \LAM\PDF\getPDFStructures($this->type->getId()); + $pdf_structures = \LAM\PDF\getPDFStructures($this->type->getId(), $_SESSION['config']->getName()); $this->printHeader(); diff --git a/lam/lib/modules.inc b/lam/lib/modules.inc index aacba5a4..b97f69e3 100644 --- a/lam/lib/modules.inc +++ b/lam/lib/modules.inc @@ -1454,7 +1454,7 @@ class accountContainer { */ private function loadProfileIfRequested() { if (isset($_POST['accountContainerLoadProfile']) && isset($_POST['accountContainerSelectLoadProfile'])) { - $profile = \LAM\PROFILES\loadAccountProfile($_POST['accountContainerSelectLoadProfile'], $this->type->getId()); + $profile = \LAM\PROFILES\loadAccountProfile($_POST['accountContainerSelectLoadProfile'], $this->type->getId(), $_SESSION['config']->getName()); $this->lastLoadedProfile = $_POST['accountContainerSelectLoadProfile']; // pass profile to each module $modules = array_keys($this->module); @@ -1775,7 +1775,7 @@ class accountContainer { $this->lastLoadedProfile = $cookieProfileName; } } - $profile = \LAM\PROFILES\loadAccountProfile($profileName, $this->type->getId()); + $profile = \LAM\PROFILES\loadAccountProfile($profileName, $this->type->getId(), $_SESSION['config']->getName()); // pass profile to each module $modules = array_keys($this->module); foreach ($modules as $module) $this->module[$module]->load_profile($profile); diff --git a/lam/lib/pdf.inc b/lam/lib/pdf.inc index de2120d2..cdf8f54a 100644 --- a/lam/lib/pdf.inc +++ b/lam/lib/pdf.inc @@ -61,7 +61,7 @@ include_once('pdfstruct.inc'); function createModulePDF($accounts, $pdf_structure, $font, $returnAsString = false) { $account_type = $accounts[0]->get_type(); // Get PDF structure from xml file - $reader = new PDFStructureReader(); + $reader = new PDFStructureReader($_SESSION['config']->getName()); $structure = $reader->read($account_type->getId(), $pdf_structure); // get list of PDF keys $pdfKeys = array(); diff --git a/lam/lib/pdfstruct.inc b/lam/lib/pdfstruct.inc index c6b7635d..ef73e703 100644 --- a/lam/lib/pdfstruct.inc +++ b/lam/lib/pdfstruct.inc @@ -34,6 +34,11 @@ use \LAM\ImageUtils\ImageManipulationFactory; /** LAM configuration */ include_once(__DIR__ . "/config.inc"); +/** + * Use as server profile name to manage global templates. + */ +const GLOBAL_PROFILE = '__GLOBAL__'; + /** LDAP object */ include_once(__DIR__ . "/ldap.inc"); @@ -44,18 +49,15 @@ include_once(__DIR__ . "/ldap.inc"); * @param string $typeId the account type * @param string $profile server profile name * - * @return array All available PDF structure definitions for the submitted account + * @return string[] All available PDF structure definitions for the submitted account * scope. Each entry is a string being the filename that may be passed to the * createModulePDF() function as second argument. */ -function getPDFStructures($typeId, $profile = null) { +function getPDFStructures($typeId, $profile) { $return = array(); if (!preg_match('/[a-zA-Z]+/', $typeId)) { return null; } - if (!isset($profile)) { - $profile = $_SESSION['config']->getName(); - } $path = dirname(__FILE__) . '/../config/pdf/' . $profile; if(is_dir($path)) { $dirHandle = opendir($path); @@ -75,14 +77,14 @@ function getPDFStructures($typeId, $profile = null) { * * @param string $typeId account type * @param string $name Name of definition to delete - * + * @param string $serverProfileName server profile name * @return boolean True if file was deleted or false if a problem occurred. */ -function deletePDFStructure($typeId, $name) { +function deletePDFStructure($typeId, $name, $serverProfileName) { if (!isValidPDFStructureName($name) || !preg_match('/[a-zA-Z]+/',$typeId)) { return false; } - $file = dirname(__FILE__) . '/../config/pdf/' . $_SESSION['config']->getName() . '/' . $name . '.' . $typeId . '.xml'; + $file = dirname(__FILE__) . '/../config/pdf/' . $serverProfileName . '/' . $name . '.' . $typeId . '.xml'; if(is_file($file) && is_writable($file)) { return unlink($file); } @@ -95,11 +97,12 @@ function deletePDFStructure($typeId, $name) { /** * This function returns an array with all aviliable logo images. * - * @return array list of logo files + * @param string $serverProfileName server profile name + * @return array list of logo files (array('filename' => PATH, 'infos' => array(width, height))) */ -function getAvailableLogos() { +function getAvailableLogos($serverProfileName) { $return = array(); - $dirPath = dirname(__FILE__) . '/../config/pdf/' . $_SESSION['config']->getName() . '/logos/'; + $dirPath = dirname(__FILE__) . '/../config/pdf/' . $serverProfileName . '/logos/'; $dirHandle = opendir($dirPath); while($file = readdir($dirHandle)) { if(!is_dir($file) && $file != '.' && $file != '..' && preg_match('/\\.(jpg|png)$/i',$file)) { @@ -120,7 +123,7 @@ function getAvailableLogos() { * @param \LAM\TYPES\ConfiguredType $sourceType source type * @param string $sourceStructureName structure name * @param \LAM\TYPES\ConfiguredType $targetType target type - * @throws Exception + * @throws LAMException error during copy */ function copyStructure($sourceType, $sourceStructureName, $targetType) { if (!isValidPDFStructureName($sourceStructureName)) { @@ -165,13 +168,17 @@ function copyStructureToTemplates($sourceType, $sourceName) { * * @param String $file full path of temporary file * @param String $name file name - * @return StatusMessage status message to display + * @param string $serverProfileName server profile name + * @return htmlStatusMessage status message to display */ -function uploadPDFLogo($file, $name) { +function uploadPDFLogo($file, $name, $serverProfileName) { if (!preg_match('/[a-zA-Z0-9_-]+\\.(png)|(jpg)/i', $name)) { return new htmlStatusMessage('ERROR', _('Unable to upload logo file.'), _('The file name must end with ".png" or ".jpg".')); } - $dirPath = dirname(__FILE__) . '/../config/pdf/' . $_SESSION['config']->getName() . '/logos/'; + if ($serverProfileName === GLOBAL_PROFILE) { + $serverProfileName = '../templates/pdf'; + } + $dirPath = dirname(__FILE__) . '/../config/pdf/' . $serverProfileName . '/logos/'; $success = copy($file, $dirPath . '/' . $name); if ($success) { return new htmlStatusMessage('INFO', _('Uploaded logo file.'), $name); @@ -185,12 +192,13 @@ function uploadPDFLogo($file, $name) { * Deletes a PDF logo file. * * @param String $name file name + * @param string $serverProfileName server profile name * @return StatusMessage status message to display */ -function deletePDFLogo($name) { +function deletePDFLogo($name, $serverProfileName) { // check if valid file $found = false; - $logos = getAvailableLogos(); + $logos = getAvailableLogos($serverProfileName); foreach ($logos as $logo) { if ($logo['filename'] === $name) { $found = true; @@ -203,9 +211,9 @@ function deletePDFLogo($name) { // check if still in use $typeManager = new \LAM\TYPES\TypeManager(); $activeTypes = $typeManager->getConfiguredTypes(); - $reader = new PDFStructureReader(); + $reader = new PDFStructureReader($serverProfileName); foreach ($activeTypes as $type) { - $structures = getPDFStructures($type->getId()); + $structures = getPDFStructures($type->getId(), $serverProfileName); foreach ($structures as $structure) { try { $data = $reader->read($type->getId(), $structure); @@ -220,7 +228,7 @@ function deletePDFLogo($name) { } } // delete file - $dirPath = dirname(__FILE__) . '/../config/pdf/' . $_SESSION['config']->getName() . '/logos/'; + $dirPath = dirname(__FILE__) . '/../config/pdf/' . $serverProfileName . '/logos/'; $success = @unlink($dirPath . '/' . $name); if ($success) { return new htmlStatusMessage('INFO', _('Logo file deleted.'), $name); @@ -242,21 +250,8 @@ function isValidPDFStructureName($name) { * Installs template structures to the current server profile. */ function installPDFTemplates() { - $templatePath = dirname(__FILE__) . '/../config/templates/pdf'; - $templateDir = @dir($templatePath); - $allTemplates = array(); - if ($templateDir) { - $entry = $templateDir->read(); - while ($entry){ - $parts = explode('.', $entry); - if ((strlen($entry) > 3) && (sizeof($parts) == 3)) { - $name = $parts[0]; - $scope = $parts[1]; - $allTemplates[$scope][] = $name; - } - $entry = $templateDir->read(); - } - } + $templatePath = __DIR__ . '/../config/templates/pdf'; + $allTemplates = getPdfTemplateNames(); $basePath = dirname(__FILE__) . '/../config/pdf/' . $_SESSION['config']->getName(); if (!file_exists($basePath)) { mkdir($basePath, 0700, true); @@ -278,20 +273,75 @@ function installPDFTemplates() { if (!file_exists($basePath . '/logos')) { mkdir($basePath . '/logos'); } - $templatePath = dirname(__FILE__) . '/../config/templates/pdf/logos'; + $logos = getPdfTemplateLogoNames(); + foreach ($logos as $logo) { + $path = $basePath . '/logos/' . $logo; + $template = $templatePath . '/logos/' . $logo; + if (!is_file($path)) { + logNewMessage(LOG_DEBUG, 'Copy template ' . $template . ' to ' . $path); + @copy($template, $path); + } + } +} + +/** + * Returns all PDF template names. + * + * @return array names (array('user' => array('default'))) + */ +function getPdfTemplateNames() { + $templatePath = __DIR__ . '/../config/templates/pdf'; $templateDir = @dir($templatePath); + $allTemplates = array(); if ($templateDir) { $entry = $templateDir->read(); while ($entry){ - $path = $basePath . '/logos/' . $entry; - if ((strpos($entry, '.') !== 0) && !is_file($path)) { - $template = $templatePath . '/' . $entry; - logNewMessage(LOG_DEBUG, 'Copy template ' . $template . ' to ' . $path); - @copy($template, $path); + $parts = explode('.', $entry); + if ((strlen($entry) > 3) && (sizeof($parts) == 3)) { + $name = $parts[0]; + $scope = $parts[1]; + $allTemplates[$scope][] = $name; } $entry = $templateDir->read(); } } + return $allTemplates; +} + +/** + * Returns all PDF template logo names. + * + * @return array names (array('user' => array('default.png'))) + */ +function getPdfTemplateLogoNames() { + $templatePath = __DIR__ . '/../config/templates/pdf/logos'; + $templateDir = @dir($templatePath); + $logos = array(); + if ($templateDir) { + $entry = $templateDir->read(); + while ($entry){ + if ((strpos($entry, '.') !== 0) && is_file($templatePath . '/' . $entry)) { + $logos[] = $entry; + } + $entry = $templateDir->read(); + } + } + return $logos; +} + +/** + * Returns the binary data of the PDF template logo. + * + * @param string $name file name (without path) + * @return string binary + */ +function getPdfTemplateLogoBinary($name) { + $templatePath = __DIR__ . '/../config/templates/pdf/logos'; + $fileName = $templatePath . '/' . $name; + $handle = fopen($fileName, 'r'); + $logoBinary = fread($handle, 100000000); + fclose($handle); + return $logoBinary; } /** @@ -301,6 +351,22 @@ function installPDFTemplates() { */ class PDFStructureReader { + private $serverProfileName; + + /** + * Constructor. + * + * @param $serverProfileName server profile name + */ + public function __construct($serverProfileName) { + if ($serverProfileName === GLOBAL_PROFILE) { + $this->serverProfileName = '../templates/pdf'; + } + else { + $this->serverProfileName = $serverProfileName; + } + } + /** * Reads a PDF structure. * @@ -324,7 +390,7 @@ class PDFStructureReader { * @return string file name */ protected function getFileName($typeId, $name) { - return dirname(__FILE__) . '/../config/pdf/' . $_SESSION['config']->getName() . '/' . $name . '.' . $typeId . '.xml'; + return dirname(__FILE__) . '/../config/pdf/' . $this->serverProfileName . '/' . $name . '.' . $typeId . '.xml'; } /** @@ -334,6 +400,7 @@ class PDFStructureReader { * @return PDFStructure structure */ private function readPDFFile($file) { + logNewMessage(LOG_DEBUG, $file); $xml = new \XMLReader(); $xml->open($file); $structure = new PDFStructure(); @@ -411,12 +478,29 @@ class PDFStructureReader { */ class PDFStructureWriter { + private $serverProfileName; + + /** + * Constructor. + * + * @param string $serverProfileName server profile name + */ + public function __construct($serverProfileName) { + if ($serverProfileName === GLOBAL_PROFILE) { + $this->serverProfileName = '../templates/pdf'; + } + else { + $this->serverProfileName = $serverProfileName; + } + } + /** * Writes the PDF structure to disk. * * @param string $typeId type ID * @param string $name structure name * @param PDFStructure $structure structure + * @throws LAMException error during write */ public function write($typeId, $name, $structure) { $fileName = $this->getFileName($typeId, $name); @@ -430,16 +514,18 @@ class PDFStructureWriter { * @param string $typeId type ID * @param string $name structure name * @return string file name + * @throws LAMException file not valid or not writable */ 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.')); + $baseDir = __DIR__ . '/../config/pdf/' . $this->serverProfileName; + if(!is_writable($baseDir)) { + throw new \LAMException(_('Could not save PDF structure, access denied to ' . $baseDir . '.')); } - return dirname(__FILE__) . '/../config/pdf/' . $_SESSION['config']->getName() . '/' . $name . '.' . $typeId . '.xml'; + return $baseDir . '/' . $name . '.' . $typeId . '.xml'; } /** @@ -488,6 +574,7 @@ class PDFStructureWriter { * * @param string $xml XML * @param string $file file name + * @throws LAMException error during write */ protected function writeXML($xml, $file) { $handle = @fopen($file,'w'); @@ -520,6 +607,57 @@ class PDFStructure { private $sections = array(); + /** + * Returns an array representation of the structure. + * + * @return array export data + */ + public function export() { + $data = array(); + $data['title'] = $this->title; + $data['foldingMarks'] = $this->foldingMarks; + $data['logo'] = $this->logo; + $data['sections'] = array(); + foreach($this->sections as $section) { + $type = ($section instanceof PDFTextSection) ? 'text' : 'entry'; + $sectionData = $section->export(); + $data['sections'][] = array( + 'type' => $type, + 'data' => $sectionData + ); + } + return $data; + } + + /** + * Imports an array representation of the structure. + * + * @param array $data import data + */ + public function import($data) { + if (isset($data['title'])) { + $this->title = $data['title']; + } + if (isset($data['foldingMarks'])) { + $this->foldingMarks = $data['foldingMarks']; + } + if (isset($data['logo'])) { + $this->logo = $data['logo']; + } + if (isset($data['sections'])) { + foreach($data['sections'] as $section) { + if ($section['type'] === 'text') { + $this->sections[] = new PDFTextSection($section['data']); + } + else { + $entrySection = new PDFEntrySection(null); + $entrySection->import($section['data']); + $this->sections[] = $entrySection; + } + } + } + } + /** * Returns the logo file path. * @@ -612,6 +750,15 @@ class PDFTextSection { $this->text = $text; } + /** + * Exports the section. + * + * @return string text + */ + public function export() { + return $this->getText(); + } + /** * Returns the text. * @@ -631,7 +778,7 @@ class PDFTextSection { class PDFEntrySection { private $title; - private $entries; + private $entries = array(); /** * Constructor @@ -642,6 +789,37 @@ class PDFEntrySection { $this->title = $title; } + /** + * Exports the section. + * + * @return array export data + */ + public function export() { + $data = array(); + $data['title'] = $this->title; + $data['entries'] = array(); + foreach($this->getEntries() as $entry) { + $data['entries'][] = $entry->getKey(); + } + return $data; + } + + /** + * Imports the section. + * + * @param array $data import data + */ + public function import($data) { + if (isset($data['title'])) { + $this->title = $data['title']; + } + if ($data['entries']) { + foreach($data['entries'] as $entry) { + $this->entries[] = new PDFSectionEntry($entry); + } + } + } + /** * Returns if the title is an attribute value. * diff --git a/lam/lib/persistence.inc b/lam/lib/persistence.inc new file mode 100644 index 00000000..2c720b57 --- /dev/null +++ b/lam/lib/persistence.inc @@ -0,0 +1,652 @@ +_getMainConfiguration(); + $jsonData = array(); + $jsonData['mainConfig'] = $this->_getMainConfigData($mainCfg); + $jsonData['certificates'] = $this->_getCertificates($mainCfg); + $serverProfileNames = getConfigProfiles(); + $serverProfiles = array(); + foreach ($serverProfileNames as $serverProfileName) { + $serverProfiles[$serverProfileName] = new \LAMConfig($serverProfileName); + } + $jsonData['serverProfiles'] = $this->_getServerProfiles($serverProfiles); + $jsonData['accountProfiles'] = $this->_getAccountProfiles($serverProfiles); + $jsonData['accountProfileTemplates'] = $this->_getAccountProfileTemplates(); + $jsonData['pdfProfiles'] = $this->_getPdfProfiles($serverProfiles); + $jsonData['pdfProfileTemplates'] = $this->_getPdfProfileTemplates(); + $jsonData['selfServiceProfiles'] = $this->_getSelfServiceProfiles(); + /** + * TODO + * + * webauthn + */ + return json_encode($jsonData); + } + + /** + * Returns the main configuration. + * + * @return LAMCfgMain main config + */ + public function _getMainConfiguration() { + return new LAMCfgMain(); + } + + /** + * Internal function to read master configuration. + * + * @param LAMCfgMain $mainCfg main config + * @return array data + */ + public function _getMainConfigData($mainCfg) { + return $mainCfg->exportData(); + } + + /** + * Returns the certificate file content. + * + * @param LAMCfgMain $mainCfg main config + * @return array data + */ + public function _getCertificates($mainCfg) { + return $mainCfg->exportCertificates(); + } + + /** + * Returns the content of the server profiles. + * + * @param array $serverProfiles list of server profiles (name => object) + * @return array $data + */ + public function _getServerProfiles($serverProfiles) { + $data = array(); + foreach ($serverProfiles as $profileName => $serverProfile) { + $data[$profileName] = $serverProfile->exportData(); + } + return $data; + } + + /** + * Returns the content of the account profiles. + * + * @param array $serverProfiles list of server profiles (name => object) + * @return array $data + */ + public function _getAccountProfiles($serverProfiles) { + $data = array(); + foreach ($serverProfiles as $profileName => $serverProfile) { + foreach ($serverProfile->get_ActiveTypes() as $typeId) { + $accountProfileNames = getAccountProfiles($typeId, $profileName); + foreach ($accountProfileNames as $accountProfileName) { + $accountProfile = loadAccountProfile($accountProfileName, $typeId, $profileName); + $data[$profileName][$typeId][$accountProfileName] = $accountProfile; + } + } + } + return $data; + } + + /** + * Returns the content of the account profile templates. + * + * @return array $data + * @throws LAMException error reading template + */ + public function _getAccountProfileTemplates() { + $data = array(); + $accountProfileTemplateNames = getProfileTemplateNames(); + foreach ($accountProfileTemplateNames as $scope => $templateNames) { + foreach ($templateNames as $templateName) { + $accountProfileTemplate = loadTemplateAccountProfile($templateName, $scope); + $data[$scope][$templateName] = $accountProfileTemplate; + } + } + return $data; + } + + /** + * Returns the content of the PDF profiles. + * + * @param array $serverProfiles list of server profiles (name => object) + * @return array $data + */ + public function _getPdfProfiles($serverProfiles) { + $data = array(); + foreach ($serverProfiles as $profileName => $serverProfile) { + foreach ($serverProfile->get_ActiveTypes() as $typeId) { + $pdfProfileNames = getPDFStructures($typeId, $profileName); + $reader = new PDFStructureReader($profileName); + foreach ($pdfProfileNames as $pdfProfileName) { + $pdfStructure = $reader->read($typeId, $pdfProfileName); + $data[$profileName]['structures'][$typeId][$pdfProfileName] = $pdfStructure->export(); + } + } + $logoData = getAvailableLogos($profileName); + foreach ($logoData as $logo) { + $logoFileName = $logo['filename']; + $logoPath = __DIR__ . '/../config/pdf/' . $profileName . '/logos/' . $logoFileName; + $handle = fopen($logoPath, 'r'); + $logoBinary = fread($handle, 100000000); + fclose($handle); + $data[$profileName]['logos'][$logoFileName] = base64_encode($logoBinary); + } + } + return $data; + } + + /** + * Returns the content of the account profile templates. + * + * @return array $data + * @throws LAMException error reading template + */ + public function _getPdfProfileTemplates() { + $data = array(); + $pdfTemplateNames = getPdfTemplateNames(); + $reader = new PDFStructureReader(\LAM\PDF\GLOBAL_PROFILE); + foreach ($pdfTemplateNames as $scope => $templateNames) { + foreach ($templateNames as $templateName) { + $pdfStructure = $reader->read($scope, $templateName); + $data['structures'][$scope][$templateName] = $pdfStructure->export(); + } + } + $logoNames = getPdfTemplateLogoNames(); + foreach ($logoNames as $logoName) { + $data['logos'][$logoName] = base64_encode(getPdfTemplateLogoBinary($logoName)); + } + return $data; + } + + /** + * Returns the content of the self service profiles. + * + * @return array data + */ + public function _getSelfServiceProfiles() { + $data = array(); + $profileTypes = getSelfServiceProfiles(); + foreach ($profileTypes as $profileType => $profileNames) { + foreach ($profileNames as $profileName) { + $profile = loadSelfServiceProfile($profileName, $profileType); + if ($profile === false) { + continue; + } + $data[$profileType][$profileName] = $profile->export(); + } + } + return $data; + } + +} + +/** + * Importer for LAM's configuration data. + */ +class ConfigDataImporter { + + /** + * Returns a list of possible import objects. + * + * @param string $json JSON data + * @return ImporterStep[] steps + * @throws LAMException if invalid format + */ + public function getPossibleImportSteps($json) { + $data = json_decode($json, true); + if ($data === null) { + throw new LAMException(_('Unable to read import file.')); + } + $steps = array(); + foreach ($data as $key => $value) { + switch ($key) { + case 'mainConfig': + $steps[] = new ImporterStep(_('General settings'), 'mainConfig', $value); + break; + case 'certificates': + $steps[] = new ImporterStep(_('SSL certificates'), 'certificates', $value); + break; + case 'serverProfiles': + $mainStep = new ImporterStep(_('Server profiles'), 'serverProfiles', $value); + foreach ($value as $profileName => $profileData) { + $mainStep->addSubStep(new ImporterStep($profileName, 'serverProfile_' . $profileName, $profileData)); + } + $steps[] = $mainStep; + break; + case 'accountProfiles': + $mainStep = new ImporterStep(_('Account profiles'), 'accountProfiles', $value); + foreach ($value as $profileName => $profileData) { + $mainStep->addSubStep(new ImporterStep($profileName, 'accountProfile_' . $profileName, $profileData)); + } + $steps[] = $mainStep; + break; + case 'accountProfileTemplates': + $steps[] = new ImporterStep(_('Account profiles') . ' - ' . _('Global templates'), 'accountProfileTemplates', $value); + break; + case 'pdfProfiles': + $mainStep = new ImporterStep(_('PDF structures'), 'pdfProfiles', $value); + foreach ($value as $profileName => $profileData) { + $mainStep->addSubStep(new ImporterStep($profileName, 'pdfProfile_' . $profileName, $profileData)); + } + $steps[] = $mainStep; + break; + case 'pdfProfileTemplates': + $steps[] = new ImporterStep(_('PDF structures') . ' - ' . _('Global templates'), 'pdfProfileTemplates', $value); + break; + case 'selfServiceProfiles': + $steps[] = new ImporterStep(_('Self service profiles'), 'selfServiceProfiles', $value); + break; + default: + logNewMessage(LOG_WARNING, 'Unknown import type: ' . $key); + } + } + if (empty($steps)) { + throw new LAMException(_('Unable to read import file.')); + } + return $steps; + } + + /** + * Runs the actual import. + * + * @param ImporterStep[] $steps import steps + * @throws LAMException if error occurred + */ + public function runImport($steps) { + foreach ($steps as $step) { + if (!$step->isActive()) { + continue; + } + $key = $step->getKey(); + switch ($key) { + case 'mainConfig': + $this->importMainConfig($step->getValue()); + break; + case 'certificates': + $this->importCertificates($step->getValue()); + break; + case 'serverProfiles': + $this->importServerProfiles($step); + break; + case 'accountProfiles': + $this->importAccountProfiles($step); + break; + case 'accountProfileTemplates': + $this->importAccountProfileTemplates($step); + break; + case 'pdfProfiles': + $this->importPdfProfiles($step); + break; + case 'pdfProfileTemplates': + $this->importPdfProfileTemplates($step); + break; + case 'selfServiceProfiles': + $this->importSelfServiceProfiles($step); + break; + default: + logNewMessage(LOG_WARNING, 'Unknown import type: ' . $key); + } + } + } + + /** + * Imports the main configuration. + * + * @param array $data main config data + * @throws LAMException error during import + */ + private function importMainConfig($data) { + $cfgMain = new LAMCfgMain(); + $cfgMain->importData($data); + $cfgMain->save(); + } + + /** + * Imports the SSL certificates. + * + * @param null|string $data file content + * @throws LAMException error during import + */ + private function importCertificates($data) { + $cfgMain = new LAMCfgMain(); + $cfgMain->importCertificates($data); + } + + /** + * Imports the server profiles. + * + * @param ImporterStep $step step + * @throws LAMException error during import + */ + private function importServerProfiles($step) { + $failedProfiles = array(); + foreach ($step->getSubSteps() as $profileStep) { + if (!$profileStep->isActive()) { + continue; + } + $data = $profileStep->getValue(); + $profileName = str_replace('serverProfile_', '', $profileStep->getKey()); + $serverProfile = new LAMConfig($profileName); + $serverProfile->importData($data); + $result = $serverProfile->save(); + if ($result === LAMConfig::SAVE_FAIL) { + $failedProfiles[] = $profileName; + } + } + if (!empty($failedProfiles)) { + throw new LAMException(_('Unable to save server profile.'), implode(', ', $failedProfiles)); + } + } + + /** + * Imports the account profiles. + * + * @param ImporterStep $step step + * @throws LAMException error during import + */ + private function importAccountProfiles($step) { + $failedProfiles = array(); + foreach ($step->getSubSteps() as $profileStep) { + if (!$profileStep->isActive()) { + continue; + } + $data = $profileStep->getValue(); + $serverProfileName = str_replace('accountProfile_', '', $profileStep->getKey()); + $serverProfile = new LAMConfig($serverProfileName); + foreach ($data as $typeId => $accountProfiles) { + foreach ($accountProfiles as $accountProfileName => $accountProfileData) { + $result = saveAccountProfile($accountProfileData, $accountProfileName, $typeId, $serverProfile); + if (!$result) { + $failedProfiles[] = $serverProfileName . ':' . $typeId . ':' . $accountProfileName; + } + } + } + } + if (!empty($failedProfiles)) { + throw new LAMException(_('Unable to save account profile.'), implode(', ', $failedProfiles)); + } + } + + /** + * Imports the account profile templates. + * + * @param ImporterStep $step step + * @throws LAMException error during import + */ + private function importAccountProfileTemplates($step) { + $data = $step->getValue(); + foreach ($data as $typeId => $accountProfileTemplates) { + foreach ($accountProfileTemplates as $accountProfileTemplateName => $accountProfileData) { + installTemplateAccountProfile($typeId, $accountProfileTemplateName, $accountProfileData); + } + } + } + + /** + * Imports the PDF profiles. + * + * @param ImporterStep $step step + * @throws LAMException error during import + */ + private function importPdfProfiles($step) { + $failedProfiles = array(); + foreach ($step->getSubSteps() as $profileStep) { + if (!$profileStep->isActive()) { + continue; + } + $data = $profileStep->getValue(); + $serverProfileName = str_replace('pdfProfile_', '', $profileStep->getKey()); + if (isset($data['structures'])) { + $writer = new PDFStructureWriter($serverProfileName); + foreach ($data['structures'] as $typeId => $pdfProfiles) { + foreach ($pdfProfiles as $pdfProfileName => $pdfProfileData) { + $structure = new PDFStructure(); + $structure->import($pdfProfileData); + try { + $writer->write($typeId, $pdfProfileName, $structure); + } + catch (LAMException $e) { + logNewMessage(LOG_ERR, $e->getTitle() . ' ' . $e->getMessage()); + $failedProfiles[] = $serverProfileName . ':' . $typeId . ':' . $pdfProfileName; + } + } + } + } + if (isset($data['logos'])) { + foreach ($data['logos'] as $logoFileName => $logoData) { + $tempFilePath = tempnam("/tmp", "lam"); + $tempFile = fopen($tempFilePath, "w"); + $logoBinary = base64_decode($logoData); + fwrite($tempFile, $logoBinary); + fclose($tempFile); + uploadPDFLogo($tempFilePath, $logoFileName, $serverProfileName); + unlink($tempFilePath); + } + } + } + if (!empty($failedProfiles)) { + throw new LAMException(_('Could not save PDF structure, access denied.'), implode(', ', $failedProfiles)); + } + } + + /** + * Imports the PDF profile templates. + * + * @param ImporterStep $step step + * @throws LAMException error during import + */ + private function importPdfProfileTemplates($step) { + $failedNames = array(); + $data = $step->getValue(); + if (isset($data['structures'])) { + $writer = new PDFStructureWriter(\LAM\PDF\GLOBAL_PROFILE); + foreach ($data['structures'] as $typeId => $pdfProfiles) { + foreach ($pdfProfiles as $pdfProfileName => $pdfProfileData) { + $structure = new PDFStructure(); + $structure->import($pdfProfileData); + try { + $writer->write($typeId, $pdfProfileName, $structure); + } + catch (LAMException $e) { + $failedNames[] = $typeId . ':' . $pdfProfileName; + logNewMessage(LOG_ERR, $e->getTitle() . ' ' . $e->getMessage()); + } + } + } + } + $failedLogos = array(); + if (isset($data['logos'])) { + foreach ($data['logos'] as $logoFileName => $logoData) { + $tempFilePath = tempnam("/tmp", "lam"); + $tempFile = fopen($tempFilePath, "w"); + $logoBinary = base64_decode($logoData); + fwrite($tempFile, $logoBinary); + fclose($tempFile); + $message = uploadPDFLogo($tempFilePath, $logoFileName, \LAM\PDF\GLOBAL_PROFILE); + unlink($tempFilePath); + if ($message->getType() === 'ERROR') { + $failedLogos[] = $logoFileName; + } + } + } + if (!empty($failedNames)) { + throw new LAMException(_('Could not save PDF structure, access denied.'), implode(', ', $failedNames)); + } + if (!empty($failedLogos)) { + throw new LAMException(_('Unable to upload logo file.'), implode(', ', $failedLogos)); + } + } + + /** + * Imports the self service profiles. + * + * @param ImporterStep $step importer step + * @throws LAMException error saving profiles + */ + private function importSelfServiceProfiles($step) { + $failedNames = array(); + $data = $step->getValue(); + foreach ($data as $typeId => $profileData) { + foreach ($profileData as $profileName => $currentProfileData) { + $profile = selfServiceProfile::import($currentProfileData); + $result = saveSelfServiceProfile($profileName, $typeId, $profile); + if (!$result) { + $failedNames[] = $profileName; + } + } + } + if (!empty($failedNames)) { + throw new LAMException(_('Unable to save profile!'), implode(', ', $failedNames)); + } + } + +} + +/** + * Step of the import process. + */ +class ImporterStep { + + private $label; + private $key; + private $value; + private $active = false; + private $subSteps = array(); + + /** + * Constructor. + * + * @param string $label label + * @param string $key key + * @param array $value value + */ + public function __construct($label, $key, $value) { + $this->label = $label; + $this->key = $key; + $this->value = $value; + } + + /** + * Returns the label. + * + * @return string label + */ + public function getLabel() { + return $this->label; + } + + /** + * Returns the key. + * + * @return string key + */ + public function getKey() { + return $this->key; + } + + /** + * Returns if this step should be executed. + * + * @return bool active + */ + public function isActive(): bool { + return $this->active; + } + + /** + * Sets if this step should be executed. + * + * @param bool $active active + */ + public function setActive(bool $active) { + $this->active = $active; + } + + /** + * Returns the value. + * + * @return string value + */ + public function getValue() { + return $this->value; + } + + /** + * Adds a sub-step. + * + * @param ImporterStep $subStep sub-step + */ + public function addSubStep($subStep) { + $this->subSteps[] = $subStep; + } + + /** + * Returns the sub-steps. + * + * @return ImporterStep[] sub-steps + */ + public function getSubSteps() { + return $this->subSteps; + } + +} diff --git a/lam/lib/profiles.inc b/lam/lib/profiles.inc index 313b70ff..72f3cfa4 100644 --- a/lam/lib/profiles.inc +++ b/lam/lib/profiles.inc @@ -1,5 +1,6 @@ read(); while ($entry){ @@ -79,30 +79,45 @@ function profileExists($name, $typeId) { } /** -* Loads an profile of the given account type -* -* @param string $profile name of the profile (without . extension) -* @param string $typeId account type -* @return array hash array (attribute => value) -*/ -function loadAccountProfile($profile, $typeId) { - $typeManager = new \LAM\TYPES\TypeManager(); - $type = $typeManager->getConfiguredType($typeId); - if (!isValidProfileName($profile) || !preg_match("/^[a-z0-9_]+$/i", $typeId) || ($type == null)) { - return false; + * Loads an profile of the given account type + * + * @param string $profile name of the profile (without . extension) + * @param string $typeId account type + * @param string $serverProfileName server profile name + * @return array hash array (attribute => value) + */ +function loadAccountProfile($profile, $typeId, $serverProfileName) { + if (!isValidProfileName($profile) || !preg_match("/^[a-z0-9_]+$/i", $typeId)) { + logNewMessage(LOG_NOTICE, "Invalid account profile name: $serverProfileName:$profile:$typeId"); + return array(); } + $file = substr(__FILE__, 0, strlen(__FILE__) - 17) . "/config/profiles/" . $serverProfileName . '/' . $profile . "." . $typeId; + try { + return readAccountProfileFile($file); + } catch (LAMException $e) { + StatusMessage('ERROR', $e->getTitle(), $e->getMessage()); + } + return array(); +} + +/** + * Reads an account profile from the given file name. + * + * @param string $fileName file name + * @return array hash array (attribute => value) + * @throws LAMException error reading file + */ +function readAccountProfileFile($fileName) { $settings = array(); - $file = substr(__FILE__, 0, strlen(__FILE__) - 17) . "/config/profiles/" . $_SESSION['config']->getName() . '/' . $profile . "." . $typeId; - if (is_file($file)) { - $file = @fopen($file, "r"); + if (is_file($fileName)) { + $file = @fopen($fileName, "r"); if ($file) { while (!feof($file)) { $line = fgets($file, 1024); - if (($line === false) || ($line == "\n") || ($line[0] == "#")) { + if (($line === false) || ($line == '') || ($line == "\n") || ($line[0] == "#")) { continue; // ignore comments } // search keywords - $parts = array(); $parts = explode(": ", $line); if (sizeof($parts) == 2) { $option = $parts[0]; @@ -113,15 +128,15 @@ function loadAccountProfile($profile, $typeId) { } } fclose($file); + return $settings; } else { - StatusMessage("ERROR", "", _("Unable to load profile!") . " " . $file); + throw new LAMException(_("Unable to load profile!"), $fileName); } } else { - StatusMessage("ERROR", "", _("Unable to load profile!") . " " . $file); + throw new LAMException(_("Unable to load profile!"), $fileName); } - return $settings; } /** @@ -132,27 +147,40 @@ function loadAccountProfile($profile, $typeId) { * @param array $attributes hash array (attribute => value) * @param string $profile name of the account profile (without . extension) * @param string $typeId account type + * @param \LAMConfig $serverProfile server profile * @return boolean true, if saving succeeded */ -function saveAccountProfile($attributes, $profile, $typeId) { - if (!isLoggedIn()) return false; +function saveAccountProfile($attributes, $profile, $typeId, $serverProfile) { // check profile name and type id - $typeManager = new \LAM\TYPES\TypeManager(); + $typeManager = new TypeManager($serverProfile); $type = $typeManager->getConfiguredType($typeId); if (!isValidProfileName($profile) || !preg_match("/^[a-z0-9_]+$/i", $typeId) || ($type == null)) { + logNewMessage(LOG_NOTICE, 'Invalid account profile name: ' . $profile . ':' . $typeId); return false; } if (!is_array($attributes)) { + logNewMessage(LOG_NOTICE, 'Invalid account profile data'); return false; } - $path = substr(__FILE__, 0, strlen(__FILE__) - 17) . "/config/profiles/" . $_SESSION['config']->getName() . '/' . $profile . "." . $typeId; - $file = @fopen($path, "w"); + $path = substr(__FILE__, 0, strlen(__FILE__) - 17) . "/config/profiles/" . $serverProfile->getName() . '/' . $profile . "." . $typeId; + return writeProfileDataToFile($path, $attributes); +} + +/** + * Writes the profile data to the given file. + * + * @param string $fileName file name + * @param array $data profile data + * @return bool writing was ok + */ +function writeProfileDataToFile($fileName, $data) { + $file = @fopen($fileName, "w"); if ($file) { - // write attributes - $keys = array_keys($attributes); + // write attributes + $keys = array_keys($data); for ($i = 0; $i < sizeof($keys); $i++) { - if (isset($attributes[$keys[$i]])) { - $line = $keys[$i] . ": " . implode("+::+", $attributes[$keys[$i]]) . "\n"; + if (isset($data[$keys[$i]])) { + $line = $keys[$i] . ": " . implode("+::+", $data[$keys[$i]]) . "\n"; } else { $line = $keys[$i] . ": \n"; @@ -163,6 +191,7 @@ function saveAccountProfile($attributes, $profile, $typeId) { fclose($file); } else { + logNewMessage(LOG_NOTICE, 'Unable to open account profile file: ' . $fileName); return false; } return true; @@ -179,7 +208,7 @@ function delAccountProfile($file, $typeId) { if (!isLoggedIn()) { return false; } - $typeManager = new \LAM\TYPES\TypeManager(); + $typeManager = new TypeManager(); $type = $typeManager->getConfiguredType($typeId); if (!isValidProfileName($file) || !preg_match("/^[a-z0-9_]+$/i", $typeId) || ($type == null)) { return false; @@ -206,7 +235,7 @@ function isValidProfileName($name) { * @param \LAM\TYPES\ConfiguredType $sourceType source type * @param string $sourceProfileName profile name * @param \LAM\TYPES\ConfiguredType $targetType target type - * @throws Exception + * @throws LAMException error during copy */ function copyAccountProfile($sourceType, $sourceProfileName, $targetType) { if (!isValidProfileName($sourceProfileName)) { @@ -229,7 +258,7 @@ function copyAccountProfile($sourceType, $sourceProfileName, $targetType) { * * @param \LAM\TYPES\ConfiguredType $sourceType source type * @param string $sourceProfileName profile name - * @throws Exception + * @throws LAMException error during copy */ function copyAccountProfileToTemplates($sourceType, $sourceProfileName) { if (!isValidProfileName($sourceProfileName)) { @@ -250,7 +279,34 @@ function copyAccountProfileToTemplates($sourceType, $sourceProfileName) { * Installs template profiles to the current server profile. */ function installProfileTemplates() { - $templatePath = dirname(__FILE__) . '/../config/templates/profiles'; + $allTemplates = getProfileTemplateNames(); + $basePath = dirname(__FILE__) . '/../config/profiles/' . $_SESSION['config']->getName(); + if (!file_exists($basePath)) { + mkdir($basePath, 0700, true); + } + $typeManager = new TypeManager(); + foreach ($typeManager->getConfiguredTypes() as $type) { + if (empty($allTemplates[$type->getScope()])) { + continue; + } + foreach ($allTemplates[$type->getScope()] as $templateName) { + $path = $basePath . '/' . $templateName . '.' . $type->getId(); + if (!is_file($path)) { + $template = getProfileTemplateFileName($type->getScope(), $templateName); + logNewMessage(LOG_DEBUG, 'Copy template ' . $template . ' to ' . $path); + @copy($template, $path); + } + } + } +} + +/** + * Returns a list of all global profile templates. + * + * @return array names (array('user' => array('default', 'extra'))) + */ +function getProfileTemplateNames() { + $templatePath = __DIR__ . '/../config/templates/profiles'; $templateDir = @dir($templatePath); $allTemplates = array(); if ($templateDir) { @@ -265,24 +321,54 @@ function installProfileTemplates() { $entry = $templateDir->read(); } } - $basePath = dirname(__FILE__) . '/../config/profiles/' . $_SESSION['config']->getName(); - if (!file_exists($basePath)) { - mkdir($basePath, 0700, true); + return $allTemplates; +} + +/** + * Returns the file name of a global template. + * + * @param string $scope e.g. user + * @param string $name profile name + * @return string file name + */ +function getProfileTemplateFileName($scope, $name) { + return __DIR__ . '/../config/templates/profiles' . '/' . $name . '.' . $scope; +} + +/** + * Loads a template profile of the given account scope. + * + * @param string $profile name of the profile (without . extension) + * @param string $scope account type + * @return array hash array (attribute => value) + * @throws LAMException error reading profile template + */ +function loadTemplateAccountProfile($profile, $scope) { + if (!isValidProfileName($profile) || !preg_match("/^[a-z0-9_]+$/i", $scope)) { + logNewMessage(LOG_NOTICE, "Invalid account profile name: $profile:$scope"); + return array(); } - $typeManager = new \LAM\TYPES\TypeManager(); - foreach ($typeManager->getConfiguredTypes() as $type) { - if (empty($allTemplates[$type->getScope()])) { - continue; - } - foreach ($allTemplates[$type->getScope()] as $templateName) { - $path = $basePath . '/' . $templateName . '.' . $type->getId(); - if (!is_file($path)) { - $template = $templatePath . '/' . $templateName . '.' . $type->getScope(); - logNewMessage(LOG_DEBUG, 'Copy template ' . $template . ' to ' . $path); - @copy($template, $path); - } - } + $fileName = getProfileTemplateFileName($scope, $profile); + return readAccountProfileFile($fileName); +} + +/** + * Installs a single template from the given data. + * + * @param string $scope account type (e.g. user) + * @param string $name template name + * @param array $data profile data + * @throws LAMException error saving file + */ +function installTemplateAccountProfile($scope, $name, $data) { + if (!isValidProfileName($name) || !preg_match("/^[a-z0-9_]+$/i", $scope)) { + logNewMessage(LOG_NOTICE, "Invalid account profile name: $name:$scope"); + return; + } + $fileName = getProfileTemplateFileName($scope, $name); + $success = writeProfileDataToFile($fileName, $data); + if (!$success) { + throw new LAMException('Unable to write account profile template: ' . $fileName); } } -?> diff --git a/lam/lib/selfService.inc b/lam/lib/selfService.inc index befa0e2a..201bd536 100644 --- a/lam/lib/selfService.inc +++ b/lam/lib/selfService.inc @@ -170,7 +170,7 @@ function checkSelfServiceOptions($scope, $fields, $attributes, $passwordChangeOn /** * Returns a list of all available self service profiles (without .conf) * -* @return array profile names (array( => array(, , ...))) +* @return array profile names (array('account type' => array('profile1', 'profile2'))) */ function getSelfServiceProfiles() { $types = LAM\TYPES\getTypes(); @@ -201,7 +201,7 @@ function getSelfServiceProfiles() { * * @param string $name profile name * @param string $scope account type -* @return selfServiceProfile|false profile if file was readable, false otherwise +* @return false|selfServiceProfile profile or false if file was not readable */ function loadSelfServiceProfile($name, $scope) { if (!preg_match("/^[0-9a-z _-]+$/i", $name) || !preg_match("/^[0-9a-z _-]+$/i", $scope)) { @@ -213,7 +213,13 @@ function loadSelfServiceProfile($name, $scope) { $file = @fopen($file, "r"); if ($file) { $data = fread($file, 10000000); - $profile = unserialize($data); + $profileData = @json_decode($data, true); + if ($profileData === null) { + $profile = unserialize($data); + } + else { + $profile = selfServiceProfile::import($profileData); + } fclose($file); } else { @@ -249,7 +255,7 @@ function saveSelfServiceProfile($name, $scope, $profile) { $file = @fopen($path, "w"); if ($file) { // write settings to file - fputs($file, serialize($profile)); + fputs($file, json_encode($profile->export())); // close file fclose($file); } @@ -521,6 +527,32 @@ class selfServiceProfile { $this->baseUrl = ''; } + /** + * Converts the export data back to a self service profile. + * + * @param array $data export data + * @return selfServiceProfile profile + */ + public static function import($data) { + $profile = new selfServiceProfile(); + $vars = get_class_vars(selfServiceProfile::class); + foreach ($data as $key => $value) { + if (in_array($key, $vars)) { + $profile->$key = $value; + } + } + return $profile; + } + + /** + * Returns a representation of the self service profile. + * + * @return array self service profile data + */ + public function export() { + return json_decode(json_encode($this), true); + } + /** * Returns the server's base URL (e.g. https://www.example.com). * diff --git a/lam/style/500_layout.css b/lam/style/500_layout.css index 1c7e962d..3312b922 100644 --- a/lam/style/500_layout.css +++ b/lam/style/500_layout.css @@ -86,6 +86,10 @@ a img { border: 0px; } +a.img-padding1 img { + padding: 1rem; +} + a.classicBold { color: blue; text-decoration: none; @@ -462,6 +466,10 @@ table.collapse { display: none; } +.display-as-block { + display: block; +} + .nowrap { white-space: nowrap; } @@ -489,7 +497,7 @@ div.smallScroll { } .fullwidth { - width: 100%; + width: 100%!important; } .halfwidth { diff --git a/lam/templates/config/confImportExport.php b/lam/templates/config/confImportExport.php new file mode 100644 index 00000000..ea2e6677 --- /dev/null +++ b/lam/templates/config/confImportExport.php @@ -0,0 +1,277 @@ +checkPassword($_SESSION["mainconf_password"])) { + $exporter = new ConfigDataExporter(); + if (!headers_sent()) { + header('Content-Type: application/json; charset=utf-8'); + header('Content-disposition: attachment; filename=lam-config.json'); + } + try { + echo $exporter->exportAsJson(); + } + catch (LAMException $e) { + logNewMessage('ERROR', $e->getTitle() . ' ' . $e->getMessage()); + } + exit; +} + +echo $_SESSION['header']; +printHeaderContents(_("Import and export configuration"), '../..'); + +?> + + + + + + + +
+ +
+
+

+ setCSSClasses(array('maxrow fullwidth roundedShadowBox spacing5')); + if ($message !== null) { + $loginContent->add($message, 12); + } + $pwdInput = new htmlResponsiveInputField(_("Master password"), 'password', '', '236'); + $pwdInput->setIsPassword(true); + $pwdInput->setCSSClasses(array('lam-initial-focus')); + $loginContent->add($pwdInput, 12); + $loginContent->addLabel(new htmlOutputText(' ', false)); + $loginContent->addField(new htmlButton('submitLogin', _("Ok"))); + + $content->add($loginContent, 12); + + parseHtml(null, $content, array(), false, $tabindex, null); + renderBackLink(); + } + + /** + * Renders the link back to login page. + */ + function renderBackLink() { + $tabindex = 0; + $content = new htmlResponsiveRow(); + $content->addVerticalSpacer('2rem'); + $content->add(new htmlLink(_('Back to login'), '../login.php', '../../graphics/undo.png'), 12); + $content->addVerticalSpacer('1rem'); + parseHtml(null, $content, array(), false, $tabindex, null); + } + + /** + * Checks the login password. + * + * @param LAMCfgMain $cfg main config + * @return bool login ok + */ + function checkLogin($cfg) { + $password = $_POST['password']; + if ($cfg->checkPassword($password)) { + $_SESSION["mainconf_password"] = $password; + return true; + } + showLoginDialog(new htmlStatusMessage('ERROR', _('The password is invalid! Please try again.'))); + return false; + } + + /** + * Displays the import/export functions. + */ + function displayImportExport() { + $tabindex = 0; + $content = new htmlResponsiveRow(); + + $content->add(new htmlSubTitle(_('Export')), 12); + $content->add(new htmlButton('exportConfig', _('Export')), 12); + + $content->add(new htmlSubTitle(_('Import')), 12); + renderImportPart($content); + + parseHtml(null, $content, array(), false, $tabindex, null); + renderBackLink(); + } + + /** + * Renders the import area. + * + * @param htmlResponsiveRow $content content where to add import part + */ + function renderImportPart($content) { + $validUpload = false; + $importSteps = array(); + if (isset($_POST['importConfig'])) { + try { + if (empty($_FILES['import-file']['tmp_name'])) { + throw new LAMException('The file you uploaded is too large. Please check php.ini, upload_max_size setting'); + } + $handle = fopen($_FILES['import-file']['tmp_name'], "r"); + $data = fread($handle, 100000000); + fclose($handle); + $importer = new ConfigDataImporter(); + $importSteps = $importer->getPossibleImportSteps($data); + $tmpFile = __DIR__ . '/../../tmp/internal/import_' . getRandomNumber() . '.tmp'; + $file = @fopen($tmpFile, "w"); + if ($file) { + fputs($file, $data); + fclose($file); + chmod($tmpFile, 0600); + } + $_SESSION['configImportFile'] = $tmpFile; + $validUpload = true; + } + catch (LAMException $e) { + $content->add(new htmlStatusMessage('ERROR', htmlspecialchars($e->getTitle()), htmlspecialchars($e->getMessage())), 12); + } + } + if (!isset($_POST['importConfigConfirm']) && !$validUpload) { + $content->add(new htmlInputFileUpload('import-file'), 12); + $content->add(new htmlButton('importConfig', _('Submit')), 12); + } + elseif (isset($_POST['importConfig'])) { + $content->add(new htmlOutputText(_('Import steps')), 12); + foreach ($importSteps as $importStep) { + $stepKey = 'step_' . $importStep->getKey(); + $stepCheckbox = new htmlResponsiveInputCheckbox($stepKey, true, $importStep->getLabel()); + $stepCheckbox->setLabelAfterCheckbox(); + $stepCheckbox->setCSSClasses(array('bold')); + $subStepIds = array(); + $content->add($stepCheckbox, 12); + $content->addVerticalSpacer('0.3rem'); + foreach ($importStep->getSubSteps() as $subStep) { + $subStepKey = 'step_' . $subStep->getKey(); + $subStepIds[] = $subStepKey; + $subStepCheckbox = new htmlResponsiveInputCheckbox($subStepKey, true, $subStep->getLabel()); + $subStepCheckbox->setLabelAfterCheckbox(); + $content->add($subStepCheckbox, 12); + } + $stepCheckbox->setTableRowsToShow($subStepIds); + $content->addVerticalSpacer('1rem'); + } + $buttonGroup = new htmlGroup(); + $buttonGroup->addElement(new htmlButton('importConfigConfirm', _('Import'))); + $buttonGroup->addElement(new htmlButton('importCancel', _('Cancel'))); + $content->add($buttonGroup, 12); + } + elseif (isset($_POST['importConfigConfirm'])) { + $handle = fopen($_SESSION['configImportFile'], "r"); + $data = fread($handle, 100000000); + fclose($handle); + try { + $importer = new ConfigDataImporter(); + $importSteps = $importer->getPossibleImportSteps($data); + foreach ($importSteps as $importStep) { + $importStep->setActive(isset($_POST['step_' . $importStep->getKey()])); + foreach ($importStep->getSubSteps() as $subStep) { + $subStep->setActive(isset($_POST['step_' . $subStep->getKey()])); + } + } + $importer->runImport($importSteps); + unlink($_SESSION['configImportFile']); + $content->add(new htmlStatusMessage('INFO', _('Configuration import ended successful.')), 12); + $content->add(new htmlButton('importNew', _('New import')), 12); + } + catch (LAMException $e) { + $content->add(new htmlStatusMessage('ERROR', htmlspecialchars($e->getTitle()), htmlspecialchars($e->getMessage())), 12); + $content->add(new htmlButton('importCancel', _('Back')), 12); + } + } + } + + ?> +
+ + diff --git a/lam/templates/config/index.php b/lam/templates/config/index.php index 48d4fef2..d6c2360c 100644 --- a/lam/templates/config/index.php +++ b/lam/templates/config/index.php @@ -1,8 +1,11 @@ @@ -59,69 +65,45 @@ printHeaderContents(_("Configuration overview"), '../..');

- - \n"; - } - else { - echo "\n"; - } - ?> - - - - - - - - - \n"; - echo "\n"; - echo "\n"; - echo "\n"; - } - ?> -
 
 
- - general settings - - - - - -
- - server settings - - - - - -
\n"; - echo "\n"; - echo "\"self\n"; - echo "\n"; - echo "\n"; - echo "\n"; - echo _("Edit self service"); - echo "\n"; - echo "
-


+ + setCSSClasses(array('maxrow fullwidth roundedShadowBox spacing5')); + $mainCfgLink = new htmlLink(_("Edit general settings"), 'mainlogin.php', '../../graphics/bigTools.png'); + $mainCfgLink->setCSSClasses(array('img-padding1 display-as-block')); + $topContent->add($mainCfgLink, 12); + $cfgLink = new htmlLink(_("Edit server profiles"), 'conflogin.php', '../../graphics/profiles.png'); + $cfgLink->setCSSClasses(array('img-padding1 display-as-block')); + $topContent->add($cfgLink, 12); + if (isLAMProVersion()) { + $selfServiceLink = new htmlLink(_("Edit self service"), '../selfService/adminLogin.php', '../../graphics/bigPeople.png'); + $selfServiceLink->setCSSClasses(array('img-padding1 display-as-block')); + $topContent->add($selfServiceLink, 12); + } + if (isDeveloperVersion(LAMVersion())) { + $topContent->addVerticalSpacer('1rem'); + $importExportLink = new htmlLink(_("Import and export configuration"), 'confImportExport.php', '../../graphics/confImportExport.png'); + $importExportLink->setCSSClasses(array('img-padding1 display-as-block')); + $topContent->add($importExportLink, 12); + } + $content->add($topContent, 12); + $content->addVerticalSpacer('4rem'); + ?> printLicenseInfo(); - echo "

"; + $content->add($printer->getLicenseInfo(), 12); + $content->addVerticalSpacer('2rem'); } - ?> -

 back 

-



+ $content->add(new htmlLink(_("Back to login"), '../login.php', '../../graphics/undo.png'), 12); + $content->addVerticalSpacer('2rem'); + + parseHtml('none', $content, array(), true, $tabindex, 'none'); + + ?> diff --git a/lam/templates/pdfedit/pdfmain.php b/lam/templates/pdfedit/pdfmain.php index 12c3b779..6a65ec6c 100644 --- a/lam/templates/pdfedit/pdfmain.php +++ b/lam/templates/pdfedit/pdfmain.php @@ -21,7 +21,7 @@ use \LAM\TYPES\TypeManager; This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) Copyright (C) 2003 - 2006 Michael Duergner - 2005 - 2018 Roland Gruber + 2005 - 2020 Roland Gruber This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -107,7 +107,7 @@ $container->add(new htmlTitle(_('PDF editor')), 12); if (isset($_POST['deleteProfile']) && ($_POST['deleteProfile'] == 'true')) { $typeToDelete = $typeManager->getConfiguredType($_POST['profileDeleteType']); // delete structure - if (\LAM\PDF\deletePDFStructure($_POST['profileDeleteType'], $_POST['profileDeleteName'])) { + if (\LAM\PDF\deletePDFStructure($_POST['profileDeleteType'], $_POST['profileDeleteName'], $_SESSION['config']->getName())) { $message = new htmlStatusMessage('INFO', _('Deleted PDF structure.'), $typeToDelete->getAlias() . ': ' . htmlspecialchars($_POST['profileDeleteName'])); $container->add($message, 12); } @@ -173,13 +173,13 @@ if (!empty($_POST['export'])) { if (isset($_POST['uploadLogo']) && !empty($_FILES['logoUpload']) && !empty($_FILES['logoUpload']['size'])) { $file = $_FILES['logoUpload']['tmp_name']; $filename = $_FILES['logoUpload']['name']; - $container->add(\LAM\PDF\uploadPDFLogo($file, $filename), 12); + $container->add(\LAM\PDF\uploadPDFLogo($file, $filename, $_SESSION['config']->getName()), 12); } // delete logo file if (isset($_POST['delLogo'])) { $toDel = $_POST['logo']; - $container->add(\LAM\PDF\deletePDFLogo($toDel), 12); + $container->add(\LAM\PDF\deletePDFLogo($toDel, $_SESSION['config']->getName()), 12); } // get list of account types @@ -192,7 +192,7 @@ foreach ($sortedTypes as $typeId => $title) { 'scope' => $type->getScope(), 'title' => $title, 'icon' => $type->getIcon(), - 'templates' => \LAM\PDF\getPDFStructures($type->getId())); + 'templates' => \LAM\PDF\getPDFStructures($type->getId(), $_SESSION['config']->getName())); $availableTypes[$title] = $type->getId(); } // check if a template should be edited @@ -275,7 +275,7 @@ include __DIR__ . '/../../lib/adminHeader.inc'; // manage logos $container->addVerticalSpacer('4rem'); $container->add(new htmlSubTitle(_('Manage logos')), 12); - $logos = \LAM\PDF\getAvailableLogos(); + $logos = \LAM\PDF\getAvailableLogos($_SESSION['config']->getName()); $logoOptions = array(); foreach ($logos as $logo) { $file = $logo['filename']; diff --git a/lam/templates/pdfedit/pdfpage.php b/lam/templates/pdfedit/pdfpage.php index db105c77..f9e17dbd 100644 --- a/lam/templates/pdfedit/pdfpage.php +++ b/lam/templates/pdfedit/pdfpage.php @@ -22,7 +22,7 @@ use LAM\PDF\PDFStructureWriter; /* This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) Copyright (C) 2003 - 2006 Michael Duergner - 2007 - 2019 Roland Gruber + 2007 - 2020 Roland Gruber This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -105,7 +105,7 @@ if(isset($_GET['abort'])) { // 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(); + $reader = new PDFStructureReader($_SESSION['config']->getName()); try { if(isset($_GET['edit'])) { $_SESSION['currentPDFStructure'] = $reader->read($type->getId(), $_GET['edit']); @@ -135,7 +135,7 @@ if (!empty($_POST['form_submit'])) { // main pdf structure page $saveErrors = array(); if(isset($_GET['submit'])) { - $writer = new PDFStructureWriter(); + $writer = new PDFStructureWriter($_SESSION['config']->getName()); try { $writer->write($type->getId(), $_POST['pdfname'], $_SESSION['currentPDFStructure']); unset($_SESSION['currentPDFStructure']); @@ -218,7 +218,7 @@ else if (isset($_POST['pdfname'])) { // headline $headline = $_SESSION['currentPDFStructure']->getTitle(); // logo -$logoFiles = \LAM\PDF\getAvailableLogos(); +$logoFiles = \LAM\PDF\getAvailableLogos($_SESSION['config']->getName()); $logos = array(_('No logo') => 'none'); foreach($logoFiles as $logoFile) { $logos[$logoFile['filename'] . ' (' . $logoFile['infos'][0] . ' x ' . $logoFile['infos'][1] . ")"] = $logoFile['filename']; diff --git a/lam/templates/profedit/profilepage.php b/lam/templates/profedit/profilepage.php index d6973745..7de209c6 100644 --- a/lam/templates/profedit/profilepage.php +++ b/lam/templates/profedit/profilepage.php @@ -10,7 +10,7 @@ use \htmlSubTitle; /* This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) - Copyright (C) 2003 - 2019 Roland Gruber + Copyright (C) 2003 - 2020 Roland Gruber This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -127,7 +127,7 @@ if (isset($_POST['save'])) { $errors = checkProfileOptions($_POST['accounttype'], $options); if (sizeof($errors) == 0) { // input data is valid, save profile // save profile - if (\LAM\PROFILES\saveAccountProfile($options, $_POST['profname'], $_POST['accounttype'])) { + if (\LAM\PROFILES\saveAccountProfile($options, $_POST['profname'], $_POST['accounttype'], $_SESSION['config'])) { metaRefresh('profilemain.php?savedSuccessfully=' . $_POST['profname']); exit(); } @@ -168,7 +168,7 @@ if (isset($_POST['save'])) { } } elseif (isset($_GET['edit'])) { - $old_options = \LAM\PROFILES\loadAccountProfile($_GET['edit'], $type->getId()); + $old_options = \LAM\PROFILES\loadAccountProfile($_GET['edit'], $type->getId(), $_SESSION['config']->getName()); } // display formular diff --git a/lam/templates/upload/masscreate.php b/lam/templates/upload/masscreate.php index 95c4196d..1f76e2a1 100644 --- a/lam/templates/upload/masscreate.php +++ b/lam/templates/upload/masscreate.php @@ -294,7 +294,7 @@ function showMainPage(\LAM\TYPES\ConfiguredType $type, $selectedModules) { $pdfCheckbox = new htmlResponsiveInputCheckbox('createPDF', $createPDF, _('Create PDF files')); $pdfCheckbox->setTableRowsToShow(array('pdfStructure', 'pdf_font')); $row->add($pdfCheckbox, 12); - $pdfStructures = \LAM\PDF\getPDFStructures($type->getId()); + $pdfStructures = \LAM\PDF\getPDFStructures($type->getId(), $_SESSION['config']->getName()); $pdfSelected = array(); if (isset($_POST['pdfStructure'])) { $pdfSelected = array($_POST['pdfStructure']); diff --git a/lam/tests/lib/LAMCfgMainTest.php b/lam/tests/lib/LAMCfgMainTest.php index aa4b2d64..d622ddd2 100644 --- a/lam/tests/lib/LAMCfgMainTest.php +++ b/lam/tests/lib/LAMCfgMainTest.php @@ -120,4 +120,56 @@ class LAMCfgMainTest extends TestCase { $this->assertFalse($this->conf->showLicenseWarningOnScreen()); } + /** + * Tests the export. + */ + public function testExportData() { + $this->conf->passwordMinLower = 3; + $this->conf->sessionTimeout = 240; + $this->conf->logLevel = LOG_ERR; + $this->conf->mailServer = 'mailserver'; + + $data = $this->conf->exportData(); + + $this->assertEquals(3, $data['passwordMinLower']); + $this->assertEquals(240, $data['sessionTimeout']); + $this->assertEquals(LOG_ERR, $data['logLevel']); + $this->assertEquals('mailserver', $data['mailServer']); + } + + /** + * Tests the import. + */ + public function testImportData() { + $importData = array(); + $importData['passwordMinLower'] = 3; + $importData['sessionTimeout'] = 240; + $importData['logLevel'] = LOG_ERR; + $importData['mailServer'] = 'mailserver'; + $importData['allowedHosts'] = null; + $importData['IGNORE_ME'] = 'ignore'; + + $this->conf->importData($importData); + + $this->assertEquals(3, $this->conf->passwordMinLower); + $this->assertEquals(240, $this->conf->sessionTimeout); + $this->assertEquals(LOG_ERR, $this->conf->logLevel); + $this->assertEquals('mailserver', $this->conf->mailServer); + $this->assertNull($this->conf->allowedHosts); + } + + /** + * Tests the import with invalid data. + */ + public function testImportData_invalid() { + $importData = array(); + $importData['passwordMinLower'] = 3; + $importData['sessionTimeout'] = 240; + $importData['logLevel'] = LOG_ERR; + $importData['mailServer'] = new LAMLanguage('de_de', 'UTF-8', 'DE'); + + $this->expectException(LAMException::class); + $this->conf->importData($importData); + } + } \ No newline at end of file diff --git a/lam/tests/lib/LAMConfigTest.php b/lam/tests/lib/LAMConfigTest.php index 972c5403..b6f9212c 100644 --- a/lam/tests/lib/LAMConfigTest.php +++ b/lam/tests/lib/LAMConfigTest.php @@ -3,7 +3,7 @@ use PHPUnit\Framework\TestCase; /* This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) - Copyright (C) 2016 - 2019 Roland Gruber + Copyright (C) 2016 - 2020 Roland Gruber This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,7 +21,7 @@ use PHPUnit\Framework\TestCase; */ -include_once 'lam/tests/utils/configuration.inc'; +include_once __DIR__ . '/../utils/configuration.inc'; /** * LAMConfig test case. @@ -858,6 +858,63 @@ class LAMConfigTest extends TestCase { $this->assertEquals($sizeTypeSettings, sizeof($this->lAMConfig->get_typeSettings())); } + /** + * Tests the export. + */ + public function testExportData() { + $this->lAMConfig->set_defaultLanguage('lang'); + $this->lAMConfig->set_ServerURL('myserver'); + $this->lAMConfig->set_typeSettings(array('typetest' => '1')); + $this->lAMConfig->set_moduleSettings(array('modtest' => '1')); + $this->lAMConfig->setToolSettings(array('tooltest' => '1')); + $this->lAMConfig->setJobSettings(array('jobtest' => '1')); + + $data = $this->lAMConfig->exportData(); + + $this->assertEquals('lang', $data['defaultLanguage']); + $this->assertEquals('myserver', $data['ServerURL']); + $this->assertEquals(array('typetest' => '1'), $data['typeSettings']); + $this->assertEquals(array('modtest' => '1'), $data['moduleSettings']); + $this->assertEquals(array('tooltest' => '1'), $data['toolSettings']); + $this->assertEquals(array('jobtest' => '1'), $data['jobSettings']); + } + + /** + * Tests the import. + */ + public function testImportData() { + $importData = array(); + $importData['ServerURL'] = 'testserver'; + $importData['defaultLanguage'] = 'de_DE.utf8'; + $importData['typeSettings'] = array('typetest' => 'value'); + $importData['toolSettings'] = array('tooltest' => 'value'); + $importData['moduleSettings'] = array('modtest' => 'value'); + $importData['jobSettings'] = array('jobtest' => 'value'); + $importData['IGNORE_ME'] = 'ignore'; + + $this->lAMConfig->importData($importData); + + $this->assertEquals('testserver', $this->lAMConfig->get_ServerURL()); + $this->assertEquals('de_DE.utf8', $this->lAMConfig->get_defaultLanguage()); + $this->assertEquals(array('typetest' => 'value'), $this->lAMConfig->get_typeSettings()); + $this->assertEquals(array('tooltest' => 'value'), $this->lAMConfig->getToolSettings()); + $this->assertEquals(array('modtest' => 'value'), $this->lAMConfig->get_moduleSettings()); + $this->assertEquals(array('jobtest' => 'value'), $this->lAMConfig->getJobSettings()); + } + + /** + * Tests the import with invalid data. + */ + public function testImportData_invalid() { + $importData = array(); + $importData['ServerURL'] = 'testserver'; + $importData['typeSettings'] = array('typetest' => 'value'); + $importData['defaultLanguage'] = new LAMLanguage('de_de', 'UTF-8', 'DE'); + + $this->expectException(LAMException::class); + $this->lAMConfig->importData($importData); + } + /** * Saves the config */ diff --git a/lam/tests/lib/accountTest.php b/lam/tests/lib/accountTest.php index bca309c1..b33623bd 100644 --- a/lam/tests/lib/accountTest.php +++ b/lam/tests/lib/accountTest.php @@ -24,7 +24,7 @@ include_once __DIR__ . '/../../lib/account.inc'; include_once __DIR__ . '/../../lib/security.inc'; /** - * LAMConfig test case. + * account.inc test cases. * * @author Roland Gruber */ @@ -155,4 +155,13 @@ class AccountTest extends TestCase { $this->assertFalse(isCommandlineSafeEmailAddress('test+abc@example.com')); } + /** + * Tests isDeveloperVersion() + */ + function testIsDeveloperVersion() { + $this->assertFalse(isDeveloperVersion('0.4.1')); + $this->assertFalse(isDeveloperVersion('3.2.RC1')); + $this->assertTrue(isDeveloperVersion('4.5.DEV')); + } + } diff --git a/lam/tests/lib/pdfstructTest.php b/lam/tests/lib/pdfstructTest.php index bf0ba93d..58933af4 100644 --- a/lam/tests/lib/pdfstructTest.php +++ b/lam/tests/lib/pdfstructTest.php @@ -1,4 +1,6 @@ getMockBuilder('\LAM\PDF\PDFStructureReader') + ->setConstructorArgs(array('test')) ->setMethods(array('getFileName')) ->getMock(); $reader->method('getFileName')->willReturn($this->getTestFileName('test.xml')); @@ -99,17 +102,144 @@ class ReadStructureTest extends TestCase { fclose($fileHandle); // read structure $reader = $this->getMockBuilder('\LAM\PDF\PDFStructureReader') + ->setConstructorArgs(array('test')) ->setMethods(array('getFileName')) ->getMock(); $reader->method('getFileName')->willReturn($file); $structure = $reader->read('type', 'name'); // create writer and get output XML - $writer = new PDFStructureWriter(); + $writer = new PDFStructureWriter('test'); $xml = $writer->getXML($structure); // compare $this->assertEquals($originalXML, $xml); } + /** + * Tests PDFTextSection + */ + public function testExportPDFTextSection() { + $section = new PDFTextSection('sometext'); + + $data = $section->export(); + + $this->assertEquals('sometext', $data); + } + + /** + * Tests PDFEntrySection + */ + public function testExportPDFEntrySection() { + $section = new PDFEntrySection('mytitle'); + $section->setEntries(array(new PDFSectionEntry('key1'), new PDFSectionEntry('key2'))); + + $data = $section->export(); + + $expected = array( + 'title' => 'mytitle', + 'entries' => array('key1', 'key2') + ); + + $this->assertEquals($expected, $data); + } + + /** + * Tests PDFStructure + */ + public function testExportPDFStructure() { + $structure = new PDFStructure(); + $structure->setFoldingMarks(PDFStructure::FOLDING_STANDARD); + $structure->setLogo('somelogo'); + $structure->setTitle('mytitle'); + $entrySection = new PDFEntrySection('sometitle'); + $entrySection->setEntries(array(new PDFSectionEntry('key1'))); + $structure->setSections(array( + new PDFTextSection('sometext'), + $entrySection + )); + + $data = $structure->export(); + + $expected = array( + 'title' => 'mytitle', + 'foldingMarks' => PDFStructure::FOLDING_STANDARD, + 'logo' => 'somelogo', + 'sections' => array( + array( + 'type' => 'text', + 'data' => 'sometext' + ), + array( + 'type' => 'entry', + 'data' => array( + 'title' => 'sometitle', + 'entries' => array('key1') + ) + ) + ) + ); + + $this->assertEquals($expected, $data); + } + + /** + * Tests import in PDFEntrySection. + */ + public function testImportPDFEntrySection() { + $data = array( + 'title' => 'mytitle', + 'entries' => array('e1', 'e2') + ); + + $section = new PDFEntrySection(null); + $section->import($data); + + $this->assertEquals('mytitle', $section->getTitle()); + $entries = $section->getEntries(); + $this->assertEquals(2, sizeof($entries)); + $this->assertEquals('e1', ($entries[0]->getKey())); + $this->assertEquals('e2', ($entries[1]->getKey())); + } + + /** + * Tests the import in PDFStructure. + */ + public function testImportPDFStructure() { + $data = array( + 'title' => 'mytitle', + 'foldingMarks' => PDFStructure::FOLDING_STANDARD, + 'logo' => 'logo', + 'sections' => array( + array( + 'type' => 'text', + 'data' => 'textvalue' + ), + array( + 'type' => 'entry', + 'data' => array( + 'title' => 'etitle', + 'entries' => array('e1', 'e2') + ) + ), + ) + ); + + $structure = new PDFStructure(); + $structure->import($data); + + $this->assertEquals('mytitle', $structure->getTitle()); + $this->assertEquals(PDFStructure::FOLDING_STANDARD, $structure->getFoldingMarks()); + $this->assertEquals('logo', $structure->getLogo()); + $sections = $structure->getSections(); + $this->assertEquals(2, sizeof($sections)); + $this->assertTrue($sections[0] instanceof PDFTextSection); + $this->assertEquals('textvalue', $sections[0]->getText()); + $this->assertTrue($sections[1] instanceof PDFEntrySection); + $entries = $sections[1]->getEntries(); + $this->assertEquals(2, sizeof($entries)); + $this->assertEquals('e1', $entries[0]->getKey()); + $this->assertEquals('e2', $entries[1]->getKey()); + } + } ?> \ No newline at end of file diff --git a/lam/tests/lib/persistenceTest.php b/lam/tests/lib/persistenceTest.php new file mode 100644 index 00000000..a3ce3f3b --- /dev/null +++ b/lam/tests/lib/persistenceTest.php @@ -0,0 +1,104 @@ + 'val', + 'confMainKey2' => 4, + 'confMainKey3' => '', + ); + $profileData = array( + 'profile1' => array('ServerURL' => 'myserver'), + 'profile2' => array('ServerURL' => 'myserver2'), + ); + $accountProfileData = array( + 'profile1' => array('user' => array('default' => array('key' => 'value'))), + 'profile1' => array( + 'user' => array('default' => array('key' => 'value')), + 'group' => array('default' => array('key' => 'value')), + ), + ); + $accountProfileTemplateData = array( + 'user' => array('default' => array('key' => 'value')), + 'group' => array('default' => array('key' => 'value')), + ); + $pdfData = array( + 'profile1' => array('structures' => array( + 'user' => array( + 'default' => array('key' => 'value')) + )), + 'profile1' => array('structures' => array( + 'user' => array('default' => array('key' => 'value')), + 'group' => array('default' => array('key' => 'value')), + )), + ); + $pdfTemplateData = array( + 'user' => array('default' => array('key' => 'value')), + 'group' => array('default' => array('key' => 'value')), + ); + $selfServiceData = array( + 'profile1' => array('key' => 'value'), + 'profile2' => array('key' => 'value'), + ); + $expectedJson = json_encode(array( + 'mainConfig' => $mainData, + 'certificates' => 'certs', + 'serverProfiles' => $profileData, + 'accountProfiles' => $accountProfileData, + 'accountProfileTemplates' => $accountProfileTemplateData, + 'pdfProfiles' => $pdfData, + 'pdfProfileTemplates' => $pdfTemplateData, + 'selfServiceProfiles' => $selfServiceData, + )); + + $exporter = $this->getMockBuilder('\LAM\PERSISTENCE\ConfigDataExporter') + ->setMethods(array('_getMainConfigData', '_getCertificates', '_getServerProfiles', + '_getAccountProfiles', '_getAccountProfileTemplates', '_getPdfProfiles', + '_getPdfProfileTemplates', '_getSelfServiceProfiles')) + ->getMock(); + $exporter->method('_getMainConfigData')->willReturn($mainData); + $exporter->method('_getCertificates')->willReturn('certs'); + $exporter->method('_getServerProfiles')->willReturn($profileData); + $exporter->method('_getAccountProfiles')->willReturn($accountProfileData); + $exporter->method('_getAccountProfileTemplates')->willReturn($accountProfileTemplateData); + $exporter->method('_getPdfProfiles')->willReturn($pdfData); + $exporter->method('_getPdfProfileTemplates')->willReturn($pdfTemplateData); + $exporter->method('_getSelfServiceProfiles')->willReturn($selfServiceData); + + $json = $exporter->exportAsJson(); + + $this->assertEquals($expectedJson, $json); + } + +} + diff --git a/lam/tests/lib/selfServiceTest.php b/lam/tests/lib/selfServiceTest.php index 67e3ce23..b7e65859 100644 --- a/lam/tests/lib/selfServiceTest.php +++ b/lam/tests/lib/selfServiceTest.php @@ -3,7 +3,7 @@ use PHPUnit\Framework\TestCase; /* This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) - Copyright (C) 2019 Roland Gruber + Copyright (C) 2019 - 2020 Roland Gruber This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,7 +21,7 @@ use PHPUnit\Framework\TestCase; */ -require_once 'lam/lib/selfService.inc'; +require_once __DIR__ . '/../../lib/selfService.inc'; /** * Checks selfServiceProfile. @@ -43,6 +43,23 @@ class selfServiceProfileTest extends TestCase { $this->assertEquals('https://test.com', $profile->getBaseUrl()); } + public function testImportExport() { + $profile = new selfServiceProfile(); + $moduleSettings = array('x1' => 'y1', 'x2' => 'y2'); + $profile->moduleSettings = $moduleSettings; + $profile->baseColor = 'green'; + $profile->language = 'de_DE@UTF8'; + $profile->pageHeader = 'header'; + + $export = $profile->export(); + $importedProfile = selfServiceProfile::import($export); + + $this->assertEquals($moduleSettings, $importedProfile->moduleSettings); + $this->assertEquals('green', $importedProfile->baseColor); + $this->assertEquals('de_DE@UTF8', $importedProfile->language); + $this->assertEquals('header', $importedProfile->pageHeader); + } + } ?> \ No newline at end of file