oreach ($schema as $name => $table) { db_create_table($ret, $name, $table); } return $ret; } /** * Remove all tables that a module defines in its hook_schema(). * * Note: This function does not pass the module's schema through * hook_schema_alter(). The module's tables will be created exactly as the * module defines them. * * @param $module * The module for which the tables will be removed. * @return * An array of arrays with the following key/value pairs: * - success: a boolean indicating whether the query succeeded. * - query: the SQL query(s) executed, passed through check_plain(). */ function drupal_uninstall_schema($module) { $schema = drupal_get_schema_unprocessed($module); _drupal_initialize_schema($module, $schema); $ret = array(); foreach ($schema as $table) { db_drop_table($ret, $table['name']); } return $ret; } /** * Returns the unprocessed and unaltered version of a module's schema. * * Use this function only if you explicitly need the original * specification of a schema, as it was defined in a module's * hook_schema(). No additional default values will be set, * hook_schema_alter() is not invoked and these unprocessed * definitions won't be cached. * * This function can be used to retrieve a schema specification in * hook_schema(), so it allows you to derive your tables from existing * specifications. * * It is also used by drupal_install_schema() and * drupal_uninstall_schema() to ensure that a module's tables are * created exactly as specified without any changes introduced by a * module that implements hook_schema_alter(). * * @param $module * The module to which the table belongs. * @param $table * The name of the table. If not given, the module's complete schema * is returned. */ function drupal_get_schema_unprocessed($module, $table = NULL) { // Load the .install file to get hook_schema. module_load_include('install', $module); $schema = module_invoke($module, 'schema'); if (!is_null($table) && isset($schema[$table])) { return $schema[$table]; } else { return $schema; } } /** * Fill in required default values for table definitions returned by hook_schema(). * * @param $module * The module for which hook_schema() was invoked. * @param $schema * The schema definition array as it was returned by the module's * hook_schema(). */ function _drupal_initialize_schema($module, &$schema) { // Set the name and module key for all tables. foreach ($schema as $name => $table) { if (empty($table['module'])) { $schema[$name]['module'] = $module; } if (!isset($table['name'])) { $schema[$name]['name'] = $name; } } } /** * Retrieve a list of fields from a table schema. The list is suitable for use in a SQL query. * * @param $table * The name of the table from which to retrieve fields. * @param * An optional prefix to to all fields. * * @return An array of fields. **/ function drupal_schema_fields_sql($table, $prefix = NULL) { $schema = drupal_get_schema($table); $fields = array_keys($schema['fields']); if ($prefix) { $columns = array(); foreach ($fields as $field) { $columns[] = "$prefix.$field"; } return $columns; } else { return $fields; } } /** * Save a record to the database based upon the schema. * * Default values are filled in for missing items, and 'serial' (auto increment) * types are filled in with IDs. * * @param $table * The name of the table; this must exist in schema API. * @param $object * The object to write. This is a reference, as defaults according to * the schema may be filled in on the object, as well as ID on the serial * type(s). Both array an object types may be passed. * @param $update * If this is an update, specify the primary keys' field names. It is the * caller's responsibility to know if a record for this object already * exists in the database. If there is only 1 key, you may pass a simple string. * @return * Failure to write a record will return FALSE. Otherwise SAVED_NEW or * SAVED_UPDATED is returned depending on the operation performed. The * $object parameter contains values for any serial fields defined by * the $table. For example, $object->nid will be populated after inserting * a new node. */ function drupal_write_record($table, &$object, $update = array()) { // Standardize $update to an array. if (is_string($update)) { $update = array($update); } $schema = drupal_get_schema($table); if (empty($schema)) { return FALSE; } // Convert to an object if needed. if (is_array($object)) { $object = (object) $object; $array = TRUE; } else { $array = FALSE; } $fields = $defs = $values = $serials = $placeholders = array(); // Go through our schema, build SQL, and when inserting, fill in defaults for // fields that are not set. foreach ($schema['fields'] as $field => $info) { // Special case -- skip serial types if we are updating. if ($info['type'] == 'serial' && count($update)) { continue; } // For inserts, populate defaults from Schema if not already provided if (!isset($object->$field) && !count($update) && isset($info['default'])) { $object->$field = $info['default']; } // Track serial fields so we can helpfully populate them after the query. if ($info['type'] == 'serial') { $serials[] = $field; // Ignore values for serials when inserting data. Unsupported. unset($object->$field); } // Build arrays for the fields, placeholders, and values in our query. if (isset($object->$field)) { $fields[] = $field; $placeholders[] = db_type_placeholder($info['type']); if (empty($info['serialize'])) { $values[] = $object->$field; } else { $values[] = serialize($object->$field); } } } // Build the SQL. $query = ''; if (!count($update)) { $query = "INSERT INTO {". $table ."} (". implode(', ', $fields) .') VALUES ('. implode(', ', $placeholders) .')'; $return = SAVED_NEW; } else { $query = ''; foreach ($fields as $id => $field) { if ($query) { $query .= ', '; } $query .= $field .' = '. $placeholders[$id]; } foreach ($update as $key){ $conditions[] = "$key = ". db_type_placeholder($schema['fields'][$key]['type']); $values[] = $object->$key; } $query = "UPDATE {". $table ."} SET $query WHERE ". implode(' AND ', $conditions); $return = SAVED_UPDATED; } // Execute the SQL. if (db_query($query, $values)) { if ($serials) { // Get last insert ids and fill them in. foreach ($serials as $field) { $object->$field = db_last_insert_id($table, $field); } } } else { $return = FALSE; } // If we began with an array, convert back so we don't surprise the caller. if ($array) { $object = (array) $object; } return $return; } /** * @} End of "ingroup schemaapi". */ /** * Parse Drupal info file format. * * Files should use an ini-like format to specify values. * White-space generally doesn't matter, except inside values. * e.g. * * @verbatim * key = value * key = "value" * key = 'value' * key = "multi-line * * value" * key = 'multi-line * * value' * key * = * 'value' * @endverbatim * * Arrays are created using a GET-like syntax: * * @verbatim * key[] = "numeric array" * key[index] = "associative array" * key[index][] = "nested numeric array" * key[index][index] = "nested associative array" * @endverbatim * * PHP constants are substituted in, but only when used as the entire value: * * Comments should start with a semi-colon at the beginning of a line. * * This function is NOT for placing arbitrary module-specific settings. Use * variable_get() and variable_set() for that. * * Information stored in the module.info file: * - name: The real name of the module for display purposes. * - description: A brief description of the module. * - dependencies: An array of shortnames of other modules this module depends on. * - package: The name of the package of modules this module belongs to. * * Example of .info file: * @verbatim * name = Forum * description = Enables threaded discussions about general topics. * dependencies[] = taxonomy * dependencies[] = comment * package = Core - optional * version = VERSION * @endverbatim * * @param $filename * The file we are parsing. Accepts file with relative or absolute path. * @return * The info array. */ function drupal_parse_info_file($filename) { $info = array(); if (!file_exists($filename)) { return $info; } $data = file_get_contents($filename); if (preg_match_all(' @^\s* # Start at the beginning of a line, ignoring leading whitespace ((?: [^=;\[\]]| # Key names cannot contain equal signs, semi-colons or square brackets, \[[^\[\]]*\] # unless they are balanced and not nested )+?) \s*=\s* # Key/value pairs are separated by equal signs (ignoring white-space) (?: ("(?:[^"]|(?<=\\\\)")*")| # Double-quoted string, which may contain slash-escaped quotes/slashes (\'(?:[^\']|(?<=\\\\)\')*\')| # Single-quoted string, which may contain slash-escaped quotes/slashes ([^\r\n]*?) # Non-quoted string )\s*$ # Stop at the next end of a line, ignoring trailing whitespace @msx', $data, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { // Fetch the key and value string $i = 0; foreach (array('key', 'value1', 'value2', 'value3') as $var) { $$var = isset($match[++$i]) ? $match[$i] : ''; } $value = stripslashes(substr($value1, 1, -1)) . stripslashes(substr($value2, 1, -1)) . $value3; // Parse array syntax $keys = preg_split('/\]?\[/', rtrim($key, ']')); $last = array_pop($keys); $parent = &$info; // Create nested arrays foreach ($keys as $key) { if ($key == '') { $key = count($parent); } if (!isset($parent[$key]) || !is_array($parent[$key])) { $parent[$key] = array(); } $parent = &$parent[$key]; } // Handle PHP constants if (defined($value)) { $value = constant($value); } // Insert actual value if ($last == '') { $last = count($parent); } $parent[$last] = $value; } } return $info; } /** * @return * Array of the possible severity levels for log messages. * * @see watchdog */ function watchdog_severity_levels() { return array( WATCHDOG_EMERG => t('emergency'), WATCHDOG_ALERT => t('alert'), WATCHDOG_CRITICAL => t('critical'), WATCHDOG_ERROR => t('error'), WATCHDOG_WARNING => t('warning'), WATCHDOG_NOTICE => t('notice'), WATCHDOG_INFO => t('info'), WATCHDOG_DEBUG => t('debug'), ); } /** * Explode a string of given tags into an array. */ function drupal_explode_tags($tags) { // This regexp allows the following types of user input: // this, "somecompany, llc", "and ""this"" w,o.rks", foo bar $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x'; preg_match_all($regexp, $tags, $matches); $typed_tags = array_unique($matches[1]); $tags = array(); foreach ($typed_tags as $tag) { // If a user has escaped a term (to demonstrate that it is a group, // or includes a comma or quote character), we remove the escape // formatting so to save the term into the database as the user intends. $tag = trim(str_replace('""', '"', preg_replace('/^"(.*)"$/', '\1', $tag))); if ($tag != "") { $tags[] = $tag; } } return $tags; } /** * Implode an array of tags into a string. */ function drupal_implode_tags($tags) { $encoded_tags = array(); foreach ($tags as $tag) { // Commas and quotes in tag names are special cases, so encode them. if (strpos($tag, ',') !== FALSE || strpos($tag, '"') !== FALSE) { $tag = '"'. str_replace('"', '""', $tag) .'"'; } $encoded_tags[] = $tag; } return implode(', ', $encoded_tags); } /** * Flush all cached data on the site. * * Empties cache tables, rebuilds the menu cache and theme registries, and * invokes a hook so that other modules' cache data can be cleared as well. */ function drupal_flush_all_caches() { // Change query-strings on css/js files to enforce reload for all users. _drupal_flush_css_js(); drupal_clear_css_cache(); drupal_clear_js_cache(); // If invoked from update.php, we must not update the theme information in the // database, or this will result in all themes being disabled. if (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update') { _system_theme_data(); } else { system_theme_data(); } drupal_rebuild_theme_registry(); menu_rebuild(); node_types_rebuild(); // Don't clear cache_form - in-progress form submissions may break. // Ordered so clearing the page cache will always be the last action. $core = array('cache', 'cache_block', 'cache_filter', 'cache_page'); $cache_tables = array_merge(module_invoke_all('flush_caches'), $core); foreach ($cache_tables as $table) { cache_clear_all('*', $table, TRUE); } } /** * Helper function to change query-strings on css/js files. * * Changes the character added to all css/js files as dummy query-string, * so that all browsers are forced to reload fresh files. We keep * 20 characters history (FIFO) to avoid repeats, but only the first * (newest) character is actually used on urls, to keep them short. * This is also called from update.php. */ function _drupal_flush_css_js() { $string_history = variable_get('css_js_query_string', '00000000000000000000'); $new_character = $string_history[0]; // Not including 'q' to allow certain JavaScripts to re-use query string. $characters = 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; while (strpos($string_history, $new_character) !== FALSE) { $new_character = $characters[mt_rand(0, strlen($characters) - 1)]; } variable_set('css_js_query_string', $new_character . substr($string_history, 0, 19)); }