Fix delete() method.
[project/data.git] / data.module
1 <?php
2 // $Id$
3 /**
4 * @file
5 * Hooks and API functions for data module.
6 */
7
8 // Constant designating an undefined export state.
9 // Used in absence of EXPORT_IN_CODE, EXPORT_IN_DATABASE
10 define('DATA_EXPORT_UNDEFINED', 0);
11
12 /**
13 * Implementation of hook_views_api().
14 */
15 function data_views_api() {
16 return array(
17 'api' => '2.0',
18 'path' => drupal_get_path('module', 'data'),
19 );
20 }
21
22 /**
23 * Implementation of hook_features_api().
24 */
25 function data_features_api() {
26 return array(
27 'default_hook' => 'data_default',
28 'file' => drupal_get_path('module', 'data') .'/data.features.inc',
29 );
30 }
31
32 /**
33 * Create a table.
34 *
35 * Usage:
36 * $table = data_create_table('my_table', $schema, 'My table');
37 * $table->save($data);
38 *
39 * @see DataTable class.
40 *
41 * @param $name
42 * String that identifies the data table. It is recommended to use
43 * data_name() to generate a table name in the data namespace. For
44 * example: $table = data_get_tabe(data_name('my_table'));
45 * @param $schema
46 * Schema for the table.
47 * @param $title
48 * A natural title for the table.
49 *
50 * @return
51 * A DataTable object if init could create one,
52 * FALSE if not.
53 */
54 function data_create_table($name, $schema, $title = NULL) {
55 if (_data_get_table($name, NULL, NULL, TRUE)) {
56 return FALSE;
57 }
58 return _data_get_table($name, $schema, $title);
59 }
60
61 /**
62 * Get a table if it exists.
63 *
64 * @see DataTable class.
65 *
66 * @param $name
67 * Unique name of the table.
68 *
69 * @return
70 * A DataTable object if there is a table with this name,
71 * FALSE if not.
72 */
73 function data_get_table($name) {
74 if ($table = _data_get_table($name)) {
75 return $table;
76 }
77 return FALSE;
78 }
79
80 /**
81 * Drop a table - use this instead of $table->drop().
82 */
83 function data_drop_table($name) {
84 if ($table = data_get_table($name)) {
85 $table->drop();
86 _data_get_table($name, NULL, NULL, TRUE);
87 }
88 }
89
90 /**
91 * Get schema info for all data allocated tables.
92 *
93 * Pull directly from database to avoid race conditions.
94 */
95 function data_get_schema() {
96 $schema = array();
97 $result = db_query('SELECT name, table_schema FROM {data_tables}');
98 while ($table = db_fetch_object($result)) {
99 $schema[$table->name] = unserialize($table->table_schema);
100 }
101 return $schema;
102 }
103
104 /**
105 * Load all data tables.
106 */
107 function data_get_all_tables() {
108 $tables = array();
109 if ($tables = _data_load_table()) {
110 foreach ($tables as $table_name => $table) {
111 if ($table = data_get_table($table_name)) {
112 $tables[$table_name] = $table;
113 }
114 }
115 }
116 return $tables;
117 }
118
119 /**
120 * Get a list of supported field definitions.
121 *
122 * This list is a sub set of Schema API data types
123 * http://drupal.org/node/159605
124 * The keys are simplified handles.
125 */
126 function data_get_field_definitions() {
127 return array(
128 'int' => array(
129 'type' => 'int',
130 'not null' => FALSE,
131 ),
132 'unsigned int' => array(
133 'type' => 'int',
134 'unsigned' => TRUE,
135 'not null' => FALSE,
136 ),
137 'varchar' => array(
138 'type' => 'varchar',
139 'length' => 255,
140 'not null' => FALSE,
141 ),
142 'text' => array(
143 'type' => 'text',
144 'not null' => FALSE,
145 ),
146 );
147 }
148
149 /**
150 * Get a definition key into a schema API type definition.
151 *
152 * If no type can be found, FALSE will be returned.
153 */
154 function data_get_field_definition($key) {
155 $definitions = data_get_field_definitions();
156 if (isset($definitions[$key])) {
157 return $definitions[$key];
158 }
159 return FALSE;
160 }
161
162 /**
163 * Get schema API field types supported by Data module.
164 */
165 function data_get_field_types() {
166 $definitions = data_get_field_definitions();
167 $types = array();
168 foreach ($definitions as $def) {
169 $types[$def['type']] = $def['type'];
170 }
171 return $types;
172 }
173
174 /**
175 * Get a Schema API PK definition for a given field type.
176 */
177 function data_get_pk_definition($name, $type) {
178 if ($type == 'text') {
179 return array($name, 255);
180 }
181 else {
182 return $name;
183 }
184 }
185
186 /**
187 * Get a Schema API index definition for a given field type.
188 * @todo: support multiple name/type combinations.
189 */
190 function data_get_index_definition($name, $type) {
191 if ($type == 'text') {
192 return array(array($name, 255));
193 }
194 else {
195 return array($name);
196 }
197 }
198
199 /**
200 * Create a table name in the data namespace.
201 * @todo: make overridable.
202 */
203 function data_name($table) {
204 return 'data_table_'. $table;
205 }
206
207 /**
208 * Create a safe name for MySQL field or table names.
209 *
210 * @todo: IMPROVE.
211 *
212 * - make sure all unsafe characters are removed.
213 * - filter magic words.
214 * - test pgsql.
215 */
216 function data_safe_name($name) {
217 $map = array(
218 '.' => '_',
219 ':' => '',
220 '/' => '',
221 '-' => '_',
222 ' ' => '_',
223 ',' => '_',
224 );
225 $simple = trim(strtolower(strip_tags($name)));
226 // Limit length to 64 as per http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
227 $simple = substr(strtr($simple, $map), 0, 64);
228
229 if (is_numeric($simple)) {
230 // We need to escape numerics because Drupal's drupal_write_record()
231 // does not properly escape token MYSQL names.
232 $simple = '__num_'. $simple;
233 }
234 return db_escape_table($simple);
235 }
236
237 /**
238 * Helper function to create a natural name.
239 * underscored_name -> Underscored name
240 */
241 function data_natural_name($name) {
242 return ucfirst(strtolower(str_replace('_', ' ', $name)));
243 }
244
245 /**
246 * Helper function to generate a schema.
247 *
248 * Example:
249 * $table->create(data_build_schema($keys));
250 *
251 * @todo: check for table name collisions
252 * @todo: add type detection
253 * @todo: add meta info handling
254 * @todo: add primary key handling
255 * @todo: may be add option to add a full fledged schema here?
256 */
257 function data_build_schema($keys) {
258 // Build the table definition.
259 // Fall back to varchar if no valid type is given.
260 $fields = $schema = array();
261 foreach ($keys as $k => $key) {
262 if ($definition = data_get_field_definition($key)) {
263 $fields[data_safe_name($k)] = $definition;
264 }
265 else {
266 $fields[data_safe_name($k)] = data_get_field_definition('varchar');
267 }
268 }
269
270 $schema['fields'] = $fields;
271 $schema['indexes'] = array();
272 return $schema;
273 }
274
275 /**
276 * Build a full schema api field definition.
277 *
278 * @param $stub
279 * Array with at least one key 'type'.
280 */
281 function data_build_field_definition($stub) {
282 $spec = array();
283 $spec['type'] = $stub['type'];
284 if ($spec['type'] == 'int') {
285 $spec['unsigned'] = empty($stub['unsigned']) ? FALSE : TRUE;
286 }
287 if ($spec['type'] == 'varchar') {
288 $spec['length'] = 255;
289 }
290 return $spec;
291 }
292
293 /**
294 * Export a data table. This does not export the content of a table - only its schema
295 * and any meta information (title, name, meta...).
296 *
297 * @param $name
298 * The name of the table to be exported.
299 *
300 * @return
301 * Exportable code.
302 *
303 * Only available if ctools is installed.
304 */
305 function data_export($name, $indent = '') {
306 if (module_exists('ctools')) {
307 ctools_include('export');
308 $result = ctools_export_load_object('data_tables', 'names', array($name));
309 if (isset($result[$name])) {
310 return ctools_export_object('data_tables', $result[$name], $indent);
311 }
312 }
313 return t('Export requires CTools http://drupal.org/project/ctools');
314 }
315
316 /**
317 * Internal singleton/factory function for creating a single instance of a DataTable class.
318 *
319 * Don't use this function directly. Call data_create_table() or data_get_table() instead.
320 *
321 * If a schema is given, _data_get_table() creates the table objects DB structure.
322 *
323 * The purpose of this function is to make sure that
324 *
325 * a) there is only a single DataTable object for accessing a specific DataTable.
326 * b) there is no DataTable object that does not have an existing table.
327 */
328 function _data_get_table($name, $schema = NULL, $title = NULL, $reset = FALSE) {
329 _data_include();
330
331 static $tables;
332 // Simple way of having a way to override the class being used.
333 // This could be refined with a $type parameter in _data_get_table() and depending
334 // functions.
335 $class = variable_get('data_table_class', 'DataTable');
336 if ($reset) {
337 unset($tables[$name]);
338 }
339
340 if (!isset($tables[$name])) {
341 // Try whether we can load table, then instantiate. Object will then load itself.
342 if (_data_load_table($name, $reset)) {
343 $tables[$name] = new $class($name);
344 }
345 }
346 if ($schema) {
347 $tables[$name] = new $class($name, $schema, $title);
348 }
349 return isset($tables[$name]) ? $tables[$name] : FALSE;
350 }
351
352 /**
353 * Loads data table info from the database. Uses CTools if available.
354 */
355 function _data_load_table($name = NULL, $reset = FALSE) {
356 if (module_exists('ctools')) {
357 ctools_include('export');
358 if (empty($name)) {
359 return ctools_export_load_object('data_tables', 'all', array(), $reset);
360 }
361 else {
362 $tables = ctools_export_load_object('data_tables', 'names', array($name), $reset);
363 if (isset($tables[$name])) {
364 return $tables[$name];
365 }
366 return FALSE;
367 }
368 }
369 // If CTools is not available, load directly from DB.
370 if (empty($name)) {
371 $result = db_query('SELECT * FROM {data_tables}');
372 $tables = array();
373 while ($row = db_fetch_object($result)) {
374 foreach (array('table_schema', 'meta') as $key) {
375 $row->$key = unserialize($row->$key);
376 }
377 // No export type.
378 $row->export_type = DATA_EXPORT_UNDEFINED;
379 $tables[$row->name] = $row;
380 }
381 return $tables;
382 }
383 if ($table = db_fetch_object(db_query('SELECT * FROM {data_tables} WHERE name = "%s"', $name))) {
384 // No export type.
385 $table->export_type = DATA_EXPORT_UNDEFINED;
386 return $table;
387 }
388 return FALSE;
389 }
390
391 /**
392 * Starts overriding a data table by copying it from the default definition into the DB.
393 * This function does not have any effect if called on a table that does already exist in
394 * data_tables.
395 */
396 function _data_override($name) {
397 if (!db_result(db_query('SELECT name FROM {data_tables} WHERE name = "%s"', $name))) {
398 if ($table = _data_load_table($name)) {
399 drupal_write_record('data_tables', $table);
400 }
401 }
402 }
403
404 /**
405 * Include class file.
406 */
407 function _data_include() {
408 static $included;
409 if (!$included) {
410 include drupal_get_path('module', 'data') .'/data.inc';
411 }
412 $included = TRUE;
413 }