diff options
Diffstat (limited to 'includes/common.inc')
-rw-r--r-- | includes/common.inc | 272 |
1 files changed, 180 insertions, 92 deletions
diff --git a/includes/common.inc b/includes/common.inc index 03b0de485..5dadb4d16 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -1,5 +1,4 @@ <?php -// $Id$ /** * @file @@ -915,7 +914,9 @@ function drupal_http_request($url, array $options = array()) { return $result; } // Parse response headers from the response body. - list($response, $result->data) = explode("\r\n\r\n", $response, 2); + // Be tolerant of malformed HTTP responses that separate header and body with + // \n\n or \r\r instead of \r\n\r\n. + list($response, $result->data) = preg_split("/\r\n\r\n|\n\n|\r\r/", $response, 2); $response = preg_split("/\r\n|\n|\r/", $response); // Parse the response status line. @@ -1050,15 +1051,15 @@ function _fix_gpc_magic_files(&$item, $key) { * Fix double-escaping problems caused by "magic quotes" in some PHP installations. */ function fix_gpc_magic() { - $fixed = &drupal_static(__FUNCTION__, FALSE); + static $fixed = FALSE; if (!$fixed && ini_get('magic_quotes_gpc')) { array_walk($_GET, '_fix_gpc_magic'); array_walk($_POST, '_fix_gpc_magic'); array_walk($_COOKIE, '_fix_gpc_magic'); array_walk($_REQUEST, '_fix_gpc_magic'); array_walk($_FILES, '_fix_gpc_magic_files'); - $fixed = TRUE; } + $fixed = TRUE; } /** @@ -1974,7 +1975,7 @@ function _format_date_callback(array $matches = NULL, $new_langcode = NULL) { /** * Format a username. * - * By default, the passed in object's 'name' property is used if it exists, or + * By default, the passed-in object's 'name' property is used if it exists, or * else, the site-defined value for the 'anonymous' variable. However, a module * may override this by implementing hook_username_alter(&$name, $account). * @@ -2238,7 +2239,7 @@ function drupal_http_header_attributes(array $attributes = array()) { * drupal_attributes(array('title' => t('<script>steal_cookie();</script>'))); * * // The statement below demonstrates dangerous use of drupal_attributes, and - * // will return an onmouseout attribute with javascript code that, when used + * // will return an onmouseout attribute with JavaScript code that, when used * // as attribute in a tag, will cause users to be redirected to another site. * // * // In this case, the 'onmouseout' attribute should not be whitelisted -- @@ -2383,9 +2384,9 @@ function l($text, $path, array $options = array()) { * basis in hook_page_delivery_callback_alter(). * * For example, the same page callback function can be used for an HTML - * version of the page and an AJAX version of the page. The page callback + * version of the page and an Ajax version of the page. The page callback * function just needs to decide what content is to be returned and the - * delivery callback function will send it as an HTML page or an AJAX + * delivery callback function will send it as an HTML page or an Ajax * response, as appropriate. * * In order for page callbacks to be reusable in different delivery formats, @@ -2602,7 +2603,8 @@ function drupal_exit($destination = NULL) { * A linear array. * @param $function * A name of a function to apply to all values before output. - * @result + * + * @return * An associative array. */ function drupal_map_assoc($array, $function = NULL) { @@ -3543,7 +3545,7 @@ function drupal_load_stylesheet_content($contents, $optimize = FALSE) { // There are different conditions for removing leading and trailing // whitespace. // @see http://php.net/manual/en/regexp.reference.subpatterns.php - $contents = preg_replace_callback('< + $contents = preg_replace('< # Strip leading and trailing whitespace. \s*([@{};,])\s* # Strip only leading whitespace from: @@ -3554,7 +3556,10 @@ function drupal_load_stylesheet_content($contents, $optimize = FALSE) { # - Colon: Retain :pseudo-selectors. | ([\(:])\s+ >xS', - '_drupal_load_stylesheet_content', + // Only one of the three capturing groups will match, so its reference + // will contain the wanted value and the references for the + // two non-matching groups will be replaced with empty strings. + '$1$2$3', $contents ); // End the file with a new line. @@ -3569,16 +3574,6 @@ function drupal_load_stylesheet_content($contents, $optimize = FALSE) { } /** - * Helper for drupal_load_stylesheet_content(). - */ -function _drupal_load_stylesheet_content($matches) { - // Discard the full match. - unset($matches[0]); - // Use the non-empty match. - return current(array_filter($matches)); -} - -/** * Loads stylesheets recursively and returns contents with corrected paths. * * This function is used for recursive loading of stylesheets and @@ -3672,17 +3667,17 @@ function drupal_html_class($class) { * blocks, and other content to be output multiple times on the same page, * without breaking (X)HTML validation. * - * For already existing ids, a counter is appended to the id string. Therefore, + * For already existing IDs, a counter is appended to the ID string. Therefore, * JavaScript and CSS code should not rely on any value that was generated by * this function and instead should rely on manually added CSS classes or * similarly reliable constructs. * - * Two consecutive hyphens separate the counter from the original id. To manage - * uniqueness across multiple AJAX requests on the same page, AJAX requests + * Two consecutive hyphens separate the counter from the original ID. To manage + * uniqueness across multiple Ajax requests on the same page, Ajax requests * POST an array of all IDs currently present on the page, which are used to * prime this function's cache upon first invocation. * - * To allow reverse-parsing of ids submitted via AJAX, any multiple consecutive + * To allow reverse-parsing of IDs submitted via Ajax, any multiple consecutive * hyphens in the originally passed $id are replaced with a single hyphen. * * @param $id @@ -3692,10 +3687,10 @@ function drupal_html_class($class) { * The cleaned ID. */ function drupal_html_id($id) { - // If this is an AJAX request, then content returned by this page request will - // be merged with content already on the base page. The HTML ids must be + // If this is an Ajax request, then content returned by this page request will + // be merged with content already on the base page. The HTML IDs must be // unique for the fully merged content. Therefore, initialize $seen_ids to - // take into account ids that are already in use on the base page. + // take into account IDs that are already in use on the base page. $seen_ids_init = &drupal_static(__FUNCTION__ . ':init'); if (!isset($seen_ids_init)) { // Ideally, Drupal would provide an API to persist state information about @@ -3703,7 +3698,7 @@ function drupal_html_id($id) { // function's $seen_ids static variable to that state information in order // to have it properly initialized for this page request. However, no such // page state API exists, so instead, ajax.js adds all of the in-use HTML - // ids to the POST data of AJAX submissions. Direct use of $_POST is + // IDs to the POST data of Ajax submissions. Direct use of $_POST is // normally not recommended as it could open up security risks, but because // the raw POST data is cast to a number before being returned by this // function, this usage is safe. @@ -3750,7 +3745,7 @@ function drupal_html_id($id) { // The counter needs to be appended with a delimiter that does not exist in // the base ID. Requiring a unique delimiter helps ensure that we really do // return unique IDs and also helps us re-create the $seen_ids array during - // AJAX requests. + // Ajax requests. if (isset($seen_ids[$id])) { $id = $id . '--' . ++$seen_ids[$id]; } @@ -3768,7 +3763,7 @@ function drupal_html_id($id) { * page region that is output by the theme (Drupal core already handles this in * the standard template preprocess implementation). Standardizing the class * names in this way allows modules to implement certain features, such as - * drag-and-drop or dynamic AJAX loading, in a theme-independent way. + * drag-and-drop or dynamic Ajax loading, in a theme-independent way. * * @param $region * The name of the page region (for example, 'page_top' or 'content'). @@ -3953,12 +3948,17 @@ function drupal_add_js($data = NULL, $options = NULL) { if (isset($data)) { // Add jquery.js and drupal.js, as well as the basePath setting, the - // first time a Javascript file is added. + // first time a JavaScript file is added. if (empty($javascript)) { + // url() generates the prefix using hook_url_outbound_alter(). Instead of + // running the hook_url_outbound_alter() again here, extract the prefix + // from url(). + url('', array('prefix' => &$prefix)); $javascript = array( 'settings' => array( 'data' => array( array('basePath' => base_path()), + array('pathPrefix' => empty($prefix) ? '' : $prefix), ), 'type' => 'setting', 'scope' => 'header', @@ -3996,7 +3996,7 @@ function drupal_add_js($data = NULL, $options = NULL) { default: // 'file' and 'external' // Local and external files must keep their name as the associative key - // so the same JavaScript file is not be added twice. + // so the same JavaScript file is not added twice. $javascript[$options['data']] = $options; } } @@ -4094,13 +4094,13 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS // page request. $default_query_string = variable_get('css_js_query_string', '0'); - // For inline Javascript to validate as XHTML, all Javascript containing + // For inline JavaScript to validate as XHTML, all JavaScript containing // XHTML needs to be wrapped in CDATA. To make that backwards compatible // with HTML 4, we need to comment out the CDATA-tag. $embed_prefix = "\n<!--//--><![CDATA[//><!--\n"; $embed_suffix = "\n//--><!]]>\n"; - // Since Javascript may look for arguments in the url and act on them, some + // Since JavaScript may look for arguments in the URL and act on them, some // third-party code might require the use of a different query string. $js_version_string = variable_get('drupal_js_version_query_string', 'v='); @@ -4318,7 +4318,7 @@ function drupal_process_attached($elements, $group = JS_DEFAULT, $dependency_che } // Add additional types of attachments specified in the render() structure. - // Libraries, Javascript and CSS have been added already, as they require + // Libraries, JavaScript and CSS have been added already, as they require // special handling. foreach ($elements['#attached'] as $callback => $options) { if (function_exists($callback)) { @@ -4785,7 +4785,7 @@ function drupal_clear_js_cache() { } /** - * Converts a PHP variable into its Javascript equivalent. + * Converts a PHP variable into its JavaScript equivalent. * * We use HTML-safe strings, i.e. with <, > and & escaped. * @@ -4881,12 +4881,12 @@ function drupal_valid_token($token, $value = '', $skip_anonymous = FALSE) { } function _drupal_bootstrap_full() { - $called = &drupal_static(__FUNCTION__); + static $called = FALSE; if ($called) { return; } - $called = 1; + $called = TRUE; require_once DRUPAL_ROOT . '/' . variable_get('path_inc', 'includes/path.inc'); require_once DRUPAL_ROOT . '/includes/theme.inc'; require_once DRUPAL_ROOT . '/includes/pager.inc'; @@ -5235,7 +5235,7 @@ function drupal_set_page_content($content = NULL) { * browsers, '#browsers' can be set to array('IE' => 'gte IE 8'). * * @return - * The passed in element with markup for conditional comments potentially + * The passed-in element with markup for conditional comments potentially * added to '#prefix' and '#suffix'. */ function drupal_pre_render_conditional_comments($elements) { @@ -5297,7 +5297,7 @@ function drupal_pre_render_conditional_comments($elements) { * - #options: (optional) An array of options to pass to l(). * * @return - * The passed in elements containing a rendered link in '#markup'. + * The passed-in elements containing a rendered link in '#markup'. */ function drupal_pre_render_link($element) { // By default, link options to pass to l() are normally set in #options. @@ -5327,7 +5327,7 @@ function drupal_pre_render_link($element) { if (!isset($element['#id'])) { $element['#id'] = $element['#options']['attributes']['id'] = drupal_html_id('ajax-link'); } - // If #ajax['path] was not specified, use the href as AJAX request URL. + // If #ajax['path] was not specified, use the href as Ajax request URL. if (!isset($element['#ajax']['path'])) { $element['#ajax']['path'] = $element['#href']; $element['#ajax']['options'] = $element['#options']; @@ -5444,7 +5444,7 @@ function drupal_pre_render_links($element) { * A structured array using the #markup key. * * @return - * The passed in elements, but #markup appended to #children. + * The passed-in elements, but #markup appended to #children. */ function drupal_pre_render_markup($elements) { $elements['#children'] = $elements['#markup']; @@ -5575,9 +5575,9 @@ function drupal_render(&$elements) { return $cached_output; } - // If #markup is not empty, set #type. This allows to specify just #markup on - // an element without setting #type. - if (!empty($elements['#markup']) && !isset($elements['#type'])) { + // If #markup is set, ensure #type is set. This allows to specify just #markup + // on an element without setting #type. + if (isset($elements['#markup']) && !isset($elements['#type'])) { $elements['#type'] = 'markup'; } @@ -5694,13 +5694,17 @@ function drupal_render_children(&$element, $children_keys = NULL) { } /** - * Render and print an element. + * Render an element. * * This function renders an element using drupal_render(). The top level - * element is always rendered even if hide() had been previously used on it. + * element is shown with show() before rendering, so it will always be rendered + * even if hide() had been previously used on it. * - * Any nested elements are only rendered if they haven't been rendered before - * or if they have been re-enabled with show(). + * @param $element + * The element to be rendered. + * + * @return + * The rendered element. * * @see drupal_render() * @see show() @@ -5721,6 +5725,21 @@ function render(&$element) { /** * Hide an element from later rendering. * + * The first time render() or drupal_render() is called on an element tree, + * as each element in the tree is rendered, it is marked with a #printed flag + * and the rendered children of the element are cached. Subsequent calls to + * render() or drupal_render() will not traverse the child tree of this element + * again: they will just use the cached children. So if you want to hide an + * element, be sure to call hide() on the element before its parent tree is + * rendered for the first time, as it will have no effect on subsequent + * renderings of the parent tree. + * + * @param $element + * The element to be hidden. + * + * @return + * The element. + * * @see render() * @see show() */ @@ -5730,10 +5749,25 @@ function hide(&$element) { } /** - * Show a hidden or already printed element from later rendering. + * Show a hidden element for later rendering. + * + * You can also use render($element), which shows the element while rendering + * it. + * + * The first time render() or drupal_render() is called on an element tree, + * as each element in the tree is rendered, it is marked with a #printed flag + * and the rendered children of the element are cached. Subsequent calls to + * render() or drupal_render() will not traverse the child tree of this element + * again: they will just use the cached children. So if you want to show an + * element, be sure to call show() on the element before its parent tree is + * rendered for the first time, as it will have no effect on subsequent + * renderings of the parent tree. + * + * @param $element + * The element to be shown. * - * Alternatively, render($element) could be used which automatically shows the - * element while rendering it. + * @return + * The element. * * @see render() * @see hide() @@ -5800,8 +5834,9 @@ function drupal_render_cache_set(&$markup, $elements) { // be retrieved and used. $data['#markup'] = &$markup; // Persist attached data associated with this element. - if (isset($elements['#attached'])) { - $data['#attached'] = $elements['#attached']; + $attached = drupal_render_collect_attached($elements, TRUE); + if ($attached) { + $data['#attached'] = $attached; } $bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'cache'; $expire = isset($elements['#cache']['expire']) ? $elements['#cache']['expire'] : CACHE_PERMANENT; @@ -5809,6 +5844,49 @@ function drupal_render_cache_set(&$markup, $elements) { } /** + * Collect #attached for an element and all child elements into a single array. + * + * When caching elements, it is necessary to collect all libraries, JavaScript + * and CSS into a single array, from both the element itself and all child + * elements. This allows drupal_render() to add these back to the page when the + * element is returned from cache. + * + * @param $elements + * The element to collect #attached from. + * @param $return + * Whether to return the attached elements and reset the internal static. + * + * @return + * The #attached array for this element and its descendants. + */ +function drupal_render_collect_attached($elements, $return = FALSE) { + $attached = &drupal_static(__FUNCTION__, array()); + + // Collect all #attached for this element. + if (isset($elements['#attached'])) { + foreach ($elements['#attached'] as $key => $value) { + if (!isset($attached[$key])) { + $attached[$key] = array(); + } + $attached[$key] = array_merge($attached[$key], $value); + } + } + if ($children = element_children($elements)) { + foreach ($children as $child) { + drupal_render_collect_attached($elements[$child]); + } + } + + // If this was the first call to the function, return all attached elements + // and reset the static cache. + if ($return) { + $return = $attached; + $attached = array(); + return $return; + } +} + +/** * Prepare an element for caching based on a query. This smart caching strategy * saves Drupal from querying and rendering to HTML when the underlying query is * unchanged. @@ -5830,7 +5908,7 @@ function drupal_render_cache_set(&$markup, $elements) { * * @return * A renderable array with the following keys and values: - * - #query: The passed in $query. + * - #query: The passed-in $query. * - #pre_render: $function with a _pre_render suffix. * - #cache: An associative array prepared for drupal_render_cache_set(). */ @@ -6237,7 +6315,7 @@ function drupal_array_set_nested_value(array &$array, array $parents, $value, $f * The array from which to get the value. * @param $parents * An array of parent keys of the value, starting with the outermost key. - * @param &$key_exists + * @param $key_exists * (optional) If given, an already defined variable that is altered by * reference. * @@ -6651,30 +6729,27 @@ function drupal_schema_fields_sql($table, $prefix = NULL) { } /** - * Saves 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. + * Saves (inserts or updates) a record to the database based upon the schema. * * @param $table * The name of the table; this must be defined by a hook_schema() * implementation. * @param $record * An object or array representing the record to write, passed in by - * reference. The function will fill in defaults from the schema and add an - * ID value to serial fields. + * reference. If inserting a new record, values not provided in $record will + * be populated in $record and in the database with the default values from + * the schema, as well as a single serial (auto-increment) field (if present). + * If updating an existing record, only provided values are updated in the + * database, and $record is not modified. * @param $primary_keys - * If this is an update, specify the primary keys' field names. If this is a - * new record, you must not provide this value. If there is only 1 field in - * the key, you may pass in a string; if there are multiple fields in the key, - * pass in an array. + * To indicate that this is a new record to be inserted, omit this argument. + * If this is an update, this argument specifies the primary keys' field + * names. If there is only 1 field in the key, you may pass in a string; if + * there are multiple fields in the key, pass in an array. * * @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 will contain values for any serial fields defined by the $table. - * For example, $record->nid or $record['nid'] will be populated after - * inserting a new a new node. + * If the record insert or update failed, returns FALSE. If it succeeded, + * returns SAVED_NEW or SAVED_UPDATED, depending on the operation performed. */ function drupal_write_record($table, &$record, $primary_keys = array()) { // Standardize $primary_keys to an array. @@ -6815,7 +6890,7 @@ function drupal_write_record($table, &$record, $primary_keys = array()) { */ /** - * Parse Drupal module and theme info file format. + * Parses Drupal module and theme .info files. * * Info files are NOT for placing arbitrary theme and module-specific settings. * Use variable_get() and variable_set() for that. @@ -6826,35 +6901,42 @@ function drupal_write_record($table, &$record, $primary_keys = array()) { * - dependencies: An array of shortnames of other modules this module requires. * - package: The name of the package of modules this module belongs to. * - * @see forum.info + * See forum.info for an example of a module .info file. * * Information stored in a theme .info file: - * - name: The real name of the theme for display purposes - * - description: Brief description + * - name: The real name of the theme for display purposes. + * - description: Brief description. * - screenshot: Path to screenshot relative to the theme's .info file. * - engine: Theme engine; typically phptemplate. - * - base: Name of a base theme, if applicable, eg: base = zen - * - regions: Listed regions eg: region[left] = Left sidebar - * - features: Features available eg: features[] = logo - * - stylesheets: Theme stylesheets eg: stylesheets[all][] = my-style.css - * - scripts: Theme scripts eg: scripts[] = my-script.css + * - base: Name of a base theme, if applicable; e.g., base = zen. + * - regions: Listed regions; e.g., region[left] = Left sidebar. + * - features: Features available; e.g., features[] = logo. + * - stylesheets: Theme stylesheets; e.g., stylesheets[all][] = my-style.css. + * - scripts: Theme scripts; e.g., scripts[] = my-script.js. * - * @see bartik.info + * See bartik.info for an example of a theme .info file. * * @param $filename * The file we are parsing. Accepts file with relative or absolute path. + * * @return * The info array. * * @see drupal_parse_info_format() */ function drupal_parse_info_file($filename) { - if (!file_exists($filename)) { - return array(); - } + $info = &drupal_static(__FUNCTION__, array()); - $data = file_get_contents($filename); - return drupal_parse_info_format($data); + if (!isset($info[$filename])) { + if (!file_exists($filename)) { + $info[$filename] = array(); + } + else { + $data = file_get_contents($filename); + $info[$filename] = drupal_parse_info_format($data); + } + } + return $info[$filename]; } /** @@ -7319,8 +7401,7 @@ function entity_create_stub_entity($entity_type, $ids) { /** * Load entities from the database. * - * This function should be used whenever you need to load more than one entity - * from the database. The entities are loaded into memory and will not require + * The entities are stored in a static memory cache, and will not require * database access if loaded again during the same page request. * * The actual loading is done through a class that has to implement the @@ -7415,8 +7496,15 @@ function entity_get_controller($entity_type) { * The type of entity, i.e. 'node', 'user'. * @param $entities * The entity objects which are being prepared for view, keyed by object ID. + * @param $langcode + * (optional) A language code to be used for rendering. Defaults to the global + * content language of the current request. */ -function entity_prepare_view($entity_type, $entities) { +function entity_prepare_view($entity_type, $entities, $langcode = NULL) { + if (!isset($langcode)) { + $langcode = $GLOBALS['language_content']->language; + } + // To ensure hooks are only run once per entity, check for an // entity_view_prepared flag and only process items without it. // @todo: resolve this more generally for both entity and field level hooks. @@ -7432,7 +7520,7 @@ function entity_prepare_view($entity_type, $entities) { } if (!empty($prepare)) { - module_invoke_all('entity_prepare_view', $prepare, $entity_type); + module_invoke_all('entity_prepare_view', $prepare, $entity_type, $langcode); } } @@ -7507,7 +7595,7 @@ function entity_label($entity_type, $entity) { $label = FALSE; $info = entity_get_info($entity_type); if (isset($info['label callback']) && function_exists($info['label callback'])) { - $label = $info['label callback']($entity); + $label = $info['label callback']($entity, $entity_type); } elseif (!empty($info['entity keys']['label']) && isset($entity->{$info['entity keys']['label']})) { $label = $entity->{$info['entity keys']['label']}; |