1<?php 2 3namespace dokuwiki\plugin\struct\meta; 4use dokuwiki\plugin\struct\types\Page; 5 6class CSVPageImporter extends CSVImporter { 7 8 protected $importedPids = array(); 9 10 /** @var bool[] */ 11 protected $createPage = []; 12 13 /** 14 * Chceck if schema is page schema 15 * 16 * @throws StructException 17 * @param string $table 18 * @param string $file 19 */ 20// public function __construct($table, $file) { 21// parent::__construct($table, $file); 22// 23// if($this->schema->isLookup()) throw new StructException($table.' is not a page schema'); 24// } 25 26 /** 27 * Import page schema only when the pid header is present. 28 */ 29 protected function readHeaders() { 30 31 //add pid to struct 32 $pageType = new Page(null, 'pid'); 33 $this->columns[] = new Column(0, $pageType, 0, true, $this->schema->getTable()); 34 35 parent::readHeaders(); 36 37 if(!in_array('pid', $this->header)) throw new StructException('There is no "pid" header in the CSV. Schema not imported.'); 38 } 39 40 /** 41 * Creates the insert string for the single value table 42 * 43 * @return string 44 */ 45 protected function getSQLforAllValues() { 46 $colnames = array(); 47 foreach($this->columns as $i => $col) { 48 $colnames[] = 'col' . $col->getColref(); 49 } 50 //replace first column with pid 51 $colnames[0] = 'pid'; 52 //insert rev at the end 53 $colnames[] = 'rev'; 54 55 $placeholds = join(', ', array_fill(0, count($colnames), '?')); 56 $colnames = join(', ', $colnames); 57 $table = $this->schema->getTable(); 58 59 return "INSERT INTO data_$table ($colnames, latest) VALUES ($placeholds, 1)"; 60 } 61 62 /** 63 * Add the revision. 64 * 65 * @param string[] $values 66 * @param $line 67 * @param string $single 68 * @param string $multi 69 */ 70 protected function saveLine($values, $line, $single, $multi) { 71 //create new page revision 72 $pid = cleanID($values[0]); 73 if ($this->createPage[$pid]) { 74 $this->createPage($pid, $line); 75 } 76 $helper = plugin_load('helper', 'struct'); 77 $revision = $helper->createPageRevision($pid, 'CSV data imported'); 78 p_get_metadata($pid); // reparse the metadata of the page top update the titles/rev/lasteditor table 79 80 // make sure this schema is assigned 81 /** @noinspection PhpUndefinedVariableInspection */ 82 Assignments::getInstance()->assignPageSchema( 83 $pid, 84 $this->schema->getTable() 85 ); 86 87 //add page revision to values 88 $values[] = $revision; 89 90 parent::saveLine($values, $line, $single, $multi); 91 } 92 93 /** 94 * Create a page from a namespace template and replace column-label-placeholders 95 * 96 * This is intended to use the same placeholders as bureaucracy in their most basic version 97 * (i.e. without default values, formatting, etc. ) 98 * 99 * @param string $pid 100 * @param array $line 101 */ 102 protected function createPage($pid, $line) 103 { 104 $text = pageTemplate($pid); 105 if (trim($text) === '') { 106 $pageParts = explode(':', $pid); 107 $pagename = end($pageParts); 108 $text = "====== $pagename ======\n"; 109 } 110 $keys = array_reduce($this->columns, 111 function ($keys, Column $col) { 112 if (!in_array($col->getLabel(), $keys, true)) { 113 return $keys; 114 } 115 $index = array_search($col->getLabel(), $keys, true); 116 $keys[$index] = $col->getFullQualifiedLabel(); 117 return $keys; 118 }, 119 $this->header 120 ); 121 122 $keysAt = array_map(function ($key) { return "@@$key@@";}, $keys); 123 $keysHash = array_map(function ($key) { return "##$key##";}, $keys); 124 $flatValues = array_map( 125 function($value) { 126 if (is_array($value)) { 127 return implode(', ', $value); 128 } 129 return $value; 130 }, $line); 131 $text = $this->evaluateIfNotEmptyTags($text, $keys, $flatValues); 132 $text = str_replace($keysAt, $flatValues, $text); 133 /** @noinspection CascadeStringReplacementInspection */ 134 $text = str_replace($keysHash, $flatValues, $text); 135 saveWikiText($pid, $text, 'Created by struct csv import'); 136 } 137 138 /** 139 * Replace conditional <ifnotempty fieldname></ifnotempty> tags 140 * 141 * @param string $text The template 142 * @param string[] $keys The array of qualified headers 143 * @param string[] $values The flat array of corresponding values 144 * 145 * @return string The template with the tags replaced 146 */ 147 protected function evaluateIfNotEmptyTags($text, $keys, $values) 148 { 149 return preg_replace_callback( 150 '/<ifnotempty (.+?)>([^<]*?)<\/ifnotempty>/', 151 function ($matches) use ($keys, $values) 152 { 153 list (,$blockKey, $textIfNotEmpty) = $matches; 154 $index = array_search($blockKey, $keys, true); 155 if ($index === false) { 156 msg('Import error: Key "' . hsc($blockKey) . '" not found!', -1); 157 return ''; 158 } 159 if (trim($values[$index]) === '') { 160 return ''; 161 } 162 return $textIfNotEmpty; 163 }, 164 $text 165 ); 166 } 167 168 /** 169 * In the paga schemas primary key is a touple of (pid, rev) 170 * 171 * @param string[] $values 172 * @param string $single 173 * @return array(pid, rev) 174 */ 175 protected function insertIntoSingle($values, $single) { 176 $pid = $values[0]; 177 $rev = $values[count($values) - 1]; 178 179 //update latest 180 $table = $this->schema->getTable(); 181 $this->sqlite->query("UPDATE data_$table SET latest = 0 WHERE latest = 1 AND pid = ?", array($pid)); 182 183 //insert into table 184 parent::insertIntoSingle($values, $single); 185 186 //primary key is touple of (pid, rev) 187 return array($pid, $rev); 188 } 189 190 /** 191 * Add pid and rev to insert query parameters 192 * 193 * @param string $multi 194 * @param string $pk 195 * @param string $column 196 * @param string $row 197 * @param string $value 198 */ 199 protected function insertIntoMulti($multi, $pk, $column, $row, $value) { 200 list($pid, $rev) = $pk; 201 202 //update latest 203 $table = $this->schema->getTable(); 204 $this->sqlite->query("UPDATE multi_$table SET latest = 0 WHERE latest = 1 AND pid = ?", array($pid)); 205 206 $this->sqlite->query($multi, array($pid, $rev, $column->getColref(), $row + 1, $value)); 207 } 208 209 /** 210 * In page schemas we use REPLACE instead of INSERT to prevent ambiguity 211 * 212 * @return string 213 */ 214 protected function getSQLforMultiValue() { 215 $table = $this->schema->getTable(); 216 /** @noinspection SqlResolve */ 217 return "INSERT INTO multi_$table (pid, rev, colref, row, value, latest) VALUES (?,?,?,?,?,1)"; 218 } 219 220 /** 221 * Check if page id realy exists 222 * 223 * @param Column $col 224 * @param mixed $rawvalue 225 * @return bool 226 */ 227 protected function validateValue(Column $col, &$rawvalue) { 228 //check if page id exists and schema is bounded to the page 229 if($col->getLabel() == 'pid') { 230 $pid = cleanID($rawvalue); 231 if (isset($this->importedPids[$pid])) { 232 $this->errors[] = 'Page "'.$pid.'" already imported. Skipping the row.'; 233 return false; 234 } 235 if(page_exists($pid)) { 236 $this->importedPids[$pid] = true; 237 return true; 238 } 239 global $INPUT; 240 if ($INPUT->bool('createPage')) { 241 $this->createPage[$pid] = true; 242 return true; 243 } 244 $this->errors[] = 'Page "'.$pid.'" does not exists. Skipping the row.'; 245 return false; 246 } 247 248 return parent::validateValue($col, $rawvalue); 249 } 250} 251