Last active
May 21, 2019 19:45
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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