true, 'attachments' => true, 'history' => true, 'images' => true, 'debug' => false]; public $structureStack = []; public function get_error() { $str = ''; foreach ($this->errors as $i => $error) { $str = $error; if (is_array($this->errorsArgs[$i])) { $str .= ': ' . implode(', ', $this->errorsArgs[$i]); } else { $str .= ': ' . $this->errorsArgs[$i]; } } return $str; } // Export a list of pages and/or a structure. This creates the Zip file which encompasses the // exported pages, the exported images, the exported structure and the wiki.xml file. public function export_pages( $pages = null, // Array of the names of the pages which should be exported, // if any $structure = null, // Name of the structure to be exported, if any $zipFile = 'dump/xml.zip', // Name of the temporary Zip file, which is being generated $config = null) // Configuration (see the $config property, above). Overrides // the default configuration (above) { // Setup the Zip archive, which is to be generated if (! class_exists('ZipArchive')) { $this->errors[] = 'Problem zip initialisation'; $this->errorsArgs[] = 'ZipArchive class not found'; return false; } $this->zip = new ZipArchive(); if (! $this->zip->open($zipFile, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)) { $this->errors[] = 'The file cannot be opened'; $this->errorsArgs[] = $zipFile; return false; } if (! empty($config)) { $this->config = array_merge($this->config, $config); } // Start the wiki.xml file $this->xml .= '' . "\n"; // Export pages, if any if (count($pages) >= 1) { $this->xml .= "\n"; foreach ($pages as $page) { if (! $this->export_page($page)) { return false; } } $this->xml .= "\n"; } // Export structure, if one is specified if (! empty($structure)) { $structlib = TikiLib::lib('struct'); $pages = $structlib->s_get_structure_pages($structure); $stack = []; foreach ($pages as $page) { while (count($stack) && $stack[count($stack) - 1] != $page['parent_id']) { array_pop($stack); $this->xml .= "\n"; } $this->xml .= "\n"; $stack[] = $page['page_ref_id']; if (! $this->export_page($page['pageName'])) { return false; } } while (count($stack)) { array_pop($stack); $this->xml .= "\n"; } } // Add the wiki.xml file and finish the Zip file. if (! $this->zip->addFromString(WIKI_XML, $this->xml)) { $this->errors[] = 'Can not add the xml'; $this->errorsArgs[] = WIKI_XML; return false; } if ($this->config['debug']) { echo '
' . htmlspecialchars($this->xml) . '
'; } $this->zip->close(); return true; } // Export one page. This adds the necessary files to the Zip file and adds the part for the page // to the wiki.xml file (in the $xml member variable). public function export_page($page) { global $prefs, $tikidomain; $tikilib = TikiLib::lib('tiki'); $smarty = TikiLib::lib('smarty'); $parserlib = TikiLib::lib('parser'); $dir = $page; // The directory inside the Zip file, which contains the page // Get all the page information from the database. This includes meta data and the page // content itself (in the attribute 'data'). $info is extended by the "zip" property and // passed to Smarty, for writing all the needed meta data to the wiki.xml file. The 'zip' // property is the file name of the page data inside the Zip file. $info = $tikilib->get_page_info($page); if (empty($info)) { $this->errors[] = 'Page does not exist'; $this->errorsArgs[] = $page; return false; } $info['zip'] = "$dir/" . $page; $smarty->assign_by_ref('info', $info); // Add the page itself to the Zip file. if (! $this->zip->addFromString($info['zip'], $info['data'])) { $this->errors[] = 'Can not add the page'; $this->errorsArgs[] = $info['zip']; return false; } // Add the Wiki Comments, if this feature is enabled. if ($prefs['feature_wiki_comments'] == 'y' && $this->config['comments']) { $commentslib = TikiLib::lib('comments'); $comments = $commentslib->get_comments('wiki page:' . $page, 0, 0, 0, 'commentDate_asc', '', 0, 'commentStyle_plain'); if (! empty($comments['cant'])) { $smarty->assign_by_ref('comments', $comments['data']); } } // Add the images to the Zip file and assign the metadata to the 'images' Smarty variable. // The meta data for each image is an associative array with these parameters: // 'filename': The base name of the image file (without path part). This is used only for a // file in the "img/wiki_up" directory. // 'where': 'wiki' for a file in the "img/wiki_up" directory, or // 'fgal' for a file in a file gallery // 'zip': The relative path to the image inside of the Zip file // 'wiki': The 'src' parameter of the Img plugin. This is for images specified by an URL. $images = []; if ( $prefs['feature_wiki_pictures'] == 'y' && $this->config['images'] // Get all the occurences of "{img...}" in the page. && preg_match_all('/\{img\s*\(?([^\}]+)\)?\s*\}/i', $info['data'], $matches) ) { global $tikiroot; // Iterate over all the occurences of "{img...}". foreach ($matches[1] as $match) { // $match: The parameters for the img plugin as one string // $args: associative array of the arguments for the img plugin $args = $parserlib->plugin_split_args($match); // An image specified va "src" (an URL) pointing into the img/wiki_up directory if (! empty($args['src']) && preg_match('|img/wiki_up/(.*)|', $args['src'], $m)) { $file = empty($tikidomain) ? $args['src'] : str_replace('img/wiki_up/', "img/wiki_up/$tikidomain/", $args['src']); $image = ['filename' => $m[1], 'where' => 'wiki', 'zip' => "$dir/images/wiki/" . $m[1], 'wiki' => $args['src']]; if (! $this->zip->addFile($file, $image['zip'])) { $this->errors[] = 'Can not add the image '; $this->errorsArgs[] = $file; return false; } } // An image in an image gallery. No longer supported in Tiki 23 and not supported here as well. /*elseif (! empty($args['src']) && preg_match('|show_image.php\?(.*)|', $args['src'], $m)) { // TODO ImageGalleryRemoval23.x - replace with tiki.file.imageid fileId }*/ // An image specified via "src" (an URL) in a file gallery (pointing to // tiki-download_file.php). elseif (! empty($args['src']) && preg_match('|tiki-download_file.php\?(.*)|', $args['src'], $m)) { if (($i = strpos($args['src'], 'tiki-download_file.php')) > 0) { $path = $_SERVER['HTTP_HOST'] . $tikiroot . substr($args['src'], $i); } else { $path = $_SERVER['HTTP_HOST'] . $tikiroot . $args['src']; } $img = $this->httprequest($path); parse_str($m[1], $p); $image = ['where' => 'fgal', 'zip' => "$dir/images/fgal/" . $p['fileId'], 'wiki' => $args['src']]; if (! $this->zip->addFromString($image['zip'], $img)) { $this->errors[] = 'Can not add the image'; $this->errorsArgs[] = $m[1]; return false; } } /* else no idea where the img comes from - suppose there are outside tw */ $images[] = $image; } } $smarty->assign_by_ref('images', $images); // Deal with attachments to the wiki page. if ($prefs['feature_wiki_attachments'] == 'y' && $this->config['attachments']) { $wikilib = TikiLib::lib('wiki'); $attachments = $wikilib->list_wiki_attachments($page, 0, -1); if (! empty($attachments['cant'])) { foreach ($attachments['data'] as $key => $att) { $att_info = $wikilib->get_item_attachment($att['attId']); $attachments['data'][$key]['zip'] = "$dir/attachments/" . $att['attId']; if ($prefs['w_use_dir']) { if (! $this->zip->addFile($prefs['w_use_dir'] . $att_info['path'], $attachments['data'][$key]['zip'])) { $this->errors[] = 'Can not add the attachment'; $this->errorsArgs[] = $att_info['attId']; return false; } } else { if (! $this->zip->addFromString($attachments['data'][$key]['zip'], $att_info['data'])) { $this->errors[] = 'Can not add the attachment'; $this->errorsArgs[] = $att_info['attId']; return false; } } } $smarty->assign_by_ref('attachments', $attachments['data']); } } // Deal with the history of the page. if ($prefs['feature_history'] == 'y' && $this->config['history']) { $histlib = TikiLib::lib('hist'); $history = $histlib->get_page_history($page, false); foreach ($history as $key => $hist) { $all = $histlib->get_version($page, $hist['version']); // can be optimised if returned in the list //$history[$key]['data'] = $all['data']; $history[$key]['zip'] = "$dir/history/" . $all['version'] . '.txt'; if (! $this->zip->addFromString($history[$key]['zip'], $all['data'])) { $this->errors[] = 'Can not add the history'; $this->errorsArgs[] = $all['version']; return false; } } $smarty->assign_by_ref('history', $history); } // Render the metadata of the page with Smarty, and append it to the wiki.xml file, which is // kept in the $xml member variable. $smarty->assign_by_ref('config', $this->config); $this->xml .= $smarty->fetch('tiki-export_page_xml.tpl'); return true; } /* import pages or structure */ public function import_pages($zipFile = 'dump/xml.zip', $config = null) { if (! empty($config)) { $this->config = array_merge($this->config, $config); } // Open the Zip file if (! ($this->zip = new ZipArchive())) { $this->errors[] = 'Problem zip initialisation'; $this->errorsArgs[] = ''; return false; } if (! $this->zip->open($zipFile)) { $this->errors[] = 'The file cannot be opened'; $this->errorsArgs[] = $zipFile; return false; } // Open the wiki.xml if (($this->xml = $this->zip->getFromName(WIKI_XML)) === false) { $this->errors[] = 'Can not unzip'; $this->errorsArgs[] = WIKI_XML; return false; } // Parse the wiki.xml $parser = new page_Parser(); $parser->setInput($this->xml); $ok = $parser->parse(); if (PEAR::isError($ok)) { $this->errors[] = $ok->getMessage(); $this->errorsArgs[] = ''; return false; } $infos = $parser->getPages(); if ($this->config['debug']) { echo 'XML PARSING
';
            print_r($infos);
            echo '
