<?php// $Id$/** * @file * Hooks and API functions for data module. */// Constant designating an undefined export state.// Used in absence of EXPORT_IN_CODE, EXPORT_IN_DATABASEdefine('DATA_EXPORT_UNDEFINED',0);/** * Implementation of hook_views_api(). */functiondata_views_api(){returnarray('api'=>'2.0','path'=>drupal_get_path('module','data'),);}/** * Implementation of hook_features_api(). */functiondata_features_api(){returnarray('default_hook'=>'data_default','file'=>drupal_get_path('module','data').'/data.features.inc',);}/** * Create a table. * * Usage: * $table = data_create_table('my_table', $schema, 'My table'); * $table->save($data); * * @see DataTable class. * * @param $name * String that identifies the data table. It is recommended to use * data_name() to generate a table name in the data namespace. For * example: $table = data_get_tabe(data_name('my_table')); * @param $schema * Schema for the table. * @param $title * A natural title for the table. * * @return * A DataTable object if init could create one, * FALSE if not. */functiondata_create_table($name,$schema,$title=NULL){if(_data_get_table($name,NULL,NULL,TRUE)){returnFALSE;}return_data_get_table($name,$schema,$title);}/** * Get a table if it exists. * * @see DataTable class. * * @param $name * Unique name of the table. * * @return * A DataTable object if there is a table with this name, * FALSE if not. */functiondata_get_table($name){if($table=_data_get_table($name)){return$table;}returnFALSE;}/** * Drop a table - use this instead of $table->drop(). */functiondata_drop_table($name){if($table=data_get_table($name)){$table->drop();_data_get_table($name,NULL,NULL,TRUE);}}/** * Get schema info for all data allocated tables. * * Pull directly from database to avoid race conditions. */functiondata_get_schema(){$schema=array();$result=db_query('SELECT name, table_schema FROM {data_tables}');while($table=db_fetch_object($result)){$schema[$table->name]=unserialize($table->table_schema);}return$schema;}/** * Load all data tables. */functiondata_get_all_tables(){$tables=array();if($tables=_data_load_table()){foreach($tablesas$table_name=>$table){if($table=data_get_table($table_name)){$tables[$table_name]=$table;}}}return$tables;}/** * Get a list of supported field definitions. * * This list is a sub set of Schema API data types * http://drupal.org/node/159605 * The keys are simplified handles. */functiondata_get_field_definitions(){returnarray('int'=>array('type'=>'int','not null'=>FALSE,),'unsigned int'=>array('type'=>'int','unsigned'=>TRUE,'not null'=>FALSE,),'varchar'=>array('type'=>'varchar','length'=>255,'not null'=>FALSE,),'text'=>array('type'=>'text','not null'=>FALSE,),);}/** * Get a definition key into a schema API type definition. * * If no type can be found, FALSE will be returned. */functiondata_get_field_definition($key){$definitions=data_get_field_definitions();if(isset($definitions[$key])){return$definitions[$key];}returnFALSE;}/** * Get schema API field types supported by Data module. */functiondata_get_field_types(){$definitions=data_get_field_definitions();$types=array();foreach($definitionsas$def){$types[$def['type']]=$def['type'];}return$types;}/** * Get a Schema API PK definition for a given field type. */functiondata_get_pk_definition($name,$type){if($type=='text'){returnarray($name,255);}else{return$name;}}/** * Get a Schema API index definition for a given field type. * @todo: support multiple name/type combinations. */functiondata_get_index_definition($name,$type){if($type=='text'){returnarray(array($name,255));}else{returnarray($name);}}/** * Create a table name in the data namespace. * @todo: make overridable. */functiondata_name($table){return'data_table_'.$table;}/** * Create a safe name for MySQL field or table names. * * @todo: IMPROVE. * * - make sure all unsafe characters are removed. * - filter magic words. * - test pgsql. */functiondata_safe_name($name){$map=array('.'=>'_',':'=>'','/'=>'','-'=>'_',' '=>'_',','=>'_',);$simple=trim(strtolower(strip_tags($name)));// Limit length to 64 as per http://dev.mysql.com/doc/refman/5.0/en/identifiers.html$simple=substr(strtr($simple,$map),0,64);if(is_numeric($simple)){// We need to escape numerics because Drupal's drupal_write_record() // does not properly escape token MYSQL names.$simple='__num_'.$simple;}returndb_escape_table($simple);}/** * Helper function to create a natural name. * underscored_name -> Underscored name */functiondata_natural_name($name){returnucfirst(strtolower(str_replace('_',' ',$name)));}/** * Helper function to generate a schema. * * Example: * $table->create(data_build_schema($keys)); * * @todo: check for table name collisions * @todo: add type detection * @todo: add meta info handling * @todo: add primary key handling * @todo: may be add option to add a full fledged schema here? */functiondata_build_schema($keys){// Build the table definition.// Fall back to varchar if no valid type is given.$fields=$schema=array();foreach($keysas$k=>$key){if($definition=data_get_field_definition($key)){$fields[data_safe_name($k)]=$definition;}else{$fields[data_safe_name($k)]=data_get_field_definition('varchar');}}$schema['fields']=$fields;$schema['indexes']=array();return$schema;}/** * Build a full schema api field definition. * * @param $stub * Array with at least one key 'type'. */functiondata_build_field_definition($stub){$spec=array();$spec['type']=$stub['type'];if($spec['type']=='int'){$spec['unsigned']=empty($stub['unsigned'])?FALSE:TRUE;}if($spec['type']=='varchar'){$spec['length']=255;}return$spec;}/** * Export a data table. This does not export the content of a table - only its schema * and any meta information (title, name, meta...). * * @param $name * The name of the table to be exported. * * @return * Exportable code. * * Only available if ctools is installed. */functiondata_export($name,$indent=''){if(module_exists('ctools')){ctools_include('export');$result=ctools_export_load_object('data_tables','names',array($name));if(isset($result[$name])){returnctools_export_object('data_tables',$result[$name],$indent);}}returnt('Export requires CTools http://drupal.org/project/ctools');}/** * Internal singleton/factory function for creating a single instance of a DataTable class. * * Don't use this function directly. Call data_create_table() or data_get_table() instead. * * If a schema is given, _data_get_table() creates the table objects DB structure. * * The purpose of this function is to make sure that * * a) there is only a single DataTable object for accessing a specific DataTable. * b) there is no DataTable object that does not have an existing table. */function_data_get_table($name,$schema=NULL,$title=NULL,$reset=FALSE){_data_include();static$tables;// Simple way of having a way to override the class being used. // This could be refined with a $type parameter in _data_get_table() and depending// functions.$class=variable_get('data_table_class','DataTable');if($reset){unset($tables[$name]);}if(!isset($tables[$name])){// Try whether we can load table, then instantiate. Object will then load itself.if(_data_load_table($name,$reset)){$tables[$name]=new$class($name);}}if($schema){$tables[$name]=new$class($name,$schema,$title);}returnisset($tables[$name])?$tables[$name]:FALSE;}/** * Loads data table info from the database. Uses CTools if available. */function_data_load_table($name=NULL,$reset=FALSE){if(module_exists('ctools')){ctools_include('export');if(empty($name)){returnctools_export_load_object('data_tables','all',array(),$reset);}else{$tables=ctools_export_load_object('data_tables','names',array($name),$reset);if(isset($tables[$name])){return$tables[$name];}returnFALSE;}}// If CTools is not available, load directly from DB.if(empty($name)){$result=db_query('SELECT * FROM {data_tables}');$tables=array();while($row=db_fetch_object($result)){foreach(array('table_schema','meta')as$key){$row->$key=unserialize($row->$key);}// No export type.$row->export_type=DATA_EXPORT_UNDEFINED;$tables[$row->name]=$row;}return$tables;}if($table=db_fetch_object(db_query('SELECT * FROM {data_tables} WHERE name = "%s"',$name))){// No export type.$table->export_type=DATA_EXPORT_UNDEFINED;return$table;}returnFALSE;}/** * Starts overriding a data table by copying it from the default definition into the DB. * This function does not have any effect if called on a table that does already exist in * data_tables. */function_data_override($name){if(!db_result(db_query('SELECT name FROM {data_tables} WHERE name = "%s"',$name))){if($table=_data_load_table($name)){drupal_write_record('data_tables',$table);}}}/** * Include class file. */function_data_include(){static$included;if(!$included){includedrupal_get_path('module','data').'/data.inc';}$included=TRUE;}