mirror of
https://github.com/cosmocode/dokuwiki-plugin-struct.git
synced 2025-07-29 11:59:24 +00:00
Merge branch 'master' into addSchemaClearButton
This commit is contained in:
109
_test/Bureaucracy.test.php
Normal file
109
_test/Bureaucracy.test.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\plugin\struct\test;
|
||||
|
||||
use dokuwiki\plugin\struct\meta;
|
||||
use dokuwiki\plugin\struct\meta\AccessTable;
|
||||
|
||||
/**
|
||||
* Tests for the integration with Bureaucracy plugin
|
||||
*
|
||||
* @group plugin_struct
|
||||
* @group plugins
|
||||
*
|
||||
*/
|
||||
class Bureaucracy_struct_test extends StructTest {
|
||||
|
||||
/** @var array alway enable the needed plugins */
|
||||
protected $pluginsEnabled = array('struct', 'sqlite', 'bureaucracy');
|
||||
|
||||
/** @var array of lookup data */
|
||||
protected $lookup = array();
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->loadSchemaJSON('bureaucracy_lookup', '', 0, true);
|
||||
$this->loadSchemaJSON('bureaucracy');
|
||||
|
||||
//insert some data to lookup
|
||||
for($i = 1; $i <= 10; ++$i) {
|
||||
$data = array(
|
||||
'lookup_first' => 'value first ' . $i,
|
||||
'lookup_second' => 'value second ' . $i
|
||||
);
|
||||
|
||||
$lookupData = AccessTable::byTableName('bureaucracy_lookup', 0);
|
||||
$lookupData->saveData($data);
|
||||
$this->lookup[] = $lookupData;
|
||||
}
|
||||
}
|
||||
|
||||
public function test_bureaucracy_lookup_replacement_empty() {
|
||||
//page created by bureaucracy
|
||||
$id = 'bureaucracy_lookup_replacement_empty';
|
||||
//id of template page
|
||||
$template_id = 'template';
|
||||
|
||||
//create template
|
||||
saveWikiText($template_id, 'Value:@@bureaucracy.lookup_select@@', 'summary');
|
||||
|
||||
//build form
|
||||
$fields = array();
|
||||
|
||||
$lookup_field = plugin_load('helper', 'struct_field');
|
||||
$lookup_field->opt['label'] = 'bureaucracy.lookup_select';
|
||||
//empty value
|
||||
$lookup_field->opt['value'] = '';
|
||||
//left pagename undefined
|
||||
//$lookup_field->opt['pagename'];
|
||||
|
||||
//$args are ommited in struct_field
|
||||
$lookup_field->initialize(array());
|
||||
$fields[] = $lookup_field;
|
||||
|
||||
//helper_plugin_bureaucracy_actiontemplate
|
||||
$actiontemplate = plugin_load('helper', 'bureaucracy_actiontemplate');
|
||||
$actiontemplate->run($fields, '', array($template_id, $id, '_'));
|
||||
|
||||
$page_content = io_readWikiPage(wikiFN($id), $id);
|
||||
|
||||
$this->assertEquals('Value:', $page_content);
|
||||
}
|
||||
|
||||
public function test_bureaucracy_lookup_replacement() {
|
||||
//page created by bureaucracy
|
||||
$id = 'bureaucracy_lookup_replacement';
|
||||
//id of template page
|
||||
$template_id = 'template';
|
||||
//pid of selected value
|
||||
$lookup_pid = $this->lookup[0]->getPid();
|
||||
//selected value
|
||||
$lookup_value = $this->lookup[0]->getData()['lookup_first']->getValue();
|
||||
|
||||
//create template
|
||||
saveWikiText($template_id, 'Value:@@bureaucracy.lookup_select@@', 'summary');
|
||||
|
||||
//build form
|
||||
$fields = array();
|
||||
|
||||
$lookup_field = plugin_load('helper', 'struct_field');
|
||||
$lookup_field->opt['label'] = 'bureaucracy.lookup_select';
|
||||
$lookup_field->opt['value'] = $lookup_pid;
|
||||
//left pagename undefined
|
||||
//$lookup_field->opt['pagename'];
|
||||
|
||||
//$args are ommited in struct_field
|
||||
$lookup_field->initialize(array());
|
||||
$fields[] = $lookup_field;
|
||||
|
||||
//helper_plugin_bureaucracy_actiontemplate
|
||||
|
||||
$actiontemplate = plugin_load('helper', 'bureaucracy_actiontemplate');
|
||||
$actiontemplate->run($fields, '', array($template_id, $id, '_'));
|
||||
|
||||
$page_content = io_readWikiPage(wikiFN($id), $id);
|
||||
|
||||
$this->assertEquals('Value:' . $lookup_value, $page_content);
|
||||
}
|
||||
}
|
@ -203,4 +203,54 @@ class SearchConfigParameter_struct_test extends StructTest {
|
||||
$this->assertArrayNotHasKey('sort', $conf);
|
||||
$this->assertArrayNotHasKey(meta\SearchConfigParameters::$PARAM_SORT, $param);
|
||||
}
|
||||
|
||||
public function test_pagination() {
|
||||
global $INPUT;
|
||||
|
||||
$data = array(
|
||||
'schemas' => array(
|
||||
array('schema2', 'alias2'),
|
||||
),
|
||||
'cols' => array(
|
||||
'afirst'
|
||||
),
|
||||
'rownumbers' => '1',
|
||||
'limit' => '5',
|
||||
);
|
||||
|
||||
$R = new \Doku_Renderer_xhtml();
|
||||
// init with offset
|
||||
$INPUT->set(meta\SearchConfigParameters::$PARAM_OFFSET, 5);
|
||||
//$params[meta\SearchConfigParameters::$PARAM_OFFSET] = 25;
|
||||
$searchConfig = new meta\SearchConfig($data);
|
||||
$aggregationTable = new meta\AggregationTable('test_pagination', 'xhtml', $R, $searchConfig);
|
||||
$aggregationTable->render();
|
||||
$expect = '<div class="structaggregation"><div class="table"><table class="inline">
|
||||
<thead>
|
||||
<tr class="row0">
|
||||
<th class="col0">#</th><th data-field="schema2.afirst"><a href="/./doku.php?id=test_pagination&ofs=5&srt=schema2.afirst" class="" title="Sort by this column">afirst</a></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="row1" data-pid="page14"><td class="col0">6</td><td class="col1">page14 first data</td>
|
||||
</tr>
|
||||
<tr class="row2" data-pid="page15"><td class="col0">7</td><td class="col1">page15 first data</td>
|
||||
</tr>
|
||||
<tr class="row3" data-pid="page16"><td class="col0">8</td><td class="col1">page16 first data</td>
|
||||
</tr>
|
||||
<tr class="row4" data-pid="page17"><td class="col0">9</td><td class="col1">page17 first data</td>
|
||||
</tr>
|
||||
<tr class="row5" data-pid="page18"><td class="col0">10</td><td class="col1">page18 first data</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr class="row6">
|
||||
<th class="col0" colspan="2"><a href="/./doku.php?id=test_pagination" class="prev">Previous page</a><a href="/./doku.php?id=test_pagination&ofs=10" class="next">Next page</a></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table></div>
|
||||
</div>';
|
||||
|
||||
$this->assertEquals($expect, $R->doc);
|
||||
}
|
||||
}
|
||||
|
36
_test/json/bureaucracy.struct.json
Normal file
36
_test/json/bureaucracy.struct.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"structversion": "2017-07-11",
|
||||
"schema": "bureaucracy",
|
||||
"id": "2",
|
||||
"user": "",
|
||||
"config": {
|
||||
"allowed editors": "",
|
||||
"label": {
|
||||
"en": ""
|
||||
}
|
||||
},
|
||||
"columns": [
|
||||
{
|
||||
"colref": 1,
|
||||
"ismulti": false,
|
||||
"isenabled": true,
|
||||
"sort": 10,
|
||||
"label": "lookup_select",
|
||||
"class": "Lookup",
|
||||
"config": {
|
||||
"visibility": {
|
||||
"inpage": true,
|
||||
"ineditor": true
|
||||
},
|
||||
"schema": "bureaucracy_lookup",
|
||||
"field": "lookup_first",
|
||||
"label": {
|
||||
"en": ""
|
||||
},
|
||||
"hint": {
|
||||
"en": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
58
_test/json/bureaucracy_lookup.struct.json
Normal file
58
_test/json/bureaucracy_lookup.struct.json
Normal file
@ -0,0 +1,58 @@
|
||||
{
|
||||
"structversion": "2017-07-11",
|
||||
"schema": "bureaucracy_lookup",
|
||||
"id": "1",
|
||||
"user": "",
|
||||
"config": {
|
||||
"allowed editors": "",
|
||||
"label": {
|
||||
"en": ""
|
||||
}
|
||||
},
|
||||
"columns": [
|
||||
{
|
||||
"colref": 1,
|
||||
"ismulti": false,
|
||||
"isenabled": true,
|
||||
"sort": 10,
|
||||
"label": "lookup_first",
|
||||
"class": "Text",
|
||||
"config": {
|
||||
"visibility": {
|
||||
"inpage": true,
|
||||
"ineditor": true
|
||||
},
|
||||
"prefix": "",
|
||||
"postfix": "",
|
||||
"label": {
|
||||
"en": ""
|
||||
},
|
||||
"hint": {
|
||||
"en": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"colref": 2,
|
||||
"ismulti": false,
|
||||
"isenabled": true,
|
||||
"sort": 20,
|
||||
"label": "lookup_second",
|
||||
"class": "Text",
|
||||
"config": {
|
||||
"visibility": {
|
||||
"inpage": true,
|
||||
"ineditor": true
|
||||
},
|
||||
"prefix": "",
|
||||
"postfix": "",
|
||||
"label": {
|
||||
"en": ""
|
||||
},
|
||||
"hint": {
|
||||
"en": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -12,6 +12,7 @@ if(!defined('DOKU_INC')) die();
|
||||
use dokuwiki\plugin\struct\meta\AccessTable;
|
||||
use dokuwiki\plugin\struct\meta\Assignments;
|
||||
use dokuwiki\plugin\struct\meta\Schema;
|
||||
use dokuwiki\plugin\struct\meta\Search;
|
||||
|
||||
/**
|
||||
* Handles bureaucracy additions
|
||||
@ -32,6 +33,7 @@ class action_plugin_struct_bureaucracy extends DokuWiki_Action_Plugin {
|
||||
* @return void
|
||||
*/
|
||||
public function register(Doku_Event_Handler $controller) {
|
||||
$controller->register_hook('PLUGIN_BUREAUCRACY_TEMPLATE_SAVE', 'BEFORE', $this, 'handle_lookup_fields');
|
||||
$controller->register_hook('PLUGIN_BUREAUCRACY_TEMPLATE_SAVE', 'AFTER', $this, 'handle_save');
|
||||
$controller->register_hook('PLUGIN_BUREAUCRACY_FIELD_UNKNOWN', 'BEFORE', $this, 'handle_schema');
|
||||
}
|
||||
@ -72,6 +74,47 @@ class action_plugin_struct_bureaucracy extends DokuWiki_Action_Plugin {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace lookup fields placeholder's values
|
||||
*
|
||||
* @param Doku_Event $event event object by reference
|
||||
* @param mixed $param [the parameters passed as fifth argument to register_hook() when this
|
||||
* handler was registered]
|
||||
* @return bool
|
||||
*/
|
||||
public function handle_lookup_fields(Doku_Event $event, $param) {
|
||||
foreach($event->data['fields'] as $field) {
|
||||
if(!is_a($field, 'helper_plugin_struct_field')) continue;
|
||||
if($field->column->getType()->getClass() != 'Lookup') continue;
|
||||
|
||||
$pid = $field->getParam('value');
|
||||
$config = $field->column->getType()->getConfig();
|
||||
|
||||
// find proper value
|
||||
// current Search implementation doesn't allow doing it using SQL
|
||||
$search = new Search();
|
||||
$search->addSchema($config['schema']);
|
||||
$search->addColumn($config['field']);
|
||||
$result = $search->execute();
|
||||
$pids = $search->getPids();
|
||||
$len = count($result);
|
||||
|
||||
$value = '';
|
||||
for($i = 0; $i < $len; $i++) {
|
||||
if ($pids[$i] == $pid) {
|
||||
$value = $result[$i][0]->getDisplayValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//replace previous value
|
||||
if ($value) {
|
||||
$event->data['values'][$field->column->getFullQualifiedLabel()] = $value;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the struct data
|
||||
*
|
||||
@ -112,7 +155,8 @@ class action_plugin_struct_bureaucracy extends DokuWiki_Action_Plugin {
|
||||
);
|
||||
|
||||
// trigger meta data rendering to set page title
|
||||
p_get_metadata($id);
|
||||
// expire the cache in order to correctly render the struct header on the first page visit
|
||||
p_get_metadata($id, array('cache' => 'expire'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,8 @@
|
||||
|
||||
use dokuwiki\Form\Form;
|
||||
use dokuwiki\plugin\struct\meta\CSVExporter;
|
||||
use dokuwiki\plugin\struct\meta\CSVImporter;
|
||||
use dokuwiki\plugin\struct\meta\CSVLookupImporter;
|
||||
use dokuwiki\plugin\struct\meta\CSVPageImporter;
|
||||
use dokuwiki\plugin\struct\meta\Schema;
|
||||
use dokuwiki\plugin\struct\meta\SchemaBuilder;
|
||||
use dokuwiki\plugin\struct\meta\SchemaEditor;
|
||||
@ -80,7 +81,13 @@ class admin_plugin_struct_schemas extends DokuWiki_Admin_Plugin {
|
||||
if($table && $INPUT->bool('importcsv')) {
|
||||
if(isset($_FILES['csvfile']['tmp_name'])) {
|
||||
try {
|
||||
new CSVImporter($table, $_FILES['csvfile']['tmp_name']);
|
||||
if ($INPUT->bool('lookup')) {
|
||||
$csvImporter = new CSVLookupImporter($table, $_FILES['csvfile']['tmp_name']);
|
||||
} else {
|
||||
$csvImporter = new CSVPageImporter($table, $_FILES['csvfile']['tmp_name']);
|
||||
}
|
||||
$csvImporter->import();
|
||||
|
||||
msg($this->getLang('admin_csvdone'), 1);
|
||||
} catch(StructException $e) {
|
||||
msg(hsc($e->getMessage()), -1);
|
||||
@ -198,13 +205,11 @@ class admin_plugin_struct_schemas extends DokuWiki_Admin_Plugin {
|
||||
$form->addButton('exportcsv', $this->getLang('btn_export'));
|
||||
$form->addFieldsetClose();
|
||||
|
||||
if($schema->isLookup()) {
|
||||
$form->addFieldsetOpen($this->getLang('admin_csvimport'));
|
||||
$form->addElement(new \dokuwiki\Form\InputElement('file', 'csvfile'));
|
||||
$form->addButton('importcsv', $this->getLang('btn_import'));
|
||||
$form->addHTML('<p><a href="https://www.dokuwiki.org/plugin:struct:csvimport">' . $this->getLang('admin_csvhelp') . '</a></p>');
|
||||
$form->addFieldsetClose();
|
||||
}
|
||||
$form->addFieldsetOpen($this->getLang('admin_csvimport'));
|
||||
$form->addElement(new \dokuwiki\Form\InputElement('file', 'csvfile'));
|
||||
$form->addButton('importcsv', $this->getLang('btn_import'));
|
||||
$form->addHTML('<p><a href="https://www.dokuwiki.org/plugin:struct:csvimport">' . $this->getLang('admin_csvhelp') . '</a></p>');
|
||||
$form->addFieldsetClose();
|
||||
|
||||
return $form->toHTML();
|
||||
}
|
||||
|
2
all.less
2
all.less
@ -1,4 +1,4 @@
|
||||
@media only screen and (max-width: @ini_tablet_width) {
|
||||
@media only screen and (max-width: 800px) {
|
||||
#plugin__struct_output {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
@ -4,16 +4,19 @@
|
||||
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
* @author Malte Lembeck <malte.lembeck@outlook.de>
|
||||
*/
|
||||
$lang['menu'] = 'Struct Schema Editor';
|
||||
$lang['menu_assignments'] = 'Struct Schema Zuweisungen';
|
||||
$lang['headline'] = 'Strukturierte Daten';
|
||||
$lang['edithl'] = 'Schema <i>%s</i> bearbeiten';
|
||||
$lang['create'] = 'Neues Schema anlegen';
|
||||
$lang['schemaname'] = 'Schema-Name:';
|
||||
$lang['save'] = 'Speichern';
|
||||
$lang['createhint'] = 'Achtung: Schemas können später nicht umbenannt werden';
|
||||
$lang['pagelabel'] = 'Seite';
|
||||
$lang['rowlabel'] = 'Reihe #';
|
||||
$lang['revisionlabel'] = 'Zuletzt geändert';
|
||||
$lang['userlabel'] = 'letzter Bearbeitender';
|
||||
$lang['summary'] = 'Struct-Daten geändert';
|
||||
$lang['export'] = 'Schema als JSON exportieren';
|
||||
$lang['btn_export'] = 'Exportieren';
|
||||
@ -38,3 +41,14 @@ $lang['admin_csvexport'] = 'Exportieren von Rohdaten in einer CSV-Datei';
|
||||
$lang['admin_csvimport'] = 'Importieren von Rohdaten aus einer CSV-Datei';
|
||||
$lang['admin_csvdone'] = 'CSV-Datei importiert';
|
||||
$lang['admin_csvhelp'] = 'Bitte konsultieren Sie das Handbuch zum CSV-Import (engl.) für Formatierungsdetails.';
|
||||
|
||||
$lang['btn_delete'] = 'Löschen';
|
||||
$lang['js']['confirmAssignmentsDelete'] = 'Wollen Sie wirklich die Zuweisung von Schma "{0}" zu Seite/Namensraum "{1}" löschen?';
|
||||
$lang['tab_export'] = 'Importieren/Exportieren';
|
||||
$lang['editor_sort'] = 'Sortieren';
|
||||
$lang['editor_conf'] = 'Konfiguration';
|
||||
$lang['editor_type'] = 'Eingeben';
|
||||
$lang['editor_enabled'] = 'Aktiviert';
|
||||
$lang['assign_add'] = 'Hinzufügen';
|
||||
$lang['assign_del'] = 'Löschen';
|
||||
$lang['multidropdown'] = 'Halte STRG oder CMD um mehrere Werte auszuwählen.';
|
||||
|
@ -131,8 +131,8 @@ class AggregationCloud {
|
||||
if (is_array($tagValue)) {
|
||||
$tagValue = $tagValue[0];
|
||||
}
|
||||
$key = $value->getColumn()->getFullQualifiedLabel() . '*~';
|
||||
$filter = SearchConfigParameters::$PARAM_FILTER . "[$key]=" . urlencode($tagValue);
|
||||
$key = $value->getColumn()->getFullQualifiedLabel() . '=';
|
||||
$filter = SearchConfigParameters::$PARAM_FILTER . '[' . urlencode($key) . ']=' . urlencode($tagValue);
|
||||
|
||||
$this->renderer->listitem_open(1);
|
||||
$this->renderer->listcontent_open();
|
||||
|
@ -360,7 +360,8 @@ class AggregationTable {
|
||||
// row number column
|
||||
if($this->data['rownumbers']) {
|
||||
$this->renderer->tablecell_open();
|
||||
$this->renderer->cdata($rownum + 1);
|
||||
$searchConfigConf = $this->searchConfig->getConf();
|
||||
$this->renderer->cdata($rownum + $searchConfigConf['offset'] + 1);
|
||||
$this->renderer->tablecell_close();
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ namespace dokuwiki\plugin\struct\meta;
|
||||
*
|
||||
* @package dokuwiki\plugin\struct\meta
|
||||
*/
|
||||
class CSVImporter {
|
||||
abstract class CSVImporter {
|
||||
|
||||
/** @var Schema */
|
||||
protected $schema;
|
||||
@ -26,6 +26,12 @@ class CSVImporter {
|
||||
/** @var int current line number */
|
||||
protected $line = 0;
|
||||
|
||||
/** @var list of headers */
|
||||
protected $header;
|
||||
|
||||
/** @var array list of validation errors */
|
||||
protected $errors;
|
||||
|
||||
/**
|
||||
* CSVImporter constructor.
|
||||
*
|
||||
@ -40,12 +46,17 @@ class CSVImporter {
|
||||
$this->schema = new Schema($table);
|
||||
if(!$this->schema->getId()) throw new StructException('Schema does not exist');
|
||||
|
||||
if(!$this->schema->isLookup()) throw new StructException('CSV import is only valid for Lookup Schemas');
|
||||
|
||||
/** @var \helper_plugin_struct_db $db */
|
||||
$db = plugin_load('helper', 'struct_db');
|
||||
$this->sqlite = $db->getDB(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import the data from file.
|
||||
*
|
||||
* @throws StructException
|
||||
*/
|
||||
public function import() {
|
||||
// Do the import
|
||||
$this->readHeaders();
|
||||
$this->importCSV();
|
||||
@ -53,6 +64,8 @@ class CSVImporter {
|
||||
|
||||
/**
|
||||
* Read the CSV headers and match it with the Schema columns
|
||||
*
|
||||
* @return array headers of file
|
||||
*/
|
||||
protected function readHeaders() {
|
||||
$header = fgetcsv($this->fh);
|
||||
@ -69,6 +82,8 @@ class CSVImporter {
|
||||
if(!$this->columns) {
|
||||
throw new StructException('None of the CSV headers matched any of the schema\'s fields');
|
||||
}
|
||||
|
||||
$this->header = $header;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -109,12 +124,108 @@ class CSVImporter {
|
||||
$single = $this->getSQLforAllValues();
|
||||
$multi = $this->getSQLforMultiValue();
|
||||
|
||||
$this->sqlite->query('BEGIN TRANSACTION');
|
||||
while(($data = fgetcsv($this->fh)) !== false) {
|
||||
$this->sqlite->query('BEGIN TRANSACTION');
|
||||
$this->line++;
|
||||
$this->importLine($data, $single, $multi);
|
||||
$this->sqlite->query('COMMIT TRANSACTION');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The errors that occured during validation
|
||||
*
|
||||
* @return string[] already translated error messages
|
||||
*/
|
||||
public function getErrors() {
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a single value
|
||||
*
|
||||
* @param Column $col the column of that value
|
||||
* @param mixed &$rawvalue the value, will be fixed according to the type
|
||||
* @return bool true if the data validates, otherwise false
|
||||
*/
|
||||
protected function validateValue(Column $col, &$rawvalue) {
|
||||
//by default no validation
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and validate CSV parsed line
|
||||
*
|
||||
* @param &$line
|
||||
*/
|
||||
protected function readLine(&$line) {
|
||||
// prepare values for single value table
|
||||
$values = array();
|
||||
foreach($this->columns as $i => $column) {
|
||||
if(!isset($line[$i])) throw new StructException('Missing field at CSV line %d', $this->line);
|
||||
|
||||
if(!$this->validateValue($column, $line[$i])) return false;
|
||||
|
||||
if($column->isMulti()) {
|
||||
// multi values get split on comma
|
||||
$line[$i] = array_map('trim', explode(',', $line[$i]));
|
||||
$values[] = $line[$i][0];
|
||||
} else {
|
||||
$values[] = $line[$i];
|
||||
}
|
||||
}
|
||||
//if no ok don't import
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* INSERT $values into data_* table
|
||||
*
|
||||
* @param string[] $values
|
||||
* @param string $single SQL for single table
|
||||
*
|
||||
* @return string last_insert_rowid()
|
||||
*/
|
||||
protected function insertIntoSingle($values, $single) {
|
||||
$this->sqlite->query($single, $values);
|
||||
$res = $this->sqlite->query('SELECT last_insert_rowid()');
|
||||
$pid = $this->sqlite->res2single($res);
|
||||
$this->sqlite->res_close($res);
|
||||
|
||||
return $pid;
|
||||
}
|
||||
|
||||
/**
|
||||
* INSERT one row into multi_* table
|
||||
*
|
||||
* @param string $multi SQL for multi table
|
||||
* @param $pid string
|
||||
* @param $column string
|
||||
* @param $row string
|
||||
* @param $value string
|
||||
*/
|
||||
protected function insertIntoMulti($multi, $pid, $column, $row, $value) {
|
||||
$this->sqlite->query($multi, array($pid, $column->getColref(), $row + 1, $value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save one CSV line into database
|
||||
*
|
||||
* @param string[] $values parsed line values
|
||||
* @param string $single SQL for single table
|
||||
* @param string $multi SQL for multi table
|
||||
*/
|
||||
protected function saveLine($values, $line, $single, $multi) {
|
||||
// insert into single value table (and create pid)
|
||||
$pid = $this->insertIntoSingle($values, $single);
|
||||
|
||||
// insert all the multi values
|
||||
foreach($this->columns as $i => $column) {
|
||||
if(!$column->isMulti()) continue;
|
||||
foreach($line[$i] as $row => $value) {
|
||||
$this->insertIntoMulti($multi, $pid, $column, $row, $value);
|
||||
}
|
||||
}
|
||||
$this->sqlite->query('COMMIT TRANSACTION');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -125,33 +236,13 @@ class CSVImporter {
|
||||
* @param string $multi SQL for multi table
|
||||
*/
|
||||
protected function importLine($line, $single, $multi) {
|
||||
// prepare values for single value table
|
||||
$values = array();
|
||||
foreach($this->columns as $i => $column) {
|
||||
if(!isset($line[$i])) throw new StructException('Missing field at CSV line %d', $this->line);
|
||||
//read values, false if no validation
|
||||
$values = $this->readLine($line);
|
||||
|
||||
if($column->isMulti()) {
|
||||
// multi values get split on comma
|
||||
$line[$i] = array_map('trim', explode(',', $line[$i]));
|
||||
$values[] = $line[$i][0];
|
||||
} else {
|
||||
$values[] = $line[$i];
|
||||
}
|
||||
}
|
||||
|
||||
// insert into single value table (and create pid)
|
||||
$this->sqlite->query($single, $values);
|
||||
$res = $this->sqlite->query('SELECT last_insert_rowid()');
|
||||
$pid = $this->sqlite->res2single($res);
|
||||
$this->sqlite->res_close($res);
|
||||
|
||||
// insert all the multi values
|
||||
foreach($this->columns as $i => $column) {
|
||||
if(!$column->isMulti()) continue;
|
||||
foreach($line[$i] as $row => $value) {
|
||||
$this->sqlite->query($multi, array($pid, $column->getColref(), $row + 1, $value));
|
||||
}
|
||||
if($values) {
|
||||
$this->saveLine($values, $line, $single, $multi);
|
||||
} else foreach($this->errors as $error) {
|
||||
msg($error, -1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
20
meta/CSVLookupImporter.php
Normal file
20
meta/CSVLookupImporter.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\plugin\struct\meta;
|
||||
|
||||
class CSVLookupImporter extends CSVImporter {
|
||||
|
||||
/**
|
||||
* Check if schema is lookup
|
||||
*
|
||||
* @throws StructException
|
||||
* @param string $table
|
||||
* @param string $file
|
||||
*/
|
||||
public function __construct($table, $file) {
|
||||
parent::__construct($table, $file);
|
||||
|
||||
if(!$this->schema->isLookup()) throw new StructException($table.' is not lookup schema');
|
||||
}
|
||||
|
||||
}
|
164
meta/CSVPageImporter.php
Normal file
164
meta/CSVPageImporter.php
Normal file
@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\plugin\struct\meta;
|
||||
use dokuwiki\plugin\struct\types\Page;
|
||||
|
||||
class CSVPageImporter extends CSVImporter {
|
||||
|
||||
protected $importedPids = array();
|
||||
|
||||
/**
|
||||
* Chceck if schema is page schema
|
||||
*
|
||||
* @throws StructException
|
||||
* @param string $table
|
||||
* @param string $file
|
||||
*/
|
||||
public function __construct($table, $file) {
|
||||
parent::__construct($table, $file);
|
||||
|
||||
if($this->schema->isLookup()) throw new StructException($table.' is not a page schema');
|
||||
}
|
||||
|
||||
/**
|
||||
* Import page schema only when the pid header is present.
|
||||
*/
|
||||
protected function readHeaders() {
|
||||
|
||||
//add pid to struct
|
||||
$pageType = new Page(null, 'pid');
|
||||
$this->columns[] = new Column(0, $pageType);
|
||||
|
||||
parent::readHeaders();
|
||||
|
||||
if(!in_array('pid', $this->header)) throw new StructException('There is no "pid" header in the CSV. Schema not imported.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the insert string for the single value table
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getSQLforAllValues() {
|
||||
$colnames = array();
|
||||
foreach($this->columns as $i => $col) {
|
||||
$colnames[] = 'col' . $col->getColref();
|
||||
}
|
||||
//replace first column with pid
|
||||
$colnames[0] = 'pid';
|
||||
//insert rev at the end
|
||||
$colnames[] = 'rev';
|
||||
|
||||
$placeholds = join(', ', array_fill(0, count($colnames), '?'));
|
||||
$colnames = join(', ', $colnames);
|
||||
$table = $this->schema->getTable();
|
||||
|
||||
return "INSERT INTO data_$table ($colnames, latest) VALUES ($placeholds, 1)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the revision.
|
||||
*
|
||||
* @param string[] $values
|
||||
* @param $line
|
||||
* @param string $single
|
||||
* @param string $multi
|
||||
*/
|
||||
protected function saveLine($values, $line, $single, $multi) {
|
||||
//create new page revision
|
||||
$pid = $values[0];
|
||||
$helper = plugin_load('helper', 'struct');
|
||||
$revision = $helper->createPageRevision($pid, 'CSV data imported');
|
||||
p_get_metadata($pid); // reparse the metadata of the page top update the titles/rev/lasteditor table
|
||||
|
||||
// make sure this schema is assigned
|
||||
/** @noinspection PhpUndefinedVariableInspection */
|
||||
Assignments::getInstance()->assignPageSchema(
|
||||
$pid,
|
||||
$this->schema->getTable()
|
||||
);
|
||||
|
||||
//add page revision to values
|
||||
$values[] = $revision;
|
||||
|
||||
parent::saveLine($values, $line, $single, $multi);
|
||||
}
|
||||
|
||||
/**
|
||||
* In the paga schemas primary key is a touple of (pid, rev)
|
||||
*
|
||||
* @param string[] $values
|
||||
* @param string $single
|
||||
* @return array(pid, rev)
|
||||
*/
|
||||
protected function insertIntoSingle($values, $single) {
|
||||
$pid = $values[0];
|
||||
$rev = $values[count($values) - 1];
|
||||
|
||||
//update latest
|
||||
$table = $this->schema->getTable();
|
||||
$this->sqlite->query("UPDATE data_$table SET latest = 0 WHERE latest = 1 AND pid = ?", array($pid));
|
||||
|
||||
//insert into table
|
||||
parent::insertIntoSingle($values, $single);
|
||||
|
||||
//primary key is touple of (pid, rev)
|
||||
return array($pid, $rev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add pid and rev to insert query parameters
|
||||
*
|
||||
* @param string $multi
|
||||
* @param string $pk
|
||||
* @param string $column
|
||||
* @param string $row
|
||||
* @param string $value
|
||||
*/
|
||||
protected function insertIntoMulti($multi, $pk, $column, $row, $value) {
|
||||
list($pid, $rev) = $pk;
|
||||
|
||||
//update latest
|
||||
$table = $this->schema->getTable();
|
||||
$this->sqlite->query("UPDATE multi_$table SET latest = 0 WHERE latest = 1 AND pid = ?", array($pid));
|
||||
|
||||
$this->sqlite->query($multi, array($pid, $rev, $column->getColref(), $row + 1, $value));
|
||||
}
|
||||
|
||||
/**
|
||||
* In page schemas we use REPLACE instead of INSERT to prevent ambiguity
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getSQLforMultiValue() {
|
||||
$table = $this->schema->getTable();
|
||||
/** @noinspection SqlResolve */
|
||||
return "INSERT INTO multi_$table (pid, rev, colref, row, value, latest) VALUES (?,?,?,?,?,1)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if page id realy exists
|
||||
*
|
||||
* @param Column $col
|
||||
* @param mixed $rawvalue
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateValue(Column $col, &$rawvalue) {
|
||||
//check if page id exists and schema is bounded to the page
|
||||
if($col->getLabel() == 'pid') {
|
||||
$pid = cleanID($rawvalue);
|
||||
if (isset($this->importedPids[$pid])) {
|
||||
$this->errors[] = 'Page "'.$pid.'" already imported. Skipping the row.';
|
||||
return false;
|
||||
}
|
||||
if(page_exists($pid)) {
|
||||
$this->importedPids[$pid] = true;
|
||||
return true;
|
||||
}
|
||||
$this->errors[] = 'Page "'.$pid.'" does not exists. Skipping the row.';
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::validateValue($col, $rawvalue);
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
base struct
|
||||
author Andreas Gohr, Michael Große
|
||||
email dokuwiki@cosmocode.de
|
||||
date 2017-06-15
|
||||
date 2017-07-11
|
||||
name struct plugin
|
||||
desc Add and query additional structured page data
|
||||
url https://www.dokuwiki.org/plugin:struct
|
||||
|
42
remote.php
42
remote.php
@ -7,6 +7,8 @@
|
||||
*/
|
||||
|
||||
// must be run within Dokuwiki
|
||||
use dokuwiki\plugin\struct\meta\ConfigParser;
|
||||
use dokuwiki\plugin\struct\meta\SearchConfig;
|
||||
use dokuwiki\plugin\struct\meta\StructException;
|
||||
|
||||
if(!defined('DOKU_INC')) die();
|
||||
@ -116,4 +118,44 @@ class remote_plugin_struct extends DokuWiki_Remote_Plugin {
|
||||
throw new RemoteException($e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data that would be shown in an aggregation
|
||||
*
|
||||
* @param array $schemas array of strings with the schema-names
|
||||
* @param array $cols array of strings with the columns
|
||||
* @param array $filter array of arrays with ['logic'=> 'and'|'or', 'condition' => 'your condition']
|
||||
* @param string $sort string indicating the column to sort by
|
||||
*
|
||||
* @return array array of rows, each row is an array of the column values
|
||||
* @throws RemoteException
|
||||
*/
|
||||
public function getAggregationData(array $schemas, array $cols, array $filter = [], $sort = '') {
|
||||
$schemaLine = 'schema: ' . implode(', ', $schemas);
|
||||
$columnLine = 'cols: ' . implode(', ', $cols);
|
||||
$filterLines = array_map(function ($filter) {
|
||||
return 'filter' . $filter['logic'] . ': ' . $filter['condition'];
|
||||
}, $filter);
|
||||
$sortLine = 'sort: ' . $sort;
|
||||
// schemas, cols, REV?, filter, order
|
||||
|
||||
try {
|
||||
$parser = new ConfigParser(array_merge([$schemaLine, $columnLine, $sortLine], $filterLines));
|
||||
$config = $parser->getConfig();
|
||||
$search = new SearchConfig($config);
|
||||
$results = $search->execute();
|
||||
$data = [];
|
||||
/** @var \dokuwiki\plugin\struct\meta\Value[] $rowValues */
|
||||
foreach ($results as $rowValues) {
|
||||
$row = [];
|
||||
foreach ($rowValues as $value) {
|
||||
$row[$value->getColumn()->getFullQualifiedLabel()] = $value->getDisplayValue();
|
||||
}
|
||||
$data[] = $row;
|
||||
}
|
||||
return $data;
|
||||
} catch (StructException $e) {
|
||||
throw new RemoteException($e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user