Table editor integrates CSVImporter and minor code cleaning
[platal.git] / classes / csvimporter.php
CommitLineData
31874f6f 1<?php
212aae73 2/***************************************************************************
3 * Copyright (C) 2003-2006 Polytechnique.org *
4 * http://opensource.polytechnique.org/ *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the Free Software *
18 * Foundation, Inc., *
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
20 ***************************************************************************/
31874f6f 21
22define('CSV_INSERT', 'insert'); // INSERT IGNORE
23define('CSV_REPLACE', 'replace'); // REPLACE
24define('CSV_UPDATE', 'update'); // INSERT and UPDATE on error
25
26class CSVImporter
27{
28 private $table;
29 private $key;
30 private $do_sql;
31
32 private $index;
33 private $separator;
34 private $data = array();
35
36 private $user_functions = array();
a6613b2e 37 private $field_desc = array();
31874f6f 38
39 public function CSVImporter($table, $key = 'id', $do_sql = true)
40 {
41 $this->table = $table;
42 $this->key = $key;
43 $this->do_sql = $do_sql;
44 }
45
46 private function processLine($line)
47 {
48 $array = split($this->separator, $line);
49 if (is_null($this->index)) {
50 $this->index = $array;
51 return true;
52 }
53
54 if (count($array) != count($this->index)) {
55 return false;
56 }
57 $assoc = array();
58 $i = 0;
59 foreach ($this->index as $key) {
60 $assoc[$key] = $array[$i];
61 $i++;
62 }
63 $this->data[] = $assoc;
64 return true;
65 }
66
a6613b2e 67 private function getValue($line, $key, $action)
68 {
69 if (@array_key_exists($action, $line)) {
70 $value = $line[$action];
71 } elseif (is_callable($action, false)) {
72 $value = call_user_func($action, $line, $key);
73 } else {
74 $value = $action;
75 }
76 if (is_null($value) || $value == 'NULL') {
77 $value = 'NULL';
78 }
79 return $value;
80 }
81
31874f6f 82 private function makeAssoc($line, $relation)
83 {
84 $ops = array();
85 foreach ($relation as $key=>$ref) {
a6613b2e 86 $ops[$key] = $this->getValue($line, $key, $ref);
31874f6f 87 }
88 return $ops;
89 }
90
91 private function makeRequestArgs($line, $relation)
92 {
93 $ops = array();
94 foreach ($relation as $key=>$ref) {
a6613b2e 95 $value = $this->getValue($line, $key, $ref);
96 if (!is_null($value) && $value != 'NULL') {
31874f6f 97 $value = "'" . addslashes($value) . "'";
98 }
99 $ops[$key] = "$key = $value";
100 }
101 return $ops;
102 }
103
104 private function makeRelation()
105 {
106 $relation = array();
107 foreach ($this->index as $title) {
108 $relation[$title] = $title;
109 }
110 return $relation;
111 }
112
113 private function execute($query)
114 {
115 if (!$this->do_sql) {
116 echo "$query;\n";
117 return false;
118 }
119 return XDB::execute($query);
120 }
121
122 private function getFieldList()
123 {
124 $res = XDB::query("SHOW COLUMNS FROM {$this->table}");
125 if ($res->numRows()) {
126 return $res->fetchColumn();
127 }
128 return null;
129 }
130
131 public function setCSV($csv, $index = null, $separator = ';')
132 {
133 $this->index = null;
134 $this->separator = $separator;
135 $csv = preg_split("/(\r\n|\r|\n)/", $csv);
136
137 foreach ($csv as $line) {
138 $this->processLine($line);
139 }
140 }
141
142 public function run($action = CSV_UPDATE, $insert_relation = null, $update_relation = null)
143 {
144 if (is_null($insert_relation)) {
145 $insert_relation = $this->makeRelation();
146 }
147 if (is_null($update_relation)) {
148 $update_relation = $insert_relation;
149 }
150 foreach ($this->data as $line) {
151 $set = join(', ', $this->makeRequestArgs($line, $insert_relation));
152 switch ($action) {
153 case CSV_INSERT:
154 $this->execute("INSERT IGNORE INTO {$this->table} SET $set");
155 break;
156 case CSV_REPLACE:
157 $this->execute("REPLACE INTO {$this->table} SET $set");
158 break;
159 case CSV_UPDATE:
160 if (!$this->execute("INSERT INTO {$this->table} SET $set")) {
161 $ops = $this->makeRequestArgs($line, $update_relation);
162 $set = join(', ', $ops);
163 $this->execute("UPDATE {$this->table} SET $set WHERE {$ops[$this->key]}");
164 }
165 break;
166 }
167 }
168 }
169
170 static public function dynamicCond($line, $key)
171 {
172 static $fields, $conds, $values, $thens, $elses;
173
174 if (!isset($fields)) {
175 $fields = Env::v('csv_cond_field');
176 $conds = Env::v('csv_cond');
177 $values = Env::v('csv_cond_value');
178 $thens = Env::v('csv_cond_then');
179 $elses = Env::v('csv_cond_else');
180 }
181 $field = $line[$fields[$key]];
182 $cond = $conds[$key];
183 $value = $values[$key];
184 if (is_numeric($field) && is_numeric($value)) {
185 $field = floatval($field);
186 $value = floatval($value);
187 }
188 switch ($cond) {
189 case 'defined': $ok = (!empty($field)); break;
190 case 'equals': $ok = ($field == $value); break;
191 case 'contains': $ok = (strpos($field, $value) !== false); break;
192 case 'contained': $ok = (strpos($value, $field) !== false); break;
193 case 'greater': $ok = ($field > $value); break;
194 case 'greater_or_equal': $ok ($field >= $value); break;
195 case 'lower': $ok = ($field < $value); break;
196 case 'lower_or_equal': $ok = ($field <= $value); break;
197 default: $ok = false;
198 }
199 if ($ok) {
200 return $thens[$key];
201 } else {
202 return $elses[$key];
203 }
204 }
205
206 public function registerFunction($name, $desc, $callback)
207 {
208 if (is_callable($callback)) {
209 $this->user_functions['func_' . $name] = array('desc' => $desc, 'callback' => $callback);
210 return true;
211 }
212 return false;
213 }
214
a6613b2e 215 public function describe($name, $desc)
216 {
217 $this->field_desc[$name] = $desc;
218 }
219
31874f6f 220 /** Handle insertion form
221 * @param $page PlatalPage to process
222 * @param $url URI of the page
223 * @param $field Editable fields
224 */
225 public function apply(&$page, $url, $fields = null)
226 {
a6613b2e 227 if (is_null($fields) || empty($fields)) {
31874f6f 228 $fields = $this->getFieldList();
229 }
230 if (is_null($fields)) {
231 return false;
232 }
233
234 $current = Env::v('csv_page');
235 if (empty($current)) {
236 $current = 'source';
237 }
238 $next = Env::v('csv_next_page');
239 if (empty($next)) {
240 $next = $current;
241 }
242 $csv = Env::v('csv');
243 if ($current == 'source' && Env::has('csv_valid')) {
244 $csv = Env::v('csv_source');
245 $next = 'values';
246 }
247 if ($csv) {
248 $this->setCSV($csv);
249 }
250 if ($current == 'values' && Env::has('csv_valid')) {
251 $next = 'valid';
252 }
253 if (empty($csv)) {
254 $next = 'source';
255 }
256 if ($next == 'valid') {
257 $insert = Env::v('csv_value');
258 $values = Env::v('csv_user_value');
259 $update = Env::v('csv_update');
260 foreach ($insert as $key=>$value) {
261 if (empty($value)) {
262 $insert[$key] = null;
263 } elseif ($value == 'user_value') {
264 $insert[$key] = $values[$key];
265 } elseif ($value == 'cond_value') {
266 $insert[$key] = array($this, 'dynamicCond');
267 } elseif (array_key_exists($value, $this->user_functions)) {
268 $insert[$key] = $this->user_functions[$value]['callback'];
269 }
270 if (isset($update[$key])) {
271 $update[$key] = $insert[$key];
272 }
273 }
274 if ($current == 'valid' && Env::has('csv_valid')) {
275 $this->run(Env::v('csv_action'), $insert, $update);
276 $page->assign('csv_done', true);
277 } else {
278 $preview = array();
279 foreach ($this->data as $line) {
280 $preview[] = $this->makeAssoc($line, $insert);
281 }
282 $page->assign('csv_preview', $preview);
283 }
284 }
285 $page->assign('csv_index', $this->index);
286 $page->assign('csv_funtions', $this->user_functions);
a6613b2e 287 $page->assign('csv_field_desc', $this->field_desc);
31874f6f 288 $page->assign('csv_page', $next);
289 $page->assign('csv_path', $url);
290 $page->assign('csv_fields', $fields);
291 $page->assign('csv', $csv);
292 }
293}
294
295?>