2022-03-08 15:55:41 +01:00
< ? php namespace ProcessWire ;
2022-11-05 18:32:48 +01:00
/**
* @ method string buildExport ()
* @ method InputfieldForm buildInputDataForm ()
* @ method string buildImport ()
* @ method void processImport ()
*
*/
2022-03-08 15:55:41 +01:00
class ProcessTemplateExportImport extends Wire {
2022-11-05 18:32:48 +01:00
2024-04-04 14:37:20 +02:00
/**
* @ var Templates | Template [] $items
*
*/
2022-03-08 15:55:41 +01:00
protected $items ;
2024-04-04 14:37:20 +02:00
public function wired () {
parent :: wired ();
$this -> items = $this -> wire () -> templates ;
2022-03-08 15:55:41 +01:00
}
protected function getItem ( $name ) {
return $this -> items -> get ( $name );
}
protected function getNewItem () {
$item = $this -> wire ( new Template ());
return $item ;
}
/**
* Return export data for all given $exportTemplates
*
* @ param array $exportTemplates template names
* @ return array
*
*/
protected function getExportData ( array $exportTemplates ) {
$data = array ();
2024-04-04 14:37:20 +02:00
foreach ( $this -> items as $template ) {
/** @var Template $template */
2022-03-08 15:55:41 +01:00
if ( ! in_array ( $template -> name , $exportTemplates )) continue ;
$a = $template -> getExportData ();
$data [ $template -> name ] = $a ;
}
return $data ;
}
/**
* Execute export
*
2024-04-04 14:37:20 +02:00
* @ return InputfieldForm
2022-03-08 15:55:41 +01:00
*
*/
public function ___buildExport () {
2024-04-04 14:37:20 +02:00
$modules = $this -> wire () -> modules ;
$input = $this -> wire () -> input ;
/** @var InputfieldForm $form */
$form = $modules -> get ( 'InputfieldForm' );
2022-03-08 15:55:41 +01:00
$form -> action = './' ;
$form -> method = 'post' ;
2024-04-04 14:37:20 +02:00
$exportTemplates = $input -> post ( 'export_templates' );
2022-03-08 15:55:41 +01:00
if ( empty ( $exportTemplates )) {
2024-04-04 14:37:20 +02:00
/** @var InputfieldSelectMultiple $f */
$f = $modules -> get ( 'InputfieldSelectMultiple' );
2022-03-08 15:55:41 +01:00
$f -> attr ( 'id+name' , 'export_templates' );
$f -> label = $this -> _ ( 'Select the templates that you want to export' );
$f -> icon = 'copy' ;
$maxName = 0 ;
$maxLabel = 0 ;
$numTemplates = 0 ;
2024-04-04 14:37:20 +02:00
foreach ( $this -> items as $template ) {
/** @var Template $template */
2022-03-08 15:55:41 +01:00
if ( strlen ( $template -> name ) > $maxName ) $maxName = strlen ( $template -> name );
$label = $template -> getLabel ();
if ( strlen ( $label ) > $maxLabel ) $maxLabel = strlen ( $label );
$numTemplates ++ ;
}
$templateName = $this -> _ ( 'NAME' ) . ' ' ;
$templateLabel = $this -> _ ( 'LABEL' ) . ' ' ;
$numFields = $this -> _ ( 'FIELDS' ) . ' ' ;
$modified = $this -> _ ( 'MODIFIED' );
$label = $templateName . ' ' . str_repeat ( '.' , $maxName - strlen ( $templateName ) + 10 ) . ' ' .
$templateLabel . str_repeat ( '.' , $maxLabel - strlen ( $templateLabel ) + 10 ) . ' ' .
str_pad ( $numFields , 13 , '.' ) . ' ' .
$modified ;
$f -> addOption ( 0 , $label , array ( 'disabled' => 'disabled' ));
2024-04-04 14:37:20 +02:00
foreach ( $this -> items as $template ) {
2022-03-08 15:55:41 +01:00
//if(!is_object($template->fieldgroup)) $this->error("Template: $template has no fieldgroup");
$templateName = $template -> name . ' ' ;
$templateLabel = $template -> getLabel () . ' ' ;
if ( $templateLabel == $templateName ) $templateLabel = '' ;
$numFields = count ( $template -> fieldgroup ) . ' ' ;
$modified = $template -> modified ? wireRelativeTimeStr ( $template -> modified ) : '' ;
$label = $templateName . str_repeat ( '.' , $maxName - strlen ( $templateName ) + 10 ) . ' ' .
$templateLabel . str_repeat ( '.' , $maxLabel - strlen ( $templateLabel ) + 10 ) . ' ' .
str_pad ( $numFields , 13 , '.' ) . ' ' .
$modified ;
$f -> addOption ( $template -> name , $label );
}
$f -> notes = $this -> _ ( 'Shift+Click to select multiple in sequence. Ctrl+Click (or Cmd+Click) to select multiple individually. Ctrl+A (or Cmd+A) to select all.' );
$f -> attr ( 'size' , $numTemplates + 1 );
$form -> add ( $f );
2024-04-04 14:37:20 +02:00
/** @var InputfieldSubmit $f */
$f = $modules -> get ( 'InputfieldSubmit' );
2022-03-08 15:55:41 +01:00
$f -> attr ( 'name' , 'submit_export' );
$f -> attr ( 'value' , $this -> _x ( 'Export' , 'button' ));
$form -> add ( $f );
} else {
2024-04-04 14:37:20 +02:00
/** @var InputfieldTextarea $f */
$f = $modules -> get ( 'InputfieldTextarea' );
2022-03-08 15:55:41 +01:00
$f -> attr ( 'id+name' , 'export_data' );
$f -> label = $this -> _ ( 'Export Data' );
$f -> description = $this -> _ ( 'Copy and paste this data into the "Import" box of another installation.' );
$f -> notes = $this -> _ ( 'Click anywhere in the box to select all export data. Once selected, copy the data with CTRL-C or CMD-C.' );
$f -> attr ( 'value' , wireEncodeJSON ( $this -> getExportData ( $exportTemplates ), true , true ));
$form -> add ( $f );
2024-04-04 14:37:20 +02:00
/** @var InputfieldButton $f */
$f = $modules -> get ( 'InputfieldButton' );
2022-03-08 15:55:41 +01:00
$f -> href = './' ;
$f -> value = $this -> _x ( 'Ok' , 'button' );
$form -> add ( $f );
}
return $form ;
}
/**
* Build Textarea input form to past JSON data into
*
* @ return InputfieldForm
*
*/
protected function ___buildInputDataForm () {
2022-11-05 18:32:48 +01:00
$modules = $this -> wire () -> modules ;
/** @var InputfieldForm $form */
$form = $modules -> get ( 'InputfieldForm' );
2022-03-08 15:55:41 +01:00
$form -> action = './' ;
$form -> method = 'post' ;
$form -> attr ( 'id' , 'import_form' );
2022-11-05 18:32:48 +01:00
/** @var InputfieldTextarea $f */
$f = $modules -> get ( 'InputfieldTextarea' );
2022-03-08 15:55:41 +01:00
$f -> attr ( 'name' , 'import_data' );
$f -> label = $this -> _x ( 'Import' , 'input' );
$f -> icon = 'paste' ;
$f -> description = $this -> _ ( 'Paste in the data from an export.' );
$f -> description .= " \n **Experimental/beta feature: database backup recommended for safety.** " ;
$f -> notes = $this -> _ ( 'Copy the export data from another installation and then paste into the box above with CTRL-V or CMD-V.' );
$form -> add ( $f );
2022-11-05 18:32:48 +01:00
/** @var InputfieldSubmit $f */
$f = $modules -> get ( 'InputfieldSubmit' ) ;
2022-03-08 15:55:41 +01:00
$f -> attr ( 'name' , 'submit_import' );
$f -> attr ( 'value' , $this -> _ ( 'Preview' ));
$form -> add ( $f );
return $form ;
}
/**
* Execute import
*
* @ return string
* @ throws WireException if given invalid import data
*
*/
public function ___buildImport () {
2024-04-04 14:37:20 +02:00
$modules = $this -> wire () -> modules ;
$session = $this -> wire () -> session ;
$notices = $this -> wire () -> notices ;
$input = $this -> wire () -> input ;
$sanitizer = $this -> wire () -> sanitizer ;
2022-03-08 15:55:41 +01:00
2024-04-04 14:37:20 +02:00
if ( $input -> post ( 'submit_commit' )) {
2022-11-05 18:32:48 +01:00
$this -> processImport ();
return '' ;
}
2022-03-08 15:55:41 +01:00
2024-04-04 14:37:20 +02:00
$verify = ( int ) $input -> get ( 'verify' );
2022-03-08 15:55:41 +01:00
if ( $verify ) {
2024-04-04 14:37:20 +02:00
$json = $session -> get ( $this , 'importData' );
2022-03-08 15:55:41 +01:00
} else {
2024-04-04 14:37:20 +02:00
$json = $input -> post ( 'import_data' );
2022-03-08 15:55:41 +01:00
}
if ( ! $json ) return $this -> buildInputDataForm ();
$data = is_array ( $json ) ? $json : wireDecodeJSON ( $json );
if ( ! $data ) throw new WireException ( " Invalid import data " );
2022-11-05 18:32:48 +01:00
/** @var InputfieldForm $form */
2024-04-04 14:37:20 +02:00
$form = $modules -> get ( 'InputfieldForm' );
2022-03-08 15:55:41 +01:00
$form -> action = './' ;
$form -> method = 'post' ;
$form -> attr ( 'id' , 'import_form' );
/*
$importDataHash1 = md5 ( print_r ( $data , true ));
$importDataHash2 = $this -> input -> post ( 'importDataHash' );
if ( $importDataHash2 && $importDataHash1 != $importDataHash2 ) {
throw new WireException ( " Data hashes did not match, aborting " );
}
$f = $this -> modules -> get ( 'InputfieldHidden' );
$f -> attr ( 'name' , 'importDataHash' );
$f -> attr ( 'value' , $importDataHash1 );
$form -> add ( $f );
*/
$numChangesTotal = 0 ;
$numErrors = 0 ;
$numExistingTemplates = 0 ;
$numNewTemplates = 0 ;
if ( ! $verify ) $notices -> removeAll ();
// iterate through data for each template
foreach ( $data as $name => $templateData ) {
$postName = str_replace ( '-' , '__' , $name );
unset ( $templateData [ 'id' ]);
$new = false ;
2024-04-04 14:37:20 +02:00
$name = $sanitizer -> name ( $name );
$template = $this -> items -> get ( $name );
2022-03-08 15:55:41 +01:00
$numChangesTemplate = 0 ;
2022-11-05 18:32:48 +01:00
/** @var InputfieldFieldset $fieldset */
2024-04-04 14:37:20 +02:00
$fieldset = $modules -> get ( 'InputfieldFieldset' );
2022-03-08 15:55:41 +01:00
$fieldset -> label = $name ;
$form -> add ( $fieldset );
if ( ! $template ) {
$new = true ;
2024-04-04 14:37:20 +02:00
/** @var Template $template */
2022-03-08 15:55:41 +01:00
$template = $this -> wire ( new Template ());
$template -> name = $name ;
$fieldset -> icon = 'sun-o' ;
$fieldset -> label .= " [ " . $this -> _ ( 'new' ) . " ] " ;
} else {
$fieldset -> icon = 'moon-o' ;
}
2022-11-05 18:32:48 +01:00
/** @var InputfieldMarkup $markup */
2024-04-04 14:37:20 +02:00
$markup = $modules -> get ( 'InputfieldMarkup' );
2022-03-08 15:55:41 +01:00
$markup -> addClass ( 'InputfieldCheckboxes' );
$markup -> value = " " ;
$fieldset -> add ( $markup );
$savedTemplateData = $template -> getExportData ();
try {
$changes = $template -> setImportData ( $templateData );
$template -> setImportData ( $savedTemplateData ); // restore
} catch ( \Exception $e ) {
2022-11-05 18:32:48 +01:00
$changes = array ();
2022-03-08 15:55:41 +01:00
$this -> error ( $e -> getMessage ());
}
2024-04-04 14:37:20 +02:00
/** @var InputfieldCheckboxes $f */
$f = $modules -> get ( 'InputfieldCheckboxes' );
2022-03-08 15:55:41 +01:00
$f -> attr ( 'name' , " item_ $postName " );
$f -> label = $this -> _ ( 'Changes' );
$f -> table = true ;
$f -> thead = $this -> _ ( 'Property' ) . '|' ;
if ( ! $new ) $f -> thead .= $this -> _ ( 'Old Value' ) . '|' ;
$f -> thead .= $this -> _ ( 'New Value' );
foreach ( $changes as $property => $info ) {
2024-04-04 14:37:20 +02:00
if ( $property === '_lazy' || $property === '_exportMode' ) continue ;
2022-03-08 15:55:41 +01:00
$oldValue = str_replace ( '|' , ' ' , $info [ 'old' ]);
$newValue = str_replace ( '|' , ' ' , $info [ 'new' ]);
$numChangesTemplate ++ ;
$numChangesTotal ++ ;
if ( $info [ 'error' ]) {
$errors = is_array ( $info [ 'error' ]) ? $info [ 'error' ] : array ( $info [ 'error' ]);
foreach ( $errors as $error ) $this -> error ( " $name . $property : $error " );
$attr = array ();
} else {
$attr = array ( 'checked' => 'checked' );
}
if ( $new ) $optionValue = " $property | $newValue " ;
else $optionValue = " $property | $oldValue | $newValue " ;
$f -> addOption ( $property , $optionValue , $attr );
}
$errors = array ();
foreach ( $notices as $notice ) {
if ( ! $notice instanceof NoticeError ) continue ;
2024-04-04 14:37:20 +02:00
$errors [] = $sanitizer -> entities1 ( $notice -> text );
2022-03-08 15:55:41 +01:00
$notices -> remove ( $notice );
}
if ( count ( $errors )) {
2024-04-04 14:37:20 +02:00
$icon = wireIconMarkup ( 'exclamation-triangle' );
2022-03-08 15:55:41 +01:00
$markup -> value .= " <ul class='ui-state-error-text'><li> $icon " . implode ( " </li><li> $icon " , $errors ) . '</li></ul>' ;
$fieldset -> label .= ' (' . sprintf ( $this -> _n ( '%d notice' , '%d notices' , count ( $errors )), count ( $errors )) . ')' ;
$numErrors ++ ;
}
//if(!$verify) $notices->removeAll();
if ( $numChangesTemplate ) {
2024-04-04 14:37:20 +02:00
$fieldset -> description = sprintf (
$this -> _n ( 'Found %d property to apply.' , 'Found %d properties to apply.' , $numChangesTemplate ),
$numChangesTemplate
);
if ( $new ) {
$numNewTemplates ++ ;
} else {
$numExistingTemplates ++ ;
}
2022-03-08 15:55:41 +01:00
} else {
$fieldset -> description = $this -> _ ( 'No changes pending.' );
}
if ( count ( $errors ) || ! $numChangesTemplate ) {
$no = ' checked' ;
$yes = '' ;
} else {
$yes = ' checked' ;
$no = '' ;
}
$importLabel = $this -> _ ( 'Modify this template?' );
if ( $new ) $importLabel = $this -> _ ( 'Create this template?' );
$markup -> value .=
" <p class='import_toggle'> $importLabel " .
" <label><input $yes type='radio' name='import_item_ $postName ' value='1' /> " . $this -> _x ( 'Yes' , 'yes-import' ) . " </label> " .
" <label><input $no type='radio' name='import_item_ $postName ' value='0' /> " . $this -> _x ( 'No' , 'no-import' ) . " </label> " .
( $no && $numChangesTemplate ? " <span class='detail'>( " . $this -> _ ( 'click yes to show changes' ) . " )</span> " : " " ) .
" </p> " ;
$markup -> value .= $f -> render ();
// $data[$name] = $templateData;
$this -> errors ( 'clear all' );
}
if ( $numChangesTotal ) {
if ( $verify ) {
$form -> description = $this -> _ ( 'Sometimes it may take two commits before all changes are applied. Please review any pending changes below and commit them as needed.' );
} else {
$form -> description = $this -> _ ( 'Please review the changes below and commit them when ready. If there are any changes that you do not want applied, uncheck the boxes where appropriate.' );
}
/** @var InputfieldSubmit $f */
2024-04-04 14:37:20 +02:00
$f = $modules -> get ( 'InputfieldSubmit' );
2022-03-08 15:55:41 +01:00
$f -> attr ( 'name' , 'submit_commit' );
$f -> attr ( 'value' , $this -> _ ( 'Commit Changes' ));
$f -> showInHeader ();
$form -> add ( $f );
} else {
if ( $verify ) {
$form -> description = $this -> _ ( 'Your changes have been applied!' );
} else {
$form -> description = $this -> _ ( 'No changes were found' );
}
2022-11-05 18:32:48 +01:00
/** @var InputfieldButton $f */
2024-04-04 14:37:20 +02:00
$f = $modules -> get ( 'InputfieldButton' );
2022-03-08 15:55:41 +01:00
$f -> href = './' ;
$f -> value = $this -> _x ( 'Ok' , 'button' );
$form -> add ( $f );
}
2024-04-04 14:37:20 +02:00
$session -> set ( $this , 'importData' , $data );
if ( $numErrors ) {
$this -> error ( sprintf (
$this -> _n ( 'Notices in %d template' , 'Notices in %d templates' , $numErrors ),
$numErrors
));
}
if ( $numNewTemplates ) {
$this -> message ( sprintf (
$this -> _n ( 'Found %d new template to add' , 'Found %d new templates to add' , $numNewTemplates ),
$numNewTemplates
));
}
if ( $numExistingTemplates ) {
$this -> message ( sprintf (
$this -> _n ( 'Found %d existing template to update' , 'Found %d existing templates to update' , $numExistingTemplates ),
$numExistingTemplates
));
}
2022-03-08 15:55:41 +01:00
return $form ;
}
/**
* Commit changed field data
*
*/
protected function ___processImport () {
2024-04-04 14:37:20 +02:00
$sanitizer = $this -> wire () -> sanitizer ;
$session = $this -> wire () -> session ;
$input = $this -> wire () -> input ;
2022-03-08 15:55:41 +01:00
2024-04-04 14:37:20 +02:00
$data = $session -> get ( $this , 'importData' );
2022-03-08 15:55:41 +01:00
if ( ! $data ) throw new WireException ( " Invalid import data " );
$numChangedItems = 0 ;
$numAddedItems = 0 ;
$skipNames = array ();
// iterate through data for each field
foreach ( $data as $name => $itemData ) {
2024-04-04 14:37:20 +02:00
$name = $sanitizer -> name ( $name );
2022-03-08 15:55:41 +01:00
$postName = str_replace ( '-' , '__' , $name );
2024-04-04 14:37:20 +02:00
if ( ! $input -> post ( " import_item_ $postName " )) {
2022-03-08 15:55:41 +01:00
$skipNames [] = $name ;
unset ( $data [ $name ]);
continue ;
}
$item = $this -> getItem ( $name );
if ( ! $item ) {
$new = true ;
$item = $this -> getNewItem ();
$item -> name = $name ;
} else {
$new = false ;
}
unset ( $itemData [ 'id' ]);
foreach ( $itemData as $property => $value ) {
2024-04-04 14:37:20 +02:00
if ( ! in_array ( $property , $input -> post ( " item_ $postName " ))) {
2022-03-08 15:55:41 +01:00
unset ( $itemData [ $property ]);
}
}
try {
$changes = $item -> setImportData ( $itemData );
2024-04-04 14:37:20 +02:00
foreach ( $changes as $key => $info ) {
$this -> message ( $this -> _ ( 'Saved:' ) . " $name . $key => $info[new] " );
}
2022-03-08 15:55:41 +01:00
$this -> saveItem ( $item , $changes );
if ( $new ) {
$numAddedItems ++ ;
$this -> message ( $this -> _ ( 'Added:' ) . " $name " );
} else {
$numChangedItems ++ ;
$this -> message ( $this -> _ ( 'Modified:' ) . " $name " );
}
} catch ( \Exception $e ) {
$this -> error ( $e -> getMessage ());
}
$data [ $name ] = $itemData ;
}
2024-04-04 14:37:20 +02:00
$session -> set ( $this , 'skipNames' , $skipNames );
$session -> set ( $this , 'importData' , $data );
2022-03-08 15:55:41 +01:00
$numSkippedItems = count ( $skipNames );
if ( $numAddedItems ) $this -> message ( sprintf ( $this -> _n ( 'Added %d item' , 'Added %d items' , $numAddedItems ), $numAddedItems ));
if ( $numChangedItems ) $this -> message ( sprintf ( $this -> _n ( 'Modified %d item' , 'Modified %d items' , $numChangedItems ), $numChangedItems ));
if ( $numSkippedItems ) $this -> message ( sprintf ( $this -> _n ( 'Skipped %d item' , 'Skipped %d items' , $numSkippedItems ), $numSkippedItems ));
2024-04-04 14:37:20 +02:00
$session -> redirect ( " ./?verify=1 " );
2022-03-08 15:55:41 +01:00
}
2022-11-05 18:32:48 +01:00
/**
* @ param Template $item
* @ param array $changes
*
*/
2022-03-08 15:55:41 +01:00
public function saveItem ( $item , array $changes ) {
2022-11-05 18:32:48 +01:00
/** @var Fieldgroup $fieldgroup */
2022-03-08 15:55:41 +01:00
$fieldgroup = $item -> fieldgroup ;
2024-04-04 14:37:20 +02:00
if ( ! $fieldgroup ) {
$fieldgroup = new Fieldgroup ();
$fieldgroup -> name = $item -> name ;
}
2022-03-08 15:55:41 +01:00
$fieldgroup -> save ();
$fieldgroup -> saveContext ();
2024-04-04 14:37:20 +02:00
$item -> setFieldgroup ( $fieldgroup );
2022-03-08 15:55:41 +01:00
$item -> save ();
}
2024-04-04 14:37:20 +02:00
2022-03-08 15:55:41 +01:00
}