Skip to content

Instantly share code, notes, and snippets.

@jmartsch
Last active May 21, 2019 19:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jmartsch/14b9e5c592b0ac2bc579b940634b57ae to your computer and use it in GitHub Desktop.
Save jmartsch/14b9e5c592b0ac2bc579b940634b57ae to your computer and use it in GitHub Desktop.
<?php namespace ProcessWire;
/**
* An action that lets you export all fields of pages to a CSV file
*
* Class ExportAllFieldsAsCSV
* @package ProcessWire
*/
class ExportAllFieldsAsCSV extends PageAction implements Module
{
public static function getModuleInfo()
{
return array(
'title' => __('Export all fields as CSV'),
'summary' => __('An action that lets you export all fields of pages to a CSV file.'),
'version' => '1.0.2',
'author' => 'Jens Martsch based on work from ProcessWire and Adrian',
'permission' => 'page-action-export-csv',
'permissions' => array('page-action-export-csv' => __('Export all fields as CSV'))
);
}
/**
* Name of files field added to user profile
*
*/
const userFiles = 'csv_files';
/**
* Column headings styles for exported CSV file
*
*/
const headingBoth = 0;
const headingLabel = 1;
const headingName = 2;
const headingNone = 3;
protected $raumdatenblattmodule;
/**
* File pointer to export file
*
*/
protected $fp = null;
/**
* A unique ID to associate with a file
*
*/
protected $uniqueID = null;
/**
* System field definitions
*
*/
protected $systemFields = array();
/**
* True if dealing with a new file, indicates we need to write headers
*
*/
protected $isNew = false;
/**
* Languages API var, if installed (null otherwise)
*
* @var Languages
*
*/
protected $languages = null;
protected $exportMultipleValuesSeparator = "|";
protected $delimiter = ";";
protected $enclosure = '"';
// protected $fieldDependencies;
protected $exportLanguage;
public function __construct()
{
$this->set('defaultFields', '');
$this->set('fieldDependencies', false);
}
/**
* Initialize CSV export module
*
*/
public function init()
{
$this->systemFields = array(
'id' => $this->_('ID'),
'name' => $this->_('Name (from URL)'),
'path' => $this->_('Path'),
'url' => $this->_('URL'),
'status' => $this->_('Status'),
'created' => $this->_('Date Created'),
'modified' => $this->_('Date Last Modified'),
'createdUser.id' => $this->_('Created by User: ID'),
'createdUser.name' => $this->_('Created by User: Name'),
'modifiedUser.id' => $this->_('Modified by User: ID'),
'modifiedUser.name' => $this->_('Modified by User: Name'),
'parent_id' => $this->_('Parent Page ID'),
'parent.name' => $this->_('Parent Page Name'),
'template.id' => $this->_('Template ID'),
'template' => $this->_('Template Name'),
);
$this->set('format', 'csv');
$this->set('exportLanguage', 0);
$this->set('exportHeadings', self::headingBoth);
$this->languages = $this->wire('languages');
$this->exportLanguage = $this->languages('de_de');
$this->raumdatenblattmodule = $this->modules->get('Raumdatenblatt');
}
/**
* Export a single page to a line in the CSV file
*
* @param Page $item
* @return bool
* @throws WireException
*
*/
public function ___action($item)
{
if (!$this->fp) {
throw new WireException("This action can only be run in multiple mode");
}
$this->message("Exportiere Seite mit der ID $item->id | $item->title");
$rowData = $this->exportPage($item);
if ($this->isNew) {
$exportLabels = $this->exportHeader($rowData);
if (is_array($exportLabels)) {
fputcsv($this->fp, $exportLabels, $this->delimiter, $this->enclosure);
}
$this->isNew = false;
}
fputcsv($this->fp, $rowData, $this->delimiter, $this->enclosure); // output the row data
return true;
}
/**
* Execute/process multiple items (part of the WireAction interface)
*
* @param PageArray $items
* @return int
* @throws WireException
*
*/
public function ___executeMultiple($items)
{
if (!$items instanceof PageArray) {
throw new WireException("PageArray required");
}
$userLanguage = null;
if ($this->languages) {
// ensure exportLanguage is set and that it is a Language object rather than an int
if (!$this->exportLanguage) {
$this->exportLanguage = $this->languages->getDefault();
} else if (!is_object($this->exportLanguage)) {
$this->exportLanguage = $this->languages->get((int) $this->exportLanguage);
}
// convert user to have the selected language temporarily
$user = $this->wire('user');
if ($user->language->id != $this->exportLanguage->id) {
$userLanguage = $user->language;
$user->language = $this->exportLanguage;
}
}
$user = $this->wire('user');
$files = $user->get(self::userFiles);
$basename = $this->wire('user')->name . '-' . $this->getUniqueID() . '.' . $this->format;
$pathname = $files->path() . $basename;
$url = $files->url() . $basename;
$this->isNew = !is_file($pathname);
$this->fp = fopen($pathname, 'a');
if (!$this->fp) {
throw new WireException("Error creating: $pathname");
}
$numItems = count($items);
$this->message("Schreibe $numItems Objekt(e) in <a href='$url'>$basename</a>", Notice::allowMarkup);
$result = parent::___executeMultiple($items);
fclose($this->fp);
$file = $files->get($basename);
if (!$file) {
// add file to user profile
$iftAction = $this->iftAction;
$of = $user->of();
if ($of) {
$user->of(false);
}
$files->add($pathname);
$file = $files->last();
if ($iftAction) {
$file->description = $iftAction->title;
}
$file->description .= $this->_('exported at') . ' ' . date($this->_('Y-m-d H:i'));
$user->save(self::userFiles);
if ($of) {
$user->of(true);
}
}
$this->summary = "Download URL: $file->httpUrl";
$this->message("Die Exportdatei $basename zum Benutzer $user->name hinzugefügt.<br>Sie können die Datei jederzeit in Ihrem Profil wiederfinden. Bitte beachten Sie die Dateien von Zeit zu Zeit zu löschen, da sonst unnötig Speicherplatz verbraucht wird. ");
if ($userLanguage) {
$this->wire('user')->language = $userLanguage;
}
return $result;
}
/**
* Export the header row as an array containing the header labels in order
*
* @param array $data Example of a row with keys being the field names
* @return array
*
*/
protected function ___exportHeader(array $data)
{
$exportHeadings = (int) $this->exportHeadings;
$exportHeadings = 2;
// if it's a new file, the first row will be the field names or labels
if ($exportHeadings === self::headingNone) {
$exportLabels = null;
} else if ($exportHeadings === self::headingName) {
$exportLabels = array_keys($data);
// d($exportLabels);
} else {
$exportLabels = array();
$labels = $this->wire('session')->get($this, 'labels');
if (!is_array($labels)) {
$labels = array();
}
foreach (array_keys($data) as $name) {
$label = isset($labels[$name]) ? $labels[$name] : $name;
if ($exportHeadings === self::headingBoth) {
$label .= " [$name]";
}
$exportLabels[$name] = $label;
}
}
return $exportLabels;
}
/**
* Export a page to an array of all fields with their values
* Support for FieldtypeFieldsetPage and Repeaters is not available
*
* @param Page $page
* @return array
*
*/
protected function ___exportPage(Page $page)
{
// $editURL = 'http://' . wire('config')->httpHost . wire('config')->urls->admin . 'page/edit/?id=';
// $viewURL = 'http://' . wire('config')->httpHost;
$data = array();
$data["id"] = $page->id;
$options = array();
$options['human'] = true;
foreach ($page->fields as $field) {
// return early if fieldtype is not supported
if ($field->type == 'FieldtypeFieldsetOpen' ||
$field->type == 'FieldtypeFieldsetClose' ||
$field->type == 'FieldtypeFieldsetGroup' ||
$field->type == 'FieldtypeFieldsetPage' ||
$field->type == 'FieldtypeFieldsetTabOpen' ||
$field->type == 'FieldtypeFieldsetTabClose') {
continue;
}
// is this needed anymore, as we always export all fields on the page instead of chosen fields?
$subfield = '';
if (strpos($field, '.') !== false) {
list($field, $subfield) = explode('.', $field);
}
$fieldName = $field->name;
$value = $page->getUnformatted($fieldName);
$formattedValue = $page->$fieldName;
// d("$fieldName: $formattedValue");
if ($this->fieldDependencies) {
if ($this->FieldDependencyIsMet($field, $page) === false) {
$data[$fieldName] = "";
return $data;
}
}
if ($field->type instanceof FieldtypeTable) {
$exportValue = "";
foreach ($page->$fieldName as $row) {
foreach ($row as $key => $value) {
if ($value instanceof Page) {
$value = $value->title;
}
$exportValue .= "$key: $value{$this->exportMultipleValuesSeparator}";
}
}
$value = $exportValue;
} else if ($field->type instanceof FieldtypeDatetime) {
// $value = $page->$fieldName;
if ($value) {
$value = date("d/m/Y", $value);
}
// d($value);
} else if ($field->type instanceof FieldtypeCheckbox) {
// $checkboxLabel = $field->get("checkboxLabel{$this->exportLanguage}");
// bd($page->$fieldName === 1, $field);
// $value = $page->$fieldName;
// d($value, $fieldName);
// if ($page->field === 1) {
// $value = $page->$fieldName;
// if ($checkboxLabel !== null) {
// $value = "$checkboxLabel";
// } else {
// $value = $this->_x("yes", "Checkbox value when selected");
// }
// }
} else if ($field->type instanceof FieldtypeOptions) {
// d($field, "Feld");
// d($page->$fieldName, "Wert");
if ($page->$fieldName && $page->$fieldName->count() == "0" && $field->initValue !== null) {
// d("Der Wert für das Feld $fieldName auf Seite $page scheint noch nicht aktualisiert worden zu sein. Bitte wenden Sie sich an den Administrator und nennen Ihm dieses Problem.");
} else {
$value = "";
foreach ($page->$fieldName as $option) {
$value .= $option->title . $this->exportMultipleValuesSeparator;
}
$value = rtrim($value, $this->exportMultipleValuesSeparator);
// d($value, "value");
}
} elseif ($field->type instanceof FieldtypePage) {
$inputfield = $page->getInputfield($fieldName);
if (is_object($value)) {
if ($value instanceof Page) {
$value = $inputfield ? $inputfield->getPageLabel($value) : $value->title;
} else if ($value instanceof PageArray) {
$value = $value->implode($this->exportMultipleValuesSeparator, "{title|name}");
}
} else {
$value = (string) $value;
}
} //FieldtypeMultiplier and FieldtypeFile
elseif ($field && ($field->type == 'FieldtypeMultiplier' || $field->type instanceof FieldtypeFile)) {
if ($field->type instanceof FieldtypeFile) {
$page->of(false);
}
//formatting off required if output format is "Rendered string of text"
if (count($page->$field) > 0) {
$values = array();
foreach ($page->$field as $value) {
$values[] = $value;
}
$value = implode($this->exportMultipleValuesSeparator, $values);
}
$page->of($this->format);
} elseif ($field->type instanceof FieldtypeMulti && count($page->$field) === 0) {
$value = '';
} //All other fields
else {
$value = $page->$field; // like in ProcessChildrenCsvExport
}
$data[$fieldName] = $value;
}
return $data;
}
/**
* Get a unique ID that will be used in the filename
*
* This has to account for the potential of combining multiple batches into one file
*
* @return int
*
*/
protected function getUniqueID()
{
$iftAction = $this->iftAction;
if ($iftAction) {
$uniqueID = $iftAction->rootParentID;
if (!$uniqueID) {
$uniqueID = $iftAction->id;
}
} else {
if ($this->uniqueID) {
return $this->uniqueID;
}
$uniqueID = time();
}
$this->uniqueID = $uniqueID;
return $uniqueID;
}
/* checks if a field dependency is met, and returns true if so or false if not
* @param $field
* @param $page
* @return bool
*/
protected function FieldDependencyIsMet($field, $page)
{
$showIf = "";
if ($field->hasFieldtype == null) {
$inputfield = $field->getInputfield($page);
} else {
$inputfield = $field;
}
$showIf = $inputfield->getSetting('showIf');
if ($showIf !== "") {
$selectors = new Selectors($showIf);
// if ($this->config->debug) bd($showIf, "$field->name has the following dependencies");
if ($selectors->matches($page)) {
// if ($this->config->debug) bd("Field dependency is met. Return true");
return true;
} else {
// if ($this->config->debug) bd($inputfield, "$inputfield->name is being skipped, because its dependency is not met.");
return false;
}
}
return true;
}
/**
* @return InputfieldWrapper
*
*/
public function ___getConfigInputfields()
{
$fieldset = parent::___getConfigInputfields();
$runner = $this->getRunner();
$lister = $runner && $runner->className() == 'ListerProActions' ? $runner->lister : null;
$field = $this->modules->get('InputfieldCheckbox');
$field->label = __('Obey field dependencies');
$field->description = __('If selected, then only fields will be exported whose requirements are met based on another field. Else all fields will be exported.');
$field->name = 'fieldDependencies';
$field->value = 1;
$field->checked = 'checked';
// $fieldset->add($field);
return $fieldset;
}
/**
* Install the CSV export module
*
* Add a new 'Export Files' field and make visible in user profile.
*
*/
public function ___install()
{
// install a special files field to user profile
$field = $this->get(self::userFiles);
if (!$field) {
$field = new Field();
$field->name = self::userFiles;
$field->type = $this->wire('modules')->get('FieldtypeFile');
$field->label = $this->_('CSV Export files');
$field->entityEncode = 1;
$field->noUpload = 1;
$field->extensions = "csv json txt";
$field->save();
$this->message("Added files field: $field->name");
}
$fieldgroup = $this->wire('fieldgroups')->get('user');
if (!$fieldgroup->hasField($field)) {
$fieldgroup->add($field);
$fieldgroup->save();
$this->message("Added files field to fieldgroup: $fieldgroup->name");
}
$data = $this->wire('modules')->getModuleConfigData('ProcessProfile');
$data['profileFields'][] = self::userFiles;
$this->wire('modules')->saveModuleConfigData('ProcessProfile', $data);
$this->message("Made files field editable in user profile");
}
/**
* Uninstall CSV export module
*
* Note that we don't delete the user files field that was added.
*
*/
public function ___uninstall()
{
$name = self::userFiles;
$this->message("Please note that the field $name added by this module has not been deleted. Delete this manually if the files are no longer needed.");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment