xref: /plugin/struct/meta/CSVPageImporter.php (revision d6d97f6064c3b0f90310be8341edc9585520ee54)
11fc2361fSSzymon Olewniczak<?php
21fc2361fSSzymon Olewniczak
31fc2361fSSzymon Olewniczaknamespace dokuwiki\plugin\struct\meta;
4*d6d97f60SAnna Dabrowska
51fc2361fSSzymon Olewniczakuse dokuwiki\plugin\struct\types\Page;
61fc2361fSSzymon Olewniczak
7*d6d97f60SAnna Dabrowskaclass CSVPageImporter extends CSVImporter
8*d6d97f60SAnna Dabrowska{
9e53295c5SSzymon Olewniczak
10e53295c5SSzymon Olewniczak    protected $importedPids = array();
11e53295c5SSzymon Olewniczak
12ee52a3aaSMichael Große    /** @var bool[]  */
1317dbef8aSMichael Große    protected $createPage = [];
1417dbef8aSMichael Große
151fc2361fSSzymon Olewniczak    /**
161fc2361fSSzymon Olewniczak     * Import page schema only when the pid header is present.
171fc2361fSSzymon Olewniczak     */
18*d6d97f60SAnna Dabrowska    protected function readHeaders()
19*d6d97f60SAnna Dabrowska    {
201fc2361fSSzymon Olewniczak
211fc2361fSSzymon Olewniczak        //add pid to struct
221fc2361fSSzymon Olewniczak        $pageType = new Page(null, 'pid');
2317dbef8aSMichael Große        $this->columns[] = new Column(0, $pageType, 0, true, $this->schema->getTable());
241fc2361fSSzymon Olewniczak
251fc2361fSSzymon Olewniczak        parent::readHeaders();
261fc2361fSSzymon Olewniczak
271fc2361fSSzymon Olewniczak        if (!in_array('pid', $this->header)) throw new StructException('There is no "pid" header in the CSV. Schema not imported.');
281fc2361fSSzymon Olewniczak    }
291fc2361fSSzymon Olewniczak
301fc2361fSSzymon Olewniczak    /**
311fc2361fSSzymon Olewniczak     * Creates the insert string for the single value table
321fc2361fSSzymon Olewniczak     *
331fc2361fSSzymon Olewniczak     * @return string
341fc2361fSSzymon Olewniczak     */
35*d6d97f60SAnna Dabrowska    protected function getSQLforAllValues()
36*d6d97f60SAnna Dabrowska    {
371fc2361fSSzymon Olewniczak        $colnames = array();
381fc2361fSSzymon Olewniczak        foreach ($this->columns as $i => $col) {
391fc2361fSSzymon Olewniczak            $colnames[] = 'col' . $col->getColref();
401fc2361fSSzymon Olewniczak        }
411fc2361fSSzymon Olewniczak        //replace first column with pid
421fc2361fSSzymon Olewniczak        $colnames[0] = 'pid';
431fc2361fSSzymon Olewniczak        //insert rev at the end
441fc2361fSSzymon Olewniczak        $colnames[] = 'rev';
451fc2361fSSzymon Olewniczak
461fc2361fSSzymon Olewniczak        $placeholds = join(', ', array_fill(0, count($colnames), '?'));
471fc2361fSSzymon Olewniczak        $colnames = join(', ', $colnames);
481fc2361fSSzymon Olewniczak        $table = $this->schema->getTable();
491fc2361fSSzymon Olewniczak
50e53295c5SSzymon Olewniczak        return "INSERT INTO data_$table ($colnames, latest) VALUES ($placeholds, 1)";
511fc2361fSSzymon Olewniczak    }
521fc2361fSSzymon Olewniczak
531fc2361fSSzymon Olewniczak    /**
541fc2361fSSzymon Olewniczak     * Add the revision.
551fc2361fSSzymon Olewniczak     *
561fc2361fSSzymon Olewniczak     * @param string[] $values
571fc2361fSSzymon Olewniczak     * @param          $line
581fc2361fSSzymon Olewniczak     * @param string   $single
591fc2361fSSzymon Olewniczak     * @param string   $multi
601fc2361fSSzymon Olewniczak     */
61*d6d97f60SAnna Dabrowska    protected function saveLine($values, $line, $single, $multi)
62*d6d97f60SAnna Dabrowska    {
63e53295c5SSzymon Olewniczak        //create new page revision
6495ea798eSMichael Große        $pid = cleanID($values[0]);
6517dbef8aSMichael Große        if ($this->createPage[$pid]) {
6617dbef8aSMichael Große            $this->createPage($pid, $line);
6717dbef8aSMichael Große        }
68e53295c5SSzymon Olewniczak        $helper = plugin_load('helper', 'struct');
69e53295c5SSzymon Olewniczak        $revision = $helper->createPageRevision($pid, 'CSV data imported');
70e53295c5SSzymon Olewniczak        p_get_metadata($pid); // reparse the metadata of the page top update the titles/rev/lasteditor table
71e53295c5SSzymon Olewniczak
72e53295c5SSzymon Olewniczak        // make sure this schema is assigned
73e53295c5SSzymon Olewniczak        /** @noinspection PhpUndefinedVariableInspection */
74e53295c5SSzymon Olewniczak        Assignments::getInstance()->assignPageSchema(
75e53295c5SSzymon Olewniczak            $pid,
76e53295c5SSzymon Olewniczak            $this->schema->getTable()
77e53295c5SSzymon Olewniczak        );
78e53295c5SSzymon Olewniczak
79e53295c5SSzymon Olewniczak        //add page revision to values
80e53295c5SSzymon Olewniczak        $values[] = $revision;
81e53295c5SSzymon Olewniczak
821fc2361fSSzymon Olewniczak        parent::saveLine($values, $line, $single, $multi);
831fc2361fSSzymon Olewniczak    }
841fc2361fSSzymon Olewniczak
851fc2361fSSzymon Olewniczak    /**
8617dbef8aSMichael Große     * Create a page from a namespace template and replace column-label-placeholders
8717dbef8aSMichael Große     *
8817dbef8aSMichael Große     * This is intended to use the same placeholders as bureaucracy in their most basic version
8917dbef8aSMichael Große     * (i.e. without default values, formatting, etc. )
9017dbef8aSMichael Große     *
9117dbef8aSMichael Große     * @param string $pid
9217dbef8aSMichael Große     * @param array  $line
9317dbef8aSMichael Große     */
9417dbef8aSMichael Große    protected function createPage($pid, $line)
9517dbef8aSMichael Große    {
9617dbef8aSMichael Große        $text = pageTemplate($pid);
9717dbef8aSMichael Große        if (trim($text) === '') {
9817dbef8aSMichael Große            $pageParts = explode(':', $pid);
9917dbef8aSMichael Große            $pagename = end($pageParts);
10017dbef8aSMichael Große            $text = "====== $pagename ======\n";
10117dbef8aSMichael Große        }
102*d6d97f60SAnna Dabrowska        $keys = array_reduce(
103*d6d97f60SAnna Dabrowska            $this->columns,
104c09fb9e3SMichael Große            function ($keys, Column $col) {
105c09fb9e3SMichael Große                if (!in_array($col->getLabel(), $keys, true)) {
106c09fb9e3SMichael Große                    return $keys;
107c09fb9e3SMichael Große                }
108c09fb9e3SMichael Große                $index = array_search($col->getLabel(), $keys, true);
109c09fb9e3SMichael Große                $keys[$index] = $col->getFullQualifiedLabel();
110c09fb9e3SMichael Große                return $keys;
111c09fb9e3SMichael Große            },
112c09fb9e3SMichael Große            $this->header
113c09fb9e3SMichael Große        );
114c09fb9e3SMichael Große
115*d6d97f60SAnna Dabrowska        $keysAt = array_map(function ($key) {
116*d6d97f60SAnna Dabrowska            return "@@$key@@";
117*d6d97f60SAnna Dabrowska        }, $keys);
118*d6d97f60SAnna Dabrowska        $keysHash = array_map(function ($key) {
119*d6d97f60SAnna Dabrowska            return "##$key##";
120*d6d97f60SAnna Dabrowska        }, $keys);
12117dbef8aSMichael Große        $flatValues = array_map(
12217dbef8aSMichael Große            function ($value) {
12317dbef8aSMichael Große                if (is_array($value)) {
12417dbef8aSMichael Große                    return implode(', ', $value);
12517dbef8aSMichael Große                }
12617dbef8aSMichael Große                return $value;
127*d6d97f60SAnna Dabrowska            },
128*d6d97f60SAnna Dabrowska            $line
129*d6d97f60SAnna Dabrowska        );
13000043bffSMichael Große        $text = $this->evaluateIfNotEmptyTags($text, $keys, $flatValues);
13117dbef8aSMichael Große        $text = str_replace($keysAt, $flatValues, $text);
13217dbef8aSMichael Große        /** @noinspection CascadeStringReplacementInspection */
13317dbef8aSMichael Große        $text = str_replace($keysHash, $flatValues, $text);
13417dbef8aSMichael Große        saveWikiText($pid, $text, 'Created by struct csv import');
13517dbef8aSMichael Große    }
13617dbef8aSMichael Große
13717dbef8aSMichael Große    /**
13800043bffSMichael Große     * Replace conditional <ifnotempty fieldname></ifnotempty> tags
13900043bffSMichael Große     *
14000043bffSMichael Große     * @param string   $text   The template
14100043bffSMichael Große     * @param string[] $keys   The array of qualified headers
14200043bffSMichael Große     * @param string[] $values The flat array of corresponding values
14300043bffSMichael Große     *
14400043bffSMichael Große     * @return string The template with the tags replaced
14500043bffSMichael Große     */
14600043bffSMichael Große    protected function evaluateIfNotEmptyTags($text, $keys, $values)
14700043bffSMichael Große    {
14800043bffSMichael Große        return preg_replace_callback(
14900043bffSMichael Große            '/<ifnotempty (.+?)>([^<]*?)<\/ifnotempty>/',
150*d6d97f60SAnna Dabrowska            function ($matches) use ($keys, $values) {
15100043bffSMichael Große                list (,$blockKey, $textIfNotEmpty) = $matches;
15200043bffSMichael Große                $index = array_search($blockKey, $keys, true);
15300043bffSMichael Große                if ($index === false) {
15400043bffSMichael Große                    msg('Import error: Key "' . hsc($blockKey) . '" not found!', -1);
15500043bffSMichael Große                    return '';
15600043bffSMichael Große                }
15700043bffSMichael Große                if (trim($values[$index]) === '') {
15800043bffSMichael Große                    return '';
15900043bffSMichael Große                }
16000043bffSMichael Große                return $textIfNotEmpty;
16100043bffSMichael Große            },
16200043bffSMichael Große            $text
16300043bffSMichael Große        );
16400043bffSMichael Große    }
16500043bffSMichael Große
16600043bffSMichael Große    /**
1671fc2361fSSzymon Olewniczak     * In the paga schemas primary key is a touple of (pid, rev)
1681fc2361fSSzymon Olewniczak     *
1691fc2361fSSzymon Olewniczak     * @param string[] $values
1701fc2361fSSzymon Olewniczak     * @param string   $single
1711fc2361fSSzymon Olewniczak     * @return array(pid, rev)
1721fc2361fSSzymon Olewniczak     */
173*d6d97f60SAnna Dabrowska    protected function insertIntoSingle($values, $single)
174*d6d97f60SAnna Dabrowska    {
1751fc2361fSSzymon Olewniczak        $pid = $values[0];
1761fc2361fSSzymon Olewniczak        $rev = $values[count($values) - 1];
177e53295c5SSzymon Olewniczak
178e53295c5SSzymon Olewniczak        //update latest
179e53295c5SSzymon Olewniczak        $table = $this->schema->getTable();
180e53295c5SSzymon Olewniczak        $this->sqlite->query("UPDATE data_$table SET latest = 0 WHERE latest = 1 AND pid = ?", array($pid));
181e53295c5SSzymon Olewniczak
182e53295c5SSzymon Olewniczak        //insert into table
183e53295c5SSzymon Olewniczak        parent::insertIntoSingle($values, $single);
184e53295c5SSzymon Olewniczak
1851fc2361fSSzymon Olewniczak        //primary key is touple of (pid, rev)
1861fc2361fSSzymon Olewniczak        return array($pid, $rev);
1871fc2361fSSzymon Olewniczak    }
1881fc2361fSSzymon Olewniczak
1891fc2361fSSzymon Olewniczak    /**
1901fc2361fSSzymon Olewniczak     * Add pid and rev to insert query parameters
1911fc2361fSSzymon Olewniczak     *
1921fc2361fSSzymon Olewniczak     * @param string $multi
1931fc2361fSSzymon Olewniczak     * @param string $pk
1941fc2361fSSzymon Olewniczak     * @param string $column
1951fc2361fSSzymon Olewniczak     * @param string $row
1961fc2361fSSzymon Olewniczak     * @param string $value
1971fc2361fSSzymon Olewniczak     */
198*d6d97f60SAnna Dabrowska    protected function insertIntoMulti($multi, $pk, $column, $row, $value)
199*d6d97f60SAnna Dabrowska    {
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     */
214*d6d97f60SAnna Dabrowska    protected function getSQLforMultiValue()
215*d6d97f60SAnna Dabrowska    {
2161fc2361fSSzymon Olewniczak        $table = $this->schema->getTable();
2171fc2361fSSzymon Olewniczak        /** @noinspection SqlResolve */
218e53295c5SSzymon Olewniczak        return "INSERT INTO multi_$table (pid, rev, colref, row, value, latest) VALUES (?,?,?,?,?,1)";
2191fc2361fSSzymon Olewniczak    }
2201fc2361fSSzymon Olewniczak
2211fc2361fSSzymon Olewniczak    /**
2221fc2361fSSzymon Olewniczak     * Check if page id realy exists
2231fc2361fSSzymon Olewniczak     *
2241fc2361fSSzymon Olewniczak     * @param Column $col
2251fc2361fSSzymon Olewniczak     * @param mixed  $rawvalue
2261fc2361fSSzymon Olewniczak     * @return bool
2271fc2361fSSzymon Olewniczak     */
228*d6d97f60SAnna Dabrowska    protected function validateValue(Column $col, &$rawvalue)
229*d6d97f60SAnna Dabrowska    {
230e53295c5SSzymon Olewniczak        //check if page id exists and schema is bounded to the page
2311fc2361fSSzymon Olewniczak        if ($col->getLabel() == 'pid') {
232e53295c5SSzymon Olewniczak            $pid = cleanID($rawvalue);
233e53295c5SSzymon Olewniczak            if (isset($this->importedPids[$pid])) {
234e53295c5SSzymon Olewniczak                $this->errors[] = 'Page "' . $pid . '" already imported. Skipping the row.';
235e53295c5SSzymon Olewniczak                return false;
236e53295c5SSzymon Olewniczak            }
237e53295c5SSzymon Olewniczak            if (page_exists($pid)) {
238e53295c5SSzymon Olewniczak                $this->importedPids[$pid] = true;
2391fc2361fSSzymon Olewniczak                return true;
2401fc2361fSSzymon Olewniczak            }
24117dbef8aSMichael Große            global $INPUT;
24217dbef8aSMichael Große            if ($INPUT->bool('createPage')) {
24317dbef8aSMichael Große                $this->createPage[$pid] = true;
24417dbef8aSMichael Große                return true;
24517dbef8aSMichael Große            }
246e53295c5SSzymon Olewniczak            $this->errors[] = 'Page "' . $pid . '" does not exists. Skipping the row.';
2471fc2361fSSzymon Olewniczak            return false;
2481fc2361fSSzymon Olewniczak        }
2491fc2361fSSzymon Olewniczak
2501fc2361fSSzymon Olewniczak        return parent::validateValue($col, $rawvalue);
2511fc2361fSSzymon Olewniczak    }
2521fc2361fSSzymon Olewniczak}
253