mirror of
https://github.com/processwire/processwire.git
synced 2025-08-26 08:04:38 +02:00
Continued updates to PagesExportImport, add ZIP export/import option to ProcessPagesExportImport (with page files support), and bump version to 3.0.72
This commit is contained in:
@@ -522,6 +522,7 @@ abstract class Fieldtype extends WireData implements Module {
|
||||
* @param Field $field
|
||||
* @param string|int|float|array|object $value
|
||||
* @return string|int|float|array
|
||||
* @see Fieldtype::wakeupValue()
|
||||
*
|
||||
*/
|
||||
public function ___sleepValue(Page $page, Field $field, $value) {
|
||||
@@ -535,11 +536,14 @@ abstract class Fieldtype extends WireData implements Module {
|
||||
* This is intended for importing from PW-driven web services. If not overridden, it does
|
||||
* the same thing as the `Fieldtype::wakeupValue()` method.
|
||||
*
|
||||
* #pw-internal
|
||||
*
|
||||
* @param Page $page
|
||||
* @param Field $field
|
||||
* @param string|int|float|array|null $value
|
||||
* @param array $options Additional options if needed/applicable
|
||||
* @return string|int|array|object $value
|
||||
* @see Fieldtype::exportValue()
|
||||
*
|
||||
*/
|
||||
public function ___importValue(Page $page, Field $field, $value, array $options = array()) {
|
||||
@@ -548,6 +552,22 @@ abstract class Fieldtype extends WireData implements Module {
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get associative array of options and info (name => value) that Fieldtype supports for importValue
|
||||
*
|
||||
* - `test` (bool): indicates Fieldtype supports testing import before committing.
|
||||
*
|
||||
* #pw-internal
|
||||
*
|
||||
* @param array Field $field
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
public function getImportValueOptions(Field $field) {
|
||||
if($field) {} // ignore
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a value, return an portable version of it as either a string, int, float or array
|
||||
*
|
||||
|
@@ -25,6 +25,87 @@
|
||||
|
||||
class PagesExportImport extends Wire {
|
||||
|
||||
/**
|
||||
* Get the path where ZIP exports are stored
|
||||
*
|
||||
* @param string $subdir Specify a subdirectory name if you want it to create it.
|
||||
* If it exists, it will create a numbered version of the subdir to ensure it is unique.
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
public function getExportPath($subdir = '') {
|
||||
|
||||
/** @var WireFileTools $files */
|
||||
$files = $this->wire('files');
|
||||
$path = $this->wire('config')->paths->assets . 'backups/' . $this->className() . '/';
|
||||
|
||||
$readmeText = "When this file is present, files and directories in here are auto-deleted after a short period of time.";
|
||||
$readmeFile = $this->className() . '.txt';
|
||||
$readmeFiles = array();
|
||||
|
||||
if(!is_dir($path)) {
|
||||
$files->mkdir($path, true);
|
||||
$readmeFiles[] = $path . $readmeFile;
|
||||
}
|
||||
|
||||
if($subdir) {
|
||||
$n = 0;
|
||||
do {
|
||||
$_path = $path . $subdir . ($n ? "-$n" : '') . '/';
|
||||
} while(++$n && is_dir($_path));
|
||||
$path = $_path;
|
||||
$files->mkdir($path, true);
|
||||
$readmeFiles[] = $path . $readmeFile;
|
||||
}
|
||||
|
||||
foreach($readmeFiles as $file) {
|
||||
file_put_contents($file, $readmeText);
|
||||
$files->chmod($readmeFile);
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove files and directories in /site/assets/backups/PagesExportImport/ that are older than $maxAge
|
||||
*
|
||||
* @param int $maxAge Maximum age in seconds
|
||||
* @return int Number of files/dirs removed
|
||||
*
|
||||
*/
|
||||
public function cleanupFiles($maxAge = 3600) {
|
||||
|
||||
/** @var WireFileTools $files */
|
||||
$files = $this->wire('files');
|
||||
$path = $this->getExportPath();
|
||||
$qty = 0;
|
||||
|
||||
foreach(new \DirectoryIterator($path) as $file) {
|
||||
|
||||
if($file->isDot()) continue;
|
||||
if($file->getBasename() == $this->className() . '.txt') continue; // we want this file to stay
|
||||
if($file->getMTime() >= (time() - $maxAge)) continue; // not expired
|
||||
|
||||
$pathname = $file->getPathname();
|
||||
|
||||
if($file->isDir()) {
|
||||
$testFile = rtrim($pathname, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $this->className() . '.txt';
|
||||
if(!is_file($testFile)) continue;
|
||||
if($files->rmdir($pathname, true)) {
|
||||
$this->message($this->_('Removed old directory') . " - $pathname", Notice::debug);
|
||||
$qty++;
|
||||
}
|
||||
} else {
|
||||
if(unlink($pathname)) {
|
||||
$this->message($this->_('Removed old file') . " - $pathname", Notice::debug);
|
||||
$qty++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $qty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export given PageArray to a ZIP file
|
||||
*
|
||||
@@ -34,22 +115,46 @@ class PagesExportImport extends Wire {
|
||||
*
|
||||
*/
|
||||
public function exportZIP(PageArray $items, array $options = array()) {
|
||||
$tempDir = new WireTempDir($this);
|
||||
$this->wire($tempDir);
|
||||
$tempDir->setRemove(false);
|
||||
$path = $tempDir->get();
|
||||
$jsonFile = $path . "pages.json";
|
||||
$jsonData = $this->exportJSON($items, $options);
|
||||
file_put_contents($jsonFile, $jsonData);
|
||||
|
||||
/** @var WireFileTools $files */
|
||||
$files = $this->wire('files');
|
||||
$zipFileItems = array($jsonFile);
|
||||
$zipFileName = $path . 'pages.zip';
|
||||
$zipFileInfo = $files->zip($zipFileName, $zipFileItems);
|
||||
foreach($zipFileItems as $file) {
|
||||
unlink($file);
|
||||
|
||||
$options['exportTarget'] = 'zip';
|
||||
$zipPath = $this->getExportPath();
|
||||
if(!is_dir($zipPath)) $files->mkdir($zipPath, true);
|
||||
|
||||
$tempDir = new WireTempDir($this);
|
||||
$this->wire($tempDir);
|
||||
$tmpPath = $tempDir->get();
|
||||
$jsonFile = $tmpPath . "pages.json";
|
||||
$zipItems = array($jsonFile);
|
||||
$data = $this->pagesToArray($items, $options);
|
||||
|
||||
// determine other files to add to ZIP
|
||||
foreach($data['pages'] as $key => $item) {
|
||||
if(!isset($item['_filesPath'])) continue;
|
||||
$zipItems[] = $item['_filesPath'];
|
||||
unset($data['pages'][$key]['_filesPath']);
|
||||
}
|
||||
return $zipFileName;
|
||||
|
||||
// write out the pages.json file
|
||||
file_put_contents($jsonFile, wireEncodeJSON($data, true, true));
|
||||
|
||||
$n = 0;
|
||||
do {
|
||||
$zipName = $zipPath . 'pages' . ($n ? "-$n" : '') . '.zip';
|
||||
} while(++$n && file_exists($zipName));
|
||||
|
||||
// @todo report errors from zipInfo
|
||||
$zipInfo = $files->zip($zipName, $zipItems, array(
|
||||
'maxDepth' => 1,
|
||||
'allowHidden' => false,
|
||||
'allowEmptyDirs' => false
|
||||
));
|
||||
|
||||
unlink($jsonFile);
|
||||
|
||||
return $zipName;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,17 +166,23 @@ class PagesExportImport extends Wire {
|
||||
*
|
||||
*/
|
||||
public function importZIP($filename, array $options = array()) {
|
||||
|
||||
$tempDir = new WireTempDir($this);
|
||||
$this->wire($tempDir);
|
||||
$path = $tempDir->get();
|
||||
$options['filesPath'] = $path;
|
||||
|
||||
$zipFileItems = $this->wire('files')->unzip($filename, $path);
|
||||
if(empty($zipFileItems)) {
|
||||
$pageArray = false;
|
||||
} else {
|
||||
$jsonFile = $path . "pages.json";
|
||||
$jsonData = file_get_contents($jsonFile);
|
||||
$pageArray = $this->importJSON($jsonData, $options);
|
||||
}
|
||||
|
||||
if(empty($zipFileItems)) return false;
|
||||
|
||||
$jsonFile = $path . "pages.json";
|
||||
$jsonData = file_get_contents($jsonFile);
|
||||
$data = json_decode($jsonData, true);
|
||||
if($data === false) return false;
|
||||
|
||||
$pageArray = $this->arrayToPages($data, $options);
|
||||
|
||||
return $pageArray;
|
||||
}
|
||||
|
||||
@@ -84,6 +195,10 @@ class PagesExportImport extends Wire {
|
||||
*
|
||||
*/
|
||||
public function exportJSON(PageArray $items, array $options = array()) {
|
||||
$defaults = array(
|
||||
'exportTarget' => 'json'
|
||||
);
|
||||
$options = array_merge($defaults, $options);
|
||||
$data = $this->pagesToArray($items, $options);
|
||||
$data = wireEncodeJSON($data, true, true);
|
||||
return $data;
|
||||
@@ -126,10 +241,14 @@ class PagesExportImport extends Wire {
|
||||
|
||||
$a = array(
|
||||
'type' => 'ProcessWire:PageArray',
|
||||
'created' => date('Y-m-d H:i:s'),
|
||||
'version' => $this->wire('config')->version,
|
||||
'pagination' => array(),
|
||||
'user' => $this->wire('user')->name,
|
||||
'host' => $this->wire('config')->httpHost,
|
||||
'pages' => array(),
|
||||
'fields' => array(),
|
||||
'timer' => Debug::timer(),
|
||||
// 'pagination' => array(),
|
||||
);
|
||||
|
||||
if($items->getLimit()) {
|
||||
@@ -190,6 +309,10 @@ class PagesExportImport extends Wire {
|
||||
}
|
||||
}
|
||||
$a['fields'][$fieldName]['blankValue'] = $blankValue;
|
||||
foreach($field->type->getImportValueOptions($field) as $k => $v) {
|
||||
if(isset($a['fields'][$fieldName][$k])) continue;
|
||||
$a['fields'][$fieldName][$k] = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,6 +328,7 @@ class PagesExportImport extends Wire {
|
||||
// sort by path to ensure parents are created before their children
|
||||
ksort($a['pages']);
|
||||
$a['pages'] = array_values($a['pages']);
|
||||
$a['timer'] = Debug::timer($a['timer']);
|
||||
|
||||
if($options['verbose']) $a['templates'] = $templates;
|
||||
|
||||
@@ -223,12 +347,18 @@ class PagesExportImport extends Wire {
|
||||
*/
|
||||
protected function pageToArray(Page $page, array $options) {
|
||||
|
||||
$defaults = array(
|
||||
'exportTarget' => '',
|
||||
);
|
||||
$options = array_merge($defaults, $options);
|
||||
|
||||
$of = $page->of();
|
||||
$page->of(false);
|
||||
|
||||
/** @var Languages $languages */
|
||||
$languages = $this->wire('languages');
|
||||
if($languages) $languages->setDefault();
|
||||
$numFiles = 0;
|
||||
|
||||
// standard page settings
|
||||
$settings = array(
|
||||
@@ -290,16 +420,20 @@ class PagesExportImport extends Wire {
|
||||
if(!empty($options['fieldNames']) && !in_array($field->name, $options['fieldNames'])) continue;
|
||||
|
||||
$info = $this->getFieldInfo($field);
|
||||
if(!$info['exportable']) {
|
||||
// $a['warnings'][$field->name] = $info['reason'];
|
||||
// $this->warning("Field '$field->name' - $info[reason]");
|
||||
continue;
|
||||
}
|
||||
if(!$info['exportable']) continue;
|
||||
|
||||
$value = $page->getUnformatted($field->name);
|
||||
$exportValue = $field->type->exportValue($page, $field, $value, $exportValueOptions);
|
||||
// $this->message($exportValue);
|
||||
|
||||
$a['data'][$field->name] = $exportValue;
|
||||
|
||||
if($field->type instanceof FieldtypeFile && $value) {
|
||||
$numFiles += count($value);
|
||||
}
|
||||
}
|
||||
|
||||
if($numFiles && $options['exportTarget'] == 'zip') {
|
||||
$a['_filesPath'] = $page->filesManager()->path();
|
||||
}
|
||||
|
||||
if($of) $page->of(true);
|
||||
@@ -412,6 +546,7 @@ class PagesExportImport extends Wire {
|
||||
'replaceFields' => array(), // array of import-data field name to replacement page field name
|
||||
'replaceTemplates' => array(), // array of import-data template name to replacement page template name
|
||||
'replaceParents' => array(), // array of import-data parent path to replacement parent path
|
||||
'filesPath' => '', // path where file field directories are located when importing from zip (internal use)
|
||||
'commit' => true, // commit the import? If false, changes aren't saved (dry run).
|
||||
'debug' => false,
|
||||
);
|
||||
@@ -747,6 +882,8 @@ class PagesExportImport extends Wire {
|
||||
}
|
||||
}
|
||||
|
||||
$fieldtypeSupportsOptions = $field->type->getImportValueOptions($field);
|
||||
|
||||
$o = array(
|
||||
'system' => true,
|
||||
'caller' => $this,
|
||||
@@ -755,8 +892,7 @@ class PagesExportImport extends Wire {
|
||||
);
|
||||
|
||||
// fake-commit for more verbose testing of certain fieldtypes
|
||||
$fakeCommitTypes = array('FieldtypePage', 'FieldtypeRepeater', 'FieldtypeComments');
|
||||
$fakeCommit = $options['commit'] || wireInstanceOf($field->type, $fakeCommitTypes);
|
||||
$fakeCommit = $options['commit'] || !empty($fieldtypeSupportsOptions['test']);
|
||||
|
||||
if($page->get('_importType') == 'create' && !$options['commit'] && !$fakeCommit) {
|
||||
// test import on a new page, so value will always be used
|
||||
@@ -828,13 +964,14 @@ class PagesExportImport extends Wire {
|
||||
$filesAdded = array();
|
||||
$filesUpdated = array();
|
||||
$filesRemoved = array();
|
||||
$filesDownloaded = array();
|
||||
$variationsAdded = array();
|
||||
|
||||
$maxFiles = (int) $field->get('maxFiles');
|
||||
$languages = $this->wire('languages');
|
||||
$filesPath = $pagefiles->path();
|
||||
/** @var WireHttp $http */
|
||||
$http = $this->wire(new WireHttp());
|
||||
/** @var null|WireHttp $http */
|
||||
$http = null;
|
||||
$pageID = $page->get('_importOriginalID');
|
||||
|
||||
foreach($data as $fileName => $fileInfo) {
|
||||
|
||||
@@ -847,7 +984,13 @@ class PagesExportImport extends Wire {
|
||||
$isNew = true;
|
||||
try {
|
||||
if($options['commit']) {
|
||||
$pagefiles->add($fileInfo['url']);
|
||||
if(empty($options['filesPath'])) {
|
||||
// importing from ZIP where files are located under filesPath option
|
||||
$pagefiles->add($fileInfo['url']);
|
||||
} else {
|
||||
// importing from URL
|
||||
$pagefiles->add("$options[filesPath]$pageID/$fileName");
|
||||
}
|
||||
$pagefile = $pagefiles->last();
|
||||
if(!$pagefile) throw new WireException("Unable to add file $fileInfo[url]");
|
||||
if($maxFiles === 1 && $pagefiles->count() > 1) {
|
||||
@@ -870,6 +1013,7 @@ class PagesExportImport extends Wire {
|
||||
// description, tags, etc.
|
||||
foreach($fileInfo as $key => $value) {
|
||||
if($key == 'url') continue;
|
||||
if($key == 'size') continue;
|
||||
if($key == 'variations') {
|
||||
$variations = $value;
|
||||
continue;
|
||||
@@ -904,22 +1048,47 @@ class PagesExportImport extends Wire {
|
||||
}
|
||||
}
|
||||
|
||||
if(count($variations)) {
|
||||
foreach($variations as $name => $url) {
|
||||
$targetFile = $filesPath . $name;
|
||||
if(!file_exists($targetFile)) {
|
||||
try {
|
||||
if($options['commit']) $http->download($url, $targetFile);
|
||||
$filesDownloaded[] = $name;
|
||||
} catch(\Exception $e) {
|
||||
$page->warning("Error downloading file $url - " . $e->getMessage());
|
||||
}
|
||||
// image variations
|
||||
foreach($variations as $name => $url) {
|
||||
|
||||
$targetFile = $filesPath . $name;
|
||||
$sourceFile = empty($options['filesPath']) ? '' : "$options[filesPath]$pageID/$name";
|
||||
$targetExists = file_exists($targetFile);
|
||||
$sourceExists = $sourceFile ? file_exists($sourceFile) : false;
|
||||
|
||||
if($sourceExists && $targetExists) {
|
||||
// skip because they are likely the same
|
||||
if(filesize($sourceFile) == filesize($targetFile)) continue;
|
||||
} else if($targetExists) {
|
||||
// target already exists so skip it (since we don't have a way to check size)
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!$options['commit']) {
|
||||
$variationsAdded[] = $name;
|
||||
continue;
|
||||
}
|
||||
|
||||
if($sourceExists) {
|
||||
// copy variation from options[filesPath]
|
||||
if($this->wire('files')->copy($sourceFile, $targetFile)) {
|
||||
$variationsAdded[] = $name;
|
||||
} else {
|
||||
$page->warning("Unable to copy file (image variation): $sourceFile");
|
||||
}
|
||||
} else {
|
||||
// download variation via http
|
||||
try {
|
||||
if(is_null($http)) $http = $this->wire(new WireHttp());
|
||||
$http->download($url, $targetFile);
|
||||
$variationsAdded[] = $name;
|
||||
} catch(\Exception $e) {
|
||||
$page->warning("Error downloading file (image variation): $url - " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// determine removed files
|
||||
foreach($pagefiles as $pagefile) {
|
||||
if(isset($data[$pagefile->name])) continue;
|
||||
@@ -927,11 +1096,12 @@ class PagesExportImport extends Wire {
|
||||
if($options['commit']) $pagefiles->remove($pagefile);
|
||||
}
|
||||
|
||||
// summarize all of the above
|
||||
$numAdded = count($filesAdded);
|
||||
$numUpdated = count($filesUpdated);
|
||||
$numRemoved = count($filesRemoved);
|
||||
$numDownloaded = count($filesDownloaded);
|
||||
$numTotal = $numAdded + $numUpdated + $numRemoved; // intentionally excludes numDownloaded
|
||||
$numVariations = count($variationsAdded);
|
||||
$numTotal = $numAdded + $numUpdated + $numRemoved; // intentionally excludes numVariations
|
||||
|
||||
if($numTotal > 0) {
|
||||
$pagefiles->trackChange('value');
|
||||
@@ -956,11 +1126,14 @@ class PagesExportImport extends Wire {
|
||||
);
|
||||
}
|
||||
|
||||
if($numDownloaded) {
|
||||
if($numVariations) {
|
||||
$addedType = $http === null ? 'ZIP copy' : 'HTTP download';
|
||||
$page->trackChange($field->name);
|
||||
$page->message("$field->name (variation): " .
|
||||
sprintf($this->_n('Downloaded %d file', 'Downloaded %d files', $numDownloaded), $numDownloaded) . ": " .
|
||||
implode(', ', $filesDownloaded)
|
||||
sprintf(
|
||||
$this->_n('Added %d file via %s', 'Added %d files via %s', $numVariations),
|
||||
$numVariations, $addedType
|
||||
) . ": " . implode(', ', $variationsAdded)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -45,7 +45,7 @@ class ProcessWire extends Wire {
|
||||
* Reversion revision number
|
||||
*
|
||||
*/
|
||||
const versionRevision = 71;
|
||||
const versionRevision = 72;
|
||||
|
||||
/**
|
||||
* Version suffix string (when applicable)
|
||||
|
@@ -433,6 +433,7 @@ class WireFileTools extends Wire {
|
||||
* Note that if you actually specify a hidden file in your $files argument, then that overrides this.
|
||||
* - `allowEmptyDirs` (boolean): allow empty directories in the ZIP file? (default=true)
|
||||
* - `overwrite` (boolean): Replaces ZIP file if already present (rather than adding to it) (default=false)
|
||||
* - `maxDepth` (int): Max dir depth 0 for no limit (default=0). Specify 1 to stay only in dirs listed in $files.
|
||||
* - `exclude` (array): Files or directories to exclude
|
||||
* - `dir` (string): Directory name to prepend to added files in the ZIP
|
||||
* @return array Returns associative array of:
|
||||
@@ -444,10 +445,13 @@ class WireFileTools extends Wire {
|
||||
*/
|
||||
public function zip($zipfile, $files, array $options = array()) {
|
||||
|
||||
static $depth = 0;
|
||||
|
||||
$defaults = array(
|
||||
'allowHidden' => false,
|
||||
'allowEmptyDirs' => true,
|
||||
'overwrite' => false,
|
||||
'maxDepth' => 0,
|
||||
'exclude' => array(), // files or dirs to exclude
|
||||
'dir' => '',
|
||||
'zip' => null, // internal use: holds ZipArchive instance for recursive use
|
||||
@@ -490,23 +494,35 @@ class WireFileTools extends Wire {
|
||||
if(!$options['allowHidden']) continue;
|
||||
if(is_array($options['allowHidden']) && !in_array($basename, $options['allowHidden'])) continue;
|
||||
}
|
||||
if(count($options['exclude']) && (in_array($name, $options['exclude']) || in_array("$name/", $options['exclude']))) continue;
|
||||
if(count($options['exclude'])) {
|
||||
if(in_array($name, $options['exclude']) || in_array("$name/", $options['exclude'])) continue;
|
||||
}
|
||||
if(is_dir($file)) {
|
||||
if($options['maxDepth'] > 0 && $depth >= $options['maxDepth']) continue;
|
||||
$_files = array();
|
||||
foreach(new \DirectoryIterator($file) as $f) if(!$f->isDot()) $_files[] = $f->getPathname();
|
||||
foreach(new \DirectoryIterator($file) as $f) {
|
||||
if($f->isDot()) continue;
|
||||
if($options['maxDepth'] > 0 && $f->isDir() && ($depth+1) >= $options['maxDepth']) continue;
|
||||
$_files[] = $f->getPathname();
|
||||
}
|
||||
if(count($_files)) {
|
||||
$zip->addEmptyDir($name);
|
||||
$options['dir'] = "$name/";
|
||||
$options['zip'] = $zip;
|
||||
$depth++;
|
||||
$_return = $this->zip($zipfile, $_files, $options);
|
||||
$depth--;
|
||||
foreach($_return['files'] as $s) $return['files'][] = $s;
|
||||
foreach($_return['errors'] as $s) $return['errors'][] = $s;
|
||||
} else if($options['allowEmptyDirs']) {
|
||||
$zip->addEmptyDir($name);
|
||||
}
|
||||
} else if(file_exists($file)) {
|
||||
if($zip->addFile($file, $name)) $return['files'][] = $name;
|
||||
else $return['errors'][] = $name;
|
||||
if($zip->addFile($file, $name)) {
|
||||
$return['files'][] = $name;
|
||||
} else {
|
||||
$return['errors'][] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1586,8 +1586,7 @@ class FieldtypeComments extends FieldtypeMulti {
|
||||
*
|
||||
*/
|
||||
public function ___exportValue(Page $page, Field $field, $value, array $options = array()) {
|
||||
$exportValue = array_values($this->exportComments($value));
|
||||
return $exportValue;
|
||||
return array_values($this->exportComments($value));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1693,5 +1692,20 @@ class FieldtypeComments extends FieldtypeMulti {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get associative array of options (name => default) that Fieldtype supports for importValue
|
||||
*
|
||||
* #pw-internal
|
||||
*
|
||||
* @param Field $field
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
public function getImportValueOptions(Field $field) {
|
||||
$options = parent::getImportValueOptions($field);
|
||||
$options['test'] = true;
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@@ -265,14 +265,17 @@ class FieldtypeFile extends FieldtypeMulti {
|
||||
foreach($value as $k => $v) {
|
||||
/** @var Pagefile $pagefile */
|
||||
$pagefile = $pagefiles->get($v['data']);
|
||||
$a = array('url' => $pagefile->httpUrl());
|
||||
|
||||
$a = array(
|
||||
'url' => $pagefile->httpUrl(),
|
||||
'size' => $pagefile->filesize(),
|
||||
);
|
||||
|
||||
if(!empty($options['system'])) {
|
||||
unset($v['created'], $v['modified']);
|
||||
$exportKey = $v['data'];
|
||||
} else {
|
||||
$a['name'] = $v['data'];
|
||||
$a['filesize'] = $pagefile->filesize();
|
||||
$exportKey = $k;
|
||||
}
|
||||
|
||||
@@ -281,7 +284,7 @@ class FieldtypeFile extends FieldtypeMulti {
|
||||
if($options['FieldtypeFile']['noJSON']) {
|
||||
// export version 2 for exported description uses array value for multi-language, rather than JSON string
|
||||
if(!isset($v['description'])) $v['description'] = '';
|
||||
$v['description'] = $this->exportDescription($v['description'], $options);
|
||||
$v['description'] = $this->exportDescription($v['description']);
|
||||
}
|
||||
|
||||
$exportValue[$exportKey] = array_merge($a, $v);
|
||||
|
@@ -334,6 +334,8 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
|
||||
} else if(is_string($item)) {
|
||||
// system option
|
||||
$path = $item;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
$p = $this->wire('pages')->get($path);
|
||||
if(!$p->id) {
|
||||
@@ -349,6 +351,20 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
|
||||
return $pageArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get associative array of options (name => default) that Fieldtype supports for importValue
|
||||
*
|
||||
* #pw-internal
|
||||
*
|
||||
* @param Field $field
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
public function getImportValueOptions(Field $field) {
|
||||
$options = parent::getImportValueOptions($field);
|
||||
$options['test'] = true;
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the given value for output.
|
||||
|
@@ -17,6 +17,7 @@
|
||||
*
|
||||
* @property int $repeatersRootPageID
|
||||
* @method saveConfigInputfields(Field $field, Template $template, Page $parent)
|
||||
* @method readyPageSaved(Page $readyPage, Page $ownerPage, Field $field) Hook called when ready page is saved
|
||||
*
|
||||
* Page status notes for repeater items:
|
||||
* - Unpublished & Hidden: Ready page, not yet used. Appears in unformatted repeater PageArray but user has not saved it.
|
||||
@@ -614,7 +615,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
||||
$parent_id = null;
|
||||
$field_parent_id = $field->get('parent_id');
|
||||
$template_id = $field->get('template_id');
|
||||
$outputFormatting = $page->outputFormatting();
|
||||
// $outputFormatting = $page->outputFormatting();
|
||||
|
||||
// if it's already in the target format, leave it
|
||||
if($value instanceof PageArray) return $value;
|
||||
@@ -885,6 +886,20 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
||||
return $pageArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get associative array of options (name => default) that Fieldtype supports for importValue
|
||||
*
|
||||
* #pw-internal
|
||||
*
|
||||
* @param Field $field
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
public function getImportValueOptions(Field $field) {
|
||||
$options = parent::getImportValueOptions($field);
|
||||
$options['test'] = true;
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information used by selectors for querying this field
|
||||
@@ -982,7 +997,6 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
||||
return $parent;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the repeater template used by Field, i.e. repeater_name
|
||||
*
|
||||
@@ -1092,6 +1106,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
||||
*/
|
||||
protected function sanitizeValueString(Page $page, Field $field, $value) {
|
||||
|
||||
if($page) {} // ignore
|
||||
$result = false;
|
||||
|
||||
if(ctype_digit("$value")) {
|
||||
@@ -1577,6 +1592,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
||||
*
|
||||
*/
|
||||
protected function ___saveConfigInputfields(Field $field, Template $template, Page $parent) {
|
||||
if($parent) {} // ignore
|
||||
require_once(__DIR__ . '/config.php');
|
||||
$helper = new FieldtypeRepeaterConfigHelper($field);
|
||||
$helper->saveConfigInputfields($template);
|
||||
@@ -1590,6 +1606,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
||||
*
|
||||
*/
|
||||
public function getModuleConfigInputfields(array $data) {
|
||||
if($data) {} // ignore
|
||||
return $this->wire(new InputfieldWrapper());
|
||||
}
|
||||
|
||||
|
@@ -41,15 +41,6 @@ class ProcessPagesExportImport extends Process {
|
||||
*/
|
||||
protected $exportImport;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->exportImport = $this->wire(new PagesExportImport());
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Main execution handler
|
||||
*
|
||||
@@ -63,6 +54,10 @@ class ProcessPagesExportImport extends Process {
|
||||
throw new WirePermissionException($this->_('Export/import is currently only available to superuser'));
|
||||
}
|
||||
|
||||
$this->exportImport = new PagesExportImport();
|
||||
$this->wire($this->exportImport);
|
||||
$this->exportImport->cleanupFiles(600);
|
||||
|
||||
$input = $this->wire('input');
|
||||
$user = $this->wire('user');
|
||||
$breadcrumbLabel = $this->wire('page')->title;
|
||||
@@ -113,6 +108,8 @@ class ProcessPagesExportImport extends Process {
|
||||
|
||||
$form = $modules->get('InputfieldForm');
|
||||
$form->attr('id', 'ProcessPagesExportImport');
|
||||
$form->attr('method', 'post');
|
||||
$form->attr('enctype', 'multipart/form-data');
|
||||
|
||||
if($user->hasPermission('page-edit-export')) {
|
||||
if(!$tab || $tab == 'export') $form->add($this->buildExportTab());
|
||||
@@ -147,19 +144,18 @@ class ProcessPagesExportImport extends Process {
|
||||
$f->description = $this->_('Paste in the JSON string previously exported from this tool.');
|
||||
$tab->add($f);
|
||||
|
||||
/*
|
||||
$f = $modules->get('InputfieldFile');
|
||||
$f->name = 'import_zip';
|
||||
$f->label = $this->_('Import from ZIP file upload');
|
||||
$f->label = $this->_('Import from ZIP file upload') . " (experimental)";
|
||||
$f->extensions = 'zip';
|
||||
$f->icon = 'upload';
|
||||
$f->maxFiles = 1;
|
||||
$f->unzip = 0;
|
||||
$f->overwrite = false;
|
||||
$f->setMaxFilesize('100g');
|
||||
$f->setMaxFilesize('10g');
|
||||
$f->collapsed = Inputfield::collapsedYes;
|
||||
$f->destinationPath = $this->exportImport->getExportPath();
|
||||
$tab->add($f);
|
||||
*/
|
||||
|
||||
$f = $modules->get('InputfieldSubmit');
|
||||
$f->attr('name', 'submit_import');
|
||||
@@ -187,29 +183,41 @@ class ProcessPagesExportImport extends Process {
|
||||
|
||||
$submitCommit = $input->post('submit_commit_import') ? true : false;
|
||||
$submitTest = $input->post('submit_test_import') ? true : false;
|
||||
$submitZIP = !empty($_FILES['import_zip']);
|
||||
$fileField = null;
|
||||
$filesPath = $this->wire('session')->getFor($this, 'filesPath');
|
||||
$jsonFile = '';
|
||||
$a = null;
|
||||
|
||||
if(!empty($_POST['import_zip']) && count($_FILES) && empty($_POST['import_json'])) {
|
||||
if($submitZIP) {
|
||||
// ZIP file import
|
||||
throw new WireException('ZIP import not yet supported');
|
||||
/*
|
||||
$tempDir = new WireTempDir();
|
||||
$this->wire($tempDir);
|
||||
$tempDir->create($this);
|
||||
$fileField = $importTab->getChildByName('import_zip');
|
||||
$fileField->destinationPath = $tempDir->get();
|
||||
$importTab->processInput($input->post);
|
||||
foreach($fileField->value as $pagefile) { }
|
||||
$this->warning($fileField->value);
|
||||
*/
|
||||
$fileField = $importTab->getChildByName('import_zip');
|
||||
$zipFile = $this->exportImport->getExportPath() . $fileField->value->first()->name;
|
||||
if(!$zipFile || !is_file($zipFile)) throw new WireException('No ZIP file found: ' . $zipFile);
|
||||
$unzipPath = $this->exportImport->getExportPath('import-zip');
|
||||
$zipFileItems = $this->wire('files')->unzip($zipFile, $unzipPath);
|
||||
unlink($zipFile);
|
||||
if(empty($zipFileItems)) throw new WireException("No files found in ZIP");
|
||||
$jsonFile = $unzipPath . "pages.json";
|
||||
$this->wire('session')->setFor($this, 'filesPath', $unzipPath);
|
||||
|
||||
} else {
|
||||
} else if(!empty($_POST['import_json'])) {
|
||||
// JSON import
|
||||
$importTab->processInput($input->post);
|
||||
$json = $importTab->getChildByName('import_json')->val();
|
||||
if(empty($json)) throw new WireException($this->_('No import data found'));
|
||||
$a = json_decode($json, true);
|
||||
$this->wire('session')->setFor($this, 'filesPath', '');
|
||||
|
||||
} else if($filesPath) {
|
||||
// ZIP import commit or test
|
||||
$jsonFile = $filesPath . "pages.json";
|
||||
}
|
||||
|
||||
if($jsonFile) {
|
||||
if(!is_file($jsonFile)) throw new WireException("No pages.json found in ZIP file");
|
||||
$a = json_decode(file_get_contents($jsonFile), true);
|
||||
}
|
||||
|
||||
if(!is_array($a)) throw new WireException($this->_('Invalid import data'));
|
||||
@@ -298,6 +306,7 @@ class ProcessPagesExportImport extends Process {
|
||||
'changeName' => in_array('name', $fieldNames),
|
||||
'changeStatus' => in_array('status', $fieldNames),
|
||||
'changeSort' => in_array('sort', $fieldNames),
|
||||
'filesPath' => $this->wire('session')->getFor($this, 'filesPath'),
|
||||
);
|
||||
|
||||
foreach($a['pages'] as $key => $item) {
|
||||
@@ -598,7 +607,6 @@ class ProcessPagesExportImport extends Process {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Import item to a Page and return it
|
||||
*
|
||||
@@ -979,7 +987,7 @@ class ProcessPagesExportImport extends Process {
|
||||
$f->showIf = $showIf;
|
||||
$f->icon = 'download';
|
||||
$f->addActionValue('json', $this->_('JSON for copy/paste (default)'), 'scissors');
|
||||
$f->addActionValue('zip', $this->_('ZIP file download (coming soon)'), 'download');
|
||||
$f->addActionValue('zip', $this->_('ZIP file download') . ' (experimental)', 'download');
|
||||
$tab->add($f);
|
||||
|
||||
return $tab;
|
||||
@@ -1015,7 +1023,6 @@ class ProcessPagesExportImport extends Process {
|
||||
$exportType = $tab->getChildByName('export_type')->val();
|
||||
$exportFields = $tab->getChildByName('export_fields')->val();
|
||||
$exportTo = $input->post('submit_export') === 'zip' ? 'zip' : 'json';
|
||||
if($exportTo == 'zip') throw new WireException('ZIP export not yet available');
|
||||
|
||||
// @todo security and access control
|
||||
// @todo paginate large sets
|
||||
@@ -1079,7 +1086,8 @@ class ProcessPagesExportImport extends Process {
|
||||
$f->val($json);
|
||||
$form->add($f);
|
||||
return $form->render() . "<p><a href='./'>" . $this->_('Run another export') . "</a></p>";
|
||||
} else {
|
||||
|
||||
} else if($exportTo == 'zip') {
|
||||
// zip file download
|
||||
$zipFile = $exporter->exportZIP($exportPages, $exportOptions);
|
||||
if($zipFile) {
|
||||
@@ -1093,6 +1101,8 @@ class ProcessPagesExportImport extends Process {
|
||||
throw new WireException('Export failed during ZIP file generation');
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user