xref: /plugin/struct/meta/CSVPageImporter.php (revision 00043bff319aef224e543da9689b4c5fc83a28b2)
11fc2361fSSzymon Olewniczak<?php
21fc2361fSSzymon Olewniczak
31fc2361fSSzymon Olewniczaknamespace dokuwiki\plugin\struct\meta;
41fc2361fSSzymon Olewniczakuse dokuwiki\plugin\struct\types\Page;
51fc2361fSSzymon Olewniczak
61fc2361fSSzymon Olewniczakclass CSVPageImporter extends CSVImporter {
7e53295c5SSzymon Olewniczak
8e53295c5SSzymon Olewniczak    protected $importedPids = array();
9e53295c5SSzymon Olewniczak
10ee52a3aaSMichael Große    /** @var bool[]  */
1117dbef8aSMichael Große    protected $createPage = [];
1217dbef8aSMichael Große
131fc2361fSSzymon Olewniczak    /**
141fc2361fSSzymon Olewniczak     * Chceck if schema is page schema
151fc2361fSSzymon Olewniczak     *
161fc2361fSSzymon Olewniczak     * @throws StructException
171fc2361fSSzymon Olewniczak     * @param string $table
181fc2361fSSzymon Olewniczak     * @param string $file
191fc2361fSSzymon Olewniczak     */
201fc2361fSSzymon Olewniczak    public function __construct($table, $file) {
211fc2361fSSzymon Olewniczak        parent::__construct($table, $file);
221fc2361fSSzymon Olewniczak
231fc2361fSSzymon Olewniczak        if($this->schema->isLookup()) throw new StructException($table.' is not a page schema');
241fc2361fSSzymon Olewniczak    }
251fc2361fSSzymon Olewniczak
261fc2361fSSzymon Olewniczak    /**
271fc2361fSSzymon Olewniczak     * Import page schema only when the pid header is present.
281fc2361fSSzymon Olewniczak     */
291fc2361fSSzymon Olewniczak    protected function readHeaders() {
301fc2361fSSzymon Olewniczak
311fc2361fSSzymon Olewniczak        //add pid to struct
321fc2361fSSzymon Olewniczak        $pageType = new Page(null, 'pid');
3317dbef8aSMichael Große        $this->columns[] = new Column(0, $pageType, 0, true, $this->schema->getTable());
341fc2361fSSzymon Olewniczak
351fc2361fSSzymon Olewniczak        parent::readHeaders();
361fc2361fSSzymon Olewniczak
371fc2361fSSzymon Olewniczak        if(!in_array('pid', $this->header)) throw new StructException('There is no "pid" header in the CSV. Schema not imported.');
381fc2361fSSzymon Olewniczak    }
391fc2361fSSzymon Olewniczak
401fc2361fSSzymon Olewniczak    /**
411fc2361fSSzymon Olewniczak     * Creates the insert string for the single value table
421fc2361fSSzymon Olewniczak     *
431fc2361fSSzymon Olewniczak     * @return string
441fc2361fSSzymon Olewniczak     */
451fc2361fSSzymon Olewniczak    protected function getSQLforAllValues() {
461fc2361fSSzymon Olewniczak        $colnames = array();
471fc2361fSSzymon Olewniczak        foreach($this->columns as $i => $col) {
481fc2361fSSzymon Olewniczak            $colnames[] = 'col' . $col->getColref();
491fc2361fSSzymon Olewniczak        }
501fc2361fSSzymon Olewniczak        //replace first column with pid
511fc2361fSSzymon Olewniczak        $colnames[0] = 'pid';
521fc2361fSSzymon Olewniczak        //insert rev at the end
531fc2361fSSzymon Olewniczak        $colnames[] = 'rev';
541fc2361fSSzymon Olewniczak
551fc2361fSSzymon Olewniczak        $placeholds = join(', ', array_fill(0, count($colnames), '?'));
561fc2361fSSzymon Olewniczak        $colnames = join(', ', $colnames);
571fc2361fSSzymon Olewniczak        $table = $this->schema->getTable();
581fc2361fSSzymon Olewniczak
59e53295c5SSzymon Olewniczak        return "INSERT INTO data_$table ($colnames, latest) VALUES ($placeholds, 1)";
601fc2361fSSzymon Olewniczak    }
611fc2361fSSzymon Olewniczak
621fc2361fSSzymon Olewniczak    /**
631fc2361fSSzymon Olewniczak     * Add the revision.
641fc2361fSSzymon Olewniczak     *
651fc2361fSSzymon Olewniczak     * @param string[] $values
661fc2361fSSzymon Olewniczak     * @param          $line
671fc2361fSSzymon Olewniczak     * @param string   $single
681fc2361fSSzymon Olewniczak     * @param string   $multi
691fc2361fSSzymon Olewniczak     */
701fc2361fSSzymon Olewniczak    protected function saveLine($values, $line, $single, $multi) {
71e53295c5SSzymon Olewniczak        //create new page revision
7295ea798eSMichael Große        $pid = cleanID($values[0]);
7317dbef8aSMichael Große        if ($this->createPage[$pid]) {
7417dbef8aSMichael Große            $this->createPage($pid, $line);
7517dbef8aSMichael Große        }
76e53295c5SSzymon Olewniczak        $helper = plugin_load('helper', 'struct');
77e53295c5SSzymon Olewniczak        $revision = $helper->createPageRevision($pid, 'CSV data imported');
78e53295c5SSzymon Olewniczak        p_get_metadata($pid); // reparse the metadata of the page top update the titles/rev/lasteditor table
79e53295c5SSzymon Olewniczak
80e53295c5SSzymon Olewniczak        // make sure this schema is assigned
81e53295c5SSzymon Olewniczak        /** @noinspection PhpUndefinedVariableInspection */
82e53295c5SSzymon Olewniczak        Assignments::getInstance()->assignPageSchema(
83e53295c5SSzymon Olewniczak            $pid,
84e53295c5SSzymon Olewniczak            $this->schema->getTable()
85e53295c5SSzymon Olewniczak        );
86e53295c5SSzymon Olewniczak
87e53295c5SSzymon Olewniczak        //add page revision to values
88e53295c5SSzymon Olewniczak        $values[] = $revision;
89e53295c5SSzymon Olewniczak
901fc2361fSSzymon Olewniczak        parent::saveLine($values, $line, $single, $multi);
911fc2361fSSzymon Olewniczak    }
921fc2361fSSzymon Olewniczak
931fc2361fSSzymon Olewniczak    /**
9417dbef8aSMichael Große     * Create a page from a namespace template and replace column-label-placeholders
9517dbef8aSMichael Große     *
9617dbef8aSMichael Große     * This is intended to use the same placeholders as bureaucracy in their most basic version
9717dbef8aSMichael Große     * (i.e. without default values, formatting, etc. )
9817dbef8aSMichael Große     *
9917dbef8aSMichael Große     * @param string $pid
10017dbef8aSMichael Große     * @param array  $line
10117dbef8aSMichael Große     */
10217dbef8aSMichael Große    protected function createPage($pid, $line)
10317dbef8aSMichael Große    {
10417dbef8aSMichael Große        $text = pageTemplate($pid);
10517dbef8aSMichael Große        if (trim($text) === '') {
10617dbef8aSMichael Große            $pageParts = explode(':', $pid);
10717dbef8aSMichael Große            $pagename = end($pageParts);
10817dbef8aSMichael Große            $text = "====== $pagename ======\n";
10917dbef8aSMichael Große        }
110c09fb9e3SMichael Große        $keys = array_reduce($this->columns,
111c09fb9e3SMichael Große            function ($keys, Column $col) {
112c09fb9e3SMichael Große                if (!in_array($col->getLabel(), $keys, true)) {
113c09fb9e3SMichael Große                    return $keys;
114c09fb9e3SMichael Große                }
115c09fb9e3SMichael Große                $index = array_search($col->getLabel(), $keys, true);
116c09fb9e3SMichael Große                $keys[$index] = $col->getFullQualifiedLabel();
117c09fb9e3SMichael Große                return $keys;
118c09fb9e3SMichael Große            },
119c09fb9e3SMichael Große            $this->header
120c09fb9e3SMichael Große        );
121c09fb9e3SMichael Große
12217dbef8aSMichael Große        $keysAt = array_map(function ($key) { return "@@$key@@";}, $keys);
12317dbef8aSMichael Große        $keysHash = array_map(function ($key) { return "##$key##";}, $keys);
12417dbef8aSMichael Große        $flatValues = array_map(
12517dbef8aSMichael Große            function($value) {
12617dbef8aSMichael Große                if (is_array($value)) {
12717dbef8aSMichael Große                    return implode(', ', $value);
12817dbef8aSMichael Große                }
12917dbef8aSMichael Große                return $value;
13017dbef8aSMichael Große            }, $line);
131*00043bffSMichael Große        $text = $this->evaluateIfNotEmptyTags($text, $keys, $flatValues);
13217dbef8aSMichael Große        $text = str_replace($keysAt, $flatValues, $text);
13317dbef8aSMichael Große        /** @noinspection CascadeStringReplacementInspection */
13417dbef8aSMichael Große        $text = str_replace($keysHash, $flatValues, $text);
13517dbef8aSMichael Große        saveWikiText($pid, $text, 'Created by struct csv import');
13617dbef8aSMichael Große    }
13717dbef8aSMichael Große
13817dbef8aSMichael Große    /**
139*00043bffSMichael Große     * Replace conditional <ifnotempty fieldname></ifnotempty> tags
140*00043bffSMichael Große     *
141*00043bffSMichael Große     * @param string   $text   The template
142*00043bffSMichael Große     * @param string[] $keys   The array of qualified headers
143*00043bffSMichael Große     * @param string[] $values The flat array of corresponding values
144*00043bffSMichael Große     *
145*00043bffSMichael Große     * @return string The template with the tags replaced
146*00043bffSMichael Große     */
147*00043bffSMichael Große    protected function evaluateIfNotEmptyTags($text, $keys, $values)
148*00043bffSMichael Große    {
149*00043bffSMichael Große        return preg_replace_callback(
150*00043bffSMichael Große            '/<ifnotempty (.+?)>([^<]*?)<\/ifnotempty>/',
151*00043bffSMichael Große            function ($matches) use ($keys, $values)
152*00043bffSMichael Große            {
153*00043bffSMichael Große                list (,$blockKey, $textIfNotEmpty) = $matches;
154*00043bffSMichael Große                $index = array_search($blockKey, $keys, true);
155*00043bffSMichael Große                if ($index === false) {
156*00043bffSMichael Große                    msg('Import error: Key "' . hsc($blockKey) . '" not found!', -1);
157*00043bffSMichael Große                    return '';
158*00043bffSMichael Große                }
159*00043bffSMichael Große                if (trim($values[$index]) === '') {
160*00043bffSMichael Große                    return '';
161*00043bffSMichael Große                }
162*00043bffSMichael Große                return $textIfNotEmpty;
163*00043bffSMichael Große            },
164*00043bffSMichael Große            $text
165*00043bffSMichael Große        );
166*00043bffSMichael Große    }
167*00043bffSMichael Große
168*00043bffSMichael Große    /**
1691fc2361fSSzymon Olewniczak     * In the paga schemas primary key is a touple of (pid, rev)
1701fc2361fSSzymon Olewniczak     *
1711fc2361fSSzymon Olewniczak     * @param string[] $values
1721fc2361fSSzymon Olewniczak     * @param string   $single
1731fc2361fSSzymon Olewniczak     * @return array(pid, rev)
1741fc2361fSSzymon Olewniczak     */
1751fc2361fSSzymon Olewniczak    protected function insertIntoSingle($values, $single) {
1761fc2361fSSzymon Olewniczak        $pid = $values[0];
1771fc2361fSSzymon Olewniczak        $rev = $values[count($values) - 1];
178e53295c5SSzymon Olewniczak
179e53295c5SSzymon Olewniczak        //update latest
180e53295c5SSzymon Olewniczak        $table = $this->schema->getTable();
181e53295c5SSzymon Olewniczak        $this->sqlite->query("UPDATE data_$table SET latest = 0 WHERE latest = 1 AND pid = ?", array($pid));
182e53295c5SSzymon Olewniczak
183e53295c5SSzymon Olewniczak        //insert into table
184e53295c5SSzymon Olewniczak        parent::insertIntoSingle($values, $single);
185e53295c5SSzymon Olewniczak
1861fc2361fSSzymon Olewniczak        //primary key is touple of (pid, rev)
1871fc2361fSSzymon Olewniczak        return array($pid, $rev);
1881fc2361fSSzymon Olewniczak    }
1891fc2361fSSzymon Olewniczak
1901fc2361fSSzymon Olewniczak    /**
1911fc2361fSSzymon Olewniczak     * Add pid and rev to insert query parameters
1921fc2361fSSzymon Olewniczak     *
1931fc2361fSSzymon Olewniczak     * @param string $multi
1941fc2361fSSzymon Olewniczak     * @param string $pk
1951fc2361fSSzymon Olewniczak     * @param string $column
1961fc2361fSSzymon Olewniczak     * @param string $row
1971fc2361fSSzymon Olewniczak     * @param string $value
1981fc2361fSSzymon Olewniczak     */
1991fc2361fSSzymon Olewniczak    protected function insertIntoMulti($multi, $pk, $column, $row, $value) {
2001fc2361fSSzymon Olewniczak        list($pid, $rev) = $pk;
201e53295c5SSzymon Olewniczak
202e53295c5SSzymon Olewniczak        //update latest
203e53295c5SSzymon Olewniczak        $table = $this->schema->getTable();
204e53295c5SSzymon Olewniczak        $this->sqlite->query("UPDATE multi_$table SET latest = 0 WHERE latest = 1 AND pid = ?", array($pid));
205e53295c5SSzymon Olewniczak
2061fc2361fSSzymon Olewniczak        $this->sqlite->query($multi, array($pid, $rev, $column->getColref(), $row + 1, $value));
2071fc2361fSSzymon Olewniczak    }
2081fc2361fSSzymon Olewniczak
2091fc2361fSSzymon Olewniczak    /**
2101fc2361fSSzymon Olewniczak     * In page schemas we use REPLACE instead of INSERT to prevent ambiguity
2111fc2361fSSzymon Olewniczak     *
2121fc2361fSSzymon Olewniczak     * @return string
2131fc2361fSSzymon Olewniczak     */
2141fc2361fSSzymon Olewniczak    protected function getSQLforMultiValue() {
2151fc2361fSSzymon Olewniczak        $table = $this->schema->getTable();
2161fc2361fSSzymon Olewniczak        /** @noinspection SqlResolve */
217e53295c5SSzymon Olewniczak        return "INSERT INTO multi_$table (pid, rev, colref, row, value, latest) VALUES (?,?,?,?,?,1)";
2181fc2361fSSzymon Olewniczak    }
2191fc2361fSSzymon Olewniczak
2201fc2361fSSzymon Olewniczak    /**
2211fc2361fSSzymon Olewniczak     * Check if page id realy exists
2221fc2361fSSzymon Olewniczak     *
2231fc2361fSSzymon Olewniczak     * @param Column $col
2241fc2361fSSzymon Olewniczak     * @param mixed  $rawvalue
2251fc2361fSSzymon Olewniczak     * @return bool
2261fc2361fSSzymon Olewniczak     */
2271fc2361fSSzymon Olewniczak    protected function validateValue(Column $col, &$rawvalue) {
228e53295c5SSzymon Olewniczak        //check if page id exists and schema is bounded to the page
2291fc2361fSSzymon Olewniczak        if($col->getLabel() == 'pid') {
230e53295c5SSzymon Olewniczak            $pid = cleanID($rawvalue);
231e53295c5SSzymon Olewniczak            if (isset($this->importedPids[$pid])) {
232e53295c5SSzymon Olewniczak                $this->errors[] = 'Page "'.$pid.'" already imported. Skipping the row.';
233e53295c5SSzymon Olewniczak                return false;
234e53295c5SSzymon Olewniczak            }
235e53295c5SSzymon Olewniczak            if(page_exists($pid)) {
236e53295c5SSzymon Olewniczak                $this->importedPids[$pid] = true;
2371fc2361fSSzymon Olewniczak                return true;
2381fc2361fSSzymon Olewniczak            }
23917dbef8aSMichael Große            global $INPUT;
24017dbef8aSMichael Große            if ($INPUT->bool('createPage')) {
24117dbef8aSMichael Große                $this->createPage[$pid] = true;
24217dbef8aSMichael Große                return true;
24317dbef8aSMichael Große            }
244e53295c5SSzymon Olewniczak            $this->errors[] = 'Page "'.$pid.'" does not exists. Skipping the row.';
2451fc2361fSSzymon Olewniczak            return false;
2461fc2361fSSzymon Olewniczak        }
2471fc2361fSSzymon Olewniczak
2481fc2361fSSzymon Olewniczak        return parent::validateValue($col, $rawvalue);
2491fc2361fSSzymon Olewniczak    }
2501fc2361fSSzymon Olewniczak}
251