debug = new siteexport_debug(); $this->debug->isAJAX = $isAJAX; $this->settings = new settings_plugin_siteexport_settings($this); $this->debug->message("Settings completed: zipFile", $this->settings->zipFile, 1); } } public function getPluginName() { return 'siteexport'; } public function downloadURL() { $params = array('cache' => 'nocache', 'siteexport' => $this->settings->pattern); if ( $this->debug->debugLevel() < 5 ) { // If debug, then debug! $params['debug'] = $this->debug->debugLevel(); } return ml($this->settings->origZipFile, $params, true, '&'); } public function checkIfCacheFileExistsForFileWithPattern($file, $pattern) { if ( !@file_exists($file) ) { // If the cache File does not exist, move the newly created one over ... $this->debug->message("'{$file}' does not exist. Checking original ZipFile", null, 3 ); $newCacheFile = mediaFN($this->getSpecialExportFileName($this->settings->origZipFile, $pattern)); if ( !@file_exists($newCacheFile) ) { $this->debug->message("The export must have gone wrong. The cached file does not exist.", array("pattern" => $pattern, "original File" => $this->settings->origZipFile, "expected cached file" => $newCacheFile), 3); } $status = io_rename($newCacheFile, $file); $this->debug->message("had to move another original file over. Did it work? " . ($status ? 'Yes, it did.' : 'No, it did not.'), null, 2 ); } else { $this->debug->message("The file does exist!", $file, 2 ); } } /** * Returns an utf8 encoded Namespace for a Page and input Namespace * @param $NS * @param $PAGE */ function getNamespaceFromID($NS, &$PAGE) { global $conf; // Check current page - if its an NS add the startpage $clean = true; resolve_pageid(getNS($NS), $NS, $clean); if ( ! page_exists($NS) && array_pop(explode(':', $NS)) != strtolower($conf['start'] )) { // Compare to lowercase since clean lowers it. $NS .= ':' . $conf['start']; resolve_pageid(getNS($NS), $NS, $clean); } $PAGE = noNS($NS); $NS = getNS($NS); return utf8_encodeFN(str_replace(':', '/', $NS)); } /** * create a file name for the page **/ public function getSiteName($ID, $overrideRewrite=false) { global $conf; if ( empty($ID) ) return false; $url = $this->wl($this->cleanID($ID), null, true, null, null, $overrideRewrite); // this must be done with rewriting set to override //$url = $this->wl($this->cleanID($ID), null, true); // this must be done with rewriting set to override $uri = @parse_url($url); if ( $uri['path'][0] == '/' ) { $uri['path'] = substr($uri['path'], 1); } return $this->shortenName($uri['path'] . '.' . $this->settings->fileType); } /** * get the Title for the page **/ public function getSiteTitle($ID) { if (useHeading('content') && $ID) { $heading = p_get_first_heading($ID,true); if ($heading) { return $this->xmlEntities($heading); } } return ucwords($this->xmlEntities(array_pop(explode(':', $ID)))); } /** * Encoding ()taken from DW - but without needing the renderer **/ public function xmlEntities($string) { return htmlspecialchars($string,ENT_QUOTES,'UTF-8'); } /** * Create name for the file inside the zip and the replacements **/ function shortenName($NAME) { $NS = $this->settings->exportNamespace; $NAME = preg_replace("%^" . DOKU_BASE . "%", "", $NAME); $NAME = preg_replace("%^(_media/)?(" . $NS . "/)?%", "", $NAME); $this->debug->message("Shortening file to '$NAME'", null, 1); return $NAME; } /** * Remove unwanted chars from ID * * Cleans a given ID to only use allowed characters. Accented characters are * converted to unaccented ones * * @author Andreas Gohr * @param string $raw_id The pageid to clean * @param boolean $ascii Force ASCII * @param boolean $media Allow leading or trailing _ for media files */ function cleanID($raw_id,$ascii=false,$media=false){ global $conf; global $lang; static $sepcharpat = null; global $cache_cleanid; $cache = & $cache_cleanid; // check if it's already in the memory cache if (isset($cache[(string)$raw_id])) { return $cache[(string)$raw_id]; } $sepchar = $conf['sepchar']; if($sepcharpat == null) // build string only once to save clock cycles $sepcharpat = '#\\'.$sepchar.'+#'; $id = trim((string)$raw_id); // $id = utf8_strtolower($id); // NO LowerCase for us! //alternative namespace seperator $id = strtr($id,';',':'); if($conf['useslash']){ $id = strtr($id,'/',':'); }else{ $id = strtr($id,'/',$sepchar); } if($conf['deaccent'] == 2 || $ascii) $id = utf8_romanize($id); if($conf['deaccent'] || $ascii) $id = utf8_deaccent($id,-1); //remove specials $id = utf8_stripspecials($id,$sepchar,'\*'); if($ascii) $id = utf8_strip($id); //clean up $id = preg_replace($sepcharpat,$sepchar,$id); $id = preg_replace('#:+#',':',$id); $id = ($media ? trim($id,':.-') : trim($id,':._-')); $id = preg_replace('#:[:\._\-]+#',':',$id); $cache[(string)$raw_id] = $id; return($id); } /** * This builds a link to a wikipage - changed for internal use here * * It handles URL rewriting and adds additional parameter if * given in $more * * @author Andreas Gohr */ function wl($id='',$more='',$abs=false,$sep='&', $IDexists=true, $overrideRewrite=false, $hadBase=false){ global $conf; $this->debug->message("Starting to build WL-URL for '$id'", $more, 1); if(is_array($more)){ $intermediateMore = ''; foreach( $more as $key => $value) { if ( strlen($intermediateMore) > 0 ) { $intermediateMore .= $sep; } if ( !is_array($value) ) { $intermediateMore .= rawurlencode($key) . '='; $intermediateMore .= rawurlencode($value); continue; } foreach( $value as $val ) { if ( strlen($intermediateMore) > 0 ) { $intermediateMore .= $sep; } $intermediateMore .= rawurlencode($key) . '[]='; $intermediateMore .= rawurlencode($val); } } $more = $intermediateMore; }else{ $more = str_replace(',',$sep,$more); } $id = idfilter($id); if($abs){ $xlink = DOKU_URL; if ( !$IDexists && !$hadBase ) { // If the file does not exist, we have to remove the base. This link my be one to an parallel BASE. $xlink = preg_replace('#' . DOKU_BASE . '$#', '', $xlink); } }else if ($IDexists || $hadBase) { // if the ID does exist, we may add the base. $xlink = DOKU_BASE; } else{ $xlink = ""; } // $this->debug->message("internal WL function Before Replacing: '$xlink'", array(DOKU_REL, DOKU_URL, DOKU_BASE, $xlink), 2); $xlink = preg_replace('#(?debug->message("'$xlink'", array(DOKU_REL, DOKU_URL, DOKU_BASE, $xlink), 2); if ( $overrideRewrite ) { $this->debug->message("Override enabled.", null, 1); $id = strtr($id,':','/'); $xlink .= $id; if($more) $xlink .= '?'.$more; } else { if($conf['userewrite'] == 2 ){ $xlink .= DOKU_SCRIPT.'/'.$id; if($more) $xlink .= '?'.$more; }elseif($conf['userewrite'] ){ $xlink .= $id; if($more) $xlink .= '?'.$more; }elseif($id){ $xlink .= DOKU_SCRIPT.'?id='.$id; if($more) $xlink .= $sep.$more; }else{ $xlink .= DOKU_SCRIPT; if($more) $xlink .= '?'.$more; } } $this->debug->message("internal WL function result: '$xlink'", null, 2); return $xlink; } /** * Create the export file name - this is the file where everything is being stored * @param $FILE * @param $PATTERN - additional pattern for re-using old files */ public function getSpecialExportFileName($FILE, $PATTERN=null) { if ( empty($FILE) ) { $FILE = $this->settings->origZipFile; } if ( empty($PATTERN) && empty($this->settings->pattern) ){ $this->debug->message("Generating an internal md5 pattern. This will go wrong - and won't cache properly.", null, 3); $PATTERN = md5(microtime(false)); } // Set Pattern Global for other stuff if ( empty($this->settings->pattern) ) { $this->settings['pattern'] = $PATTERN; } else { $PATTERN = $this->settings->pattern; } $FA = explode('.', $FILE); $EXT = array_pop($FA); array_push($FA, 'auto'); array_push($FA, $PATTERN); array_push($FA, $EXT); $fileName = implode('.', $FA); $this->debug->message("Export Filename for '$FILE' will be: '$fileName'", null, 2); return $fileName; } public function getCacheFileNameForPattern($PATTERN = null) { if ( $PATTERN == null ) { $PATTERN = $this->settings->pattern; } return getCacheName($this->getSpecialExportFileName($this->settings->origZipFile, $PATTERN), '.' . basename(mediaFN($this->settings->origZipFile)) ); } function startRedirctProcess($counter) { global $ID; $URL = wl($ID); $additionalParameters = $_REQUEST; $additionalParameters['startcounter'] = $counter; $additionalParameters['pattern'] = $this->settings->pattern; unset($additionalParameters['id']); unset($additionalParameters['u']); unset($additionalParameters['p']); unset($additionalParameters['r']); unset($additionalParameters['http_credentials']); $this->addAdditionalParametersToURL($URL, $additionalParameters); $this->debug->message("Redirecting to '$URL'", null, 2); send_redirect($URL); exit(0); // Should not be reached, but anyways } /** * Builds additional Parameters into the URL given * @param $URL * @param $newAdditionalParameters */ function addAdditionalParametersToURL(&$URL, $newAdditionalParameters) { // Add additionalParameters if ( !empty($newAdditionalParameters) ) { foreach($newAdditionalParameters as $key => $value ) { if ( empty($key) || empty($value) ) { continue; } $append = ''; if ( is_array($value) ) { foreach( array_values($value) as $aValue ) { // Array Handling $URL .= (strstr($URL, '?') ? '&' : '?') . $key . "[]=$aValue"; } } else { $append = "$key=$value"; $URL .= empty($append) || strstr($URL, $append) ? '' : (strstr($URL, '?') ? '&' : '?') . $append; } } } } /** * Cleans the wiki variables and returns a rebuild URL that has the new variables at hand * @param $data */ function prepare_POSTData($data) { $NS = !empty($data['ns']) ? $data['ns'] : $data['id']; $this->removeWikiVariables($data); $data['do'] = 'siteexport'; $additionalKeys = ''; ksort($data); $this->debug->message("Prepared POST data:", $data, 1); foreach( $data as $key => $value ) { if ( !is_array($value) ) { continue; } $this->debug->message("Found inner Array:", $value, 1); asort($value); foreach ( $value as $innerKey => $aValue ) { if ( is_numeric($innerKey)) { $innerKey = ''; } $additionalKeys .= "&$key" . "[$innerKey]=$aValue"; } unset($data[$key]); } return wl($NS, $data, true, '&') . $additionalKeys; } /** * Parses a String into a $_REQUEST Like variant. You have to tell if a decode of the values is needed * @param $inputArray * @param $decode */ public function parseStringToRequestArray($inputArray, $decode=false) { global $plugin_controller; $outputArray = $inputArray; if ( !is_array($inputArray) ) { $intermediate = str_replace("&", "&", $inputArray); $outputArray = array(); foreach( explode("&", $intermediate) as $param ) { list($key, $value) = explode("=", $param, 2); // This is needed if we do want to calculate $_REQUEST for a non HTTP-Request if ( $decode) { $value = urldecode($value); } if ( empty($key) ) { continue; } // Don't check on Value, because there may be only the key that should be preserved if ( substr($key, -2) == '[]' ) { $key = substr($key, 0, -2); if ( !is_array($outputArray[$key]) ) { $outputArray[$key] = array(); } array_push($outputArray[$key], $value); // Array Handling } else { $outputArray[$key] = $value; } } } if ( !empty($outputArray['diPlu']) ) { $allPlugins = array(); foreach($plugin_controller->getList(null,true) as $plugin ) { // check for CSS or JS if ( !file_exists(DOKU_PLUGIN."$plugin/script.js") && !file_exists(DOKU_PLUGIN."$p/style.css") ) { continue; } $allPlugins[] = $plugin; } if ( count($outputArray['diPlu']) > (count($allPlugins) / 2) ) { $outputArray['diInv'] = 1; $outputArray['diPlu'] = array_diff($allPlugins, $outputArray['diPlu']); } } return $outputArray; } /** * Remove certain fields from the list. * @param $removeArray * @param $advanced * @param $isString */ function removeWikiVariables(&$removeArray, $advanced=false, $isString=false) { $removeArray = $this->parseStringToRequestArray($removeArray); // 2010-08-23 - If there is still the media set, retain the id for e.g. detail.php if ( !isset($removeArray['media']) ) { unset($removeArray['id']); } unset($removeArray['do']); unset($removeArray['ns']); unset($removeArray['call']); unset($removeArray['sectok']); unset($removeArray['rndval']); unset($removeArray['tseed']); unset($removeArray['http_credentials']); unset($removeArray['u']); unset($removeArray['p']); unset($removeArray['r']); unset($removeArray['base']); unset($removeArray['siteexport']); unset($removeArray['DokuWiki']); unset($removeArray['cronOverwriteExisting']); if ( $removeArray['renderer'] == 'xhtml' ) { $removeArray['do'] = 'export_' . $removeArray['renderer']; unset($removeArray['renderer']); } // Keep custom options if ( is_array($removeArray['customoptionname']) && is_array($removeArray['customoptionvalue']) && count($removeArray['customoptionname']) == count($removeArray['customoptionvalue']) ) { for( $index=0; $index $value ) { if ( is_array($value) ) { foreach( array_values($value) as $aValue ) { // Array Handling $removeArray[] = $key . "[]=$aValue"; } } else { $value = trim($value); $removeArray[] = "$key" . ( ((empty($value) && intval($value) !== 0)) || $value == '' ? '' : "=$value" ); // If the Value is empty, the Key must be preserved } } //$removeArray = implode( ($this->settings->fileType == 'pdf' ? "&" : "&"), $removeArray); $removeArray = implode( "&", $removeArray); // The & made problems with the HTTPClient / Apache. It should not be a problem to have & } } /** * returns a hashed name for the parameters * @param $parameters */ public function cronJobNameForParameters($parameters) { return md5($parameters); } /** * Takes an URL and transforms it into the path+query part * Used several times, e.g. for genering the hash for the cache file * @param $url */ public function urlToPathAndParams($url) { $query = parse_url($url, PHP_URL_QUERY); $path = preg_replace(":^".DOKU_REL.":", "", parse_url($url, PHP_URL_PATH)); return "{$path}?{$query}"; } /** * Transforms an $_REQUEST into a Hash that can be used for cron and cache file * @param $request */ public function requestParametersToCacheHash($request) { $params = $this->urlToPathAndParams($this->prepare_POSTData($request)); $this->debug->message("Calculated the following Cache Hash URL: ", $params, 2); return $this->cronJobNameForParameters($params); } /** * Check a replaceID against a baseID - and make the replaceID relative against it * @param $replaceID - ID which will be made relative if needed * @param $baseID - ID which is the reference to be made relative against */ public function getRelativeURL($replaceID, $baseID) { $origReplaceID = $replaceID; $replaceTmp = cleanID($replaceID); $file = noNS($replaceTmp); $replaceID = getNS($replaceTmp); $baseID = getNS($baseID); $replaceParts = explode(':', $replaceID); $baseParts = explode(':', $baseID); $exportNSParts = explode(':', cleanID($this->settings->exportNamespace)); $newBase = array(); foreach($exportNSParts as $exportNS) { if ( $replaceParts[0] == $exportNS && $baseParts[0] == $exportNS ) { array_shift($replaceParts); array_shift($baseParts); array_shift($exportNSParts); } else { // Nothing is matching anymore break; } } $i = count($exportNSParts); $this->debug->message("Checking", array('current extra removing amount'=>$i,'replace'=>$replaceParts,'base'=>$baseParts,'exportNSParts'=>$exportNSParts),1); // Now if there is just one item in the ens left and it matches the base, but not the replace, this miiiiiight be the case we want. if ( count($exportNSParts) == 1 && $exportNSParts[0] == $baseParts[0] ) { array_shift($replaceParts); $newBase = implode('/', $replaceParts); if ( substr($newBase, -1) != '/' ) { $newBase .= '/'; } $this->debug->message("new Base: ", $newBase, 1); // Now check from the beginning ... return $newBase . $file; } return $origReplaceID; } } ?>