'; } // Create the pages from the Zip file contents and the wiki.xml parse result. foreach ($infos as $info) { if (! $this->create_page($info)) { return false; } } $this->zip->close(); return true; } // Create or update a page from within the Zip file and an xml parsing result public function create_page($info) { global $prefs, $tiki_p_wiki_attach_files, $tiki_p_edit_comments, $tikidomain; $tikilib = TikiLib::lib('tiki'); // Get the page content from the Zip file. if (($info['data'] = $this->zip->getFromName($info['zip'])) === false) { $this->errors[] = 'Can not unzip'; $this->errorsArgs[] = $info['zip']; return false; } // Create or update the page. if ($this->page_exists($info['name'])) { // Page already exists. Update it with the page from the Zip file. $old = true; $tikilib->update_page( $info['name'], $info['data'], 'Updated from import', ! empty($this->config['fromUser']) ? $this->config['fromUser'] : $info['user'], ! empty($this->config['fromSite']) ? $this->config['fromSite'] : $info['ip'], $info['description'], 0, isset($info['lang']) ? $info['lang'] : '', isset($info['is_html']) ? $info['is_html'] : false, null, null, isset($info['wysiwyg']) ? $info['wysiwyg'] : null ); } else { // Page doesn't exists yet. Create it. $old = false; $tikilib->create_page( $info['name'], $info['hits'], $info['data'], $info['lastModif'], $info['comment'], ! empty($this->config['fromUser']) ? $this->config['fromUser'] : $info['user'], ! empty($this->config['fromSite']) ? $this->config['fromSite'] : $info['ip'], $info['description'], isset($info['lang']) ? $info['lang'] : '', isset($info['is_html']) ? $info['is_html'] : false, null, isset($info['wysiwyg']) ? $info['wysiwyg'] : null, '', 0, $info['created'] ); } // Add the wiki page comments to the new or updated page. if ($prefs['feature_wiki_comments'] == 'y' && $tiki_p_edit_comments == 'y' && ! empty($info['comments'])) { $newThreadIds = []; foreach ($info['comments'] as $comment) { $commentslib = TikiLib::lib('comments'); $parentId = empty($comment['parentId']) ? 0 : $newThreadIds[$comment['parentId']]; if ($parentId) { $reply_info = $commentslib->get_comment($parentId); $in_reply_to = $reply_info['message_id']; } $newThreadIds[$comment['threadId']] = $commentslib->post_new_comment( 'wiki page:' . $info['name'], $parentId, $this->config['fromUser'] ? $this->config['fromUser'] : $comment['user'], $comment['title'], $comment['data'], $message_id, $in_reply_to, 'n', '', '', '', '', $comment['date'] ); } } // Add the attachments to the new or updated page. if ($prefs['feature_wiki_attachments'] == 'y' && $tiki_p_wiki_attach_files == 'y' && ! empty($info['attachments'])) { // Interate over the attachments of the page foreach ($info['attachments'] as $attachment) { // Unzip the attachment, save its data in $attachment['data']. if (($attachment['data'] = $this->zip->getFromName($attachment['zip'])) === false) { $this->errors[] = 'Can not unzip attachment'; $this->errorsArgs[] = $attachment['zip']; return false; } // $fhash: A unique name for the attached file, iff stored in directory (and not in // database). This begins with a (MD5-) hash of the attachment file name. if ($prefs['w_use_db'] == 'y') { $fhash = ''; } else { $fhash = $this->get_attach_hash_file_name($attachment['filename']); // Write the attachment to the attachments directory, with the unique file name. if ($fw = fopen($prefs['w_use_dir'] . $fhash, 'wb')) { if (! fwrite($fw, $attachment['data'])) { $this->errors[] = 'Cannot write to this file'; $this->errorsArgs[] = $prefs['w_use_dir'] . $fhash; } fclose($fw); $attachment['data'] = ''; } else { $this->errors[] = 'Cannot open this file'; $this->errorsArgs[] = $prefs['w_use_dir'] . $fhash; } } // Register the attachment in the database. $wikilib = TikiLib::lib('wiki'); $wikilib->wiki_attach_file( $info['name'], $attachment['filename'], $attachment['filetype'], $attachment['filesize'], $attachment['data'], // The data of the attachment, iff it is stored in the database $attachment['comment'], $attachment['user'], $fhash, $attachment['created'] ); } } // ??? Add some of the images from the Zip file. This only does so for images in the // img/wiki_up directory. Images in file galleries are silently ignored. if ($prefs['feature_wiki_pictures'] == 'y' && ! empty($info['images'])) { foreach ($info['images'] as $image) { if (empty($image['zip'])) {//external link to image continue; } if (($image['data'] = $this->zip->getFromName($image['zip'])) === false) { $this->errors[] = 'Can not unzip image'; $this->errorsArgs[] = $image['zip']; return false; } if ($image['where'] == 'wiki') { $wiki_up = 'img/wiki_up/'; if ($tikidomain) { $wiki_up .= "$tikidomain/"; } $name = str_replace('img/wiki_up/', '', $image['wiki']); file_put_contents($wiki_up . $name, $image['data']); chmod($wiki_up . $name, 0644); } } } // Add the page history to the new or updated page. if ($prefs['feature_history'] == 'y' && ! empty($info['history'])) { $query = 'select max(`version`) from `tiki_history` where `pageName`=?'; $maxVersion = $this->getOne($query, [$info['name']]); if (! $maxVersion) { $maxVersion = 0; } $newVersion = $maxVersion; foreach ($info['history'] as $version) { if (($version['data'] = $this->zip->getFromName($version['zip'])) === false) { $this->errors[] = 'Can not unzip history'; $this->errorsArgs[] = $version['version']; return false; } $query = 'insert into `tiki_history`(`pageName`, `version`, `lastModif`, `user`, `ip`, `comment`, `data`, `description`) values(?,?,?,?,?,?,?,?)'; $this->query( $query, [ $info['name'], $version['version'] + $maxVersion, $old ? $tikilib->now : $version['lastModif'], $version['user'], $version['ip'], $version['comment'], $version['data'], $version['description'] ] ); $newVersion = max($version['version'] + $maxVersion, $newVersion); } $query = 'update `tiki_pages` set `version`=? where `pageName`=?'; $this->query($query, [$newVersion, $info['name']]); } // Import a structure. if ($prefs['feature_wiki_structure'] == 'y' && ! empty($info['structure'])) { $structlib = TikiLib::lib('struct'); //TODO alias if ($info['structure'] == 1) { $this->structureStack[$info['structure']] = $structlib->s_create_page(null, null, $info['name'], ''); if (empty($this->structureStack[$info['structure']])) { $this->errors[] = 'A structure already exists'; $this->errorsArgs[] = $info['name']; return false; } } elseif (! empty($info['structure'])) { $this->structureStack[$info['structure']] = $structlib->s_create_page( $this->structureStack[$info['structure'] - 1], isset($this->structureStack[$info['structure']]) ? $this->structureStack[$info['structure']] : '', $info['name'], '', $this->structureStack[1] ); } } return true; } // create_page() } // class XmlLib $xmllib = new XmlLib(); class page_Parser extends XML_Parser { public $page; public $currentTag = null; public $context = null; public $folding = false; // keep tag as original public $commentsStack = []; public $commentId = 0; public $iStructure = 0; public function startHandler($parser, $name, $attribs) { switch ($name) { case 'page': $this->context = null; if (is_array($attribs)) { $this->page = [ 'data' => '', 'comment' => '', 'description' => '', 'user' => 'admin', 'ip' => '0.0.0.0', 'lang' => '', 'is_html' => false, 'hash' => null, 'wysiwyg' => null ]; $this->page = array_merge($this->page, $attribs); } if ($this->iStructure > 0) { $this->page['structure'] = $this->iStructure; } break; case 'structure': ++$this->iStructure; break; case 'comments': $comentsStack = []; break; case 'attachments': case 'history': case 'images': $this->context = $name; $this->i = -1; break; case 'comment': if ($this->context == 'comments') { ++$this->i; $this->page[$this->context][$this->i] = $attribs; $this->page[$this->context][$this->i]['parentId'] = empty($this->commentsStack) ? 0 : $this->commentsStack[count($this->commentsStack) - 1]; $this->page[$this->context][$this->i]['threadId'] = ++$this->commentId; array_push($this->commentsStack, $this->commentId); } else { $this->currentTag = $name; } break; case 'attachment': ++$this->i; $this->page[$this->context][$this->i] = ['comment' => '']; $this->page[$this->context][$this->i] = array_merge($this->page[$this->context][$this->i], $attribs); break; case 'version': ++$this->i; $this->page[$this->context][$this->i] = ['comment' => '', 'description' => '', 'ip' => '0.0.0.0']; $this->page[$this->context][$this->i] = array_merge($this->page[$this->context][$this->i], $attribs); break; case 'image': ++$this->i; $this->page[$this->context][$this->i] = $attribs; break; default: $this->currentTag = $name; break; } } public function endHandler($parser, $name) { $this->currentTag = null; switch ($name) { case 'comments': case 'attachments': case 'history': case 'images': $this->context = null; break; case 'comment': array_pop($this->commentsStack); break; case 'page': $this->pages[] = $this->page; break; case 'structure': --$this->iStructure; break; } } public function cdataHandler($parser, $data) { $data = trim($data); if (empty($data)) { return true; } if (empty($this->context)) { $this->page[$this->currentTag] = $data; } else { $this->page[$this->context][$this->i][$this->currentTag] = $data; } } public function getPages() { return $this->pages; } }