summaryrefslogtreecommitdiff
path: root/kolab.org/www/drupal-7.26/modules/system
diff options
context:
space:
mode:
Diffstat (limited to 'kolab.org/www/drupal-7.26/modules/system')
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/html.tpl.php63
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/image.gd.inc367
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/language.api.php186
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/maintenance-page.tpl.php95
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/page.tpl.php156
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/region.tpl.php35
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.admin-rtl.css86
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.admin.css271
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.admin.inc3214
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.api.php4899
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.archiver.inc139
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.base-rtl.css58
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.base.css270
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.cron.js19
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.info19
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.install3156
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.js140
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.mail.inc133
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.maintenance.css55
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.menus-rtl.css37
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.menus.css116
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.messages-rtl.css13
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.messages.css63
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.module4021
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.queue.inc370
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.tar.inc1892
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.test2761
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.theme-rtl.css53
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.theme.css239
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.tokens.inc269
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/system.updater.inc150
-rw-r--r--kolab.org/www/drupal-7.26/modules/system/theme.api.php240
32 files changed, 23585 insertions, 0 deletions
diff --git a/kolab.org/www/drupal-7.26/modules/system/html.tpl.php b/kolab.org/www/drupal-7.26/modules/system/html.tpl.php
new file mode 100644
index 0000000..c3e24c9
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/html.tpl.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Default theme implementation to display the basic html structure of a single
+ * Drupal page.
+ *
+ * Variables:
+ * - $css: An array of CSS files for the current page.
+ * - $language: (object) The language the site is being displayed in.
+ * $language->language contains its textual representation.
+ * $language->dir contains the language direction. It will either be 'ltr' or 'rtl'.
+ * - $rdf_namespaces: All the RDF namespace prefixes used in the HTML document.
+ * - $grddl_profile: A GRDDL profile allowing agents to extract the RDF data.
+ * - $head_title: A modified version of the page title, for use in the TITLE
+ * tag.
+ * - $head_title_array: (array) An associative array containing the string parts
+ * that were used to generate the $head_title variable, already prepared to be
+ * output as TITLE tag. The key/value pairs may contain one or more of the
+ * following, depending on conditions:
+ * - title: The title of the current page, if any.
+ * - name: The name of the site.
+ * - slogan: The slogan of the site, if any, and if there is no title.
+ * - $head: Markup for the HEAD section (including meta tags, keyword tags, and
+ * so on).
+ * - $styles: Style tags necessary to import all CSS files for the page.
+ * - $scripts: Script tags necessary to load the JavaScript files and settings
+ * for the page.
+ * - $page_top: Initial markup from any modules that have altered the
+ * page. This variable should always be output first, before all other dynamic
+ * content.
+ * - $page: The rendered page content.
+ * - $page_bottom: Final closing markup from any modules that have altered the
+ * page. This variable should always be output last, after all other dynamic
+ * content.
+ * - $classes String of classes that can be used to style contextually through
+ * CSS.
+ *
+ * @see template_preprocess()
+ * @see template_preprocess_html()
+ * @see template_process()
+ *
+ * @ingroup themeable
+ */
+?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN"
+ "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print $language->language; ?>" version="XHTML+RDFa 1.0" dir="<?php print $language->dir; ?>"<?php print $rdf_namespaces; ?>>
+
+<head profile="<?php print $grddl_profile; ?>">
+ <?php print $head; ?>
+ <title><?php print $head_title; ?></title>
+ <?php print $styles; ?>
+ <?php print $scripts; ?>
+</head>
+<body class="<?php print $classes; ?>" <?php print $attributes;?>>
+ <div id="skip-link">
+ <a href="#main-content" class="element-invisible element-focusable"><?php print t('Skip to main content'); ?></a>
+ </div>
+ <?php print $page_top; ?>
+ <?php print $page; ?>
+ <?php print $page_bottom; ?>
+</body>
+</html>
diff --git a/kolab.org/www/drupal-7.26/modules/system/image.gd.inc b/kolab.org/www/drupal-7.26/modules/system/image.gd.inc
new file mode 100644
index 0000000..91c0b05
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/image.gd.inc
@@ -0,0 +1,367 @@
+<?php
+
+/**
+ * @file
+ * GD2 toolkit for image manipulation within Drupal.
+ */
+
+/**
+ * @addtogroup image
+ * @{
+ */
+
+/**
+ * Retrieve settings for the GD2 toolkit.
+ */
+function image_gd_settings() {
+ if (image_gd_check_settings()) {
+ $form['status'] = array(
+ '#markup' => t('The GD toolkit is installed and working properly.')
+ );
+
+ $form['image_jpeg_quality'] = array(
+ '#type' => 'textfield',
+ '#title' => t('JPEG quality'),
+ '#description' => t('Define the image quality for JPEG manipulations. Ranges from 0 to 100. Higher values mean better image quality but bigger files.'),
+ '#size' => 10,
+ '#maxlength' => 3,
+ '#default_value' => variable_get('image_jpeg_quality', 75),
+ '#field_suffix' => t('%'),
+ );
+ $form['#element_validate'] = array('image_gd_settings_validate');
+
+ return $form;
+ }
+ else {
+ form_set_error('image_toolkit', t('The GD image toolkit requires that the GD module for PHP be installed and configured properly. For more information see <a href="@url">PHP\'s image documentation</a>.', array('@url' => 'http://php.net/image')));
+ return FALSE;
+ }
+}
+
+/**
+ * Validate the submitted GD settings.
+ */
+function image_gd_settings_validate($form, &$form_state) {
+ // Validate image quality range.
+ $value = $form_state['values']['image_jpeg_quality'];
+ if (!is_numeric($value) || $value < 0 || $value > 100) {
+ form_set_error('image_jpeg_quality', t('JPEG quality must be a number between 0 and 100.'));
+ }
+}
+
+/**
+ * Verify GD2 settings (that the right version is actually installed).
+ *
+ * @return
+ * A boolean indicating if the GD toolkit is available on this machine.
+ */
+function image_gd_check_settings() {
+ if ($check = get_extension_funcs('gd')) {
+ if (in_array('imagegd2', $check)) {
+ // GD2 support is available.
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Scale an image to the specified size using GD.
+ *
+ * @param $image
+ * An image object. The $image->resource, $image->info['width'], and
+ * $image->info['height'] values will be modified by this call.
+ * @param $width
+ * The new width of the resized image, in pixels.
+ * @param $height
+ * The new height of the resized image, in pixels.
+ * @return
+ * TRUE or FALSE, based on success.
+ *
+ * @see image_resize()
+ */
+function image_gd_resize(stdClass $image, $width, $height) {
+ $res = image_gd_create_tmp($image, $width, $height);
+
+ if (!imagecopyresampled($res, $image->resource, 0, 0, 0, 0, $width, $height, $image->info['width'], $image->info['height'])) {
+ return FALSE;
+ }
+
+ imagedestroy($image->resource);
+ // Update image object.
+ $image->resource = $res;
+ $image->info['width'] = $width;
+ $image->info['height'] = $height;
+ return TRUE;
+}
+
+/**
+ * Rotate an image the given number of degrees.
+ *
+ * @param $image
+ * An image object. The $image->resource, $image->info['width'], and
+ * $image->info['height'] values will be modified by this call.
+ * @param $degrees
+ * The number of (clockwise) degrees to rotate the image.
+ * @param $background
+ * An hexadecimal integer specifying the background color to use for the
+ * uncovered area of the image after the rotation. E.g. 0x000000 for black,
+ * 0xff00ff for magenta, and 0xffffff for white. For images that support
+ * transparency, this will default to transparent. Otherwise it will
+ * be white.
+ * @return
+ * TRUE or FALSE, based on success.
+ *
+ * @see image_rotate()
+ */
+function image_gd_rotate(stdClass $image, $degrees, $background = NULL) {
+ // PHP installations using non-bundled GD do not have imagerotate.
+ if (!function_exists('imagerotate')) {
+ watchdog('image', 'The image %file could not be rotated because the imagerotate() function is not available in this PHP installation.', array('%file' => $image->source));
+ return FALSE;
+ }
+
+ $width = $image->info['width'];
+ $height = $image->info['height'];
+
+ // Convert the hexadecimal background value to a color index value.
+ if (isset($background)) {
+ $rgb = array();
+ for ($i = 16; $i >= 0; $i -= 8) {
+ $rgb[] = (($background >> $i) & 0xFF);
+ }
+ $background = imagecolorallocatealpha($image->resource, $rgb[0], $rgb[1], $rgb[2], 0);
+ }
+ // Set the background color as transparent if $background is NULL.
+ else {
+ // Get the current transparent color.
+ $background = imagecolortransparent($image->resource);
+
+ // If no transparent colors, use white.
+ if ($background == 0) {
+ $background = imagecolorallocatealpha($image->resource, 255, 255, 255, 0);
+ }
+ }
+
+ // Images are assigned a new color palette when rotating, removing any
+ // transparency flags. For GIF images, keep a record of the transparent color.
+ if ($image->info['extension'] == 'gif') {
+ $transparent_index = imagecolortransparent($image->resource);
+ if ($transparent_index != 0) {
+ $transparent_gif_color = imagecolorsforindex($image->resource, $transparent_index);
+ }
+ }
+
+ $image->resource = imagerotate($image->resource, 360 - $degrees, $background);
+
+ // GIFs need to reassign the transparent color after performing the rotate.
+ if (isset($transparent_gif_color)) {
+ $background = imagecolorexactalpha($image->resource, $transparent_gif_color['red'], $transparent_gif_color['green'], $transparent_gif_color['blue'], $transparent_gif_color['alpha']);
+ imagecolortransparent($image->resource, $background);
+ }
+
+ $image->info['width'] = imagesx($image->resource);
+ $image->info['height'] = imagesy($image->resource);
+ return TRUE;
+}
+
+/**
+ * Crop an image using the GD toolkit.
+ *
+ * @param $image
+ * An image object. The $image->resource, $image->info['width'], and
+ * $image->info['height'] values will be modified by this call.
+ * @param $x
+ * The starting x offset at which to start the crop, in pixels.
+ * @param $y
+ * The starting y offset at which to start the crop, in pixels.
+ * @param $width
+ * The width of the cropped area, in pixels.
+ * @param $height
+ * The height of the cropped area, in pixels.
+ * @return
+ * TRUE or FALSE, based on success.
+ *
+ * @see image_crop()
+ */
+function image_gd_crop(stdClass $image, $x, $y, $width, $height) {
+ $res = image_gd_create_tmp($image, $width, $height);
+
+ if (!imagecopyresampled($res, $image->resource, 0, 0, $x, $y, $width, $height, $width, $height)) {
+ return FALSE;
+ }
+
+ // Destroy the original image and return the modified image.
+ imagedestroy($image->resource);
+ $image->resource = $res;
+ $image->info['width'] = $width;
+ $image->info['height'] = $height;
+ return TRUE;
+}
+
+/**
+ * Convert an image resource to grayscale.
+ *
+ * Note that transparent GIFs loose transparency when desaturated.
+ *
+ * @param $image
+ * An image object. The $image->resource value will be modified by this call.
+ * @return
+ * TRUE or FALSE, based on success.
+ *
+ * @see image_desaturate()
+ */
+function image_gd_desaturate(stdClass $image) {
+ // PHP installations using non-bundled GD do not have imagefilter.
+ if (!function_exists('imagefilter')) {
+ watchdog('image', 'The image %file could not be desaturated because the imagefilter() function is not available in this PHP installation.', array('%file' => $image->source));
+ return FALSE;
+ }
+
+ return imagefilter($image->resource, IMG_FILTER_GRAYSCALE);
+}
+
+/**
+ * GD helper function to create an image resource from a file.
+ *
+ * @param $image
+ * An image object. The $image->resource value will populated by this call.
+ * @return
+ * TRUE or FALSE, based on success.
+ *
+ * @see image_load()
+ */
+function image_gd_load(stdClass $image) {
+ $extension = str_replace('jpg', 'jpeg', $image->info['extension']);
+ $function = 'imagecreatefrom' . $extension;
+ return (function_exists($function) && $image->resource = $function($image->source));
+}
+
+/**
+ * GD helper to write an image resource to a destination file.
+ *
+ * @param $image
+ * An image object.
+ * @param $destination
+ * A string file URI or path where the image should be saved.
+ * @return
+ * TRUE or FALSE, based on success.
+ *
+ * @see image_save()
+ */
+function image_gd_save(stdClass $image, $destination) {
+ $scheme = file_uri_scheme($destination);
+ // Work around lack of stream wrapper support in imagejpeg() and imagepng().
+ if ($scheme && file_stream_wrapper_valid_scheme($scheme)) {
+ // If destination is not local, save image to temporary local file.
+ $local_wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_LOCAL);
+ if (!isset($local_wrappers[$scheme])) {
+ $permanent_destination = $destination;
+ $destination = drupal_tempnam('temporary://', 'gd_');
+ }
+ // Convert stream wrapper URI to normal path.
+ $destination = drupal_realpath($destination);
+ }
+
+ $extension = str_replace('jpg', 'jpeg', $image->info['extension']);
+ $function = 'image' . $extension;
+ if (!function_exists($function)) {
+ return FALSE;
+ }
+ if ($extension == 'jpeg') {
+ $success = $function($image->resource, $destination, variable_get('image_jpeg_quality', 75));
+ }
+ else {
+ // Always save PNG images with full transparency.
+ if ($extension == 'png') {
+ imagealphablending($image->resource, FALSE);
+ imagesavealpha($image->resource, TRUE);
+ }
+ $success = $function($image->resource, $destination);
+ }
+ // Move temporary local file to remote destination.
+ if (isset($permanent_destination) && $success) {
+ return (bool) file_unmanaged_move($destination, $permanent_destination, FILE_EXISTS_REPLACE);
+ }
+ return $success;
+}
+
+/**
+ * Create a truecolor image preserving transparency from a provided image.
+ *
+ * @param $image
+ * An image object.
+ * @param $width
+ * The new width of the new image, in pixels.
+ * @param $height
+ * The new height of the new image, in pixels.
+ * @return
+ * A GD image handle.
+ */
+function image_gd_create_tmp(stdClass $image, $width, $height) {
+ $res = imagecreatetruecolor($width, $height);
+
+ if ($image->info['extension'] == 'gif') {
+ // Grab transparent color index from image resource.
+ $transparent = imagecolortransparent($image->resource);
+
+ if ($transparent >= 0) {
+ // The original must have a transparent color, allocate to the new image.
+ $transparent_color = imagecolorsforindex($image->resource, $transparent);
+ $transparent = imagecolorallocate($res, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
+
+ // Flood with our new transparent color.
+ imagefill($res, 0, 0, $transparent);
+ imagecolortransparent($res, $transparent);
+ }
+ }
+ elseif ($image->info['extension'] == 'png') {
+ imagealphablending($res, FALSE);
+ $transparency = imagecolorallocatealpha($res, 0, 0, 0, 127);
+ imagefill($res, 0, 0, $transparency);
+ imagealphablending($res, TRUE);
+ imagesavealpha($res, TRUE);
+ }
+ else {
+ imagefill($res, 0, 0, imagecolorallocate($res, 255, 255, 255));
+ }
+
+ return $res;
+}
+
+/**
+ * Get details about an image.
+ *
+ * @param $image
+ * An image object.
+ * @return
+ * FALSE, if the file could not be found or is not an image. Otherwise, a
+ * keyed array containing information about the image:
+ * - "width": Width, in pixels.
+ * - "height": Height, in pixels.
+ * - "extension": Commonly used file extension for the image.
+ * - "mime_type": MIME type ('image/jpeg', 'image/gif', 'image/png').
+ *
+ * @see image_get_info()
+ */
+function image_gd_get_info(stdClass $image) {
+ $details = FALSE;
+ $data = getimagesize($image->source);
+
+ if (isset($data) && is_array($data)) {
+ $extensions = array('1' => 'gif', '2' => 'jpg', '3' => 'png');
+ $extension = isset($extensions[$data[2]]) ? $extensions[$data[2]] : '';
+ $details = array(
+ 'width' => $data[0],
+ 'height' => $data[1],
+ 'extension' => $extension,
+ 'mime_type' => $data['mime'],
+ );
+ }
+
+ return $details;
+}
+
+/**
+ * @} End of "addtogroup image".
+ */
diff --git a/kolab.org/www/drupal-7.26/modules/system/language.api.php b/kolab.org/www/drupal-7.26/modules/system/language.api.php
new file mode 100644
index 0000000..40bb3f3
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/language.api.php
@@ -0,0 +1,186 @@
+<?php
+
+/**
+ * @file
+ * Hooks provided by the base system for language support.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Allows modules to act after language initialization has been performed.
+ *
+ * This is primarily needed to provide translation for configuration variables
+ * in the proper bootstrap phase. Variables are user-defined strings and
+ * therefore should not be translated via t(), since the source string can
+ * change without notice and any previous translation would be lost. Moreover,
+ * since variables can be used in the bootstrap phase, we need a bootstrap hook
+ * to provide a translation early enough to avoid misalignments between code
+ * using the original values and code using the translated values. However
+ * modules implementing hook_boot() should be aware that language initialization
+ * did not happen yet and thus they cannot rely on translated variables.
+ */
+function hook_language_init() {
+ global $language, $conf;
+
+ switch ($language->language) {
+ case 'it':
+ $conf['site_name'] = 'Il mio sito Drupal';
+ break;
+
+ case 'fr':
+ $conf['site_name'] = 'Mon site Drupal';
+ break;
+ }
+}
+
+/**
+ * Perform alterations on language switcher links.
+ *
+ * A language switcher link may need to point to a different path or use a
+ * translated link text before going through l(), which will just handle the
+ * path aliases.
+ *
+ * @param $links
+ * Nested array of links keyed by language code.
+ * @param $type
+ * The language type the links will switch.
+ * @param $path
+ * The current path.
+ */
+function hook_language_switch_links_alter(array &$links, $type, $path) {
+ global $language;
+
+ if ($type == LANGUAGE_TYPE_CONTENT && isset($links[$language->language])) {
+ foreach ($links[$language->language] as $link) {
+ $link['attributes']['class'][] = 'active-language';
+ }
+ }
+}
+
+/**
+ * Define language types.
+ *
+ * @return
+ * An associative array of language type definitions. The keys are the
+ * identifiers, which are also used as names for global variables representing
+ * the types in the bootstrap phase. The values are associative arrays that
+ * may contain the following elements:
+ * - name: The human-readable language type identifier.
+ * - description: A description of the language type.
+ * - fixed: A fixed array of language negotiation provider identifiers to use
+ * to initialize this language. Defining this key makes the language type
+ * non-configurable, so it will always use the specified providers in the
+ * given priority order. Omit to make the language type configurable.
+ *
+ * @see hook_language_types_info_alter()
+ * @ingroup language_negotiation
+ */
+function hook_language_types_info() {
+ return array(
+ 'custom_language_type' => array(
+ 'name' => t('Custom language'),
+ 'description' => t('A custom language type.'),
+ ),
+ 'fixed_custom_language_type' => array(
+ 'fixed' => array('custom_language_provider'),
+ ),
+ );
+}
+
+/**
+ * Perform alterations on language types.
+ *
+ * @param $language_types
+ * Array of language type definitions.
+ *
+ * @see hook_language_types_info()
+ * @ingroup language_negotiation
+ */
+function hook_language_types_info_alter(array &$language_types) {
+ if (isset($language_types['custom_language_type'])) {
+ $language_types['custom_language_type_custom']['description'] = t('A far better description.');
+ }
+}
+
+/**
+ * Define language negotiation providers.
+ *
+ * @return
+ * An associative array of language negotiation provider definitions. The keys
+ * are provider identifiers, and the values are associative arrays defining
+ * each provider, with the following elements:
+ * - types: An array of allowed language types. If a language negotiation
+ * provider does not specify which language types it should be used with, it
+ * will be available for all the configurable language types.
+ * - callbacks: An associative array of functions that will be called to
+ * perform various tasks. Possible elements are:
+ * - language: (required) Name of the callback function that determines the
+ * language value.
+ * - switcher: (optional) Name of the callback function that determines
+ * links for a language switcher block associated with this provider. See
+ * language_switcher_url() for an example.
+ * - url_rewrite: (optional) Name of the callback function that provides URL
+ * rewriting, if needed by this provider.
+ * - file: The file where callback functions are defined (this file will be
+ * included before the callbacks are invoked).
+ * - weight: The default weight of the provider.
+ * - name: The translated human-readable name for the provider.
+ * - description: A translated longer description of the provider.
+ * - config: An internal path pointing to the provider's configuration page.
+ * - cache: The value Drupal's page cache should be set to for the current
+ * provider to be invoked.
+ *
+ * @see hook_language_negotiation_info_alter()
+ * @ingroup language_negotiation
+ */
+function hook_language_negotiation_info() {
+ return array(
+ 'custom_language_provider' => array(
+ 'callbacks' => array(
+ 'language' => 'custom_language_provider_callback',
+ 'switcher' => 'custom_language_switcher_callback',
+ 'url_rewrite' => 'custom_language_url_rewrite_callback',
+ ),
+ 'file' => drupal_get_path('module', 'custom') . '/custom.module',
+ 'weight' => -4,
+ 'types' => array('custom_language_type'),
+ 'name' => t('Custom language negotiation provider'),
+ 'description' => t('This is a custom language negotiation provider.'),
+ 'cache' => 0,
+ ),
+ );
+}
+
+/**
+ * Perform alterations on language negoiation providers.
+ *
+ * @param $language_providers
+ * Array of language negotiation provider definitions.
+ *
+ * @see hook_language_negotiation_info()
+ * @ingroup language_negotiation
+ */
+function hook_language_negotiation_info_alter(array &$language_providers) {
+ if (isset($language_providers['custom_language_provider'])) {
+ $language_providers['custom_language_provider']['config'] = 'admin/config/regional/language/configure/custom-language-provider';
+ }
+}
+
+/**
+ * Perform alterations on the language fallback candidates.
+ *
+ * @param $fallback_candidates
+ * An array of language codes whose order will determine the language fallback
+ * order.
+ */
+function hook_language_fallback_candidates_alter(array &$fallback_candidates) {
+ $fallback_candidates = array_reverse($fallback_candidates);
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
diff --git a/kolab.org/www/drupal-7.26/modules/system/maintenance-page.tpl.php b/kolab.org/www/drupal-7.26/modules/system/maintenance-page.tpl.php
new file mode 100644
index 0000000..69d267f
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/maintenance-page.tpl.php
@@ -0,0 +1,95 @@
+<?php
+
+/**
+ * @file
+ * Default theme implementation to display a single Drupal page while offline.
+ *
+ * All the available variables are mirrored in html.tpl.php and page.tpl.php.
+ * Some may be blank but they are provided for consistency.
+ *
+ * @see template_preprocess()
+ * @see template_preprocess_maintenance_page()
+ *
+ * @ingroup themeable
+ */
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print $language->language ?>" lang="<?php print $language->language ?>" dir="<?php print $language->dir ?>">
+
+<head>
+ <title><?php print $head_title; ?></title>
+ <?php print $head; ?>
+ <?php print $styles; ?>
+ <?php print $scripts; ?>
+</head>
+<body class="<?php print $classes; ?>">
+ <div id="page">
+ <div id="header">
+ <div id="logo-title">
+
+ <?php if (!empty($logo)): ?>
+ <a href="<?php print $base_path; ?>" title="<?php print t('Home'); ?>" rel="home" id="logo">
+ <img src="<?php print $logo; ?>" alt="<?php print t('Home'); ?>" />
+ </a>
+ <?php endif; ?>
+
+ <div id="name-and-slogan">
+ <?php if (!empty($site_name)): ?>
+ <h1 id="site-name">
+ <a href="<?php print $base_path ?>" title="<?php print t('Home'); ?>" rel="home"><span><?php print $site_name; ?></span></a>
+ </h1>
+ <?php endif; ?>
+
+ <?php if (!empty($site_slogan)): ?>
+ <div id="site-slogan"><?php print $site_slogan; ?></div>
+ <?php endif; ?>
+ </div> <!-- /name-and-slogan -->
+ </div> <!-- /logo-title -->
+
+ <?php if (!empty($header)): ?>
+ <div id="header-region">
+ <?php print $header; ?>
+ </div>
+ <?php endif; ?>
+
+ </div> <!-- /header -->
+
+ <div id="container" class="clearfix">
+
+ <?php if (!empty($sidebar_first)): ?>
+ <div id="sidebar-first" class="column sidebar">
+ <?php print $sidebar_first; ?>
+ </div> <!-- /sidebar-first -->
+ <?php endif; ?>
+
+ <div id="main" class="column"><div id="main-squeeze">
+
+ <div id="content">
+ <?php if (!empty($title)): ?><h1 class="title" id="page-title"><?php print $title; ?></h1><?php endif; ?>
+ <?php if (!empty($messages)): print $messages; endif; ?>
+ <div id="content-content" class="clearfix">
+ <?php print $content; ?>
+ </div> <!-- /content-content -->
+ </div> <!-- /content -->
+
+ </div></div> <!-- /main-squeeze /main -->
+
+ <?php if (!empty($sidebar_second)): ?>
+ <div id="sidebar-second" class="column sidebar">
+ <?php print $sidebar_second; ?>
+ </div> <!-- /sidebar-second -->
+ <?php endif; ?>
+
+ </div> <!-- /container -->
+
+ <div id="footer-wrapper">
+ <div id="footer">
+ <?php if (!empty($footer)): print $footer; endif; ?>
+ </div> <!-- /footer -->
+ </div> <!-- /footer-wrapper -->
+
+ </div> <!-- /page -->
+
+</body>
+</html>
diff --git a/kolab.org/www/drupal-7.26/modules/system/page.tpl.php b/kolab.org/www/drupal-7.26/modules/system/page.tpl.php
new file mode 100644
index 0000000..bd61489
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/page.tpl.php
@@ -0,0 +1,156 @@
+<?php
+
+/**
+ * @file
+ * Default theme implementation to display a single Drupal page.
+ *
+ * The doctype, html, head and body tags are not in this template. Instead they
+ * can be found in the html.tpl.php template in this directory.
+ *
+ * Available variables:
+ *
+ * General utility variables:
+ * - $base_path: The base URL path of the Drupal installation. At the very
+ * least, this will always default to /.
+ * - $directory: The directory the template is located in, e.g. modules/system
+ * or themes/bartik.
+ * - $is_front: TRUE if the current page is the front page.
+ * - $logged_in: TRUE if the user is registered and signed in.
+ * - $is_admin: TRUE if the user has permission to access administration pages.
+ *
+ * Site identity:
+ * - $front_page: The URL of the front page. Use this instead of $base_path,
+ * when linking to the front page. This includes the language domain or
+ * prefix.
+ * - $logo: The path to the logo image, as defined in theme configuration.
+ * - $site_name: The name of the site, empty when display has been disabled
+ * in theme settings.
+ * - $site_slogan: The slogan of the site, empty when display has been disabled
+ * in theme settings.
+ *
+ * Navigation:
+ * - $main_menu (array): An array containing the Main menu links for the
+ * site, if they have been configured.
+ * - $secondary_menu (array): An array containing the Secondary menu links for
+ * the site, if they have been configured.
+ * - $breadcrumb: The breadcrumb trail for the current page.
+ *
+ * Page content (in order of occurrence in the default page.tpl.php):
+ * - $title_prefix (array): An array containing additional output populated by
+ * modules, intended to be displayed in front of the main title tag that
+ * appears in the template.
+ * - $title: The page title, for use in the actual HTML content.
+ * - $title_suffix (array): An array containing additional output populated by
+ * modules, intended to be displayed after the main title tag that appears in
+ * the template.
+ * - $messages: HTML for status and error messages. Should be displayed
+ * prominently.
+ * - $tabs (array): Tabs linking to any sub-pages beneath the current page
+ * (e.g., the view and edit tabs when displaying a node).
+ * - $action_links (array): Actions local to the page, such as 'Add menu' on the
+ * menu administration interface.
+ * - $feed_icons: A string of all feed icons for the current page.
+ * - $node: The node object, if there is an automatically-loaded node
+ * associated with the page, and the node ID is the second argument
+ * in the page's path (e.g. node/12345 and node/12345/revisions, but not
+ * comment/reply/12345).
+ *
+ * Regions:
+ * - $page['help']: Dynamic help text, mostly for admin pages.
+ * - $page['highlighted']: Items for the highlighted content region.
+ * - $page['content']: The main content of the current page.
+ * - $page['sidebar_first']: Items for the first sidebar.
+ * - $page['sidebar_second']: Items for the second sidebar.
+ * - $page['header']: Items for the header region.
+ * - $page['footer']: Items for the footer region.
+ *
+ * @see template_preprocess()
+ * @see template_preprocess_page()
+ * @see template_process()
+ * @see html.tpl.php
+ *
+ * @ingroup themeable
+ */
+?>
+
+ <div id="page-wrapper"><div id="page">
+
+ <div id="header"><div class="section clearfix">
+
+ <?php if ($logo): ?>
+ <a href="<?php print $front_page; ?>" title="<?php print t('Home'); ?>" rel="home" id="logo">
+ <img src="<?php print $logo; ?>" alt="<?php print t('Home'); ?>" />
+ </a>
+ <?php endif; ?>
+
+ <?php if ($site_name || $site_slogan): ?>
+ <div id="name-and-slogan">
+ <?php if ($site_name): ?>
+ <?php if ($title): ?>
+ <div id="site-name"><strong>
+ <a href="<?php print $front_page; ?>" title="<?php print t('Home'); ?>" rel="home"><span><?php print $site_name; ?></span></a>
+ </strong></div>
+ <?php else: /* Use h1 when the content title is empty */ ?>
+ <h1 id="site-name">
+ <a href="<?php print $front_page; ?>" title="<?php print t('Home'); ?>" rel="home"><span><?php print $site_name; ?></span></a>
+ </h1>
+ <?php endif; ?>
+ <?php endif; ?>
+
+ <?php if ($site_slogan): ?>
+ <div id="site-slogan"><?php print $site_slogan; ?></div>
+ <?php endif; ?>
+ </div> <!-- /#name-and-slogan -->
+ <?php endif; ?>
+
+ <?php print render($page['header']); ?>
+
+ </div></div> <!-- /.section, /#header -->
+
+ <?php if ($main_menu || $secondary_menu): ?>
+ <div id="navigation"><div class="section">
+ <?php print theme('links__system_main_menu', array('links' => $main_menu, 'attributes' => array('id' => 'main-menu', 'class' => array('links', 'inline', 'clearfix')), 'heading' => t('Main menu'))); ?>
+ <?php print theme('links__system_secondary_menu', array('links' => $secondary_menu, 'attributes' => array('id' => 'secondary-menu', 'class' => array('links', 'inline', 'clearfix')), 'heading' => t('Secondary menu'))); ?>
+ </div></div> <!-- /.section, /#navigation -->
+ <?php endif; ?>
+
+ <?php if ($breadcrumb): ?>
+ <div id="breadcrumb"><?php print $breadcrumb; ?></div>
+ <?php endif; ?>
+
+ <?php print $messages; ?>
+
+ <div id="main-wrapper"><div id="main" class="clearfix">
+
+ <div id="content" class="column"><div class="section">
+ <?php if ($page['highlighted']): ?><div id="highlighted"><?php print render($page['highlighted']); ?></div><?php endif; ?>
+ <a id="main-content"></a>
+ <?php print render($title_prefix); ?>
+ <?php if ($title): ?><h1 class="title" id="page-title"><?php print $title; ?></h1><?php endif; ?>
+ <?php print render($title_suffix); ?>
+ <?php if ($tabs): ?><div class="tabs"><?php print render($tabs); ?></div><?php endif; ?>
+ <?php print render($page['help']); ?>
+ <?php if ($action_links): ?><ul class="action-links"><?php print render($action_links); ?></ul><?php endif; ?>
+ <?php print render($page['content']); ?>
+ <?php print $feed_icons; ?>
+ </div></div> <!-- /.section, /#content -->
+
+ <?php if ($page['sidebar_first']): ?>
+ <div id="sidebar-first" class="column sidebar"><div class="section">
+ <?php print render($page['sidebar_first']); ?>
+ </div></div> <!-- /.section, /#sidebar-first -->
+ <?php endif; ?>
+
+ <?php if ($page['sidebar_second']): ?>
+ <div id="sidebar-second" class="column sidebar"><div class="section">
+ <?php print render($page['sidebar_second']); ?>
+ </div></div> <!-- /.section, /#sidebar-second -->
+ <?php endif; ?>
+
+ </div></div> <!-- /#main, /#main-wrapper -->
+
+ <div id="footer"><div class="section">
+ <?php print render($page['footer']); ?>
+ </div></div> <!-- /.section, /#footer -->
+
+ </div></div> <!-- /#page, /#page-wrapper -->
diff --git a/kolab.org/www/drupal-7.26/modules/system/region.tpl.php b/kolab.org/www/drupal-7.26/modules/system/region.tpl.php
new file mode 100644
index 0000000..deb8d3a
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/region.tpl.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * @file
+ * Default theme implementation to display a region.
+ *
+ * Available variables:
+ * - $content: The content for this region, typically blocks.
+ * - $classes: String of classes that can be used to style contextually through
+ * CSS. It can be manipulated through the variable $classes_array from
+ * preprocess functions. The default values can be one or more of the following:
+ * - region: The current template type, i.e., "theming hook".
+ * - region-[name]: The name of the region with underscores replaced with
+ * dashes. For example, the page_top region would have a region-page-top class.
+ * - $region: The name of the region variable as defined in the theme's .info file.
+ *
+ * Helper variables:
+ * - $classes_array: Array of html class attribute values. It is flattened
+ * into a string within the variable $classes.
+ * - $is_admin: Flags true when the current user is an administrator.
+ * - $is_front: Flags true when presented in the front page.
+ * - $logged_in: Flags true when the current user is a logged-in member.
+ *
+ * @see template_preprocess()
+ * @see template_preprocess_region()
+ * @see template_process()
+ *
+ * @ingroup themeable
+ */
+?>
+<?php if ($content): ?>
+ <div class="<?php print $classes; ?>">
+ <?php print $content; ?>
+ </div>
+<?php endif; ?>
diff --git a/kolab.org/www/drupal-7.26/modules/system/system.admin-rtl.css b/kolab.org/www/drupal-7.26/modules/system/system.admin-rtl.css
new file mode 100644
index 0000000..362a406
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/system.admin-rtl.css
@@ -0,0 +1,86 @@
+
+/**
+ * @file
+ * RTL styles for administration pages.
+ */
+
+/**
+ * Administration blocks.
+ */
+div.admin-panel .body {
+ padding: 0 8px 2px 4px;
+}
+div.admin .left {
+ float: right;
+ margin-left: 0;
+ margin-right: 1em;
+}
+div.admin .right {
+ float: left;
+ margin-left: 1em;
+ margin-right: 0;
+}
+div.admin .expert-link {
+ margin-right: 0;
+ margin-left: 1em;
+ padding-right: 0;
+ padding-left: 4px;
+ text-align: left;
+}
+
+/**
+ * Status report.
+ */
+table.system-status-report td.status-icon {
+ padding-left: 0;
+ padding-right: 6px;
+}
+table.system-status-report tr.merge-up td {
+ padding: 0 28px 8px 6px;
+}
+
+/**
+ * Appearance page.
+ */
+table.screenshot {
+ margin-left: 1em;
+}
+.system-themes-list-enabled .theme-selector .screenshot,
+.system-themes-list-enabled .theme-selector .no-screenshot {
+ float: right;
+ margin: 0 0 0 20px;
+}
+.system-themes-list-disabled .theme-selector {
+ float: right;
+ padding: 20px 0 20px 20px;
+}
+.theme-selector .operations li {
+ border-right: none;
+ border-left: 1px solid #cdcdcd;
+ float: right;
+}
+.theme-selector .operations li.last {
+ border-left: none;
+ padding: 0 0.7em 0 0;
+}
+.theme-selector .operations li.first {
+ padding: 0 0 0 0.7em;
+}
+
+/**
+ * Exposed filters.
+ */
+.exposed-filters .filters {
+ float: right;
+ margin-left: 1em;
+ margin-right: 0;
+}
+.exposed-filters .form-item label {
+ float: right;
+}
+/* Current filters */
+.exposed-filters .additional-filters {
+ float: right;
+ margin-left: 1em;
+ margin-right: 0;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/system/system.admin.css b/kolab.org/www/drupal-7.26/modules/system/system.admin.css
new file mode 100644
index 0000000..43340b5
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/system.admin.css
@@ -0,0 +1,271 @@
+
+/**
+ * @file
+ * Styles for administration pages.
+ */
+
+/**
+ * Administration blocks.
+ */
+div.admin-panel {
+ margin: 0;
+ padding: 5px 5px 15px 5px;
+}
+div.admin-panel .description {
+ margin: 0 0 3px;
+ padding: 2px 0 3px 0;
+}
+div.admin-panel .body {
+ padding: 0 4px 2px 8px; /* LTR */
+}
+div.admin {
+ padding-top: 15px;
+}
+div.admin .left {
+ float: left; /* LTR */
+ width: 47%;
+ margin-left: 1em; /* LTR */
+}
+div.admin .right {
+ float: right; /* LTR */
+ width: 47%;
+ margin-right: 1em; /* LTR */
+}
+div.admin .expert-link {
+ text-align: right; /* LTR */
+ margin-right: 1em; /* LTR */
+ padding-right: 4px; /* LTR */
+}
+
+/**
+ * Markup generated by theme_system_compact_link().
+ */
+.compact-link {
+ margin: 0 0 0.5em 0;
+}
+
+/**
+ * Quick inline admin links.
+ */
+small .admin-link:before {
+ content: '[';
+}
+small .admin-link:after {
+ content: ']';
+}
+
+/**
+ * Modules page.
+ */
+#system-modules div.incompatible {
+ font-weight: bold;
+}
+div.admin-requirements,
+div.admin-required {
+ font-size: 0.9em;
+ color: #444;
+}
+span.admin-disabled {
+ color: #800;
+}
+span.admin-enabled {
+ color: #080;
+}
+span.admin-missing {
+ color: #f00;
+}
+a.module-link {
+ display: block;
+ padding: 1px 0 1px 20px; /* LTR */
+ white-space: nowrap;
+}
+a.module-link-help {
+ background: url(../../misc/help.png) 0 50% no-repeat; /* LTR */
+}
+a.module-link-permissions {
+ background: url(../../misc/permissions.png) 0 50% no-repeat; /* LTR */
+}
+a.module-link-configure {
+ background: url(../../misc/configure.png) 0 50% no-repeat; /* LTR */
+}
+.module-help {
+ margin-left: 1em; /* LTR */
+ float: right; /* LTR */
+}
+
+/**
+ * Status report.
+ */
+table.system-status-report td {
+ padding: 6px;
+ vertical-align: middle;
+}
+table.system-status-report tr.merge-up td {
+ padding: 0 6px 8px 28px; /* LTR */
+}
+table.system-status-report td.status-icon {
+ width: 16px;
+ padding-right: 0; /* LTR */
+}
+table.system-status-report td.status-icon div {
+ background-repeat: no-repeat;
+ height: 16px;
+ width: 16px;
+}
+table.system-status-report tr.error td.status-icon div {
+ background-image: url(../../misc/message-16-error.png);
+}
+table.system-status-report tr.warning td.status-icon div {
+ background-image: url(../../misc/message-16-warning.png);
+}
+tr.merge-down,
+tr.merge-down td {
+ border-bottom-width: 0 !important;
+}
+tr.merge-up,
+tr.merge-up td {
+ border-top-width: 0 !important;
+}
+
+/**
+ * Theme settings.
+ */
+.theme-settings-left {
+ float: left;
+ width: 49%;
+}
+.theme-settings-right {
+ float: right;
+ width: 49%;
+}
+.theme-settings-bottom {
+ clear: both;
+}
+
+/**
+ * Appearance page.
+ */
+table.screenshot {
+ margin-right: 1em; /* LTR */
+}
+.theme-info h2 {
+ margin-bottom: 0;
+}
+.theme-info p {
+ margin-top: 0;
+}
+.system-themes-list {
+ margin-bottom: 20px;
+}
+.system-themes-list-disabled {
+ border-top: 1px solid #cdcdcd;
+ padding-top: 20px;
+}
+.system-themes-list h2 {
+ margin: 0;
+}
+.theme-selector {
+ padding-top: 20px;
+}
+.theme-selector .screenshot,
+.theme-selector .no-screenshot {
+ border: 1px solid #e0e0d8;
+ padding: 2px;
+ vertical-align: bottom;
+ width: 294px;
+ height: 219px;
+ line-height: 219px;
+ text-align: center;
+}
+.theme-default .screenshot {
+ border: 1px solid #aaa;
+}
+.system-themes-list-enabled .theme-selector .screenshot,
+.system-themes-list-enabled .theme-selector .no-screenshot {
+ float: left; /* LTR */
+ margin: 0 20px 0 0; /* LTR */
+}
+.system-themes-list-disabled .theme-selector .screenshot,
+.system-themes-list-disabled .theme-selector .no-screenshot {
+ width: 194px;
+ height: 144px;
+ line-height: 144px;
+}
+.theme-selector h3 {
+ font-weight: normal;
+}
+.theme-default h3 {
+ font-weight: bold;
+}
+.system-themes-list-enabled .theme-selector h3 {
+ margin-top: 0;
+}
+.system-themes-list-disabled .theme-selector {
+ width: 300px;
+ float: left; /* LTR */
+ padding: 20px 20px 20px 0; /* LTR */
+}
+.system-themes-list-enabled .theme-info {
+ max-width: 940px;
+}
+.system-themes-list-disabled .theme-info {
+ min-height: 170px;
+}
+.theme-selector .incompatible {
+ margin-top: 10px;
+ font-weight: bold;
+}
+.theme-selector .operations {
+ margin: 10px 0 0 0;
+ padding: 0;
+}
+.theme-selector .operations li {
+ float: left; /* LTR */
+ margin: 0;
+ padding: 0 0.7em;
+ list-style-type: none;
+ border-right: 1px solid #cdcdcd; /* LTR */
+}
+.theme-selector .operations li.last {
+ padding: 0 0 0 0.7em; /* LTR */
+ border-right: none; /* LTR */
+}
+.theme-selector .operations li.first {
+ padding: 0 0.7em 0 0; /* LTR */
+}
+#system-themes-admin-form {
+ clear: left;
+}
+
+/**
+ * Exposed filters.
+ */
+.exposed-filters .filters {
+ float: left; /* LTR */
+ margin-right: 1em; /* LTR */
+ width: 25em; /* IE6 */
+}
+.exposed-filters .form-item {
+ margin: 0 0 0.1em 0;
+ padding: 0;
+}
+.exposed-filters .form-item label {
+ float: left; /* LTR */
+ font-weight: normal;
+ width: 10em;
+}
+.exposed-filters .form-select {
+ width: 14em;
+}
+/* Current filters */
+.exposed-filters .current-filters {
+ margin-bottom: 1em;
+}
+.exposed-filters .current-filters .placeholder {
+ font-style: normal;
+ font-weight: bold;
+}
+.exposed-filters .additional-filters {
+ float: left; /* LTR */
+ margin-right: 1em; /* LTR */
+}
diff --git a/kolab.org/www/drupal-7.26/modules/system/system.admin.inc b/kolab.org/www/drupal-7.26/modules/system/system.admin.inc
new file mode 100644
index 0000000..465fd9f
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/system.admin.inc
@@ -0,0 +1,3214 @@
+<?php
+
+/**
+ * @file
+ * Admin page callbacks for the system module.
+ */
+
+/**
+ * Menu callback; Provide the administration overview page.
+ */
+function system_admin_config_page() {
+ // Check for status report errors.
+ if (system_status(TRUE) && user_access('administer site configuration')) {
+ drupal_set_message(t('One or more problems were detected with your Drupal installation. Check the <a href="@status">status report</a> for more information.', array('@status' => url('admin/reports/status'))), 'error');
+ }
+ $blocks = array();
+ if ($admin = db_query("SELECT menu_name, mlid FROM {menu_links} WHERE link_path = 'admin/config' AND module = 'system'")->fetchAssoc()) {
+ $result = db_query("
+ SELECT m.*, ml.*
+ FROM {menu_links} ml
+ INNER JOIN {menu_router} m ON ml.router_path = m.path
+ WHERE ml.link_path <> 'admin/help' AND menu_name = :menu_name AND ml.plid = :mlid AND hidden = 0", $admin, array('fetch' => PDO::FETCH_ASSOC));
+ foreach ($result as $item) {
+ _menu_link_translate($item);
+ if (!$item['access']) {
+ continue;
+ }
+ // The link description, either derived from 'description' in hook_menu()
+ // or customized via menu module is used as title attribute.
+ if (!empty($item['localized_options']['attributes']['title'])) {
+ $item['description'] = $item['localized_options']['attributes']['title'];
+ unset($item['localized_options']['attributes']['title']);
+ }
+ $block = $item;
+ $block['content'] = '';
+ $block['content'] .= theme('admin_block_content', array('content' => system_admin_menu_block($item)));
+ if (!empty($block['content'])) {
+ $block['show'] = TRUE;
+ }
+
+ // Prepare for sorting as in function _menu_tree_check_access().
+ // The weight is offset so it is always positive, with a uniform 5-digits.
+ $blocks[(50000 + $item['weight']) . ' ' . $item['title'] . ' ' . $item['mlid']] = $block;
+ }
+ }
+ if ($blocks) {
+ ksort($blocks);
+ return theme('admin_page', array('blocks' => $blocks));
+ }
+ else {
+ return t('You do not have any administrative items.');
+ }
+}
+
+/**
+ * Provide a single block from the administration menu as a page.
+ *
+ * This function is often a destination for these blocks.
+ * For example, 'admin/structure/types' needs to have a destination to be valid
+ * in the Drupal menu system, but too much information there might be
+ * hidden, so we supply the contents of the block.
+ *
+ * @return
+ * The output HTML.
+ */
+function system_admin_menu_block_page() {
+ $item = menu_get_item();
+ if ($content = system_admin_menu_block($item)) {
+ $output = theme('admin_block_content', array('content' => $content));
+ }
+ else {
+ $output = t('You do not have any administrative items.');
+ }
+ return $output;
+}
+
+/**
+ * Menu callback; prints a listing of admin tasks, organized by module.
+ */
+function system_admin_index() {
+ $module_info = system_get_info('module');
+ foreach ($module_info as $module => $info) {
+ $module_info[$module] = new stdClass();
+ $module_info[$module]->info = $info;
+ }
+ uasort($module_info, 'system_sort_modules_by_info_name');
+ $menu_items = array();
+
+ foreach ($module_info as $module => $info) {
+ // Only display a section if there are any available tasks.
+ if ($admin_tasks = system_get_module_admin_tasks($module, $info->info)) {
+ // Sort links by title.
+ uasort($admin_tasks, 'drupal_sort_title');
+ // Move 'Configure permissions' links to the bottom of each section.
+ $permission_key = "admin/people/permissions#module-$module";
+ if (isset($admin_tasks[$permission_key])) {
+ $permission_task = $admin_tasks[$permission_key];
+ unset($admin_tasks[$permission_key]);
+ $admin_tasks[$permission_key] = $permission_task;
+ }
+
+ $menu_items[$info->info['name']] = array($info->info['description'], $admin_tasks);
+ }
+ }
+ return theme('system_admin_index', array('menu_items' => $menu_items));
+}
+
+/**
+ * Displays the configuration overview page.
+ *
+ * This menu callback implementation is a legacy function that used to display
+ * the configuration overview page at admin/config. It is currently unused and
+ * will be removed in Drupal 8. The page at admin/config is now generated by
+ * system_admin_config_page().
+ *
+ * @deprecated
+ * @see system_admin_config_page()
+ */
+function system_settings_overview() {
+ // Check database setup if necessary
+ if (function_exists('db_check_setup') && empty($_POST)) {
+ db_check_setup();
+ }
+
+ $item = menu_get_item('admin/config');
+ $content = system_admin_menu_block($item);
+
+ $output = theme('admin_block_content', array('content' => $content));
+
+ return $output;
+}
+
+/**
+ * Menu callback; displays a listing of all themes.
+ */
+function system_themes_page() {
+ // Get current list of themes.
+ $themes = system_rebuild_theme_data();
+ uasort($themes, 'system_sort_modules_by_info_name');
+
+ $theme_default = variable_get('theme_default', 'bartik');
+ $theme_groups = array();
+
+ foreach ($themes as &$theme) {
+ if (!empty($theme->info['hidden'])) {
+ continue;
+ }
+ $admin_theme_options[$theme->name] = $theme->info['name'];
+ $theme->is_default = ($theme->name == $theme_default);
+
+ // Identify theme screenshot.
+ $theme->screenshot = NULL;
+ // Create a list which includes the current theme and all its base themes.
+ if (isset($themes[$theme->name]->base_themes)) {
+ $theme_keys = array_keys($themes[$theme->name]->base_themes);
+ $theme_keys[] = $theme->name;
+ }
+ else {
+ $theme_keys = array($theme->name);
+ }
+ // Look for a screenshot in the current theme or in its closest ancestor.
+ foreach (array_reverse($theme_keys) as $theme_key) {
+ if (isset($themes[$theme_key]) && file_exists($themes[$theme_key]->info['screenshot'])) {
+ $theme->screenshot = array(
+ 'path' => $themes[$theme_key]->info['screenshot'],
+ 'alt' => t('Screenshot for !theme theme', array('!theme' => $theme->info['name'])),
+ 'title' => t('Screenshot for !theme theme', array('!theme' => $theme->info['name'])),
+ 'attributes' => array('class' => array('screenshot')),
+ );
+ break;
+ }
+ }
+
+ if (empty($theme->status)) {
+ // Ensure this theme is compatible with this version of core.
+ // Require the 'content' region to make sure the main page
+ // content has a common place in all themes.
+ $theme->incompatible_core = !isset($theme->info['core']) || ($theme->info['core'] != DRUPAL_CORE_COMPATIBILITY) || (!isset($theme->info['regions']['content']));
+ $theme->incompatible_php = version_compare(phpversion(), $theme->info['php']) < 0;
+ }
+ $query['token'] = drupal_get_token('system-theme-operation-link');
+ $theme->operations = array();
+ if (!empty($theme->status) || !$theme->incompatible_core && !$theme->incompatible_php) {
+ // Create the operations links.
+ $query['theme'] = $theme->name;
+ if (drupal_theme_access($theme)) {
+ $theme->operations[] = array(
+ 'title' => t('Settings'),
+ 'href' => 'admin/appearance/settings/' . $theme->name,
+ 'attributes' => array('title' => t('Settings for !theme theme', array('!theme' => $theme->info['name']))),
+ );
+ }
+ if (!empty($theme->status)) {
+ if (!$theme->is_default) {
+ $theme->operations[] = array(
+ 'title' => t('Disable'),
+ 'href' => 'admin/appearance/disable',
+ 'query' => $query,
+ 'attributes' => array('title' => t('Disable !theme theme', array('!theme' => $theme->info['name']))),
+ );
+ $theme->operations[] = array(
+ 'title' => t('Set default'),
+ 'href' => 'admin/appearance/default',
+ 'query' => $query,
+ 'attributes' => array('title' => t('Set !theme as default theme', array('!theme' => $theme->info['name']))),
+ );
+ }
+ }
+ else {
+ $theme->operations[] = array(
+ 'title' => t('Enable'),
+ 'href' => 'admin/appearance/enable',
+ 'query' => $query,
+ 'attributes' => array('title' => t('Enable !theme theme', array('!theme' => $theme->info['name']))),
+ );
+ $theme->operations[] = array(
+ 'title' => t('Enable and set default'),
+ 'href' => 'admin/appearance/default',
+ 'query' => $query,
+ 'attributes' => array('title' => t('Enable !theme as default theme', array('!theme' => $theme->info['name']))),
+ );
+ }
+ }
+
+ // Add notes to default and administration theme.
+ $theme->notes = array();
+ $theme->classes = array();
+ if ($theme->is_default) {
+ $theme->classes[] = 'theme-default';
+ $theme->notes[] = t('default theme');
+ }
+
+ // Sort enabled and disabled themes into their own groups.
+ $theme_groups[$theme->status ? 'enabled' : 'disabled'][] = $theme;
+ }
+
+ // There are two possible theme groups.
+ $theme_group_titles = array(
+ 'enabled' => format_plural(count($theme_groups['enabled']), 'Enabled theme', 'Enabled themes'),
+ );
+ if (!empty($theme_groups['disabled'])) {
+ $theme_group_titles['disabled'] = format_plural(count($theme_groups['disabled']), 'Disabled theme', 'Disabled themes');
+ }
+
+ uasort($theme_groups['enabled'], 'system_sort_themes');
+ drupal_alter('system_themes_page', $theme_groups);
+
+ $admin_form = drupal_get_form('system_themes_admin_form', $admin_theme_options);
+ return theme('system_themes_page', array('theme_groups' => $theme_groups, 'theme_group_titles' => $theme_group_titles)) . drupal_render($admin_form);
+}
+
+/**
+ * Form to select the administration theme.
+ *
+ * @ingroup forms
+ * @see system_themes_admin_form_submit()
+ */
+function system_themes_admin_form($form, &$form_state, $theme_options) {
+ // Administration theme settings.
+ $form['admin_theme'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Administration theme'),
+ );
+ $form['admin_theme']['admin_theme'] = array(
+ '#type' => 'select',
+ '#options' => array(0 => t('Default theme')) + $theme_options,
+ '#title' => t('Administration theme'),
+ '#description' => t('Choose "Default theme" to always use the same theme as the rest of the site.'),
+ '#default_value' => variable_get('admin_theme', 0),
+ );
+ $form['admin_theme']['node_admin_theme'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Use the administration theme when editing or creating content'),
+ '#default_value' => variable_get('node_admin_theme', '0'),
+ );
+ $form['admin_theme']['actions'] = array('#type' => 'actions');
+ $form['admin_theme']['actions']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save configuration'),
+ );
+ return $form;
+}
+
+/**
+ * Process system_themes_admin_form form submissions.
+ */
+function system_themes_admin_form_submit($form, &$form_state) {
+ drupal_set_message(t('The configuration options have been saved.'));
+ variable_set('admin_theme', $form_state['values']['admin_theme']);
+ variable_set('node_admin_theme', $form_state['values']['node_admin_theme']);
+}
+
+/**
+ * Menu callback; Enables a theme.
+ */
+function system_theme_enable() {
+ if (isset($_REQUEST['theme']) && isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'system-theme-operation-link')) {
+ $theme = $_REQUEST['theme'];
+ // Get current list of themes.
+ $themes = list_themes();
+
+ // Check if the specified theme is one recognized by the system.
+ if (!empty($themes[$theme])) {
+ theme_enable(array($theme));
+ drupal_set_message(t('The %theme theme has been enabled.', array('%theme' => $themes[$theme]->info['name'])));
+ }
+ else {
+ drupal_set_message(t('The %theme theme was not found.', array('%theme' => $theme)), 'error');
+ }
+ drupal_goto('admin/appearance');
+ }
+ return MENU_ACCESS_DENIED;
+}
+
+/**
+ * Menu callback; Disables a theme.
+ */
+function system_theme_disable() {
+ if (isset($_REQUEST['theme']) && isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'system-theme-operation-link')) {
+ $theme = $_REQUEST['theme'];
+ // Get current list of themes.
+ $themes = list_themes();
+
+ // Check if the specified theme is one recognized by the system.
+ if (!empty($themes[$theme])) {
+ if ($theme == variable_get('theme_default', 'bartik')) {
+ // Don't disable the default theme.
+ drupal_set_message(t('%theme is the default theme and cannot be disabled.', array('%theme' => $themes[$theme]->info['name'])), 'error');
+ }
+ else {
+ theme_disable(array($theme));
+ drupal_set_message(t('The %theme theme has been disabled.', array('%theme' => $themes[$theme]->info['name'])));
+ }
+ }
+ else {
+ drupal_set_message(t('The %theme theme was not found.', array('%theme' => $theme)), 'error');
+ }
+ drupal_goto('admin/appearance');
+ }
+ return MENU_ACCESS_DENIED;
+}
+
+/**
+ * Menu callback; Set the default theme.
+ */
+function system_theme_default() {
+ if (isset($_REQUEST['theme']) && isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'system-theme-operation-link')) {
+ $theme = $_REQUEST['theme'];
+ // Get current list of themes.
+ $themes = list_themes();
+
+ // Check if the specified theme is one recognized by the system.
+ if (!empty($themes[$theme])) {
+ // Enable the theme if it is currently disabled.
+ if (empty($themes[$theme]->status)) {
+ theme_enable(array($theme));
+ }
+ // Set the default theme.
+ variable_set('theme_default', $theme);
+
+ // Rebuild the menu. This duplicates the menu_rebuild() in theme_enable().
+ // However, modules must know the current default theme in order to use
+ // this information in hook_menu() or hook_menu_alter() implementations,
+ // and doing the variable_set() before the theme_enable() could result
+ // in a race condition where the theme is default but not enabled.
+ menu_rebuild();
+
+ // The status message depends on whether an admin theme is currently in use:
+ // a value of 0 means the admin theme is set to be the default theme.
+ $admin_theme = variable_get('admin_theme', 0);
+ if ($admin_theme != 0 && $admin_theme != $theme) {
+ drupal_set_message(t('Please note that the administration theme is still set to the %admin_theme theme; consequently, the theme on this page remains unchanged. All non-administrative sections of the site, however, will show the selected %selected_theme theme by default.', array(
+ '%admin_theme' => $themes[$admin_theme]->info['name'],
+ '%selected_theme' => $themes[$theme]->info['name'],
+ )));
+ }
+ else {
+ drupal_set_message(t('%theme is now the default theme.', array('%theme' => $themes[$theme]->info['name'])));
+ }
+ }
+ else {
+ drupal_set_message(t('The %theme theme was not found.', array('%theme' => $theme)), 'error');
+ }
+ drupal_goto('admin/appearance');
+ }
+ return MENU_ACCESS_DENIED;
+}
+
+/**
+ * Form builder; display theme configuration for entire site and individual themes.
+ *
+ * @param $key
+ * A theme name.
+ * @return
+ * The form structure.
+ * @ingroup forms
+ * @see system_theme_settings_submit()
+ */
+function system_theme_settings($form, &$form_state, $key = '') {
+ // Default settings are defined in theme_get_setting() in includes/theme.inc
+ if ($key) {
+ $var = 'theme_' . $key . '_settings';
+ $themes = list_themes();
+ $features = $themes[$key]->info['features'];
+ }
+ else {
+ $var = 'theme_settings';
+ }
+
+ $form['var'] = array('#type' => 'hidden', '#value' => $var);
+
+ // Toggle settings
+ $toggles = array(
+ 'logo' => t('Logo'),
+ 'name' => t('Site name'),
+ 'slogan' => t('Site slogan'),
+ 'node_user_picture' => t('User pictures in posts'),
+ 'comment_user_picture' => t('User pictures in comments'),
+ 'comment_user_verification' => t('User verification status in comments'),
+ 'favicon' => t('Shortcut icon'),
+ 'main_menu' => t('Main menu'),
+ 'secondary_menu' => t('Secondary menu'),
+ );
+
+ // Some features are not always available
+ $disabled = array();
+ if (!variable_get('user_pictures', 0)) {
+ $disabled['toggle_node_user_picture'] = TRUE;
+ $disabled['toggle_comment_user_picture'] = TRUE;
+ }
+ if (!module_exists('comment')) {
+ $disabled['toggle_comment_user_picture'] = TRUE;
+ $disabled['toggle_comment_user_verification'] = TRUE;
+ }
+
+ $form['theme_settings'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Toggle display'),
+ '#description' => t('Enable or disable the display of certain page elements.'),
+ );
+ foreach ($toggles as $name => $title) {
+ if ((!$key) || in_array($name, $features)) {
+ $form['theme_settings']['toggle_' . $name] = array('#type' => 'checkbox', '#title' => $title, '#default_value' => theme_get_setting('toggle_' . $name, $key));
+ // Disable checkboxes for features not supported in the current configuration.
+ if (isset($disabled['toggle_' . $name])) {
+ $form['theme_settings']['toggle_' . $name]['#disabled'] = TRUE;
+ }
+ }
+ }
+
+ if (!element_children($form['theme_settings'])) {
+ // If there is no element in the theme settings fieldset then do not show
+ // it -- but keep it in the form if another module wants to alter.
+ $form['theme_settings']['#access'] = FALSE;
+ }
+
+ // Logo settings
+ if ((!$key) || in_array('logo', $features)) {
+ $form['logo'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Logo image settings'),
+ '#description' => t('If toggled on, the following logo will be displayed.'),
+ '#attributes' => array('class' => array('theme-settings-bottom')),
+ );
+ $form['logo']['default_logo'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Use the default logo'),
+ '#default_value' => theme_get_setting('default_logo', $key),
+ '#tree' => FALSE,
+ '#description' => t('Check here if you want the theme to use the logo supplied with it.')
+ );
+ $form['logo']['settings'] = array(
+ '#type' => 'container',
+ '#states' => array(
+ // Hide the logo settings when using the default logo.
+ 'invisible' => array(
+ 'input[name="default_logo"]' => array('checked' => TRUE),
+ ),
+ ),
+ );
+ $form['logo']['settings']['logo_path'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Path to custom logo'),
+ '#description' => t('The path to the file you would like to use as your logo file instead of the default logo.'),
+ '#default_value' => theme_get_setting('logo_path', $key),
+ );
+ $form['logo']['settings']['logo_upload'] = array(
+ '#type' => 'file',
+ '#title' => t('Upload logo image'),
+ '#maxlength' => 40,
+ '#description' => t("If you don't have direct file access to the server, use this field to upload your logo.")
+ );
+ }
+
+ if ((!$key) || in_array('favicon', $features)) {
+ $form['favicon'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Shortcut icon settings'),
+ '#description' => t("Your shortcut icon, or 'favicon', is displayed in the address bar and bookmarks of most browsers."),
+ );
+ $form['favicon']['default_favicon'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Use the default shortcut icon.'),
+ '#default_value' => theme_get_setting('default_favicon', $key),
+ '#description' => t('Check here if you want the theme to use the default shortcut icon.')
+ );
+ $form['favicon']['settings'] = array(
+ '#type' => 'container',
+ '#states' => array(
+ // Hide the favicon settings when using the default favicon.
+ 'invisible' => array(
+ 'input[name="default_favicon"]' => array('checked' => TRUE),
+ ),
+ ),
+ );
+ $form['favicon']['settings']['favicon_path'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Path to custom icon'),
+ '#description' => t('The path to the image file you would like to use as your custom shortcut icon.'),
+ '#default_value' => theme_get_setting('favicon_path', $key),
+ );
+ $form['favicon']['settings']['favicon_upload'] = array(
+ '#type' => 'file',
+ '#title' => t('Upload icon image'),
+ '#description' => t("If you don't have direct file access to the server, use this field to upload your shortcut icon.")
+ );
+ }
+
+ // Inject human-friendly values for logo and favicon.
+ foreach (array('logo' => 'logo.png', 'favicon' => 'favicon.ico') as $type => $default) {
+ if (isset($form[$type]['settings'][$type . '_path'])) {
+ $element = &$form[$type]['settings'][$type . '_path'];
+
+ // If path is a public:// URI, display the path relative to the files
+ // directory; stream wrappers are not end-user friendly.
+ $original_path = $element['#default_value'];
+ $friendly_path = NULL;
+ if (file_uri_scheme($original_path) == 'public') {
+ $friendly_path = file_uri_target($original_path);
+ $element['#default_value'] = $friendly_path;
+ }
+ }
+ }
+
+ if ($key) {
+ // Call engine-specific settings.
+ $function = $themes[$key]->prefix . '_engine_settings';
+ if (function_exists($function)) {
+ $form['engine_specific'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Theme-engine-specific settings'),
+ '#description' => t('These settings only exist for the themes based on the %engine theme engine.', array('%engine' => $themes[$key]->prefix)),
+ );
+ $function($form, $form_state);
+ }
+
+ // Create a list which includes the current theme and all its base themes.
+ if (isset($themes[$key]->base_themes)) {
+ $theme_keys = array_keys($themes[$key]->base_themes);
+ $theme_keys[] = $key;
+ }
+ else {
+ $theme_keys = array($key);
+ }
+
+ // Save the name of the current theme (if any), so that we can temporarily
+ // override the current theme and allow theme_get_setting() to work
+ // without having to pass the theme name to it.
+ $default_theme = !empty($GLOBALS['theme_key']) ? $GLOBALS['theme_key'] : NULL;
+ $GLOBALS['theme_key'] = $key;
+
+ // Process the theme and all its base themes.
+ foreach ($theme_keys as $theme) {
+ // Include the theme-settings.php file.
+ $filename = DRUPAL_ROOT . '/' . str_replace("/$theme.info", '', $themes[$theme]->filename) . '/theme-settings.php';
+ if (file_exists($filename)) {
+ require_once $filename;
+ }
+
+ // Call theme-specific settings.
+ $function = $theme . '_form_system_theme_settings_alter';
+ if (function_exists($function)) {
+ $function($form, $form_state);
+ }
+ }
+
+ // Restore the original current theme.
+ if (isset($default_theme)) {
+ $GLOBALS['theme_key'] = $default_theme;
+ }
+ else {
+ unset($GLOBALS['theme_key']);
+ }
+ }
+
+ $form = system_settings_form($form);
+ // We don't want to call system_settings_form_submit(), so change #submit.
+ array_pop($form['#submit']);
+ $form['#submit'][] = 'system_theme_settings_submit';
+ $form['#validate'][] = 'system_theme_settings_validate';
+ return $form;
+}
+
+/**
+ * Validator for the system_theme_settings() form.
+ */
+function system_theme_settings_validate($form, &$form_state) {
+ // Handle file uploads.
+ $validators = array('file_validate_is_image' => array());
+
+ // Check for a new uploaded logo.
+ $file = file_save_upload('logo_upload', $validators);
+ if (isset($file)) {
+ // File upload was attempted.
+ if ($file) {
+ // Put the temporary file in form_values so we can save it on submit.
+ $form_state['values']['logo_upload'] = $file;
+ }
+ else {
+ // File upload failed.
+ form_set_error('logo_upload', t('The logo could not be uploaded.'));
+ }
+ }
+
+ $validators = array('file_validate_extensions' => array('ico png gif jpg jpeg apng svg'));
+
+ // Check for a new uploaded favicon.
+ $file = file_save_upload('favicon_upload', $validators);
+ if (isset($file)) {
+ // File upload was attempted.
+ if ($file) {
+ // Put the temporary file in form_values so we can save it on submit.
+ $form_state['values']['favicon_upload'] = $file;
+ }
+ else {
+ // File upload failed.
+ form_set_error('favicon_upload', t('The favicon could not be uploaded.'));
+ }
+ }
+
+ // If the user provided a path for a logo or favicon file, make sure a file
+ // exists at that path.
+ if ($form_state['values']['logo_path']) {
+ $path = _system_theme_settings_validate_path($form_state['values']['logo_path']);
+ if (!$path) {
+ form_set_error('logo_path', t('The custom logo path is invalid.'));
+ }
+ }
+ if ($form_state['values']['favicon_path']) {
+ $path = _system_theme_settings_validate_path($form_state['values']['favicon_path']);
+ if (!$path) {
+ form_set_error('favicon_path', t('The custom favicon path is invalid.'));
+ }
+ }
+}
+
+/**
+ * Helper function for the system_theme_settings form.
+ *
+ * Attempts to validate normal system paths, paths relative to the public files
+ * directory, or stream wrapper URIs. If the given path is any of the above,
+ * returns a valid path or URI that the theme system can display.
+ *
+ * @param $path
+ * A path relative to the Drupal root or to the public files directory, or
+ * a stream wrapper URI.
+ * @return mixed
+ * A valid path that can be displayed through the theme system, or FALSE if
+ * the path could not be validated.
+ */
+function _system_theme_settings_validate_path($path) {
+ // Absolute local file paths are invalid.
+ if (drupal_realpath($path) == $path) {
+ return FALSE;
+ }
+ // A path relative to the Drupal root or a fully qualified URI is valid.
+ if (is_file($path)) {
+ return $path;
+ }
+ // Prepend 'public://' for relative file paths within public filesystem.
+ if (file_uri_scheme($path) === FALSE) {
+ $path = 'public://' . $path;
+ }
+ if (is_file($path)) {
+ return $path;
+ }
+ return FALSE;
+}
+
+/**
+ * Process system_theme_settings form submissions.
+ */
+function system_theme_settings_submit($form, &$form_state) {
+ // Exclude unnecessary elements before saving.
+ form_state_values_clean($form_state);
+
+ $values = $form_state['values'];
+
+ // Extract the name of the theme from the submitted form values, then remove
+ // it from the array so that it is not saved as part of the variable.
+ $key = $values['var'];
+ unset($values['var']);
+
+ // If the user uploaded a new logo or favicon, save it to a permanent location
+ // and use it in place of the default theme-provided file.
+ if ($file = $values['logo_upload']) {
+ unset($values['logo_upload']);
+ $filename = file_unmanaged_copy($file->uri);
+ $values['default_logo'] = 0;
+ $values['logo_path'] = $filename;
+ $values['toggle_logo'] = 1;
+ }
+ if ($file = $values['favicon_upload']) {
+ unset($values['favicon_upload']);
+ $filename = file_unmanaged_copy($file->uri);
+ $values['default_favicon'] = 0;
+ $values['favicon_path'] = $filename;
+ $values['toggle_favicon'] = 1;
+ }
+
+ // If the user entered a path relative to the system files directory for
+ // a logo or favicon, store a public:// URI so the theme system can handle it.
+ if (!empty($values['logo_path'])) {
+ $values['logo_path'] = _system_theme_settings_validate_path($values['logo_path']);
+ }
+ if (!empty($values['favicon_path'])) {
+ $values['favicon_path'] = _system_theme_settings_validate_path($values['favicon_path']);
+ }
+
+ if (empty($values['default_favicon']) && !empty($values['favicon_path'])) {
+ $values['favicon_mimetype'] = file_get_mimetype($values['favicon_path']);
+ }
+
+ variable_set($key, $values);
+ drupal_set_message(t('The configuration options have been saved.'));
+
+ cache_clear_all();
+}
+
+/**
+ * Recursively check compatibility.
+ *
+ * @param $incompatible
+ * An associative array which at the end of the check contains all
+ * incompatible files as the keys, their values being TRUE.
+ * @param $files
+ * The set of files that will be tested.
+ * @param $file
+ * The file at which the check starts.
+ * @return
+ * Returns TRUE if an incompatible file is found, NULL (no return value)
+ * otherwise.
+ */
+function _system_is_incompatible(&$incompatible, $files, $file) {
+ if (isset($incompatible[$file->name])) {
+ return TRUE;
+ }
+ // Recursively traverse required modules, looking for incompatible modules.
+ foreach ($file->requires as $requires) {
+ if (isset($files[$requires]) && _system_is_incompatible($incompatible, $files, $files[$requires])) {
+ $incompatible[$file->name] = TRUE;
+ return TRUE;
+ }
+ }
+}
+
+/**
+ * Menu callback; provides module enable/disable interface.
+ *
+ * The list of modules gets populated by module.info files, which contain each
+ * module's name, description, and information about which modules it requires.
+ * See drupal_parse_info_file() for information on module.info descriptors.
+ *
+ * Dependency checking is performed to ensure that a module:
+ * - can not be enabled if there are disabled modules it requires.
+ * - can not be disabled if there are enabled modules which depend on it.
+ *
+ * @param $form_state
+ * An associative array containing the current state of the form.
+ *
+ * @return
+ * The form array.
+ *
+ * @ingroup forms
+ * @see theme_system_modules()
+ * @see system_modules_submit()
+ */
+function system_modules($form, $form_state = array()) {
+ // Get current list of modules.
+ $files = system_rebuild_module_data();
+
+ // Remove hidden modules from display list.
+ $visible_files = $files;
+ foreach ($visible_files as $filename => $file) {
+ if (!empty($file->info['hidden'])) {
+ unset($visible_files[$filename]);
+ }
+ }
+
+ uasort($visible_files, 'system_sort_modules_by_info_name');
+
+ // If the modules form was submitted, then system_modules_submit() runs first
+ // and if there are unfilled required modules, then $form_state['storage'] is
+ // filled, triggering a rebuild. In this case we need to display a
+ // confirmation form.
+ if (!empty($form_state['storage'])) {
+ return system_modules_confirm_form($visible_files, $form_state['storage']);
+ }
+
+ $modules = array();
+ $form['modules'] = array('#tree' => TRUE);
+
+ // Used when checking if module implements a help page.
+ $help_arg = module_exists('help') ? drupal_help_arg() : FALSE;
+
+ // Used when displaying modules that are required by the installation profile.
+ require_once DRUPAL_ROOT . '/includes/install.inc';
+ $distribution_name = check_plain(drupal_install_profile_distribution_name());
+
+ // Iterate through each of the modules.
+ foreach ($visible_files as $filename => $module) {
+ $extra = array();
+ $extra['enabled'] = (bool) $module->status;
+ if (!empty($module->info['required'] )) {
+ $extra['disabled'] = TRUE;
+ $extra['required_by'][] = $distribution_name . (!empty($module->info['explanation']) ? ' ('. $module->info['explanation'] .')' : '');
+ }
+
+ // If this module requires other modules, add them to the array.
+ foreach ($module->requires as $requires => $v) {
+ if (!isset($files[$requires])) {
+ $extra['requires'][$requires] = t('@module (<span class="admin-missing">missing</span>)', array('@module' => drupal_ucfirst($requires)));
+ $extra['disabled'] = TRUE;
+ }
+ // Only display visible modules.
+ elseif (isset($visible_files[$requires])) {
+ $requires_name = $files[$requires]->info['name'];
+ // Disable this module if it is incompatible with the dependency's version.
+ if ($incompatible_version = drupal_check_incompatibility($v, str_replace(DRUPAL_CORE_COMPATIBILITY . '-', '', $files[$requires]->info['version']))) {
+ $extra['requires'][$requires] = t('@module (<span class="admin-missing">incompatible with</span> version @version)', array(
+ '@module' => $requires_name . $incompatible_version,
+ '@version' => $files[$requires]->info['version'],
+ ));
+ $extra['disabled'] = TRUE;
+ }
+ // Disable this module if the dependency is incompatible with this
+ // version of Drupal core.
+ elseif ($files[$requires]->info['core'] != DRUPAL_CORE_COMPATIBILITY) {
+ $extra['requires'][$requires] = t('@module (<span class="admin-missing">incompatible with</span> this version of Drupal core)', array(
+ '@module' => $requires_name,
+ ));
+ $extra['disabled'] = TRUE;
+ }
+ elseif ($files[$requires]->status) {
+ $extra['requires'][$requires] = t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => $requires_name));
+ }
+ else {
+ $extra['requires'][$requires] = t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => $requires_name));
+ }
+ }
+ }
+ // Generate link for module's help page, if there is one.
+ if ($help_arg && $module->status && in_array($filename, module_implements('help'))) {
+ if (module_invoke($filename, 'help', "admin/help#$filename", $help_arg)) {
+ $extra['links']['help'] = array(
+ '#type' => 'link',
+ '#title' => t('Help'),
+ '#href' => "admin/help/$filename",
+ '#options' => array('attributes' => array('class' => array('module-link', 'module-link-help'), 'title' => t('Help'))),
+ );
+ }
+ }
+ // Generate link for module's permission, if the user has access to it.
+ if ($module->status && user_access('administer permissions') && in_array($filename, module_implements('permission'))) {
+ $extra['links']['permissions'] = array(
+ '#type' => 'link',
+ '#title' => t('Permissions'),
+ '#href' => 'admin/people/permissions',
+ '#options' => array('fragment' => 'module-' . $filename, 'attributes' => array('class' => array('module-link', 'module-link-permissions'), 'title' => t('Configure permissions'))),
+ );
+ }
+ // Generate link for module's configuration page, if the module provides
+ // one.
+ if ($module->status && isset($module->info['configure'])) {
+ $configure_link = menu_get_item($module->info['configure']);
+ if ($configure_link['access']) {
+ $extra['links']['configure'] = array(
+ '#type' => 'link',
+ '#title' => t('Configure'),
+ '#href' => $configure_link['href'],
+ '#options' => array('attributes' => array('class' => array('module-link', 'module-link-configure'), 'title' => $configure_link['description'])),
+ );
+ }
+ }
+
+ // If this module is required by other modules, list those, and then make it
+ // impossible to disable this one.
+ foreach ($module->required_by as $required_by => $v) {
+ // Hidden modules are unset already.
+ if (isset($visible_files[$required_by])) {
+ if ($files[$required_by]->status == 1 && $module->status == 1) {
+ $extra['required_by'][] = t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => $files[$required_by]->info['name']));
+ $extra['disabled'] = TRUE;
+ }
+ else {
+ $extra['required_by'][] = t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => $files[$required_by]->info['name']));
+ }
+ }
+ }
+ $form['modules'][$module->info['package']][$filename] = _system_modules_build_row($module->info, $extra);
+ }
+
+ // Add basic information to the fieldsets.
+ foreach (element_children($form['modules']) as $package) {
+ $form['modules'][$package] += array(
+ '#type' => 'fieldset',
+ '#title' => t($package),
+ '#collapsible' => TRUE,
+ '#theme' => 'system_modules_fieldset',
+ '#header' => array(
+ array('data' => t('Enabled'), 'class' => array('checkbox')),
+ t('Name'),
+ t('Version'),
+ t('Description'),
+ array('data' => t('Operations'), 'colspan' => 3),
+ ),
+ // Ensure that the "Core" package fieldset comes first.
+ '#weight' => $package == 'Core' ? -10 : NULL,
+ );
+ }
+
+ // Lastly, sort all fieldsets by title.
+ uasort($form['modules'], 'element_sort_by_title');
+
+ $form['actions'] = array('#type' => 'actions');
+ $form['actions']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save configuration'),
+ );
+ $form['#action'] = url('admin/modules/list/confirm');
+
+ return $form;
+}
+
+/**
+ * Array sorting callback; sorts modules or themes by their name.
+ */
+function system_sort_modules_by_info_name($a, $b) {
+ return strcasecmp($a->info['name'], $b->info['name']);
+}
+
+/**
+ * Array sorting callback; sorts modules or themes by their name.
+ */
+function system_sort_themes($a, $b) {
+ if ($a->is_default) {
+ return -1;
+ }
+ if ($b->is_default) {
+ return 1;
+ }
+ return strcasecmp($a->info['name'], $b->info['name']);
+}
+
+/**
+ * Build a table row for the system modules page.
+ */
+function _system_modules_build_row($info, $extra) {
+ // Add in the defaults.
+ $extra += array(
+ 'requires' => array(),
+ 'required_by' => array(),
+ 'disabled' => FALSE,
+ 'enabled' => FALSE,
+ 'links' => array(),
+ );
+ $form = array(
+ '#tree' => TRUE,
+ );
+ // Set the basic properties.
+ $form['name'] = array(
+ '#markup' => $info['name'],
+ );
+ $form['description'] = array(
+ '#markup' => t($info['description']),
+ );
+ $form['version'] = array(
+ '#markup' => $info['version'],
+ );
+ $form['#requires'] = $extra['requires'];
+ $form['#required_by'] = $extra['required_by'];
+
+ // Check the compatibilities.
+ $compatible = TRUE;
+ $status_short = '';
+ $status_long = '';
+
+ // Check the core compatibility.
+ if (!isset($info['core']) || $info['core'] != DRUPAL_CORE_COMPATIBILITY) {
+ $compatible = FALSE;
+ $status_short .= t('Incompatible with this version of Drupal core.');
+ $status_long .= t('This version is not compatible with Drupal !core_version and should be replaced.', array('!core_version' => DRUPAL_CORE_COMPATIBILITY));
+ }
+
+ // Ensure this module is compatible with the currently installed version of PHP.
+ if (version_compare(phpversion(), $info['php']) < 0) {
+ $compatible = FALSE;
+ $status_short .= t('Incompatible with this version of PHP');
+ $php_required = $info['php'];
+ if (substr_count($info['php'], '.') < 2) {
+ $php_required .= '.*';
+ }
+ $status_long .= t('This module requires PHP version @php_required and is incompatible with PHP version !php_version.', array('@php_required' => $php_required, '!php_version' => phpversion()));
+ }
+
+ // If this module is compatible, present a checkbox indicating
+ // this module may be installed. Otherwise, show a big red X.
+ if ($compatible) {
+ $form['enable'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Enable'),
+ '#default_value' => $extra['enabled'],
+ );
+ if ($extra['disabled']) {
+ $form['enable']['#disabled'] = TRUE;
+ }
+ }
+ else {
+ $form['enable'] = array(
+ '#markup' => theme('image', array('path' => 'misc/watchdog-error.png', 'alt' => $status_short, 'title' => $status_short)),
+ );
+ $form['description']['#markup'] .= theme('system_modules_incompatible', array('message' => $status_long));
+ }
+
+ // Build operation links.
+ foreach (array('help', 'permissions', 'configure') as $key) {
+ $form['links'][$key] = (isset($extra['links'][$key]) ? $extra['links'][$key] : array());
+ }
+
+ return $form;
+}
+
+/**
+ * Display confirmation form for required modules.
+ *
+ * @param $modules
+ * Array of module file objects as returned from system_rebuild_module_data().
+ * @param $storage
+ * The contents of $form_state['storage']; an array with two
+ * elements: the list of required modules and the list of status
+ * form field values from the previous screen.
+ * @ingroup forms
+ */
+function system_modules_confirm_form($modules, $storage) {
+ $items = array();
+
+ $form['validation_modules'] = array('#type' => 'value', '#value' => $modules);
+ $form['status']['#tree'] = TRUE;
+
+ foreach ($storage['more_required'] as $info) {
+ $t_argument = array(
+ '@module' => $info['name'],
+ '@required' => implode(', ', $info['requires']),
+ );
+ $items[] = format_plural(count($info['requires']), 'You must enable the @required module to install @module.', 'You must enable the @required modules to install @module.', $t_argument);
+ }
+
+ foreach ($storage['missing_modules'] as $name => $info) {
+ $t_argument = array(
+ '@module' => $name,
+ '@depends' => implode(', ', $info['depends']),
+ );
+ $items[] = format_plural(count($info['depends']), 'The @module module is missing, so the following module will be disabled: @depends.', 'The @module module is missing, so the following modules will be disabled: @depends.', $t_argument);
+ }
+
+ $form['text'] = array('#markup' => theme('item_list', array('items' => $items)));
+
+ if ($form) {
+ // Set some default form values
+ $form = confirm_form(
+ $form,
+ t('Some required modules must be enabled'),
+ 'admin/modules',
+ t('Would you like to continue with the above?'),
+ t('Continue'),
+ t('Cancel'));
+ return $form;
+ }
+}
+
+/**
+ * Submit callback; handles modules form submission.
+ */
+function system_modules_submit($form, &$form_state) {
+ include_once DRUPAL_ROOT . '/includes/install.inc';
+
+ // Builds list of modules.
+ $modules = array();
+ // If we're not coming from the confirmation form, build the list of modules.
+ if (empty($form_state['storage'])) {
+ // If we're not coming from the confirmation form, build the module list.
+ foreach ($form_state['values']['modules'] as $group_name => $group) {
+ foreach ($group as $module => $enabled) {
+ $modules[$module] = array('group' => $group_name, 'enabled' => $enabled['enable']);
+ }
+ }
+ }
+ else {
+ // If we are coming from the confirmation form, fetch
+ // the modules out of $form_state.
+ $modules = $form_state['storage']['modules'];
+ }
+
+ // Collect data for all modules to be able to determine dependencies.
+ $files = system_rebuild_module_data();
+
+ // Sorts modules by weight.
+ $sort = array();
+ foreach (array_keys($modules) as $module) {
+ $sort[$module] = $files[$module]->sort;
+ }
+ array_multisort($sort, $modules);
+
+ // Makes sure all required modules are set to be enabled.
+ $more_required = array();
+ $missing_modules = array();
+ foreach ($modules as $name => $module) {
+ if ($module['enabled']) {
+ // Checks that all dependencies are set to be enabled. Stores the ones
+ // that are not in $dependencies variable so that the user can be alerted
+ // in the confirmation form that more modules need to be enabled.
+ $dependencies = array();
+ foreach (array_keys($files[$name]->requires) as $required) {
+ if (empty($modules[$required]['enabled'])) {
+ if (isset($files[$required])) {
+ $dependencies[] = $files[$required]->info['name'];
+ $modules[$required]['enabled'] = TRUE;
+ }
+ else {
+ $missing_modules[$required]['depends'][] = $name;
+ $modules[$name]['enabled'] = FALSE;
+ }
+ }
+ }
+
+ // Stores additional modules that need to be enabled in $more_required.
+ if (!empty($dependencies)) {
+ $more_required[$name] = array(
+ 'name' => $files[$name]->info['name'],
+ 'requires' => $dependencies,
+ );
+ }
+ }
+ }
+
+ // Redirects to confirmation form if more modules need to be enabled.
+ if ((!empty($more_required) || !empty($missing_modules)) && !isset($form_state['values']['confirm'])) {
+ $form_state['storage'] = array(
+ 'more_required' => $more_required,
+ 'modules' => $modules,
+ 'missing_modules' => $missing_modules,
+ );
+ $form_state['rebuild'] = TRUE;
+ return;
+ }
+
+ // Invokes hook_requirements('install'). If failures are detected, makes sure
+ // the dependent modules aren't installed either.
+ foreach ($modules as $name => $module) {
+ // Only invoke hook_requirements() on modules that are going to be installed.
+ if ($module['enabled'] && drupal_get_installed_schema_version($name) == SCHEMA_UNINSTALLED) {
+ if (!drupal_check_module($name)) {
+ $modules[$name]['enabled'] = FALSE;
+ foreach (array_keys($files[$name]->required_by) as $required_by) {
+ $modules[$required_by]['enabled'] = FALSE;
+ }
+ }
+ }
+ }
+
+ // Initializes array of actions.
+ $actions = array(
+ 'enable' => array(),
+ 'disable' => array(),
+ 'install' => array(),
+ );
+
+ // Builds arrays of modules that need to be enabled, disabled, and installed.
+ foreach ($modules as $name => $module) {
+ if ($module['enabled']) {
+ if (drupal_get_installed_schema_version($name) == SCHEMA_UNINSTALLED) {
+ $actions['install'][] = $name;
+ $actions['enable'][] = $name;
+ }
+ elseif (!module_exists($name)) {
+ $actions['enable'][] = $name;
+ }
+ }
+ elseif (module_exists($name)) {
+ $actions['disable'][] = $name;
+ }
+ }
+
+ // Gets list of modules prior to install process, unsets $form_state['storage']
+ // so we don't get redirected back to the confirmation form.
+ $pre_install_list = module_list();
+ unset($form_state['storage']);
+
+ // Reverse the 'enable' list, to order dependencies before dependents.
+ krsort($actions['enable']);
+
+ // Installs, enables, and disables modules.
+ module_enable($actions['enable'], FALSE);
+ module_disable($actions['disable'], FALSE);
+
+ // Gets module list after install process, flushes caches and displays a
+ // message if there are changes.
+ $post_install_list = module_list(TRUE);
+ if ($pre_install_list != $post_install_list) {
+ drupal_flush_all_caches();
+ drupal_set_message(t('The configuration options have been saved.'));
+ }
+
+ $form_state['redirect'] = 'admin/modules';
+}
+
+/**
+ * Uninstall functions
+ */
+
+/**
+ * Builds a form of currently disabled modules.
+ *
+ * @ingroup forms
+ * @see system_modules_uninstall_validate()
+ * @see system_modules_uninstall_submit()
+ * @param $form_state['values']
+ * Submitted form values.
+ * @return
+ * A form array representing the currently disabled modules.
+ */
+function system_modules_uninstall($form, $form_state = NULL) {
+ // Make sure the install API is available.
+ include_once DRUPAL_ROOT . '/includes/install.inc';
+
+ // Display the confirm form if any modules have been submitted.
+ if (!empty($form_state['storage']) && $confirm_form = system_modules_uninstall_confirm_form($form_state['storage'])) {
+ return $confirm_form;
+ }
+
+ // Get a list of disabled, installed modules.
+ $all_modules = system_rebuild_module_data();
+ $disabled_modules = array();
+ foreach ($all_modules as $name => $module) {
+ if (empty($module->status) && $module->schema_version > SCHEMA_UNINSTALLED) {
+ $disabled_modules[$name] = $module;
+ }
+ }
+
+ // Only build the rest of the form if there are any modules available to
+ // uninstall.
+ if (!empty($disabled_modules)) {
+ $profile = drupal_get_profile();
+ uasort($disabled_modules, 'system_sort_modules_by_info_name');
+ $form['uninstall'] = array('#tree' => TRUE);
+ foreach ($disabled_modules as $module) {
+ $module_name = $module->info['name'] ? $module->info['name'] : $module->name;
+ $form['modules'][$module->name]['#module_name'] = $module_name;
+ $form['modules'][$module->name]['name']['#markup'] = $module_name;
+ $form['modules'][$module->name]['description']['#markup'] = t($module->info['description']);
+ $form['uninstall'][$module->name] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Uninstall @module module', array('@module' => $module_name)),
+ '#title_display' => 'invisible',
+ );
+ // All modules which depend on this one must be uninstalled first, before
+ // we can allow this module to be uninstalled. (The installation profile
+ // is excluded from this list.)
+ foreach (array_keys($module->required_by) as $dependent) {
+ if ($dependent != $profile && drupal_get_installed_schema_version($dependent) != SCHEMA_UNINSTALLED) {
+ $dependent_name = isset($all_modules[$dependent]->info['name']) ? $all_modules[$dependent]->info['name'] : $dependent;
+ $form['modules'][$module->name]['#required_by'][] = $dependent_name;
+ $form['uninstall'][$module->name]['#disabled'] = TRUE;
+ }
+ }
+ }
+ $form['actions'] = array('#type' => 'actions');
+ $form['actions']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Uninstall'),
+ );
+ $form['#action'] = url('admin/modules/uninstall/confirm');
+ }
+ else {
+ $form['modules'] = array();
+ }
+
+ return $form;
+}
+
+/**
+ * Confirm uninstall of selected modules.
+ *
+ * @ingroup forms
+ * @param $storage
+ * An associative array of modules selected to be uninstalled.
+ * @return
+ * A form array representing modules to confirm.
+ */
+function system_modules_uninstall_confirm_form($storage) {
+ // Nothing to build.
+ if (empty($storage)) {
+ return;
+ }
+
+ // Construct the hidden form elements and list items.
+ foreach (array_filter($storage['uninstall']) as $module => $value) {
+ $info = drupal_parse_info_file(drupal_get_path('module', $module) . '/' . $module . '.info');
+ $uninstall[] = $info['name'];
+ $form['uninstall'][$module] = array('#type' => 'hidden',
+ '#value' => 1,
+ );
+ }
+
+ // Display a confirm form if modules have been selected.
+ if (isset($uninstall)) {
+ $form['#confirmed'] = TRUE;
+ $form['uninstall']['#tree'] = TRUE;
+ $form['modules'] = array('#markup' => '<p>' . t('The following modules will be completely uninstalled from your site, and <em>all data from these modules will be lost</em>!') . '</p>' . theme('item_list', array('items' => $uninstall)));
+ $form = confirm_form(
+ $form,
+ t('Confirm uninstall'),
+ 'admin/modules/uninstall',
+ t('Would you like to continue with uninstalling the above?'),
+ t('Uninstall'),
+ t('Cancel'));
+ return $form;
+ }
+}
+
+/**
+ * Validates the submitted uninstall form.
+ */
+function system_modules_uninstall_validate($form, &$form_state) {
+ // Form submitted, but no modules selected.
+ if (!count(array_filter($form_state['values']['uninstall']))) {
+ drupal_set_message(t('No modules selected.'), 'error');
+ drupal_goto('admin/modules/uninstall');
+ }
+}
+
+/**
+ * Processes the submitted uninstall form.
+ */
+function system_modules_uninstall_submit($form, &$form_state) {
+ // Make sure the install API is available.
+ include_once DRUPAL_ROOT . '/includes/install.inc';
+
+ if (!empty($form['#confirmed'])) {
+ // Call the uninstall routine for each selected module.
+ $modules = array_keys($form_state['values']['uninstall']);
+ drupal_uninstall_modules($modules);
+ drupal_set_message(t('The selected modules have been uninstalled.'));
+
+ $form_state['redirect'] = 'admin/modules/uninstall';
+ }
+ else {
+ $form_state['storage'] = $form_state['values'];
+ $form_state['rebuild'] = TRUE;
+ }
+}
+
+/**
+ * Menu callback. Display blocked IP addresses.
+ *
+ * @param $default_ip
+ * Optional IP address to be passed on to drupal_get_form() for
+ * use as the default value of the IP address form field.
+ */
+function system_ip_blocking($default_ip = '') {
+ $rows = array();
+ $header = array(t('Blocked IP addresses'), t('Operations'));
+ $result = db_query('SELECT * FROM {blocked_ips}');
+ foreach ($result as $ip) {
+ $rows[] = array(
+ $ip->ip,
+ l(t('delete'), "admin/config/people/ip-blocking/delete/$ip->iid"),
+ );
+ }
+
+ $build['system_ip_blocking_form'] = drupal_get_form('system_ip_blocking_form', $default_ip);
+
+ $build['system_ip_blocking_table'] = array(
+ '#theme' => 'table',
+ '#header' => $header,
+ '#rows' => $rows,
+ '#empty' => t('No blocked IP addresses available.'),
+ );
+
+ return $build;
+}
+
+/**
+ * Define the form for blocking IP addresses.
+ *
+ * @ingroup forms
+ * @see system_ip_blocking_form_validate()
+ * @see system_ip_blocking_form_submit()
+ */
+function system_ip_blocking_form($form, $form_state, $default_ip) {
+ $form['ip'] = array(
+ '#title' => t('IP address'),
+ '#type' => 'textfield',
+ '#size' => 48,
+ '#maxlength' => 40,
+ '#default_value' => $default_ip,
+ '#description' => t('Enter a valid IP address.'),
+ );
+ $form['actions'] = array('#type' => 'actions');
+ $form['actions']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Add'),
+ );
+ $form['#submit'][] = 'system_ip_blocking_form_submit';
+ $form['#validate'][] = 'system_ip_blocking_form_validate';
+ return $form;
+}
+
+function system_ip_blocking_form_validate($form, &$form_state) {
+ $ip = trim($form_state['values']['ip']);
+ if (db_query("SELECT * FROM {blocked_ips} WHERE ip = :ip", array(':ip' => $ip))->fetchField()) {
+ form_set_error('ip', t('This IP address is already blocked.'));
+ }
+ elseif ($ip == ip_address()) {
+ form_set_error('ip', t('You may not block your own IP address.'));
+ }
+ elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) == FALSE) {
+ form_set_error('ip', t('Enter a valid IP address.'));
+ }
+}
+
+function system_ip_blocking_form_submit($form, &$form_state) {
+ $ip = trim($form_state['values']['ip']);
+ db_insert('blocked_ips')
+ ->fields(array('ip' => $ip))
+ ->execute();
+ drupal_set_message(t('The IP address %ip has been blocked.', array('%ip' => $ip)));
+ $form_state['redirect'] = 'admin/config/people/ip-blocking';
+ return;
+}
+
+/**
+ * IP deletion confirm page.
+ *
+ * @see system_ip_blocking_delete_submit()
+ */
+function system_ip_blocking_delete($form, &$form_state, $iid) {
+ $form['blocked_ip'] = array(
+ '#type' => 'value',
+ '#value' => $iid,
+ );
+ return confirm_form($form, t('Are you sure you want to delete %ip?', array('%ip' => $iid['ip'])), 'admin/config/people/ip-blocking', t('This action cannot be undone.'), t('Delete'), t('Cancel'));
+}
+
+/**
+ * Process system_ip_blocking_delete form submissions.
+ */
+function system_ip_blocking_delete_submit($form, &$form_state) {
+ $blocked_ip = $form_state['values']['blocked_ip'];
+ db_delete('blocked_ips')
+ ->condition('iid', $blocked_ip['iid'])
+ ->execute();
+ watchdog('user', 'Deleted %ip', array('%ip' => $blocked_ip['ip']));
+ drupal_set_message(t('The IP address %ip was deleted.', array('%ip' => $blocked_ip['ip'])));
+ $form_state['redirect'] = 'admin/config/people/ip-blocking';
+}
+
+/**
+ * Form builder; The general site information form.
+ *
+ * @ingroup forms
+ * @see system_settings_form()
+ */
+function system_site_information_settings() {
+ $form['site_information'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Site details'),
+ );
+ $form['site_information']['site_name'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Site name'),
+ '#default_value' => variable_get('site_name', 'Drupal'),
+ '#required' => TRUE
+ );
+ $form['site_information']['site_slogan'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Slogan'),
+ '#default_value' => variable_get('site_slogan', ''),
+ '#description' => t("How this is used depends on your site's theme."),
+ );
+ $form['site_information']['site_mail'] = array(
+ '#type' => 'textfield',
+ '#title' => t('E-mail address'),
+ '#default_value' => variable_get('site_mail', ini_get('sendmail_from')),
+ '#description' => t("The <em>From</em> address in automated e-mails sent during registration and new password requests, and other notifications. (Use an address ending in your site's domain to help prevent this e-mail being flagged as spam.)"),
+ '#required' => TRUE,
+ );
+ $form['front_page'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Front page'),
+ );
+ $form['front_page']['default_nodes_main'] = array(
+ '#type' => 'select', '#title' => t('Number of posts on front page'),
+ '#default_value' => variable_get('default_nodes_main', 10),
+ '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30)),
+ '#description' => t('The maximum number of posts displayed on overview pages such as the front page.')
+ );
+ $form['front_page']['site_frontpage'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Default front page'),
+ '#default_value' => (variable_get('site_frontpage')!='node'?drupal_get_path_alias(variable_get('site_frontpage', 'node')):''),
+ '#size' => 40,
+ '#description' => t('Optionally, specify a relative URL to display as the front page. Leave blank to display the default content feed.'),
+ '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='),
+ );
+ $form['error_page'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Error pages'),
+ );
+ $form['error_page']['site_403'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Default 403 (access denied) page'),
+ '#default_value' => variable_get('site_403', ''),
+ '#size' => 40,
+ '#description' => t('This page is displayed when the requested document is denied to the current user. Leave blank to display a generic "access denied" page.'),
+ '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=')
+ );
+ $form['error_page']['site_404'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Default 404 (not found) page'),
+ '#default_value' => variable_get('site_404', ''),
+ '#size' => 40,
+ '#description' => t('This page is displayed when no other content matches the requested document. Leave blank to display a generic "page not found" page.'),
+ '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=')
+ );
+
+ $form['#validate'][] = 'system_site_information_settings_validate';
+
+ return system_settings_form($form);
+}
+
+/**
+ * Validates the submitted site-information form.
+ */
+function system_site_information_settings_validate($form, &$form_state) {
+ // Validate the e-mail address.
+ if ($error = user_validate_mail($form_state['values']['site_mail'])) {
+ form_set_error('site_mail', $error);
+ }
+ // Check for empty front page path.
+ if (empty($form_state['values']['site_frontpage'])) {
+ // Set to default "node".
+ form_set_value($form['front_page']['site_frontpage'], 'node', $form_state);
+ }
+ else {
+ // Get the normal path of the front page.
+ form_set_value($form['front_page']['site_frontpage'], drupal_get_normal_path($form_state['values']['site_frontpage']), $form_state);
+ }
+ // Validate front page path.
+ if (!drupal_valid_path($form_state['values']['site_frontpage'])) {
+ form_set_error('site_frontpage', t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_frontpage'])));
+ }
+ // Get the normal paths of both error pages.
+ if (!empty($form_state['values']['site_403'])) {
+ form_set_value($form['error_page']['site_403'], drupal_get_normal_path($form_state['values']['site_403']), $form_state);
+ }
+ if (!empty($form_state['values']['site_404'])) {
+ form_set_value($form['error_page']['site_404'], drupal_get_normal_path($form_state['values']['site_404']), $form_state);
+ }
+ // Validate 403 error path.
+ if (!empty($form_state['values']['site_403']) && !drupal_valid_path($form_state['values']['site_403'])) {
+ form_set_error('site_403', t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_403'])));
+ }
+ // Validate 404 error path.
+ if (!empty($form_state['values']['site_404']) && !drupal_valid_path($form_state['values']['site_404'])) {
+ form_set_error('site_404', t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_404'])));
+ }
+}
+
+/**
+ * Form builder; Cron form.
+ *
+ * @see system_settings_form()
+ * @ingroup forms
+ */
+function system_cron_settings() {
+ global $base_url;
+ $form['description'] = array(
+ '#markup' => '<p>' . t('Cron takes care of running periodic tasks like checking for updates and indexing content for search.') . '</p>',
+ );
+ $form['run'] = array(
+ '#type' => 'submit',
+ '#value' => t('Run cron'),
+ '#submit' => array('system_run_cron_submit'),
+ );
+ $status = '<p>' . t('Last run: %cron-last ago.', array('%cron-last' => format_interval(REQUEST_TIME - variable_get('cron_last')),)) . '</p>';
+ $form['status'] = array(
+ '#markup' => $status,
+ );
+
+ $form['cron_url'] = array(
+ '#markup' => '<p>' . t('To run cron from outside the site, go to <a href="!cron">!cron</a>', array('!cron' => url($base_url . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => variable_get('cron_key', 'drupal')))))) . '</p>',
+ );
+
+ $form['cron'] = array(
+ '#type' => 'fieldset',
+ );
+ $form['cron']['cron_safe_threshold'] = array(
+ '#type' => 'select',
+ '#title' => t('Run cron every'),
+ '#description' => t('More information about setting up scheduled tasks can be found by <a href="@url">reading the cron tutorial on drupal.org</a>.', array('@url' => url('http://drupal.org/cron'))),
+ '#default_value' => variable_get('cron_safe_threshold', DRUPAL_CRON_DEFAULT_THRESHOLD),
+ '#options' => array(0 => t('Never')) + drupal_map_assoc(array(3600, 10800, 21600, 43200, 86400, 604800), 'format_interval'),
+ );
+
+ return system_settings_form($form);
+}
+
+/**
+ * Submit callback; run cron.
+ *
+ * @ingroup forms
+ */
+function system_run_cron_submit($form, &$form_state) {
+ // Run cron manually from Cron form.
+ if (drupal_cron_run()) {
+ drupal_set_message(t('Cron run successfully.'));
+ }
+ else {
+ drupal_set_message(t('Cron run failed.'), 'error');
+ }
+
+ drupal_goto('admin/config/system/cron');
+}
+
+/**
+ * Form builder; Configure error reporting settings.
+ *
+ * @ingroup forms
+ * @see system_settings_form()
+ */
+function system_logging_settings() {
+ $form['error_level'] = array(
+ '#type' => 'radios',
+ '#title' => t('Error messages to display'),
+ '#default_value' => variable_get('error_level', ERROR_REPORTING_DISPLAY_ALL),
+ '#options' => array(
+ ERROR_REPORTING_HIDE => t('None'),
+ ERROR_REPORTING_DISPLAY_SOME => t('Errors and warnings'),
+ ERROR_REPORTING_DISPLAY_ALL => t('All messages'),
+ ),
+ '#description' => t('It is recommended that sites running on production environments do not display any errors.'),
+ );
+
+ return system_settings_form($form);
+}
+
+/**
+ * Form builder; Configure site performance settings.
+ *
+ * @ingroup forms
+ * @see system_settings_form()
+ */
+function system_performance_settings() {
+ drupal_add_js(drupal_get_path('module', 'system') . '/system.js');
+
+ $form['clear_cache'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Clear cache'),
+ );
+
+ $form['clear_cache']['clear'] = array(
+ '#type' => 'submit',
+ '#value' => t('Clear all caches'),
+ '#submit' => array('system_clear_cache_submit'),
+ );
+
+ $form['caching'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Caching'),
+ );
+
+ $cache = variable_get('cache', 0);
+ $form['caching']['cache'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Cache pages for anonymous users'),
+ '#default_value' => $cache,
+ '#weight' => -2,
+ );
+ $period = drupal_map_assoc(array(0, 60, 180, 300, 600, 900, 1800, 2700, 3600, 10800, 21600, 32400, 43200, 86400), 'format_interval');
+ $period[0] = '<' . t('none') . '>';
+ $form['caching']['cache_lifetime'] = array(
+ '#type' => 'select',
+ '#title' => t('Minimum cache lifetime'),
+ '#default_value' => variable_get('cache_lifetime', 0),
+ '#options' => $period,
+ '#description' => t('Cached pages will not be re-created until at least this much time has elapsed.')
+ );
+ $form['caching']['page_cache_maximum_age'] = array(
+ '#type' => 'select',
+ '#title' => t('Expiration of cached pages'),
+ '#default_value' => variable_get('page_cache_maximum_age', 0),
+ '#options' => $period,
+ '#description' => t('The maximum time an external cache can use an old version of a page.')
+ );
+
+ $directory = 'public://';
+ $is_writable = is_dir($directory) && is_writable($directory);
+ $disabled = !$is_writable;
+ $disabled_message = '';
+ if (!$is_writable) {
+ $disabled_message = ' ' . t('<strong class="error">Set up the <a href="!file-system">public files directory</a> to make these optimizations available.</strong>', array('!file-system' => url('admin/config/media/file-system')));
+ }
+
+ $form['bandwidth_optimization'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Bandwidth optimization'),
+ '#description' => t('External resources can be optimized automatically, which can reduce both the size and number of requests made to your website.') . $disabled_message,
+ );
+
+ $js_hide = $cache ? '' : ' class="js-hide"';
+ $form['bandwidth_optimization']['page_compression'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Compress cached pages.'),
+ '#default_value' => variable_get('page_compression', TRUE),
+ '#prefix' => '<div id="page-compression-wrapper"' . $js_hide . '>',
+ '#suffix' => '</div>',
+ );
+ $form['bandwidth_optimization']['preprocess_css'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Aggregate and compress CSS files.'),
+ '#default_value' => intval(variable_get('preprocess_css', 0) && $is_writable),
+ '#disabled' => $disabled,
+ );
+ $form['bandwidth_optimization']['preprocess_js'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Aggregate JavaScript files.'),
+ '#default_value' => intval(variable_get('preprocess_js', 0) && $is_writable),
+ '#disabled' => $disabled,
+ );
+
+ $form['#submit'][] = 'drupal_clear_css_cache';
+ $form['#submit'][] = 'drupal_clear_js_cache';
+ // This form allows page compression settings to be changed, which can
+ // invalidate the page cache, so it needs to be cleared on form submit.
+ $form['#submit'][] = 'system_clear_page_cache_submit';
+
+ return system_settings_form($form);
+}
+
+/**
+ * Submit callback; clear system caches.
+ *
+ * @ingroup forms
+ */
+function system_clear_cache_submit($form, &$form_state) {
+ drupal_flush_all_caches();
+ drupal_set_message(t('Caches cleared.'));
+}
+
+/**
+ * Submit callback; clear the page cache.
+ *
+ * @ingroup forms
+ */
+function system_clear_page_cache_submit($form, &$form_state) {
+ cache_clear_all('*', 'cache_page', TRUE);
+}
+
+/**
+ * Form builder; Configure the site file handling.
+ *
+ * @ingroup forms
+ * @see system_settings_form()
+ */
+function system_file_system_settings() {
+ $form['file_public_path'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Public file system path'),
+ '#default_value' => variable_get('file_public_path', conf_path() . '/files'),
+ '#maxlength' => 255,
+ '#description' => t('A local file system path where public files will be stored. This directory must exist and be writable by Drupal. This directory must be relative to the Drupal installation directory and be accessible over the web.'),
+ '#after_build' => array('system_check_directory'),
+ );
+
+ $form['file_private_path'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Private file system path'),
+ '#default_value' => variable_get('file_private_path', ''),
+ '#maxlength' => 255,
+ '#description' => t('An existing local file system path for storing private files. It should be writable by Drupal and not accessible over the web. See the online handbook for <a href="@handbook">more information about securing private files</a>.', array('@handbook' => 'http://drupal.org/documentation/modules/file')),
+ '#after_build' => array('system_check_directory'),
+ );
+
+ $form['file_temporary_path'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Temporary directory'),
+ '#default_value' => variable_get('file_temporary_path', file_directory_temp()),
+ '#maxlength' => 255,
+ '#description' => t('A local file system path where temporary files will be stored. This directory should not be accessible over the web.'),
+ '#after_build' => array('system_check_directory'),
+ );
+ // Any visible, writeable wrapper can potentially be used for the files
+ // directory, including a remote file system that integrates with a CDN.
+ foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) {
+ $options[$scheme] = check_plain($info['description']);
+ }
+
+ if (!empty($options)) {
+ $form['file_default_scheme'] = array(
+ '#type' => 'radios',
+ '#title' => t('Default download method'),
+ '#default_value' => variable_get('file_default_scheme', isset($options['public']) ? 'public' : key($options)),
+ '#options' => $options,
+ '#description' => t('This setting is used as the preferred download method. The use of public files is more efficient, but does not provide any access control.'),
+ );
+ }
+
+ return system_settings_form($form);
+}
+
+/**
+ * Form builder; Configure site image toolkit usage.
+ *
+ * @ingroup forms
+ * @see system_settings_form()
+ */
+function system_image_toolkit_settings() {
+ $toolkits_available = image_get_available_toolkits();
+ $current_toolkit = image_get_toolkit();
+
+ if (count($toolkits_available) == 0) {
+ variable_del('image_toolkit');
+ $form['image_toolkit_help'] = array(
+ '#markup' => t("No image toolkits were detected. Drupal includes support for <a href='!gd-link'>PHP's built-in image processing functions</a> but they were not detected on this system. You should consult your system administrator to have them enabled, or try using a third party toolkit.", array('gd-link' => url('http://php.net/gd'))),
+ );
+ return $form;
+ }
+
+ if (count($toolkits_available) > 1) {
+ $form['image_toolkit'] = array(
+ '#type' => 'radios',
+ '#title' => t('Select an image processing toolkit'),
+ '#default_value' => variable_get('image_toolkit', $current_toolkit),
+ '#options' => $toolkits_available
+ );
+ }
+ else {
+ variable_set('image_toolkit', key($toolkits_available));
+ }
+
+ // Get the toolkit's settings form.
+ $function = 'image_' . $current_toolkit . '_settings';
+ if (function_exists($function)) {
+ $form['image_toolkit_settings'] = $function();
+ }
+
+ return system_settings_form($form);
+}
+
+/**
+ * Form builder; Configure how the site handles RSS feeds.
+ *
+ * @ingroup forms
+ * @see system_settings_form()
+ */
+function system_rss_feeds_settings() {
+ $form['feed_description'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Feed description'),
+ '#default_value' => variable_get('feed_description', ''),
+ '#description' => t('Description of your site, included in each feed.')
+ );
+ $form['feed_default_items'] = array(
+ '#type' => 'select',
+ '#title' => t('Number of items in each feed'),
+ '#default_value' => variable_get('feed_default_items', 10),
+ '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30)),
+ '#description' => t('Default number of items to include in each feed.')
+ );
+ $form['feed_item_length'] = array(
+ '#type' => 'select',
+ '#title' => t('Feed content'),
+ '#default_value' => variable_get('feed_item_length', 'fulltext'),
+ '#options' => array('title' => t('Titles only'), 'teaser' => t('Titles plus teaser'), 'fulltext' => t('Full text')),
+ '#description' => t('Global setting for the default display of content items in each feed.')
+ );
+
+ return system_settings_form($form);
+}
+
+/**
+ * Form builder; Configure the site regional settings.
+ *
+ * @ingroup forms
+ * @see system_settings_form()
+ * @see system_regional_settings_submit()
+ */
+function system_regional_settings() {
+ include_once DRUPAL_ROOT . '/includes/locale.inc';
+ $countries = country_get_list();
+
+ // Date settings:
+ $zones = system_time_zones();
+
+ $form['locale'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Locale'),
+ );
+
+ $form['locale']['site_default_country'] = array(
+ '#type' => 'select',
+ '#title' => t('Default country'),
+ '#empty_value' => '',
+ '#default_value' => variable_get('site_default_country', ''),
+ '#options' => $countries,
+ '#attributes' => array('class' => array('country-detect')),
+ );
+
+ $form['locale']['date_first_day'] = array(
+ '#type' => 'select',
+ '#title' => t('First day of week'),
+ '#default_value' => variable_get('date_first_day', 0),
+ '#options' => array(0 => t('Sunday'), 1 => t('Monday'), 2 => t('Tuesday'), 3 => t('Wednesday'), 4 => t('Thursday'), 5 => t('Friday'), 6 => t('Saturday')),
+ );
+
+ $form['timezone'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Time zones'),
+ );
+
+ $form['timezone']['date_default_timezone'] = array(
+ '#type' => 'select',
+ '#title' => t('Default time zone'),
+ '#default_value' => variable_get('date_default_timezone', date_default_timezone_get()),
+ '#options' => $zones,
+ );
+
+ $configurable_timezones = variable_get('configurable_timezones', 1);
+ $form['timezone']['configurable_timezones'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Users may set their own time zone.'),
+ '#default_value' => $configurable_timezones,
+ );
+
+ $form['timezone']['configurable_timezones_wrapper'] = array(
+ '#type' => 'container',
+ '#states' => array(
+ // Hide the user configured timezone settings when users are forced to use
+ // the default setting.
+ 'invisible' => array(
+ 'input[name="configurable_timezones"]' => array('checked' => FALSE),
+ ),
+ ),
+ );
+ $form['timezone']['configurable_timezones_wrapper']['empty_timezone_message'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Remind users at login if their time zone is not set.'),
+ '#default_value' => variable_get('empty_timezone_message', 0),
+ '#description' => t('Only applied if users may set their own time zone.')
+ );
+
+ $form['timezone']['configurable_timezones_wrapper']['user_default_timezone'] = array(
+ '#type' => 'radios',
+ '#title' => t('Time zone for new users'),
+ '#default_value' => variable_get('user_default_timezone', DRUPAL_USER_TIMEZONE_DEFAULT),
+ '#options' => array(
+ DRUPAL_USER_TIMEZONE_DEFAULT => t('Default time zone.'),
+ DRUPAL_USER_TIMEZONE_EMPTY => t('Empty time zone.'),
+ DRUPAL_USER_TIMEZONE_SELECT => t('Users may set their own time zone at registration.'),
+ ),
+ '#description' => t('Only applied if users may set their own time zone.')
+ );
+
+ return system_settings_form($form);
+}
+
+/**
+ * Form builder; Configure the site date and time settings.
+ *
+ * @ingroup forms
+ * @see system_settings_form()
+ */
+function system_date_time_settings() {
+ // Get list of all available date types.
+ drupal_static_reset('system_get_date_types');
+ $format_types = system_get_date_types();
+
+ // Get list of all available date formats.
+ $all_formats = array();
+ drupal_static_reset('system_get_date_formats');
+ $date_formats = system_get_date_formats(); // Call this to rebuild the list, and to have default list.
+ foreach ($date_formats as $type => $format_info) {
+ $all_formats = array_merge($all_formats, $format_info);
+ }
+ $custom_formats = system_get_date_formats('custom');
+ if (!empty($format_types)) {
+ foreach ($format_types as $type => $type_info) {
+ // If a system type, only show the available formats for that type and
+ // custom ones.
+ if ($type_info['locked'] == 1) {
+ $formats = system_get_date_formats($type);
+ if (empty($formats)) {
+ $formats = $all_formats;
+ }
+ elseif (!empty($custom_formats)) {
+ $formats = array_merge($formats, $custom_formats);
+ }
+ }
+ // If a user configured type, show all available date formats.
+ else {
+ $formats = $all_formats;
+ }
+
+ $choices = array();
+ foreach ($formats as $f => $format) {
+ $choices[$f] = format_date(REQUEST_TIME, 'custom', $f);
+ }
+ reset($formats);
+ $default = variable_get('date_format_' . $type, key($formats));
+
+ // Get date type info for this date type.
+ $type_info = system_get_date_types($type);
+ $form['formats']['#theme'] = 'system_date_time_settings';
+
+ // Show date format select list.
+ $form['formats']['format']['date_format_' . $type] = array(
+ '#type' => 'select',
+ '#title' => check_plain($type_info['title']),
+ '#attributes' => array('class' => array('date-format')),
+ '#default_value' => (isset($choices[$default]) ? $default : 'custom'),
+ '#options' => $choices,
+ );
+
+ // If this isn't a system provided type, allow the user to remove it from
+ // the system.
+ if ($type_info['locked'] == 0) {
+ $form['formats']['delete']['date_format_' . $type . '_delete'] = array(
+ '#type' => 'link',
+ '#title' => t('delete'),
+ '#href' => 'admin/config/regional/date-time/types/' . $type . '/delete',
+ );
+ }
+ }
+ }
+
+ // Display a message if no date types configured.
+ $form['#empty_text'] = t('No date types available. <a href="@link">Add date type</a>.', array('@link' => url('admin/config/regional/date-time/types/add')));
+
+ return system_settings_form($form);
+}
+
+/**
+ * Returns HTML for the date settings form.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - form: A render element representing the form.
+ *
+ * @ingroup themeable
+ */
+function theme_system_date_time_settings($variables) {
+ $form = $variables['form'];
+ $header = array(
+ t('Date type'),
+ t('Format'),
+ t('Operations'),
+ );
+
+ foreach (element_children($form['format']) as $key) {
+ $delete_key = $key . '_delete';
+ $row = array();
+ $row[] = $form['format'][$key]['#title'];
+ $form['format'][$key]['#title_display'] = 'invisible';
+ $row[] = array('data' => drupal_render($form['format'][$key]));
+ $row[] = array('data' => drupal_render($form['delete'][$delete_key]));
+ $rows[] = $row;
+ }
+
+ $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'system-date-types')));
+ $output .= drupal_render_children($form);
+
+ return $output;
+}
+
+
+/**
+ * Add new date type.
+ *
+ * @ingroup forms
+ * @ingroup system_add_date_format_type_form_validate()
+ * @ingroup system_add_date_format_type_form_submit()
+ */
+function system_add_date_format_type_form($form, &$form_state) {
+ $form['date_type'] = array(
+ '#title' => t('Date type'),
+ '#type' => 'textfield',
+ '#required' => TRUE,
+ );
+ $form['machine_name'] = array(
+ '#type' => 'machine_name',
+ '#machine_name' => array(
+ 'exists' => 'system_get_date_types',
+ 'source' => array('date_type'),
+ ),
+ );
+
+ // Get list of all available date formats.
+ $formats = array();
+ drupal_static_reset('system_get_date_formats');
+ $date_formats = system_get_date_formats(); // Call this to rebuild the list, and to have default list.
+ foreach ($date_formats as $type => $format_info) {
+ $formats = array_merge($formats, $format_info);
+ }
+ $custom_formats = system_get_date_formats('custom');
+ if (!empty($custom_formats)) {
+ $formats = array_merge($formats, $custom_formats);
+ }
+ $choices = array();
+ foreach ($formats as $f => $format) {
+ $choices[$f] = format_date(REQUEST_TIME, 'custom', $f);
+ }
+ // Show date format select list.
+ $form['date_format'] = array(
+ '#type' => 'select',
+ '#title' => t('Date format'),
+ '#attributes' => array('class' => array('date-format')),
+ '#options' => $choices,
+ '#required' => TRUE,
+ );
+
+ $form['actions'] = array('#type' => 'actions');
+ $form['actions']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Add date type'),
+ );
+
+ $form['#validate'][] = 'system_add_date_format_type_form_validate';
+ $form['#submit'][] = 'system_add_date_format_type_form_submit';
+
+ return $form;
+}
+
+/**
+ * Validate system_add_date_format_type form submissions.
+ */
+function system_add_date_format_type_form_validate($form, &$form_state) {
+ if (!empty($form_state['values']['machine_name']) && !empty($form_state['values']['date_type'])) {
+ if (!preg_match("/^[a-zA-Z0-9_]+$/", trim($form_state['values']['machine_name']))) {
+ form_set_error('machine_name', t('The date type must contain only alphanumeric characters and underscores.'));
+ }
+ $types = system_get_date_types();
+ if (in_array(trim($form_state['values']['machine_name']), array_keys($types))) {
+ form_set_error('machine_name', t('This date type already exists. Enter a unique type.'));
+ }
+ }
+}
+
+/**
+ * Process system_add_date_format_type form submissions.
+ */
+function system_add_date_format_type_form_submit($form, &$form_state) {
+ $machine_name = trim($form_state['values']['machine_name']);
+
+ $format_type = array();
+ $format_type['title'] = trim($form_state['values']['date_type']);
+ $format_type['type'] = $machine_name;
+ $format_type['locked'] = 0;
+ $format_type['is_new'] = 1;
+ system_date_format_type_save($format_type);
+ variable_set('date_format_' . $machine_name, $form_state['values']['date_format']);
+
+ drupal_set_message(t('New date type added successfully.'));
+ $form_state['redirect'] = 'admin/config/regional/date-time';
+}
+
+/**
+ * Return the date for a given format string via Ajax.
+ */
+function system_date_time_lookup() {
+ $result = format_date(REQUEST_TIME, 'custom', $_GET['format']);
+ drupal_json_output($result);
+}
+
+/**
+ * Form builder; Configure the site's maintenance status.
+ *
+ * @ingroup forms
+ * @see system_settings_form()
+ */
+function system_site_maintenance_mode() {
+ $form['maintenance_mode'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Put site into maintenance mode'),
+ '#default_value' => variable_get('maintenance_mode', 0),
+ '#description' => t('When enabled, only users with the "Use the site in maintenance mode" <a href="@permissions-url">permission</a> are able to access your site to perform maintenance; all other visitors see the maintenance mode message configured below. Authorized users can log in directly via the <a href="@user-login">user login</a> page.', array('@permissions-url' => url('admin/people/permissions'), '@user-login' => url('user'))),
+ );
+ $form['maintenance_mode_message'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Maintenance mode message'),
+ '#default_value' => variable_get('maintenance_mode_message', t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal')))),
+ '#description' => t('Message to show visitors when the site is in maintenance mode.')
+ );
+
+ return system_settings_form($form);
+}
+
+/**
+ * Form builder; Configure clean URL settings.
+ *
+ * @ingroup forms
+ * @see system_settings_form()
+ */
+function system_clean_url_settings($form, &$form_state) {
+ $available = FALSE;
+ $conflict = FALSE;
+
+ // If the request URI is a clean URL, clean URLs must be available.
+ // Otherwise, run a test.
+ if (strpos(request_uri(), '?q=') === FALSE && strpos(request_uri(), '&q=') === FALSE) {
+ $available = TRUE;
+ }
+ else {
+ $request = drupal_http_request($GLOBALS['base_url'] . '/admin/config/search/clean-urls/check');
+ // If the request returns HTTP 200, clean URLs are available.
+ if (isset($request->code) && $request->code == 200) {
+ $available = TRUE;
+ // If the user started the clean URL test, provide explicit feedback.
+ if (isset($form_state['input']['clean_url_test_execute'])) {
+ drupal_set_message(t('The clean URL test passed.'));
+ }
+ }
+ else {
+ // If the test failed while clean URLs are enabled, make sure clean URLs
+ // can be disabled.
+ if (variable_get('clean_url', 0)) {
+ $conflict = TRUE;
+ // Warn the user of a conflicting situation, unless after processing
+ // a submitted form.
+ if (!isset($form_state['input']['op'])) {
+ drupal_set_message(t('Clean URLs are enabled, but the clean URL test failed. Uncheck the box below to disable clean URLs.'), 'warning');
+ }
+ }
+ // If the user started the clean URL test, provide explicit feedback.
+ elseif (isset($form_state['input']['clean_url_test_execute'])) {
+ drupal_set_message(t('The clean URL test failed.'), 'warning');
+ }
+ }
+ }
+
+ // Show the enable/disable form if clean URLs are available or if the user
+ // must be able to resolve a conflicting setting.
+ if ($available || $conflict) {
+ $form['clean_url'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Enable clean URLs'),
+ '#default_value' => variable_get('clean_url', 0),
+ '#description' => t('Use URLs like <code>example.com/user</code> instead of <code>example.com/?q=user</code>.'),
+ );
+ $form = system_settings_form($form);
+ if ($conflict) {
+ // $form_state['redirect'] needs to be set to the non-clean URL,
+ // otherwise the setting is not saved.
+ $form_state['redirect'] = url('', array('query' => array('q' => '/admin/config/search/clean-urls')));
+ }
+ }
+ // Show the clean URLs test form.
+ else {
+ drupal_add_js(drupal_get_path('module', 'system') . '/system.js');
+
+ $form_state['redirect'] = url('admin/config/search/clean-urls');
+ $form['clean_url_description'] = array(
+ '#type' => 'markup',
+ '#markup' => '<p>' . t('Use URLs like <code>example.com/user</code> instead of <code>example.com/?q=user</code>.'),
+ );
+ // Explain why the user is seeing this page and what to expect after
+ // clicking the 'Run the clean URL test' button.
+ $form['clean_url_test_result'] = array(
+ '#type' => 'markup',
+ '#markup' => '<p>' . t('Clean URLs cannot be enabled. If you are directed to this page or to a <em>Page not found (404)</em> error after testing for clean URLs, see the <a href="@handbook">online handbook</a>.', array('@handbook' => 'http://drupal.org/node/15365')) . '</p>',
+ );
+ $form['actions'] = array(
+ '#type' => 'actions',
+ 'clean_url_test' => array(
+ '#type' => 'submit',
+ '#value' => t('Run the clean URL test'),
+ ),
+ );
+ $form['clean_url_test_execute'] = array(
+ '#type' => 'hidden',
+ '#value' => 1,
+ );
+ }
+
+ return $form;
+}
+
+/**
+ * Menu callback: displays the site status report. Can also be used as a pure check.
+ *
+ * @param $check
+ * If true, only returns a boolean whether there are system status errors.
+ */
+function system_status($check = FALSE) {
+ // Load .install files
+ include_once DRUPAL_ROOT . '/includes/install.inc';
+ drupal_load_updates();
+
+ // Check run-time requirements and status information.
+ $requirements = module_invoke_all('requirements', 'runtime');
+ usort($requirements, '_system_sort_requirements');
+
+ if ($check) {
+ return drupal_requirements_severity($requirements) == REQUIREMENT_ERROR;
+ }
+ // MySQL import might have set the uid of the anonymous user to autoincrement
+ // value. Let's try fixing it. See http://drupal.org/node/204411
+ db_update('users')
+ ->expression('uid', 'uid - uid')
+ ->condition('name', '')
+ ->condition('pass', '')
+ ->condition('status', 0)
+ ->execute();
+ return theme('status_report', array('requirements' => $requirements));
+}
+
+/**
+ * Menu callback: run cron manually.
+ */
+function system_run_cron() {
+ // Run cron manually
+ if (drupal_cron_run()) {
+ drupal_set_message(t('Cron ran successfully.'));
+ }
+ else {
+ drupal_set_message(t('Cron run failed.'), 'error');
+ }
+
+ drupal_goto('admin/reports/status');
+}
+
+/**
+ * Menu callback: return information about PHP.
+ */
+function system_php() {
+ phpinfo();
+ drupal_exit();
+}
+
+/**
+ * Default page callback for batches.
+ */
+function system_batch_page() {
+ require_once DRUPAL_ROOT . '/includes/batch.inc';
+ $output = _batch_page();
+
+ if ($output === FALSE) {
+ drupal_access_denied();
+ }
+ elseif (isset($output)) {
+ // Force a page without blocks or messages to
+ // display a list of collected messages later.
+ drupal_set_page_content($output);
+ $page = element_info('page');
+ $page['#show_messages'] = FALSE;
+ return $page;
+ }
+}
+
+/**
+ * Returns HTML for an administrative block for display.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - block: An array containing information about the block:
+ * - show: A Boolean whether to output the block. Defaults to FALSE.
+ * - title: The block's title.
+ * - content: (optional) Formatted content for the block.
+ * - description: (optional) Description of the block. Only output if
+ * 'content' is not set.
+ *
+ * @ingroup themeable
+ */
+function theme_admin_block($variables) {
+ $block = $variables['block'];
+ $output = '';
+
+ // Don't display the block if it has no content to display.
+ if (empty($block['show'])) {
+ return $output;
+ }
+
+ $output .= '<div class="admin-panel">';
+ if (!empty($block['title'])) {
+ $output .= '<h3>' . $block['title'] . '</h3>';
+ }
+ if (!empty($block['content'])) {
+ $output .= '<div class="body">' . $block['content'] . '</div>';
+ }
+ else {
+ $output .= '<div class="description">' . $block['description'] . '</div>';
+ }
+ $output .= '</div>';
+
+ return $output;
+}
+
+/**
+ * Returns HTML for the content of an administrative block.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - content: An array containing information about the block. Each element
+ * of the array represents an administrative menu item, and must at least
+ * contain the keys 'title', 'href', and 'localized_options', which are
+ * passed to l(). A 'description' key may also be provided.
+ *
+ * @ingroup themeable
+ */
+function theme_admin_block_content($variables) {
+ $content = $variables['content'];
+ $output = '';
+
+ if (!empty($content)) {
+ $class = 'admin-list';
+ if ($compact = system_admin_compact_mode()) {
+ $class .= ' compact';
+ }
+ $output .= '<dl class="' . $class . '">';
+ foreach ($content as $item) {
+ $output .= '<dt>' . l($item['title'], $item['href'], $item['localized_options']) . '</dt>';
+ if (!$compact && isset($item['description'])) {
+ $output .= '<dd>' . filter_xss_admin($item['description']) . '</dd>';
+ }
+ }
+ $output .= '</dl>';
+ }
+ return $output;
+}
+
+/**
+ * Returns HTML for an administrative page.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - blocks: An array of blocks to display. Each array should include a
+ * 'title', a 'description', a formatted 'content' and a 'position' which
+ * will control which container it will be in. This is usually 'left' or
+ * 'right'.
+ *
+ * @ingroup themeable
+ */
+function theme_admin_page($variables) {
+ $blocks = $variables['blocks'];
+
+ $stripe = 0;
+ $container = array();
+
+ foreach ($blocks as $block) {
+ if ($block_output = theme('admin_block', array('block' => $block))) {
+ if (empty($block['position'])) {
+ // perform automatic striping.
+ $block['position'] = ++$stripe % 2 ? 'left' : 'right';
+ }
+ if (!isset($container[$block['position']])) {
+ $container[$block['position']] = '';
+ }
+ $container[$block['position']] .= $block_output;
+ }
+ }
+
+ $output = '<div class="admin clearfix">';
+ $output .= theme('system_compact_link');
+
+ foreach ($container as $id => $data) {
+ $output .= '<div class="' . $id . ' clearfix">';
+ $output .= $data;
+ $output .= '</div>';
+ }
+ $output .= '</div>';
+ return $output;
+}
+
+/**
+ * Returns HTML for the output of the dashboard page.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - menu_items: An array of modules to be displayed.
+ *
+ * @ingroup themeable
+ */
+function theme_system_admin_index($variables) {
+ $menu_items = $variables['menu_items'];
+
+ $stripe = 0;
+ $container = array('left' => '', 'right' => '');
+ $flip = array('left' => 'right', 'right' => 'left');
+ $position = 'left';
+
+ // Iterate over all modules.
+ foreach ($menu_items as $module => $block) {
+ list($description, $items) = $block;
+
+ // Output links.
+ if (count($items)) {
+ $block = array();
+ $block['title'] = $module;
+ $block['content'] = theme('admin_block_content', array('content' => $items));
+ $block['description'] = t($description);
+ $block['show'] = TRUE;
+
+ if ($block_output = theme('admin_block', array('block' => $block))) {
+ if (!isset($block['position'])) {
+ // Perform automatic striping.
+ $block['position'] = $position;
+ $position = $flip[$position];
+ }
+ $container[$block['position']] .= $block_output;
+ }
+ }
+ }
+
+ $output = '<div class="admin clearfix">';
+ $output .= theme('system_compact_link');
+ foreach ($container as $id => $data) {
+ $output .= '<div class="' . $id . ' clearfix">';
+ $output .= $data;
+ $output .= '</div>';
+ }
+ $output .= '</div>';
+
+ return $output;
+}
+
+/**
+ * Returns HTML for the status report.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - requirements: An array of requirements.
+ *
+ * @ingroup themeable
+ */
+function theme_status_report($variables) {
+ $requirements = $variables['requirements'];
+ $severities = array(
+ REQUIREMENT_INFO => array(
+ 'title' => t('Info'),
+ 'class' => 'info',
+ ),
+ REQUIREMENT_OK => array(
+ 'title' => t('OK'),
+ 'class' => 'ok',
+ ),
+ REQUIREMENT_WARNING => array(
+ 'title' => t('Warning'),
+ 'class' => 'warning',
+ ),
+ REQUIREMENT_ERROR => array(
+ 'title' => t('Error'),
+ 'class' => 'error',
+ ),
+ );
+ $output = '<table class="system-status-report">';
+
+ foreach ($requirements as $requirement) {
+ if (empty($requirement['#type'])) {
+ $severity = $severities[isset($requirement['severity']) ? (int) $requirement['severity'] : REQUIREMENT_OK];
+ $severity['icon'] = '<div title="' . $severity['title'] . '"><span class="element-invisible">' . $severity['title'] . '</span></div>';
+
+ // Output table row(s)
+ if (!empty($requirement['description'])) {
+ $output .= '<tr class="' . $severity['class'] . ' merge-down"><td class="status-icon">' . $severity['icon'] . '</td><td class="status-title">' . $requirement['title'] . '</td><td class="status-value">' . $requirement['value'] . '</td></tr>';
+ $output .= '<tr class="' . $severity['class'] . ' merge-up"><td colspan="3" class="status-description">' . $requirement['description'] . '</td></tr>';
+ }
+ else {
+ $output .= '<tr class="' . $severity['class'] . '"><td class="status-icon">' . $severity['icon'] . '</td><td class="status-title">' . $requirement['title'] . '</td><td class="status-value">' . $requirement['value'] . '</td></tr>';
+ }
+ }
+ }
+
+ $output .= '</table>';
+ return $output;
+}
+
+/**
+ * Returns HTML for the modules form.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - form: A render element representing the form.
+ *
+ * @ingroup themeable
+ */
+function theme_system_modules_fieldset($variables) {
+ $form = $variables['form'];
+
+ // Individual table headers.
+ $rows = array();
+ // Iterate through all the modules, which are
+ // children of this fieldset.
+ foreach (element_children($form) as $key) {
+ // Stick it into $module for easier accessing.
+ $module = $form[$key];
+ $row = array();
+ unset($module['enable']['#title']);
+ $row[] = array('class' => array('checkbox'), 'data' => drupal_render($module['enable']));
+ $label = '<label';
+ if (isset($module['enable']['#id'])) {
+ $label .= ' for="' . $module['enable']['#id'] . '"';
+ }
+ $row[] = $label . '><strong>' . drupal_render($module['name']) . '</strong></label>';
+ $row[] = drupal_render($module['version']);
+ // Add the description, along with any modules it requires.
+ $description = drupal_render($module['description']);
+ if ($module['#requires']) {
+ $description .= '<div class="admin-requirements">' . t('Requires: !module-list', array('!module-list' => implode(', ', $module['#requires']))) . '</div>';
+ }
+ if ($module['#required_by']) {
+ $description .= '<div class="admin-requirements">' . t('Required by: !module-list', array('!module-list' => implode(', ', $module['#required_by']))) . '</div>';
+ }
+ $row[] = array('data' => $description, 'class' => array('description'));
+ // Display links (such as help or permissions) in their own columns.
+ foreach (array('help', 'permissions', 'configure') as $key) {
+ $row[] = array('data' => drupal_render($module['links'][$key]), 'class' => array($key));
+ }
+ $rows[] = $row;
+ }
+
+ return theme('table', array('header' => $form['#header'], 'rows' => $rows));
+}
+
+/**
+ * Returns HTML for a message about incompatible modules.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - message: The form array representing the currently disabled modules.
+ *
+ * @ingroup themeable
+ */
+function theme_system_modules_incompatible($variables) {
+ return '<div class="incompatible">' . $variables['message'] . '</div>';
+}
+
+/**
+ * Returns HTML for a table of currently disabled modules.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - form: A render element representing the form.
+ *
+ * @ingroup themeable
+ */
+function theme_system_modules_uninstall($variables) {
+ $form = $variables['form'];
+
+ // No theming for the confirm form.
+ if (isset($form['confirm'])) {
+ return drupal_render($form);
+ }
+
+ // Table headers.
+ $header = array(t('Uninstall'),
+ t('Name'),
+ t('Description'),
+ );
+
+ // Display table.
+ $rows = array();
+ foreach (element_children($form['modules']) as $module) {
+ if (!empty($form['modules'][$module]['#required_by'])) {
+ $disabled_message = format_plural(count($form['modules'][$module]['#required_by']),
+ 'To uninstall @module, the following module must be uninstalled first: @required_modules',
+ 'To uninstall @module, the following modules must be uninstalled first: @required_modules',
+ array('@module' => $form['modules'][$module]['#module_name'], '@required_modules' => implode(', ', $form['modules'][$module]['#required_by'])));
+ $disabled_message = '<div class="admin-requirements">' . $disabled_message . '</div>';
+ }
+ else {
+ $disabled_message = '';
+ }
+ $rows[] = array(
+ array('data' => drupal_render($form['uninstall'][$module]), 'align' => 'center'),
+ '<strong><label for="' . $form['uninstall'][$module]['#id'] . '">' . drupal_render($form['modules'][$module]['name']) . '</label></strong>',
+ array('data' => drupal_render($form['modules'][$module]['description']) . $disabled_message, 'class' => array('description')),
+ );
+ }
+
+ $output = theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No modules are available to uninstall.')));
+ $output .= drupal_render_children($form);
+
+ return $output;
+}
+
+/**
+ * Returns HTML for the Appearance page.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - theme_groups: An associative array containing groups of themes.
+ *
+ * @ingroup themeable
+ */
+function theme_system_themes_page($variables) {
+ $theme_groups = $variables['theme_groups'];
+
+ $output = '<div id="system-themes-page">';
+
+ foreach ($variables['theme_group_titles'] as $state => $title) {
+ if (!count($theme_groups[$state])) {
+ // Skip this group of themes if no theme is there.
+ continue;
+ }
+ // Start new theme group.
+ $output .= '<div class="system-themes-list system-themes-list-'. $state .' clearfix"><h2>'. $title .'</h2>';
+
+ foreach ($theme_groups[$state] as $theme) {
+
+ // Theme the screenshot.
+ $screenshot = $theme->screenshot ? theme('image', $theme->screenshot) : '<div class="no-screenshot">' . t('no screenshot') . '</div>';
+
+ // Localize the theme description.
+ $description = t($theme->info['description']);
+
+ // Style theme info
+ $notes = count($theme->notes) ? ' (' . join(', ', $theme->notes) . ')' : '';
+ $theme->classes[] = 'theme-selector';
+ $theme->classes[] = 'clearfix';
+ $output .= '<div class="'. join(' ', $theme->classes) .'">' . $screenshot . '<div class="theme-info"><h3>' . $theme->info['name'] . ' ' . (isset($theme->info['version']) ? $theme->info['version'] : '') . $notes . '</h3><div class="theme-description">' . $description . '</div>';
+
+ // Make sure to provide feedback on compatibility.
+ if (!empty($theme->incompatible_core)) {
+ $output .= '<div class="incompatible">' . t('This version is not compatible with Drupal !core_version and should be replaced.', array('!core_version' => DRUPAL_CORE_COMPATIBILITY)) . '</div>';
+ }
+ elseif (!empty($theme->incompatible_php)) {
+ if (substr_count($theme->info['php'], '.') < 2) {
+ $theme->info['php'] .= '.*';
+ }
+ $output .= '<div class="incompatible">' . t('This theme requires PHP version @php_required and is incompatible with PHP version !php_version.', array('@php_required' => $theme->info['php'], '!php_version' => phpversion())) . '</div>';
+ }
+ else {
+ $output .= theme('links', array('links' => $theme->operations, 'attributes' => array('class' => array('operations', 'clearfix'))));
+ }
+ $output .= '</div></div>';
+ }
+ $output .= '</div>';
+ }
+ $output .= '</div>';
+
+ return $output;
+}
+
+/**
+ * Menu callback; present a form for deleting a date format.
+ */
+function system_date_delete_format_form($form, &$form_state, $dfid) {
+ $form['dfid'] = array(
+ '#type' => 'value',
+ '#value' => $dfid,
+ );
+ $format = system_get_date_format($dfid);
+
+ $output = confirm_form($form,
+ t('Are you sure you want to remove the format %format?', array('%format' => format_date(REQUEST_TIME, 'custom', $format->format))),
+ 'admin/config/regional/date-time/formats',
+ t('This action cannot be undone.'),
+ t('Remove'), t('Cancel'),
+ 'confirm'
+ );
+
+ return $output;
+}
+
+/**
+ * Delete a configured date format.
+ */
+function system_date_delete_format_form_submit($form, &$form_state) {
+ if ($form_state['values']['confirm']) {
+ $format = system_get_date_format($form_state['values']['dfid']);
+ system_date_format_delete($form_state['values']['dfid']);
+ drupal_set_message(t('Removed date format %format.', array('%format' => format_date(REQUEST_TIME, 'custom', $format->format))));
+ $form_state['redirect'] = 'admin/config/regional/date-time/formats';
+ }
+}
+
+/**
+ * Menu callback; present a form for deleting a date type.
+ */
+function system_delete_date_format_type_form($form, &$form_state, $format_type) {
+ $form['format_type'] = array(
+ '#type' => 'value',
+ '#value' => $format_type,
+ );
+ $type_info = system_get_date_types($format_type);
+
+ $output = confirm_form($form,
+ t('Are you sure you want to remove the date type %type?', array('%type' => $type_info['title'])),
+ 'admin/config/regional/date-time',
+ t('This action cannot be undone.'),
+ t('Remove'), t('Cancel'),
+ 'confirm'
+ );
+
+ return $output;
+}
+
+/**
+ * Delete a configured date type.
+ */
+function system_delete_date_format_type_form_submit($form, &$form_state) {
+ if ($form_state['values']['confirm']) {
+ $type_info = system_get_date_types($form_state['values']['format_type']);
+ system_date_format_type_delete($form_state['values']['format_type']);
+ drupal_set_message(t('Removed date type %type.', array('%type' => $type_info['title'])));
+ $form_state['redirect'] = 'admin/config/regional/date-time';
+ }
+}
+
+
+/**
+ * Displays the date format strings overview page.
+ */
+function system_date_time_formats() {
+ $header = array(t('Format'), array('data' => t('Operations'), 'colspan' => '2'));
+ $rows = array();
+
+ drupal_static_reset('system_get_date_formats');
+ $formats = system_get_date_formats('custom');
+ if (!empty($formats)) {
+ foreach ($formats as $format) {
+ $row = array();
+ $row[] = array('data' => format_date(REQUEST_TIME, 'custom', $format['format']));
+ $row[] = array('data' => l(t('edit'), 'admin/config/regional/date-time/formats/' . $format['dfid'] . '/edit'));
+ $row[] = array('data' => l(t('delete'), 'admin/config/regional/date-time/formats/' . $format['dfid'] . '/delete'));
+ $rows[] = $row;
+ }
+ }
+
+ $build['date_formats_table'] = array(
+ '#theme' => 'table',
+ '#header' => $header,
+ '#rows' => $rows,
+ '#empty' => t('No custom date formats available. <a href="@link">Add date format</a>.', array('@link' => url('admin/config/regional/date-time/formats/add'))),
+ );
+
+ return $build;
+}
+
+/**
+ * Allow users to add additional date formats.
+ */
+function system_configure_date_formats_form($form, &$form_state, $dfid = 0) {
+ $js_settings = array(
+ 'type' => 'setting',
+ 'data' => array(
+ 'dateTime' => array(
+ 'date-format' => array(
+ 'text' => t('Displayed as'),
+ 'lookup' => url('admin/config/regional/date-time/formats/lookup'),
+ ),
+ ),
+ ),
+ );
+
+ if ($dfid) {
+ $form['dfid'] = array(
+ '#type' => 'value',
+ '#value' => $dfid,
+ );
+ $format = system_get_date_format($dfid);
+ }
+
+ $now = ($dfid ? t('Displayed as %date', array('%date' => format_date(REQUEST_TIME, 'custom', $format->format))) : '');
+
+ $form['date_format'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Format string'),
+ '#maxlength' => 100,
+ '#description' => t('A user-defined date format. See the <a href="@url">PHP manual</a> for available options.', array('@url' => 'http://php.net/manual/function.date.php')),
+ '#default_value' => ($dfid ? $format->format : ''),
+ '#field_suffix' => ' <small id="edit-date-format-suffix">' . $now . '</small>',
+ '#attached' => array(
+ 'js' => array(drupal_get_path('module', 'system') . '/system.js', $js_settings),
+ ),
+ '#required' => TRUE,
+ );
+
+ $form['actions'] = array('#type' => 'actions');
+ $form['actions']['update'] = array(
+ '#type' => 'submit',
+ '#value' => ($dfid ? t('Save format') : t('Add format')),
+ );
+
+ $form['#validate'][] = 'system_add_date_formats_form_validate';
+ $form['#submit'][] = 'system_add_date_formats_form_submit';
+
+ return $form;
+}
+
+/**
+ * Validate new date format string submission.
+ */
+function system_add_date_formats_form_validate($form, &$form_state) {
+ $formats = system_get_date_formats('custom');
+ $format = trim($form_state['values']['date_format']);
+ if (!empty($formats) && in_array($format, array_keys($formats)) && (!isset($form_state['values']['dfid']) || $form_state['values']['dfid'] != $formats[$format]['dfid'])) {
+ form_set_error('date_format', t('This format already exists. Enter a unique format string.'));
+ }
+}
+
+/**
+ * Process new date format string submission.
+ */
+function system_add_date_formats_form_submit($form, &$form_state) {
+ $format = array();
+ $format['format'] = trim($form_state['values']['date_format']);
+ $format['type'] = 'custom';
+ $format['locked'] = 0;
+ if (!empty($form_state['values']['dfid'])) {
+ system_date_format_save($format, $form_state['values']['dfid']);
+ drupal_set_message(t('Custom date format updated.'));
+ }
+ else {
+ $format['is_new'] = 1;
+ system_date_format_save($format);
+ drupal_set_message(t('Custom date format added.'));
+ }
+
+ $form_state['redirect'] = 'admin/config/regional/date-time/formats';
+}
+
+/**
+ * Menu callback; Displays an overview of available and configured actions.
+ */
+function system_actions_manage() {
+ actions_synchronize();
+ $actions = actions_list();
+ $actions_map = actions_actions_map($actions);
+ $options = array();
+ $unconfigurable = array();
+
+ foreach ($actions_map as $key => $array) {
+ if ($array['configurable']) {
+ $options[$key] = $array['label'] . '...';
+ }
+ else {
+ $unconfigurable[] = $array;
+ }
+ }
+
+ $row = array();
+ $instances_present = db_query("SELECT aid FROM {actions} WHERE parameters <> ''")->fetchField();
+ $header = array(
+ array('data' => t('Action type'), 'field' => 'type'),
+ array('data' => t('Label'), 'field' => 'label'),
+ array('data' => $instances_present ? t('Operations') : '', 'colspan' => '2')
+ );
+ $query = db_select('actions')->extend('PagerDefault')->extend('TableSort');
+ $result = $query
+ ->fields('actions')
+ ->limit(50)
+ ->orderByHeader($header)
+ ->execute();
+
+ foreach ($result as $action) {
+ $row[] = array(
+ array('data' => $action->type),
+ array('data' => check_plain($action->label)),
+ array('data' => $action->parameters ? l(t('configure'), "admin/config/system/actions/configure/$action->aid") : ''),
+ array('data' => $action->parameters ? l(t('delete'), "admin/config/system/actions/delete/$action->aid") : '')
+ );
+ }
+
+ if ($row) {
+ $pager = theme('pager');
+ if (!empty($pager)) {
+ $row[] = array(array('data' => $pager, 'colspan' => '3'));
+ }
+ $build['system_actions_header'] = array('#markup' => '<h3>' . t('Available actions:') . '</h3>');
+ $build['system_actions_table'] = array('#markup' => theme('table', array('header' => $header, 'rows' => $row)));
+ }
+
+ if ($actions_map) {
+ $build['system_actions_manage_form'] = drupal_get_form('system_actions_manage_form', $options);
+ }
+
+ return $build;
+}
+
+/**
+ * Define the form for the actions overview page.
+ *
+ * @param $form_state
+ * An associative array containing the current state of the form; not used.
+ * @param $options
+ * An array of configurable actions.
+ * @return
+ * Form definition.
+ *
+ * @ingroup forms
+ * @see system_actions_manage_form_submit()
+ */
+function system_actions_manage_form($form, &$form_state, $options = array()) {
+ $form['parent'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Create an advanced action'),
+ '#attributes' => array('class' => array('container-inline')),
+ );
+ $form['parent']['action'] = array(
+ '#type' => 'select',
+ '#title' => t('Action'),
+ '#title_display' => 'invisible',
+ '#options' => $options,
+ '#empty_option' => t('Choose an advanced action'),
+ );
+ $form['parent']['actions'] = array('#type' => 'actions');
+ $form['parent']['actions']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Create'),
+ );
+ return $form;
+}
+
+/**
+ * Process system_actions_manage form submissions.
+ *
+ * @see system_actions_manage_form()
+ */
+function system_actions_manage_form_submit($form, &$form_state) {
+ if ($form_state['values']['action']) {
+ $form_state['redirect'] = 'admin/config/system/actions/configure/' . $form_state['values']['action'];
+ }
+}
+
+/**
+ * Menu callback; Creates the form for configuration of a single action.
+ *
+ * We provide the "Description" field. The rest of the form is provided by the
+ * action. We then provide the Save button. Because we are combining unknown
+ * form elements with the action configuration form, we use an 'actions_' prefix
+ * on our elements.
+ *
+ * @param $action
+ * Hash of an action ID or an integer. If it is a hash, we are
+ * creating a new instance. If it is an integer, we are editing an existing
+ * instance.
+ * @return
+ * A form definition.
+ *
+ * @see system_actions_configure_validate()
+ * @see system_actions_configure_submit()
+ */
+function system_actions_configure($form, &$form_state, $action = NULL) {
+ if ($action === NULL) {
+ drupal_goto('admin/config/system/actions');
+ }
+
+ $actions_map = actions_actions_map(actions_list());
+ $edit = array();
+
+ // Numeric action denotes saved instance of a configurable action.
+ if (is_numeric($action)) {
+ $aid = $action;
+ // Load stored parameter values from database.
+ $data = db_query("SELECT * FROM {actions} WHERE aid = :aid", array(':aid' => $aid))->fetch();
+ $edit['actions_label'] = $data->label;
+ $edit['actions_type'] = $data->type;
+ $function = $data->callback;
+ $action = drupal_hash_base64($data->callback);
+ $params = unserialize($data->parameters);
+ if ($params) {
+ foreach ($params as $name => $val) {
+ $edit[$name] = $val;
+ }
+ }
+ }
+ // Otherwise, we are creating a new action instance.
+ else {
+ $function = $actions_map[$action]['callback'];
+ $edit['actions_label'] = $actions_map[$action]['label'];
+ $edit['actions_type'] = $actions_map[$action]['type'];
+ }
+
+ $form['actions_label'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Label'),
+ '#default_value' => $edit['actions_label'],
+ '#maxlength' => '255',
+ '#description' => t('A unique label for this advanced action. This label will be displayed in the interface of modules that integrate with actions, such as Trigger module.'),
+ '#weight' => -10
+ );
+ $action_form = $function . '_form';
+ $form = array_merge($form, $action_form($edit));
+ $form['actions_type'] = array(
+ '#type' => 'value',
+ '#value' => $edit['actions_type'],
+ );
+ $form['actions_action'] = array(
+ '#type' => 'hidden',
+ '#value' => $action,
+ );
+ // $aid is set when configuring an existing action instance.
+ if (isset($aid)) {
+ $form['actions_aid'] = array(
+ '#type' => 'hidden',
+ '#value' => $aid,
+ );
+ }
+ $form['actions_configured'] = array(
+ '#type' => 'hidden',
+ '#value' => '1',
+ );
+ $form['actions'] = array('#type' => 'actions');
+ $form['actions']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save'),
+ '#weight' => 13
+ );
+
+ return $form;
+}
+
+/**
+ * Validate system_actions_configure() form submissions.
+ */
+function system_actions_configure_validate($form, &$form_state) {
+ $function = actions_function_lookup($form_state['values']['actions_action']) . '_validate';
+ // Hand off validation to the action.
+ if (function_exists($function)) {
+ $function($form, $form_state);
+ }
+}
+
+/**
+ * Process system_actions_configure() form submissions.
+ */
+function system_actions_configure_submit($form, &$form_state) {
+ $function = actions_function_lookup($form_state['values']['actions_action']);
+ $submit_function = $function . '_submit';
+
+ // Action will return keyed array of values to store.
+ $params = $submit_function($form, $form_state);
+ $aid = isset($form_state['values']['actions_aid']) ? $form_state['values']['actions_aid'] : NULL;
+
+ actions_save($function, $form_state['values']['actions_type'], $params, $form_state['values']['actions_label'], $aid);
+ drupal_set_message(t('The action has been successfully saved.'));
+
+ $form_state['redirect'] = 'admin/config/system/actions/manage';
+}
+
+/**
+ * Create the form for confirmation of deleting an action.
+ *
+ * @see system_actions_delete_form_submit()
+ * @ingroup forms
+ */
+function system_actions_delete_form($form, &$form_state, $action) {
+ $form['aid'] = array(
+ '#type' => 'hidden',
+ '#value' => $action->aid,
+ );
+ return confirm_form($form,
+ t('Are you sure you want to delete the action %action?', array('%action' => $action->label)),
+ 'admin/config/system/actions/manage',
+ t('This cannot be undone.'),
+ t('Delete'),
+ t('Cancel')
+ );
+}
+
+/**
+ * Process system_actions_delete form submissions.
+ *
+ * Post-deletion operations for action deletion.
+ */
+function system_actions_delete_form_submit($form, &$form_state) {
+ $aid = $form_state['values']['aid'];
+ $action = actions_load($aid);
+ actions_delete($aid);
+ watchdog('user', 'Deleted action %aid (%action)', array('%aid' => $aid, '%action' => $action->label));
+ drupal_set_message(t('Action %action was deleted', array('%action' => $action->label)));
+ $form_state['redirect'] = 'admin/config/system/actions/manage';
+}
+
+/**
+ * Post-deletion operations for deleting action orphans.
+ *
+ * @param $orphaned
+ * An array of orphaned actions.
+ */
+function system_action_delete_orphans_post($orphaned) {
+ foreach ($orphaned as $callback) {
+ drupal_set_message(t("Deleted orphaned action (%action).", array('%action' => $callback)));
+ }
+}
+
+/**
+ * Remove actions that are in the database but not supported by any enabled module.
+ */
+function system_actions_remove_orphans() {
+ actions_synchronize(TRUE);
+ drupal_goto('admin/config/system/actions/manage');
+}
diff --git a/kolab.org/www/drupal-7.26/modules/system/system.api.php b/kolab.org/www/drupal-7.26/modules/system/system.api.php
new file mode 100644
index 0000000..f54078b
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/system.api.php
@@ -0,0 +1,4899 @@
+<?php
+
+/**
+ * @file
+ * Hooks provided by Drupal core and the System module.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Defines one or more hooks that are exposed by a module.
+ *
+ * Normally hooks do not need to be explicitly defined. However, by declaring a
+ * hook explicitly, a module may define a "group" for it. Modules that implement
+ * a hook may then place their implementation in either $module.module or in
+ * $module.$group.inc. If the hook is located in $module.$group.inc, then that
+ * file will be automatically loaded when needed.
+ * In general, hooks that are rarely invoked and/or are very large should be
+ * placed in a separate include file, while hooks that are very short or very
+ * frequently called should be left in the main module file so that they are
+ * always available.
+ *
+ * @return
+ * An associative array whose keys are hook names and whose values are an
+ * associative array containing:
+ * - group: A string defining the group to which the hook belongs. The module
+ * system will determine whether a file with the name $module.$group.inc
+ * exists, and automatically load it when required.
+ *
+ * See system_hook_info() for all hook groups defined by Drupal core.
+ *
+ * @see hook_hook_info_alter().
+ */
+function hook_hook_info() {
+ $hooks['token_info'] = array(
+ 'group' => 'tokens',
+ );
+ $hooks['tokens'] = array(
+ 'group' => 'tokens',
+ );
+ return $hooks;
+}
+
+/**
+ * Alter information from hook_hook_info().
+ *
+ * @param $hooks
+ * Information gathered by module_hook_info() from other modules'
+ * implementations of hook_hook_info(). Alter this array directly.
+ * See hook_hook_info() for information on what this may contain.
+ */
+function hook_hook_info_alter(&$hooks) {
+ // Our module wants to completely override the core tokens, so make
+ // sure the core token hooks are not found.
+ $hooks['token_info']['group'] = 'mytokens';
+ $hooks['tokens']['group'] = 'mytokens';
+}
+
+/**
+ * Inform the base system and the Field API about one or more entity types.
+ *
+ * Inform the system about one or more entity types (i.e., object types that
+ * can be loaded via entity_load() and, optionally, to which fields can be
+ * attached).
+ *
+ * @return
+ * An array whose keys are entity type names and whose values identify
+ * properties of those types that the system needs to know about:
+ * - label: The human-readable name of the type.
+ * - controller class: The name of the class that is used to load the objects.
+ * The class has to implement the DrupalEntityControllerInterface interface.
+ * Leave blank to use the DrupalDefaultEntityController implementation.
+ * - base table: (used by DrupalDefaultEntityController) The name of the
+ * entity type's base table.
+ * - revision table: The name of the entity type's revision table (if any).
+ * - static cache: (used by DrupalDefaultEntityController) FALSE to disable
+ * static caching of entities during a page request. Defaults to TRUE.
+ * - field cache: (used by Field API loading and saving of field data) FALSE
+ * to disable Field API's persistent cache of field data. Only recommended
+ * if a higher level persistent cache is available for the entity type.
+ * Defaults to TRUE.
+ * - load hook: The name of the hook which should be invoked by
+ * DrupalDefaultEntityController:attachLoad(), for example 'node_load'.
+ * - uri callback: The name of an implementation of
+ * callback_entity_info_uri().
+ * - label callback: (optional) The name of an implementation of
+ * callback_entity_info_label(), which returns the label of the entity. The
+ * entity label is the main string associated with an entity; for example,
+ * the title of a node or the subject of a comment. If there is an entity
+ * object property that defines the label, then using the 'label' element of
+ * the 'entity keys' return value component suffices to provide this
+ * information (see below). Alternatively, specifying this callback allows
+ * more complex logic to determine the label of an entity. See also the
+ * entity_label() function, which implements this logic.
+ * - language callback: (optional) The name of an implementation of
+ * callback_entity_info_language(). In most situations, when needing to
+ * determine this value, inspecting a property named after the 'language'
+ * element of the 'entity keys' should be enough. The language callback is
+ * meant to be used primarily for temporary alterations of the property
+ * value: entity-defining modules are encouraged to always define a
+ * language property, instead of using the callback as main entity language
+ * source. In fact not having a language property defined is likely to
+ * prevent an entity from being queried by language. Moreover, given that
+ * entity_language() is not necessarily used everywhere it would be
+ * appropriate, modules implementing the language callback should be aware
+ * that this might not be always called.
+ * - fieldable: Set to TRUE if you want your entity type to accept fields
+ * being attached to it.
+ * - translation: An associative array of modules registered as field
+ * translation handlers. Array keys are the module names, array values
+ * can be any data structure the module uses to provide field translation.
+ * Any empty value disallows the module to appear as a translation handler.
+ * - entity keys: An array describing how the Field API can extract the
+ * information it needs from the objects of the type. Elements:
+ * - id: The name of the property that contains the primary id of the
+ * entity. Every entity object passed to the Field API must have this
+ * property and its value must be numeric.
+ * - revision: The name of the property that contains the revision id of
+ * the entity. The Field API assumes that all revision ids are unique
+ * across all entities of a type. This entry can be omitted if the
+ * entities of this type are not versionable.
+ * - bundle: The name of the property that contains the bundle name for the
+ * entity. The bundle name defines which set of fields are attached to
+ * the entity (e.g. what nodes call "content type"). This entry can be
+ * omitted if this entity type exposes a single bundle (all entities have
+ * the same collection of fields). The name of this single bundle will be
+ * the same as the entity type.
+ * - label: The name of the property that contains the entity label. For
+ * example, if the entity's label is located in $entity->subject, then
+ * 'subject' should be specified here. If complex logic is required to
+ * build the label, a 'label callback' should be defined instead (see
+ * the 'label callback' section above for details).
+ * - language: The name of the property, typically 'language', that contains
+ * the language code representing the language the entity has been created
+ * in. This value may be changed when editing the entity and represents
+ * the language its textual components are supposed to have. If no
+ * language property is available, the 'language callback' may be used
+ * instead. This entry can be omitted if the entities of this type are not
+ * language-aware.
+ * - bundle keys: An array describing how the Field API can extract the
+ * information it needs from the bundle objects for this type. This entry
+ * is required if the 'path' provided in the 'bundles'/'admin' section
+ * identifies the bundle using a named menu placeholder whose loader
+ * callback returns an object (e.g., $vocabulary for taxonomy terms, or
+ * $node_type for nodes). If the path does not include the bundle, or the
+ * bundle is just a string rather than an automatically loaded object, then
+ * this can be omitted. Elements:
+ * - bundle: The name of the property of the bundle object that contains
+ * the name of the bundle object.
+ * - bundles: An array describing all bundles for this object type. Keys are
+ * bundles machine names, as found in the objects' 'bundle' property
+ * (defined in the 'entity keys' entry above). This entry can be omitted if
+ * this entity type exposes a single bundle (all entities have the same
+ * collection of fields). The name of this single bundle will be the same as
+ * the entity type. Elements:
+ * - label: The human-readable name of the bundle.
+ * - uri callback: Same as the 'uri callback' key documented above for the
+ * entity type, but for the bundle only. When determining the URI of an
+ * entity, if a 'uri callback' is defined for both the entity type and
+ * the bundle, the one for the bundle is used.
+ * - admin: An array of information that allows Field UI pages to attach
+ * themselves to the existing administration pages for the bundle.
+ * Elements:
+ * - path: the path of the bundle's main administration page, as defined
+ * in hook_menu(). If the path includes a placeholder for the bundle,
+ * the 'bundle argument' and 'real path' keys below are required.
+ * - bundle argument: The position of the bundle placeholder in 'path', if
+ * any.
+ * - real path: The actual path (no placeholder) of the bundle's main
+ * administration page. This will be used to generate links.
+ * - access callback: As in hook_menu(). 'user_access' will be assumed if
+ * no value is provided.
+ * - access arguments: As in hook_menu().
+ * - view modes: An array describing the view modes for the entity type. View
+ * modes let entities be displayed differently depending on the context.
+ * For instance, a node can be displayed differently on its own page
+ * ('full' mode), on the home page or taxonomy listings ('teaser' mode), or
+ * in an RSS feed ('rss' mode). Modules taking part in the display of the
+ * entity (notably the Field API) can adjust their behavior depending on
+ * the requested view mode. An additional 'default' view mode is available
+ * for all entity types. This view mode is not intended for actual entity
+ * display, but holds default display settings. For each available view
+ * mode, administrators can configure whether it should use its own set of
+ * field display settings, or just replicate the settings of the 'default'
+ * view mode, thus reducing the amount of display configurations to keep
+ * track of. Keys of the array are view mode names. Each view mode is
+ * described by an array with the following key/value pairs:
+ * - label: The human-readable name of the view mode
+ * - custom settings: A boolean specifying whether the view mode should by
+ * default use its own custom field display settings. If FALSE, entities
+ * displayed in this view mode will reuse the 'default' display settings
+ * by default (e.g. right after the module exposing the view mode is
+ * enabled), but administrators can later use the Field UI to apply custom
+ * display settings specific to the view mode.
+ *
+ * @see entity_load()
+ * @see hook_entity_info_alter()
+ */
+function hook_entity_info() {
+ $return = array(
+ 'node' => array(
+ 'label' => t('Node'),
+ 'controller class' => 'NodeController',
+ 'base table' => 'node',
+ 'revision table' => 'node_revision',
+ 'uri callback' => 'node_uri',
+ 'fieldable' => TRUE,
+ 'translation' => array(
+ 'locale' => TRUE,
+ ),
+ 'entity keys' => array(
+ 'id' => 'nid',
+ 'revision' => 'vid',
+ 'bundle' => 'type',
+ 'language' => 'language',
+ ),
+ 'bundle keys' => array(
+ 'bundle' => 'type',
+ ),
+ 'bundles' => array(),
+ 'view modes' => array(
+ 'full' => array(
+ 'label' => t('Full content'),
+ 'custom settings' => FALSE,
+ ),
+ 'teaser' => array(
+ 'label' => t('Teaser'),
+ 'custom settings' => TRUE,
+ ),
+ 'rss' => array(
+ 'label' => t('RSS'),
+ 'custom settings' => FALSE,
+ ),
+ ),
+ ),
+ );
+
+ // Search integration is provided by node.module, so search-related
+ // view modes for nodes are defined here and not in search.module.
+ if (module_exists('search')) {
+ $return['node']['view modes'] += array(
+ 'search_index' => array(
+ 'label' => t('Search index'),
+ 'custom settings' => FALSE,
+ ),
+ 'search_result' => array(
+ 'label' => t('Search result'),
+ 'custom settings' => FALSE,
+ ),
+ );
+ }
+
+ // Bundles must provide a human readable name so we can create help and error
+ // messages, and the path to attach Field admin pages to.
+ foreach (node_type_get_names() as $type => $name) {
+ $return['node']['bundles'][$type] = array(
+ 'label' => $name,
+ 'admin' => array(
+ 'path' => 'admin/structure/types/manage/%node_type',
+ 'real path' => 'admin/structure/types/manage/' . str_replace('_', '-', $type),
+ 'bundle argument' => 4,
+ 'access arguments' => array('administer content types'),
+ ),
+ );
+ }
+
+ return $return;
+}
+
+/**
+ * Alter the entity info.
+ *
+ * Modules may implement this hook to alter the information that defines an
+ * entity. All properties that are available in hook_entity_info() can be
+ * altered here.
+ *
+ * @param $entity_info
+ * The entity info array, keyed by entity name.
+ *
+ * @see hook_entity_info()
+ */
+function hook_entity_info_alter(&$entity_info) {
+ // Set the controller class for nodes to an alternate implementation of the
+ // DrupalEntityController interface.
+ $entity_info['node']['controller class'] = 'MyCustomNodeController';
+}
+
+/**
+ * Act on entities when loaded.
+ *
+ * This is a generic load hook called for all entity types loaded via the
+ * entity API.
+ *
+ * @param $entities
+ * The entities keyed by entity ID.
+ * @param $type
+ * The type of entities being loaded (i.e. node, user, comment).
+ */
+function hook_entity_load($entities, $type) {
+ foreach ($entities as $entity) {
+ $entity->foo = mymodule_add_something($entity, $type);
+ }
+}
+
+/**
+ * Act on an entity before it is about to be created or updated.
+ *
+ * @param $entity
+ * The entity object.
+ * @param $type
+ * The type of entity being saved (i.e. node, user, comment).
+ */
+function hook_entity_presave($entity, $type) {
+ $entity->changed = REQUEST_TIME;
+}
+
+/**
+ * Act on entities when inserted.
+ *
+ * @param $entity
+ * The entity object.
+ * @param $type
+ * The type of entity being inserted (i.e. node, user, comment).
+ */
+function hook_entity_insert($entity, $type) {
+ // Insert the new entity into a fictional table of all entities.
+ $info = entity_get_info($type);
+ list($id) = entity_extract_ids($type, $entity);
+ db_insert('example_entity')
+ ->fields(array(
+ 'type' => $type,
+ 'id' => $id,
+ 'created' => REQUEST_TIME,
+ 'updated' => REQUEST_TIME,
+ ))
+ ->execute();
+}
+
+/**
+ * Act on entities when updated.
+ *
+ * @param $entity
+ * The entity object.
+ * @param $type
+ * The type of entity being updated (i.e. node, user, comment).
+ */
+function hook_entity_update($entity, $type) {
+ // Update the entity's entry in a fictional table of all entities.
+ $info = entity_get_info($type);
+ list($id) = entity_extract_ids($type, $entity);
+ db_update('example_entity')
+ ->fields(array(
+ 'updated' => REQUEST_TIME,
+ ))
+ ->condition('type', $type)
+ ->condition('id', $id)
+ ->execute();
+}
+
+/**
+ * Act on entities when deleted.
+ *
+ * @param $entity
+ * The entity object.
+ * @param $type
+ * The type of entity being deleted (i.e. node, user, comment).
+ */
+function hook_entity_delete($entity, $type) {
+ // Delete the entity's entry from a fictional table of all entities.
+ $info = entity_get_info($type);
+ list($id) = entity_extract_ids($type, $entity);
+ db_delete('example_entity')
+ ->condition('type', $type)
+ ->condition('id', $id)
+ ->execute();
+}
+
+/**
+ * Alter or execute an EntityFieldQuery.
+ *
+ * @param EntityFieldQuery $query
+ * An EntityFieldQuery. One of the most important properties to be changed is
+ * EntityFieldQuery::executeCallback. If this is set to an existing function,
+ * this function will get the query as its single argument and its result
+ * will be the returned as the result of EntityFieldQuery::execute(). This can
+ * be used to change the behavior of EntityFieldQuery entirely. For example,
+ * the default implementation can only deal with one field storage engine, but
+ * it is possible to write a module that can query across field storage
+ * engines. Also, the default implementation presumes entities are stored in
+ * SQL, but the execute callback could instead query any other entity storage,
+ * local or remote.
+ *
+ * Note the $query->altered attribute which is TRUE in case the query has
+ * already been altered once. This happens with cloned queries.
+ * If there is a pager, then such a cloned query will be executed to count
+ * all elements. This query can be detected by checking for
+ * ($query->pager && $query->count), allowing the driver to return 0 from
+ * the count query and disable the pager.
+ */
+function hook_entity_query_alter($query) {
+ $query->executeCallback = 'my_module_query_callback';
+}
+
+/**
+ * Act on entities being assembled before rendering.
+ *
+ * @param $entity
+ * The entity object.
+ * @param $type
+ * The type of entity being rendered (i.e. node, user, comment).
+ * @param $view_mode
+ * The view mode the entity is rendered in.
+ * @param $langcode
+ * The language code used for rendering.
+ *
+ * The module may add elements to $entity->content prior to rendering. The
+ * structure of $entity->content is a renderable array as expected by
+ * drupal_render().
+ *
+ * @see hook_entity_view_alter()
+ * @see hook_comment_view()
+ * @see hook_node_view()
+ * @see hook_user_view()
+ */
+function hook_entity_view($entity, $type, $view_mode, $langcode) {
+ $entity->content['my_additional_field'] = array(
+ '#markup' => $additional_field,
+ '#weight' => 10,
+ '#theme' => 'mymodule_my_additional_field',
+ );
+}
+
+/**
+ * Alter the results of ENTITY_view().
+ *
+ * This hook is called after the content has been assembled in a structured
+ * array and may be used for doing processing which requires that the complete
+ * entity content structure has been built.
+ *
+ * If a module wishes to act on the rendered HTML of the entity rather than the
+ * structured content array, it may use this hook to add a #post_render
+ * callback. Alternatively, it could also implement hook_preprocess_ENTITY().
+ * See drupal_render() and theme() for details.
+ *
+ * @param $build
+ * A renderable array representing the entity content.
+ * @param $type
+ * The type of entity being rendered (i.e. node, user, comment).
+ *
+ * @see hook_entity_view()
+ * @see hook_comment_view_alter()
+ * @see hook_node_view_alter()
+ * @see hook_taxonomy_term_view_alter()
+ * @see hook_user_view_alter()
+ */
+function hook_entity_view_alter(&$build, $type) {
+ if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) {
+ // Change its weight.
+ $build['an_additional_field']['#weight'] = -10;
+
+ // Add a #post_render callback to act on the rendered HTML of the entity.
+ $build['#post_render'][] = 'my_module_node_post_render';
+ }
+}
+
+/**
+ * Change the view mode of an entity that is being displayed.
+ *
+ * @param string $view_mode
+ * The view_mode that is to be used to display the entity.
+ * @param array $context
+ * Array with contextual information, including:
+ * - entity_type: The type of the entity that is being viewed.
+ * - entity: The entity object.
+ * - langcode: The langcode the entity is being viewed in.
+ */
+function hook_entity_view_mode_alter(&$view_mode, $context) {
+ // For nodes, change the view mode when it is teaser.
+ if ($context['entity_type'] == 'node' && $view_mode == 'teaser') {
+ $view_mode = 'my_custom_view_mode';
+ }
+}
+
+/**
+ * Define administrative paths.
+ *
+ * Modules may specify whether or not the paths they define in hook_menu() are
+ * to be considered administrative. Other modules may use this information to
+ * display those pages differently (e.g. in a modal overlay, or in a different
+ * theme).
+ *
+ * To change the administrative status of menu items defined in another module's
+ * hook_menu(), modules should implement hook_admin_paths_alter().
+ *
+ * @return
+ * An associative array. For each item, the key is the path in question, in
+ * a format acceptable to drupal_match_path(). The value for each item should
+ * be TRUE (for paths considered administrative) or FALSE (for non-
+ * administrative paths).
+ *
+ * @see hook_menu()
+ * @see drupal_match_path()
+ * @see hook_admin_paths_alter()
+ */
+function hook_admin_paths() {
+ $paths = array(
+ 'mymodule/*/add' => TRUE,
+ 'mymodule/*/edit' => TRUE,
+ );
+ return $paths;
+}
+
+/**
+ * Redefine administrative paths defined by other modules.
+ *
+ * @param $paths
+ * An associative array of administrative paths, as defined by implementations
+ * of hook_admin_paths().
+ *
+ * @see hook_admin_paths()
+ */
+function hook_admin_paths_alter(&$paths) {
+ // Treat all user pages as administrative.
+ $paths['user'] = TRUE;
+ $paths['user/*'] = TRUE;
+ // Treat the forum topic node form as a non-administrative page.
+ $paths['node/add/forum'] = FALSE;
+}
+
+/**
+ * Act on entities as they are being prepared for view.
+ *
+ * Allows you to operate on multiple entities as they are being prepared for
+ * view. Only use this if attaching the data during the entity_load() phase
+ * is not appropriate, for example when attaching other 'entity' style objects.
+ *
+ * @param $entities
+ * The entities keyed by entity ID.
+ * @param $type
+ * The type of entities being loaded (i.e. node, user, comment).
+ * @param $langcode
+ * The language to display the entity in.
+ */
+function hook_entity_prepare_view($entities, $type, $langcode) {
+ // Load a specific node into the user object for later theming.
+ if ($type == 'user') {
+ $nodes = mymodule_get_user_nodes(array_keys($entities));
+ foreach ($entities as $uid => $entity) {
+ $entity->user_node = $nodes[$uid];
+ }
+ }
+}
+
+/**
+ * Perform periodic actions.
+ *
+ * Modules that require some commands to be executed periodically can
+ * implement hook_cron(). The engine will then call the hook whenever a cron
+ * run happens, as defined by the administrator. Typical tasks managed by
+ * hook_cron() are database maintenance, backups, recalculation of settings
+ * or parameters, automated mailing, and retrieving remote data.
+ *
+ * Short-running or non-resource-intensive tasks can be executed directly in
+ * the hook_cron() implementation.
+ *
+ * Long-running tasks and tasks that could time out, such as retrieving remote
+ * data, sending email, and intensive file tasks, should use the queue API
+ * instead of executing the tasks directly. To do this, first define one or
+ * more queues via hook_cron_queue_info(). Then, add items that need to be
+ * processed to the defined queues.
+ */
+function hook_cron() {
+ // Short-running operation example, not using a queue:
+ // Delete all expired records since the last cron run.
+ $expires = variable_get('mymodule_cron_last_run', REQUEST_TIME);
+ db_delete('mymodule_table')
+ ->condition('expires', $expires, '>=')
+ ->execute();
+ variable_set('mymodule_cron_last_run', REQUEST_TIME);
+
+ // Long-running operation example, leveraging a queue:
+ // Fetch feeds from other sites.
+ $result = db_query('SELECT * FROM {aggregator_feed} WHERE checked + refresh < :time AND refresh <> :never', array(
+ ':time' => REQUEST_TIME,
+ ':never' => AGGREGATOR_CLEAR_NEVER,
+ ));
+ $queue = DrupalQueue::get('aggregator_feeds');
+ foreach ($result as $feed) {
+ $queue->createItem($feed);
+ }
+}
+
+/**
+ * Declare queues holding items that need to be run periodically.
+ *
+ * While there can be only one hook_cron() process running at the same time,
+ * there can be any number of processes defined here running. Because of
+ * this, long running tasks are much better suited for this API. Items queued
+ * in hook_cron() might be processed in the same cron run if there are not many
+ * items in the queue, otherwise it might take several requests, which can be
+ * run in parallel.
+ *
+ * @return
+ * An associative array where the key is the queue name and the value is
+ * again an associative array. Possible keys are:
+ * - 'worker callback': The name of the function to call. It will be called
+ * with one argument, the item created via DrupalQueue::createItem().
+ * - 'time': (optional) How much time Drupal should spend on calling this
+ * worker in seconds. Defaults to 15.
+ * - 'skip on cron': (optional) Set to TRUE to avoid being processed during
+ * cron runs (for example, if you want to control all queue execution
+ * manually).
+ *
+ * @see hook_cron()
+ * @see hook_cron_queue_info_alter()
+ */
+function hook_cron_queue_info() {
+ $queues['aggregator_feeds'] = array(
+ 'worker callback' => 'aggregator_refresh',
+ 'time' => 60,
+ );
+ return $queues;
+}
+
+/**
+ * Alter cron queue information before cron runs.
+ *
+ * Called by drupal_cron_run() to allow modules to alter cron queue settings
+ * before any jobs are processesed.
+ *
+ * @param array $queues
+ * An array of cron queue information.
+ *
+ * @see hook_cron_queue_info()
+ * @see drupal_cron_run()
+ */
+function hook_cron_queue_info_alter(&$queues) {
+ // This site has many feeds so let's spend 90 seconds on each cron run
+ // updating feeds instead of the default 60.
+ $queues['aggregator_feeds']['time'] = 90;
+}
+
+/**
+ * Allows modules to declare their own Form API element types and specify their
+ * default values.
+ *
+ * This hook allows modules to declare their own form element types and to
+ * specify their default values. The values returned by this hook will be
+ * merged with the elements returned by hook_form() implementations and so
+ * can return defaults for any Form APIs keys in addition to those explicitly
+ * mentioned below.
+ *
+ * Each of the form element types defined by this hook is assumed to have
+ * a matching theme function, e.g. theme_elementtype(), which should be
+ * registered with hook_theme() as normal.
+ *
+ * For more information about custom element types see the explanation at
+ * http://drupal.org/node/169815.
+ *
+ * @return
+ * An associative array describing the element types being defined. The array
+ * contains a sub-array for each element type, with the machine-readable type
+ * name as the key. Each sub-array has a number of possible attributes:
+ * - "#input": boolean indicating whether or not this element carries a value
+ * (even if it's hidden).
+ * - "#process": array of callback functions taking $element, $form_state,
+ * and $complete_form.
+ * - "#after_build": array of callback functions taking $element and $form_state.
+ * - "#validate": array of callback functions taking $form and $form_state.
+ * - "#element_validate": array of callback functions taking $element and
+ * $form_state.
+ * - "#pre_render": array of callback functions taking $element and $form_state.
+ * - "#post_render": array of callback functions taking $element and $form_state.
+ * - "#submit": array of callback functions taking $form and $form_state.
+ * - "#title_display": optional string indicating if and how #title should be
+ * displayed, see theme_form_element() and theme_form_element_label().
+ *
+ * @see hook_element_info_alter()
+ * @see system_element_info()
+ */
+function hook_element_info() {
+ $types['filter_format'] = array(
+ '#input' => TRUE,
+ );
+ return $types;
+}
+
+/**
+ * Alter the element type information returned from modules.
+ *
+ * A module may implement this hook in order to alter the element type defaults
+ * defined by a module.
+ *
+ * @param $type
+ * All element type defaults as collected by hook_element_info().
+ *
+ * @see hook_element_info()
+ */
+function hook_element_info_alter(&$type) {
+ // Decrease the default size of textfields.
+ if (isset($type['textfield']['#size'])) {
+ $type['textfield']['#size'] = 40;
+ }
+}
+
+/**
+ * Perform cleanup tasks.
+ *
+ * This hook is run at the end of most regular page requests. It is often
+ * used for page logging and specialized cleanup. This hook MUST NOT print
+ * anything because by the time it runs the response is already sent to
+ * the browser.
+ *
+ * Only use this hook if your code must run even for cached page views.
+ * If you have code which must run once on all non-cached pages, use
+ * hook_init() instead. That is the usual case. If you implement this hook
+ * and see an error like 'Call to undefined function', it is likely that
+ * you are depending on the presence of a module which has not been loaded yet.
+ * It is not loaded because Drupal is still in bootstrap mode.
+ *
+ * @param $destination
+ * If this hook is invoked as part of a drupal_goto() call, then this argument
+ * will be a fully-qualified URL that is the destination of the redirect.
+ */
+function hook_exit($destination = NULL) {
+ db_update('counter')
+ ->expression('hits', 'hits + 1')
+ ->condition('type', 1)
+ ->execute();
+}
+
+/**
+ * Perform necessary alterations to the JavaScript before it is presented on
+ * the page.
+ *
+ * @param $javascript
+ * An array of all JavaScript being presented on the page.
+ *
+ * @see drupal_add_js()
+ * @see drupal_get_js()
+ * @see drupal_js_defaults()
+ */
+function hook_js_alter(&$javascript) {
+ // Swap out jQuery to use an updated version of the library.
+ $javascript['misc/jquery.js']['data'] = drupal_get_path('module', 'jquery_update') . '/jquery.js';
+}
+
+/**
+ * Registers JavaScript/CSS libraries associated with a module.
+ *
+ * Modules implementing this return an array of arrays. The key to each
+ * sub-array is the machine readable name of the library. Each library may
+ * contain the following items:
+ *
+ * - 'title': The human readable name of the library.
+ * - 'website': The URL of the library's web site.
+ * - 'version': A string specifying the version of the library; intentionally
+ * not a float because a version like "1.2.3" is not a valid float. Use PHP's
+ * version_compare() to compare different versions.
+ * - 'js': An array of JavaScript elements; each element's key is used as $data
+ * argument, each element's value is used as $options array for
+ * drupal_add_js(). To add library-specific (not module-specific) JavaScript
+ * settings, the key may be skipped, the value must specify
+ * 'type' => 'setting', and the actual settings must be contained in a 'data'
+ * element of the value.
+ * - 'css': Like 'js', an array of CSS elements passed to drupal_add_css().
+ * - 'dependencies': An array of libraries that are required for a library. Each
+ * element is an array listing the module and name of another library. Note
+ * that all dependencies for each dependent library will also be added when
+ * this library is added.
+ *
+ * Registered information for a library should contain re-usable data only.
+ * Module- or implementation-specific data and integration logic should be added
+ * separately.
+ *
+ * @return
+ * An array defining libraries associated with a module.
+ *
+ * @see system_library()
+ * @see drupal_add_library()
+ * @see drupal_get_library()
+ */
+function hook_library() {
+ // Library One.
+ $libraries['library-1'] = array(
+ 'title' => 'Library One',
+ 'website' => 'http://example.com/library-1',
+ 'version' => '1.2',
+ 'js' => array(
+ drupal_get_path('module', 'my_module') . '/library-1.js' => array(),
+ ),
+ 'css' => array(
+ drupal_get_path('module', 'my_module') . '/library-2.css' => array(
+ 'type' => 'file',
+ 'media' => 'screen',
+ ),
+ ),
+ );
+ // Library Two.
+ $libraries['library-2'] = array(
+ 'title' => 'Library Two',
+ 'website' => 'http://example.com/library-2',
+ 'version' => '3.1-beta1',
+ 'js' => array(
+ // JavaScript settings may use the 'data' key.
+ array(
+ 'type' => 'setting',
+ 'data' => array('library2' => TRUE),
+ ),
+ ),
+ 'dependencies' => array(
+ // Require jQuery UI core by System module.
+ array('system', 'ui'),
+ // Require our other library.
+ array('my_module', 'library-1'),
+ // Require another library.
+ array('other_module', 'library-3'),
+ ),
+ );
+ return $libraries;
+}
+
+/**
+ * Alters the JavaScript/CSS library registry.
+ *
+ * Allows certain, contributed modules to update libraries to newer versions
+ * while ensuring backwards compatibility. In general, such manipulations should
+ * only be done by designated modules, since most modules that integrate with a
+ * certain library also depend on the API of a certain library version.
+ *
+ * @param $libraries
+ * The JavaScript/CSS libraries provided by $module. Keyed by internal library
+ * name and passed by reference.
+ * @param $module
+ * The name of the module that registered the libraries.
+ *
+ * @see hook_library()
+ */
+function hook_library_alter(&$libraries, $module) {
+ // Update Farbtastic to version 2.0.
+ if ($module == 'system' && isset($libraries['farbtastic'])) {
+ // Verify existing version is older than the one we are updating to.
+ if (version_compare($libraries['farbtastic']['version'], '2.0', '<')) {
+ // Update the existing Farbtastic to version 2.0.
+ $libraries['farbtastic']['version'] = '2.0';
+ $libraries['farbtastic']['js'] = array(
+ drupal_get_path('module', 'farbtastic_update') . '/farbtastic-2.0.js' => array(),
+ );
+ }
+ }
+}
+
+/**
+ * Alter CSS files before they are output on the page.
+ *
+ * @param $css
+ * An array of all CSS items (files and inline CSS) being requested on the page.
+ *
+ * @see drupal_add_css()
+ * @see drupal_get_css()
+ */
+function hook_css_alter(&$css) {
+ // Remove defaults.css file.
+ unset($css[drupal_get_path('module', 'system') . '/defaults.css']);
+}
+
+/**
+ * Alter the commands that are sent to the user through the Ajax framework.
+ *
+ * @param $commands
+ * An array of all commands that will be sent to the user.
+ *
+ * @see ajax_render()
+ */
+function hook_ajax_render_alter($commands) {
+ // Inject any new status messages into the content area.
+ $commands[] = ajax_command_prepend('#block-system-main .content', theme('status_messages'));
+}
+
+/**
+ * Add elements to a page before it is rendered.
+ *
+ * Use this hook when you want to add elements at the page level. For your
+ * additions to be printed, they have to be placed below a top level array key
+ * of the $page array that has the name of a region of the active theme.
+ *
+ * By default, valid region keys are 'page_top', 'header', 'sidebar_first',
+ * 'content', 'sidebar_second' and 'page_bottom'. To get a list of all regions
+ * of the active theme, use system_region_list($theme). Note that $theme is a
+ * global variable.
+ *
+ * If you want to alter the elements added by other modules or if your module
+ * depends on the elements of other modules, use hook_page_alter() instead which
+ * runs after this hook.
+ *
+ * @param $page
+ * Nested array of renderable elements that make up the page.
+ *
+ * @see hook_page_alter()
+ * @see drupal_render_page()
+ */
+function hook_page_build(&$page) {
+ if (menu_get_object('node', 1)) {
+ // We are on a node detail page. Append a standard disclaimer to the
+ // content region.
+ $page['content']['disclaimer'] = array(
+ '#markup' => t('Acme, Inc. is not responsible for the contents of this sample code.'),
+ '#weight' => 25,
+ );
+ }
+}
+
+/**
+ * Alter a menu router item right after it has been retrieved from the database or cache.
+ *
+ * This hook is invoked by menu_get_item() and allows for run-time alteration of router
+ * information (page_callback, title, and so on) before it is translated and checked for
+ * access. The passed-in $router_item is statically cached for the current request, so this
+ * hook is only invoked once for any router item that is retrieved via menu_get_item().
+ *
+ * Usually, modules will only want to inspect the router item and conditionally
+ * perform other actions (such as preparing a state for the current request).
+ * Note that this hook is invoked for any router item that is retrieved by
+ * menu_get_item(), which may or may not be called on the path itself, so implementations
+ * should check the $path parameter if the alteration should fire for the current request
+ * only.
+ *
+ * @param $router_item
+ * The menu router item for $path.
+ * @param $path
+ * The originally passed path, for which $router_item is responsible.
+ * @param $original_map
+ * The path argument map, as contained in $path.
+ *
+ * @see menu_get_item()
+ */
+function hook_menu_get_item_alter(&$router_item, $path, $original_map) {
+ // When retrieving the router item for the current path...
+ if ($path == $_GET['q']) {
+ // ...call a function that prepares something for this request.
+ mymodule_prepare_something();
+ }
+}
+
+/**
+ * Define menu items and page callbacks.
+ *
+ * This hook enables modules to register paths in order to define how URL
+ * requests are handled. Paths may be registered for URL handling only, or they
+ * can register a link to be placed in a menu (usually the Navigation menu). A
+ * path and its associated information is commonly called a "menu router item".
+ * This hook is rarely called (for example, when modules are enabled), and
+ * its results are cached in the database.
+ *
+ * hook_menu() implementations return an associative array whose keys define
+ * paths and whose values are an associative array of properties for each
+ * path. (The complete list of properties is in the return value section below.)
+ *
+ * @section sec_callback_funcs Callback Functions
+ * The definition for each path may include a page callback function, which is
+ * invoked when the registered path is requested. If there is no other
+ * registered path that fits the requested path better, any further path
+ * components are passed to the callback function. For example, your module
+ * could register path 'abc/def':
+ * @code
+ * function mymodule_menu() {
+ * $items['abc/def'] = array(
+ * 'page callback' => 'mymodule_abc_view',
+ * );
+ * return $items;
+ * }
+ *
+ * function mymodule_abc_view($ghi = 0, $jkl = '') {
+ * // ...
+ * }
+ * @endcode
+ * When path 'abc/def' is requested, no further path components are in the
+ * request, and no additional arguments are passed to the callback function (so
+ * $ghi and $jkl would take the default values as defined in the function
+ * signature). When 'abc/def/123/foo' is requested, $ghi will be '123' and
+ * $jkl will be 'foo'. Note that this automatic passing of optional path
+ * arguments applies only to page and theme callback functions.
+ *
+ * @subsection sub_callback_arguments Callback Arguments
+ * In addition to optional path arguments, the page callback and other callback
+ * functions may specify argument lists as arrays. These argument lists may
+ * contain both fixed/hard-coded argument values and integers that correspond
+ * to path components. When integers are used and the callback function is
+ * called, the corresponding path components will be substituted for the
+ * integers. That is, the integer 0 in an argument list will be replaced with
+ * the first path component, integer 1 with the second, and so on (path
+ * components are numbered starting from zero). To pass an integer without it
+ * being replaced with its respective path component, use the string value of
+ * the integer (e.g., '1') as the argument value. This substitution feature
+ * allows you to re-use a callback function for several different paths. For
+ * example:
+ * @code
+ * function mymodule_menu() {
+ * $items['abc/def'] = array(
+ * 'page callback' => 'mymodule_abc_view',
+ * 'page arguments' => array(1, 'foo'),
+ * );
+ * return $items;
+ * }
+ * @endcode
+ * When path 'abc/def' is requested, the page callback function will get 'def'
+ * as the first argument and (always) 'foo' as the second argument.
+ *
+ * If a page callback function uses an argument list array, and its path is
+ * requested with optional path arguments, then the list array's arguments are
+ * passed to the callback function first, followed by the optional path
+ * arguments. Using the above example, when path 'abc/def/bar/baz' is requested,
+ * mymodule_abc_view() will be called with 'def', 'foo', 'bar' and 'baz' as
+ * arguments, in that order.
+ *
+ * Special care should be taken for the page callback drupal_get_form(), because
+ * your specific form callback function will always receive $form and
+ * &$form_state as the first function arguments:
+ * @code
+ * function mymodule_abc_form($form, &$form_state) {
+ * // ...
+ * return $form;
+ * }
+ * @endcode
+ * See @link form_api Form API documentation @endlink for details.
+ *
+ * @section sec_path_wildcards Wildcards in Paths
+ * @subsection sub_simple_wildcards Simple Wildcards
+ * Wildcards within paths also work with integer substitution. For example,
+ * your module could register path 'my-module/%/edit':
+ * @code
+ * $items['my-module/%/edit'] = array(
+ * 'page callback' => 'mymodule_abc_edit',
+ * 'page arguments' => array(1),
+ * );
+ * @endcode
+ * When path 'my-module/foo/edit' is requested, integer 1 will be replaced
+ * with 'foo' and passed to the callback function. Note that wildcards may not
+ * be used as the first component.
+ *
+ * @subsection sub_autoload_wildcards Auto-Loader Wildcards
+ * Registered paths may also contain special "auto-loader" wildcard components
+ * in the form of '%mymodule_abc', where the '%' part means that this path
+ * component is a wildcard, and the 'mymodule_abc' part defines the prefix for a
+ * load function, which here would be named mymodule_abc_load(). When a matching
+ * path is requested, your load function will receive as its first argument the
+ * path component in the position of the wildcard; load functions may also be
+ * passed additional arguments (see "load arguments" in the return value
+ * section below). For example, your module could register path
+ * 'my-module/%mymodule_abc/edit':
+ * @code
+ * $items['my-module/%mymodule_abc/edit'] = array(
+ * 'page callback' => 'mymodule_abc_edit',
+ * 'page arguments' => array(1),
+ * );
+ * @endcode
+ * When path 'my-module/123/edit' is requested, your load function
+ * mymodule_abc_load() will be invoked with the argument '123', and should
+ * load and return an "abc" object with internal id 123:
+ * @code
+ * function mymodule_abc_load($abc_id) {
+ * return db_query("SELECT * FROM {mymodule_abc} WHERE abc_id = :abc_id", array(':abc_id' => $abc_id))->fetchObject();
+ * }
+ * @endcode
+ * This 'abc' object will then be passed into the callback functions defined
+ * for the menu item, such as the page callback function mymodule_abc_edit()
+ * to replace the integer 1 in the argument array. Note that a load function
+ * should return FALSE when it is unable to provide a loadable object. For
+ * example, the node_load() function for the 'node/%node/edit' menu item will
+ * return FALSE for the path 'node/999/edit' if a node with a node ID of 999
+ * does not exist. The menu routing system will return a 404 error in this case.
+ *
+ * @subsection sub_argument_wildcards Argument Wildcards
+ * You can also define a %wildcard_to_arg() function (for the example menu
+ * entry above this would be 'mymodule_abc_to_arg()'). The _to_arg() function
+ * is invoked to retrieve a value that is used in the path in place of the
+ * wildcard. A good example is user.module, which defines
+ * user_uid_optional_to_arg() (corresponding to the menu entry
+ * 'tracker/%user_uid_optional'). This function returns the user ID of the
+ * current user.
+ *
+ * The _to_arg() function will get called with three arguments:
+ * - $arg: A string representing whatever argument may have been supplied by
+ * the caller (this is particularly useful if you want the _to_arg()
+ * function only supply a (default) value if no other value is specified,
+ * as in the case of user_uid_optional_to_arg().
+ * - $map: An array of all path fragments (e.g. array('node','123','edit') for
+ * 'node/123/edit').
+ * - $index: An integer indicating which element of $map corresponds to $arg.
+ *
+ * _load() and _to_arg() functions may seem similar at first glance, but they
+ * have different purposes and are called at different times. _load()
+ * functions are called when the menu system is collecting arguments to pass
+ * to the callback functions defined for the menu item. _to_arg() functions
+ * are called when the menu system is generating links to related paths, such
+ * as the tabs for a set of MENU_LOCAL_TASK items.
+ *
+ * @section sec_render_tabs Rendering Menu Items As Tabs
+ * You can also make groups of menu items to be rendered (by default) as tabs
+ * on a page. To do that, first create one menu item of type MENU_NORMAL_ITEM,
+ * with your chosen path, such as 'foo'. Then duplicate that menu item, using a
+ * subdirectory path, such as 'foo/tab1', and changing the type to
+ * MENU_DEFAULT_LOCAL_TASK to make it the default tab for the group. Then add
+ * the additional tab items, with paths such as "foo/tab2" etc., with type
+ * MENU_LOCAL_TASK. Example:
+ * @code
+ * // Make "Foo settings" appear on the admin Config page
+ * $items['admin/config/system/foo'] = array(
+ * 'title' => 'Foo settings',
+ * 'type' => MENU_NORMAL_ITEM,
+ * // Page callback, etc. need to be added here.
+ * );
+ * // Make "Tab 1" the main tab on the "Foo settings" page
+ * $items['admin/config/system/foo/tab1'] = array(
+ * 'title' => 'Tab 1',
+ * 'type' => MENU_DEFAULT_LOCAL_TASK,
+ * // Access callback, page callback, and theme callback will be inherited
+ * // from 'admin/config/system/foo', if not specified here to override.
+ * );
+ * // Make an additional tab called "Tab 2" on "Foo settings"
+ * $items['admin/config/system/foo/tab2'] = array(
+ * 'title' => 'Tab 2',
+ * 'type' => MENU_LOCAL_TASK,
+ * // Page callback and theme callback will be inherited from
+ * // 'admin/config/system/foo', if not specified here to override.
+ * // Need to add access callback or access arguments.
+ * );
+ * @endcode
+ *
+ * @return
+ * An array of menu items. Each menu item has a key corresponding to the
+ * Drupal path being registered. The corresponding array value is an
+ * associative array that may contain the following key-value pairs:
+ * - "title": Required. The untranslated title of the menu item.
+ * - "title callback": Function to generate the title; defaults to t().
+ * If you require only the raw string to be output, set this to FALSE.
+ * - "title arguments": Arguments to send to t() or your custom callback,
+ * with path component substitution as described above.
+ * - "description": The untranslated description of the menu item.
+ * - "page callback": The function to call to display a web page when the user
+ * visits the path. If omitted, the parent menu item's callback will be used
+ * instead.
+ * - "page arguments": An array of arguments to pass to the page callback
+ * function, with path component substitution as described above.
+ * - "delivery callback": The function to call to package the result of the
+ * page callback function and send it to the browser. Defaults to
+ * drupal_deliver_html_page() unless a value is inherited from a parent menu
+ * item. Note that this function is called even if the access checks fail,
+ * so any custom delivery callback function should take that into account.
+ * See drupal_deliver_html_page() for an example.
+ * - "access callback": A function returning TRUE if the user has access
+ * rights to this menu item, and FALSE if not. It can also be a boolean
+ * constant instead of a function, and you can also use numeric values
+ * (will be cast to boolean). Defaults to user_access() unless a value is
+ * inherited from the parent menu item; only MENU_DEFAULT_LOCAL_TASK items
+ * can inherit access callbacks. To use the user_access() default callback,
+ * you must specify the permission to check as 'access arguments' (see
+ * below).
+ * - "access arguments": An array of arguments to pass to the access callback
+ * function, with path component substitution as described above. If the
+ * access callback is inherited (see above), the access arguments will be
+ * inherited with it, unless overridden in the child menu item.
+ * - "theme callback": (optional) A function returning the machine-readable
+ * name of the theme that will be used to render the page. If not provided,
+ * the value will be inherited from a parent menu item. If there is no
+ * theme callback, or if the function does not return the name of a current
+ * active theme on the site, the theme for this page will be determined by
+ * either hook_custom_theme() or the default theme instead. As a general
+ * rule, the use of theme callback functions should be limited to pages
+ * whose functionality is very closely tied to a particular theme, since
+ * they can only be overridden by modules which specifically target those
+ * pages in hook_menu_alter(). Modules implementing more generic theme
+ * switching functionality (for example, a module which allows the theme to
+ * be set dynamically based on the current user's role) should use
+ * hook_custom_theme() instead.
+ * - "theme arguments": An array of arguments to pass to the theme callback
+ * function, with path component substitution as described above.
+ * - "file": A file that will be included before the page callback is called;
+ * this allows page callback functions to be in separate files. The file
+ * should be relative to the implementing module's directory unless
+ * otherwise specified by the "file path" option. Does not apply to other
+ * callbacks (only page callback).
+ * - "file path": The path to the directory containing the file specified in
+ * "file". This defaults to the path to the module implementing the hook.
+ * - "load arguments": An array of arguments to be passed to each of the
+ * wildcard object loaders in the path, after the path argument itself.
+ * For example, if a module registers path node/%node/revisions/%/view
+ * with load arguments set to array(3), the '%node' in the path indicates
+ * that the loader function node_load() will be called with the second
+ * path component as the first argument. The 3 in the load arguments
+ * indicates that the fourth path component will also be passed to
+ * node_load() (numbering of path components starts at zero). So, if path
+ * node/12/revisions/29/view is requested, node_load(12, 29) will be called.
+ * There are also two "magic" values that can be used in load arguments.
+ * "%index" indicates the index of the wildcard path component. "%map"
+ * indicates the path components as an array. For example, if a module
+ * registers for several paths of the form 'user/%user_category/edit/*', all
+ * of them can use the same load function user_category_load(), by setting
+ * the load arguments to array('%map', '%index'). For instance, if the user
+ * is editing category 'foo' by requesting path 'user/32/edit/foo', the load
+ * function user_category_load() will be called with 32 as its first
+ * argument, the array ('user', 32, 'edit', 'foo') as the map argument,
+ * and 1 as the index argument (because %user_category is the second path
+ * component and numbering starts at zero). user_category_load() can then
+ * use these values to extract the information that 'foo' is the category
+ * being requested.
+ * - "weight": An integer that determines the relative position of items in
+ * the menu; higher-weighted items sink. Defaults to 0. Menu items with the
+ * same weight are ordered alphabetically.
+ * - "menu_name": Optional. Set this to a custom menu if you don't want your
+ * item to be placed in Navigation.
+ * - "expanded": Optional. If set to TRUE, and if a menu link is provided for
+ * this menu item (as a result of other properties), then the menu link is
+ * always expanded, equivalent to its 'always expanded' checkbox being set
+ * in the UI.
+ * - "context": (optional) Defines the context a tab may appear in. By
+ * default, all tabs are only displayed as local tasks when being rendered
+ * in a page context. All tabs that should be accessible as contextual links
+ * in page region containers outside of the parent menu item's primary page
+ * context should be registered using one of the following contexts:
+ * - MENU_CONTEXT_PAGE: (default) The tab is displayed as local task for the
+ * page context only.
+ * - MENU_CONTEXT_INLINE: The tab is displayed as contextual link outside of
+ * the primary page context only.
+ * Contexts can be combined. For example, to display a tab both on a page
+ * and inline, a menu router item may specify:
+ * @code
+ * 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
+ * @endcode
+ * - "tab_parent": For local task menu items, the path of the task's parent
+ * item; defaults to the same path without the last component (e.g., the
+ * default parent for 'admin/people/create' is 'admin/people').
+ * - "tab_root": For local task menu items, the path of the closest non-tab
+ * item; same default as "tab_parent".
+ * - "position": Position of the block ('left' or 'right') on the system
+ * administration page for this item.
+ * - "type": A bitmask of flags describing properties of the menu item.
+ * Many shortcut bitmasks are provided as constants in menu.inc:
+ * - MENU_NORMAL_ITEM: Normal menu items show up in the menu tree and can be
+ * moved/hidden by the administrator.
+ * - MENU_CALLBACK: Callbacks simply register a path so that the correct
+ * information is generated when the path is accessed.
+ * - MENU_SUGGESTED_ITEM: Modules may "suggest" menu items that the
+ * administrator may enable.
+ * - MENU_LOCAL_ACTION: Local actions are menu items that describe actions
+ * on the parent item such as adding a new user or block, and are
+ * rendered in the action-links list in your theme.
+ * - MENU_LOCAL_TASK: Local tasks are menu items that describe different
+ * displays of data, and are generally rendered as tabs.
+ * - MENU_DEFAULT_LOCAL_TASK: Every set of local tasks should provide one
+ * "default" task, which should display the same page as the parent item.
+ * If the "type" element is omitted, MENU_NORMAL_ITEM is assumed.
+ * - "options": An array of options to be passed to l() when generating a link
+ * from this menu item. Note that the "options" parameter has no effect on
+ * MENU_LOCAL_TASK, MENU_DEFAULT_LOCAL_TASK, and MENU_LOCAL_ACTION items.
+ *
+ * For a detailed usage example, see page_example.module.
+ * For comprehensive documentation on the menu system, see
+ * http://drupal.org/node/102338.
+ */
+function hook_menu() {
+ $items['example'] = array(
+ 'title' => 'Example Page',
+ 'page callback' => 'example_page',
+ 'access arguments' => array('access content'),
+ 'type' => MENU_SUGGESTED_ITEM,
+ );
+ $items['example/feed'] = array(
+ 'title' => 'Example RSS feed',
+ 'page callback' => 'example_feed',
+ 'access arguments' => array('access content'),
+ 'type' => MENU_CALLBACK,
+ );
+
+ return $items;
+}
+
+/**
+ * Alter the data being saved to the {menu_router} table after hook_menu is invoked.
+ *
+ * This hook is invoked by menu_router_build(). The menu definitions are passed
+ * in by reference. Each element of the $items array is one item returned
+ * by a module from hook_menu. Additional items may be added, or existing items
+ * altered.
+ *
+ * @param $items
+ * Associative array of menu router definitions returned from hook_menu().
+ */
+function hook_menu_alter(&$items) {
+ // Example - disable the page at node/add
+ $items['node/add']['access callback'] = FALSE;
+}
+
+/**
+ * Alter the data being saved to the {menu_links} table by menu_link_save().
+ *
+ * @param $item
+ * Associative array defining a menu link as passed into menu_link_save().
+ *
+ * @see hook_translated_menu_link_alter()
+ */
+function hook_menu_link_alter(&$item) {
+ // Make all new admin links hidden (a.k.a disabled).
+ if (strpos($item['link_path'], 'admin') === 0 && empty($item['mlid'])) {
+ $item['hidden'] = 1;
+ }
+ // Flag a link to be altered by hook_translated_menu_link_alter().
+ if ($item['link_path'] == 'devel/cache/clear') {
+ $item['options']['alter'] = TRUE;
+ }
+ // Flag a link to be altered by hook_translated_menu_link_alter(), but only
+ // if it is derived from a menu router item; i.e., do not alter a custom
+ // menu link pointing to the same path that has been created by a user.
+ if ($item['link_path'] == 'user' && $item['module'] == 'system') {
+ $item['options']['alter'] = TRUE;
+ }
+}
+
+/**
+ * Alter a menu link after it has been translated and before it is rendered.
+ *
+ * This hook is invoked from _menu_link_translate() after a menu link has been
+ * translated; i.e., after dynamic path argument placeholders (%) have been
+ * replaced with actual values, the user access to the link's target page has
+ * been checked, and the link has been localized. It is only invoked if
+ * $item['options']['alter'] has been set to a non-empty value (e.g., TRUE).
+ * This flag should be set using hook_menu_link_alter().
+ *
+ * Implementations of this hook are able to alter any property of the menu link.
+ * For example, this hook may be used to add a page-specific query string to all
+ * menu links, or hide a certain link by setting:
+ * @code
+ * 'hidden' => 1,
+ * @endcode
+ *
+ * @param $item
+ * Associative array defining a menu link after _menu_link_translate()
+ * @param $map
+ * Associative array containing the menu $map (path parts and/or objects).
+ *
+ * @see hook_menu_link_alter()
+ */
+function hook_translated_menu_link_alter(&$item, $map) {
+ if ($item['href'] == 'devel/cache/clear') {
+ $item['localized_options']['query'] = drupal_get_destination();
+ }
+}
+
+/**
+ * Inform modules that a menu link has been created.
+ *
+ * This hook is used to notify modules that menu items have been
+ * created. Contributed modules may use the information to perform
+ * actions based on the information entered into the menu system.
+ *
+ * @param $link
+ * Associative array defining a menu link as passed into menu_link_save().
+ *
+ * @see hook_menu_link_update()
+ * @see hook_menu_link_delete()
+ */
+function hook_menu_link_insert($link) {
+ // In our sample case, we track menu items as editing sections
+ // of the site. These are stored in our table as 'disabled' items.
+ $record['mlid'] = $link['mlid'];
+ $record['menu_name'] = $link['menu_name'];
+ $record['status'] = 0;
+ drupal_write_record('menu_example', $record);
+}
+
+/**
+ * Inform modules that a menu link has been updated.
+ *
+ * This hook is used to notify modules that menu items have been
+ * updated. Contributed modules may use the information to perform
+ * actions based on the information entered into the menu system.
+ *
+ * @param $link
+ * Associative array defining a menu link as passed into menu_link_save().
+ *
+ * @see hook_menu_link_insert()
+ * @see hook_menu_link_delete()
+ */
+function hook_menu_link_update($link) {
+ // If the parent menu has changed, update our record.
+ $menu_name = db_query("SELECT menu_name FROM {menu_example} WHERE mlid = :mlid", array(':mlid' => $link['mlid']))->fetchField();
+ if ($menu_name != $link['menu_name']) {
+ db_update('menu_example')
+ ->fields(array('menu_name' => $link['menu_name']))
+ ->condition('mlid', $link['mlid'])
+ ->execute();
+ }
+}
+
+/**
+ * Inform modules that a menu link has been deleted.
+ *
+ * This hook is used to notify modules that menu items have been
+ * deleted. Contributed modules may use the information to perform
+ * actions based on the information entered into the menu system.
+ *
+ * @param $link
+ * Associative array defining a menu link as passed into menu_link_save().
+ *
+ * @see hook_menu_link_insert()
+ * @see hook_menu_link_update()
+ */
+function hook_menu_link_delete($link) {
+ // Delete the record from our table.
+ db_delete('menu_example')
+ ->condition('mlid', $link['mlid'])
+ ->execute();
+}
+
+/**
+ * Alter tabs and actions displayed on the page before they are rendered.
+ *
+ * This hook is invoked by menu_local_tasks(). The system-determined tabs and
+ * actions are passed in by reference. Additional tabs or actions may be added,
+ * or existing items altered.
+ *
+ * Each tab or action is an associative array containing:
+ * - #theme: The theme function to use to render.
+ * - #link: An associative array containing:
+ * - title: The localized title of the link.
+ * - href: The system path to link to.
+ * - localized_options: An array of options to pass to l().
+ * - #active: Whether the link should be marked as 'active'.
+ *
+ * @param $data
+ * An associative array containing:
+ * - actions: An associative array containing:
+ * - count: The amount of actions determined by the menu system, which can
+ * be ignored.
+ * - output: A list of of actions, each one being an associative array
+ * as described above.
+ * - tabs: An indexed array (list) of tab levels (up to 2 levels), each
+ * containing an associative array:
+ * - count: The amount of tabs determined by the menu system. This value
+ * does not need to be altered if there is more than one tab.
+ * - output: A list of of tabs, each one being an associative array as
+ * described above.
+ * @param $router_item
+ * The menu system router item of the page.
+ * @param $root_path
+ * The path to the root item for this set of tabs.
+ */
+function hook_menu_local_tasks_alter(&$data, $router_item, $root_path) {
+ // Add an action linking to node/add to all pages.
+ $data['actions']['output'][] = array(
+ '#theme' => 'menu_local_task',
+ '#link' => array(
+ 'title' => t('Add new content'),
+ 'href' => 'node/add',
+ 'localized_options' => array(
+ 'attributes' => array(
+ 'title' => t('Add new content'),
+ ),
+ ),
+ ),
+ );
+
+ // Add a tab linking to node/add to all pages.
+ $data['tabs'][0]['output'][] = array(
+ '#theme' => 'menu_local_task',
+ '#link' => array(
+ 'title' => t('Example tab'),
+ 'href' => 'node/add',
+ 'localized_options' => array(
+ 'attributes' => array(
+ 'title' => t('Add new content'),
+ ),
+ ),
+ ),
+ // Define whether this link is active. This can be omitted for
+ // implementations that add links to pages outside of the current page
+ // context.
+ '#active' => ($router_item['path'] == $root_path),
+ );
+}
+
+/**
+ * Alter links in the active trail before it is rendered as the breadcrumb.
+ *
+ * This hook is invoked by menu_get_active_breadcrumb() and allows alteration
+ * of the breadcrumb links for the current page, which may be preferred instead
+ * of setting a custom breadcrumb via drupal_set_breadcrumb().
+ *
+ * Implementations should take into account that menu_get_active_breadcrumb()
+ * subsequently performs the following adjustments to the active trail *after*
+ * this hook has been invoked:
+ * - The last link in $active_trail is removed, if its 'href' is identical to
+ * the 'href' of $item. This happens, because the breadcrumb normally does
+ * not contain a link to the current page.
+ * - The (second to) last link in $active_trail is removed, if the current $item
+ * is a MENU_DEFAULT_LOCAL_TASK. This happens in order to do not show a link
+ * to the current page, when being on the path for the default local task;
+ * e.g. when being on the path node/%/view, the breadcrumb should not contain
+ * a link to node/%.
+ *
+ * Each link in the active trail must contain:
+ * - title: The localized title of the link.
+ * - href: The system path to link to.
+ * - localized_options: An array of options to pass to url().
+ *
+ * @param $active_trail
+ * An array containing breadcrumb links for the current page.
+ * @param $item
+ * The menu router item of the current page.
+ *
+ * @see drupal_set_breadcrumb()
+ * @see menu_get_active_breadcrumb()
+ * @see menu_get_active_trail()
+ * @see menu_set_active_trail()
+ */
+function hook_menu_breadcrumb_alter(&$active_trail, $item) {
+ // Always display a link to the current page by duplicating the last link in
+ // the active trail. This means that menu_get_active_breadcrumb() will remove
+ // the last link (for the current page), but since it is added once more here,
+ // it will appear.
+ if (!drupal_is_front_page()) {
+ $end = end($active_trail);
+ if ($item['href'] == $end['href']) {
+ $active_trail[] = $end;
+ }
+ }
+}
+
+/**
+ * Alter contextual links before they are rendered.
+ *
+ * This hook is invoked by menu_contextual_links(). The system-determined
+ * contextual links are passed in by reference. Additional links may be added
+ * or existing links can be altered.
+ *
+ * Each contextual link must at least contain:
+ * - title: The localized title of the link.
+ * - href: The system path to link to.
+ * - localized_options: An array of options to pass to url().
+ *
+ * @param $links
+ * An associative array containing contextual links for the given $root_path,
+ * as described above. The array keys are used to build CSS class names for
+ * contextual links and must therefore be unique for each set of contextual
+ * links.
+ * @param $router_item
+ * The menu router item belonging to the $root_path being requested.
+ * @param $root_path
+ * The (parent) path that has been requested to build contextual links for.
+ * This is a normalized path, which means that an originally passed path of
+ * 'node/123' became 'node/%'.
+ *
+ * @see hook_contextual_links_view_alter()
+ * @see menu_contextual_links()
+ * @see hook_menu()
+ * @see contextual_preprocess()
+ */
+function hook_menu_contextual_links_alter(&$links, $router_item, $root_path) {
+ // Add a link to all contextual links for nodes.
+ if ($root_path == 'node/%') {
+ $links['foo'] = array(
+ 'title' => t('Do fu'),
+ 'href' => 'foo/do',
+ 'localized_options' => array(
+ 'query' => array(
+ 'foo' => 'bar',
+ ),
+ ),
+ );
+ }
+}
+
+/**
+ * Perform alterations before a page is rendered.
+ *
+ * Use this hook when you want to remove or alter elements at the page
+ * level, or add elements at the page level that depend on an other module's
+ * elements (this hook runs after hook_page_build().
+ *
+ * If you are making changes to entities such as forms, menus, or user
+ * profiles, use those objects' native alter hooks instead (hook_form_alter(),
+ * for example).
+ *
+ * The $page array contains top level elements for each block region:
+ * @code
+ * $page['page_top']
+ * $page['header']
+ * $page['sidebar_first']
+ * $page['content']
+ * $page['sidebar_second']
+ * $page['page_bottom']
+ * @endcode
+ *
+ * The 'content' element contains the main content of the current page, and its
+ * structure will vary depending on what module is responsible for building the
+ * page. Some legacy modules may not return structured content at all: their
+ * pre-rendered markup will be located in $page['content']['main']['#markup'].
+ *
+ * Pages built by Drupal's core Node and Blog modules use a standard structure:
+ *
+ * @code
+ * // Node body.
+ * $page['content']['system_main']['nodes'][$nid]['body']
+ * // Array of links attached to the node (add comments, read more).
+ * $page['content']['system_main']['nodes'][$nid]['links']
+ * // The node object itself.
+ * $page['content']['system_main']['nodes'][$nid]['#node']
+ * // The results pager.
+ * $page['content']['system_main']['pager']
+ * @endcode
+ *
+ * Blocks may be referenced by their module/delta pair within a region:
+ * @code
+ * // The login block in the first sidebar region.
+ * $page['sidebar_first']['user_login']['#block'];
+ * @endcode
+ *
+ * @param $page
+ * Nested array of renderable elements that make up the page.
+ *
+ * @see hook_page_build()
+ * @see drupal_render_page()
+ */
+function hook_page_alter(&$page) {
+ // Add help text to the user login block.
+ $page['sidebar_first']['user_login']['help'] = array(
+ '#weight' => -10,
+ '#markup' => t('To post comments or add new content, you first have to log in.'),
+ );
+}
+
+/**
+ * Perform alterations before a form is rendered.
+ *
+ * One popular use of this hook is to add form elements to the node form. When
+ * altering a node form, the node object can be accessed at $form['#node'].
+ *
+ * In addition to hook_form_alter(), which is called for all forms, there are
+ * two more specific form hooks available. The first,
+ * hook_form_BASE_FORM_ID_alter(), allows targeting of a form/forms via a base
+ * form (if one exists). The second, hook_form_FORM_ID_alter(), can be used to
+ * target a specific form directly.
+ *
+ * The call order is as follows: all existing form alter functions are called
+ * for module A, then all for module B, etc., followed by all for any base
+ * theme(s), and finally for the theme itself. The module order is determined
+ * by system weight, then by module name.
+ *
+ * Within each module, form alter hooks are called in the following order:
+ * first, hook_form_alter(); second, hook_form_BASE_FORM_ID_alter(); third,
+ * hook_form_FORM_ID_alter(). So, for each module, the more general hooks are
+ * called first followed by the more specific.
+ *
+ * @param $form
+ * Nested array of form elements that comprise the form.
+ * @param $form_state
+ * A keyed array containing the current state of the form. The arguments
+ * that drupal_get_form() was originally called with are available in the
+ * array $form_state['build_info']['args'].
+ * @param $form_id
+ * String representing the name of the form itself. Typically this is the
+ * name of the function that generated the form.
+ *
+ * @see hook_form_BASE_FORM_ID_alter()
+ * @see hook_form_FORM_ID_alter()
+ * @see forms_api_reference.html
+ */
+function hook_form_alter(&$form, &$form_state, $form_id) {
+ if (isset($form['type']) && $form['type']['#value'] . '_node_settings' == $form_id) {
+ $form['workflow']['upload_' . $form['type']['#value']] = array(
+ '#type' => 'radios',
+ '#title' => t('Attachments'),
+ '#default_value' => variable_get('upload_' . $form['type']['#value'], 1),
+ '#options' => array(t('Disabled'), t('Enabled')),
+ );
+ }
+}
+
+/**
+ * Provide a form-specific alteration instead of the global hook_form_alter().
+ *
+ * Modules can implement hook_form_FORM_ID_alter() to modify a specific form,
+ * rather than implementing hook_form_alter() and checking the form ID, or
+ * using long switch statements to alter multiple forms.
+ *
+ * Form alter hooks are called in the following order: hook_form_alter(),
+ * hook_form_BASE_FORM_ID_alter(), hook_form_FORM_ID_alter(). See
+ * hook_form_alter() for more details.
+ *
+ * @param $form
+ * Nested array of form elements that comprise the form.
+ * @param $form_state
+ * A keyed array containing the current state of the form. The arguments
+ * that drupal_get_form() was originally called with are available in the
+ * array $form_state['build_info']['args'].
+ * @param $form_id
+ * String representing the name of the form itself. Typically this is the
+ * name of the function that generated the form.
+ *
+ * @see hook_form_alter()
+ * @see hook_form_BASE_FORM_ID_alter()
+ * @see drupal_prepare_form()
+ * @see forms_api_reference.html
+ */
+function hook_form_FORM_ID_alter(&$form, &$form_state, $form_id) {
+ // Modification for the form with the given form ID goes here. For example, if
+ // FORM_ID is "user_register_form" this code would run only on the user
+ // registration form.
+
+ // Add a checkbox to registration form about agreeing to terms of use.
+ $form['terms_of_use'] = array(
+ '#type' => 'checkbox',
+ '#title' => t("I agree with the website's terms and conditions."),
+ '#required' => TRUE,
+ );
+}
+
+/**
+ * Provide a form-specific alteration for shared ('base') forms.
+ *
+ * By default, when drupal_get_form() is called, Drupal looks for a function
+ * with the same name as the form ID, and uses that function to build the form.
+ * In contrast, base forms allow multiple form IDs to be mapped to a single base
+ * (also called 'factory') form function.
+ *
+ * Modules can implement hook_form_BASE_FORM_ID_alter() to modify a specific
+ * base form, rather than implementing hook_form_alter() and checking for
+ * conditions that would identify the shared form constructor.
+ *
+ * To identify the base form ID for a particular form (or to determine whether
+ * one exists) check the $form_state. The base form ID is stored under
+ * $form_state['build_info']['base_form_id'].
+ *
+ * See hook_forms() for more information on how to implement base forms in
+ * Drupal.
+ *
+ * Form alter hooks are called in the following order: hook_form_alter(),
+ * hook_form_BASE_FORM_ID_alter(), hook_form_FORM_ID_alter(). See
+ * hook_form_alter() for more details.
+ *
+ * @param $form
+ * Nested array of form elements that comprise the form.
+ * @param $form_state
+ * A keyed array containing the current state of the form.
+ * @param $form_id
+ * String representing the name of the form itself. Typically this is the
+ * name of the function that generated the form.
+ *
+ * @see hook_form_alter()
+ * @see hook_form_FORM_ID_alter()
+ * @see drupal_prepare_form()
+ * @see hook_forms()
+ */
+function hook_form_BASE_FORM_ID_alter(&$form, &$form_state, $form_id) {
+ // Modification for the form with the given BASE_FORM_ID goes here. For
+ // example, if BASE_FORM_ID is "node_form", this code would run on every
+ // node form, regardless of node type.
+
+ // Add a checkbox to the node form about agreeing to terms of use.
+ $form['terms_of_use'] = array(
+ '#type' => 'checkbox',
+ '#title' => t("I agree with the website's terms and conditions."),
+ '#required' => TRUE,
+ );
+}
+
+/**
+ * Map form_ids to form builder functions.
+ *
+ * By default, when drupal_get_form() is called, the system will look for a
+ * function with the same name as the form ID, and use that function to build
+ * the form. If no such function is found, Drupal calls this hook. Modules
+ * implementing this hook can then provide their own instructions for mapping
+ * form IDs to constructor functions. As a result, you can easily map multiple
+ * form IDs to a single form constructor (referred to as a 'base' form).
+ *
+ * Using a base form can help to avoid code duplication, by allowing many
+ * similar forms to use the same code base. Another benefit is that it becomes
+ * much easier for other modules to apply a general change to the group of
+ * forms; hook_form_BASE_FORM_ID_alter() can be used to easily alter multiple
+ * forms at once by directly targeting the shared base form.
+ *
+ * Two example use cases where base forms may be useful are given below.
+ *
+ * First, you can use this hook to tell the form system to use a different
+ * function to build certain forms in your module; this is often used to define
+ * a form "factory" function that is used to build several similar forms. In
+ * this case, your hook implementation will likely ignore all of the input
+ * arguments. See node_forms() for an example of this. Note, node_forms() is the
+ * hook_forms() implementation; the base form itself is defined in node_form().
+ *
+ * Second, you could use this hook to define how to build a form with a
+ * dynamically-generated form ID. In this case, you would need to verify that
+ * the $form_id input matched your module's format for dynamically-generated
+ * form IDs, and if so, act appropriately.
+ *
+ * @param $form_id
+ * The unique string identifying the desired form.
+ * @param $args
+ * An array containing the original arguments provided to drupal_get_form()
+ * or drupal_form_submit(). These are always passed to the form builder and
+ * do not have to be specified manually in 'callback arguments'.
+ *
+ * @return
+ * An associative array whose keys define form_ids and whose values are an
+ * associative array defining the following keys:
+ * - callback: The name of the form builder function to invoke. This will be
+ * used for the base form ID, for example, to target a base form using
+ * hook_form_BASE_FORM_ID_alter().
+ * - callback arguments: (optional) Additional arguments to pass to the
+ * function defined in 'callback', which are prepended to $args.
+ * - wrapper_callback: (optional) The name of a form builder function to
+ * invoke before the form builder defined in 'callback' is invoked. This
+ * wrapper callback may prepopulate the $form array with form elements,
+ * which will then be already contained in the $form that is passed on to
+ * the form builder defined in 'callback'. For example, a wrapper callback
+ * could setup wizard-alike form buttons that are the same for a variety of
+ * forms that belong to the wizard, which all share the same wrapper
+ * callback.
+ */
+function hook_forms($form_id, $args) {
+ // Simply reroute the (non-existing) $form_id 'mymodule_first_form' to
+ // 'mymodule_main_form'.
+ $forms['mymodule_first_form'] = array(
+ 'callback' => 'mymodule_main_form',
+ );
+
+ // Reroute the $form_id and prepend an additional argument that gets passed to
+ // the 'mymodule_main_form' form builder function.
+ $forms['mymodule_second_form'] = array(
+ 'callback' => 'mymodule_main_form',
+ 'callback arguments' => array('some parameter'),
+ );
+
+ // Reroute the $form_id, but invoke the form builder function
+ // 'mymodule_main_form_wrapper' first, so we can prepopulate the $form array
+ // that is passed to the actual form builder 'mymodule_main_form'.
+ $forms['mymodule_wrapped_form'] = array(
+ 'callback' => 'mymodule_main_form',
+ 'wrapper_callback' => 'mymodule_main_form_wrapper',
+ );
+
+ return $forms;
+}
+
+/**
+ * Perform setup tasks for all page requests.
+ *
+ * This hook is run at the beginning of the page request. It is typically
+ * used to set up global parameters that are needed later in the request.
+ *
+ * Only use this hook if your code must run even for cached page views. This
+ * hook is called before the theme, modules, or most include files are loaded
+ * into memory. It happens while Drupal is still in bootstrap mode.
+ *
+ * @see hook_init()
+ */
+function hook_boot() {
+ // We need user_access() in the shutdown function. Make sure it gets loaded.
+ drupal_load('module', 'user');
+ drupal_register_shutdown_function('devel_shutdown');
+}
+
+/**
+ * Perform setup tasks for non-cached page requests.
+ *
+ * This hook is run at the beginning of the page request. It is typically
+ * used to set up global parameters that are needed later in the request.
+ * When this hook is called, the theme and all modules are already loaded in
+ * memory.
+ *
+ * This hook is not run on cached pages.
+ *
+ * To add CSS or JS that should be present on all pages, modules should not
+ * implement this hook, but declare these files in their .info file.
+ *
+ * @see hook_boot()
+ */
+function hook_init() {
+ // Since this file should only be loaded on the front page, it cannot be
+ // declared in the info file.
+ if (drupal_is_front_page()) {
+ drupal_add_css(drupal_get_path('module', 'foo') . '/foo.css');
+ }
+}
+
+/**
+ * Define image toolkits provided by this module.
+ *
+ * The file which includes each toolkit's functions must be declared as part of
+ * the files array in the module .info file so that the registry will find and
+ * parse it.
+ *
+ * The toolkit's functions must be named image_toolkitname_operation().
+ * where the operation may be:
+ * - 'load': Required. See image_gd_load() for usage.
+ * - 'save': Required. See image_gd_save() for usage.
+ * - 'settings': Optional. See image_gd_settings() for usage.
+ * - 'resize': Optional. See image_gd_resize() for usage.
+ * - 'rotate': Optional. See image_gd_rotate() for usage.
+ * - 'crop': Optional. See image_gd_crop() for usage.
+ * - 'desaturate': Optional. See image_gd_desaturate() for usage.
+ *
+ * @return
+ * An array with the toolkit name as keys and sub-arrays with these keys:
+ * - 'title': A string with the toolkit's title.
+ * - 'available': A Boolean value to indicate that the toolkit is operating
+ * properly, e.g. all required libraries exist.
+ *
+ * @see system_image_toolkits()
+ */
+function hook_image_toolkits() {
+ return array(
+ 'working' => array(
+ 'title' => t('A toolkit that works.'),
+ 'available' => TRUE,
+ ),
+ 'broken' => array(
+ 'title' => t('A toolkit that is "broken" and will not be listed.'),
+ 'available' => FALSE,
+ ),
+ );
+}
+
+/**
+ * Alter an email message created with the drupal_mail() function.
+ *
+ * hook_mail_alter() allows modification of email messages created and sent
+ * with drupal_mail(). Usage examples include adding and/or changing message
+ * text, message fields, and message headers.
+ *
+ * Email messages sent using functions other than drupal_mail() will not
+ * invoke hook_mail_alter(). For example, a contributed module directly
+ * calling the drupal_mail_system()->mail() or PHP mail() function
+ * will not invoke this hook. All core modules use drupal_mail() for
+ * messaging, it is best practice but not mandatory in contributed modules.
+ *
+ * @param $message
+ * An array containing the message data. Keys in this array include:
+ * - 'id':
+ * The drupal_mail() id of the message. Look at module source code or
+ * drupal_mail() for possible id values.
+ * - 'to':
+ * The address or addresses the message will be sent to. The formatting of
+ * this string will be validated with the
+ * @link http://php.net/manual/filter.filters.validate.php PHP e-mail validation filter. @endlink
+ * - 'from':
+ * The address the message will be marked as being from, which is
+ * either a custom address or the site-wide default email address.
+ * - 'subject':
+ * Subject of the email to be sent. This must not contain any newline
+ * characters, or the email may not be sent properly.
+ * - 'body':
+ * An array of strings containing the message text. The message body is
+ * created by concatenating the individual array strings into a single text
+ * string using "\n\n" as a separator.
+ * - 'headers':
+ * Associative array containing mail headers, such as From, Sender,
+ * MIME-Version, Content-Type, etc.
+ * - 'params':
+ * An array of optional parameters supplied by the caller of drupal_mail()
+ * that is used to build the message before hook_mail_alter() is invoked.
+ * - 'language':
+ * The language object used to build the message before hook_mail_alter()
+ * is invoked.
+ * - 'send':
+ * Set to FALSE to abort sending this email message.
+ *
+ * @see drupal_mail()
+ */
+function hook_mail_alter(&$message) {
+ if ($message['id'] == 'modulename_messagekey') {
+ if (!example_notifications_optin($message['to'], $message['id'])) {
+ // If the recipient has opted to not receive such messages, cancel
+ // sending.
+ $message['send'] = FALSE;
+ return;
+ }
+ $message['body'][] = "--\nMail sent out from " . variable_get('site_name', t('Drupal'));
+ }
+}
+
+/**
+ * Alter the registry of modules implementing a hook.
+ *
+ * This hook is invoked during module_implements(). A module may implement this
+ * hook in order to reorder the implementing modules, which are otherwise
+ * ordered by the module's system weight.
+ *
+ * Note that hooks invoked using drupal_alter() can have multiple variations
+ * (such as hook_form_alter() and hook_form_FORM_ID_alter()). drupal_alter()
+ * will call all such variants defined by a single module in turn. For the
+ * purposes of hook_module_implements_alter(), these variants are treated as
+ * a single hook. Thus, to ensure that your implementation of
+ * hook_form_FORM_ID_alter() is called at the right time, you will have to
+ * change the order of hook_form_alter() implementation in
+ * hook_module_implements_alter().
+ *
+ * @param $implementations
+ * An array keyed by the module's name. The value of each item corresponds
+ * to a $group, which is usually FALSE, unless the implementation is in a
+ * file named $module.$group.inc.
+ * @param $hook
+ * The name of the module hook being implemented.
+ */
+function hook_module_implements_alter(&$implementations, $hook) {
+ if ($hook == 'rdf_mapping') {
+ // Move my_module_rdf_mapping() to the end of the list. module_implements()
+ // iterates through $implementations with a foreach loop which PHP iterates
+ // in the order that the items were added, so to move an item to the end of
+ // the array, we remove it and then add it.
+ $group = $implementations['my_module'];
+ unset($implementations['my_module']);
+ $implementations['my_module'] = $group;
+ }
+}
+
+/**
+ * Return additional themes provided by modules.
+ *
+ * Only use this hook for testing purposes. Use a hidden MYMODULE_test.module
+ * to implement this hook. Testing themes should be hidden, too.
+ *
+ * This hook is invoked from _system_rebuild_theme_data() and allows modules to
+ * register additional themes outside of the regular 'themes' directories of a
+ * Drupal installation.
+ *
+ * @return
+ * An associative array. Each key is the system name of a theme and each value
+ * is the corresponding path to the theme's .info file.
+ */
+function hook_system_theme_info() {
+ $themes['mymodule_test_theme'] = drupal_get_path('module', 'mymodule') . '/mymodule_test_theme/mymodule_test_theme.info';
+ return $themes;
+}
+
+/**
+ * Alter the information parsed from module and theme .info files
+ *
+ * This hook is invoked in _system_rebuild_module_data() and in
+ * _system_rebuild_theme_data(). A module may implement this hook in order to
+ * add to or alter the data generated by reading the .info file with
+ * drupal_parse_info_file().
+ *
+ * @param $info
+ * The .info file contents, passed by reference so that it can be altered.
+ * @param $file
+ * Full information about the module or theme, including $file->name, and
+ * $file->filename
+ * @param $type
+ * Either 'module' or 'theme', depending on the type of .info file that was
+ * passed.
+ */
+function hook_system_info_alter(&$info, $file, $type) {
+ // Only fill this in if the .info file does not define a 'datestamp'.
+ if (empty($info['datestamp'])) {
+ $info['datestamp'] = filemtime($file->filename);
+ }
+}
+
+/**
+ * Define user permissions.
+ *
+ * This hook can supply permissions that the module defines, so that they
+ * can be selected on the user permissions page and used to grant or restrict
+ * access to actions the module performs.
+ *
+ * Permissions are checked using user_access().
+ *
+ * For a detailed usage example, see page_example.module.
+ *
+ * @return
+ * An array whose keys are permission names and whose corresponding values
+ * are arrays containing the following key-value pairs:
+ * - title: The human-readable name of the permission, to be shown on the
+ * permission administration page. This should be wrapped in the t()
+ * function so it can be translated.
+ * - description: (optional) A description of what the permission does. This
+ * should be wrapped in the t() function so it can be translated.
+ * - restrict access: (optional) A boolean which can be set to TRUE to
+ * indicate that site administrators should restrict access to this
+ * permission to trusted users. This should be used for permissions that
+ * have inherent security risks across a variety of potential use cases
+ * (for example, the "administer filters" and "bypass node access"
+ * permissions provided by Drupal core). When set to TRUE, a standard
+ * warning message defined in user_admin_permissions() and output via
+ * theme_user_permission_description() will be associated with the
+ * permission and displayed with it on the permission administration page.
+ * Defaults to FALSE.
+ * - warning: (optional) A translated warning message to display for this
+ * permission on the permission administration page. This warning overrides
+ * the automatic warning generated by 'restrict access' being set to TRUE.
+ * This should rarely be used, since it is important for all permissions to
+ * have a clear, consistent security warning that is the same across the
+ * site. Use the 'description' key instead to provide any information that
+ * is specific to the permission you are defining.
+ *
+ * @see theme_user_permission_description()
+ */
+function hook_permission() {
+ return array(
+ 'administer my module' => array(
+ 'title' => t('Administer my module'),
+ 'description' => t('Perform administration tasks for my module.'),
+ ),
+ );
+}
+
+/**
+ * Register a module (or theme's) theme implementations.
+ *
+ * The implementations declared by this hook have two purposes: either they
+ * specify how a particular render array is to be rendered as HTML (this is
+ * usually the case if the theme function is assigned to the render array's
+ * #theme property), or they return the HTML that should be returned by an
+ * invocation of theme(). See
+ * @link http://drupal.org/node/933976 Using the theme layer Drupal 7.x @endlink
+ * for more information on how to implement theme hooks.
+ *
+ * The following parameters are all optional.
+ *
+ * @param array $existing
+ * An array of existing implementations that may be used for override
+ * purposes. This is primarily useful for themes that may wish to examine
+ * existing implementations to extract data (such as arguments) so that
+ * it may properly register its own, higher priority implementations.
+ * @param $type
+ * Whether a theme, module, etc. is being processed. This is primarily useful
+ * so that themes tell if they are the actual theme being called or a parent
+ * theme. May be one of:
+ * - 'module': A module is being checked for theme implementations.
+ * - 'base_theme_engine': A theme engine is being checked for a theme that is
+ * a parent of the actual theme being used.
+ * - 'theme_engine': A theme engine is being checked for the actual theme
+ * being used.
+ * - 'base_theme': A base theme is being checked for theme implementations.
+ * - 'theme': The actual theme in use is being checked.
+ * @param $theme
+ * The actual name of theme, module, etc. that is being being processed.
+ * @param $path
+ * The directory path of the theme or module, so that it doesn't need to be
+ * looked up.
+ *
+ * @return array
+ * An associative array of theme hook information. The keys on the outer
+ * array are the internal names of the hooks, and the values are arrays
+ * containing information about the hook. Each information array must contain
+ * either a 'variables' element or a 'render element' element, but not both.
+ * Use 'render element' if you are theming a single element or element tree
+ * composed of elements, such as a form array, a page array, or a single
+ * checkbox element. Use 'variables' if your theme implementation is
+ * intended to be called directly through theme() and has multiple arguments
+ * for the data and style; in this case, the variables not supplied by the
+ * calling function will be given default values and passed to the template
+ * or theme function. The returned theme information array can contain the
+ * following key/value pairs:
+ * - variables: (see above) Each array key is the name of the variable, and
+ * the value given is used as the default value if the function calling
+ * theme() does not supply it. Template implementations receive each array
+ * key as a variable in the template file (so they must be legal PHP
+ * variable names). Function implementations are passed the variables in a
+ * single $variables function argument.
+ * - render element: (see above) The name of the renderable element or element
+ * tree to pass to the theme function. This name is used as the name of the
+ * variable that holds the renderable element or tree in preprocess and
+ * process functions.
+ * - file: The file the implementation resides in. This file will be included
+ * prior to the theme being rendered, to make sure that the function or
+ * preprocess function (as needed) is actually loaded; this makes it
+ * possible to split theme functions out into separate files quite easily.
+ * - path: Override the path of the file to be used. Ordinarily the module or
+ * theme path will be used, but if the file will not be in the default
+ * path, include it here. This path should be relative to the Drupal root
+ * directory.
+ * - template: If specified, this theme implementation is a template, and
+ * this is the template file without an extension. Do not put .tpl.php on
+ * this file; that extension will be added automatically by the default
+ * rendering engine (which is PHPTemplate). If 'path', above, is specified,
+ * the template should also be in this path.
+ * - function: If specified, this will be the function name to invoke for
+ * this implementation. If neither 'template' nor 'function' is specified,
+ * a default function name will be assumed. For example, if a module
+ * registers the 'node' theme hook, 'theme_node' will be assigned to its
+ * function. If the chameleon theme registers the node hook, it will be
+ * assigned 'chameleon_node' as its function.
+ * - base hook: A string declaring the base theme hook if this theme
+ * implementation is actually implementing a suggestion for another theme
+ * hook.
+ * - pattern: A regular expression pattern to be used to allow this theme
+ * implementation to have a dynamic name. The convention is to use __ to
+ * differentiate the dynamic portion of the theme. For example, to allow
+ * forums to be themed individually, the pattern might be: 'forum__'. Then,
+ * when the forum is themed, call:
+ * @code
+ * theme(array('forum__' . $tid, 'forum'), $forum)
+ * @endcode
+ * - preprocess functions: A list of functions used to preprocess this data.
+ * Ordinarily this won't be used; it's automatically filled in. By default,
+ * for a module this will be filled in as template_preprocess_HOOK. For
+ * a theme this will be filled in as phptemplate_preprocess and
+ * phptemplate_preprocess_HOOK as well as themename_preprocess and
+ * themename_preprocess_HOOK.
+ * - override preprocess functions: Set to TRUE when a theme does NOT want
+ * the standard preprocess functions to run. This can be used to give a
+ * theme FULL control over how variables are set. For example, if a theme
+ * wants total control over how certain variables in the page.tpl.php are
+ * set, this can be set to true. Please keep in mind that when this is used
+ * by a theme, that theme becomes responsible for making sure necessary
+ * variables are set.
+ * - type: (automatically derived) Where the theme hook is defined:
+ * 'module', 'theme_engine', or 'theme'.
+ * - theme path: (automatically derived) The directory path of the theme or
+ * module, so that it doesn't need to be looked up.
+ *
+ * @see hook_theme_registry_alter()
+ */
+function hook_theme($existing, $type, $theme, $path) {
+ return array(
+ 'forum_display' => array(
+ 'variables' => array('forums' => NULL, 'topics' => NULL, 'parents' => NULL, 'tid' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL),
+ ),
+ 'forum_list' => array(
+ 'variables' => array('forums' => NULL, 'parents' => NULL, 'tid' => NULL),
+ ),
+ 'forum_topic_list' => array(
+ 'variables' => array('tid' => NULL, 'topics' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL),
+ ),
+ 'forum_icon' => array(
+ 'variables' => array('new_posts' => NULL, 'num_posts' => 0, 'comment_mode' => 0, 'sticky' => 0),
+ ),
+ 'status_report' => array(
+ 'render element' => 'requirements',
+ 'file' => 'system.admin.inc',
+ ),
+ 'system_date_time_settings' => array(
+ 'render element' => 'form',
+ 'file' => 'system.admin.inc',
+ ),
+ );
+}
+
+/**
+ * Alter the theme registry information returned from hook_theme().
+ *
+ * The theme registry stores information about all available theme hooks,
+ * including which callback functions those hooks will call when triggered,
+ * what template files are exposed by these hooks, and so on.
+ *
+ * Note that this hook is only executed as the theme cache is re-built.
+ * Changes here will not be visible until the next cache clear.
+ *
+ * The $theme_registry array is keyed by theme hook name, and contains the
+ * information returned from hook_theme(), as well as additional properties
+ * added by _theme_process_registry().
+ *
+ * For example:
+ * @code
+ * $theme_registry['user_profile'] = array(
+ * 'variables' => array(
+ * 'account' => NULL,
+ * ),
+ * 'template' => 'modules/user/user-profile',
+ * 'file' => 'modules/user/user.pages.inc',
+ * 'type' => 'module',
+ * 'theme path' => 'modules/user',
+ * 'preprocess functions' => array(
+ * 0 => 'template_preprocess',
+ * 1 => 'template_preprocess_user_profile',
+ * ),
+ * );
+ * @endcode
+ *
+ * @param $theme_registry
+ * The entire cache of theme registry information, post-processing.
+ *
+ * @see hook_theme()
+ * @see _theme_process_registry()
+ */
+function hook_theme_registry_alter(&$theme_registry) {
+ // Kill the next/previous forum topic navigation links.
+ foreach ($theme_registry['forum_topic_navigation']['preprocess functions'] as $key => $value) {
+ if ($value == 'template_preprocess_forum_topic_navigation') {
+ unset($theme_registry['forum_topic_navigation']['preprocess functions'][$key]);
+ }
+ }
+}
+
+/**
+ * Return the machine-readable name of the theme to use for the current page.
+ *
+ * This hook can be used to dynamically set the theme for the current page
+ * request. It should be used by modules which need to override the theme
+ * based on dynamic conditions (for example, a module which allows the theme to
+ * be set based on the current user's role). The return value of this hook will
+ * be used on all pages except those which have a valid per-page or per-section
+ * theme set via a theme callback function in hook_menu(); the themes on those
+ * pages can only be overridden using hook_menu_alter().
+ *
+ * Note that returning different themes for the same path may not work with page
+ * caching. This is most likely to be a problem if an anonymous user on a given
+ * path could have different themes returned under different conditions.
+ *
+ * Since only one theme can be used at a time, the last (i.e., highest
+ * weighted) module which returns a valid theme name from this hook will
+ * prevail.
+ *
+ * @return
+ * The machine-readable name of the theme that should be used for the current
+ * page request. The value returned from this function will only have an
+ * effect if it corresponds to a currently-active theme on the site. Do not
+ * return a value if you do not wish to set a custom theme.
+ */
+function hook_custom_theme() {
+ // Allow the user to request a particular theme via a query parameter.
+ if (isset($_GET['theme'])) {
+ return $_GET['theme'];
+ }
+}
+
+/**
+ * Register XML-RPC callbacks.
+ *
+ * This hook lets a module register callback functions to be called when
+ * particular XML-RPC methods are invoked by a client.
+ *
+ * @return
+ * An array which maps XML-RPC methods to Drupal functions. Each array
+ * element is either a pair of method => function or an array with four
+ * entries:
+ * - The XML-RPC method name (for example, module.function).
+ * - The Drupal callback function (for example, module_function).
+ * - The method signature is an array of XML-RPC types. The first element
+ * of this array is the type of return value and then you should write a
+ * list of the types of the parameters. XML-RPC types are the following
+ * (See the types at http://www.xmlrpc.com/spec):
+ * - "boolean": 0 (false) or 1 (true).
+ * - "double": a floating point number (for example, -12.214).
+ * - "int": a integer number (for example, -12).
+ * - "array": an array without keys (for example, array(1, 2, 3)).
+ * - "struct": an associative array or an object (for example,
+ * array('one' => 1, 'two' => 2)).
+ * - "date": when you return a date, then you may either return a
+ * timestamp (time(), mktime() etc.) or an ISO8601 timestamp. When
+ * date is specified as an input parameter, then you get an object,
+ * which is described in the function xmlrpc_date
+ * - "base64": a string containing binary data, automatically
+ * encoded/decoded automatically.
+ * - "string": anything else, typically a string.
+ * - A descriptive help string, enclosed in a t() function for translation
+ * purposes.
+ * Both forms are shown in the example.
+ */
+function hook_xmlrpc() {
+ return array(
+ 'drupal.login' => 'drupal_login',
+ array(
+ 'drupal.site.ping',
+ 'drupal_directory_ping',
+ array('boolean', 'string', 'string', 'string', 'string', 'string'),
+ t('Handling ping request'))
+ );
+}
+
+/**
+ * Alters the definition of XML-RPC methods before they are called.
+ *
+ * This hook allows modules to modify the callback definition of declared
+ * XML-RPC methods, right before they are invoked by a client. Methods may be
+ * added, or existing methods may be altered.
+ *
+ * Note that hook_xmlrpc() supports two distinct and incompatible formats to
+ * define a callback, so care must be taken when altering other methods.
+ *
+ * @param $methods
+ * An asssociative array of method callback definitions, as returned from
+ * hook_xmlrpc() implementations.
+ *
+ * @see hook_xmlrpc()
+ * @see xmlrpc_server()
+ */
+function hook_xmlrpc_alter(&$methods) {
+ // Directly change a simple method.
+ $methods['drupal.login'] = 'mymodule_login';
+
+ // Alter complex definitions.
+ foreach ($methods as $key => &$method) {
+ // Skip simple method definitions.
+ if (!is_int($key)) {
+ continue;
+ }
+ // Perform the wanted manipulation.
+ if ($method[0] == 'drupal.site.ping') {
+ $method[1] = 'mymodule_directory_ping';
+ }
+ }
+}
+
+/**
+ * Log an event message.
+ *
+ * This hook allows modules to route log events to custom destinations, such as
+ * SMS, Email, pager, syslog, ...etc.
+ *
+ * @param $log_entry
+ * An associative array containing the following keys:
+ * - type: The type of message for this entry.
+ * - user: The user object for the user who was logged in when the event
+ * happened.
+ * - uid: The user ID for the user who was logged in when the event happened.
+ * - request_uri: The request URI for the page the event happened in.
+ * - referer: The page that referred the user to the page where the event
+ * occurred.
+ * - ip: The IP address where the request for the page came from.
+ * - timestamp: The UNIX timestamp of the date/time the event occurred.
+ * - severity: The severity of the message; one of the following values as
+ * defined in @link http://www.faqs.org/rfcs/rfc3164.html RFC 3164: @endlink
+ * - WATCHDOG_EMERGENCY: Emergency, system is unusable.
+ * - WATCHDOG_ALERT: Alert, action must be taken immediately.
+ * - WATCHDOG_CRITICAL: Critical conditions.
+ * - WATCHDOG_ERROR: Error conditions.
+ * - WATCHDOG_WARNING: Warning conditions.
+ * - WATCHDOG_NOTICE: Normal but significant conditions.
+ * - WATCHDOG_INFO: Informational messages.
+ * - WATCHDOG_DEBUG: Debug-level messages.
+ * - link: An optional link provided by the module that called the watchdog()
+ * function.
+ * - message: The text of the message to be logged. Variables in the message
+ * are indicated by using placeholder strings alongside the variables
+ * argument to declare the value of the placeholders. See t() for
+ * documentation on how the message and variable parameters interact.
+ * - variables: An array of variables to be inserted into the message on
+ * display. Will be NULL or missing if a message is already translated or if
+ * the message is not possible to translate.
+ */
+function hook_watchdog(array $log_entry) {
+ global $base_url, $language;
+
+ $severity_list = array(
+ WATCHDOG_EMERGENCY => 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'),
+ );
+
+ $to = 'someone@example.com';
+ $params = array();
+ $params['subject'] = t('[@site_name] @severity_desc: Alert from your web site', array(
+ '@site_name' => variable_get('site_name', 'Drupal'),
+ '@severity_desc' => $severity_list[$log_entry['severity']],
+ ));
+
+ $params['message'] = "\nSite: @base_url";
+ $params['message'] .= "\nSeverity: (@severity) @severity_desc";
+ $params['message'] .= "\nTimestamp: @timestamp";
+ $params['message'] .= "\nType: @type";
+ $params['message'] .= "\nIP Address: @ip";
+ $params['message'] .= "\nRequest URI: @request_uri";
+ $params['message'] .= "\nReferrer URI: @referer_uri";
+ $params['message'] .= "\nUser: (@uid) @name";
+ $params['message'] .= "\nLink: @link";
+ $params['message'] .= "\nMessage: \n\n@message";
+
+ $params['message'] = t($params['message'], array(
+ '@base_url' => $base_url,
+ '@severity' => $log_entry['severity'],
+ '@severity_desc' => $severity_list[$log_entry['severity']],
+ '@timestamp' => format_date($log_entry['timestamp']),
+ '@type' => $log_entry['type'],
+ '@ip' => $log_entry['ip'],
+ '@request_uri' => $log_entry['request_uri'],
+ '@referer_uri' => $log_entry['referer'],
+ '@uid' => $log_entry['uid'],
+ '@name' => $log_entry['user']->name,
+ '@link' => strip_tags($log_entry['link']),
+ '@message' => strip_tags($log_entry['message']),
+ ));
+
+ drupal_mail('emaillog', 'entry', $to, $language, $params);
+}
+
+/**
+ * Prepare a message based on parameters; called from drupal_mail().
+ *
+ * Note that hook_mail(), unlike hook_mail_alter(), is only called on the
+ * $module argument to drupal_mail(), not all modules.
+ *
+ * @param $key
+ * An identifier of the mail.
+ * @param $message
+ * An array to be filled in. Elements in this array include:
+ * - id: An ID to identify the mail sent. Look at module source code
+ * or drupal_mail() for possible id values.
+ * - to: The address or addresses the message will be sent to. The formatting
+ * of this string will be validated with the
+ * @link http://php.net/manual/filter.filters.validate.php PHP e-mail validation filter. @endlink
+ * - subject: Subject of the e-mail to be sent. This must not contain any
+ * newline characters, or the mail may not be sent properly. drupal_mail()
+ * sets this to an empty string when the hook is invoked.
+ * - body: An array of lines containing the message to be sent. Drupal will
+ * format the correct line endings for you. drupal_mail() sets this to an
+ * empty array when the hook is invoked.
+ * - from: The address the message will be marked as being from, which is
+ * set by drupal_mail() to either a custom address or the site-wide
+ * default email address when the hook is invoked.
+ * - headers: Associative array containing mail headers, such as From,
+ * Sender, MIME-Version, Content-Type, etc. drupal_mail() pre-fills
+ * several headers in this array.
+ * @param $params
+ * An array of parameters supplied by the caller of drupal_mail().
+ */
+function hook_mail($key, &$message, $params) {
+ $account = $params['account'];
+ $context = $params['context'];
+ $variables = array(
+ '%site_name' => variable_get('site_name', 'Drupal'),
+ '%username' => format_username($account),
+ );
+ if ($context['hook'] == 'taxonomy') {
+ $entity = $params['entity'];
+ $vocabulary = taxonomy_vocabulary_load($entity->vid);
+ $variables += array(
+ '%term_name' => $entity->name,
+ '%term_description' => $entity->description,
+ '%term_id' => $entity->tid,
+ '%vocabulary_name' => $vocabulary->name,
+ '%vocabulary_description' => $vocabulary->description,
+ '%vocabulary_id' => $vocabulary->vid,
+ );
+ }
+
+ // Node-based variable translation is only available if we have a node.
+ if (isset($params['node'])) {
+ $node = $params['node'];
+ $variables += array(
+ '%uid' => $node->uid,
+ '%node_url' => url('node/' . $node->nid, array('absolute' => TRUE)),
+ '%node_type' => node_type_get_name($node),
+ '%title' => $node->title,
+ '%teaser' => $node->teaser,
+ '%body' => $node->body,
+ );
+ }
+ $subject = strtr($context['subject'], $variables);
+ $body = strtr($context['message'], $variables);
+ $message['subject'] .= str_replace(array("\r", "\n"), '', $subject);
+ $message['body'][] = drupal_html_to_text($body);
+}
+
+/**
+ * Add a list of cache tables to be cleared.
+ *
+ * This hook allows your module to add cache table names to the list of cache
+ * tables that will be cleared by the Clear button on the Performance page or
+ * whenever drupal_flush_all_caches is invoked.
+ *
+ * @return
+ * An array of cache table names.
+ *
+ * @see drupal_flush_all_caches()
+ */
+function hook_flush_caches() {
+ return array('cache_example');
+}
+
+/**
+ * Perform necessary actions after modules are installed.
+ *
+ * This function differs from hook_install() in that it gives all other modules
+ * a chance to perform actions when a module is installed, whereas
+ * hook_install() is only called on the module actually being installed. See
+ * module_enable() for a detailed description of the order in which install and
+ * enable hooks are invoked.
+ *
+ * @param $modules
+ * An array of the modules that were installed.
+ *
+ * @see module_enable()
+ * @see hook_modules_enabled()
+ * @see hook_install()
+ */
+function hook_modules_installed($modules) {
+ if (in_array('lousy_module', $modules)) {
+ variable_set('lousy_module_conflicting_variable', FALSE);
+ }
+}
+
+/**
+ * Perform necessary actions after modules are enabled.
+ *
+ * This function differs from hook_enable() in that it gives all other modules a
+ * chance to perform actions when modules are enabled, whereas hook_enable() is
+ * only called on the module actually being enabled. See module_enable() for a
+ * detailed description of the order in which install and enable hooks are
+ * invoked.
+ *
+ * @param $modules
+ * An array of the modules that were enabled.
+ *
+ * @see hook_enable()
+ * @see hook_modules_installed()
+ * @see module_enable()
+ */
+function hook_modules_enabled($modules) {
+ if (in_array('lousy_module', $modules)) {
+ drupal_set_message(t('mymodule is not compatible with lousy_module'), 'error');
+ mymodule_disable_functionality();
+ }
+}
+
+/**
+ * Perform necessary actions after modules are disabled.
+ *
+ * This function differs from hook_disable() in that it gives all other modules
+ * a chance to perform actions when modules are disabled, whereas hook_disable()
+ * is only called on the module actually being disabled.
+ *
+ * @param $modules
+ * An array of the modules that were disabled.
+ *
+ * @see hook_disable()
+ * @see hook_modules_uninstalled()
+ */
+function hook_modules_disabled($modules) {
+ if (in_array('lousy_module', $modules)) {
+ mymodule_enable_functionality();
+ }
+}
+
+/**
+ * Perform necessary actions after modules are uninstalled.
+ *
+ * This function differs from hook_uninstall() in that it gives all other
+ * modules a chance to perform actions when a module is uninstalled, whereas
+ * hook_uninstall() is only called on the module actually being uninstalled.
+ *
+ * It is recommended that you implement this hook if your module stores
+ * data that may have been set by other modules.
+ *
+ * @param $modules
+ * An array of the modules that were uninstalled.
+ *
+ * @see hook_uninstall()
+ * @see hook_modules_disabled()
+ */
+function hook_modules_uninstalled($modules) {
+ foreach ($modules as $module) {
+ db_delete('mymodule_table')
+ ->condition('module', $module)
+ ->execute();
+ }
+ mymodule_cache_rebuild();
+}
+
+/**
+ * Registers PHP stream wrapper implementations associated with a module.
+ *
+ * Provide a facility for managing and querying user-defined stream wrappers
+ * in PHP. PHP's internal stream_get_wrappers() doesn't return the class
+ * registered to handle a stream, which we need to be able to find the handler
+ * for class instantiation.
+ *
+ * If a module registers a scheme that is already registered with PHP, it will
+ * be unregistered and replaced with the specified class.
+ *
+ * @return
+ * A nested array, keyed first by scheme name ("public" for "public://"),
+ * then keyed by the following values:
+ * - 'name' A short string to name the wrapper.
+ * - 'class' A string specifying the PHP class that implements the
+ * DrupalStreamWrapperInterface interface.
+ * - 'description' A string with a short description of what the wrapper does.
+ * - 'type' (Optional) A bitmask of flags indicating what type of streams this
+ * wrapper will access - local or remote, readable and/or writeable, etc.
+ * Many shortcut constants are defined in stream_wrappers.inc. Defaults to
+ * STREAM_WRAPPERS_NORMAL which includes all of these bit flags:
+ * - STREAM_WRAPPERS_READ
+ * - STREAM_WRAPPERS_WRITE
+ * - STREAM_WRAPPERS_VISIBLE
+ *
+ * @see file_get_stream_wrappers()
+ * @see hook_stream_wrappers_alter()
+ * @see system_stream_wrappers()
+ */
+function hook_stream_wrappers() {
+ return array(
+ 'public' => array(
+ 'name' => t('Public files'),
+ 'class' => 'DrupalPublicStreamWrapper',
+ 'description' => t('Public local files served by the webserver.'),
+ 'type' => STREAM_WRAPPERS_LOCAL_NORMAL,
+ ),
+ 'private' => array(
+ 'name' => t('Private files'),
+ 'class' => 'DrupalPrivateStreamWrapper',
+ 'description' => t('Private local files served by Drupal.'),
+ 'type' => STREAM_WRAPPERS_LOCAL_NORMAL,
+ ),
+ 'temp' => array(
+ 'name' => t('Temporary files'),
+ 'class' => 'DrupalTempStreamWrapper',
+ 'description' => t('Temporary local files for upload and previews.'),
+ 'type' => STREAM_WRAPPERS_LOCAL_HIDDEN,
+ ),
+ 'cdn' => array(
+ 'name' => t('Content delivery network files'),
+ 'class' => 'MyModuleCDNStreamWrapper',
+ 'description' => t('Files served by a content delivery network.'),
+ // 'type' can be omitted to use the default of STREAM_WRAPPERS_NORMAL
+ ),
+ 'youtube' => array(
+ 'name' => t('YouTube video'),
+ 'class' => 'MyModuleYouTubeStreamWrapper',
+ 'description' => t('Video streamed from YouTube.'),
+ // A module implementing YouTube integration may decide to support using
+ // the YouTube API for uploading video, but here, we assume that this
+ // particular module only supports playing YouTube video.
+ 'type' => STREAM_WRAPPERS_READ_VISIBLE,
+ ),
+ );
+}
+
+/**
+ * Alters the list of PHP stream wrapper implementations.
+ *
+ * @see file_get_stream_wrappers()
+ * @see hook_stream_wrappers()
+ */
+function hook_stream_wrappers_alter(&$wrappers) {
+ // Change the name of private files to reflect the performance.
+ $wrappers['private']['name'] = t('Slow files');
+}
+
+/**
+ * Load additional information into file objects.
+ *
+ * file_load_multiple() calls this hook to allow modules to load
+ * additional information into each file.
+ *
+ * @param $files
+ * An array of file objects, indexed by fid.
+ *
+ * @see file_load_multiple()
+ * @see file_load()
+ */
+function hook_file_load($files) {
+ // Add the upload specific data into the file object.
+ $result = db_query('SELECT * FROM {upload} u WHERE u.fid IN (:fids)', array(':fids' => array_keys($files)))->fetchAll(PDO::FETCH_ASSOC);
+ foreach ($result as $record) {
+ foreach ($record as $key => $value) {
+ $files[$record['fid']]->$key = $value;
+ }
+ }
+}
+
+/**
+ * Check that files meet a given criteria.
+ *
+ * This hook lets modules perform additional validation on files. They're able
+ * to report a failure by returning one or more error messages.
+ *
+ * @param $file
+ * The file object being validated.
+ * @return
+ * An array of error messages. If there are no problems with the file return
+ * an empty array.
+ *
+ * @see file_validate()
+ */
+function hook_file_validate($file) {
+ $errors = array();
+
+ if (empty($file->filename)) {
+ $errors[] = t("The file's name is empty. Please give a name to the file.");
+ }
+ if (strlen($file->filename) > 255) {
+ $errors[] = t("The file's name exceeds the 255 characters limit. Please rename the file and try again.");
+ }
+
+ return $errors;
+}
+
+/**
+ * Act on a file being inserted or updated.
+ *
+ * This hook is called when a file has been added to the database. The hook
+ * doesn't distinguish between files created as a result of a copy or those
+ * created by an upload.
+ *
+ * @param $file
+ * The file that has just been created.
+ *
+ * @see file_save()
+ */
+function hook_file_presave($file) {
+ // Change the file timestamp to an hour prior.
+ $file->timestamp -= 3600;
+}
+
+/**
+ * Respond to a file being added.
+ *
+ * This hook is called after a file has been added to the database. The hook
+ * doesn't distinguish between files created as a result of a copy or those
+ * created by an upload.
+ *
+ * @param $file
+ * The file that has been added.
+ *
+ * @see file_save()
+ */
+function hook_file_insert($file) {
+ // Add a message to the log, if the file is a jpg
+ $validate = file_validate_extensions($file, 'jpg');
+ if (empty($validate)) {
+ watchdog('file', 'A jpg has been added.');
+ }
+}
+
+/**
+ * Respond to a file being updated.
+ *
+ * This hook is called when file_save() is called on an existing file.
+ *
+ * @param $file
+ * The file that has just been updated.
+ *
+ * @see file_save()
+ */
+function hook_file_update($file) {
+ $file_user = user_load($file->uid);
+ // Make sure that the file name starts with the owner's user name.
+ if (strpos($file->filename, $file_user->name) !== 0) {
+ $old_filename = $file->filename;
+ $file->filename = $file_user->name . '_' . $file->filename;
+ $file->save();
+
+ watchdog('file', t('%source has been renamed to %destination', array('%source' => $old_filename, '%destination' => $file->filename)));
+ }
+}
+
+/**
+ * Respond to a file that has been copied.
+ *
+ * @param $file
+ * The newly copied file object.
+ * @param $source
+ * The original file before the copy.
+ *
+ * @see file_copy()
+ */
+function hook_file_copy($file, $source) {
+ $file_user = user_load($file->uid);
+ // Make sure that the file name starts with the owner's user name.
+ if (strpos($file->filename, $file_user->name) !== 0) {
+ $file->filename = $file_user->name . '_' . $file->filename;
+ $file->save();
+
+ watchdog('file', t('Copied file %source has been renamed to %destination', array('%source' => $source->filename, '%destination' => $file->filename)));
+ }
+}
+
+/**
+ * Respond to a file that has been moved.
+ *
+ * @param $file
+ * The updated file object after the move.
+ * @param $source
+ * The original file object before the move.
+ *
+ * @see file_move()
+ */
+function hook_file_move($file, $source) {
+ $file_user = user_load($file->uid);
+ // Make sure that the file name starts with the owner's user name.
+ if (strpos($file->filename, $file_user->name) !== 0) {
+ $file->filename = $file_user->name . '_' . $file->filename;
+ $file->save();
+
+ watchdog('file', t('Moved file %source has been renamed to %destination', array('%source' => $source->filename, '%destination' => $file->filename)));
+ }
+}
+
+/**
+ * Respond to a file being deleted.
+ *
+ * @param $file
+ * The file that has just been deleted.
+ *
+ * @see file_delete()
+ */
+function hook_file_delete($file) {
+ // Delete all information associated with the file.
+ db_delete('upload')->condition('fid', $file->fid)->execute();
+}
+
+/**
+ * Control access to private file downloads and specify HTTP headers.
+ *
+ * This hook allows modules enforce permissions on file downloads when the
+ * private file download method is selected. Modules can also provide headers
+ * to specify information like the file's name or MIME type.
+ *
+ * @param $uri
+ * The URI of the file.
+ * @return
+ * If the user does not have permission to access the file, return -1. If the
+ * user has permission, return an array with the appropriate headers. If the
+ * file is not controlled by the current module, the return value should be
+ * NULL.
+ *
+ * @see file_download()
+ */
+function hook_file_download($uri) {
+ // Check if the file is controlled by the current module.
+ if (!file_prepare_directory($uri)) {
+ $uri = FALSE;
+ }
+ if (strpos(file_uri_target($uri), variable_get('user_picture_path', 'pictures') . '/picture-') === 0) {
+ if (!user_access('access user profiles')) {
+ // Access to the file is denied.
+ return -1;
+ }
+ else {
+ $info = image_get_info($uri);
+ return array('Content-Type' => $info['mime_type']);
+ }
+ }
+}
+
+/**
+ * Alter the URL to a file.
+ *
+ * This hook is called from file_create_url(), and is called fairly
+ * frequently (10+ times per page), depending on how many files there are in a
+ * given page.
+ * If CSS and JS aggregation are disabled, this can become very frequently
+ * (50+ times per page) so performance is critical.
+ *
+ * This function should alter the URI, if it wants to rewrite the file URL.
+ *
+ * @param $uri
+ * The URI to a file for which we need an external URL, or the path to a
+ * shipped file.
+ */
+function hook_file_url_alter(&$uri) {
+ global $user;
+
+ // User 1 will always see the local file in this example.
+ if ($user->uid == 1) {
+ return;
+ }
+
+ $cdn1 = 'http://cdn1.example.com';
+ $cdn2 = 'http://cdn2.example.com';
+ $cdn_extensions = array('css', 'js', 'gif', 'jpg', 'jpeg', 'png');
+
+ // Most CDNs don't support private file transfers without a lot of hassle,
+ // so don't support this in the common case.
+ $schemes = array('public');
+
+ $scheme = file_uri_scheme($uri);
+
+ // Only serve shipped files and public created files from the CDN.
+ if (!$scheme || in_array($scheme, $schemes)) {
+ // Shipped files.
+ if (!$scheme) {
+ $path = $uri;
+ }
+ // Public created files.
+ else {
+ $wrapper = file_stream_wrapper_get_instance_by_scheme($scheme);
+ $path = $wrapper->getDirectoryPath() . '/' . file_uri_target($uri);
+ }
+
+ // Clean up Windows paths.
+ $path = str_replace('\\', '/', $path);
+
+ // Serve files with one of the CDN extensions from CDN 1, all others from
+ // CDN 2.
+ $pathinfo = pathinfo($path);
+ if (isset($pathinfo['extension']) && in_array($pathinfo['extension'], $cdn_extensions)) {
+ $uri = $cdn1 . '/' . $path;
+ }
+ else {
+ $uri = $cdn2 . '/' . $path;
+ }
+ }
+}
+
+/**
+ * Check installation requirements and do status reporting.
+ *
+ * This hook has three closely related uses, determined by the $phase argument:
+ * - Checking installation requirements ($phase == 'install').
+ * - Checking update requirements ($phase == 'update').
+ * - Status reporting ($phase == 'runtime').
+ *
+ * Note that this hook, like all others dealing with installation and updates,
+ * must reside in a module_name.install file, or it will not properly abort
+ * the installation of the module if a critical requirement is missing.
+ *
+ * During the 'install' phase, modules can for example assert that
+ * library or server versions are available or sufficient.
+ * Note that the installation of a module can happen during installation of
+ * Drupal itself (by install.php) with an installation profile or later by hand.
+ * As a consequence, install-time requirements must be checked without access
+ * to the full Drupal API, because it is not available during install.php.
+ * For localization you should for example use $t = get_t() to
+ * retrieve the appropriate localization function name (t() or st()).
+ * If a requirement has a severity of REQUIREMENT_ERROR, install.php will abort
+ * or at least the module will not install.
+ * Other severity levels have no effect on the installation.
+ * Module dependencies do not belong to these installation requirements,
+ * but should be defined in the module's .info file.
+ *
+ * The 'runtime' phase is not limited to pure installation requirements
+ * but can also be used for more general status information like maintenance
+ * tasks and security issues.
+ * The returned 'requirements' will be listed on the status report in the
+ * administration section, with indication of the severity level.
+ * Moreover, any requirement with a severity of REQUIREMENT_ERROR severity will
+ * result in a notice on the administration configuration page.
+ *
+ * @param $phase
+ * The phase in which requirements are checked:
+ * - install: The module is being installed.
+ * - update: The module is enabled and update.php is run.
+ * - runtime: The runtime requirements are being checked and shown on the
+ * status report page.
+ *
+ * @return
+ * An associative array where the keys are arbitrary but must be unique (it
+ * is suggested to use the module short name as a prefix) and the values are
+ * themselves associative arrays with the following elements:
+ * - title: The name of the requirement.
+ * - value: The current value (e.g., version, time, level, etc). During
+ * install phase, this should only be used for version numbers, do not set
+ * it if not applicable.
+ * - description: The description of the requirement/status.
+ * - severity: The requirement's result/severity level, one of:
+ * - REQUIREMENT_INFO: For info only.
+ * - REQUIREMENT_OK: The requirement is satisfied.
+ * - REQUIREMENT_WARNING: The requirement failed with a warning.
+ * - REQUIREMENT_ERROR: The requirement failed with an error.
+ */
+function hook_requirements($phase) {
+ $requirements = array();
+ // Ensure translations don't break during installation.
+ $t = get_t();
+
+ // Report Drupal version
+ if ($phase == 'runtime') {
+ $requirements['drupal'] = array(
+ 'title' => $t('Drupal'),
+ 'value' => VERSION,
+ 'severity' => REQUIREMENT_INFO
+ );
+ }
+
+ // Test PHP version
+ $requirements['php'] = array(
+ 'title' => $t('PHP'),
+ 'value' => ($phase == 'runtime') ? l(phpversion(), 'admin/reports/status/php') : phpversion(),
+ );
+ if (version_compare(phpversion(), DRUPAL_MINIMUM_PHP) < 0) {
+ $requirements['php']['description'] = $t('Your PHP installation is too old. Drupal requires at least PHP %version.', array('%version' => DRUPAL_MINIMUM_PHP));
+ $requirements['php']['severity'] = REQUIREMENT_ERROR;
+ }
+
+ // Report cron status
+ if ($phase == 'runtime') {
+ $cron_last = variable_get('cron_last');
+
+ if (is_numeric($cron_last)) {
+ $requirements['cron']['value'] = $t('Last run !time ago', array('!time' => format_interval(REQUEST_TIME - $cron_last)));
+ }
+ else {
+ $requirements['cron'] = array(
+ 'description' => $t('Cron has not run. It appears cron jobs have not been setup on your system. Check the help pages for <a href="@url">configuring cron jobs</a>.', array('@url' => 'http://drupal.org/cron')),
+ 'severity' => REQUIREMENT_ERROR,
+ 'value' => $t('Never run'),
+ );
+ }
+
+ $requirements['cron']['description'] .= ' ' . $t('You can <a href="@cron">run cron manually</a>.', array('@cron' => url('admin/reports/status/run-cron')));
+
+ $requirements['cron']['title'] = $t('Cron maintenance tasks');
+ }
+
+ return $requirements;
+}
+
+/**
+ * Define the current version of the database schema.
+ *
+ * A Drupal schema definition is an array structure representing one or more
+ * tables and their related keys and indexes. A schema is defined by
+ * hook_schema() which must live in your module's .install file.
+ *
+ * This hook is called at install and uninstall time, and in the latter case, it
+ * cannot rely on the .module file being loaded or hooks being known. If the
+ * .module file is needed, it may be loaded with drupal_load().
+ *
+ * The tables declared by this hook will be automatically created when the
+ * module is first enabled, and removed when the module is uninstalled. This
+ * happens before hook_install() is invoked, and after hook_uninstall() is
+ * invoked, respectively.
+ *
+ * By declaring the tables used by your module via an implementation of
+ * hook_schema(), these tables will be available on all supported database
+ * engines. You don't have to deal with the different SQL dialects for table
+ * creation and alteration of the supported database engines.
+ *
+ * See the Schema API Handbook at http://drupal.org/node/146843 for details on
+ * schema definition structures.
+ *
+ * @return array
+ * A schema definition structure array. For each element of the
+ * array, the key is a table name and the value is a table structure
+ * definition.
+ *
+ * @see hook_schema_alter()
+ *
+ * @ingroup schemaapi
+ */
+function hook_schema() {
+ $schema['node'] = array(
+ // Example (partial) specification for table "node".
+ 'description' => 'The base table for nodes.',
+ 'fields' => array(
+ 'nid' => array(
+ 'description' => 'The primary identifier for a node.',
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ 'vid' => array(
+ 'description' => 'The current {node_revision}.vid version identifier.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'type' => array(
+ 'description' => 'The {node_type} of this node.',
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'title' => array(
+ 'description' => 'The title of this node, always treated as non-markup plain text.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ ),
+ 'indexes' => array(
+ 'node_changed' => array('changed'),
+ 'node_created' => array('created'),
+ ),
+ 'unique keys' => array(
+ 'nid_vid' => array('nid', 'vid'),
+ 'vid' => array('vid'),
+ ),
+ 'foreign keys' => array(
+ 'node_revision' => array(
+ 'table' => 'node_revision',
+ 'columns' => array('vid' => 'vid'),
+ ),
+ 'node_author' => array(
+ 'table' => 'users',
+ 'columns' => array('uid' => 'uid'),
+ ),
+ ),
+ 'primary key' => array('nid'),
+ );
+ return $schema;
+}
+
+/**
+ * Perform alterations to existing database schemas.
+ *
+ * When a module modifies the database structure of another module (by
+ * changing, adding or removing fields, keys or indexes), it should
+ * implement hook_schema_alter() to update the default $schema to take its
+ * changes into account.
+ *
+ * See hook_schema() for details on the schema definition structure.
+ *
+ * @param $schema
+ * Nested array describing the schemas for all modules.
+ *
+ * @ingroup schemaapi
+ */
+function hook_schema_alter(&$schema) {
+ // Add field to existing schema.
+ $schema['users']['fields']['timezone_id'] = array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'Per-user timezone configuration.',
+ );
+}
+
+/**
+ * Perform alterations to a structured query.
+ *
+ * Structured (aka dynamic) queries that have tags associated may be altered by any module
+ * before the query is executed.
+ *
+ * @param $query
+ * A Query object describing the composite parts of a SQL query.
+ *
+ * @see hook_query_TAG_alter()
+ * @see node_query_node_access_alter()
+ * @see QueryAlterableInterface
+ * @see SelectQueryInterface
+ */
+function hook_query_alter(QueryAlterableInterface $query) {
+ if ($query->hasTag('micro_limit')) {
+ $query->range(0, 2);
+ }
+}
+
+/**
+ * Perform alterations to a structured query for a given tag.
+ *
+ * @param $query
+ * An Query object describing the composite parts of a SQL query.
+ *
+ * @see hook_query_alter()
+ * @see node_query_node_access_alter()
+ * @see QueryAlterableInterface
+ * @see SelectQueryInterface
+ */
+function hook_query_TAG_alter(QueryAlterableInterface $query) {
+ // Skip the extra expensive alterations if site has no node access control modules.
+ if (!node_access_view_all_nodes()) {
+ // Prevent duplicates records.
+ $query->distinct();
+ // The recognized operations are 'view', 'update', 'delete'.
+ if (!$op = $query->getMetaData('op')) {
+ $op = 'view';
+ }
+ // Skip the extra joins and conditions for node admins.
+ if (!user_access('bypass node access')) {
+ // The node_access table has the access grants for any given node.
+ $access_alias = $query->join('node_access', 'na', '%alias.nid = n.nid');
+ $or = db_or();
+ // If any grant exists for the specified user, then user has access to the node for the specified operation.
+ foreach (node_access_grants($op, $query->getMetaData('account')) as $realm => $gids) {
+ foreach ($gids as $gid) {
+ $or->condition(db_and()
+ ->condition($access_alias . '.gid', $gid)
+ ->condition($access_alias . '.realm', $realm)
+ );
+ }
+ }
+
+ if (count($or->conditions())) {
+ $query->condition($or);
+ }
+
+ $query->condition($access_alias . 'grant_' . $op, 1, '>=');
+ }
+ }
+}
+
+/**
+ * Perform setup tasks when the module is installed.
+ *
+ * If the module implements hook_schema(), the database tables will
+ * be created before this hook is fired.
+ *
+ * Implementations of this hook are by convention declared in the module's
+ * .install file. The implementation can rely on the .module file being loaded.
+ * The hook will only be called the first time a module is enabled or after it
+ * is re-enabled after being uninstalled. The module's schema version will be
+ * set to the module's greatest numbered update hook. Because of this, any time
+ * a hook_update_N() is added to the module, this function needs to be updated
+ * to reflect the current version of the database schema.
+ *
+ * See the @link http://drupal.org/node/146843 Schema API documentation @endlink
+ * for details on hook_schema and how database tables are defined.
+ *
+ * Note that since this function is called from a full bootstrap, all functions
+ * (including those in modules enabled by the current page request) are
+ * available when this hook is called. Use cases could be displaying a user
+ * message, or calling a module function necessary for initial setup, etc.
+ *
+ * Please be sure that anything added or modified in this function that can
+ * be removed during uninstall should be removed with hook_uninstall().
+ *
+ * @see hook_schema()
+ * @see module_enable()
+ * @see hook_enable()
+ * @see hook_disable()
+ * @see hook_uninstall()
+ * @see hook_modules_installed()
+ */
+function hook_install() {
+ // Populate the default {node_access} record.
+ db_insert('node_access')
+ ->fields(array(
+ 'nid' => 0,
+ 'gid' => 0,
+ 'realm' => 'all',
+ 'grant_view' => 1,
+ 'grant_update' => 0,
+ 'grant_delete' => 0,
+ ))
+ ->execute();
+}
+
+/**
+ * Perform a single update.
+ *
+ * For each change that requires one or more actions to be performed when
+ * updating a site, add a new hook_update_N(), which will be called by
+ * update.php. The documentation block preceding this function is stripped of
+ * newlines and used as the description for the update on the pending updates
+ * task list. Schema updates should adhere to the
+ * @link http://drupal.org/node/150215 Schema API. @endlink
+ *
+ * Implementations of hook_update_N() are named (module name)_update_(number).
+ * The numbers are composed of three parts:
+ * - 1 digit for Drupal core compatibility.
+ * - 1 digit for your module's major release version (e.g., is this the 7.x-1.*
+ * (1) or 7.x-2.* (2) series of your module?). This digit should be 0 for
+ * initial porting of your module to a new Drupal core API.
+ * - 2 digits for sequential counting, starting with 00.
+ *
+ * Examples:
+ * - mymodule_update_7000(): This is the required update for mymodule to run
+ * with Drupal core API 7.x when upgrading from Drupal core API 6.x.
+ * - mymodule_update_7100(): This is the first update to get the database ready
+ * to run mymodule 7.x-1.*.
+ * - mymodule_update_7200(): This is the first update to get the database ready
+ * to run mymodule 7.x-2.*. Users can directly update from 6.x-2.* to 7.x-2.*
+ * and they get all 70xx and 72xx updates, but not 71xx updates, because
+ * those reside in the 7.x-1.x branch only.
+ *
+ * A good rule of thumb is to remove updates older than two major releases of
+ * Drupal. See hook_update_last_removed() to notify Drupal about the removals.
+ * For further information about releases and release numbers see:
+ * @link http://drupal.org/node/711070 Maintaining a drupal.org project with Git @endlink
+ *
+ * Never renumber update functions.
+ *
+ * Implementations of this hook should be placed in a mymodule.install file in
+ * the same directory as mymodule.module. Drupal core's updates are implemented
+ * using the system module as a name and stored in database/updates.inc.
+ *
+ * Not all module functions are available from within a hook_update_N() function.
+ * In order to call a function from your mymodule.module or an include file,
+ * you need to explicitly load that file first.
+ *
+ * During database updates the schema of any module could be out of date. For
+ * this reason, caution is needed when using any API function within an update
+ * function - particularly CRUD functions, functions that depend on the schema
+ * (for example by using drupal_write_record()), and any functions that invoke
+ * hooks. See @link update_api Update versions of API functions @endlink for
+ * details.
+ *
+ * If your update task is potentially time-consuming, you'll need to implement a
+ * multipass update to avoid PHP timeouts. Multipass updates use the $sandbox
+ * parameter provided by the batch API (normally, $context['sandbox']) to store
+ * information between successive calls, and the $sandbox['#finished'] value
+ * to provide feedback regarding completion level.
+ *
+ * See the batch operations page for more information on how to use the
+ * @link http://drupal.org/node/180528 Batch API. @endlink
+ *
+ * @param $sandbox
+ * Stores information for multipass updates. See above for more information.
+ *
+ * @throws DrupalUpdateException, PDOException
+ * In case of error, update hooks should throw an instance of DrupalUpdateException
+ * with a meaningful message for the user. If a database query fails for whatever
+ * reason, it will throw a PDOException.
+ *
+ * @return
+ * Optionally, update hooks may return a translated string that will be
+ * displayed to the user after the update has completed. If no message is
+ * returned, no message will be presented to the user.
+ *
+ * @see batch
+ * @see schemaapi
+ * @see update_api
+ * @see hook_update_last_removed()
+ * @see update_get_update_list()
+ */
+function hook_update_N(&$sandbox) {
+ // For non-multipass updates, the signature can simply be;
+ // function hook_update_N() {
+
+ // For most updates, the following is sufficient.
+ db_add_field('mytable1', 'newcol', array('type' => 'int', 'not null' => TRUE, 'description' => 'My new integer column.'));
+
+ // However, for more complex operations that may take a long time,
+ // you may hook into Batch API as in the following example.
+
+ // Update 3 users at a time to have an exclamation point after their names.
+ // (They're really happy that we can do batch API in this hook!)
+ if (!isset($sandbox['progress'])) {
+ $sandbox['progress'] = 0;
+ $sandbox['current_uid'] = 0;
+ // We'll -1 to disregard the uid 0...
+ $sandbox['max'] = db_query('SELECT COUNT(DISTINCT uid) FROM {users}')->fetchField() - 1;
+ }
+
+ $users = db_select('users', 'u')
+ ->fields('u', array('uid', 'name'))
+ ->condition('uid', $sandbox['current_uid'], '>')
+ ->range(0, 3)
+ ->orderBy('uid', 'ASC')
+ ->execute();
+
+ foreach ($users as $user) {
+ $user->name .= '!';
+ db_update('users')
+ ->fields(array('name' => $user->name))
+ ->condition('uid', $user->uid)
+ ->execute();
+
+ $sandbox['progress']++;
+ $sandbox['current_uid'] = $user->uid;
+ }
+
+ $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']);
+
+ // To display a message to the user when the update is completed, return it.
+ // If you do not want to display a completion message, simply return nothing.
+ return t('The update did what it was supposed to do.');
+
+ // In case of an error, simply throw an exception with an error message.
+ throw new DrupalUpdateException('Something went wrong; here is what you should do.');
+}
+
+/**
+ * Return an array of information about module update dependencies.
+ *
+ * This can be used to indicate update functions from other modules that your
+ * module's update functions depend on, or vice versa. It is used by the update
+ * system to determine the appropriate order in which updates should be run, as
+ * well as to search for missing dependencies.
+ *
+ * Implementations of this hook should be placed in a mymodule.install file in
+ * the same directory as mymodule.module.
+ *
+ * @return
+ * A multidimensional array containing information about the module update
+ * dependencies. The first two levels of keys represent the module and update
+ * number (respectively) for which information is being returned, and the
+ * value is an array of information about that update's dependencies. Within
+ * this array, each key represents a module, and each value represents the
+ * number of an update function within that module. In the event that your
+ * update function depends on more than one update from a particular module,
+ * you should always list the highest numbered one here (since updates within
+ * a given module always run in numerical order).
+ *
+ * @see update_resolve_dependencies()
+ * @see hook_update_N()
+ */
+function hook_update_dependencies() {
+ // Indicate that the mymodule_update_7000() function provided by this module
+ // must run after the another_module_update_7002() function provided by the
+ // 'another_module' module.
+ $dependencies['mymodule'][7000] = array(
+ 'another_module' => 7002,
+ );
+ // Indicate that the mymodule_update_7001() function provided by this module
+ // must run before the yet_another_module_update_7004() function provided by
+ // the 'yet_another_module' module. (Note that declaring dependencies in this
+ // direction should be done only in rare situations, since it can lead to the
+ // following problem: If a site has already run the yet_another_module
+ // module's database updates before it updates its codebase to pick up the
+ // newest mymodule code, then the dependency declared here will be ignored.)
+ $dependencies['yet_another_module'][7004] = array(
+ 'mymodule' => 7001,
+ );
+ return $dependencies;
+}
+
+/**
+ * Return a number which is no longer available as hook_update_N().
+ *
+ * If you remove some update functions from your mymodule.install file, you
+ * should notify Drupal of those missing functions. This way, Drupal can
+ * ensure that no update is accidentally skipped.
+ *
+ * Implementations of this hook should be placed in a mymodule.install file in
+ * the same directory as mymodule.module.
+ *
+ * @return
+ * An integer, corresponding to hook_update_N() which has been removed from
+ * mymodule.install.
+ *
+ * @see hook_update_N()
+ */
+function hook_update_last_removed() {
+ // We've removed the 5.x-1.x version of mymodule, including database updates.
+ // The next update function is mymodule_update_5200().
+ return 5103;
+}
+
+/**
+ * Remove any information that the module sets.
+ *
+ * The information that the module should remove includes:
+ * - variables that the module has set using variable_set() or system_settings_form()
+ * - modifications to existing tables
+ *
+ * The module should not remove its entry from the {system} table. Database
+ * tables defined by hook_schema() will be removed automatically.
+ *
+ * The uninstall hook must be implemented in the module's .install file. It
+ * will fire when the module gets uninstalled but before the module's database
+ * tables are removed, allowing your module to query its own tables during
+ * this routine.
+ *
+ * When hook_uninstall() is called, your module will already be disabled, so
+ * its .module file will not be automatically included. If you need to call API
+ * functions from your .module file in this hook, use drupal_load() to make
+ * them available. (Keep this usage to a minimum, though, especially when
+ * calling API functions that invoke hooks, or API functions from modules
+ * listed as dependencies, since these may not be available or work as expected
+ * when the module is disabled.)
+ *
+ * @see hook_install()
+ * @see hook_schema()
+ * @see hook_disable()
+ * @see hook_modules_uninstalled()
+ */
+function hook_uninstall() {
+ variable_del('upload_file_types');
+}
+
+/**
+ * Perform necessary actions after module is enabled.
+ *
+ * The hook is called every time the module is enabled. It should be
+ * implemented in the module's .install file. The implementation can
+ * rely on the .module file being loaded.
+ *
+ * @see module_enable()
+ * @see hook_install()
+ * @see hook_modules_enabled()
+ */
+function hook_enable() {
+ mymodule_cache_rebuild();
+}
+
+/**
+ * Perform necessary actions before module is disabled.
+ *
+ * The hook is called every time the module is disabled. It should be
+ * implemented in the module's .install file. The implementation can rely
+ * on the .module file being loaded.
+ *
+ * @see hook_uninstall()
+ * @see hook_modules_disabled()
+ */
+function hook_disable() {
+ mymodule_cache_rebuild();
+}
+
+/**
+ * Perform necessary alterations to the list of files parsed by the registry.
+ *
+ * Modules can manually modify the list of files before the registry parses
+ * them. The $modules array provides the .info file information, which includes
+ * the list of files registered to each module. Any files in the list can then
+ * be added to the list of files that the registry will parse, or modify
+ * attributes of a file.
+ *
+ * A necessary alteration made by the core SimpleTest module is to force .test
+ * files provided by disabled modules into the list of files parsed by the
+ * registry.
+ *
+ * @param $files
+ * List of files to be parsed by the registry. The list will contain
+ * files found in each enabled module's info file and the core includes
+ * directory. The array is keyed by the file path and contains an array of
+ * the related module's name and weight as used internally by
+ * _registry_update() and related functions.
+ *
+ * For example:
+ * @code
+ * $files["modules/system/system.module"] = array(
+ * 'module' => 'system',
+ * 'weight' => 0,
+ * );
+ * @endcode
+ * @param $modules
+ * An array containing all module information stored in the {system} table.
+ * Each element of the array also contains the module's .info file
+ * information in the property 'info'. An additional 'dir' property has been
+ * added to the module information which provides the path to the directory
+ * in which the module resides. The example shows how to take advantage of
+ * both properties.
+ *
+ * @see _registry_update()
+ * @see simpletest_test_get_all()
+ */
+function hook_registry_files_alter(&$files, $modules) {
+ foreach ($modules as $module) {
+ // Only add test files for disabled modules, as enabled modules should
+ // already include any test files they provide.
+ if (!$module->status) {
+ $dir = $module->dir;
+ foreach ($module->info['files'] as $file) {
+ if (substr($file, -5) == '.test') {
+ $files["$dir/$file"] = array('module' => $module->name, 'weight' => $module->weight);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Return an array of tasks to be performed by an installation profile.
+ *
+ * Any tasks you define here will be run, in order, after the installer has
+ * finished the site configuration step but before it has moved on to the
+ * final import of languages and the end of the installation. You can have any
+ * number of custom tasks to perform during this phase.
+ *
+ * Each task you define here corresponds to a callback function which you must
+ * separately define and which is called when your task is run. This function
+ * will receive the global installation state variable, $install_state, as
+ * input, and has the opportunity to access or modify any of its settings. See
+ * the install_state_defaults() function in the installer for the list of
+ * $install_state settings used by Drupal core.
+ *
+ * At the end of your task function, you can indicate that you want the
+ * installer to pause and display a page to the user by returning any themed
+ * output that should be displayed on that page (but see below for tasks that
+ * use the form API or batch API; the return values of these task functions are
+ * handled differently). You should also use drupal_set_title() within the task
+ * callback function to set a custom page title. For some tasks, however, you
+ * may want to simply do some processing and pass control to the next task
+ * without ending the page request; to indicate this, simply do not send back
+ * a return value from your task function at all. This can be used, for
+ * example, by installation profiles that need to configure certain site
+ * settings in the database without obtaining any input from the user.
+ *
+ * The task function is treated specially if it defines a form or requires
+ * batch processing; in that case, you should return either the form API
+ * definition or batch API array, as appropriate. See below for more
+ * information on the 'type' key that you must define in the task definition
+ * to inform the installer that your task falls into one of those two
+ * categories. It is important to use these APIs directly, since the installer
+ * may be run non-interactively (for example, via a command line script), all
+ * in one page request; in that case, the installer will automatically take
+ * care of submitting forms and processing batches correctly for both types of
+ * installations. You can inspect the $install_state['interactive'] boolean to
+ * see whether or not the current installation is interactive, if you need
+ * access to this information.
+ *
+ * Remember that a user installing Drupal interactively will be able to reload
+ * an installation page multiple times, so you should use variable_set() and
+ * variable_get() if you are collecting any data that you need to store and
+ * inspect later. It is important to remove any temporary variables using
+ * variable_del() before your last task has completed and control is handed
+ * back to the installer.
+ *
+ * @param array $install_state
+ * An array of information about the current installation state.
+ *
+ * @return array
+ * A keyed array of tasks the profile will perform during the final stage of
+ * the installation. Each key represents the name of a function (usually a
+ * function defined by this profile, although that is not strictly required)
+ * that is called when that task is run. The values are associative arrays
+ * containing the following key-value pairs (all of which are optional):
+ * - display_name: The human-readable name of the task. This will be
+ * displayed to the user while the installer is running, along with a list
+ * of other tasks that are being run. Leave this unset to prevent the task
+ * from appearing in the list.
+ * - display: This is a boolean which can be used to provide finer-grained
+ * control over whether or not the task will display. This is mostly useful
+ * for tasks that are intended to display only under certain conditions;
+ * for these tasks, you can set 'display_name' to the name that you want to
+ * display, but then use this boolean to hide the task only when certain
+ * conditions apply.
+ * - type: A string representing the type of task. This parameter has three
+ * possible values:
+ * - normal: (default) This indicates that the task will be treated as a
+ * regular callback function, which does its processing and optionally
+ * returns HTML output.
+ * - batch: This indicates that the task function will return a batch API
+ * definition suitable for batch_set(). The installer will then take care
+ * of automatically running the task via batch processing.
+ * - form: This indicates that the task function will return a standard
+ * form API definition (and separately define validation and submit
+ * handlers, as appropriate). The installer will then take care of
+ * automatically directing the user through the form submission process.
+ * - run: A constant representing the manner in which the task will be run.
+ * This parameter has three possible values:
+ * - INSTALL_TASK_RUN_IF_NOT_COMPLETED: (default) This indicates that the
+ * task will run once during the installation of the profile.
+ * - INSTALL_TASK_SKIP: This indicates that the task will not run during
+ * the current installation page request. It can be used to skip running
+ * an installation task when certain conditions are met, even though the
+ * task may still show on the list of installation tasks presented to the
+ * user.
+ * - INSTALL_TASK_RUN_IF_REACHED: This indicates that the task will run on
+ * each installation page request that reaches it. This is rarely
+ * necessary for an installation profile to use; it is primarily used by
+ * the Drupal installer for bootstrap-related tasks.
+ * - function: Normally this does not need to be set, but it can be used to
+ * force the installer to call a different function when the task is run
+ * (rather than the function whose name is given by the array key). This
+ * could be used, for example, to allow the same function to be called by
+ * two different tasks.
+ *
+ * @see install_state_defaults()
+ * @see batch_set()
+ */
+function hook_install_tasks(&$install_state) {
+ // Here, we define a variable to allow tasks to indicate that a particular,
+ // processor-intensive batch process needs to be triggered later on in the
+ // installation.
+ $myprofile_needs_batch_processing = variable_get('myprofile_needs_batch_processing', FALSE);
+ $tasks = array(
+ // This is an example of a task that defines a form which the user who is
+ // installing the site will be asked to fill out. To implement this task,
+ // your profile would define a function named myprofile_data_import_form()
+ // as a normal form API callback function, with associated validation and
+ // submit handlers. In the submit handler, in addition to saving whatever
+ // other data you have collected from the user, you might also call
+ // variable_set('myprofile_needs_batch_processing', TRUE) if the user has
+ // entered data which requires that batch processing will need to occur
+ // later on.
+ 'myprofile_data_import_form' => array(
+ 'display_name' => st('Data import options'),
+ 'type' => 'form',
+ ),
+ // Similarly, to implement this task, your profile would define a function
+ // named myprofile_settings_form() with associated validation and submit
+ // handlers. This form might be used to collect and save additional
+ // information from the user that your profile needs. There are no extra
+ // steps required for your profile to act as an "installation wizard"; you
+ // can simply define as many tasks of type 'form' as you wish to execute,
+ // and the forms will be presented to the user, one after another.
+ 'myprofile_settings_form' => array(
+ 'display_name' => st('Additional options'),
+ 'type' => 'form',
+ ),
+ // This is an example of a task that performs batch operations. To
+ // implement this task, your profile would define a function named
+ // myprofile_batch_processing() which returns a batch API array definition
+ // that the installer will use to execute your batch operations. Due to the
+ // 'myprofile_needs_batch_processing' variable used here, this task will be
+ // hidden and skipped unless your profile set it to TRUE in one of the
+ // previous tasks.
+ 'myprofile_batch_processing' => array(
+ 'display_name' => st('Import additional data'),
+ 'display' => $myprofile_needs_batch_processing,
+ 'type' => 'batch',
+ 'run' => $myprofile_needs_batch_processing ? INSTALL_TASK_RUN_IF_NOT_COMPLETED : INSTALL_TASK_SKIP,
+ ),
+ // This is an example of a task that will not be displayed in the list that
+ // the user sees. To implement this task, your profile would define a
+ // function named myprofile_final_site_setup(), in which additional,
+ // automated site setup operations would be performed. Since this is the
+ // last task defined by your profile, you should also use this function to
+ // call variable_del('myprofile_needs_batch_processing') and clean up the
+ // variable that was used above. If you want the user to pass to the final
+ // Drupal installation tasks uninterrupted, return no output from this
+ // function. Otherwise, return themed output that the user will see (for
+ // example, a confirmation page explaining that your profile's tasks are
+ // complete, with a link to reload the current page and therefore pass on
+ // to the final Drupal installation tasks when the user is ready to do so).
+ 'myprofile_final_site_setup' => array(
+ ),
+ );
+ return $tasks;
+}
+
+/**
+ * Change the page the user is sent to by drupal_goto().
+ *
+ * @param $path
+ * A Drupal path or a full URL.
+ * @param $options
+ * An associative array of additional URL options to pass to url().
+ * @param $http_response_code
+ * The HTTP status code to use for the redirection. See drupal_goto() for more
+ * information.
+ */
+function hook_drupal_goto_alter(&$path, &$options, &$http_response_code) {
+ // A good addition to misery module.
+ $http_response_code = 500;
+}
+
+/**
+ * Alter XHTML HEAD tags before they are rendered by drupal_get_html_head().
+ *
+ * Elements available to be altered are only those added using
+ * drupal_add_html_head_link() or drupal_add_html_head(). CSS and JS files
+ * are handled using drupal_add_css() and drupal_add_js(), so the head links
+ * for those files will not appear in the $head_elements array.
+ *
+ * @param $head_elements
+ * An array of renderable elements. Generally the values of the #attributes
+ * array will be the most likely target for changes.
+ */
+function hook_html_head_alter(&$head_elements) {
+ foreach ($head_elements as $key => $element) {
+ if (isset($element['#attributes']['rel']) && $element['#attributes']['rel'] == 'canonical') {
+ // I want a custom canonical URL.
+ $head_elements[$key]['#attributes']['href'] = mymodule_canonical_url();
+ }
+ }
+}
+
+/**
+ * Alter the full list of installation tasks.
+ *
+ * @param $tasks
+ * An array of all available installation tasks, including those provided by
+ * Drupal core. You can modify this array to change or replace any part of
+ * the Drupal installation process that occurs after the installation profile
+ * is selected.
+ * @param $install_state
+ * An array of information about the current installation state.
+ */
+function hook_install_tasks_alter(&$tasks, $install_state) {
+ // Replace the "Choose language" installation task provided by Drupal core
+ // with a custom callback function defined by this installation profile.
+ $tasks['install_select_locale']['function'] = 'myprofile_locale_selection';
+}
+
+/**
+ * Alter MIME type mappings used to determine MIME type from a file extension.
+ *
+ * This hook is run when file_mimetype_mapping() is called. It is used to
+ * allow modules to add to or modify the default mapping from
+ * file_default_mimetype_mapping().
+ *
+ * @param $mapping
+ * An array of mimetypes correlated to the extensions that relate to them.
+ * The array has 'mimetypes' and 'extensions' elements, each of which is an
+ * array.
+ *
+ * @see file_default_mimetype_mapping()
+ */
+function hook_file_mimetype_mapping_alter(&$mapping) {
+ // Add new MIME type 'drupal/info'.
+ $mapping['mimetypes']['example_info'] = 'drupal/info';
+ // Add new extension '.info' and map it to the 'drupal/info' MIME type.
+ $mapping['extensions']['info'] = 'example_info';
+ // Override existing extension mapping for '.ogg' files.
+ $mapping['extensions']['ogg'] = 189;
+}
+
+/**
+ * Declares information about actions.
+ *
+ * Any module can define actions, and then call actions_do() to make those
+ * actions happen in response to events. The trigger module provides a user
+ * interface for associating actions with module-defined triggers, and it makes
+ * sure the core triggers fire off actions when their events happen.
+ *
+ * An action consists of two or three parts:
+ * - an action definition (returned by this hook)
+ * - a function which performs the action (which by convention is named
+ * MODULE_description-of-function_action)
+ * - an optional form definition function that defines a configuration form
+ * (which has the name of the action function with '_form' appended to it.)
+ *
+ * The action function takes two to four arguments, which come from the input
+ * arguments to actions_do().
+ *
+ * @return
+ * An associative array of action descriptions. The keys of the array
+ * are the names of the action functions, and each corresponding value
+ * is an associative array with the following key-value pairs:
+ * - 'type': The type of object this action acts upon. Core actions have types
+ * 'node', 'user', 'comment', and 'system'.
+ * - 'label': The human-readable name of the action, which should be passed
+ * through the t() function for translation.
+ * - 'configurable': If FALSE, then the action doesn't require any extra
+ * configuration. If TRUE, then your module must define a form function with
+ * the same name as the action function with '_form' appended (e.g., the
+ * form for 'node_assign_owner_action' is 'node_assign_owner_action_form'.)
+ * This function takes $context as its only parameter, and is paired with
+ * the usual _submit function, and possibly a _validate function.
+ * - 'triggers': An array of the events (that is, hooks) that can trigger this
+ * action. For example: array('node_insert', 'user_update'). You can also
+ * declare support for any trigger by returning array('any') for this value.
+ * - 'behavior': (optional) A machine-readable array of behaviors of this
+ * action, used to signal additionally required actions that may need to be
+ * triggered. Currently recognized behaviors by Trigger module:
+ * - 'changes_property': If an action with this behavior is assigned to a
+ * trigger other than a "presave" hook, any save actions also assigned to
+ * this trigger are moved later in the list. If no save action is present,
+ * one will be added.
+ * Modules that are processing actions (like Trigger module) should take
+ * special care for the "presave" hook, in which case a dependent "save"
+ * action should NOT be invoked.
+ *
+ * @ingroup actions
+ */
+function hook_action_info() {
+ return array(
+ 'comment_unpublish_action' => array(
+ 'type' => 'comment',
+ 'label' => t('Unpublish comment'),
+ 'configurable' => FALSE,
+ 'behavior' => array('changes_property'),
+ 'triggers' => array('comment_presave', 'comment_insert', 'comment_update'),
+ ),
+ 'comment_unpublish_by_keyword_action' => array(
+ 'type' => 'comment',
+ 'label' => t('Unpublish comment containing keyword(s)'),
+ 'configurable' => TRUE,
+ 'behavior' => array('changes_property'),
+ 'triggers' => array('comment_presave', 'comment_insert', 'comment_update'),
+ ),
+ 'comment_save_action' => array(
+ 'type' => 'comment',
+ 'label' => t('Save comment'),
+ 'configurable' => FALSE,
+ 'triggers' => array('comment_insert', 'comment_update'),
+ ),
+ );
+}
+
+/**
+ * Executes code after an action is deleted.
+ *
+ * @param $aid
+ * The action ID.
+ */
+function hook_actions_delete($aid) {
+ db_delete('actions_assignments')
+ ->condition('aid', $aid)
+ ->execute();
+}
+
+/**
+ * Alters the actions declared by another module.
+ *
+ * Called by actions_list() to allow modules to alter the return values from
+ * implementations of hook_action_info().
+ *
+ * @see trigger_example_action_info_alter()
+ */
+function hook_action_info_alter(&$actions) {
+ $actions['node_unpublish_action']['label'] = t('Unpublish and remove from public view.');
+}
+
+/**
+ * Declare archivers to the system.
+ *
+ * An archiver is a class that is able to package and unpackage one or more files
+ * into a single possibly compressed file. Common examples of such files are
+ * zip files and tar.gz files. All archiver classes must implement
+ * ArchiverInterface.
+ *
+ * Each entry should be keyed on a unique value, and specify three
+ * additional keys:
+ * - class: The name of the PHP class for this archiver.
+ * - extensions: An array of file extensions that this archiver supports.
+ * - weight: This optional key specifies the weight of this archiver.
+ * When mapping file extensions to archivers, the first archiver by
+ * weight found that supports the requested extension will be used.
+ *
+ * @see hook_archiver_info_alter()
+ */
+function hook_archiver_info() {
+ return array(
+ 'tar' => array(
+ 'class' => 'ArchiverTar',
+ 'extensions' => array('tar', 'tar.gz', 'tar.bz2'),
+ ),
+ );
+}
+
+/**
+ * Alter archiver information declared by other modules.
+ *
+ * See hook_archiver_info() for a description of archivers and the archiver
+ * information structure.
+ *
+ * @param $info
+ * Archiver information to alter (return values from hook_archiver_info()).
+ */
+function hook_archiver_info_alter(&$info) {
+ $info['tar']['extensions'][] = 'tgz';
+}
+
+/**
+ * Define additional date types.
+ *
+ * Next to the 'long', 'medium' and 'short' date types defined in core, any
+ * module can define additional types that can be used when displaying dates,
+ * by implementing this hook. A date type is basically just a name for a date
+ * format.
+ *
+ * Date types are used in the administration interface: a user can assign
+ * date format types defined in hook_date_formats() to date types defined in
+ * this hook. Once a format has been assigned by a user, the machine name of a
+ * type can be used in the format_date() function to format a date using the
+ * chosen formatting.
+ *
+ * To define a date type in a module and make sure a format has been assigned to
+ * it, without requiring a user to visit the administrative interface, use
+ * @code variable_set('date_format_' . $type, $format); @endcode
+ * where $type is the machine-readable name defined here, and $format is a PHP
+ * date format string.
+ *
+ * To avoid namespace collisions with date types defined by other modules, it is
+ * recommended that each date type starts with the module name. A date type
+ * can consist of letters, numbers and underscores.
+ *
+ * @return
+ * An array of date types where the keys are the machine-readable names and
+ * the values are the human-readable labels.
+ *
+ * @see hook_date_formats()
+ * @see format_date()
+ */
+function hook_date_format_types() {
+ // Define the core date format types.
+ return array(
+ 'long' => t('Long'),
+ 'medium' => t('Medium'),
+ 'short' => t('Short'),
+ );
+}
+
+/**
+ * Modify existing date types.
+ *
+ * Allows other modules to modify existing date types like 'long'. Called by
+ * _system_date_format_types_build(). For instance, A module may use this hook
+ * to apply settings across all date types, such as locking all date types so
+ * they appear to be provided by the system.
+ *
+ * @param $types
+ * A list of date types. Each date type is keyed by the machine-readable name
+ * and the values are associative arrays containing:
+ * - is_new: Set to FALSE to override previous settings.
+ * - module: The name of the module that created the date type.
+ * - type: The machine-readable date type name.
+ * - title: The human-readable date type name.
+ * - locked: Specifies that the date type is system-provided.
+ */
+function hook_date_format_types_alter(&$types) {
+ foreach ($types as $name => $type) {
+ $types[$name]['locked'] = 1;
+ }
+}
+
+/**
+ * Define additional date formats.
+ *
+ * This hook is used to define the PHP date format strings that can be assigned
+ * to date types in the administrative interface. A module can provide date
+ * format strings for the core-provided date types ('long', 'medium', and
+ * 'short'), or for date types defined in hook_date_format_types() by itself
+ * or another module.
+ *
+ * Since date formats can be locale-specific, you can specify the locales that
+ * each date format string applies to. There may be more than one locale for a
+ * format. There may also be more than one format for the same locale. For
+ * example d/m/Y and Y/m/d work equally well in some locales. You may wish to
+ * define some additional date formats that aren't specific to any one locale,
+ * for example, "Y m". For these cases, the 'locales' component of the return
+ * value should be omitted.
+ *
+ * Providing a date format here does not normally assign the format to be
+ * used with the associated date type -- a user has to choose a format for each
+ * date type in the administrative interface. There is one exception: locale
+ * initialization chooses a locale-specific format for the three core-provided
+ * types (see locale_get_localized_date_format() for details). If your module
+ * needs to ensure that a date type it defines has a format associated with it,
+ * call @code variable_set('date_format_' . $type, $format); @endcode
+ * where $type is the machine-readable name defined in hook_date_format_types(),
+ * and $format is a PHP date format string.
+ *
+ * @return
+ * A list of date formats to offer as choices in the administrative
+ * interface. Each date format is a keyed array consisting of three elements:
+ * - 'type': The date type name that this format can be used with, as
+ * declared in an implementation of hook_date_format_types().
+ * - 'format': A PHP date format string to use when formatting dates. It
+ * can contain any of the formatting options described at
+ * http://php.net/manual/function.date.php
+ * - 'locales': (optional) An array of 2 and 5 character locale codes,
+ * defining which locales this format applies to (for example, 'en',
+ * 'en-us', etc.). If your date format is not language-specific, leave this
+ * array empty.
+ *
+ * @see hook_date_format_types()
+ */
+function hook_date_formats() {
+ return array(
+ array(
+ 'type' => 'mymodule_extra_long',
+ 'format' => 'l jS F Y H:i:s e',
+ 'locales' => array('en-ie'),
+ ),
+ array(
+ 'type' => 'mymodule_extra_long',
+ 'format' => 'l jS F Y h:i:sa',
+ 'locales' => array('en', 'en-us'),
+ ),
+ array(
+ 'type' => 'short',
+ 'format' => 'F Y',
+ 'locales' => array(),
+ ),
+ );
+}
+
+/**
+ * Alter date formats declared by another module.
+ *
+ * Called by _system_date_format_types_build() to allow modules to alter the
+ * return values from implementations of hook_date_formats().
+ */
+function hook_date_formats_alter(&$formats) {
+ foreach ($formats as $id => $format) {
+ $formats[$id]['locales'][] = 'en-ca';
+ }
+}
+
+/**
+ * Alters the delivery callback used to send the result of the page callback to the browser.
+ *
+ * Called by drupal_deliver_page() to allow modules to alter how the
+ * page is delivered to the browser.
+ *
+ * This hook is intended for altering the delivery callback based on
+ * information unrelated to the path of the page accessed. For example,
+ * it can be used to set the delivery callback based on a HTTP request
+ * header (as shown in the code sample). To specify a delivery callback
+ * based on path information, use hook_menu() or hook_menu_alter().
+ *
+ * This hook can also be used as an API function that can be used to explicitly
+ * set the delivery callback from some other function. For example, for a module
+ * named MODULE:
+ * @code
+ * function MODULE_page_delivery_callback_alter(&$callback, $set = FALSE) {
+ * static $stored_callback;
+ * if ($set) {
+ * $stored_callback = $callback;
+ * }
+ * elseif (isset($stored_callback)) {
+ * $callback = $stored_callback;
+ * }
+ * }
+ * function SOMEWHERE_ELSE() {
+ * $desired_delivery_callback = 'foo';
+ * MODULE_page_delivery_callback_alter($desired_delivery_callback, TRUE);
+ * }
+ * @endcode
+ *
+ * @param $callback
+ * The name of a function.
+ *
+ * @see drupal_deliver_page()
+ */
+function hook_page_delivery_callback_alter(&$callback) {
+ // jQuery sets a HTTP_X_REQUESTED_WITH header of 'XMLHttpRequest'.
+ // If a page would normally be delivered as an html page, and it is called
+ // from jQuery, deliver it instead as an Ajax response.
+ if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' && $callback == 'drupal_deliver_html_page') {
+ $callback = 'ajax_deliver';
+ }
+}
+
+/**
+ * Alters theme operation links.
+ *
+ * @param $theme_groups
+ * An associative array containing groups of themes.
+ *
+ * @see system_themes_page()
+ */
+function hook_system_themes_page_alter(&$theme_groups) {
+ foreach ($theme_groups as $state => &$group) {
+ foreach ($theme_groups[$state] as &$theme) {
+ // Add a foo link to each list of theme operations.
+ $theme->operations[] = array(
+ 'title' => t('Foo'),
+ 'href' => 'admin/appearance/foo',
+ 'query' => array('theme' => $theme->name)
+ );
+ }
+ }
+}
+
+/**
+ * Alters inbound URL requests.
+ *
+ * @param $path
+ * The path being constructed, which, if a path alias, has been resolved to a
+ * Drupal path by the database, and which also may have been altered by other
+ * modules before this one.
+ * @param $original_path
+ * The original path, before being checked for path aliases or altered by any
+ * modules.
+ * @param $path_language
+ * The language of the path.
+ *
+ * @see drupal_get_normal_path()
+ */
+function hook_url_inbound_alter(&$path, $original_path, $path_language) {
+ // Create the path user/me/edit, which allows a user to edit their account.
+ if (preg_match('|^user/me/edit(/.*)?|', $path, $matches)) {
+ global $user;
+ $path = 'user/' . $user->uid . '/edit' . $matches[1];
+ }
+}
+
+/**
+ * Alters outbound URLs.
+ *
+ * @param $path
+ * The outbound path to alter, not adjusted for path aliases yet. It won't be
+ * adjusted for path aliases until all modules are finished altering it, thus
+ * being consistent with hook_url_inbound_alter(), which adjusts for all path
+ * aliases before allowing modules to alter it. This may have been altered by
+ * other modules before this one.
+ * @param $options
+ * A set of URL options for the URL so elements such as a fragment or a query
+ * string can be added to the URL.
+ * @param $original_path
+ * The original path, before being altered by any modules.
+ *
+ * @see url()
+ */
+function hook_url_outbound_alter(&$path, &$options, $original_path) {
+ // Use an external RSS feed rather than the Drupal one.
+ if ($path == 'rss.xml') {
+ $path = 'http://example.com/rss.xml';
+ $options['external'] = TRUE;
+ }
+
+ // Instead of pointing to user/[uid]/edit, point to user/me/edit.
+ if (preg_match('|^user/([0-9]*)/edit(/.*)?|', $path, $matches)) {
+ global $user;
+ if ($user->uid == $matches[1]) {
+ $path = 'user/me/edit' . $matches[2];
+ }
+ }
+}
+
+/**
+ * Alter the username that is displayed for a user.
+ *
+ * Called by format_username() to allow modules to alter the username that's
+ * displayed. Can be used to ensure user privacy in situations where
+ * $account->name is too revealing.
+ *
+ * @param $name
+ * The string that format_username() will return.
+ *
+ * @param $account
+ * The account object passed to format_username().
+ *
+ * @see format_username()
+ */
+function hook_username_alter(&$name, $account) {
+ // Display the user's uid instead of name.
+ if (isset($account->uid)) {
+ $name = t('User !uid', array('!uid' => $account->uid));
+ }
+}
+
+/**
+ * Provide replacement values for placeholder tokens.
+ *
+ * This hook is invoked when someone calls token_replace(). That function first
+ * scans the text for [type:token] patterns, and splits the needed tokens into
+ * groups by type. Then hook_tokens() is invoked on each token-type group,
+ * allowing your module to respond by providing replacement text for any of
+ * the tokens in the group that your module knows how to process.
+ *
+ * A module implementing this hook should also implement hook_token_info() in
+ * order to list its available tokens on editing screens.
+ *
+ * @param $type
+ * The machine-readable name of the type (group) of token being replaced, such
+ * as 'node', 'user', or another type defined by a hook_token_info()
+ * implementation.
+ * @param $tokens
+ * An array of tokens to be replaced. The keys are the machine-readable token
+ * names, and the values are the raw [type:token] strings that appeared in the
+ * original text.
+ * @param $data
+ * (optional) An associative array of data objects to be used when generating
+ * replacement values, as supplied in the $data parameter to token_replace().
+ * @param $options
+ * (optional) An associative array of options for token replacement; see
+ * token_replace() for possible values.
+ *
+ * @return
+ * An associative array of replacement values, keyed by the raw [type:token]
+ * strings from the original text.
+ *
+ * @see hook_token_info()
+ * @see hook_tokens_alter()
+ */
+function hook_tokens($type, $tokens, array $data = array(), array $options = array()) {
+ $url_options = array('absolute' => TRUE);
+ if (isset($options['language'])) {
+ $url_options['language'] = $options['language'];
+ $language_code = $options['language']->language;
+ }
+ else {
+ $language_code = NULL;
+ }
+ $sanitize = !empty($options['sanitize']);
+
+ $replacements = array();
+
+ if ($type == 'node' && !empty($data['node'])) {
+ $node = $data['node'];
+
+ foreach ($tokens as $name => $original) {
+ switch ($name) {
+ // Simple key values on the node.
+ case 'nid':
+ $replacements[$original] = $node->nid;
+ break;
+
+ case 'title':
+ $replacements[$original] = $sanitize ? check_plain($node->title) : $node->title;
+ break;
+
+ case 'edit-url':
+ $replacements[$original] = url('node/' . $node->nid . '/edit', $url_options);
+ break;
+
+ // Default values for the chained tokens handled below.
+ case 'author':
+ $name = ($node->uid == 0) ? variable_get('anonymous', t('Anonymous')) : $node->name;
+ $replacements[$original] = $sanitize ? filter_xss($name) : $name;
+ break;
+
+ case 'created':
+ $replacements[$original] = format_date($node->created, 'medium', '', NULL, $language_code);
+ break;
+ }
+ }
+
+ if ($author_tokens = token_find_with_prefix($tokens, 'author')) {
+ $author = user_load($node->uid);
+ $replacements += token_generate('user', $author_tokens, array('user' => $author), $options);
+ }
+
+ if ($created_tokens = token_find_with_prefix($tokens, 'created')) {
+ $replacements += token_generate('date', $created_tokens, array('date' => $node->created), $options);
+ }
+ }
+
+ return $replacements;
+}
+
+/**
+ * Alter replacement values for placeholder tokens.
+ *
+ * @param $replacements
+ * An associative array of replacements returned by hook_tokens().
+ * @param $context
+ * The context in which hook_tokens() was called. An associative array with
+ * the following keys, which have the same meaning as the corresponding
+ * parameters of hook_tokens():
+ * - 'type'
+ * - 'tokens'
+ * - 'data'
+ * - 'options'
+ *
+ * @see hook_tokens()
+ */
+function hook_tokens_alter(array &$replacements, array $context) {
+ $options = $context['options'];
+
+ if (isset($options['language'])) {
+ $url_options['language'] = $options['language'];
+ $language_code = $options['language']->language;
+ }
+ else {
+ $language_code = NULL;
+ }
+ $sanitize = !empty($options['sanitize']);
+
+ if ($context['type'] == 'node' && !empty($context['data']['node'])) {
+ $node = $context['data']['node'];
+
+ // Alter the [node:title] token, and replace it with the rendered content
+ // of a field (field_title).
+ if (isset($context['tokens']['title'])) {
+ $title = field_view_field('node', $node, 'field_title', 'default', $language_code);
+ $replacements[$context['tokens']['title']] = drupal_render($title);
+ }
+ }
+}
+
+/**
+ * Provide information about available placeholder tokens and token types.
+ *
+ * Tokens are placeholders that can be put into text by using the syntax
+ * [type:token], where type is the machine-readable name of a token type, and
+ * token is the machine-readable name of a token within this group. This hook
+ * provides a list of types and tokens to be displayed on text editing screens,
+ * so that people editing text can see what their token options are.
+ *
+ * The actual token replacement is done by token_replace(), which invokes
+ * hook_tokens(). Your module will need to implement that hook in order to
+ * generate token replacements from the tokens defined here.
+ *
+ * @return
+ * An associative array of available tokens and token types. The outer array
+ * has two components:
+ * - types: An associative array of token types (groups). Each token type is
+ * an associative array with the following components:
+ * - name: The translated human-readable short name of the token type.
+ * - description: A translated longer description of the token type.
+ * - needs-data: The type of data that must be provided to token_replace()
+ * in the $data argument (i.e., the key name in $data) in order for tokens
+ * of this type to be used in the $text being processed. For instance, if
+ * the token needs a node object, 'needs-data' should be 'node', and to
+ * use this token in token_replace(), the caller needs to supply a node
+ * object as $data['node']. Some token data can also be supplied
+ * indirectly; for instance, a node object in $data supplies a user object
+ * (the author of the node), allowing user tokens to be used when only
+ * a node data object is supplied.
+ * - tokens: An associative array of tokens. The outer array is keyed by the
+ * group name (the same key as in the types array). Within each group of
+ * tokens, each token item is keyed by the machine name of the token, and
+ * each token item has the following components:
+ * - name: The translated human-readable short name of the token.
+ * - description: A translated longer description of the token.
+ * - type (optional): A 'needs-data' data type supplied by this token, which
+ * should match a 'needs-data' value from another token type. For example,
+ * the node author token provides a user object, which can then be used
+ * for token replacement data in token_replace() without having to supply
+ * a separate user object.
+ *
+ * @see hook_token_info_alter()
+ * @see hook_tokens()
+ */
+function hook_token_info() {
+ $type = array(
+ 'name' => t('Nodes'),
+ 'description' => t('Tokens related to individual nodes.'),
+ 'needs-data' => 'node',
+ );
+
+ // Core tokens for nodes.
+ $node['nid'] = array(
+ 'name' => t("Node ID"),
+ 'description' => t("The unique ID of the node."),
+ );
+ $node['title'] = array(
+ 'name' => t("Title"),
+ 'description' => t("The title of the node."),
+ );
+ $node['edit-url'] = array(
+ 'name' => t("Edit URL"),
+ 'description' => t("The URL of the node's edit page."),
+ );
+
+ // Chained tokens for nodes.
+ $node['created'] = array(
+ 'name' => t("Date created"),
+ 'description' => t("The date the node was posted."),
+ 'type' => 'date',
+ );
+ $node['author'] = array(
+ 'name' => t("Author"),
+ 'description' => t("The author of the node."),
+ 'type' => 'user',
+ );
+
+ return array(
+ 'types' => array('node' => $type),
+ 'tokens' => array('node' => $node),
+ );
+}
+
+/**
+ * Alter the metadata about available placeholder tokens and token types.
+ *
+ * @param $data
+ * The associative array of token definitions from hook_token_info().
+ *
+ * @see hook_token_info()
+ */
+function hook_token_info_alter(&$data) {
+ // Modify description of node tokens for our site.
+ $data['tokens']['node']['nid'] = array(
+ 'name' => t("Node ID"),
+ 'description' => t("The unique ID of the article."),
+ );
+ $data['tokens']['node']['title'] = array(
+ 'name' => t("Title"),
+ 'description' => t("The title of the article."),
+ );
+
+ // Chained tokens for nodes.
+ $data['tokens']['node']['created'] = array(
+ 'name' => t("Date created"),
+ 'description' => t("The date the article was posted."),
+ 'type' => 'date',
+ );
+}
+
+/**
+ * Alter batch information before a batch is processed.
+ *
+ * Called by batch_process() to allow modules to alter a batch before it is
+ * processed.
+ *
+ * @param $batch
+ * The associative array of batch information. See batch_set() for details on
+ * what this could contain.
+ *
+ * @see batch_set()
+ * @see batch_process()
+ *
+ * @ingroup batch
+ */
+function hook_batch_alter(&$batch) {
+ // If the current page request is inside the overlay, add ?render=overlay to
+ // the success callback URL, so that it appears correctly within the overlay.
+ if (overlay_get_mode() == 'child') {
+ if (isset($batch['url_options']['query'])) {
+ $batch['url_options']['query']['render'] = 'overlay';
+ }
+ else {
+ $batch['url_options']['query'] = array('render' => 'overlay');
+ }
+ }
+}
+
+/**
+ * Provide information on Updaters (classes that can update Drupal).
+ *
+ * An Updater is a class that knows how to update various parts of the Drupal
+ * file system, for example to update modules that have newer releases, or to
+ * install a new theme.
+ *
+ * @return
+ * An associative array of information about the updater(s) being provided.
+ * This array is keyed by a unique identifier for each updater, and the
+ * values are subarrays that can contain the following keys:
+ * - class: The name of the PHP class which implements this updater.
+ * - name: Human-readable name of this updater.
+ * - weight: Controls what order the Updater classes are consulted to decide
+ * which one should handle a given task. When an update task is being run,
+ * the system will loop through all the Updater classes defined in this
+ * registry in weight order and let each class respond to the task and
+ * decide if each Updater wants to handle the task. In general, this
+ * doesn't matter, but if you need to override an existing Updater, make
+ * sure your Updater has a lighter weight so that it comes first.
+ *
+ * @see drupal_get_updaters()
+ * @see hook_updater_info_alter()
+ */
+function hook_updater_info() {
+ return array(
+ 'module' => array(
+ 'class' => 'ModuleUpdater',
+ 'name' => t('Update modules'),
+ 'weight' => 0,
+ ),
+ 'theme' => array(
+ 'class' => 'ThemeUpdater',
+ 'name' => t('Update themes'),
+ 'weight' => 0,
+ ),
+ );
+}
+
+/**
+ * Alter the Updater information array.
+ *
+ * An Updater is a class that knows how to update various parts of the Drupal
+ * file system, for example to update modules that have newer releases, or to
+ * install a new theme.
+ *
+ * @param array $updaters
+ * Associative array of updaters as defined through hook_updater_info().
+ * Alter this array directly.
+ *
+ * @see drupal_get_updaters()
+ * @see hook_updater_info()
+ */
+function hook_updater_info_alter(&$updaters) {
+ // Adjust weight so that the theme Updater gets a chance to handle a given
+ // update task before module updaters.
+ $updaters['theme']['weight'] = -1;
+}
+
+/**
+ * Alter the default country list.
+ *
+ * @param $countries
+ * The associative array of countries keyed by ISO 3166-1 country code.
+ *
+ * @see country_get_list()
+ * @see _country_get_predefined_list()
+ */
+function hook_countries_alter(&$countries) {
+ // Elbonia is now independent, so add it to the country list.
+ $countries['EB'] = 'Elbonia';
+}
+
+/**
+ * Control site status before menu dispatching.
+ *
+ * The hook is called after checking whether the site is offline but before
+ * the current router item is retrieved and executed by
+ * menu_execute_active_handler(). If the site is in offline mode,
+ * $menu_site_status is set to MENU_SITE_OFFLINE.
+ *
+ * @param $menu_site_status
+ * Supported values are MENU_SITE_OFFLINE, MENU_ACCESS_DENIED,
+ * MENU_NOT_FOUND and MENU_SITE_ONLINE. Any other value than
+ * MENU_SITE_ONLINE will skip the default menu handling system and be passed
+ * for delivery to drupal_deliver_page() with a NULL
+ * $default_delivery_callback.
+ * @param $path
+ * Contains the system path that is going to be loaded. This is read only,
+ * use hook_url_inbound_alter() to change the path.
+ */
+function hook_menu_site_status_alter(&$menu_site_status, $path) {
+ // Allow access to my_module/authentication even if site is in offline mode.
+ if ($menu_site_status == MENU_SITE_OFFLINE && user_is_anonymous() && $path == 'my_module/authentication') {
+ $menu_site_status = MENU_SITE_ONLINE;
+ }
+}
+
+/**
+ * Register information about FileTransfer classes provided by a module.
+ *
+ * The FileTransfer class allows transferring files over a specific type of
+ * connection. Core provides classes for FTP and SSH. Contributed modules are
+ * free to extend the FileTransfer base class to add other connection types,
+ * and if these classes are registered via hook_filetransfer_info(), those
+ * connection types will be available to site administrators using the Update
+ * manager when they are redirected to the authorize.php script to authorize
+ * the file operations.
+ *
+ * @return array
+ * Nested array of information about FileTransfer classes. Each key is a
+ * FileTransfer type (not human readable, used for form elements and
+ * variable names, etc), and the values are subarrays that define properties
+ * of that type. The keys in each subarray are:
+ * - 'title': Required. The human-readable name of the connection type.
+ * - 'class': Required. The name of the FileTransfer class. The constructor
+ * will always be passed the full path to the root of the site that should
+ * be used to restrict where file transfer operations can occur (the $jail)
+ * and an array of settings values returned by the settings form.
+ * - 'file': Required. The include file containing the FileTransfer class.
+ * This should be a separate .inc file, not just the .module file, so that
+ * the minimum possible code is loaded when authorize.php is running.
+ * - 'file path': Optional. The directory (relative to the Drupal root)
+ * where the include file lives. If not defined, defaults to the base
+ * directory of the module implementing the hook.
+ * - 'weight': Optional. Integer weight used for sorting connection types on
+ * the authorize.php form.
+ *
+ * @see FileTransfer
+ * @see authorize.php
+ * @see hook_filetransfer_info_alter()
+ * @see drupal_get_filetransfer_info()
+ */
+function hook_filetransfer_info() {
+ $info['sftp'] = array(
+ 'title' => t('SFTP (Secure FTP)'),
+ 'file' => 'sftp.filetransfer.inc',
+ 'class' => 'FileTransferSFTP',
+ 'weight' => 10,
+ );
+ return $info;
+}
+
+/**
+ * Alter the FileTransfer class registry.
+ *
+ * @param array $filetransfer_info
+ * Reference to a nested array containing information about the FileTransfer
+ * class registry.
+ *
+ * @see hook_filetransfer_info()
+ */
+function hook_filetransfer_info_alter(&$filetransfer_info) {
+ if (variable_get('paranoia', FALSE)) {
+ // Remove the FTP option entirely.
+ unset($filetransfer_info['ftp']);
+ // Make sure the SSH option is listed first.
+ $filetransfer_info['ssh']['weight'] = -10;
+ }
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
+
+/**
+ * @addtogroup callbacks
+ * @{
+ */
+
+/**
+ * Return the URI for an entity.
+ *
+ * Callback for hook_entity_info().
+ *
+ * @param $entity
+ * The entity to return the URI for.
+ *
+ * @return
+ * An associative array with the following elements:
+ * - 'path': The URL path for the entity.
+ * - 'options': (optional) An array of options for the url() function.
+ * The actual entity URI can be constructed by passing these elements to
+ * url().
+ */
+function callback_entity_info_uri($entity) {
+ return array(
+ 'path' => 'node/' . $entity->nid,
+ );
+}
+
+/**
+ * Return the label of an entity.
+ *
+ * Callback for hook_entity_info().
+ *
+ * @param $entity
+ * The entity for which to generate the label.
+ * @param $entity_type
+ * The entity type; e.g., 'node' or 'user'.
+ *
+ * @return
+ * An unsanitized string with the label of the entity.
+ *
+ * @see entity_label()
+ */
+function callback_entity_info_label($entity, $entity_type) {
+ return empty($entity->title) ? 'Untitled entity' : $entity->title;
+}
+
+/**
+ * Return the language code of the entity.
+ *
+ * Callback for hook_entity_info().
+ *
+ * The language callback is meant to be used primarily for temporary alterations
+ * of the property value.
+ *
+ * @param $entity
+ * The entity for which to return the language.
+ * @param $entity_type
+ * The entity type; e.g., 'node' or 'user'.
+ *
+ * @return
+ * The language code for the language of the entity.
+ *
+ * @see entity_language()
+ */
+function callback_entity_info_language($entity, $entity_type) {
+ return $entity->language;
+}
+
+/**
+ * @} End of "addtogroup callbacks".
+ */
+
+/**
+ * @defgroup update_api Update versions of API functions
+ * @{
+ * Functions that are similar to normal API functions, but do not invoke hooks.
+ *
+ * These simplified versions of core API functions are provided for use by
+ * update functions (hook_update_N() implementations).
+ *
+ * During database updates the schema of any module could be out of date. For
+ * this reason, caution is needed when using any API function within an update
+ * function - particularly CRUD functions, functions that depend on the schema
+ * (for example by using drupal_write_record()), and any functions that invoke
+ * hooks.
+ *
+ * Instead, a simplified utility function should be used. If a utility version
+ * of the API function you require does not already exist, then you should
+ * create a new function. The new utility function should be named
+ * _update_N_mymodule_my_function(). N is the schema version the function acts
+ * on (the schema version is the number N from the hook_update_N()
+ * implementation where this schema was introduced, or a number following the
+ * same numbering scheme), and mymodule_my_function is the name of the original
+ * API function including the module's name.
+ *
+ * Examples:
+ * - _update_6000_mymodule_save(): This function performs a save operation
+ * without invoking any hooks using the 6.x schema.
+ * - _update_7000_mymodule_save(): This function performs the same save
+ * operation using the 7.x schema.
+ *
+ * The utility function should not invoke any hooks, and should perform database
+ * operations using functions from the
+ * @link database Database abstraction layer, @endlink
+ * like db_insert(), db_update(), db_delete(), db_query(), and so on.
+ *
+ * If a change to the schema necessitates a change to the utility function, a
+ * new function should be created with a name based on the version of the schema
+ * it acts on. See _update_7000_bar_get_types() and _update_7001_bar_get_types()
+ * in the code examples that follow.
+ *
+ * For example, foo.install could contain:
+ * @code
+ * function foo_update_dependencies() {
+ * // foo_update_7010() needs to run after bar_update_7000().
+ * $dependencies['foo'][7010] = array(
+ * 'bar' => 7000,
+ * );
+ *
+ * // foo_update_7036() needs to run after bar_update_7001().
+ * $dependencies['foo'][7036] = array(
+ * 'bar' => 7001,
+ * );
+ *
+ * return $dependencies;
+ * }
+ *
+ * function foo_update_7000() {
+ * // No updates have been run on the {bar_types} table yet, so this needs
+ * // to work with the 6.x schema.
+ * foreach (_update_6000_bar_get_types() as $type) {
+ * // Rename a variable.
+ * }
+ * }
+ *
+ * function foo_update_7010() {
+ * // Since foo_update_7010() is going to run after bar_update_7000(), it
+ * // needs to operate on the new schema, not the old one.
+ * foreach (_update_7000_bar_get_types() as $type) {
+ * // Rename a different variable.
+ * }
+ * }
+ *
+ * function foo_update_7036() {
+ * // This update will run after bar_update_7001().
+ * foreach (_update_7001_bar_get_types() as $type) {
+ * }
+ * }
+ * @endcode
+ *
+ * And bar.install could contain:
+ * @code
+ * function bar_update_7000() {
+ * // Type and bundle are confusing, so we renamed the table.
+ * db_rename_table('bar_types', 'bar_bundles');
+ * }
+ *
+ * function bar_update_7001() {
+ * // Database table names should be singular when possible.
+ * db_rename_table('bar_bundles', 'bar_bundle');
+ * }
+ *
+ * function _update_6000_bar_get_types() {
+ * db_query('SELECT * FROM {bar_types}')->fetchAll();
+ * }
+ *
+ * function _update_7000_bar_get_types() {
+ * db_query('SELECT * FROM {bar_bundles'})->fetchAll();
+ * }
+ *
+ * function _update_7001_bar_get_types() {
+ * db_query('SELECT * FROM {bar_bundle}')->fetchAll();
+ * }
+ * @endcode
+ *
+ * @see hook_update_N()
+ * @see hook_update_dependencies()
+ */
+
+/**
+ * @} End of "defgroup update_api".
+ */
diff --git a/kolab.org/www/drupal-7.26/modules/system/system.archiver.inc b/kolab.org/www/drupal-7.26/modules/system/system.archiver.inc
new file mode 100644
index 0000000..c37f07d
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/system.archiver.inc
@@ -0,0 +1,139 @@
+<?php
+
+/**
+ * @file
+ * Archiver implementations provided by the system module.
+ */
+
+/**
+ * Archiver for .tar files.
+ */
+class ArchiverTar implements ArchiverInterface {
+
+ /**
+ * The underlying Archive_Tar instance that does the heavy lifting.
+ *
+ * @var Archive_Tar
+ */
+ protected $tar;
+
+ public function __construct($file_path) {
+ $this->tar = new Archive_Tar($file_path);
+ }
+
+ public function add($file_path) {
+ $this->tar->add($file_path);
+
+ return $this;
+ }
+
+ public function remove($file_path) {
+ // @todo Archive_Tar doesn't have a remove operation
+ // so we'll have to simulate it somehow, probably by
+ // creating a new archive with everything but the removed
+ // file.
+
+ return $this;
+ }
+
+ public function extract($path, Array $files = array()) {
+ if ($files) {
+ $this->tar->extractList($files, $path);
+ }
+ else {
+ $this->tar->extract($path);
+ }
+
+ return $this;
+ }
+
+ public function listContents() {
+ $files = array();
+ foreach ($this->tar->listContent() as $file_data) {
+ $files[] = $file_data['filename'];
+ }
+ return $files;
+ }
+
+ /**
+ * Retrieve the tar engine itself.
+ *
+ * In some cases it may be necessary to directly access the underlying
+ * Archive_Tar object for implementation-specific logic. This is for advanced
+ * use only as it is not shared by other implementations of ArchiveInterface.
+ *
+ * @return
+ * The Archive_Tar object used by this object.
+ */
+ public function getArchive() {
+ return $this->tar;
+ }
+}
+
+/**
+ * Archiver for .zip files.
+ *
+ * @link http://php.net/zip
+ */
+class ArchiverZip implements ArchiverInterface {
+
+ /**
+ * The underlying ZipArchive instance that does the heavy lifting.
+ *
+ * @var ZipArchive
+ */
+ protected $zip;
+
+ public function __construct($file_path) {
+ $this->zip = new ZipArchive();
+ if ($this->zip->open($file_path) !== TRUE) {
+ // @todo: This should be an interface-specific exception some day.
+ throw new Exception(t('Cannot open %file_path', array('%file_path' => $file_path)));
+ }
+ }
+
+ public function add($file_path) {
+ $this->zip->addFile($file_path);
+
+ return $this;
+ }
+
+ public function remove($file_path) {
+ $this->zip->deleteName($file_path);
+
+ return $this;
+ }
+
+ public function extract($path, Array $files = array()) {
+ if ($files) {
+ $this->zip->extractTo($path, $files);
+ }
+ else {
+ $this->zip->extractTo($path);
+ }
+
+ return $this;
+ }
+
+ public function listContents() {
+ $files = array();
+ for ($i=0; $i < $this->zip->numFiles; $i++) {
+ $files[] = $this->zip->getNameIndex($i);
+ }
+ return $files;
+ }
+
+ /**
+ * Retrieve the zip engine itself.
+ *
+ * In some cases it may be necessary to directly access the underlying
+ * ZipArchive object for implementation-specific logic. This is for advanced
+ * use only as it is not shared by other implementations of ArchiveInterface.
+ *
+ * @return
+ * The ZipArchive object used by this object.
+ */
+ public function getArchive() {
+ return $this->zip;
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/system/system.base-rtl.css b/kolab.org/www/drupal-7.26/modules/system/system.base-rtl.css
new file mode 100644
index 0000000..075cafe
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/system.base-rtl.css
@@ -0,0 +1,58 @@
+
+/**
+ * @file
+ * Generic theme-independent base styles.
+ */
+
+/**
+ * Autocomplete.
+ */
+/* Animated throbber */
+html.js input.form-autocomplete {
+ background-position: 0% 2px;
+}
+html.js input.throbbing {
+ background-position: 0% -18px;
+}
+
+/**
+ * Progress bar.
+ */
+.progress .percentage {
+ float: left;
+}
+.progress-disabled {
+ float: right;
+}
+.ajax-progress {
+ float: right;
+}
+.ajax-progress .throbber {
+ float: right;
+}
+
+/**
+ * TableDrag behavior.
+ */
+.draggable a.tabledrag-handle {
+ float: right;
+ margin-right: -1em;
+ margin-left: 0;
+}
+a.tabledrag-handle .handle {
+ margin: -0.4em 0.5em;
+ padding: 0.42em 0.5em;
+}
+div.indentation {
+ float: right;
+ margin: -0.4em -0.4em -0.4em 0.2em;
+ padding: 0.42em 0.6em 0.42em 0;
+}
+div.tree-child,
+div.tree-child-last {
+ background-position: -65px center;
+}
+.tabledrag-toggle-weight-wrapper {
+ text-align: left;
+}
+
diff --git a/kolab.org/www/drupal-7.26/modules/system/system.base.css b/kolab.org/www/drupal-7.26/modules/system/system.base.css
new file mode 100644
index 0000000..412b18a
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/system.base.css
@@ -0,0 +1,270 @@
+
+/**
+ * @file
+ * Generic theme-independent base styles.
+ */
+
+/**
+ * Autocomplete.
+ *
+ * @see autocomplete.js
+ */
+/* Suggestion list */
+#autocomplete {
+ border: 1px solid;
+ overflow: hidden;
+ position: absolute;
+ z-index: 100;
+}
+#autocomplete ul {
+ list-style: none;
+ list-style-image: none;
+ margin: 0;
+ padding: 0;
+}
+#autocomplete li {
+ background: #fff;
+ color: #000;
+ cursor: default;
+ white-space: pre;
+ zoom: 1; /* IE7 */
+}
+/* Animated throbber */
+html.js input.form-autocomplete {
+ background-image: url(../../misc/throbber.gif);
+ background-position: 100% 2px; /* LTR */
+ background-repeat: no-repeat;
+}
+html.js input.throbbing {
+ background-position: 100% -18px; /* LTR */
+}
+
+/**
+ * Collapsible fieldsets.
+ *
+ * @see collapse.js
+ */
+html.js fieldset.collapsed {
+ border-bottom-width: 0;
+ border-left-width: 0;
+ border-right-width: 0;
+ height: 1em;
+}
+html.js fieldset.collapsed .fieldset-wrapper {
+ display: none;
+}
+fieldset.collapsible {
+ position: relative;
+}
+fieldset.collapsible .fieldset-legend {
+ display: block;
+}
+
+/**
+ * Resizable textareas.
+ *
+ * @see textarea.js
+ */
+.form-textarea-wrapper textarea {
+ display: block;
+ margin: 0;
+ width: 100%;
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.resizable-textarea .grippie {
+ background: #eee url(../../misc/grippie.png) no-repeat center 2px;
+ border: 1px solid #ddd;
+ border-top-width: 0;
+ cursor: s-resize;
+ height: 9px;
+ overflow: hidden;
+}
+
+/**
+ * TableDrag behavior.
+ *
+ * @see tabledrag.js
+ */
+body.drag {
+ cursor: move;
+}
+.draggable a.tabledrag-handle {
+ cursor: move;
+ float: left; /* LTR */
+ height: 1.7em;
+ margin-left: -1em; /* LTR */
+ overflow: hidden;
+ text-decoration: none;
+}
+a.tabledrag-handle:hover {
+ text-decoration: none;
+}
+a.tabledrag-handle .handle {
+ background: url(../../misc/draggable.png) no-repeat 6px 9px;
+ height: 13px;
+ margin: -0.4em 0.5em; /* LTR */
+ padding: 0.42em 0.5em; /* LTR */
+ width: 13px;
+}
+a.tabledrag-handle-hover .handle {
+ background-position: 6px -11px;
+}
+div.indentation {
+ float: left; /* LTR */
+ height: 1.7em;
+ margin: -0.4em 0.2em -0.4em -0.4em; /* LTR */
+ padding: 0.42em 0 0.42em 0.6em; /* LTR */
+ width: 20px;
+}
+div.tree-child {
+ background: url(../../misc/tree.png) no-repeat 11px center; /* LTR */
+}
+div.tree-child-last {
+ background: url(../../misc/tree-bottom.png) no-repeat 11px center; /* LTR */
+}
+div.tree-child-horizontal {
+ background: url(../../misc/tree.png) no-repeat -11px center;
+}
+.tabledrag-toggle-weight-wrapper {
+ text-align: right; /* LTR */
+}
+
+/**
+ * TableHeader behavior.
+ *
+ * @see tableheader.js
+ */
+table.sticky-header {
+ background-color: #fff;
+ margin-top: 0;
+}
+
+/**
+ * Progress behavior.
+ *
+ * @see progress.js
+ */
+/* Bar */
+.progress .bar {
+ background-color: #fff;
+ border: 1px solid;
+}
+.progress .filled {
+ background-color: #000;
+ height: 1.5em;
+ width: 5px;
+}
+.progress .percentage {
+ float: right; /* LTR */
+}
+/* Throbber */
+.ajax-progress {
+ display: inline-block;
+}
+.ajax-progress .throbber {
+ background: transparent url(../../misc/throbber.gif) no-repeat 0px -18px;
+ float: left; /* LTR */
+ height: 15px;
+ margin: 2px;
+ width: 15px;
+}
+.ajax-progress .message {
+ padding-left: 20px;
+}
+tr .ajax-progress .throbber {
+ margin: 0 2px;
+}
+.ajax-progress-bar {
+ width: 16em;
+}
+
+/**
+ * Inline items.
+ */
+.container-inline div,
+.container-inline label {
+ display: inline;
+}
+/* Fieldset contents always need to be rendered as block. */
+.container-inline .fieldset-wrapper {
+ display: block;
+}
+
+/**
+ * Prevent text wrapping.
+ */
+.nowrap {
+ white-space: nowrap;
+}
+
+/**
+ * For anything you want to hide on page load when JS is enabled, so
+ * that you can use the JS to control visibility and avoid flicker.
+ */
+html.js .js-hide {
+ display: none;
+}
+
+/**
+ * Hide elements from all users.
+ *
+ * Used for elements which should not be immediately displayed to any user. An
+ * example would be a collapsible fieldset that will be expanded with a click
+ * from a user. The effect of this class can be toggled with the jQuery show()
+ * and hide() functions.
+ */
+.element-hidden {
+ display: none;
+}
+
+/**
+ * Hide elements visually, but keep them available for screen-readers.
+ *
+ * Used for information required for screen-reader users to understand and use
+ * the site where visual display is undesirable. Information provided in this
+ * manner should be kept concise, to avoid unnecessary burden on the user.
+ * "!important" is used to prevent unintentional overrides.
+ */
+.element-invisible {
+ position: absolute !important;
+ clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
+ clip: rect(1px, 1px, 1px, 1px);
+ overflow: hidden;
+ height: 1px;
+}
+
+/**
+ * The .element-focusable class extends the .element-invisible class to allow
+ * the element to be focusable when navigated to via the keyboard.
+ */
+.element-invisible.element-focusable:active,
+.element-invisible.element-focusable:focus {
+ position: static !important;
+ clip: auto;
+ overflow: visible;
+ height: auto;
+}
+
+/**
+ * Markup free clearing.
+ *
+ * @see http://perishablepress.com/press/2009/12/06/new-clearfix-hack
+ */
+.clearfix:after {
+ content: ".";
+ display: block;
+ height: 0;
+ clear: both;
+ visibility: hidden;
+}
+/* IE6 */
+* html .clearfix {
+ height: 1%;
+}
+/* IE7 */
+*:first-child + html .clearfix {
+ min-height: 1%;
+}
+
diff --git a/kolab.org/www/drupal-7.26/modules/system/system.cron.js b/kolab.org/www/drupal-7.26/modules/system/system.cron.js
new file mode 100644
index 0000000..af17dab
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/system.cron.js
@@ -0,0 +1,19 @@
+(function ($) {
+
+/**
+ * Checks to see if the cron should be automatically run.
+ */
+Drupal.behaviors.cronCheck = {
+ attach: function(context, settings) {
+ if (settings.cronCheck || false) {
+ $('body').once('cron-check', function() {
+ // Only execute the cron check if its the right time.
+ if (Math.round(new Date().getTime() / 1000.0) > settings.cronCheck) {
+ $.get(settings.basePath + 'system/run-cron-check');
+ }
+ });
+ }
+ }
+};
+
+})(jQuery);
diff --git a/kolab.org/www/drupal-7.26/modules/system/system.info b/kolab.org/www/drupal-7.26/modules/system/system.info
new file mode 100644
index 0000000..667581a
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/system.info
@@ -0,0 +1,19 @@
+name = System
+description = Handles general site configuration for administrators.
+package = Core
+version = VERSION
+core = 7.x
+files[] = system.archiver.inc
+files[] = system.mail.inc
+files[] = system.queue.inc
+files[] = system.tar.inc
+files[] = system.updater.inc
+files[] = system.test
+required = TRUE
+configure = admin/config/system
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/system/system.install b/kolab.org/www/drupal-7.26/modules/system/system.install
new file mode 100644
index 0000000..43c7383
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/system.install
@@ -0,0 +1,3156 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the system module.
+ */
+
+/**
+ * Implements hook_requirements().
+ */
+function system_requirements($phase) {
+ global $base_url;
+ $requirements = array();
+ // Ensure translations don't break during installation.
+ $t = get_t();
+
+ // Report Drupal version
+ if ($phase == 'runtime') {
+ $requirements['drupal'] = array(
+ 'title' => $t('Drupal'),
+ 'value' => VERSION,
+ 'severity' => REQUIREMENT_INFO,
+ 'weight' => -10,
+ );
+
+ // Display the currently active installation profile, if the site
+ // is not running the default installation profile.
+ $profile = drupal_get_profile();
+ if ($profile != 'standard') {
+ $info = system_get_info('module', $profile);
+ $requirements['install_profile'] = array(
+ 'title' => $t('Install profile'),
+ 'value' => $t('%profile_name (%profile-%version)', array(
+ '%profile_name' => $info['name'],
+ '%profile' => $profile,
+ '%version' => $info['version']
+ )),
+ 'severity' => REQUIREMENT_INFO,
+ 'weight' => -9
+ );
+ }
+ }
+
+ // Web server information.
+ $software = $_SERVER['SERVER_SOFTWARE'];
+ $requirements['webserver'] = array(
+ 'title' => $t('Web server'),
+ 'value' => $software,
+ );
+
+ // Test PHP version and show link to phpinfo() if it's available
+ $phpversion = phpversion();
+ if (function_exists('phpinfo')) {
+ $requirements['php'] = array(
+ 'title' => $t('PHP'),
+ 'value' => ($phase == 'runtime') ? $phpversion .' ('. l($t('more information'), 'admin/reports/status/php') .')' : $phpversion,
+ );
+ }
+ else {
+ $requirements['php'] = array(
+ 'title' => $t('PHP'),
+ 'value' => $phpversion,
+ 'description' => $t('The phpinfo() function has been disabled for security reasons. To see your server\'s phpinfo() information, change your PHP settings or contact your server administrator. For more information, <a href="@phpinfo">Enabling and disabling phpinfo()</a> handbook page.', array('@phpinfo' => 'http://drupal.org/node/243993')),
+ 'severity' => REQUIREMENT_INFO,
+ );
+ }
+
+ if (version_compare($phpversion, DRUPAL_MINIMUM_PHP) < 0) {
+ $requirements['php']['description'] = $t('Your PHP installation is too old. Drupal requires at least PHP %version.', array('%version' => DRUPAL_MINIMUM_PHP));
+ $requirements['php']['severity'] = REQUIREMENT_ERROR;
+ // If PHP is old, it's not safe to continue with the requirements check.
+ return $requirements;
+ }
+ // Check that htmlspecialchars() is secure if the site is running any PHP
+ // version older than 5.2.5. We don't simply require 5.2.5, because Ubuntu
+ // 8.04 ships with PHP 5.2.4, but includes the necessary security patch.
+ elseif (version_compare($phpversion, '5.2.5') < 0 && strlen(@htmlspecialchars(chr(0xC0) . chr(0xAF), ENT_QUOTES, 'UTF-8'))) {
+ $requirements['php']['description'] = $t('Your PHP installation is too old. Drupal requires at least PHP 5.2.5, or PHP @version with the htmlspecialchars security patch backported.', array('@version' => DRUPAL_MINIMUM_PHP));
+ $requirements['php']['severity'] = REQUIREMENT_ERROR;
+ // If PHP is old, it's not safe to continue with the requirements check.
+ return $requirements;
+ }
+
+ // Test PHP register_globals setting.
+ $requirements['php_register_globals'] = array(
+ 'title' => $t('PHP register globals'),
+ );
+ $register_globals = trim(ini_get('register_globals'));
+ // Unfortunately, ini_get() may return many different values, and we can't
+ // be certain which values mean 'on', so we instead check for 'not off'
+ // since we never want to tell the user that their site is secure
+ // (register_globals off), when it is in fact on. We can only guarantee
+ // register_globals is off if the value returned is 'off', '', or 0.
+ if (!empty($register_globals) && strtolower($register_globals) != 'off') {
+ $requirements['php_register_globals']['description'] = $t('<em>register_globals</em> is enabled. Drupal requires this configuration directive to be disabled. Your site may not be secure when <em>register_globals</em> is enabled. The PHP manual has instructions for <a href="http://php.net/configuration.changes">how to change configuration settings</a>.');
+ $requirements['php_register_globals']['severity'] = REQUIREMENT_ERROR;
+ $requirements['php_register_globals']['value'] = $t("Enabled ('@value')", array('@value' => $register_globals));
+ }
+ else {
+ $requirements['php_register_globals']['value'] = $t('Disabled');
+ }
+
+ // Test for PHP extensions.
+ $requirements['php_extensions'] = array(
+ 'title' => $t('PHP extensions'),
+ );
+
+ $missing_extensions = array();
+ $required_extensions = array(
+ 'date',
+ 'dom',
+ 'filter',
+ 'gd',
+ 'hash',
+ 'json',
+ 'pcre',
+ 'pdo',
+ 'session',
+ 'SimpleXML',
+ 'SPL',
+ 'xml',
+ );
+ foreach ($required_extensions as $extension) {
+ if (!extension_loaded($extension)) {
+ $missing_extensions[] = $extension;
+ }
+ }
+
+ if (!empty($missing_extensions)) {
+ $description = $t('Drupal requires you to enable the PHP extensions in the following list (see the <a href="@system_requirements">system requirements page</a> for more information):', array(
+ '@system_requirements' => 'http://drupal.org/requirements',
+ ));
+
+ $description .= theme('item_list', array('items' => $missing_extensions));
+
+ $requirements['php_extensions']['value'] = $t('Disabled');
+ $requirements['php_extensions']['severity'] = REQUIREMENT_ERROR;
+ $requirements['php_extensions']['description'] = $description;
+ }
+ else {
+ $requirements['php_extensions']['value'] = $t('Enabled');
+ }
+
+ if ($phase == 'install' || $phase == 'update') {
+ // Test for PDO (database).
+ $requirements['database_extensions'] = array(
+ 'title' => $t('Database support'),
+ );
+
+ // Make sure PDO is available.
+ $database_ok = extension_loaded('pdo');
+ if (!$database_ok) {
+ $pdo_message = $t('Your web server does not appear to support PDO (PHP Data Objects). Ask your hosting provider if they support the native PDO extension. See the <a href="@link">system requirements</a> page for more information.', array(
+ '@link' => 'http://drupal.org/requirements/pdo',
+ ));
+ }
+ else {
+ // Make sure at least one supported database driver exists.
+ $drivers = drupal_detect_database_types();
+ if (empty($drivers)) {
+ $database_ok = FALSE;
+ $pdo_message = $t('Your web server does not appear to support any common PDO database extensions. Check with your hosting provider to see if they support PDO (PHP Data Objects) and offer any databases that <a href="@drupal-databases">Drupal supports</a>.', array(
+ '@drupal-databases' => 'http://drupal.org/node/270#database',
+ ));
+ }
+ // Make sure the native PDO extension is available, not the older PEAR
+ // version. (See install_verify_pdo() for details.)
+ if (!defined('PDO::ATTR_DEFAULT_FETCH_MODE')) {
+ $database_ok = FALSE;
+ $pdo_message = $t('Your web server seems to have the wrong version of PDO installed. Drupal 7 requires the PDO extension from PHP core. This system has the older PECL version. See the <a href="@link">system requirements</a> page for more information.', array(
+ '@link' => 'http://drupal.org/requirements/pdo#pecl',
+ ));
+ }
+ }
+
+ if (!$database_ok) {
+ $requirements['database_extensions']['value'] = $t('Disabled');
+ $requirements['database_extensions']['severity'] = REQUIREMENT_ERROR;
+ $requirements['database_extensions']['description'] = $pdo_message;
+ }
+ else {
+ $requirements['database_extensions']['value'] = $t('Enabled');
+ }
+ }
+ else {
+ // Database information.
+ $class = 'DatabaseTasks_' . Database::getConnection()->driver();
+ $tasks = new $class();
+ $requirements['database_system'] = array(
+ 'title' => $t('Database system'),
+ 'value' => $tasks->name(),
+ );
+ $requirements['database_system_version'] = array(
+ 'title' => $t('Database system version'),
+ 'value' => Database::getConnection()->version(),
+ );
+ }
+
+ // Test PHP memory_limit
+ $memory_limit = ini_get('memory_limit');
+ $requirements['php_memory_limit'] = array(
+ 'title' => $t('PHP memory limit'),
+ 'value' => $memory_limit == -1 ? t('-1 (Unlimited)') : $memory_limit,
+ );
+
+ if (!drupal_check_memory_limit(DRUPAL_MINIMUM_PHP_MEMORY_LIMIT, $memory_limit)) {
+ $description = '';
+ if ($phase == 'install') {
+ $description = $t('Consider increasing your PHP memory limit to %memory_minimum_limit to help prevent errors in the installation process.', array('%memory_minimum_limit' => DRUPAL_MINIMUM_PHP_MEMORY_LIMIT));
+ }
+ elseif ($phase == 'update') {
+ $description = $t('Consider increasing your PHP memory limit to %memory_minimum_limit to help prevent errors in the update process.', array('%memory_minimum_limit' => DRUPAL_MINIMUM_PHP_MEMORY_LIMIT));
+ }
+ elseif ($phase == 'runtime') {
+ $description = $t('Depending on your configuration, Drupal can run with a %memory_limit PHP memory limit. However, a %memory_minimum_limit PHP memory limit or above is recommended, especially if your site uses additional custom or contributed modules.', array('%memory_limit' => $memory_limit, '%memory_minimum_limit' => DRUPAL_MINIMUM_PHP_MEMORY_LIMIT));
+ }
+
+ if (!empty($description)) {
+ if ($php_ini_path = get_cfg_var('cfg_file_path')) {
+ $description .= ' ' . $t('Increase the memory limit by editing the memory_limit parameter in the file %configuration-file and then restart your web server (or contact your system administrator or hosting provider for assistance).', array('%configuration-file' => $php_ini_path));
+ }
+ else {
+ $description .= ' ' . $t('Contact your system administrator or hosting provider for assistance with increasing your PHP memory limit.');
+ }
+
+ $requirements['php_memory_limit']['description'] = $description . ' ' . $t('See the <a href="@url">Drupal requirements</a> for more information.', array('@url' => 'http://drupal.org/requirements'));
+ $requirements['php_memory_limit']['severity'] = REQUIREMENT_WARNING;
+ }
+ }
+
+ // Test settings.php file writability
+ if ($phase == 'runtime') {
+ $conf_dir = drupal_verify_install_file(conf_path(), FILE_NOT_WRITABLE, 'dir');
+ $conf_file = drupal_verify_install_file(conf_path() . '/settings.php', FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE);
+ if (!$conf_dir || !$conf_file) {
+ $requirements['settings.php'] = array(
+ 'value' => $t('Not protected'),
+ 'severity' => REQUIREMENT_ERROR,
+ 'description' => '',
+ );
+ if (!$conf_dir) {
+ $requirements['settings.php']['description'] .= $t('The directory %file is not protected from modifications and poses a security risk. You must change the directory\'s permissions to be non-writable. ', array('%file' => conf_path()));
+ }
+ if (!$conf_file) {
+ $requirements['settings.php']['description'] .= $t('The file %file is not protected from modifications and poses a security risk. You must change the file\'s permissions to be non-writable.', array('%file' => conf_path() . '/settings.php'));
+ }
+ }
+ else {
+ $requirements['settings.php'] = array(
+ 'value' => $t('Protected'),
+ );
+ }
+ $requirements['settings.php']['title'] = $t('Configuration file');
+ }
+
+ // Test the contents of the .htaccess files.
+ if ($phase == 'runtime') {
+ // Try to write the .htaccess files first, to prevent false alarms in case
+ // (for example) the /tmp directory was wiped.
+ file_ensure_htaccess();
+ $htaccess_files['public://.htaccess'] = array(
+ 'title' => $t('Public files directory'),
+ 'directory' => variable_get('file_public_path', conf_path() . '/files'),
+ );
+ if ($private_files_directory = variable_get('file_private_path')) {
+ $htaccess_files['private://.htaccess'] = array(
+ 'title' => $t('Private files directory'),
+ 'directory' => $private_files_directory,
+ );
+ }
+ $htaccess_files['temporary://.htaccess'] = array(
+ 'title' => $t('Temporary files directory'),
+ 'directory' => variable_get('file_temporary_path', file_directory_temp()),
+ );
+ foreach ($htaccess_files as $htaccess_file => $info) {
+ // Check for the string which was added to the recommended .htaccess file
+ // in the latest security update.
+ if (!file_exists($htaccess_file) || !($contents = @file_get_contents($htaccess_file)) || strpos($contents, 'Drupal_Security_Do_Not_Remove_See_SA_2013_003') === FALSE) {
+ $requirements[$htaccess_file] = array(
+ 'title' => $info['title'],
+ 'value' => $t('Not fully protected'),
+ 'severity' => REQUIREMENT_ERROR,
+ 'description' => $t('See <a href="@url">@url</a> for information about the recommended .htaccess file which should be added to the %directory directory to help protect against arbitrary code execution.', array('@url' => 'http://drupal.org/SA-CORE-2013-003', '%directory' => $info['directory'])),
+ );
+ }
+ }
+ }
+
+ // Report cron status.
+ if ($phase == 'runtime') {
+ // Cron warning threshold defaults to two days.
+ $threshold_warning = variable_get('cron_threshold_warning', 172800);
+ // Cron error threshold defaults to two weeks.
+ $threshold_error = variable_get('cron_threshold_error', 1209600);
+ // Cron configuration help text.
+ $help = $t('For more information, see the online handbook entry for <a href="@cron-handbook">configuring cron jobs</a>.', array('@cron-handbook' => 'http://drupal.org/cron'));
+
+ // Determine when cron last ran.
+ $cron_last = variable_get('cron_last');
+ if (!is_numeric($cron_last)) {
+ $cron_last = variable_get('install_time', 0);
+ }
+
+ // Determine severity based on time since cron last ran.
+ $severity = REQUIREMENT_OK;
+ if (REQUEST_TIME - $cron_last > $threshold_error) {
+ $severity = REQUIREMENT_ERROR;
+ }
+ elseif (REQUEST_TIME - $cron_last > $threshold_warning) {
+ $severity = REQUIREMENT_WARNING;
+ }
+
+ // Set summary and description based on values determined above.
+ $summary = $t('Last run !time ago', array('!time' => format_interval(REQUEST_TIME - $cron_last)));
+ $description = '';
+ if ($severity != REQUIREMENT_OK) {
+ $description = $t('Cron has not run recently.') . ' ' . $help;
+ }
+
+ $description .= ' ' . $t('You can <a href="@cron">run cron manually</a>.', array('@cron' => url('admin/reports/status/run-cron')));
+ $description .= '<br />' . $t('To run cron from outside the site, go to <a href="!cron">!cron</a>', array('!cron' => url($base_url . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => variable_get('cron_key', 'drupal'))))));
+
+ $requirements['cron'] = array(
+ 'title' => $t('Cron maintenance tasks'),
+ 'severity' => $severity,
+ 'value' => $summary,
+ 'description' => $description
+ );
+ }
+
+ // Test files directories.
+ $directories = array(
+ variable_get('file_public_path', conf_path() . '/files'),
+ // By default no private files directory is configured. For private files
+ // to be secure the admin needs to provide a path outside the webroot.
+ variable_get('file_private_path', FALSE),
+ );
+
+ // Do not check for the temporary files directory during installation
+ // unless it has been set in settings.php. In this case the user has
+ // no alternative but to fix the directory if it is not writable.
+ if ($phase == 'install') {
+ $directories[] = variable_get('file_temporary_path', FALSE);
+ }
+ else {
+ $directories[] = variable_get('file_temporary_path', file_directory_temp());
+ }
+
+ $requirements['file system'] = array(
+ 'title' => $t('File system'),
+ );
+
+ $error = '';
+ // For installer, create the directories if possible.
+ foreach ($directories as $directory) {
+ if (!$directory) {
+ continue;
+ }
+ if ($phase == 'install') {
+ file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
+ }
+ $is_writable = is_writable($directory);
+ $is_directory = is_dir($directory);
+ if (!$is_writable || !$is_directory) {
+ $description = '';
+ $requirements['file system']['value'] = $t('Not writable');
+ if (!$is_directory) {
+ $error .= $t('The directory %directory does not exist.', array('%directory' => $directory)) . ' ';
+ }
+ else {
+ $error .= $t('The directory %directory is not writable.', array('%directory' => $directory)) . ' ';
+ }
+ // The files directory requirement check is done only during install and runtime.
+ if ($phase == 'runtime') {
+ $description = $error . $t('You may need to set the correct directory at the <a href="@admin-file-system">file system settings page</a> or change the current directory\'s permissions so that it is writable.', array('@admin-file-system' => url('admin/config/media/file-system')));
+ }
+ elseif ($phase == 'install') {
+ // For the installer UI, we need different wording. 'value' will
+ // be treated as version, so provide none there.
+ $description = $error . $t('An automated attempt to create this directory failed, possibly due to a permissions problem. To proceed with the installation, either create the directory and modify its permissions manually or ensure that the installer has the permissions to create it automatically. For more information, see INSTALL.txt or the <a href="@handbook_url">online handbook</a>.', array('@handbook_url' => 'http://drupal.org/server-permissions'));
+ $requirements['file system']['value'] = '';
+ }
+ if (!empty($description)) {
+ $requirements['file system']['description'] = $description;
+ $requirements['file system']['severity'] = REQUIREMENT_ERROR;
+ }
+ }
+ else {
+ if (file_default_scheme() == 'public') {
+ $requirements['file system']['value'] = $t('Writable (<em>public</em> download method)');
+ }
+ else {
+ $requirements['file system']['value'] = $t('Writable (<em>private</em> download method)');
+ }
+ }
+ }
+
+ // See if updates are available in update.php.
+ if ($phase == 'runtime') {
+ $requirements['update'] = array(
+ 'title' => $t('Database updates'),
+ 'severity' => REQUIREMENT_OK,
+ 'value' => $t('Up to date'),
+ );
+
+ // Check installed modules.
+ foreach (module_list() as $module) {
+ $updates = drupal_get_schema_versions($module);
+ if ($updates !== FALSE) {
+ $default = drupal_get_installed_schema_version($module);
+ if (max($updates) > $default) {
+ $requirements['update']['severity'] = REQUIREMENT_ERROR;
+ $requirements['update']['value'] = $t('Out of date');
+ $requirements['update']['description'] = $t('Some modules have database schema updates to install. You should run the <a href="@update">database update script</a> immediately.', array('@update' => base_path() . 'update.php'));
+ break;
+ }
+ }
+ }
+ }
+
+ // Verify the update.php access setting
+ if ($phase == 'runtime') {
+ if (!empty($GLOBALS['update_free_access'])) {
+ $requirements['update access'] = array(
+ 'value' => $t('Not protected'),
+ 'severity' => REQUIREMENT_ERROR,
+ 'description' => $t('The update.php script is accessible to everyone without authentication check, which is a security risk. You must change the $update_free_access value in your settings.php back to FALSE.'),
+ );
+ }
+ else {
+ $requirements['update access'] = array(
+ 'value' => $t('Protected'),
+ );
+ }
+ $requirements['update access']['title'] = $t('Access to update.php');
+ }
+
+ // Display an error if a newly introduced dependency in a module is not resolved.
+ if ($phase == 'update') {
+ $profile = drupal_get_profile();
+ $files = system_rebuild_module_data();
+ foreach ($files as $module => $file) {
+ // Ignore disabled modules and installation profiles.
+ if (!$file->status || $module == $profile) {
+ continue;
+ }
+ // Check the module's PHP version.
+ $name = $file->info['name'];
+ $php = $file->info['php'];
+ if (version_compare($php, PHP_VERSION, '>')) {
+ $requirements['php']['description'] .= $t('@name requires at least PHP @version.', array('@name' => $name, '@version' => $php));
+ $requirements['php']['severity'] = REQUIREMENT_ERROR;
+ }
+ // Check the module's required modules.
+ foreach ($file->requires as $requirement) {
+ $required_module = $requirement['name'];
+ // Check if the module exists.
+ if (!isset($files[$required_module])) {
+ $requirements["$module-$required_module"] = array(
+ 'title' => $t('Unresolved dependency'),
+ 'description' => $t('@name requires this module.', array('@name' => $name)),
+ 'value' => t('@required_name (Missing)', array('@required_name' => $required_module)),
+ 'severity' => REQUIREMENT_ERROR,
+ );
+ continue;
+ }
+ // Check for an incompatible version.
+ $required_file = $files[$required_module];
+ $required_name = $required_file->info['name'];
+ $version = str_replace(DRUPAL_CORE_COMPATIBILITY . '-', '', $required_file->info['version']);
+ $compatibility = drupal_check_incompatibility($requirement, $version);
+ if ($compatibility) {
+ $compatibility = rtrim(substr($compatibility, 2), ')');
+ $requirements["$module-$required_module"] = array(
+ 'title' => $t('Unresolved dependency'),
+ 'description' => $t('@name requires this module and version. Currently using @required_name version @version', array('@name' => $name, '@required_name' => $required_name, '@version' => $version)),
+ 'value' => t('@required_name (Version @compatibility required)', array('@required_name' => $required_name, '@compatibility' => $compatibility)),
+ 'severity' => REQUIREMENT_ERROR,
+ );
+ continue;
+ }
+ }
+ }
+ }
+
+ // Test Unicode library
+ include_once DRUPAL_ROOT . '/includes/unicode.inc';
+ $requirements = array_merge($requirements, unicode_requirements());
+
+ if ($phase == 'runtime') {
+ // Check for update status module.
+ if (!module_exists('update')) {
+ $requirements['update status'] = array(
+ 'value' => $t('Not enabled'),
+ 'severity' => REQUIREMENT_WARNING,
+ 'description' => $t('Update notifications are not enabled. It is <strong>highly recommended</strong> that you enable the update manager module from the <a href="@module">module administration page</a> in order to stay up-to-date on new releases. For more information, <a href="@update">Update status handbook page</a>.', array('@update' => 'http://drupal.org/documentation/modules/update', '@module' => url('admin/modules'))),
+ );
+ }
+ else {
+ $requirements['update status'] = array(
+ 'value' => $t('Enabled'),
+ );
+ }
+ $requirements['update status']['title'] = $t('Update notifications');
+
+ // Check that Drupal can issue HTTP requests.
+ if (variable_get('drupal_http_request_fails', TRUE) && !system_check_http_request()) {
+ $requirements['http requests'] = array(
+ 'title' => $t('HTTP request status'),
+ 'value' => $t('Fails'),
+ 'severity' => REQUIREMENT_ERROR,
+ 'description' => $t('Your system or network configuration does not allow Drupal to access web pages, resulting in reduced functionality. This could be due to your webserver configuration or PHP settings, and should be resolved in order to download information about available updates, fetch aggregator feeds, sign in via OpenID, or use other network-dependent services. If you are certain that Drupal can access web pages but you are still seeing this message, you may add <code>$conf[\'drupal_http_request_fails\'] = FALSE;</code> to the bottom of your settings.php file.'),
+ );
+ }
+ }
+
+ return $requirements;
+}
+
+/**
+ * Implements hook_install().
+ */
+function system_install() {
+ // Create tables.
+ drupal_install_schema('system');
+ $versions = drupal_get_schema_versions('system');
+ $version = $versions ? max($versions) : SCHEMA_INSTALLED;
+ drupal_set_installed_schema_version('system', $version);
+
+ // Clear out module list and hook implementation statics before calling
+ // system_rebuild_theme_data().
+ module_list(TRUE);
+ module_implements('', FALSE, TRUE);
+
+ // Load system theme data appropriately.
+ system_rebuild_theme_data();
+
+ // Enable the default theme.
+ variable_set('theme_default', 'bartik');
+ db_update('system')
+ ->fields(array('status' => 1))
+ ->condition('type', 'theme')
+ ->condition('name', 'bartik')
+ ->execute();
+
+ // Populate the cron key variable.
+ $cron_key = drupal_random_key();
+ variable_set('cron_key', $cron_key);
+}
+
+/**
+ * Implements hook_schema().
+ */
+function system_schema() {
+ // NOTE: {variable} needs to be created before all other tables, as
+ // some database drivers, e.g. Oracle and DB2, will require variable_get()
+ // and variable_set() for overcoming some database specific limitations.
+ $schema['variable'] = array(
+ 'description' => 'Named variable/value pairs created by Drupal core or any other module or theme. All variables are cached in memory at the start of every Drupal request so developers should not be careless about what is stored here.',
+ 'fields' => array(
+ 'name' => array(
+ 'description' => 'The name of the variable.',
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'value' => array(
+ 'description' => 'The value of the variable.',
+ 'type' => 'blob',
+ 'not null' => TRUE,
+ 'size' => 'big',
+ 'translatable' => TRUE,
+ ),
+ ),
+ 'primary key' => array('name'),
+ );
+
+ $schema['actions'] = array(
+ 'description' => 'Stores action information.',
+ 'fields' => array(
+ 'aid' => array(
+ 'description' => 'Primary Key: Unique actions ID.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '0',
+ ),
+ 'type' => array(
+ 'description' => 'The object that that action acts on (node, user, comment, system or custom types.)',
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'callback' => array(
+ 'description' => 'The callback function that executes when the action runs.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'parameters' => array(
+ 'description' => 'Parameters to be passed to the callback function.',
+ 'type' => 'blob',
+ 'not null' => TRUE,
+ 'size' => 'big',
+ ),
+ 'label' => array(
+ 'description' => 'Label of the action.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '0',
+ ),
+ ),
+ 'primary key' => array('aid'),
+ );
+
+ $schema['batch'] = array(
+ 'description' => 'Stores details about batches (processes that run in multiple HTTP requests).',
+ 'fields' => array(
+ 'bid' => array(
+ 'description' => 'Primary Key: Unique batch ID.',
+ // This is not a serial column, to allow both progressive and
+ // non-progressive batches. See batch_process().
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ 'token' => array(
+ 'description' => "A string token generated against the current user's session id and the batch id, used to ensure that only the user who submitted the batch can effectively access it.",
+ 'type' => 'varchar',
+ 'length' => 64,
+ 'not null' => TRUE,
+ ),
+ 'timestamp' => array(
+ 'description' => 'A Unix timestamp indicating when this batch was submitted for processing. Stale batches are purged at cron time.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ ),
+ 'batch' => array(
+ 'description' => 'A serialized array containing the processing data for the batch.',
+ 'type' => 'blob',
+ 'not null' => FALSE,
+ 'size' => 'big',
+ ),
+ ),
+ 'primary key' => array('bid'),
+ 'indexes' => array(
+ 'token' => array('token'),
+ ),
+ );
+
+ $schema['blocked_ips'] = array(
+ 'description' => 'Stores blocked IP addresses.',
+ 'fields' => array(
+ 'iid' => array(
+ 'description' => 'Primary Key: unique ID for IP addresses.',
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ 'ip' => array(
+ 'description' => 'IP address',
+ 'type' => 'varchar',
+ 'length' => 40,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ ),
+ 'indexes' => array(
+ 'blocked_ip' => array('ip'),
+ ),
+ 'primary key' => array('iid'),
+ );
+
+ $schema['cache'] = array(
+ 'description' => 'Generic cache table for caching things not separated out into their own tables. Contributed modules may also use this to store cached items.',
+ 'fields' => array(
+ 'cid' => array(
+ 'description' => 'Primary Key: Unique cache ID.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'data' => array(
+ 'description' => 'A collection of data to cache.',
+ 'type' => 'blob',
+ 'not null' => FALSE,
+ 'size' => 'big',
+ ),
+ 'expire' => array(
+ 'description' => 'A Unix timestamp indicating when the cache entry should expire, or 0 for never.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'created' => array(
+ 'description' => 'A Unix timestamp indicating when the cache entry was created.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'serialized' => array(
+ 'description' => 'A flag to indicate whether content is serialized (1) or not (0).',
+ 'type' => 'int',
+ 'size' => 'small',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ ),
+ 'indexes' => array(
+ 'expire' => array('expire'),
+ ),
+ 'primary key' => array('cid'),
+ );
+ $schema['cache_bootstrap'] = $schema['cache'];
+ $schema['cache_bootstrap']['description'] = 'Cache table for data required to bootstrap Drupal, may be routed to a shared memory cache.';
+ $schema['cache_form'] = $schema['cache'];
+ $schema['cache_form']['description'] = 'Cache table for the form system to store recently built forms and their storage data, to be used in subsequent page requests.';
+ $schema['cache_page'] = $schema['cache'];
+ $schema['cache_page']['description'] = 'Cache table used to store compressed pages for anonymous users, if page caching is enabled.';
+ $schema['cache_menu'] = $schema['cache'];
+ $schema['cache_menu']['description'] = 'Cache table for the menu system to store router information as well as generated link trees for various menu/page/user combinations.';
+ $schema['cache_path'] = $schema['cache'];
+ $schema['cache_path']['description'] = 'Cache table for path alias lookup.';
+
+ $schema['date_format_type'] = array(
+ 'description' => 'Stores configured date format types.',
+ 'fields' => array(
+ 'type' => array(
+ 'description' => 'The date format type, e.g. medium.',
+ 'type' => 'varchar',
+ 'length' => 64,
+ 'not null' => TRUE,
+ ),
+ 'title' => array(
+ 'description' => 'The human readable name of the format type.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ ),
+ 'locked' => array(
+ 'description' => 'Whether or not this is a system provided format.',
+ 'type' => 'int',
+ 'size' => 'tiny',
+ 'default' => 0,
+ 'not null' => TRUE,
+ ),
+ ),
+ 'primary key' => array('type'),
+ 'indexes' => array(
+ 'title' => array('title'),
+ ),
+ );
+
+ // This table's name is plural as some versions of MySQL can't create a
+ // table named 'date_format'.
+ $schema['date_formats'] = array(
+ 'description' => 'Stores configured date formats.',
+ 'fields' => array(
+ 'dfid' => array(
+ 'description' => 'The date format identifier.',
+ 'type' => 'serial',
+ 'not null' => TRUE,
+ 'unsigned' => TRUE,
+ ),
+ 'format' => array(
+ 'description' => 'The date format string.',
+ 'type' => 'varchar',
+ 'length' => 100,
+ 'not null' => TRUE,
+ 'binary' => TRUE,
+ ),
+ 'type' => array(
+ 'description' => 'The date format type, e.g. medium.',
+ 'type' => 'varchar',
+ 'length' => 64,
+ 'not null' => TRUE,
+ ),
+ 'locked' => array(
+ 'description' => 'Whether or not this format can be modified.',
+ 'type' => 'int',
+ 'size' => 'tiny',
+ 'default' => 0,
+ 'not null' => TRUE,
+ ),
+ ),
+ 'primary key' => array('dfid'),
+ 'unique keys' => array('formats' => array('format', 'type')),
+ );
+
+ $schema['date_format_locale'] = array(
+ 'description' => 'Stores configured date formats for each locale.',
+ 'fields' => array(
+ 'format' => array(
+ 'description' => 'The date format string.',
+ 'type' => 'varchar',
+ 'length' => 100,
+ 'not null' => TRUE,
+ ),
+ 'type' => array(
+ 'description' => 'The date format type, e.g. medium.',
+ 'type' => 'varchar',
+ 'length' => 64,
+ 'not null' => TRUE,
+ ),
+ 'language' => array(
+ 'description' => 'A {languages}.language for this format to be used with.',
+ 'type' => 'varchar',
+ 'length' => 12,
+ 'not null' => TRUE,
+ ),
+ ),
+ 'primary key' => array('type', 'language'),
+ );
+
+ $schema['file_managed'] = array(
+ 'description' => 'Stores information for uploaded files.',
+ 'fields' => array(
+ 'fid' => array(
+ 'description' => 'File ID.',
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ 'uid' => array(
+ 'description' => 'The {users}.uid of the user who is associated with the file.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'filename' => array(
+ 'description' => 'Name of the file with no path components. This may differ from the basename of the URI if the file is renamed to avoid overwriting an existing file.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'uri' => array(
+ 'description' => 'The URI to access the file (either local or remote).',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'binary' => TRUE,
+ ),
+ 'filemime' => array(
+ 'description' => "The file's MIME type.",
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'filesize' => array(
+ 'description' => 'The size of the file in bytes.',
+ 'type' => 'int',
+ 'size' => 'big',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'status' => array(
+ 'description' => 'A field indicating the status of the file. Two status are defined in core: temporary (0) and permanent (1). Temporary files older than DRUPAL_MAXIMUM_TEMP_FILE_AGE will be removed during a cron run.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'size' => 'tiny',
+ ),
+ 'timestamp' => array(
+ 'description' => 'UNIX timestamp for when the file was added.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ ),
+ 'indexes' => array(
+ 'uid' => array('uid'),
+ 'status' => array('status'),
+ 'timestamp' => array('timestamp'),
+ ),
+ 'unique keys' => array(
+ 'uri' => array('uri'),
+ ),
+ 'primary key' => array('fid'),
+ 'foreign keys' => array(
+ 'file_owner' => array(
+ 'table' => 'users',
+ 'columns' => array('uid' => 'uid'),
+ ),
+ ),
+ );
+
+ $schema['file_usage'] = array(
+ 'description' => 'Track where a file is used.',
+ 'fields' => array(
+ 'fid' => array(
+ 'description' => 'File ID.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ 'module' => array(
+ 'description' => 'The name of the module that is using the file.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'type' => array(
+ 'description' => 'The name of the object type in which the file is used.',
+ 'type' => 'varchar',
+ 'length' => 64,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'id' => array(
+ 'description' => 'The primary key of the object using the file.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'count' => array(
+ 'description' => 'The number of times this file is used by this object.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ ),
+ 'primary key' => array('fid', 'type', 'id', 'module'),
+ 'indexes' => array(
+ 'type_id' => array('type', 'id'),
+ 'fid_count' => array('fid', 'count'),
+ 'fid_module' => array('fid', 'module'),
+ ),
+ );
+
+ $schema['flood'] = array(
+ 'description' => 'Flood controls the threshold of events, such as the number of contact attempts.',
+ 'fields' => array(
+ 'fid' => array(
+ 'description' => 'Unique flood event ID.',
+ 'type' => 'serial',
+ 'not null' => TRUE,
+ ),
+ 'event' => array(
+ 'description' => 'Name of event (e.g. contact).',
+ 'type' => 'varchar',
+ 'length' => 64,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'identifier' => array(
+ 'description' => 'Identifier of the visitor, such as an IP address or hostname.',
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'timestamp' => array(
+ 'description' => 'Timestamp of the event.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'expiration' => array(
+ 'description' => 'Expiration timestamp. Expired events are purged on cron run.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ ),
+ 'primary key' => array('fid'),
+ 'indexes' => array(
+ 'allow' => array('event', 'identifier', 'timestamp'),
+ 'purge' => array('expiration'),
+ ),
+ );
+
+ $schema['menu_router'] = array(
+ 'description' => 'Maps paths to various callbacks (access, page and title)',
+ 'fields' => array(
+ 'path' => array(
+ 'description' => 'Primary Key: the Drupal path this entry describes',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'load_functions' => array(
+ 'description' => 'A serialized array of function names (like node_load) to be called to load an object corresponding to a part of the current path.',
+ 'type' => 'blob',
+ 'not null' => TRUE,
+ ),
+ 'to_arg_functions' => array(
+ 'description' => 'A serialized array of function names (like user_uid_optional_to_arg) to be called to replace a part of the router path with another string.',
+ 'type' => 'blob',
+ 'not null' => TRUE,
+ ),
+ 'access_callback' => array(
+ 'description' => 'The callback which determines the access to this router path. Defaults to user_access.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'access_arguments' => array(
+ 'description' => 'A serialized array of arguments for the access callback.',
+ 'type' => 'blob',
+ 'not null' => FALSE,
+ ),
+ 'page_callback' => array(
+ 'description' => 'The name of the function that renders the page.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'page_arguments' => array(
+ 'description' => 'A serialized array of arguments for the page callback.',
+ 'type' => 'blob',
+ 'not null' => FALSE,
+ ),
+ 'delivery_callback' => array(
+ 'description' => 'The name of the function that sends the result of the page_callback function to the browser.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'fit' => array(
+ 'description' => 'A numeric representation of how specific the path is.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'number_parts' => array(
+ 'description' => 'Number of parts in this router path.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'size' => 'small',
+ ),
+ 'context' => array(
+ 'description' => 'Only for local tasks (tabs) - the context of a local task to control its placement.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'tab_parent' => array(
+ 'description' => 'Only for local tasks (tabs) - the router path of the parent page (which may also be a local task).',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'tab_root' => array(
+ 'description' => 'Router path of the closest non-tab parent page. For pages that are not local tasks, this will be the same as the path.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'title' => array(
+ 'description' => 'The title for the current page, or the title for the tab if this is a local task.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'title_callback' => array(
+ 'description' => 'A function which will alter the title. Defaults to t()',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'title_arguments' => array(
+ 'description' => 'A serialized array of arguments for the title callback. If empty, the title will be used as the sole argument for the title callback.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'theme_callback' => array(
+ 'description' => 'A function which returns the name of the theme that will be used to render this page. If left empty, the default theme will be used.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'theme_arguments' => array(
+ 'description' => 'A serialized array of arguments for the theme callback.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'type' => array(
+ 'description' => 'Numeric representation of the type of the menu item, like MENU_LOCAL_TASK.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'description' => array(
+ 'description' => 'A description of this item.',
+ 'type' => 'text',
+ 'not null' => TRUE,
+ ),
+ 'position' => array(
+ 'description' => 'The position of the block (left or right) on the system administration page for this item.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'weight' => array(
+ 'description' => 'Weight of the element. Lighter weights are higher up, heavier weights go down.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'include_file' => array(
+ 'description' => 'The file to include for this element, usually the page callback function lives in this file.',
+ 'type' => 'text',
+ 'size' => 'medium',
+ ),
+ ),
+ 'indexes' => array(
+ 'fit' => array('fit'),
+ 'tab_parent' => array(array('tab_parent', 64), 'weight', 'title'),
+ 'tab_root_weight_title' => array(array('tab_root', 64), 'weight', 'title'),
+ ),
+ 'primary key' => array('path'),
+ );
+
+ $schema['menu_links'] = array(
+ 'description' => 'Contains the individual links within a menu.',
+ 'fields' => array(
+ 'menu_name' => array(
+ 'description' => "The menu name. All links with the same menu name (such as 'navigation') are part of the same menu.",
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'mlid' => array(
+ 'description' => 'The menu link ID (mlid) is the integer primary key.',
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ 'plid' => array(
+ 'description' => 'The parent link ID (plid) is the mlid of the link above in the hierarchy, or zero if the link is at the top level in its menu.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'link_path' => array(
+ 'description' => 'The Drupal path or external path this link points to.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'router_path' => array(
+ 'description' => 'For links corresponding to a Drupal path (external = 0), this connects the link to a {menu_router}.path for joins.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'link_title' => array(
+ 'description' => 'The text displayed for the link, which may be modified by a title callback stored in {menu_router}.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'translatable' => TRUE,
+ ),
+ 'options' => array(
+ 'description' => 'A serialized array of options to be passed to the url() or l() function, such as a query string or HTML attributes.',
+ 'type' => 'blob',
+ 'not null' => FALSE,
+ 'translatable' => TRUE,
+ ),
+ 'module' => array(
+ 'description' => 'The name of the module that generated this link.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => 'system',
+ ),
+ 'hidden' => array(
+ 'description' => 'A flag for whether the link should be rendered in menus. (1 = a disabled menu item that may be shown on admin screens, -1 = a menu callback, 0 = a normal, visible link)',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'size' => 'small',
+ ),
+ 'external' => array(
+ 'description' => 'A flag to indicate if the link points to a full URL starting with a protocol, like http:// (1 = external, 0 = internal).',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'size' => 'small',
+ ),
+ 'has_children' => array(
+ 'description' => 'Flag indicating whether any links have this link as a parent (1 = children exist, 0 = no children).',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'size' => 'small',
+ ),
+ 'expanded' => array(
+ 'description' => 'Flag for whether this link should be rendered as expanded in menus - expanded links always have their child links displayed, instead of only when the link is in the active trail (1 = expanded, 0 = not expanded)',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'size' => 'small',
+ ),
+ 'weight' => array(
+ 'description' => 'Link weight among links in the same menu at the same depth.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'depth' => array(
+ 'description' => 'The depth relative to the top level. A link with plid == 0 will have depth == 1.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'size' => 'small',
+ ),
+ 'customized' => array(
+ 'description' => 'A flag to indicate that the user has manually created or edited the link (1 = customized, 0 = not customized).',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'size' => 'small',
+ ),
+ 'p1' => array(
+ 'description' => 'The first mlid in the materialized path. If N = depth, then pN must equal the mlid. If depth > 1 then p(N-1) must equal the plid. All pX where X > depth must equal zero. The columns p1 .. p9 are also called the parents.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'p2' => array(
+ 'description' => 'The second mlid in the materialized path. See p1.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'p3' => array(
+ 'description' => 'The third mlid in the materialized path. See p1.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'p4' => array(
+ 'description' => 'The fourth mlid in the materialized path. See p1.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'p5' => array(
+ 'description' => 'The fifth mlid in the materialized path. See p1.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'p6' => array(
+ 'description' => 'The sixth mlid in the materialized path. See p1.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'p7' => array(
+ 'description' => 'The seventh mlid in the materialized path. See p1.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'p8' => array(
+ 'description' => 'The eighth mlid in the materialized path. See p1.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'p9' => array(
+ 'description' => 'The ninth mlid in the materialized path. See p1.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'updated' => array(
+ 'description' => 'Flag that indicates that this link was generated during the update from Drupal 5.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'size' => 'small',
+ ),
+ ),
+ 'indexes' => array(
+ 'path_menu' => array(array('link_path', 128), 'menu_name'),
+ 'menu_plid_expand_child' => array('menu_name', 'plid', 'expanded', 'has_children'),
+ 'menu_parents' => array('menu_name', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9'),
+ 'router_path' => array(array('router_path', 128)),
+ ),
+ 'primary key' => array('mlid'),
+ );
+
+ $schema['queue'] = array(
+ 'description' => 'Stores items in queues.',
+ 'fields' => array(
+ 'item_id' => array(
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'description' => 'Primary Key: Unique item ID.',
+ ),
+ 'name' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'The queue name.',
+ ),
+ 'data' => array(
+ 'type' => 'blob',
+ 'not null' => FALSE,
+ 'size' => 'big',
+ 'serialize' => TRUE,
+ 'description' => 'The arbitrary data for the item.',
+ ),
+ 'expire' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'Timestamp when the claim lease expires on the item.',
+ ),
+ 'created' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'Timestamp when the item was created.',
+ ),
+ ),
+ 'primary key' => array('item_id'),
+ 'indexes' => array(
+ 'name_created' => array('name', 'created'),
+ 'expire' => array('expire'),
+ ),
+ );
+
+ $schema['registry'] = array(
+ 'description' => "Each record is a function, class, or interface name and the file it is in.",
+ 'fields' => array(
+ 'name' => array(
+ 'description' => 'The name of the function, class, or interface.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'type' => array(
+ 'description' => 'Either function or class or interface.',
+ 'type' => 'varchar',
+ 'length' => 9,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'filename' => array(
+ 'description' => 'Name of the file.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ ),
+ 'module' => array(
+ 'description' => 'Name of the module the file belongs to.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => ''
+ ),
+ 'weight' => array(
+ 'description' => "The order in which this module's hooks should be invoked relative to other modules. Equal-weighted modules are ordered by name.",
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ ),
+ 'primary key' => array('name', 'type'),
+ 'indexes' => array(
+ 'hook' => array('type', 'weight', 'module'),
+ ),
+ );
+
+ $schema['registry_file'] = array(
+ 'description' => "Files parsed to build the registry.",
+ 'fields' => array(
+ 'filename' => array(
+ 'description' => 'Path to the file.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ ),
+ 'hash' => array(
+ 'description' => "sha-256 hash of the file's contents when last parsed.",
+ 'type' => 'varchar',
+ 'length' => 64,
+ 'not null' => TRUE,
+ ),
+ ),
+ 'primary key' => array('filename'),
+ );
+
+ $schema['semaphore'] = array(
+ 'description' => 'Table for holding semaphores, locks, flags, etc. that cannot be stored as Drupal variables since they must not be cached.',
+ 'fields' => array(
+ 'name' => array(
+ 'description' => 'Primary Key: Unique name.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => ''
+ ),
+ 'value' => array(
+ 'description' => 'A value for the semaphore.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => ''
+ ),
+ 'expire' => array(
+ 'description' => 'A Unix timestamp with microseconds indicating when the semaphore should expire.',
+ 'type' => 'float',
+ 'size' => 'big',
+ 'not null' => TRUE
+ ),
+ ),
+ 'indexes' => array(
+ 'value' => array('value'),
+ 'expire' => array('expire'),
+ ),
+ 'primary key' => array('name'),
+ );
+
+ $schema['sequences'] = array(
+ 'description' => 'Stores IDs.',
+ 'fields' => array(
+ 'value' => array(
+ 'description' => 'The value of the sequence.',
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ ),
+ 'primary key' => array('value'),
+ );
+
+ $schema['sessions'] = array(
+ 'description' => "Drupal's session handlers read and write into the sessions table. Each record represents a user session, either anonymous or authenticated.",
+ 'fields' => array(
+ 'uid' => array(
+ 'description' => 'The {users}.uid corresponding to a session, or 0 for anonymous user.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ 'sid' => array(
+ 'description' => "A session ID. The value is generated by Drupal's session handlers.",
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ ),
+ 'ssid' => array(
+ 'description' => "Secure session ID. The value is generated by Drupal's session handlers.",
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'hostname' => array(
+ 'description' => 'The IP address that last used this session ID (sid).',
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'timestamp' => array(
+ 'description' => 'The Unix timestamp when this session last requested a page. Old records are purged by PHP automatically.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'cache' => array(
+ 'description' => "The time of this user's last post. This is used when the site has specified a minimum_cache_lifetime. See cache_get().",
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'session' => array(
+ 'description' => 'The serialized contents of $_SESSION, an array of name/value pairs that persists across page requests by this session ID. Drupal loads $_SESSION from here at the start of each request and saves it at the end.',
+ 'type' => 'blob',
+ 'not null' => FALSE,
+ 'size' => 'big',
+ ),
+ ),
+ 'primary key' => array(
+ 'sid',
+ 'ssid',
+ ),
+ 'indexes' => array(
+ 'timestamp' => array('timestamp'),
+ 'uid' => array('uid'),
+ 'ssid' => array('ssid'),
+ ),
+ 'foreign keys' => array(
+ 'session_user' => array(
+ 'table' => 'users',
+ 'columns' => array('uid' => 'uid'),
+ ),
+ ),
+ );
+
+ $schema['system'] = array(
+ 'description' => "A list of all modules, themes, and theme engines that are or have been installed in Drupal's file system.",
+ 'fields' => array(
+ 'filename' => array(
+ 'description' => 'The path of the primary file for this item, relative to the Drupal root; e.g. modules/node/node.module.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'name' => array(
+ 'description' => 'The name of the item; e.g. node.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'type' => array(
+ 'description' => 'The type of the item, either module, theme, or theme_engine.',
+ 'type' => 'varchar',
+ 'length' => 12,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'owner' => array(
+ 'description' => "A theme's 'parent' . Can be either a theme or an engine.",
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'status' => array(
+ 'description' => 'Boolean indicating whether or not this item is enabled.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'bootstrap' => array(
+ 'description' => "Boolean indicating whether this module is loaded during Drupal's early bootstrapping phase (e.g. even before the page cache is consulted).",
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'schema_version' => array(
+ 'description' => "The module's database schema version number. -1 if the module is not installed (its tables do not exist); 0 or the largest N of the module's hook_update_N() function that has either been run or existed when the module was first installed.",
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => -1,
+ 'size' => 'small',
+ ),
+ 'weight' => array(
+ 'description' => "The order in which this module's hooks should be invoked relative to other modules. Equal-weighted modules are ordered by name.",
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'info' => array(
+ 'description' => "A serialized array containing information from the module's .info file; keys can include name, description, package, version, core, dependencies, and php.",
+ 'type' => 'blob',
+ 'not null' => FALSE,
+ ),
+ ),
+ 'primary key' => array('filename'),
+ 'indexes' => array(
+ 'system_list' => array('status', 'bootstrap', 'type', 'weight', 'name'),
+ 'type_name' => array('type', 'name'),
+ ),
+ );
+
+ $schema['url_alias'] = array(
+ 'description' => 'A list of URL aliases for Drupal paths; a user may visit either the source or destination path.',
+ 'fields' => array(
+ 'pid' => array(
+ 'description' => 'A unique path alias identifier.',
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ 'source' => array(
+ 'description' => 'The Drupal path this alias is for; e.g. node/12.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'alias' => array(
+ 'description' => 'The alias for this path; e.g. title-of-the-story.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'language' => array(
+ 'description' => "The language this alias is for; if 'und', the alias will be used for unknown languages. Each Drupal path can have an alias for each supported language.",
+ 'type' => 'varchar',
+ 'length' => 12,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ ),
+ 'primary key' => array('pid'),
+ 'indexes' => array(
+ 'alias_language_pid' => array('alias', 'language', 'pid'),
+ 'source_language_pid' => array('source', 'language', 'pid'),
+ ),
+ );
+
+ return $schema;
+}
+
+/**
+ * The cache schema corresponding to system_update_7054.
+ *
+ * Drupal 7 adds several new cache tables. Since they all have identical schema
+ * this helper function allows them to re-use the same definition, without
+ * relying on system_schema(), which may change with future updates.
+ */
+function system_schema_cache_7054() {
+ return array(
+ 'description' => 'Generic cache table for caching things not separated out into their own tables. Contributed modules may also use this to store cached items.',
+ 'fields' => array(
+ 'cid' => array(
+ 'description' => 'Primary Key: Unique cache ID.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'data' => array(
+ 'description' => 'A collection of data to cache.',
+ 'type' => 'blob',
+ 'not null' => FALSE,
+ 'size' => 'big',
+ ),
+ 'expire' => array(
+ 'description' => 'A Unix timestamp indicating when the cache entry should expire, or 0 for never.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'created' => array(
+ 'description' => 'A Unix timestamp indicating when the cache entry was created.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'serialized' => array(
+ 'description' => 'A flag to indicate whether content is serialized (1) or not (0).',
+ 'type' => 'int',
+ 'size' => 'small',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ ),
+ 'indexes' => array(
+ 'expire' => array('expire'),
+ ),
+ 'primary key' => array('cid'),
+ );
+}
+
+
+// Updates for core.
+
+function system_update_last_removed() {
+ return 6055;
+}
+
+/**
+ * Implements hook_update_dependencies().
+ */
+function system_update_dependencies() {
+ // system_update_7053() queries the {block} table, so it must run after
+ // block_update_7002(), which creates that table.
+ $dependencies['system'][7053] = array(
+ 'block' => 7002,
+ );
+
+ // system_update_7061() queries the {node_revision} table, so it must run
+ // after node_update_7001(), which renames the {node_revisions} table.
+ $dependencies['system'][7061] = array(
+ 'node' => 7001,
+ );
+
+ // system_update_7067() migrates role permissions and therefore must run
+ // after the {role} and {role_permission} tables are properly set up, which
+ // happens in user_update_7007().
+ $dependencies['system'][7067] = array(
+ 'user' => 7007,
+ );
+
+ return $dependencies;
+}
+
+/**
+ * @defgroup updates-6.x-to-7.x Updates from 6.x to 7.x
+ * @{
+ * Update functions from 6.x to 7.x.
+ */
+
+/**
+ * Rename blog and forum permissions to be consistent with other content types.
+ */
+function system_update_7000() {
+ $result = db_query("SELECT rid, perm FROM {permission} ORDER BY rid");
+ foreach ($result as $role) {
+ $renamed_permission = $role->perm;
+ $renamed_permission = preg_replace('/(?<=^|,\ )create\ blog\ entries(?=,|$)/', 'create blog content', $renamed_permission);
+ $renamed_permission = preg_replace('/(?<=^|,\ )edit\ own\ blog\ entries(?=,|$)/', 'edit own blog content', $renamed_permission);
+ $renamed_permission = preg_replace('/(?<=^|,\ )edit\ any\ blog\ entry(?=,|$)/', 'edit any blog content', $renamed_permission);
+ $renamed_permission = preg_replace('/(?<=^|,\ )delete\ own\ blog\ entries(?=,|$)/', 'delete own blog content', $renamed_permission);
+ $renamed_permission = preg_replace('/(?<=^|,\ )delete\ any\ blog\ entry(?=,|$)/', 'delete any blog content', $renamed_permission);
+
+ $renamed_permission = preg_replace('/(?<=^|,\ )create\ forum\ topics(?=,|$)/', 'create forum content', $renamed_permission);
+ $renamed_permission = preg_replace('/(?<=^|,\ )delete\ any\ forum\ topic(?=,|$)/', 'delete any forum content', $renamed_permission);
+ $renamed_permission = preg_replace('/(?<=^|,\ )delete\ own\ forum\ topics(?=,|$)/', 'delete own forum content', $renamed_permission);
+ $renamed_permission = preg_replace('/(?<=^|,\ )edit\ any\ forum\ topic(?=,|$)/', 'edit any forum content', $renamed_permission);
+ $renamed_permission = preg_replace('/(?<=^|,\ )edit\ own\ forum\ topics(?=,|$)/', 'edit own forum content', $renamed_permission);
+
+ if ($renamed_permission != $role->perm) {
+ db_update('permission')
+ ->fields(array('perm' => $renamed_permission))
+ ->condition('rid', $role->rid)
+ ->execute();
+ }
+ }
+}
+
+/**
+ * Generate a cron key and save it in the variables table.
+ */
+function system_update_7001() {
+ variable_set('cron_key', drupal_random_key());
+}
+
+/**
+ * Add a table to store blocked IP addresses.
+ */
+function system_update_7002() {
+ $schema['blocked_ips'] = array(
+ 'description' => 'Stores blocked IP addresses.',
+ 'fields' => array(
+ 'iid' => array(
+ 'description' => 'Primary Key: unique ID for IP addresses.',
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ 'ip' => array(
+ 'description' => 'IP address',
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ ),
+ 'indexes' => array(
+ 'blocked_ip' => array('ip'),
+ ),
+ 'primary key' => array('iid'),
+ );
+
+ db_create_table('blocked_ips', $schema['blocked_ips']);
+}
+
+/**
+ * Update {blocked_ips} with valid IP addresses from {access}.
+ */
+function system_update_7003() {
+ $messages = array();
+ $type = 'host';
+ $result = db_query("SELECT mask FROM {access} WHERE status = :status AND type = :type", array(
+ ':status' => 0,
+ ':type' => $type,
+ ));
+ foreach ($result as $blocked) {
+ if (filter_var($blocked->mask, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) !== FALSE) {
+ db_insert('blocked_ips')
+ ->fields(array('ip' => $blocked->mask))
+ ->execute();
+ }
+ else {
+ $invalid_host = check_plain($blocked->mask);
+ $messages[] = t('The host !host is no longer blocked because it is not a valid IP address.', array('!host' => $invalid_host ));
+ }
+ }
+ if (isset($invalid_host)) {
+ drupal_set_message('Drupal no longer supports wildcard IP address blocking. Visitors whose IP addresses match ranges you have previously set using <em>access rules</em> will no longer be blocked from your site when you put the site online. See the <a href="http://drupal.org/node/24302">IP address and referrer blocking Handbook page</a> for alternative methods.', 'warning');
+ }
+ // Make sure not to block any IP addresses that were specifically allowed by access rules.
+ if (!empty($result)) {
+ $result = db_query("SELECT mask FROM {access} WHERE status = :status AND type = :type", array(
+ ':status' => 1,
+ ':type' => $type,
+ ));
+ $or = db_condition('or');
+ foreach ($result as $allowed) {
+ $or->condition('ip', $allowed->mask, 'LIKE');
+ }
+ if (count($or)) {
+ db_delete('blocked_ips')
+ ->condition($or)
+ ->execute();
+ }
+ }
+}
+
+/**
+ * Remove hardcoded numeric deltas from all blocks in core.
+ */
+function system_update_7004(&$sandbox) {
+ // Get an array of the renamed block deltas, organized by module.
+ $renamed_deltas = array(
+ 'blog' => array('0' => 'recent'),
+ 'book' => array('0' => 'navigation'),
+ 'comment' => array('0' => 'recent'),
+ 'forum' => array(
+ '0' => 'active',
+ '1' => 'new',
+ ),
+ 'locale' => array('0' => LANGUAGE_TYPE_INTERFACE),
+ 'node' => array('0' => 'syndicate'),
+ 'poll' => array('0' => 'recent'),
+ 'profile' => array('0' => 'author-information'),
+ 'search' => array('0' => 'form'),
+ 'statistics' => array('0' => 'popular'),
+ 'system' => array('0' => 'powered-by'),
+ 'user' => array(
+ '0' => 'login',
+ '1' => 'navigation',
+ '2' => 'new',
+ '3' => 'online',
+ ),
+ );
+
+ $moved_deltas = array(
+ 'user' => array('navigation' => 'system'),
+ );
+
+ // Only run this the first time through the batch update.
+ if (!isset($sandbox['progress'])) {
+ // Rename forum module's block variables.
+ $forum_block_num_0 = variable_get('forum_block_num_0');
+ if (isset($forum_block_num_0)) {
+ variable_set('forum_block_num_active', $forum_block_num_0);
+ variable_del('forum_block_num_0');
+ }
+ $forum_block_num_1 = variable_get('forum_block_num_1');
+ if (isset($forum_block_num_1)) {
+ variable_set('forum_block_num_new', $forum_block_num_1);
+ variable_del('forum_block_num_1');
+ }
+ }
+
+ update_fix_d7_block_deltas($sandbox, $renamed_deltas, $moved_deltas);
+
+}
+
+/**
+ * Remove throttle columns and variables.
+ */
+function system_update_7005() {
+ db_drop_field('blocks', 'throttle');
+ db_drop_field('system', 'throttle');
+ variable_del('throttle_user');
+ variable_del('throttle_anonymous');
+ variable_del('throttle_level');
+ variable_del('throttle_probability_limiter');
+}
+
+/**
+ * Convert to new method of storing permissions.
+ */
+function system_update_7007() {
+ // Copy the permissions from the old {permission} table to the new {role_permission} table.
+ $messages = array();
+ $result = db_query("SELECT rid, perm FROM {permission} ORDER BY rid ASC");
+ $query = db_insert('role_permission')->fields(array('rid', 'permission'));
+ foreach ($result as $role) {
+ foreach (array_unique(explode(', ', $role->perm)) as $perm) {
+ $query->values(array(
+ 'rid' => $role->rid,
+ 'permission' => $perm,
+ ));
+ }
+ $messages[] = t('Inserted into {role_permission} the permissions for role ID !id', array('!id' => $role->rid));
+ }
+ $query->execute();
+ db_drop_table('permission');
+
+ return implode(', ', $messages);
+}
+
+/**
+ * Rename the variable for primary links.
+ */
+function system_update_7009() {
+ $current_primary = variable_get('menu_primary_links_source');
+ if (isset($current_primary)) {
+ variable_set('menu_main_links_source', $current_primary);
+ variable_del('menu_primary_links_source');
+ }
+}
+
+/**
+ * Split the 'bypass node access' permission from 'administer nodes'.
+ */
+function system_update_7011() {
+ // Get existing roles that can 'administer nodes'.
+ $rids = array();
+ $rids = db_query("SELECT rid FROM {role_permission} WHERE permission = :perm", array(':perm' => 'administer nodes'))->fetchCol();
+ // None found.
+ if (empty($rids)) {
+ return;
+ }
+ $insert = db_insert('role_permission')->fields(array('rid', 'permission'));
+ foreach ($rids as $rid) {
+ $insert->values(array(
+ 'rid' => $rid,
+ 'permission' => 'bypass node access',
+ ));
+ }
+ $insert->execute();
+}
+
+/**
+ * Convert default time zone offset to default time zone name.
+ */
+function system_update_7013() {
+ $timezone = NULL;
+ $timezones = system_time_zones();
+ // If the contributed Date module set a default time zone name, use this
+ // setting as the default time zone.
+ if (($timezone_name = variable_get('date_default_timezone_name')) && isset($timezones[$timezone_name])) {
+ $timezone = $timezone_name;
+ }
+ // If the contributed Event module has set a default site time zone, look up
+ // the time zone name and use it as the default time zone.
+ if (!$timezone && ($timezone_id = variable_get('date_default_timezone_id', 0))) {
+ try {
+ $timezone_name = db_query('SELECT name FROM {event_timezones} WHERE timezone = :timezone_id', array(':timezone_id' => $timezone_id))->fetchField();
+ if (($timezone_name = str_replace(' ', '_', $timezone_name)) && isset($timezones[$timezone_name])) {
+ $timezone = $timezone_name;
+ }
+ }
+ catch (PDOException $e) {
+ // Ignore error if event_timezones table does not exist or unexpected
+ // schema found.
+ }
+ }
+
+ // Check to see if timezone was overriden in update_prepare_d7_bootstrap().
+ $offset = variable_get('date_temporary_timezone');
+ // If not, use the default.
+ if (!isset($offset)) {
+ $offset = variable_get('date_default_timezone', 0);
+ }
+
+ // If the previous default time zone was a non-zero offset, guess the site's
+ // intended time zone based on that offset and the server's daylight saving
+ // time status.
+ if (!$timezone && $offset) {
+ $timezone_name = timezone_name_from_abbr('', intval($offset), date('I'));
+ if ($timezone_name && isset($timezones[$timezone_name])) {
+ $timezone = $timezone_name;
+ }
+ }
+ // Otherwise, the default time zone offset was zero, which is UTC.
+ if (!$timezone) {
+ $timezone = 'UTC';
+ }
+ variable_set('date_default_timezone', $timezone);
+ drupal_set_message(format_string('The default time zone has been set to %timezone. Check the <a href="@config-url">date and time configuration page</a> to configure it correctly.', array('%timezone' => $timezone, '@config-url' => url('admin/config/regional/settings'))), 'warning');
+ // Remove temporary override.
+ variable_del('date_temporary_timezone');
+}
+
+/**
+ * Change the user logout path.
+ */
+function system_update_7015() {
+ db_update('menu_links')
+ ->fields(array('link_path' => 'user/logout'))
+ ->condition('link_path', 'logout')
+ ->execute();
+ db_update('menu_links')
+ ->fields(array('router_path' => 'user/logout'))
+ ->condition('router_path', 'logout')
+ ->execute();
+
+ db_update('menu_links')
+ ->fields(array(
+ 'menu_name' => 'user-menu',
+ 'plid' => 0,
+ ))
+ ->condition(db_or()
+ ->condition('link_path', 'user/logout')
+ ->condition('router_path', 'user/logout')
+ )
+ ->condition('module', 'system')
+ ->condition('customized', 0)
+ ->execute();
+}
+
+/**
+ * Remove custom datatype *_unsigned in PostgreSQL.
+ */
+function system_update_7016() {
+ // Only run these queries if the driver used is pgsql.
+ if (db_driver() == 'pgsql') {
+ $result = db_query("SELECT c.relname AS table, a.attname AS field,
+ pg_catalog.format_type(a.atttypid, a.atttypmod) AS type
+ FROM pg_catalog.pg_attribute a
+ LEFT JOIN pg_class c ON (c.oid = a.attrelid)
+ WHERE pg_catalog.pg_table_is_visible(c.oid) AND c.relkind = 'r'
+ AND pg_catalog.format_type(a.atttypid, a.atttypmod) LIKE '%unsigned%'");
+ foreach ($result as $row) {
+ switch ($row->type) {
+ case 'smallint_unsigned':
+ $datatype = 'int';
+ break;
+ case 'int_unsigned':
+ case 'bigint_unsigned':
+ default:
+ $datatype = 'bigint';
+ break;
+ }
+ db_query('ALTER TABLE ' . $row->table . ' ALTER COLUMN "' . $row->field . '" TYPE ' . $datatype);
+ db_query('ALTER TABLE ' . $row->table . ' ADD CHECK ("' . $row->field . '" >= 0)');
+ }
+ db_query('DROP DOMAIN IF EXISTS smallint_unsigned');
+ db_query('DROP DOMAIN IF EXISTS int_unsigned');
+ db_query('DROP DOMAIN IF EXISTS bigint_unsigned');
+ }
+}
+
+/**
+ * Change the theme setting 'toggle_node_info' into a per content type variable.
+ */
+function system_update_7017() {
+ // Get the global theme settings.
+ $settings = variable_get('theme_settings', array());
+ // Get the settings of the default theme.
+ $settings = array_merge($settings, variable_get('theme_' . variable_get('theme_default', 'garland') . '_settings', array()));
+
+ $types = _update_7000_node_get_types();
+ foreach ($types as $type) {
+ if (isset($settings['toggle_node_info_' . $type->type])) {
+ variable_set('node_submitted_' . $type->type, $settings['toggle_node_info_' . $type->type]);
+ }
+ }
+
+ // Unset deprecated 'toggle_node_info' theme settings.
+ $theme_settings = variable_get('theme_settings', array());
+ foreach ($theme_settings as $setting => $value) {
+ if (substr($setting, 0, 16) == 'toggle_node_info') {
+ unset($theme_settings[$setting]);
+ }
+ }
+ variable_set('theme_settings', $theme_settings);
+}
+
+/**
+ * Shorten the {system}.type column and modify indexes.
+ */
+function system_update_7018() {
+ db_drop_index('system', 'modules');
+ db_drop_index('system', 'type_name');
+ db_change_field('system', 'type', 'type', array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => ''));
+ db_add_index('system', 'type_name', array('type', 'name'));
+}
+
+/**
+ * Enable field and field_ui modules.
+ */
+function system_update_7020() {
+ $module_list = array('field_sql_storage', 'field', 'field_ui');
+ module_enable($module_list, FALSE);
+}
+
+/**
+ * Change the PHP for settings permission.
+ */
+function system_update_7021() {
+ db_update('role_permission')
+ ->fields(array('permission' => 'use PHP for settings'))
+ ->condition('permission', 'use PHP for block visibility')
+ ->execute();
+}
+
+/**
+ * Enable field type modules.
+ */
+function system_update_7027() {
+ $module_list = array('text', 'number', 'list', 'options');
+ module_enable($module_list, FALSE);
+}
+
+/**
+ * Add new 'view own unpublished content' permission for authenticated users.
+ * Preserves legacy behavior from Drupal 6.x.
+ */
+function system_update_7029() {
+ db_insert('role_permission')
+ ->fields(array(
+ 'rid' => DRUPAL_AUTHENTICATED_RID,
+ 'permission' => 'view own unpublished content',
+ ))
+ ->execute();
+}
+
+/**
+* Alter field hostname to identifier in the {flood} table.
+ */
+function system_update_7032() {
+ db_drop_index('flood', 'allow');
+ db_change_field('flood', 'hostname', 'identifier', array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''));
+ db_add_index('flood', 'allow', array('event', 'identifier', 'timestamp'));
+}
+
+/**
+ * Move CACHE_AGGRESSIVE to CACHE_NORMAL.
+ */
+function system_update_7033() {
+ if (variable_get('cache') == 2) {
+ variable_set('cache', 1);
+ return t('Aggressive caching was disabled and replaced with normal caching. Read the page caching section in default.settings.php for more information on how to enable similar functionality.');
+ }
+}
+
+/**
+ * Migrate the file path settings and create the new {file_managed} table.
+ */
+function system_update_7034() {
+ $files_directory = variable_get('file_directory_path', NULL);
+ if (variable_get('file_downloads', 1) == 1) {
+ // Our default is public, so we don't need to set anything.
+ if (!empty($files_directory)) {
+ variable_set('file_public_path', $files_directory);
+ }
+ }
+ elseif (variable_get('file_downloads', 1) == 2) {
+ variable_set('file_default_scheme', 'private');
+ if (!empty($files_directory)) {
+ variable_set('file_private_path', $files_directory);
+ }
+ }
+ variable_del('file_downloads');
+
+ $schema['file_managed'] = array(
+ 'description' => 'Stores information for uploaded files.',
+ 'fields' => array(
+ 'fid' => array(
+ 'description' => 'File ID.',
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ 'uid' => array(
+ 'description' => 'The {user}.uid of the user who is associated with the file.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'filename' => array(
+ 'description' => 'Name of the file with no path components. This may differ from the basename of the filepath if the file is renamed to avoid overwriting an existing file.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'binary' => TRUE,
+ ),
+ 'uri' => array(
+ 'description' => 'URI of file.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'binary' => TRUE,
+ ),
+ 'filemime' => array(
+ 'description' => "The file's MIME type.",
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'filesize' => array(
+ 'description' => 'The size of the file in bytes.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'status' => array(
+ 'description' => 'A field indicating the status of the file. Two status are defined in core: temporary (0) and permanent (1). Temporary files older than DRUPAL_MAXIMUM_TEMP_FILE_AGE will be removed during a cron run.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'size' => 'tiny',
+ ),
+ 'timestamp' => array(
+ 'description' => 'UNIX timestamp for when the file was added.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ ),
+ 'indexes' => array(
+ 'uid' => array('uid'),
+ 'status' => array('status'),
+ 'timestamp' => array('timestamp'),
+ ),
+ 'unique keys' => array(
+ 'uri' => array('uri'),
+ ),
+ 'primary key' => array('fid'),
+ );
+
+ db_create_table('file_managed', $schema['file_managed']);
+}
+
+/**
+ * Split the 'access site in maintenance mode' permission from 'administer site configuration'.
+ */
+function system_update_7036() {
+ // Get existing roles that can 'administer site configuration'.
+ $rids = db_query("SELECT rid FROM {role_permission} WHERE permission = :perm", array(':perm' => 'administer site configuration'))->fetchCol();
+ // None found.
+ if (empty($rids)) {
+ return;
+ }
+ $insert = db_insert('role_permission')->fields(array('rid', 'permission'));
+ foreach ($rids as $rid) {
+ $insert->values(array(
+ 'rid' => $rid,
+ 'permission' => 'access site in maintenance mode',
+ ));
+ }
+ $insert->execute();
+}
+
+/**
+ * Upgrade the {url_alias} table and create a cache bin for path aliases.
+ */
+function system_update_7042() {
+ // update_fix_d7_requirements() adds 'fake' source and alias columns to
+ // allow bootstrap to run without fatal errors. Remove those columns now
+ // so that we can rename properly.
+ db_drop_field('url_alias', 'source');
+ db_drop_field('url_alias', 'alias');
+
+ // Drop indexes.
+ db_drop_index('url_alias', 'src_language_pid');
+ db_drop_unique_key('url_alias', 'dst_language_pid');
+ // Rename the fields, and increase their length to 255 characters.
+ db_change_field('url_alias', 'src', 'source', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''));
+ db_change_field('url_alias', 'dst', 'alias', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''));
+ // Add indexes back. We replace the unique key with an index since it never
+ // provided any meaningful unique constraint ('pid' is a primary key).
+ db_add_index('url_alias', 'source_language_pid', array('source', 'language', 'pid'));
+ db_add_index('url_alias', 'alias_language_pid', array('alias', 'language', 'pid'));
+
+ // Now that the URL aliases are correct, we can rebuild the whitelist.
+ drupal_path_alias_whitelist_rebuild();
+}
+
+/**
+ * Drop the actions_aid table.
+ */
+function system_update_7044() {
+ // The current value of the increment has been taken into account when
+ // creating the sequences table in update_fix_d7_requirements().
+ db_drop_table('actions_aid');
+}
+
+/**
+ * Add expiration field to the {flood} table.
+ */
+function system_update_7045() {
+ db_add_field('flood', 'expiration', array('description' => 'Expiration timestamp. Expired events are purged on cron run.', 'type' => 'int', 'not null' => TRUE, 'default' => 0));
+ db_add_index('flood', 'purge', array('expiration'));
+}
+
+/**
+ * Switch from the Minnelli theme if it is the default or admin theme.
+ */
+function system_update_7046() {
+ if (variable_get('theme_default') == 'minnelli' || variable_get('admin_theme') == 'minnelli') {
+ // Make sure Garland is enabled.
+ db_update('system')
+ ->fields(array('status' => 1))
+ ->condition('type', 'theme')
+ ->condition('name', 'garland')
+ ->execute();
+ if (variable_get('theme_default') != 'garland') {
+ // If the default theme isn't Garland, transfer all of Minnelli's old
+ // settings to Garland.
+ $settings = variable_get('theme_minnelli_settings', array());
+ // Set the theme setting width to "fixed" to match Minnelli's old layout.
+ $settings['garland_width'] = 'fixed';
+ variable_set('theme_garland_settings', $settings);
+ // Remove Garland's color files since they won't match Minnelli's.
+ foreach (variable_get('color_garland_files', array()) as $file) {
+ @drupal_unlink($file);
+ }
+ if (isset($file) && $file = dirname($file)) {
+ @drupal_rmdir($file);
+ }
+ variable_del('color_garland_palette');
+ variable_del('color_garland_stylesheets');
+ variable_del('color_garland_logo');
+ variable_del('color_garland_files');
+ variable_del('color_garland_screenshot');
+ }
+ if (variable_get('theme_default') == 'minnelli') {
+ variable_set('theme_default', 'garland');
+ }
+ if (variable_get('admin_theme') == 'minnelli') {
+ variable_set('admin_theme', 'garland');
+ }
+ }
+}
+
+/**
+ * Normalize the front page path variable.
+ */
+function system_update_7047() {
+ variable_set('site_frontpage', drupal_get_normal_path(variable_get('site_frontpage', 'node')));
+}
+
+/**
+ * Convert path languages from the empty string to LANGUAGE_NONE.
+ */
+function system_update_7048() {
+ db_update('url_alias')
+ ->fields(array('language' => LANGUAGE_NONE))
+ ->condition('language', '')
+ ->execute();
+}
+
+/**
+ * Rename 'Default' profile to 'Standard.'
+ */
+function system_update_7049() {
+ if (variable_get('install_profile', 'standard') == 'default') {
+ variable_set('install_profile', 'standard');
+ }
+}
+
+/**
+ * Change {batch}.id column from serial to regular int.
+ */
+function system_update_7050() {
+ db_change_field('batch', 'bid', 'bid', array('description' => 'Primary Key: Unique batch ID.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE));
+}
+
+/**
+ * make the IP field IPv6 compatible
+ */
+function system_update_7051() {
+ db_change_field('blocked_ips', 'ip', 'ip', array('description' => 'IP address', 'type' => 'varchar', 'length' => 40, 'not null' => TRUE, 'default' => ''));
+}
+
+/**
+ * Rename file to include_file in {menu_router} table.
+ */
+function system_update_7052() {
+ db_change_field('menu_router', 'file', 'include_file', array('type' => 'text', 'size' => 'medium'));
+}
+
+/**
+ * Upgrade standard blocks and menus.
+ */
+function system_update_7053() {
+ if (db_table_exists('menu_custom')) {
+ // Create the same menus as in menu_install().
+ db_insert('menu_custom')
+ ->fields(array('menu_name' => 'user-menu', 'title' => 'User Menu', 'description' => "The <em>User</em> menu contains links related to the user's account, as well as the 'Log out' link."))
+ ->execute();
+
+ db_insert('menu_custom')
+ ->fields(array('menu_name' => 'management', 'title' => 'Management', 'description' => "The <em>Management</em> menu contains links for administrative tasks."))
+ ->execute();
+ }
+
+ block_flush_caches();
+
+ // Show the new menu blocks along the navigation block.
+ $blocks = db_query("SELECT theme, status, region, weight, visibility, pages FROM {block} WHERE module = 'system' AND delta = 'navigation'");
+ $deltas = db_or()
+ ->condition('delta', 'user-menu')
+ ->condition('delta', 'management');
+
+ foreach ($blocks as $block) {
+ db_update('block')
+ ->fields(array(
+ 'status' => $block->status,
+ 'region' => $block->region,
+ 'weight' => $block->weight,
+ 'visibility' => $block->visibility,
+ 'pages' => $block->pages,
+ ))
+ ->condition('theme', $block->theme)
+ ->condition('module', 'system')
+ ->condition($deltas)
+ ->execute();
+ }
+}
+
+/**
+ * Remove {cache_*}.headers columns.
+ */
+function system_update_7054() {
+ // Update: update_fix_d7_requirements() installs this version for cache_path
+ // already, so we don't include it in this particular update. It should be
+ // included in later updates though.
+ $cache_tables = array(
+ 'cache' => 'Generic cache table for caching things not separated out into their own tables. Contributed modules may also use this to store cached items.',
+ 'cache_form' => 'Cache table for the form system to store recently built forms and their storage data, to be used in subsequent page requests.',
+ 'cache_page' => 'Cache table used to store compressed pages for anonymous users, if page caching is enabled.',
+ 'cache_menu' => 'Cache table for the menu system to store router information as well as generated link trees for various menu/page/user combinations.',
+ );
+ $schema = system_schema_cache_7054();
+ foreach ($cache_tables as $table => $description) {
+ $schema['description'] = $description;
+ db_drop_table($table);
+ db_create_table($table, $schema);
+ }
+}
+
+/**
+ * Converts fields that store serialized variables from text to blob.
+ */
+function system_update_7055() {
+ $spec = array(
+ 'description' => 'The value of the variable.',
+ 'type' => 'blob',
+ 'not null' => TRUE,
+ 'size' => 'big',
+ 'translatable' => TRUE,
+ );
+ db_change_field('variable', 'value', 'value', $spec);
+
+ $spec = array(
+ 'description' => 'Parameters to be passed to the callback function.',
+ 'type' => 'blob',
+ 'not null' => TRUE,
+ 'size' => 'big',
+ );
+ db_change_field('actions', 'parameters', 'parameters', $spec);
+
+ $spec = array(
+ 'description' => 'A serialized array containing the processing data for the batch.',
+ 'type' => 'blob',
+ 'not null' => FALSE,
+ 'size' => 'big',
+ );
+ db_change_field('batch', 'batch', 'batch', $spec);
+
+ $spec = array(
+ 'description' => 'A serialized array of function names (like node_load) to be called to load an object corresponding to a part of the current path.',
+ 'type' => 'blob',
+ 'not null' => TRUE,
+ );
+ db_change_field('menu_router', 'load_functions', 'load_functions', $spec);
+
+ $spec = array(
+ 'description' => 'A serialized array of function names (like user_uid_optional_to_arg) to be called to replace a part of the router path with another string.',
+ 'type' => 'blob',
+ 'not null' => TRUE,
+ );
+ db_change_field('menu_router', 'to_arg_functions', 'to_arg_functions', $spec);
+
+ $spec = array(
+ 'description' => 'A serialized array of arguments for the access callback.',
+ 'type' => 'blob',
+ 'not null' => FALSE,
+ );
+ db_change_field('menu_router', 'access_arguments', 'access_arguments', $spec);
+
+ $spec = array(
+ 'description' => 'A serialized array of arguments for the page callback.',
+ 'type' => 'blob',
+ 'not null' => FALSE,
+ );
+ db_change_field('menu_router', 'page_arguments', 'page_arguments', $spec);
+
+ $spec = array(
+ 'description' => 'A serialized array of options to be passed to the url() or l() function, such as a query string or HTML attributes.',
+ 'type' => 'blob',
+ 'not null' => FALSE,
+ 'translatable' => TRUE,
+ );
+ db_change_field('menu_links', 'options', 'options', $spec);
+
+ $spec = array(
+ 'description' => 'The serialized contents of $_SESSION, an array of name/value pairs that persists across page requests by this session ID. Drupal loads $_SESSION from here at the start of each request and saves it at the end.',
+ 'type' => 'blob',
+ 'not null' => FALSE,
+ 'size' => 'big',
+ );
+ db_change_field('sessions', 'session', 'session', $spec);
+
+ $spec = array(
+ 'description' => "A serialized array containing information from the module's .info file; keys can include name, description, package, version, core, dependencies, and php.",
+ 'type' => 'blob',
+ 'not null' => FALSE,
+ );
+ db_change_field('system', 'info', 'info', $spec);
+}
+
+/**
+ * Increase the size of session-ids.
+ */
+function system_update_7057() {
+ $spec = array(
+ 'description' => "A session ID. The value is generated by PHP's Session API.",
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ );
+ db_change_field('sessions', 'sid', 'sid', $spec);
+}
+
+/**
+ * Remove cron semaphore variable.
+ */
+function system_update_7058() {
+ variable_del('cron_semaphore');
+}
+
+/**
+ * Create the {file_usage} table.
+ */
+function system_update_7059() {
+ $spec = array(
+ 'description' => 'Track where a file is used.',
+ 'fields' => array(
+ 'fid' => array(
+ 'description' => 'File ID.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ 'module' => array(
+ 'description' => 'The name of the module that is using the file.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'type' => array(
+ 'description' => 'The name of the object type in which the file is used.',
+ 'type' => 'varchar',
+ 'length' => 64,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'id' => array(
+ 'description' => 'The primary key of the object using the file.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'count' => array(
+ 'description' => 'The number of times this file is used by this object.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ ),
+ 'primary key' => array('fid', 'type', 'id', 'module'),
+ 'indexes' => array(
+ 'type_id' => array('type', 'id'),
+ 'fid_count' => array('fid', 'count'),
+ 'fid_module' => array('fid', 'module'),
+ ),
+ );
+ db_create_table('file_usage', $spec);
+}
+
+/**
+ * Create fields in preparation for migrating upload.module to file.module.
+ */
+function system_update_7060() {
+ if (!db_table_exists('upload')) {
+ return;
+ }
+
+ if (!db_query_range('SELECT 1 FROM {upload}', 0, 1)->fetchField()) {
+ // There is nothing to migrate. Delete variables and the empty table. There
+ // is no need to create fields that are not going to be used.
+ foreach (_update_7000_node_get_types() as $node_type) {
+ variable_del('upload_' . $node_type->type);
+ }
+ db_drop_table('upload');
+ return;
+ }
+
+ // Check which node types have upload.module attachments enabled.
+ $context['types'] = array();
+ foreach (_update_7000_node_get_types() as $node_type) {
+ if (variable_get('upload_' . $node_type->type, 0)) {
+ $context['types'][$node_type->type] = $node_type->type;
+ }
+ }
+
+ // The {upload} table will be deleted when this update is complete so we
+ // want to be careful to migrate all the data, even for node types that
+ // may have had attachments disabled after files were uploaded. Look for
+ // any other node types referenced by the upload records and add those to
+ // the list. The admin can always remove the field later.
+ $results = db_query('SELECT DISTINCT type FROM {node} n INNER JOIN {node_revision} nr ON n.nid = nr.nid INNER JOIN {upload} u ON nr.vid = u.vid');
+ foreach ($results as $row) {
+ if (!isset($context['types'][$row->type])) {
+ drupal_set_message(t('The content type %rowtype had uploads disabled but contained uploaded file data. Uploads have been re-enabled to migrate the existing data. You may delete the "File attachments" field in the %rowtype type if this data is not necessary.', array('%rowtype' => $row->type)));
+ $context['types'][$row->type] = $row->type;
+ }
+ }
+
+ // Create a single "upload" field on all the content types that have uploads
+ // enabled, then add an instance to each enabled type.
+ if (count($context['types']) > 0) {
+ module_enable(array('file'));
+ $field = array(
+ 'field_name' => 'upload',
+ 'type' => 'file',
+ 'module' => 'file',
+ 'locked' => FALSE,
+ 'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+ 'translatable' => FALSE,
+ 'settings' => array(
+ 'display_field' => 1,
+ 'display_default' => variable_get('upload_list_default', 1),
+ 'uri_scheme' => file_default_scheme(),
+ 'default_file' => 0,
+ ),
+ );
+
+ $upload_size = variable_get('upload_uploadsize_default', 1);
+ $instance = array(
+ 'field_name' => 'upload',
+ 'entity_type' => 'node',
+ 'bundle' => NULL,
+ 'label' => 'File attachments',
+ 'required' => 0,
+ 'description' => '',
+ 'widget' => array(
+ 'weight' => '1',
+ 'settings' => array(
+ 'progress_indicator' => 'throbber',
+ ),
+ 'type' => 'file_generic',
+ ),
+ 'settings' => array(
+ 'max_filesize' => $upload_size ? ($upload_size . ' MB') : '',
+ 'file_extensions' => variable_get('upload_extensions_default', 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp'),
+ 'file_directory' => '',
+ 'description_field' => 1,
+ ),
+ 'display' => array(
+ 'default' => array(
+ 'label' => 'hidden',
+ 'type' => 'file_table',
+ 'settings' => array(),
+ 'weight' => 0,
+ 'module' => 'file',
+ ),
+ 'full' => array(
+ 'label' => 'hidden',
+ 'type' => 'file_table',
+ 'settings' => array(),
+ 'weight' => 0,
+ 'module' => 'file',
+ ),
+ 'teaser' => array(
+ 'label' => 'hidden',
+ 'type' => 'hidden',
+ 'settings' => array(),
+ 'weight' => 0,
+ 'module' => NULL,
+ ),
+ 'rss' => array(
+ 'label' => 'hidden',
+ 'type' => 'file_table',
+ 'settings' => array(),
+ 'weight' => 0,
+ 'module' => 'file',
+ ),
+ ),
+ );
+
+ // Create the field.
+ _update_7000_field_create_field($field);
+
+ // Create the instances.
+ foreach ($context['types'] as $bundle) {
+ $instance['bundle'] = $bundle;
+ _update_7000_field_create_instance($field, $instance);
+ // Now that the instance is created, we can safely delete any legacy
+ // node type information.
+ variable_del('upload_' . $bundle);
+ }
+ }
+ else {
+ // No uploads or content types with uploads enabled.
+ db_drop_table('upload');
+ }
+}
+
+/**
+ * Migrate upload.module data to the newly created file field.
+ */
+function system_update_7061(&$sandbox) {
+ if (!db_table_exists('upload')) {
+ return;
+ }
+
+ if (!isset($sandbox['progress'])) {
+ // Delete stale rows from {upload} where the fid is not in the {files} table.
+ db_delete('upload')
+ ->notExists(
+ db_select('files', 'f')
+ ->fields('f', array('fid'))
+ ->where('f.fid = {upload}.fid')
+ )
+ ->execute();
+
+ // Delete stale rows from {upload} where the vid is not in the
+ // {node_revision} table. The table has already been renamed in
+ // node_update_7001().
+ db_delete('upload')
+ ->notExists(
+ db_select('node_revision', 'nr')
+ ->fields('nr', array('vid'))
+ ->where('nr.vid = {upload}.vid')
+ )
+ ->execute();
+
+ // Retrieve a list of node revisions that have uploaded files attached.
+ // DISTINCT queries are expensive, especially when paged, so we store the
+ // data in its own table for the duration of the update.
+ if (!db_table_exists('system_update_7061')) {
+ $table = array(
+ 'description' => t('Stores temporary data for system_update_7061.'),
+ 'fields' => array('vid' => array('type' => 'int')),
+ 'primary key' => array('vid'),
+ );
+ db_create_table('system_update_7061', $table);
+ }
+ $query = db_select('upload', 'u');
+ $query->distinct();
+ $query->addField('u','vid');
+ db_insert('system_update_7061')
+ ->from($query)
+ ->execute();
+
+ // Initialize batch update information.
+ $sandbox['progress'] = 0;
+ $sandbox['last_vid_processed'] = -1;
+ $sandbox['max'] = db_query("SELECT COUNT(*) FROM {system_update_7061}")->fetchField();
+ }
+
+ // Determine vids for this batch.
+ // Process all files attached to a given revision during the same batch.
+ $limit = variable_get('upload_update_batch_size', 100);
+ $vids = db_query_range('SELECT vid FROM {system_update_7061} WHERE vid > :lastvid ORDER BY vid', 0, $limit, array(':lastvid' => $sandbox['last_vid_processed']))
+ ->fetchCol();
+
+ // Retrieve information on all the files attached to these revisions.
+ if (!empty($vids)) {
+ $node_revisions = array();
+ $result = db_query('SELECT u.fid, u.vid, u.list, u.description, n.nid, n.type, u.weight FROM {upload} u INNER JOIN {node_revision} nr ON u.vid = nr.vid INNER JOIN {node} n ON n.nid = nr.nid WHERE u.vid IN (:vids) ORDER BY u.vid, u.weight, u.fid', array(':vids' => $vids));
+ foreach ($result as $record) {
+ // For each uploaded file, retrieve the corresponding data from the old
+ // files table (since upload doesn't know about the new entry in the
+ // file_managed table).
+ $file = db_select('files', 'f')
+ ->fields('f', array('fid', 'uid', 'filename', 'filepath', 'filemime', 'filesize', 'status', 'timestamp'))
+ ->condition('f.fid', $record->fid)
+ ->execute()
+ ->fetchAssoc();
+ if (!$file) {
+ continue;
+ }
+
+ // Add in the file information from the upload table.
+ $file['description'] = $record->description;
+ $file['display'] = $record->list;
+
+ // Create one record for each revision that contains all the uploaded
+ // files.
+ $node_revisions[$record->vid]['nid'] = $record->nid;
+ $node_revisions[$record->vid]['vid'] = $record->vid;
+ $node_revisions[$record->vid]['type'] = $record->type;
+ $node_revisions[$record->vid]['file'][LANGUAGE_NONE][] = $file;
+ }
+
+ // Now that we know which files belong to which revisions, update the
+ // files'// database entries, and save a reference to each file in the
+ // upload field on their node revisions.
+ $basename = variable_get('file_directory_path', conf_path() . '/files');
+ $scheme = file_default_scheme() . '://';
+ foreach ($node_revisions as $vid => $revision) {
+ foreach ($revision['file'][LANGUAGE_NONE] as $delta => $file) {
+ // We will convert filepaths to URI using the default scheme
+ // and stripping off the existing file directory path.
+ $file['uri'] = $scheme . preg_replace('!^' . preg_quote($basename) . '!', '', $file['filepath']);
+ $file['uri'] = file_stream_wrapper_uri_normalize($file['uri']);
+ unset($file['filepath']);
+ // Insert into the file_managed table.
+ // Each fid should only be stored once in file_managed.
+ db_merge('file_managed')
+ ->key(array(
+ 'fid' => $file['fid'],
+ ))
+ ->fields(array(
+ 'uid' => $file['uid'],
+ 'filename' => $file['filename'],
+ 'uri' => $file['uri'],
+ 'filemime' => $file['filemime'],
+ 'filesize' => $file['filesize'],
+ 'status' => $file['status'],
+ 'timestamp' => $file['timestamp'],
+ ))
+ ->execute();
+
+ // Add the usage entry for the file.
+ $file = (object) $file;
+ file_usage_add($file, 'file', 'node', $revision['nid']);
+
+ // Update the node revision's upload file field with the file data.
+ $revision['file'][LANGUAGE_NONE][$delta] = array('fid' => $file->fid, 'display' => $file->display, 'description' => $file->description);
+ }
+
+ // Write the revision's upload field data into the field_upload tables.
+ $node = (object) $revision;
+ _update_7000_field_sql_storage_write('node', $node->type, $node->nid, $node->vid, 'upload', $node->file);
+
+ // Update our progress information for the batch update.
+ $sandbox['progress']++;
+ $sandbox['last_vid_processed'] = $vid;
+ }
+ }
+
+ // If less than limit node revisions were processed, the update process is
+ // finished.
+ if (count($vids) < $limit) {
+ $finished = TRUE;
+ }
+
+ // If there's no max value then there's nothing to update and we're finished.
+ if (empty($sandbox['max']) || isset($finished)) {
+ db_drop_table('upload');
+ db_drop_table('system_update_7061');
+ return t('Upload module has been migrated to File module.');
+ }
+ else {
+ // Indicate our current progress to the batch update system.
+ $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
+ }
+}
+
+/**
+ * Replace 'system_list' index with 'bootstrap' index on {system}.
+ */
+function system_update_7062() {
+ db_drop_index('system', 'bootstrap');
+ db_drop_index('system', 'system_list');
+ db_add_index('system', 'system_list', array('status', 'bootstrap', 'type', 'weight', 'name'));
+}
+
+/**
+ * Delete {menu_links} records for 'type' => MENU_CALLBACK which would not appear in a fresh install.
+ */
+function system_update_7063() {
+ // For router items where 'type' => MENU_CALLBACK, {menu_router}.type is
+ // stored as 4 in Drupal 6, and 0 in Drupal 7. Fortunately Drupal 7 doesn't
+ // store any types as 4, so delete both.
+ $result = db_query('SELECT ml.mlid FROM {menu_links} ml INNER JOIN {menu_router} mr ON ml.router_path = mr.path WHERE ml.module = :system AND ml.customized = 0 AND mr.type IN(:callbacks)', array(':callbacks' => array(0, 4), ':system' => 'system'));
+ foreach ($result as $record) {
+ db_delete('menu_links')->condition('mlid', $record->mlid)->execute();
+ }
+}
+
+/**
+ * Remove block_callback field from {menu_router}.
+ */
+function system_update_7064() {
+ db_drop_field('menu_router', 'block_callback');
+}
+
+/**
+ * Remove the default value for sid.
+ */
+function system_update_7065() {
+ $spec = array(
+ 'description' => "A session ID. The value is generated by Drupal's session handlers.",
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ );
+ db_drop_primary_key('sessions');
+ db_change_field('sessions', 'sid', 'sid', $spec, array('primary key' => array('sid', 'ssid')));
+ // Delete any sessions with empty session ID.
+ db_delete('sessions')->condition('sid', '')->execute();
+}
+
+/**
+ * Migrate the 'file_directory_temp' variable.
+ */
+function system_update_7066() {
+ $d6_file_directory_temp = variable_get('file_directory_temp', file_directory_temp());
+ variable_set('file_temporary_path', $d6_file_directory_temp);
+ variable_del('file_directory_temp');
+}
+
+/**
+ * Grant administrators permission to view the administration theme.
+ */
+function system_update_7067() {
+ // Users with access to administration pages already see the administration
+ // theme in some places (if one is enabled on the site), so we want them to
+ // continue seeing it.
+ $admin_roles = user_roles(FALSE, 'access administration pages');
+ foreach (array_keys($admin_roles) as $rid) {
+ _update_7000_user_role_grant_permissions($rid, array('view the administration theme'), 'system');
+ }
+ // The above check is not guaranteed to reach all administrative users of the
+ // site, so if the site is currently using an administration theme, display a
+ // message also.
+ if (variable_get('admin_theme')) {
+ if (empty($admin_roles)) {
+ drupal_set_message('The new "View the administration theme" permission is required in order to view your site\'s administration theme. You can grant this permission to your site\'s administrators on the <a href="' . url('admin/people/permissions', array('fragment' => 'module-system')) . '">permissions page</a>.');
+ }
+ else {
+ drupal_set_message('The new "View the administration theme" permission is required in order to view your site\'s administration theme. This permission has been automatically granted to the following roles: <em>' . check_plain(implode(', ', $admin_roles)) . '</em>. You can grant this permission to other roles on the <a href="' . url('admin/people/permissions', array('fragment' => 'module-system')) . '">permissions page</a>.');
+ }
+ }
+}
+
+/**
+ * Update {url_alias}.language description.
+ */
+function system_update_7068() {
+ $spec = array(
+ 'description' => "The language this alias is for; if 'und', the alias will be used for unknown languages. Each Drupal path can have an alias for each supported language.",
+ 'type' => 'varchar',
+ 'length' => 12,
+ 'not null' => TRUE,
+ 'default' => '',
+ );
+ db_change_field('url_alias', 'language', 'language', $spec);
+}
+
+/**
+ * Remove the obsolete 'site_offline' variable.
+ *
+ * @see update_fix_d7_requirements()
+ */
+function system_update_7069() {
+ variable_del('site_offline');
+}
+
+/**
+ * @} End of "defgroup updates-6.x-to-7.x".
+ * The next series of updates should start at 8000.
+ */
+
+/**
+ * @defgroup updates-7.x-extra Extra updates for 7.x
+ * @{
+ * Update functions between 7.x versions.
+ */
+
+/**
+ * Remove the obsolete 'drupal_badge_color' and 'drupal_badge_size' variables.
+ */
+function system_update_7070() {
+ variable_del('drupal_badge_color');
+ variable_del('drupal_badge_size');
+}
+
+/**
+ * Add index missed during upgrade, and fix field default.
+ */
+function system_update_7071() {
+ db_drop_index('date_format_type', 'title');
+ db_add_index('date_format_type', 'title', array('title'));
+ db_change_field('registry', 'filename', 'filename', array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ ));
+}
+
+/**
+ * Remove the obsolete 'site_offline_message' variable.
+ *
+ * @see update_fix_d7_requirements()
+ */
+function system_update_7072() {
+ variable_del('site_offline_message');
+}
+
+/**
+ * Add binary to {file_managed}, in case system_update_7034() was run without
+ * it.
+ */
+function system_update_7073() {
+ db_change_field('file_managed', 'filename', 'filename', array(
+ 'description' => 'Name of the file with no path components. This may differ from the basename of the URI if the file is renamed to avoid overwriting an existing file.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'binary' => TRUE,
+ ));
+ db_drop_unique_key('file_managed', 'uri');
+ db_change_field('file_managed', 'uri', 'uri', array(
+ 'description' => 'The URI to access the file (either local or remote).',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'binary' => TRUE,
+ ));
+ db_add_unique_key('file_managed', 'uri', array('uri'));
+}
+
+/**
+ * This update has been removed and will not run.
+ */
+function system_update_7074() {
+ // This update function previously converted menu_links query strings to
+ // arrays. It has been removed for now due to incompatibility with
+ // PostgreSQL.
+}
+
+/**
+ * Convert menu_links query strings into arrays.
+ */
+function system_update_7076() {
+ $query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC))
+ ->fields('ml', array('mlid', 'options'));
+ foreach ($query->execute() as $menu_link) {
+ if (strpos($menu_link['options'], 'query') !== FALSE) {
+ $menu_link['options'] = unserialize($menu_link['options']);
+ if (isset($menu_link['options']['query']) && is_string($menu_link['options']['query'])) {
+ $menu_link['options']['query'] = drupal_get_query_array($menu_link['options']['query']);
+ db_update('menu_links')
+ ->fields(array(
+ 'options' => serialize($menu_link['options']),
+ ))
+ ->condition('mlid', $menu_link['mlid'], '=')
+ ->execute();
+ }
+ }
+ }
+}
+
+/**
+ * Revert {file_managed}.filename changed to a binary column.
+ */
+function system_update_7077() {
+ db_change_field('file_managed', 'filename', 'filename', array(
+ 'description' => 'Name of the file with no path components. This may differ from the basename of the URI if the file is renamed to avoid overwriting an existing file.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ));
+}
+
+
+/**
+ * Add binary to {date_formats}.format.
+ */
+function system_update_7078() {
+ db_drop_unique_key('date_formats', 'formats');
+ db_change_field('date_formats', 'format', 'format', array(
+ 'description' => 'The date format string.',
+ 'type' => 'varchar',
+ 'length' => 100,
+ 'not null' => TRUE,
+ 'binary' => TRUE,
+ ), array('unique keys' => array('formats' => array('format', 'type'))));
+}
+
+/**
+ * Convert the 'filesize' column in {file_managed} to a bigint.
+ */
+function system_update_7079() {
+ $spec = array(
+ 'description' => 'The size of the file in bytes.',
+ 'type' => 'int',
+ 'size' => 'big',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ );
+ db_change_field('file_managed', 'filesize', 'filesize', $spec);
+}
+
+/**
+ * @} End of "defgroup updates-7.x-extra".
+ * The next series of updates should start at 8000.
+ */
diff --git a/kolab.org/www/drupal-7.26/modules/system/system.js b/kolab.org/www/drupal-7.26/modules/system/system.js
new file mode 100644
index 0000000..910fb5d
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/system.js
@@ -0,0 +1,140 @@
+(function ($) {
+
+/**
+ * Show/hide the 'Email site administrator when updates are available' checkbox
+ * on the install page.
+ */
+Drupal.hideEmailAdministratorCheckbox = function () {
+ // Make sure the secondary box is shown / hidden as necessary on page load.
+ if ($('#edit-update-status-module-1').is(':checked')) {
+ $('.form-item-update-status-module-2').show();
+ }
+ else {
+ $('.form-item-update-status-module-2').hide();
+ }
+
+ // Toggle the display as necessary when the checkbox is clicked.
+ $('#edit-update-status-module-1').change( function () {
+ $('.form-item-update-status-module-2').toggle();
+ });
+};
+
+/**
+ * Internal function to check using Ajax if clean URLs can be enabled on the
+ * settings page.
+ *
+ * This function is not used to verify whether or not clean URLs
+ * are currently enabled.
+ */
+Drupal.behaviors.cleanURLsSettingsCheck = {
+ attach: function (context, settings) {
+ // This behavior attaches by ID, so is only valid once on a page.
+ // Also skip if we are on an install page, as Drupal.cleanURLsInstallCheck will handle
+ // the processing.
+ if (!($('#edit-clean-url').length) || $('#edit-clean-url.install').once('clean-url').length) {
+ return;
+ }
+ var url = settings.basePath + 'admin/config/search/clean-urls/check';
+ $.ajax({
+ url: location.protocol + '//' + location.host + url,
+ dataType: 'json',
+ success: function () {
+ // Check was successful. Redirect using a "clean URL". This will force the form that allows enabling clean URLs.
+ location = settings.basePath +"admin/config/search/clean-urls";
+ }
+ });
+ }
+};
+
+/**
+ * Internal function to check using Ajax if clean URLs can be enabled on the
+ * install page.
+ *
+ * This function is not used to verify whether or not clean URLs
+ * are currently enabled.
+ */
+Drupal.cleanURLsInstallCheck = function () {
+ var url = location.protocol + '//' + location.host + Drupal.settings.basePath + 'admin/config/search/clean-urls/check';
+ // Submit a synchronous request to avoid database errors associated with
+ // concurrent requests during install.
+ $.ajax({
+ async: false,
+ url: url,
+ dataType: 'json',
+ success: function () {
+ // Check was successful.
+ $('#edit-clean-url').attr('value', 1);
+ }
+ });
+};
+
+/**
+ * When a field is filled out, apply its value to other fields that will likely
+ * use the same value. In the installer this is used to populate the
+ * administrator e-mail address with the same value as the site e-mail address.
+ */
+Drupal.behaviors.copyFieldValue = {
+ attach: function (context, settings) {
+ for (var sourceId in settings.copyFieldValue) {
+ $('#' + sourceId, context).once('copy-field-values').bind('blur', function () {
+ // Get the list of target fields.
+ var targetIds = settings.copyFieldValue[sourceId];
+ // Add the behavior to update target fields on blur of the primary field.
+ for (var delta in targetIds) {
+ var targetField = $('#' + targetIds[delta]);
+ if (targetField.val() == '') {
+ targetField.val(this.value);
+ }
+ }
+ });
+ }
+ }
+};
+
+/**
+ * Show/hide custom format sections on the regional settings page.
+ */
+Drupal.behaviors.dateTime = {
+ attach: function (context, settings) {
+ for (var fieldName in settings.dateTime) {
+ if (settings.dateTime.hasOwnProperty(fieldName)) {
+ (function (fieldSettings, fieldName) {
+ var source = '#edit-' + fieldName;
+ var suffix = source + '-suffix';
+
+ // Attach keyup handler to custom format inputs.
+ $('input' + source, context).once('date-time').keyup(function () {
+ var input = $(this);
+ var url = fieldSettings.lookup + (/\?q=/.test(fieldSettings.lookup) ? '&format=' : '?format=') + encodeURIComponent(input.val());
+ $.getJSON(url, function (data) {
+ $(suffix).empty().append(' ' + fieldSettings.text + ': <em>' + data + '</em>');
+ });
+ });
+ })(settings.dateTime[fieldName], fieldName);
+ }
+ }
+ }
+};
+
+ /**
+ * Show/hide settings for page caching depending on whether page caching is
+ * enabled or not.
+ */
+Drupal.behaviors.pageCache = {
+ attach: function (context, settings) {
+ $('#edit-cache-0', context).change(function () {
+ $('#page-compression-wrapper').hide();
+ $('#cache-error').hide();
+ });
+ $('#edit-cache-1', context).change(function () {
+ $('#page-compression-wrapper').show();
+ $('#cache-error').hide();
+ });
+ $('#edit-cache-2', context).change(function () {
+ $('#page-compression-wrapper').show();
+ $('#cache-error').show();
+ });
+ }
+};
+
+})(jQuery);
diff --git a/kolab.org/www/drupal-7.26/modules/system/system.mail.inc b/kolab.org/www/drupal-7.26/modules/system/system.mail.inc
new file mode 100644
index 0000000..443e574
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/system.mail.inc
@@ -0,0 +1,133 @@
+<?php
+
+/**
+ * @file
+ * Drupal core implementations of MailSystemInterface.
+ */
+
+/**
+ * The default Drupal mail backend using PHP's mail function.
+ */
+class DefaultMailSystem implements MailSystemInterface {
+ /**
+ * Concatenate and wrap the e-mail body for plain-text mails.
+ *
+ * @param $message
+ * A message array, as described in hook_mail_alter().
+ *
+ * @return
+ * The formatted $message.
+ */
+ public function format(array $message) {
+ // Join the body array into one string.
+ $message['body'] = implode("\n\n", $message['body']);
+ // Convert any HTML to plain-text.
+ $message['body'] = drupal_html_to_text($message['body']);
+ // Wrap the mail body for sending.
+ $message['body'] = drupal_wrap_mail($message['body']);
+ return $message;
+ }
+
+ /**
+ * Send an e-mail message, using Drupal variables and default settings.
+ *
+ * @see http://php.net/manual/function.mail.php
+ * @see drupal_mail()
+ *
+ * @param $message
+ * A message array, as described in hook_mail_alter().
+ * @return
+ * TRUE if the mail was successfully accepted, otherwise FALSE.
+ */
+ public function mail(array $message) {
+ // If 'Return-Path' isn't already set in php.ini, we pass it separately
+ // as an additional parameter instead of in the header.
+ // However, if PHP's 'safe_mode' is on, this is not allowed.
+ if (isset($message['headers']['Return-Path']) && !ini_get('safe_mode')) {
+ $return_path_set = strpos(ini_get('sendmail_path'), ' -f');
+ if (!$return_path_set) {
+ $message['Return-Path'] = $message['headers']['Return-Path'];
+ unset($message['headers']['Return-Path']);
+ }
+ }
+ $mimeheaders = array();
+ foreach ($message['headers'] as $name => $value) {
+ $mimeheaders[] = $name . ': ' . mime_header_encode($value);
+ }
+ $line_endings = variable_get('mail_line_endings', MAIL_LINE_ENDINGS);
+ // Prepare mail commands.
+ $mail_subject = mime_header_encode($message['subject']);
+ // Note: e-mail uses CRLF for line-endings. PHP's API requires LF
+ // on Unix and CRLF on Windows. Drupal automatically guesses the
+ // line-ending format appropriate for your system. If you need to
+ // override this, adjust $conf['mail_line_endings'] in settings.php.
+ $mail_body = preg_replace('@\r?\n@', $line_endings, $message['body']);
+ // For headers, PHP's API suggests that we use CRLF normally,
+ // but some MTAs incorrectly replace LF with CRLF. See #234403.
+ $mail_headers = join("\n", $mimeheaders);
+
+ // We suppress warnings and notices from mail() because of issues on some
+ // hosts. The return value of this method will still indicate whether mail
+ // was sent successfully.
+ if (!isset($_SERVER['WINDIR']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Win32') === FALSE) {
+ if (isset($message['Return-Path']) && !ini_get('safe_mode')) {
+ // On most non-Windows systems, the "-f" option to the sendmail command
+ // is used to set the Return-Path. There is no space between -f and
+ // the value of the return path.
+ $mail_result = @mail(
+ $message['to'],
+ $mail_subject,
+ $mail_body,
+ $mail_headers,
+ '-f' . $message['Return-Path']
+ );
+ }
+ else {
+ // The optional $additional_parameters argument to mail() is not
+ // allowed if safe_mode is enabled. Passing any value throws a PHP
+ // warning and makes mail() return FALSE.
+ $mail_result = @mail(
+ $message['to'],
+ $mail_subject,
+ $mail_body,
+ $mail_headers
+ );
+ }
+ }
+ else {
+ // On Windows, PHP will use the value of sendmail_from for the
+ // Return-Path header.
+ $old_from = ini_get('sendmail_from');
+ ini_set('sendmail_from', $message['Return-Path']);
+ $mail_result = @mail(
+ $message['to'],
+ $mail_subject,
+ $mail_body,
+ $mail_headers
+ );
+ ini_set('sendmail_from', $old_from);
+ }
+ return $mail_result;
+ }
+}
+
+/**
+ * A mail sending implementation that captures sent messages to a variable.
+ *
+ * This class is for running tests or for development.
+ */
+class TestingMailSystem extends DefaultMailSystem implements MailSystemInterface {
+ /**
+ * Accept an e-mail message and store it in a variable.
+ *
+ * @param $message
+ * An e-mail message.
+ */
+ public function mail(array $message) {
+ $captured_emails = variable_get('drupal_test_email_collector', array());
+ $captured_emails[] = $message;
+ variable_set('drupal_test_email_collector', $captured_emails);
+ return TRUE;
+ }
+}
+
diff --git a/kolab.org/www/drupal-7.26/modules/system/system.maintenance.css b/kolab.org/www/drupal-7.26/modules/system/system.maintenance.css
new file mode 100644
index 0000000..5543c2d
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/system.maintenance.css
@@ -0,0 +1,55 @@
+
+/**
+ * Update styles
+ */
+#update-results {
+ margin-top: 3em;
+ padding: 0.25em;
+ border: 1px solid #ccc;
+ background: #eee;
+ font-size: smaller;
+}
+#update-results h2 {
+ margin-top: 0.25em;
+}
+#update-results h4 {
+ margin-bottom: 0.25em;
+}
+#update-results li.none {
+ color: #888;
+ font-style: italic;
+}
+#update-results li.failure strong {
+ color: #b63300;
+}
+
+/**
+ * Authorize.php styles
+ */
+.connection-settings-update-filetransfer-default-wrapper {
+ float: left;
+}
+#edit-submit-connection {
+ clear: both;
+}
+.filetransfer {
+ display: none;
+ clear: both;
+}
+#edit-connection-settings-change-connection-type {
+ margin: 2.6em 0.5em 0em 1em;
+}
+
+/**
+ * Installation task list
+ */
+ol.task-list li.active {
+ font-weight: bold;
+}
+
+/**
+ * Installation clean URLs
+ */
+#clean-url.install {
+ display: none;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/system/system.menus-rtl.css b/kolab.org/www/drupal-7.26/modules/system/system.menus-rtl.css
new file mode 100644
index 0000000..be85245
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/system.menus-rtl.css
@@ -0,0 +1,37 @@
+
+/**
+ * @file
+ * RTL styles for menus and navigation markup.
+ */
+
+ul.menu {
+ text-align:right;
+}
+ul.menu li {
+ margin: 0 0.5em 0 0;
+}
+ul li.collapsed {
+ list-style-image: url(../../misc/menu-collapsed-rtl.png);
+}
+li.expanded,
+li.collapsed,
+li.leaf {
+ padding: 0.2em 0 0 0.5em;
+}
+
+/**
+ * Markup generated by theme_menu_local_tasks().
+ */
+ul.primary {
+ padding: 0 1em 0 0;
+}
+ul.primary li a {
+ margin-right: 5px;
+ margin-left: 0.5em;
+}
+ul.secondary li {
+ border-left: 1px solid #ccc;
+ border-right: none;
+ display: inline;
+ padding: 0 1em;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/system/system.menus.css b/kolab.org/www/drupal-7.26/modules/system/system.menus.css
new file mode 100644
index 0000000..514b029
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/system.menus.css
@@ -0,0 +1,116 @@
+
+/**
+ * @file
+ * Styles for menus and navigation markup.
+ */
+
+/**
+ * Markup generated by theme_menu_tree().
+ */
+ul.menu {
+ border: none;
+ list-style: none;
+ text-align: left; /* LTR */
+}
+ul.menu li {
+ margin: 0 0 0 0.5em; /* LTR */
+}
+ul li.expanded {
+ list-style-image: url(../../misc/menu-expanded.png);
+ list-style-type: circle;
+}
+ul li.collapsed {
+ list-style-image: url(../../misc/menu-collapsed.png); /* LTR */
+ list-style-type: disc;
+}
+ul li.leaf {
+ list-style-image: url(../../misc/menu-leaf.png);
+ list-style-type: square;
+}
+li.expanded,
+li.collapsed,
+li.leaf {
+ padding: 0.2em 0.5em 0 0; /* LTR */
+ margin: 0;
+}
+li a.active {
+ color: #000;
+}
+td.menu-disabled {
+ background: #ccc;
+}
+
+/**
+ * Markup generated by theme_links().
+ */
+ul.inline,
+ul.links.inline {
+ display: inline;
+ padding-left: 0;
+}
+ul.inline li {
+ display: inline;
+ list-style-type: none;
+ padding: 0 0.5em;
+}
+
+/**
+ * Markup generated by theme_breadcrumb().
+ */
+.breadcrumb {
+ padding-bottom: 0.5em;
+}
+
+/**
+ * Markup generated by theme_menu_local_tasks().
+ */
+ul.primary {
+ border-bottom: 1px solid #bbb;
+ border-collapse: collapse;
+ height: auto;
+ line-height: normal;
+ list-style: none;
+ margin: 5px;
+ padding: 0 0 0 1em; /* LTR */
+ white-space: nowrap;
+}
+ul.primary li {
+ display: inline;
+}
+ul.primary li a {
+ background-color: #ddd;
+ border-color: #bbb;
+ border-style: solid solid none solid;
+ border-width: 1px;
+ height: auto;
+ margin-right: 0.5em; /* LTR */
+ padding: 0 1em;
+ text-decoration: none;
+}
+ul.primary li.active a {
+ background-color: #fff;
+ border: 1px solid #bbb;
+ border-bottom: 1px solid #fff;
+}
+ul.primary li a:hover {
+ background-color: #eee;
+ border-color: #ccc;
+ border-bottom-color: #eee;
+}
+ul.secondary {
+ border-bottom: 1px solid #bbb;
+ padding: 0.5em 1em;
+ margin: 5px;
+}
+ul.secondary li {
+ border-right: 1px solid #ccc; /* LTR */
+ display: inline;
+ padding: 0 1em;
+}
+ul.secondary a {
+ padding: 0;
+ text-decoration: none;
+}
+ul.secondary a.active {
+ border-bottom: 4px solid #999;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/system/system.messages-rtl.css b/kolab.org/www/drupal-7.26/modules/system/system.messages-rtl.css
new file mode 100644
index 0000000..445417b
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/system.messages-rtl.css
@@ -0,0 +1,13 @@
+
+/**
+ * @file
+ * RTL Styles for system messages.
+ */
+
+div.messages {
+ background-position: 99% 8px;
+ padding: 10px 50px 10px 10px;
+}
+div.messages ul {
+ margin: 0 1em 0 0;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/system/system.messages.css b/kolab.org/www/drupal-7.26/modules/system/system.messages.css
new file mode 100644
index 0000000..ffd4e8e
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/system.messages.css
@@ -0,0 +1,63 @@
+
+/**
+ * @file
+ * Styles for system messages.
+ */
+
+div.messages {
+ background-position: 8px 8px; /* LTR */
+ background-repeat: no-repeat;
+ border: 1px solid;
+ margin: 6px 0;
+ padding: 10px 10px 10px 50px; /* LTR */
+}
+
+div.status {
+ background-image: url(../../misc/message-24-ok.png);
+ border-color: #be7;
+}
+div.status,
+.ok {
+ color: #234600;
+}
+div.status,
+table tr.ok {
+ background-color: #f8fff0;
+}
+
+div.warning {
+ background-image: url(../../misc/message-24-warning.png);
+ border-color: #ed5;
+}
+div.warning,
+.warning {
+ color: #840;
+}
+div.warning,
+table tr.warning {
+ background-color: #fffce5;
+}
+
+div.error {
+ background-image: url(../../misc/message-24-error.png);
+ border-color: #ed541d;
+}
+div.error,
+.error {
+ color: #8c2e0b;
+}
+div.error,
+table tr.error {
+ background-color: #fef5f1;
+}
+div.error p.error {
+ color: #333;
+}
+
+div.messages ul {
+ margin: 0 0 0 1em; /* LTR */
+ padding: 0;
+}
+div.messages ul li {
+ list-style-image: none;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/system/system.module b/kolab.org/www/drupal-7.26/modules/system/system.module
new file mode 100644
index 0000000..eada9ff
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/system/system.module
@@ -0,0 +1,4021 @@
+<?php
+
+/**
+ * @file
+ * Configuration system that lets administrators modify the workings of the site.
+ */
+
+/**
+ * Maximum age of temporary files in seconds.
+ */
+define('DRUPAL_MAXIMUM_TEMP_FILE_AGE', 21600);
+
+/**
+ * Default interval for automatic cron executions in seconds.
+ */
+define('DRUPAL_CRON_DEFAULT_THRESHOLD', 10800);
+
+/**
+ * New users will be set to the default time zone at registration.
+ */
+define('DRUPAL_USER_TIMEZONE_DEFAULT', 0);
+
+/**
+ * New users will get an empty time zone at registration.
+ */
+define('DRUPAL_USER_TIMEZONE_EMPTY', 1);
+
+/**
+ * New users will select their own timezone at registration.
+ */
+define('DRUPAL_USER_TIMEZONE_SELECT', 2);
+
+ /**
+ * Disabled option on forms and settings
+ */
+define('DRUPAL_DISABLED', 0);
+
+/**
+ * Optional option on forms and settings
+ */
+define('DRUPAL_OPTIONAL', 1);
+
+/**
+ * Required option on forms and settings
+ */
+define('DRUPAL_REQUIRED', 2);
+
+/**
+ * Maximum number of values in a weight select element.
+ *
+ * If the number of values is over the maximum, a text field is used instead.
+ */
+define('DRUPAL_WEIGHT_SELECT_MAX', 100);
+
+/**
+ * Return only visible regions.
+ *
+ * @see system_region_list()
+ */
+define('REGIONS_VISIBLE', 'visible');
+
+/**
+ * Return all regions.
+ *
+ * @see system_region_list()
+ */
+define('REGIONS_ALL', 'all');
+
+/**
+ * Implements hook_help().
+ */
+function system_help($path, $arg) {
+ global $base_url;
+
+ switch ($path) {
+ case 'admin/help#system':
+ $output = '';
+ $output .= '<h3>' . t('About') . '</h3>';
+ $output .= '<p>' . t('The System module is integral to the site, and provides basic but extensible functionality for use by other modules and themes. Some integral elements of Drupal are contained in and managed by the System module, including caching, enabling and disabling modules and themes, preparing and displaying the administrative page, and configuring fundamental site settings. A number of key system maintenance operations are also part of the System module. For more information, see the online handbook entry for <a href="@system">System module</a>.', array('@system' => 'http://drupal.org/documentation/modules/system')) . '</p>';
+ $output .= '<h3>' . t('Uses') . '</h3>';
+ $output .= '<dl>';
+ $output .= '<dt>' . t('Managing modules') . '</dt>';
+ $output .= '<dd>' . t('The System module allows users with the appropriate permissions to enable and disable modules on the <a href="@modules">Modules administration page</a>. Drupal comes with a number of core modules, and each module provides a discrete set of features and may be enabled or disabled depending on the needs of the site. Many additional modules contributed by members of the Drupal community are available for download at the <a href="@drupal-modules">Drupal.org module page</a>.', array('@modules' => url('admin/modules'), '@drupal-modules' => 'http://drupal.org/project/modules')) . '</dd>';
+ $output .= '<dt>' . t('Managing themes') . '</dt>';
+ $output .= '<dd>' . t('The System module allows users with the appropriate permissions to enable and disable themes on the <a href="@themes">Appearance administration page</a>. Themes determine the design and presentation of your site. Drupal comes packaged with several core themes, and additional contributed themes are available at the <a href="@drupal-themes">Drupal.org theme page</a>.', array('@themes' => url('admin/appearance'), '@drupal-themes' => 'http://drupal.org/project/themes')) . '</dd>';
+ $output .= '<dt>' . t('Managing caching') . '</dt>';
+ $output .= '<dd>' . t("The System module allows users with the appropriate permissions to manage caching on the <a href='@cache-settings'>Performance settings page</a>. Drupal has a robust caching system that allows the efficient re-use of previously-constructed web pages and web page components. Pages requested by anonymous users are stored in a compressed format; depending on your site configuration and the amount of your web traffic tied to anonymous visitors, the caching system may significantly increase the speed of your site.", array('@cache-settings' => url('admin/config/development/performance'))) . '</dd>';
+ $output .= '<dt>' . t('Performing system maintenance') . '</dt>';
+ $output .= '<dd>' . t('In order for the site and its modules to continue to operate well, a set of routine administrative operations must run on a regular basis. The System module manages this task by making use of a system cron job. You can verify the status of cron tasks by visiting the <a href="@status">Status report page</a>. For more information, see the online handbook entry for <a href="@handbook">configuring cron jobs</a>. You can set up cron job by visiting <a href="@cron">Cron configuration</a> page', array('@status' => url('admin/reports/status'), '@handbook' => 'http://drupal.org/cron', '@cron' => url('admin/config/system/cron'))) . '</dd>';
+ $output .= '<dt>' . t('Configuring basic site settings') . '</dt>';
+ $output .= '<dd>' . t('The System module also handles basic configuration options for your site, including <a href="@date-time-settings">Date and time settings</a>, <a href="@file-system">File system settings</a>, <a href="@clean-url">Clean URL support</a>, <a href="@site-info">Site name and other information</a>, and a <a href="@maintenance-mode">Maintenance mode</a> for taking your site temporarily offline.', array('@date-time-settings' => url('admin/config/regional/date-time'), '@file-system' => url('admin/config/media/file-system'), '@clean-url' => url('admin/config/search/clean-urls'), '@site-info' => url('admin/config/system/site-information'), '@maintenance-mode' => url('admin/config/development/maintenance'))) . '</dd>';
+ $output .= '<dt>' . t('Configuring actions') . '</dt>';
+ $output .= '<dd>' . t('Actions are individual tasks that the system can do, such as unpublishing a piece of content or banning a user. Modules, such as the <a href="@trigger-help">Trigger module</a>, can fire these actions when certain system events happen; for example, when a new post is added or when a user logs in. Modules may also provide additional actions. Visit the <a href="@actions">Actions page</a> to configure actions.', array('@trigger-help' => url('admin/help/trigger'), '@actions' => url('admin/config/system/actions'))) . '</dd>';
+ $output .= '</dl>';
+ return $output;
+ case 'admin/index':
+ return '<p>' . t('This page shows you all available administration tasks for each module.') . '</p>';
+ case 'admin/appearance':
+ $output = '<p>' . t('Set and configure the default theme for your website. Alternative <a href="@themes">themes</a> are available.', array('@themes' => 'http://drupal.org/project/themes')) . '</p>';
+ return $output;
+ case 'admin/appearance/settings/' . $arg[3]:
+ $theme_list = list_themes();
+ $theme = $theme_list[$arg[3]];
+ return '<p>' . t('These options control the display settings for the %name theme. When your site is displayed using this theme, these settings will be used.', array('%name' => $theme->info['name'])) . '</p>';
+ case 'admin/appearance/settings':
+ return '<p>' . t('These options control the default display settings for your entire site, across all themes. Unless they have been overridden by a specific theme, these settings will be used.') . '</p>';
+ case 'admin/modules':
+ $output = '<p>' . t('Download additional <a href="@modules">contributed modules</a> to extend Drupal\'s functionality.', array('@modules' => 'http://drupal.org/project/modules')) . '</p>';
+ if (module_exists('update')) {
+ if (update_manager_access()) {
+ $output .= '<p>' . t('Regularly review and install <a href="@updates">available updates</a> to maintain a secure and current site. Always run the <a href="@update-php">update script</a> each time a module is updated.', array('@update-php' => $base_url . '/update.php', '@updates' => url('admin/reports/updates'))) . '</p>';
+ }
+ else {
+ $output .= '<p>' . t('Regularly review <a href="@updates">available updates</a> to maintain a secure and current site. Always run the <a href="@update-php">update script</a> each time a module is updated.', array('@update-php' => $base_url . '/update.php', '@updates' => url('admin/reports/updates'))) . '</p>';
+ }
+ }
+ else {
+ $output .= '<p>' . t('Regularly review available updates to maintain a secure and current site. Always run the <a href="@update-php">update script</a> each time a module is updated. Enable the Update manager module to update and install modules and themes.', array('@update-php' => $base_url . '/update.php')) . '</p>';
+ }
+ return $output;
+ case 'admin/modules/uninstall':
+ return '<p>' . t('The uninstall process removes all data related to a module. To uninstall a module, you must first disable it on the main <a href="@modules">Modules page</a>.', array('@modules' => url('admin/modules'))) . '</p>';
+ case 'admin/structure/block/manage':
+ if ($arg[4] == 'system' && $arg[5] == 'powered-by') {
+ return '<p>' . t('The <em>Powered by Drupal</em> block is an optional link to the home page of the Drupal project. While there is absolutely no requirement that sites feature this link, it may be used to show support for Drupal.') . '</p>';
+ }
+ break;
+ case 'admin/config/development/maintenance':
+ global $user;
+ if ($user->uid == 1) {
+ return '<p>' . t('If you are upgrading to a newer version of Drupal or upgrading contributed modules or themes, you may need to run the <a href="@update-php">update script</a>.', array('@update-php' => $base_url . '/update.php')) . '</p>';
+ }
+ break;
+ case 'admin/config/system/actions':
+ case 'admin/config/system/actions/manage':
+ $output = '';
+ $output .= '<p>' . t('There are two types of actions: simple and advanced. Simple actions do not require any additional configuration, and are listed here automatically. Advanced actions need to be created and configured before they can be used, because they have options that need to be specified; for example, sending an e-mail to a specified address, or unpublishing content containing certain words. To create an advanced action, select the action from the drop-down list in the advanced action section below and click the <em>Create</em> button.') . '</p>';
+ if (module_exists('trigger')) {
+ $output .= '<p>' . t('You may proceed to the <a href="@url">Triggers</a> page to assign these actions to system events.', array('@url' => url('admin/structure/trigger'))) . '</p>';
+ }
+ return $output;
+ case 'admin/config/system/actions/configure':
+ return t('An advanced action offers additional configuration options which may be filled out below. Changing the <em>Description</em> field is recommended, in order to better identify the precise action taking place. This description will be displayed in modules such as the Trigger module when assigning actions to system events, so it is best if it is as descriptive as possible (for example, "Send e-mail to Moderation Team" rather than simply "Send e-mail").');
+ case 'admin/config/people/ip-blocking':
+ return '<p>' . t('IP addresses listed here are blocked from your site. Blocked addresses are completely forbidden from accessing the site and instead see a brief message explaining the situation.') . '</p>';
+ case 'admin/reports/status':
+ return '<p>' . t("Here you can find a short overview of your site's parameters as well as any problems detected with your installation. It may be useful to copy and paste this information into support requests filed on drupal.org's support forums and project issue queues.") . '</p>';
+ }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function system_theme() {
+ return array_merge(drupal_common_theme(), array(
+ 'system_themes_page' => array(
+ 'variables' => array('theme_groups' => NULL),
+ 'file' => 'system.admin.inc',
+ ),
+ 'system_settings_form' => array(
+ 'render element' => 'form',
+ ),
+ 'confirm_form' => array(
+ 'render element' => 'form',
+ ),
+ 'system_modules_fieldset' => array(
+ 'render element' => 'form',
+ 'file' => 'system.admin.inc',
+ ),
+ 'system_modules_incompatible' => array(
+ 'variables' => array('message' => NULL),
+ 'file' => 'system.admin.inc',
+ ),
+ 'system_modules_uninstall' => array(
+ 'render element' => 'form',
+ 'file' => 'system.admin.inc',
+ ),
+ 'status_report' => array(
+ 'render element' => 'requirements',
+ 'file' => 'system.admin.inc',
+ ),
+ 'admin_page' => array(
+ 'variables' => array('blocks' => NULL),
+ 'file' => 'system.admin.inc',
+ ),
+ 'admin_block' => array(
+ 'variables' => array('block' => NULL),
+ 'file' => 'system.admin.inc',
+ ),
+ 'admin_block_content' => array(
+ 'variables' => array('content' => NULL),
+ 'file' => 'system.admin.inc',
+ ),
+ 'system_admin_index' => array(
+ 'variables' => array('menu_items' => NULL),
+ 'file' => 'system.admin.inc',
+ ),
+ 'system_powered_by' => array(
+ 'variables' => array(),
+ ),
+ 'system_compact_link' => array(
+ 'variables' => array(),
+ ),
+ 'system_date_time_settings' => array(
+ 'render element' => 'form',
+ 'file' => 'system.admin.inc',
+ ),
+ ));
+}
+
+/**
+ * Implements hook_permission().
+ */
+function system_permission() {
+ return array(
+ 'administer modules' => array(
+ 'title' => t('Administer modules'),
+ ),
+ 'administer site configuration' => array(
+ 'title' => t('Administer site configuration'),
+ 'restrict access' => TRUE,
+ ),
+ 'administer themes' => array(
+ 'title' => t('Administer themes'),
+ ),
+ 'administer software updates' => array(
+ 'title' => t('Administer software updates'),
+ 'restrict access' => TRUE,
+ ),
+ 'administer actions' => array(
+ 'title' => t('Administer actions'),
+ ),
+ 'access administration pages' => array(
+ 'title' => t('Use the administration pages and help'),
+ ),
+ 'access site in maintenance mode' => array(
+ 'title' => t('Use the site in maintenance mode'),
+ ),
+ 'view the administration theme' => array(
+ 'title' => t('View the administration theme'),
+ 'description' => variable_get('admin_theme') ? '' : t('This is only used when the site is configured to use a separate administration theme on the <a href="@appearance-url">Appearance</a> page.', array('@appearance-url' => url('admin/appearance'))),
+ ),
+ 'access site reports' => array(
+ 'title' => t('View site reports'),
+ ),
+ 'block IP addresses' => array(
+ 'title' => t('Block IP addresses'),
+ ),
+ );
+}
+
+/**
+ * Implements hook_hook_info().
+ */
+function system_hook_info() {
+ $hooks['token_info'] = array(
+ 'group' => 'tokens',
+ );
+ $hooks['token_info_alter'] = array(
+ 'group' => 'tokens',
+ );
+ $hooks['tokens'] = array(
+ 'group' => 'tokens',
+ );
+ $hooks['tokens_alter'] = array(
+ 'group' => 'tokens',
+ );
+
+ return $hooks;
+}
+
+/**
+ * Implements hook_entity_info().
+ */
+function system_entity_info() {
+ return array(
+ 'file' => array(
+ 'label' => t('File'),
+ 'base table' => 'file_managed',
+ 'entity keys' => array(
+ 'id' => 'fid',
+ 'label' => 'filename',
+ ),
+ 'static cache' => FALSE,
+ ),
+ );
+}
+
+/**
+ * Implements hook_element_info().
+ */
+function system_element_info() {
+ // Top level elements.
+ $types['form'] = array(
+ '#method' => 'post',
+ '#action' => request_uri(),
+ '#theme_wrappers' => array('form'),
+ );
+ $types['page'] = array(
+ '#show_messages' => TRUE,
+ '#theme' => 'page',
+ '#theme_wrappers' => array('html'),
+ );
+ // By default, we don't want Ajax commands being rendered in the context of an
+ // HTML page, so we don't provide defaults for #theme or #theme_wrappers.
+ // However, modules can set these properties (for example, to provide an HTML
+ // debugging page that displays rather than executes Ajax commands).
+ $types['ajax'] = array(
+ '#header' => TRUE,
+ '#commands' => array(),
+ '#error' => NULL,
+ );
+ $types['html_tag'] = array(
+ '#theme' => 'html_tag',
+ '#pre_render' => array('drupal_pre_render_conditional_comments'),
+ '#attributes' => array(),
+ '#value' => NULL,
+ );
+ $types['styles'] = array(
+ '#items' => array(),
+ '#pre_render' => array('drupal_pre_render_styles'),
+ '#group_callback' => 'drupal_group_css',
+ '#aggregate_callback' => 'drupal_aggregate_css',
+ );
+
+ // Input elements.
+ $types['submit'] = array(
+ '#input' => TRUE,
+ '#name' => 'op',
+ '#button_type' => 'submit',
+ '#executes_submit_callback' => TRUE,
+ '#limit_validation_errors' => FALSE,
+ '#process' => array('ajax_process_form'),
+ '#theme_wrappers' => array('button'),
+ );
+ $types['button'] = array(
+ '#input' => TRUE,
+ '#name' => 'op',
+ '#button_type' => 'submit',
+ '#executes_submit_callback' => FALSE,
+ '#limit_validation_errors' => FALSE,
+ '#process' => array('ajax_process_form'),
+ '#theme_wrappers' => array('button'),
+ );
+ $types['image_button'] = array(
+ '#input' => TRUE,
+ '#button_type' => 'submit',
+ '#executes_submit_callback' => TRUE,
+ '#limit_validation_errors' => FALSE,
+ '#process' => array('ajax_process_form'),
+ '#return_value' => TRUE,
+ '#has_garbage_value' => TRUE,
+ '#src' => NULL,
+ '#theme_wrappers' => array('image_button'),
+ );
+ $types['textfield'] = array(
+ '#input' => TRUE,
+ '#size' => 60,
+ '#maxlength' => 128,
+ '#autocomplete_path' => FALSE,
+ '#process' => array('ajax_process_form'),
+ '#theme' => 'textfield',
+ '#theme_wrappers' => array('form_element'),
+ );
+ $types['machine_name'] = array(
+ '#input' => TRUE,
+ '#default_value' => NULL,
+ '#required' => TRUE,
+ '#maxlength' => 64,
+ '#size' => 60,
+ '#autocomplete_path' => FALSE,
+ '#process' => array('form_process_machine_name', 'ajax_process_form'),
+ '#element_validate' => array('form_validate_machine_name'),
+ '#theme' => 'textfield',
+ '#theme_wrappers' => array('form_element'),
+ );
+ $types['password'] = array(
+ '#input' => TRUE,
+ '#size' => 60,
+ '#maxlength' => 128,
+ '#process' => array('ajax_process_form'),
+ '#theme' => 'password',
+ '#theme_wrappers' => array('form_element'),
+ );
+ $types['password_confirm'] = array(
+ '#input' => TRUE,
+ '#process' => array('form_process_password_confirm', 'user_form_process_password_confirm'),
+ '#theme_wrappers' => array('form_element'),
+ );
+ $types['textarea'] = array(
+ '#input' => TRUE,
+ '#cols' => 60,
+ '#rows' => 5,
+ '#resizable' => TRUE,
+ '#process' => array('ajax_process_form'),
+ '#theme' => 'textarea',
+ '#theme_wrappers' => array('form_element'),
+ );
+ $types['radios'] = array(
+ '#input' => TRUE,
+ '#process' => array('form_process_radios'),
+ '#theme_wrappers' => array('radios'),
+ '#pre_render' => array('form_pre_render_conditional_form_element'),
+ );
+ $types['radio'] = array(
+ '#input' => TRUE,
+ '#default_value' => NULL,
+ '#process' => array('ajax_process_form'),
+ '#theme' => 'radio',
+ '#theme_wrappers' => array('form_element'),
+ '#title_display' => 'after',
+ );
+ $types['checkboxes'] = array(
+ '#input' => TRUE,
+ '#process' => array('form_process_checkboxes'),
+ '#theme_wrappers' => array('checkboxes'),
+ '#pre_render' => array('form_pre_render_conditional_form_element'),
+ );
+ $types['checkbox'] = array(
+ '#input' => TRUE,
+ '#return_value' => 1,
+ '#theme' => 'checkbox',
+ '#process' => array('form_process_checkbox', 'ajax_process_form'),
+ '#theme_wrappers' => array('form_element'),
+ '#title_display' => 'after',
+ );
+ $types['select'] = array(
+ '#input' => TRUE,
+ '#multiple' => FALSE,
+ '#process' => array('form_process_select', 'ajax_process_form'),
+ '#theme' => 'select',
+ '#theme_wrappers' => array('form_element'),
+ );
+ $types['weight'] = array(
+ '#input' => TRUE,
+ '#delta' => 10,
+ '#default_value' => 0,
+ '#process' => array('form_process_weight', 'ajax_process_form'),
+ );
+ $types['date'] = array(
+ '#input' => TRUE,
+ '#element_validate' => array('date_validate'),
+ '#process' => array('form_process_date'),
+ '#theme' => 'date',
+ '#theme_wrappers' => array('form_element'),
+ );
+ $types['file'] = array(
+ '#input' => TRUE,
+ '#size' => 60,
+ '#theme' => 'file',
+ '#theme_wrappers' => array('form_element'),
+ );
+ $types['tableselect'] = array(
+ '#input' => TRUE,
+ '#js_select' => TRUE,
+ '#multiple' => TRUE,
+ '#process' => array('form_process_tableselect'),
+ '#options' => array(),
+ '#empty' => '',
+ '#theme' => 'tableselect',
+ );
+
+ // Form structure.
+ $types['item'] = array(
+ '#markup' => '',
+ '#pre_render' => array('drupal_pre_render_markup'),
+ '#theme_wrappers' => array('form_element'),
+ );
+ $types['hidden'] = array(
+ '#input' => TRUE,
+ '#process' => array('ajax_process_form'),
+ '#theme' => 'hidden',
+ );
+ $types['value'] = array(
+ '#input' => TRUE,
+ );
+ $types['markup'] = array(
+ '#markup' => '',
+ '#pre_render' => array('drupal_pre_render_markup'),
+ );
+ $types['link'] = array(
+ '#pre_render' => array('drupal_pre_render_link', 'drupal_pre_render_markup'),
+ );
+ $types['fieldset'] = array(
+ '#collapsible' => FALSE,
+ '#collapsed' => FALSE,
+ '#value' => NULL,
+ '#process' => array('form_process_fieldset', 'ajax_process_form'),
+ '#pre_render' => array('form_pre_render_fieldset'),
+ '#theme_wrappers' => array('fieldset'),
+ );
+ $types['vertical_tabs'] = array(
+ '#theme_wrappers' => array('vertical_tabs'),
+ '#default_tab' => '',
+ '#process' => array('form_process_vertical_tabs'),
+ );
+
+ $types['container'] = array(
+ '#theme_wrappers' => array('container'),
+ '#process' => array('form_process_container'),
+ );
+ $types['actions'] = array(
+ '#theme_wrappers' => array('container'),
+ '#process' => array('form_process_actions', 'form_process_container'),
+ '#weight' => 100,
+ );
+
+ $types['token'] = array(
+ '#input' => TRUE,
+ '#theme' => 'hidden',
+ );
+
+ return $types;
+}
+
+/**
+ * Implements hook_menu().
+ */
+function system_menu() {
+ $items['system/files'] = array(
+ 'title' => 'File download',
+ 'page callback' => 'file_download',
+ 'page arguments' => array('private'),
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+ $items['system/temporary'] = array(
+ 'title' => 'Temporary files',
+ 'page callback' => 'file_download',
+ 'page arguments' => array('temporary'),
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+ $items['system/ajax'] = array(
+ 'title' => 'AHAH callback',
+ 'page callback' => 'ajax_form_callback',
+ 'delivery callback' => 'ajax_deliver',
+ 'access callback' => TRUE,
+ 'theme callback' => 'ajax_base_page_theme',
+ 'type' => MENU_CALLBACK,
+ 'file path' => 'includes',
+ 'file' => 'form.inc',
+ );
+ $items['system/timezone'] = array(
+ 'title' => 'Time zone',
+ 'page callback' => 'system_timezone',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin'] = array(
+ 'title' => 'Administration',
+ 'access arguments' => array('access administration pages'),
+ 'page callback' => 'system_admin_menu_block_page',
+ 'weight' => 9,
+ 'menu_name' => 'management',
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/compact'] = array(
+ 'title' => 'Compact mode',
+ 'page callback' => 'system_admin_compact_page',
+ 'access arguments' => array('access administration pages'),
+ 'type' => MENU_CALLBACK,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/tasks'] = array(
+ 'title' => 'Tasks',
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ 'weight' => -20,
+ );
+ $items['admin/index'] = array(
+ 'title' => 'Index',
+ 'page callback' => 'system_admin_index',
+ 'access arguments' => array('access administration pages'),
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => -18,
+ 'file' => 'system.admin.inc',
+ );
+
+ // Menu items that are basically just menu blocks.
+ $items['admin/structure'] = array(
+ 'title' => 'Structure',
+ 'description' => 'Administer blocks, content types, menus, etc.',
+ 'position' => 'right',
+ 'weight' => -8,
+ 'page callback' => 'system_admin_menu_block_page',
+ 'access arguments' => array('access administration pages'),
+ 'file' => 'system.admin.inc',
+ );
+ // Appearance.
+ $items['admin/appearance'] = array(
+ 'title' => 'Appearance',
+ 'description' => 'Select and configure your themes.',
+ 'page callback' => 'system_themes_page',
+ 'access arguments' => array('administer themes'),
+ 'position' => 'left',
+ 'weight' => -6,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/appearance/list'] = array(
+ 'title' => 'List',
+ 'description' => 'Select and configure your theme',
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ 'weight' => -1,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/appearance/enable'] = array(
+ 'title' => 'Enable theme',
+ 'page callback' => 'system_theme_enable',
+ 'access arguments' => array('administer themes'),
+ 'type' => MENU_CALLBACK,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/appearance/disable'] = array(
+ 'title' => 'Disable theme',
+ 'page callback' => 'system_theme_disable',
+ 'access arguments' => array('administer themes'),
+ 'type' => MENU_CALLBACK,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/appearance/default'] = array(
+ 'title' => 'Set default theme',
+ 'page callback' => 'system_theme_default',
+ 'access arguments' => array('administer themes'),
+ 'type' => MENU_CALLBACK,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/appearance/settings'] = array(
+ 'title' => 'Settings',
+ 'description' => 'Configure default and theme specific settings.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_theme_settings'),
+ 'access arguments' => array('administer themes'),
+ 'type' => MENU_LOCAL_TASK,
+ 'file' => 'system.admin.inc',
+ 'weight' => 20,
+ );
+ // Theme configuration subtabs.
+ $items['admin/appearance/settings/global'] = array(
+ 'title' => 'Global settings',
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ 'weight' => -1,
+ );
+
+ foreach (list_themes() as $theme) {
+ $items['admin/appearance/settings/' . $theme->name] = array(
+ 'title' => $theme->info['name'],
+ 'page arguments' => array('system_theme_settings', $theme->name),
+ 'type' => MENU_LOCAL_TASK,
+ 'access callback' => '_system_themes_access',
+ 'access arguments' => array($theme),
+ 'file' => 'system.admin.inc',
+ );
+ }
+
+ // Modules.
+ $items['admin/modules'] = array(
+ 'title' => 'Modules',
+ 'description' => 'Extend site functionality.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_modules'),
+ 'access arguments' => array('administer modules'),
+ 'file' => 'system.admin.inc',
+ 'weight' => -2,
+ );
+ $items['admin/modules/list'] = array(
+ 'title' => 'List',
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ );
+ $items['admin/modules/list/confirm'] = array(
+ 'title' => 'List',
+ 'access arguments' => array('administer modules'),
+ 'type' => MENU_VISIBLE_IN_BREADCRUMB,
+ );
+ $items['admin/modules/uninstall'] = array(
+ 'title' => 'Uninstall',
+ 'page arguments' => array('system_modules_uninstall'),
+ 'access arguments' => array('administer modules'),
+ 'type' => MENU_LOCAL_TASK,
+ 'file' => 'system.admin.inc',
+ 'weight' => 20,
+ );
+ $items['admin/modules/uninstall/confirm'] = array(
+ 'title' => 'Uninstall',
+ 'access arguments' => array('administer modules'),
+ 'type' => MENU_VISIBLE_IN_BREADCRUMB,
+ 'file' => 'system.admin.inc',
+ );
+
+ // Configuration.
+ $items['admin/config'] = array(
+ 'title' => 'Configuration',
+ 'description' => 'Administer settings.',
+ 'page callback' => 'system_admin_config_page',
+ 'access arguments' => array('access administration pages'),
+ 'file' => 'system.admin.inc',
+ );
+
+ // IP address blocking.
+ $items['admin/config/people/ip-blocking'] = array(
+ 'title' => 'IP address blocking',
+ 'description' => 'Manage blocked IP addresses.',
+ 'page callback' => 'system_ip_blocking',
+ 'access arguments' => array('block IP addresses'),
+ 'file' => 'system.admin.inc',
+ 'weight' => 10,
+ );
+ $items['admin/config/people/ip-blocking/delete/%blocked_ip'] = array(
+ 'title' => 'Delete IP address',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_ip_blocking_delete', 5),
+ 'access arguments' => array('block IP addresses'),
+ 'file' => 'system.admin.inc',
+ );
+
+ // Media settings.
+ $items['admin/config/media'] = array(
+ 'title' => 'Media',
+ 'description' => 'Media tools.',
+ 'position' => 'left',
+ 'weight' => -10,
+ 'page callback' => 'system_admin_menu_block_page',
+ 'access arguments' => array('access administration pages'),
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/media/file-system'] = array(
+ 'title' => 'File system',
+ 'description' => 'Tell Drupal where to store uploaded files and how they are accessed.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_file_system_settings'),
+ 'access arguments' => array('administer site configuration'),
+ 'weight' => -10,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/media/image-toolkit'] = array(
+ 'title' => 'Image toolkit',
+ 'description' => 'Choose which image toolkit to use if you have installed optional toolkits.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_image_toolkit_settings'),
+ 'access arguments' => array('administer site configuration'),
+ 'weight' => 20,
+ 'file' => 'system.admin.inc',
+ );
+
+ // Service settings.
+ $items['admin/config/services'] = array(
+ 'title' => 'Web services',
+ 'description' => 'Tools related to web services.',
+ 'position' => 'right',
+ 'weight' => 0,
+ 'page callback' => 'system_admin_menu_block_page',
+ 'access arguments' => array('access administration pages'),
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/services/rss-publishing'] = array(
+ 'title' => 'RSS publishing',
+ 'description' => 'Configure the site description, the number of items per feed and whether feeds should be titles/teasers/full-text.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_rss_feeds_settings'),
+ 'access arguments' => array('administer site configuration'),
+ 'file' => 'system.admin.inc',
+ );
+
+ // Development settings.
+ $items['admin/config/development'] = array(
+ 'title' => 'Development',
+ 'description' => 'Development tools.',
+ 'position' => 'right',
+ 'weight' => -10,
+ 'page callback' => 'system_admin_menu_block_page',
+ 'access arguments' => array('access administration pages'),
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/development/maintenance'] = array(
+ 'title' => 'Maintenance mode',
+ 'description' => 'Take the site offline for maintenance or bring it back online.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_site_maintenance_mode'),
+ 'access arguments' => array('administer site configuration'),
+ 'file' => 'system.admin.inc',
+ 'weight' => -10,
+ );
+ $items['admin/config/development/performance'] = array(
+ 'title' => 'Performance',
+ 'description' => 'Enable or disable page caching for anonymous users and set CSS and JS bandwidth optimization options.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_performance_settings'),
+ 'access arguments' => array('administer site configuration'),
+ 'file' => 'system.admin.inc',
+ 'weight' => -20,
+ );
+ $items['admin/config/development/logging'] = array(
+ 'title' => 'Logging and errors',
+ 'description' => "Settings for logging and alerts modules. Various modules can route Drupal's system events to different destinations, such as syslog, database, email, etc.",
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_logging_settings'),
+ 'access arguments' => array('administer site configuration'),
+ 'file' => 'system.admin.inc',
+ 'weight' => -15,
+ );
+
+ // Regional and date settings.
+ $items['admin/config/regional'] = array(
+ 'title' => 'Regional and language',
+ 'description' => 'Regional settings, localization and translation.',
+ 'position' => 'left',
+ 'weight' => -5,
+ 'page callback' => 'system_admin_menu_block_page',
+ 'access arguments' => array('access administration pages'),
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/regional/settings'] = array(
+ 'title' => 'Regional settings',
+ 'description' => "Settings for the site's default time zone and country.",
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_regional_settings'),
+ 'access arguments' => array('administer site configuration'),
+ 'weight' => -20,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/regional/date-time'] = array(
+ 'title' => 'Date and time',
+ 'description' => 'Configure display formats for date and time.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_date_time_settings'),
+ 'access arguments' => array('administer site configuration'),
+ 'weight' => -15,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/regional/date-time/types'] = array(
+ 'title' => 'Types',
+ 'description' => 'Configure display formats for date and time.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_date_time_settings'),
+ 'access arguments' => array('administer site configuration'),
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ 'weight' => -10,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/regional/date-time/types/add'] = array(
+ 'title' => 'Add date type',
+ 'description' => 'Add new date type.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_add_date_format_type_form'),
+ 'access arguments' => array('administer site configuration'),
+ 'type' => MENU_LOCAL_ACTION,
+ 'weight' => -10,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/regional/date-time/types/%/delete'] = array(
+ 'title' => 'Delete date type',
+ 'description' => 'Allow users to delete a configured date type.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_delete_date_format_type_form', 5),
+ 'access arguments' => array('administer site configuration'),
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/regional/date-time/formats'] = array(
+ 'title' => 'Formats',
+ 'description' => 'Configure display format strings for date and time.',
+ 'page callback' => 'system_date_time_formats',
+ 'access arguments' => array('administer site configuration'),
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => -9,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/regional/date-time/formats/add'] = array(
+ 'title' => 'Add format',
+ 'description' => 'Allow users to add additional date formats.',
+ 'type' => MENU_LOCAL_ACTION,
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_configure_date_formats_form'),
+ 'access arguments' => array('administer site configuration'),
+ 'weight' => -10,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/regional/date-time/formats/%/edit'] = array(
+ 'title' => 'Edit date format',
+ 'description' => 'Allow users to edit a configured date format.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_configure_date_formats_form', 5),
+ 'access arguments' => array('administer site configuration'),
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/regional/date-time/formats/%/delete'] = array(
+ 'title' => 'Delete date format',
+ 'description' => 'Allow users to delete a configured date format.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_date_delete_format_form', 5),
+ 'access arguments' => array('administer site configuration'),
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/regional/date-time/formats/lookup'] = array(
+ 'title' => 'Date and time lookup',
+ 'page callback' => 'system_date_time_lookup',
+ 'access arguments' => array('administer site configuration'),
+ 'type' => MENU_CALLBACK,
+ 'file' => 'system.admin.inc',
+ );
+
+ // Search settings.
+ $items['admin/config/search'] = array(
+ 'title' => 'Search and metadata',
+ 'description' => 'Local site search, metadata and SEO.',
+ 'position' => 'left',
+ 'weight' => -10,
+ 'page callback' => 'system_admin_menu_block_page',
+ 'access arguments' => array('access administration pages'),
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/search/clean-urls'] = array(
+ 'title' => 'Clean URLs',
+ 'description' => 'Enable or disable clean URLs for your site.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_clean_url_settings'),
+ 'access arguments' => array('administer site configuration'),
+ 'file' => 'system.admin.inc',
+ 'weight' => 5,
+ );
+ $items['admin/config/search/clean-urls/check'] = array(
+ 'title' => 'Clean URL check',
+ 'page callback' => 'drupal_json_output',
+ 'page arguments' => array(array('status' => TRUE)),
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ 'file' => 'system.admin.inc',
+ );
+
+ // System settings.
+ $items['admin/config/system'] = array(
+ 'title' => 'System',
+ 'description' => 'General system related configuration.',
+ 'position' => 'right',
+ 'weight' => -20,
+ 'page callback' => 'system_admin_menu_block_page',
+ 'access arguments' => array('access administration pages'),
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/system/actions'] = array(
+ 'title' => 'Actions',
+ 'description' => 'Manage the actions defined for your site.',
+ 'access arguments' => array('administer actions'),
+ 'page callback' => 'system_actions_manage',
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/system/actions/manage'] = array(
+ 'title' => 'Manage actions',
+ 'description' => 'Manage the actions defined for your site.',
+ 'page callback' => 'system_actions_manage',
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ 'weight' => -2,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/system/actions/configure'] = array(
+ 'title' => 'Configure an advanced action',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_actions_configure'),
+ 'access arguments' => array('administer actions'),
+ 'type' => MENU_VISIBLE_IN_BREADCRUMB,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/system/actions/delete/%actions'] = array(
+ 'title' => 'Delete action',
+ 'description' => 'Delete an action.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_actions_delete_form', 5),
+ 'access arguments' => array('administer actions'),
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/system/actions/orphan'] = array(
+ 'title' => 'Remove orphans',
+ 'page callback' => 'system_actions_remove_orphans',
+ 'access arguments' => array('administer actions'),
+ 'type' => MENU_CALLBACK,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/system/site-information'] = array(
+ 'title' => 'Site information',
+ 'description' => 'Change site name, e-mail address, slogan, default front page, and number of posts per page, error pages.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_site_information_settings'),
+ 'access arguments' => array('administer site configuration'),
+ 'file' => 'system.admin.inc',
+ 'weight' => -20,
+ );
+ $items['admin/config/system/cron'] = array(
+ 'title' => 'Cron',
+ 'description' => 'Manage automatic site maintenance tasks.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_cron_settings'),
+ 'access arguments' => array('administer site configuration'),
+ 'file' => 'system.admin.inc',
+ 'weight' => 20,
+ );
+ // Additional categories
+ $items['admin/config/user-interface'] = array(
+ 'title' => 'User interface',
+ 'description' => 'Tools that enhance the user interface.',
+ 'position' => 'right',
+ 'page callback' => 'system_admin_menu_block_page',
+ 'access arguments' => array('access administration pages'),
+ 'file' => 'system.admin.inc',
+ 'weight' => -15,
+ );
+ $items['admin/config/workflow'] = array(
+ 'title' => 'Workflow',
+ 'description' => 'Content workflow, editorial workflow tools.',
+ 'position' => 'right',
+ 'weight' => 5,
+ 'page callback' => 'system_admin_menu_block_page',
+ 'access arguments' => array('access administration pages'),
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/config/content'] = array(
+ 'title' => 'Content authoring',
+ 'description' => 'Settings related to formatting and authoring content.',
+ 'position' => 'left',
+ 'weight' => -15,
+ 'page callback' => 'system_admin_menu_block_page',
+ 'access arguments' => array('access administration pages'),
+ 'file' => 'system.admin.inc',
+ );
+
+ // Reports.
+ $items['admin/reports'] = array(
+ 'title' => 'Reports',
+ 'description' => 'View reports, updates, and errors.',
+ 'page callback' => 'system_admin_menu_block_page',
+ 'access arguments' => array('access site reports'),
+ 'weight' => 5,
+ 'position' => 'left',
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/reports/status'] = array(
+ 'title' => 'Status report',
+ 'description' => "Get a status report about your site's operation and any detected problems.",
+ 'page callback' => 'system_status',
+ 'weight' => -60,
+ 'access arguments' => array('administer site configuration'),
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/reports/status/run-cron'] = array(
+ 'title' => 'Run cron',
+ 'page callback' => 'system_run_cron',
+ 'access arguments' => array('administer site configuration'),
+ 'type' => MENU_CALLBACK,
+ 'file' => 'system.admin.inc',
+ );
+ $items['admin/reports/status/php'] = array(
+ 'title' => 'PHP',
+ 'page callback' => 'system_php',
+ 'access arguments' => array('administer site configuration'),
+ 'type' => MENU_CALLBACK,
+ 'file' => 'system.admin.inc',
+ );
+
+ // Default page for batch operations.
+ $items['batch'] = array(
+ 'page callback' => 'system_batch_page',
+ 'access callback' => TRUE,
+ 'theme callback' => '_system_batch_theme',
+ 'type' => MENU_CALLBACK,
+ 'file' => 'system.admin.inc',
+ );
+ return $items;
+}
+
+/**
+ * Theme callback for the default batch page.
+ */
+function _system_batch_theme() {
+ // Retrieve the current state of the batch.
+ $batch = &batch_get();
+ if (!$batch && isset($_REQUEST['id'])) {
+ require_once DRUPAL_ROOT . '/includes/batch.inc';
+ $batch = batch_load($_REQUEST['id']);
+ }
+ // Use the same theme as the page that started the batch.
+ if (!empty($batch['theme'])) {
+ return $batch['theme'];
+ }
+}
+
+/**
+ * Implements hook_library().
+ */
+function system_library() {
+ // Drupal's Ajax framework.
+ $libraries['drupal.ajax'] = array(
+ 'title' => 'Drupal AJAX',
+ 'website' => 'http://api.drupal.org/api/drupal/includes--ajax.inc/group/ajax/7',
+ 'version' => VERSION,
+ 'js' => array(
+ 'misc/ajax.js' => array('group' => JS_LIBRARY, 'weight' => 2),
+ ),
+ 'dependencies' => array(
+ array('system', 'drupal.progress'),
+ ),
+ );
+
+ // Drupal's batch API.
+ $libraries['drupal.batch'] = array(
+ 'title' => 'Drupal batch API',
+ 'version' => VERSION,
+ 'js' => array(
+ 'misc/batch.js' => array('group' => JS_DEFAULT, 'cache' => FALSE),
+ ),
+ 'dependencies' => array(
+ array('system', 'drupal.progress'),
+ ),
+ );
+
+ // Drupal's progress indicator.
+ $libraries['drupal.progress'] = array(
+ 'title' => 'Drupal progress indicator',
+ 'version' => VERSION,
+ 'js' => array(
+ 'misc/progress.js' => array('group' => JS_DEFAULT),
+ ),
+ );
+
+ // Drupal's form library.
+ $libraries['drupal.form'] = array(
+ 'title' => 'Drupal form library',
+ 'version' => VERSION,
+ 'js' => array(
+ 'misc/form.js' => array('group' => JS_LIBRARY, 'weight' => 1),
+ ),
+ );
+
+ // Drupal's states library.
+ $libraries['drupal.states'] = array(
+ 'title' => 'Drupal states',
+ 'version' => VERSION,
+ 'js' => array(
+ 'misc/states.js' => array('group' => JS_LIBRARY, 'weight' => 1),
+ ),
+ );
+
+ // Drupal's collapsible fieldset.
+ $libraries['drupal.collapse'] = array(
+ 'title' => 'Drupal collapsible fieldset',
+ 'version' => VERSION,
+ 'js' => array(
+ 'misc/collapse.js' => array('group' => JS_DEFAULT),
+ ),
+ 'dependencies' => array(
+ // collapse.js relies on drupalGetSummary in form.js
+ array('system', 'drupal.form'),
+ ),
+ );
+
+ // Drupal's resizable textarea.
+ $libraries['drupal.textarea'] = array(
+ 'title' => 'Drupal resizable textarea',
+ 'version' => VERSION,
+ 'js' => array(
+ 'misc/textarea.js' => array('group' => JS_DEFAULT),
+ ),
+ );
+
+ // Drupal's autocomplete widget.
+ $libraries['drupal.autocomplete'] = array(
+ 'title' => 'Drupal autocomplete',
+ 'version' => VERSION,
+ 'js' => array(
+ 'misc/autocomplete.js' => array('group' => JS_DEFAULT),
+ ),
+ );
+
+ // jQuery.
+ $libraries['jquery'] = array(
+ 'title' => 'jQuery',
+ 'website' => 'http://jquery.com',
+ 'version' => '1.4.4',
+ 'js' => array(
+ 'misc/jquery.js' => array('group' => JS_LIBRARY, 'weight' => -20),
+ ),
+ );
+
+ // jQuery Once.
+ $libraries['jquery.once'] = array(
+ 'title' => 'jQuery Once',
+ 'website' => 'http://plugins.jquery.com/project/once',
+ 'version' => '1.2',
+ 'js' => array(
+ 'misc/jquery.once.js' => array('group' => JS_LIBRARY, 'weight' => -19),
+ ),
+ );
+
+ // jQuery Form Plugin.
+ $libraries['jquery.form'] = array(
+ 'title' => 'jQuery Form Plugin',
+ 'website' => 'http://malsup.com/jquery/form/',
+ 'version' => '2.52',
+ 'js' => array(
+ 'misc/jquery.form.js' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'jquery.cookie'),
+ ),
+ );
+
+ // jQuery BBQ plugin.
+ $libraries['jquery.bbq'] = array(
+ 'title' => 'jQuery BBQ',
+ 'website' => 'http://benalman.com/projects/jquery-bbq-plugin/',
+ 'version' => '1.2.1',
+ 'js' => array(
+ 'misc/jquery.ba-bbq.js' => array(),
+ ),
+ );
+
+ // Vertical Tabs.
+ $libraries['drupal.vertical-tabs'] = array(
+ 'title' => 'Vertical Tabs',
+ 'website' => 'http://drupal.org/node/323112',
+ 'version' => '1.0',
+ 'js' => array(
+ 'misc/vertical-tabs.js' => array(),
+ ),
+ 'css' => array(
+ 'misc/vertical-tabs.css' => array(),
+ ),
+ 'dependencies' => array(
+ // Vertical tabs relies on drupalGetSummary in form.js
+ array('system', 'drupal.form'),
+ ),
+ );
+
+ // Farbtastic.
+ $libraries['farbtastic'] = array(
+ 'title' => 'Farbtastic',
+ 'website' => 'http://code.google.com/p/farbtastic/',
+ 'version' => '1.2',
+ 'js' => array(
+ 'misc/farbtastic/farbtastic.js' => array(),
+ ),
+ 'css' => array(
+ 'misc/farbtastic/farbtastic.css' => array(),
+ ),
+ );
+
+ // Cookie.
+ $libraries['jquery.cookie'] = array(
+ 'title' => 'Cookie',
+ 'website' => 'http://plugins.jquery.com/project/cookie',
+ 'version' => '1.0',
+ 'js' => array(
+ 'misc/jquery.cookie.js' => array(),
+ ),
+ );
+
+ // jQuery UI.
+ $libraries['ui'] = array(
+ 'title' => 'jQuery UI: Core',
+ 'website' => 'http://jqueryui.com',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.ui.core.min.js' => array('group' => JS_LIBRARY, 'weight' => -11),
+ ),
+ 'css' => array(
+ 'misc/ui/jquery.ui.core.css' => array(),
+ 'misc/ui/jquery.ui.theme.css' => array(),
+ ),
+ );
+ $libraries['ui.accordion'] = array(
+ 'title' => 'jQuery UI: Accordion',
+ 'website' => 'http://jqueryui.com/demos/accordion/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.ui.accordion.min.js' => array(),
+ ),
+ 'css' => array(
+ 'misc/ui/jquery.ui.accordion.css' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'ui.widget'),
+ ),
+ );
+ $libraries['ui.autocomplete'] = array(
+ 'title' => 'jQuery UI: Autocomplete',
+ 'website' => 'http://jqueryui.com/demos/autocomplete/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.ui.autocomplete.min.js' => array(),
+ ),
+ 'css' => array(
+ 'misc/ui/jquery.ui.autocomplete.css' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'ui.widget'),
+ array('system', 'ui.position'),
+ ),
+ );
+ $libraries['ui.button'] = array(
+ 'title' => 'jQuery UI: Button',
+ 'website' => 'http://jqueryui.com/demos/button/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.ui.button.min.js' => array(),
+ ),
+ 'css' => array(
+ 'misc/ui/jquery.ui.button.css' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'ui.widget'),
+ ),
+ );
+ $libraries['ui.datepicker'] = array(
+ 'title' => 'jQuery UI: Date Picker',
+ 'website' => 'http://jqueryui.com/demos/datepicker/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.ui.datepicker.min.js' => array(),
+ ),
+ 'css' => array(
+ 'misc/ui/jquery.ui.datepicker.css' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'ui'),
+ ),
+ );
+ $libraries['ui.dialog'] = array(
+ 'title' => 'jQuery UI: Dialog',
+ 'website' => 'http://jqueryui.com/demos/dialog/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.ui.dialog.min.js' => array(),
+ ),
+ 'css' => array(
+ 'misc/ui/jquery.ui.dialog.css' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'ui.widget'),
+ array('system', 'ui.button'),
+ array('system', 'ui.draggable'),
+ array('system', 'ui.mouse'),
+ array('system', 'ui.position'),
+ array('system', 'ui.resizable'),
+ ),
+ );
+ $libraries['ui.draggable'] = array(
+ 'title' => 'jQuery UI: Draggable',
+ 'website' => 'http://jqueryui.com/demos/draggable/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.ui.draggable.min.js' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'ui.widget'),
+ array('system', 'ui.mouse'),
+ ),
+ );
+ $libraries['ui.droppable'] = array(
+ 'title' => 'jQuery UI: Droppable',
+ 'website' => 'http://jqueryui.com/demos/droppable/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.ui.droppable.min.js' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'ui.widget'),
+ array('system', 'ui.mouse'),
+ array('system', 'ui.draggable'),
+ ),
+ );
+ $libraries['ui.mouse'] = array(
+ 'title' => 'jQuery UI: Mouse',
+ 'website' => 'http://docs.jquery.com/UI/Mouse',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.ui.mouse.min.js' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'ui.widget'),
+ ),
+ );
+ $libraries['ui.position'] = array(
+ 'title' => 'jQuery UI: Position',
+ 'website' => 'http://jqueryui.com/demos/position/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.ui.position.min.js' => array(),
+ ),
+ );
+ $libraries['ui.progressbar'] = array(
+ 'title' => 'jQuery UI: Progress Bar',
+ 'website' => 'http://jqueryui.com/demos/progressbar/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.ui.progressbar.min.js' => array(),
+ ),
+ 'css' => array(
+ 'misc/ui/jquery.ui.progressbar.css' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'ui.widget'),
+ ),
+ );
+ $libraries['ui.resizable'] = array(
+ 'title' => 'jQuery UI: Resizable',
+ 'website' => 'http://jqueryui.com/demos/resizable/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.ui.resizable.min.js' => array(),
+ ),
+ 'css' => array(
+ 'misc/ui/jquery.ui.resizable.css' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'ui.widget'),
+ array('system', 'ui.mouse'),
+ ),
+ );
+ $libraries['ui.selectable'] = array(
+ 'title' => 'jQuery UI: Selectable',
+ 'website' => 'http://jqueryui.com/demos/selectable/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.ui.selectable.min.js' => array(),
+ ),
+ 'css' => array(
+ 'misc/ui/jquery.ui.selectable.css' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'ui.widget'),
+ array('system', 'ui.mouse'),
+ ),
+ );
+ $libraries['ui.slider'] = array(
+ 'title' => 'jQuery UI: Slider',
+ 'website' => 'http://jqueryui.com/demos/slider/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.ui.slider.min.js' => array(),
+ ),
+ 'css' => array(
+ 'misc/ui/jquery.ui.slider.css' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'ui.widget'),
+ array('system', 'ui.mouse'),
+ ),
+ );
+ $libraries['ui.sortable'] = array(
+ 'title' => 'jQuery UI: Sortable',
+ 'website' => 'http://jqueryui.com/demos/sortable/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.ui.sortable.min.js' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'ui.widget'),
+ array('system', 'ui.mouse'),
+ ),
+ );
+ $libraries['ui.tabs'] = array(
+ 'title' => 'jQuery UI: Tabs',
+ 'website' => 'http://jqueryui.com/demos/tabs/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.ui.tabs.min.js' => array(),
+ ),
+ 'css' => array(
+ 'misc/ui/jquery.ui.tabs.css' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'ui.widget'),
+ ),
+ );
+ $libraries['ui.widget'] = array(
+ 'title' => 'jQuery UI: Widget',
+ 'website' => 'http://docs.jquery.com/UI/Widget',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.ui.widget.min.js' => array('group' => JS_LIBRARY, 'weight' => -10),
+ ),
+ 'dependencies' => array(
+ array('system', 'ui'),
+ ),
+ );
+ $libraries['effects'] = array(
+ 'title' => 'jQuery UI: Effects',
+ 'website' => 'http://jqueryui.com/demos/effect/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.effects.core.min.js' => array('group' => JS_LIBRARY, 'weight' => -9),
+ ),
+ );
+ $libraries['effects.blind'] = array(
+ 'title' => 'jQuery UI: Effects Blind',
+ 'website' => 'http://jqueryui.com/demos/effect/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.effects.blind.min.js' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'effects'),
+ ),
+ );
+ $libraries['effects.bounce'] = array(
+ 'title' => 'jQuery UI: Effects Bounce',
+ 'website' => 'http://jqueryui.com/demos/effect/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.effects.bounce.min.js' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'effects'),
+ ),
+ );
+ $libraries['effects.clip'] = array(
+ 'title' => 'jQuery UI: Effects Clip',
+ 'website' => 'http://jqueryui.com/demos/effect/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.effects.clip.min.js' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'effects'),
+ ),
+ );
+ $libraries['effects.drop'] = array(
+ 'title' => 'jQuery UI: Effects Drop',
+ 'website' => 'http://jqueryui.com/demos/effect/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.effects.drop.min.js' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'effects'),
+ ),
+ );
+ $libraries['effects.explode'] = array(
+ 'title' => 'jQuery UI: Effects Explode',
+ 'website' => 'http://jqueryui.com/demos/effect/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.effects.explode.min.js' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'effects'),
+ ),
+ );
+ $libraries['effects.fade'] = array(
+ 'title' => 'jQuery UI: Effects Fade',
+ 'website' => 'http://jqueryui.com/demos/effect/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.effects.fade.min.js' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'effects'),
+ ),
+ );
+ $libraries['effects.fold'] = array(
+ 'title' => 'jQuery UI: Effects Fold',
+ 'website' => 'http://jqueryui.com/demos/effect/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.effects.fold.min.js' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'effects'),
+ ),
+ );
+ $libraries['effects.highlight'] = array(
+ 'title' => 'jQuery UI: Effects Highlight',
+ 'website' => 'http://jqueryui.com/demos/effect/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.effects.highlight.min.js' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'effects'),
+ ),
+ );
+ $libraries['effects.pulsate'] = array(
+ 'title' => 'jQuery UI: Effects Pulsate',
+ 'website' => 'http://jqueryui.com/demos/effect/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.effects.pulsate.min.js' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'effects'),
+ ),
+ );
+ $libraries['effects.scale'] = array(
+ 'title' => 'jQuery UI: Effects Scale',
+ 'website' => 'http://jqueryui.com/demos/effect/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.effects.scale.min.js' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'effects'),
+ ),
+ );
+ $libraries['effects.shake'] = array(
+ 'title' => 'jQuery UI: Effects Shake',
+ 'website' => 'http://jqueryui.com/demos/effect/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.effects.shake.min.js' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'effects'),
+ ),
+ );
+ $libraries['effects.slide'] = array(
+ 'title' => 'jQuery UI: Effects Slide',
+ 'website' => 'http://jqueryui.com/demos/effect/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.effects.slide.min.js' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'effects'),
+ ),
+ );
+ $libraries['effects.transfer'] = array(
+ 'title' => 'jQuery UI: Effects Transfer',
+ 'website' => 'http://jqueryui.com/demos/effect/',
+ 'version' => '1.8.7',
+ 'js' => array(
+ 'misc/ui/jquery.effects.transfer.min.js' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'effects'),
+ ),
+ );
+
+ // These library names are deprecated. Earlier versions of Drupal 7 didn't
+ // consistently namespace their libraries, so these names are included for
+ // backwards compatibility with those versions.
+ $libraries['once'] = &$libraries['jquery.once'];
+ $libraries['form'] = &$libraries['jquery.form'];
+ $libraries['jquery-bbq'] = &$libraries['jquery.bbq'];
+ $libraries['vertical-tabs'] = &$libraries['drupal.vertical-tabs'];
+ $libraries['cookie'] = &$libraries['jquery.cookie'];
+
+ return $libraries;
+}
+
+/**
+ * Implements hook_stream_wrappers().
+ */
+function system_stream_wrappers() {
+ $wrappers = array(
+ 'public' => array(
+ 'name' => t('Public files'),
+ 'class' => 'DrupalPublicStreamWrapper',
+ 'description' => t('Public local files served by the webserver.'),
+ 'type' => STREAM_WRAPPERS_LOCAL_NORMAL,
+ ),
+ 'temporary' => array(
+ 'name' => t('Temporary files'),
+ 'class' => 'DrupalTemporaryStreamWrapper',
+ 'description' => t('Temporary local files for upload and previews.'),
+ 'type' => STREAM_WRAPPERS_LOCAL_HIDDEN,
+ ),
+ );
+
+ // Only register the private file stream wrapper if a file path has been set.
+ if (variable_get('file_private_path', FALSE)) {
+ $wrappers['private'] = array(
+ 'name' => t('Private files'),
+ 'class' => 'DrupalPrivateStreamWrapper',
+ 'description' => t('Private local files served by Drupal.'),
+ 'type' => STREAM_WRAPPERS_LOCAL_NORMAL,
+ );
+ }
+
+ return $wrappers;
+}
+
+/**
+ * Retrieve a blocked IP address from the database.
+ *
+ * @param $iid integer
+ * The ID of the blocked IP address to retrieve.
+ *
+ * @return
+ * The blocked IP address from the database as an array.
+ */
+function blocked_ip_load($iid) {
+ return db_query("SELECT * FROM {blocked_ips} WHERE iid = :iid", array(':iid' => $iid))->fetchAssoc();
+}
+
+/**
+ * Menu item access callback - only admin or enabled themes can be accessed.
+ */
+function _system_themes_access($theme) {
+ return user_access('administer themes') && drupal_theme_access($theme);
+}
+
+/**
+ * @defgroup authorize Authorized operations
+ * @{
+ * Functions to run operations with elevated privileges via authorize.php.
+ *
+ * Because of the Update manager functionality included in Drupal core, there
+ * is a mechanism for running operations with elevated file system privileges,
+ * the top-level authorize.php script. This script runs at a reduced Drupal
+ * bootstrap level so that it is not reliant on the entire site being
+ * functional. The operations use a FileTransfer class to manipulate code
+ * installed on the system as the user that owns the files, not the user that
+ * the httpd is running as.
+ *
+ * The first setup is to define a callback function that should be authorized
+ * to run with the elevated privileges. This callback should take a
+ * FileTransfer as its first argument, although you can define an array of
+ * other arguments it should be invoked with. The callback should be placed in
+ * a separate .inc file that will be included by authorize.php.
+ *
+ * To run the operation, certain data must be saved into the SESSION, and then
+ * the flow of control should be redirected to the authorize.php script. There
+ * are two ways to do this, either to call system_authorized_run() directly,
+ * or to call system_authorized_init() and then redirect to authorize.php,
+ * using the URL from system_authorized_get_url(). Redirecting yourself is
+ * necessary when your authorized operation is being triggered by a form
+ * submit handler, since calling drupal_goto() in a submit handler is a bad
+ * idea, and you should instead set $form_state['redirect'].
+ *
+ * Once the SESSION is setup for the operation and the user is redirected to
+ * authorize.php, they will be prompted for their connection credentials (core
+ * provides FTP and SSH by default, although other connection classes can be
+ * added via contributed modules). With valid credentials, authorize.php will
+ * instantiate the appropriate FileTransfer object, and then invoke the
+ * desired operation passing in that object. The authorize.php script can act
+ * as a Batch API processing page, if the operation requires a batch.
+ *
+ * @see authorize.php
+ * @see FileTransfer
+ * @see hook_filetransfer_info()
+ */
+
+/**
+ * Setup a given callback to run via authorize.php with elevated privileges.
+ *
+ * To use authorize.php, certain variables must be stashed into $_SESSION. This
+ * function sets up all the necessary $_SESSION variables. The calling function
+ * should then redirect to authorize.php, using the full path returned by
+ * system_authorized_get_url(). That initiates the workflow that will eventually
+ * lead to the callback being invoked. The callback will be invoked at a low
+ * bootstrap level, without all modules being invoked, so it needs to be careful
+ * not to assume any code exists. Example (system_authorized_run()):
+ * @code
+ * system_authorized_init($callback, $file, $arguments, $page_title);
+ * drupal_goto(system_authorized_get_url());
+ * @endcode
+ * Example (update_manager_install_form_submit()):
+ * @code
+ * system_authorized_init('update_authorize_run_install',
+ * drupal_get_path('module', 'update') . '/update.authorize.inc',
+ * $arguments, t('Update manager'));
+ * $form_state['redirect'] = system_authorized_get_url();
+ * @endcode
+ *
+ * @param $callback
+ * The name of the function to invoke once the user authorizes the operation.
+ * @param $file
+ * The full path to the file where the callback function is implemented.
+ * @param $arguments
+ * Optional array of arguments to pass into the callback when it is invoked.
+ * Note that the first argument to the callback is always the FileTransfer
+ * object created by authorize.php when the user authorizes the operation.
+ * @param $page_title
+ * Optional string to use as the page title once redirected to authorize.php.
+ * @return
+ * Nothing, this function just initializes variables in the user's session.
+ */
+function system_authorized_init($callback, $file, $arguments = array(), $page_title = NULL) {
+ // First, figure out what file transfer backends the site supports, and put
+ // all of those in the SESSION so that authorize.php has access to all of
+ // them via the class autoloader, even without a full bootstrap.
+ $_SESSION['authorize_filetransfer_info'] = drupal_get_filetransfer_info();
+
+ // Now, define the callback to invoke.
+ $_SESSION['authorize_operation'] = array(
+ 'callback' => $callback,
+ 'file' => $file,
+ 'arguments' => $arguments,
+ );
+
+ if (isset($page_title)) {
+ $_SESSION['authorize_operation']['page_title'] = $page_title;
+ }
+}
+
+/**
+ * Return the URL for the authorize.php script.
+ *
+ * @param array $options
+ * Optional array of options to pass to url().
+ * @return
+ * The full URL to authorize.php, using HTTPS if available.
+ *
+ * @see system_authorized_init()
+ */
+function system_authorized_get_url(array $options = array()) {
+ global $base_url;
+ // Force HTTPS if available, regardless of what the caller specifies.
+ $options['https'] = TRUE;
+ // We prefix with $base_url so we get a full path even if clean URLs are
+ // disabled.
+ return url($base_url . '/authorize.php', $options);
+}
+
+/**
+ * Returns the URL for the authorize.php script when it is processing a batch.
+ */
+function system_authorized_batch_processing_url() {
+ return system_authorized_get_url(array('query' => array('batch' => '1')));
+}
+
+/**
+ * Setup and invoke an operation using authorize.php.
+ *
+ * @see system_authorized_init()
+ */
+function system_authorized_run($callback, $file, $arguments = array(), $page_title = NULL) {
+ system_authorized_init($callback, $file, $arguments, $page_title);
+ drupal_goto(system_authorized_get_url());
+}
+
+/**
+ * Use authorize.php to run batch_process().
+ *
+ * @see batch_process()
+ */
+function system_authorized_batch_process() {
+ $finish_url = system_authorized_get_url();
+ $process_url = system_authorized_batch_processing_url();
+ batch_process($finish_url, $process_url);
+}
+
+/**
+ * @} End of "defgroup authorize".
+ */
+
+/**
+ * Implements hook_updater_info().
+ */
+function system_updater_info() {
+ return array(
+ 'module' => array(
+ 'class' => 'ModuleUpdater',
+ 'name' => t('Update modules'),
+ 'weight' => 0,
+ ),
+ 'theme' => array(
+ 'class' => 'ThemeUpdater',
+ 'name' => t('Update themes'),
+ 'weight' => 0,
+ ),
+ );
+}
+
+/**
+ * Implements hook_filetransfer_info().
+ */
+function system_filetransfer_info() {
+ $backends = array();
+
+ // This is the default, will be available on most systems.
+ if (function_exists('ftp_connect')) {
+ $backends['ftp'] = array(
+ 'title' => t('FTP'),
+ 'class' => 'FileTransferFTP',
+ 'file' => 'ftp.inc',
+ 'file path' => 'includes/filetransfer',
+ 'weight' => 0,
+ );
+ }
+
+ // SSH2 lib connection is only available if the proper PHP extension is
+ // installed.
+ if (function_exists('ssh2_connect')) {
+ $backends['ssh'] = array(
+ 'title' => t('SSH'),
+ 'class' => 'FileTransferSSH',
+ 'file' => 'ssh.inc',
+ 'file path' => 'includes/filetransfer',
+ 'weight' => 20,
+ );
+ }
+ return $backends;
+}
+
+/**
+ * Implements hook_init().
+ */
+function system_init() {
+ $path = drupal_get_path('module', 'system');
+ // Add the CSS for this module. These aren't in system.info, because they
+ // need to be in the CSS_SYSTEM group rather than the CSS_DEFAULT group.
+ drupal_add_css($path . '/system.base.css', array('group' => CSS_SYSTEM, 'every_page' => TRUE));
+ if (path_is_admin(current_path())) {
+ drupal_add_css($path . '/system.admin.css', array('group' => CSS_SYSTEM));
+ }
+ drupal_add_css($path . '/system.menus.css', array('group' => CSS_SYSTEM, 'every_page' => TRUE));
+ drupal_add_css($path . '/system.messages.css', array('group' => CSS_SYSTEM, 'every_page' => TRUE));
+ drupal_add_css($path . '/system.theme.css', array('group' => CSS_SYSTEM, 'every_page' => TRUE));
+
+ // Ignore slave database servers for this request.
+ //
+ // In Drupal's distributed database structure, new data is written to the
+ // master and then propagated to the slave servers. This means there is a
+ // lag between when data is written to the master and when it is available on
+ // the slave. At these times, we will want to avoid using a slave server
+ // temporarily. For example, if a user posts a new node then we want to
+ // disable the slave server for that user temporarily to allow the slave
+ // server to catch up. That way, that user will see their changes immediately
+ // while for other users we still get the benefits of having a slave server,
+ // just with slightly stale data. Code that wants to disable the slave
+ // server should use the db_ignore_slave() function to set
+ // $_SESSION['ignore_slave_server'] to the timestamp after which the slave
+ // can be re-enabled.
+ if (isset($_SESSION['ignore_slave_server'])) {
+ if ($_SESSION['ignore_slave_server'] >= REQUEST_TIME) {
+ Database::ignoreTarget('default', 'slave');
+ }
+ else {
+ unset($_SESSION['ignore_slave_server']);
+ }
+ }
+
+ // Add CSS/JS files from module .info files.
+ system_add_module_assets();
+}
+
+/**
+ * Adds CSS and JavaScript files declared in module .info files.
+ */
+function system_add_module_assets() {
+ foreach (system_get_info('module') as $module => $info) {
+ if (!empty($info['stylesheets'])) {
+ foreach ($info['stylesheets'] as $media => $stylesheets) {
+ foreach ($stylesheets as $stylesheet) {
+ drupal_add_css($stylesheet, array('every_page' => TRUE, 'media' => $media));
+ }
+ }
+ }
+ if (!empty($info['scripts'])) {
+ foreach ($info['scripts'] as $script) {
+ drupal_add_js($script, array('every_page' => TRUE));
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_custom_theme().
+ */
+function system_custom_theme() {
+ if (user_access('view the administration theme') && path_is_admin(current_path())) {
+ return variable_get('admin_theme');
+ }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function system_form_user_profile_form_alter(&$form, &$form_state) {
+ if ($form['#user_category'] == 'account') {
+ if (variable_get('configurable_timezones', 1)) {
+ system_user_timezone($form, $form_state);
+ }
+ return $form;
+ }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function system_form_user_register_form_alter(&$form, &$form_state) {
+ if (variable_get('configurable_timezones', 1)) {
+ if (variable_get('user_default_timezone', DRUPAL_USER_TIMEZONE_DEFAULT) == DRUPAL_USER_TIMEZONE_SELECT) {
+ system_user_timezone($form, $form_state);
+ }
+ else {
+ $form['account']['timezone'] = array(
+ '#type' => 'hidden',
+ '#value' => variable_get('user_default_timezone', DRUPAL_USER_TIMEZONE_DEFAULT) ? '' : variable_get('date_default_timezone', ''),
+ );
+ }
+ return $form;
+ }
+}
+
+/**
+ * Implements hook_user_login().
+ */
+function system_user_login(&$edit, $account) {
+ // If the user has a NULL time zone, notify them to set a time zone.
+ if (!$account->timezone && variable_get('configurable_timezones', 1) && variable_get('empty_timezone_message', 0)) {
+ drupal_set_message(t('Configure your <a href="@user-edit">account time zone setting</a>.', array('@user-edit' => url("user/$account->uid/edit", array('query' => drupal_get_destination(), 'fragment' => 'edit-timezone')))));
+ }
+}
+
+/**
+ * Add the time zone field to the user edit and register forms.
+ */
+function system_user_timezone(&$form, &$form_state) {
+ global $user;
+
+ $account = $form['#user'];
+
+ $form['timezone'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Locale settings'),
+ '#weight' => 6,
+ '#collapsible' => TRUE,
+ );
+ $form['timezone']['timezone'] = array(
+ '#type' => 'select',
+ '#title' => t('Time zone'),
+ '#default_value' => isset($account->timezone) ? $account->timezone : ($account->uid == $user->uid ? variable_get('date_default_timezone', '') : ''),
+ '#options' => system_time_zones($account->uid != $user->uid),
+ '#description' => t('Select the desired local time and time zone. Dates and times throughout this site will be displayed using this time zone.'),
+ );
+ if (!isset($account->timezone) && $account->uid == $user->uid && empty($form_state['input']['timezone'])) {
+ $form['timezone']['#description'] = t('Your time zone setting will be automatically detected if possible. Confirm the selection and click save.');
+ $form['timezone']['timezone']['#attributes'] = array('class' => array('timezone-detect'));
+ drupal_add_js('misc/timezone.js');
+ }
+}
+
+/**
+ * Implements hook_block_info().
+ */
+function system_block_info() {
+ $blocks['main'] = array(
+ 'info' => t('Main page content'),
+ // Cached elsewhere.
+ 'cache' => DRUPAL_NO_CACHE,
+ // Auto-enable in 'content' region by default, which always exists.
+ // @see system_themes_page(), drupal_render_page()
+ 'status' => 1,
+ 'region' => 'content',
+ );
+ $blocks['powered-by'] = array(
+ 'info' => t('Powered by Drupal'),
+ 'weight' => '10',
+ 'cache' => DRUPAL_NO_CACHE,
+ );
+ $blocks['help'] = array(
+ 'info' => t('System help'),
+ 'weight' => '5',
+ 'cache' => DRUPAL_NO_CACHE,
+ // Auto-enable in 'help' region by default, if the theme defines one.
+ 'status' => 1,
+ 'region' => 'help',
+ );
+ // System-defined menu blocks.
+ foreach (menu_list_system_menus() as $menu_name => $title) {
+ $blocks[$menu_name]['info'] = t($title);
+ // Menu blocks can't be cached because each menu item can have
+ // a custom access callback. menu.inc manages its own caching.
+ $blocks[$menu_name]['cache'] = DRUPAL_NO_CACHE;
+ }
+ return $blocks;
+}
+
+/**
+ * Implements hook_block_view().
+ *
+ * Generate a block with a promotional link to Drupal.org and
+ * all system menu blocks.
+ */
+function system_block_view($delta = '') {
+ $block = array();
+ switch ($delta) {
+ case 'main':
+ $block['subject'] = NULL;
+ $block['content'] = drupal_set_page_content();
+ return $block;
+ case 'powered-by':
+ $block['subject'] = NULL;
+ $block['content'] = theme('system_powered_by');
+ return $block;
+ case 'help':
+ $block['subject'] = NULL;
+ $block['content'] = menu_get_active_help();
+ return $block;
+ default:
+ // All system menu blocks.
+ $system_menus = menu_list_system_menus();
+ if (isset($system_menus[$delta])) {
+ $block['subject'] = t($system_menus[$delta]);
+ $block['content'] = menu_tree($delta);
+ return $block;
+ }
+ break;
+ }
+}
+
+/**
+ * Implements hook_preprocess_block().
+ */
+function system_preprocess_block(&$variables) {
+ // System menu blocks should get the same class as menu module blocks.
+ if ($variables['block']->module == 'system' && in_array($variables['block']->delta, array_keys(menu_list_system_menus()))) {
+ $variables['classes_array'][] = 'block-menu';
+ }
+}
+
+/**
+ * Provide a single block on the administration overview page.
+ *
+ * @param $item
+ * The menu item to be displayed.
+ */
+function system_admin_menu_block($item) {
+ $cache = &drupal_static(__FUNCTION__, array());
+ // If we are calling this function for a menu item that corresponds to a
+ // local task (for example, admin/tasks), then we want to retrieve the
+ // parent item's child links, not this item's (since this item won't have
+ // any).
+ if ($item['tab_root'] != $item['path']) {
+ $item = menu_get_item($item['tab_root_href']);
+ }
+
+ if (!isset($item['mlid'])) {
+ $item += db_query("SELECT mlid, menu_name FROM {menu_links} ml WHERE ml.router_path = :path AND module = 'system'", array(':path' => $item['path']))->fetchAssoc();
+ }
+
+ if (isset($cache[$item['mlid']])) {
+ return $cache[$item['mlid']];
+ }
+
+ $content = array();
+ $query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC));
+ $query->join('menu_router', 'm', 'm.path = ml.router_path');
+ $query
+ ->fields('ml')
+ // Weight should be taken from {menu_links}, not {menu_router}.
+ ->fields('m', array_diff(drupal_schema_fields_sql('menu_router'), array('weight')))
+ ->condition('ml.plid', $item['mlid'])
+ ->condition('ml.menu_name', $item['menu_name'])
+ ->condition('ml.hidden', 0);
+
+ foreach ($query->execute() as $link) {
+ _menu_link_translate($link);
+ if ($link['access']) {
+ // The link description, either derived from 'description' in
+ // hook_menu() or customized via menu module is used as title attribute.
+ if (!empty($link['localized_options']['attributes']['title'])) {
+ $link['description'] = $link['localized_options']['attributes']['title'];
+ unset($link['localized_options']['attributes']['title']);
+ }
+ // Prepare for sorting as in function _menu_tree_check_access().
+ // The weight is offset so it is always positive, with a uniform 5-digits.
+ $key = (50000 + $link['weight']) . ' ' . drupal_strtolower($link['title']) . ' ' . $link['mlid'];
+ $content[$key] = $link;
+ }
+ }
+ ksort($content);
+ $cache[$item['mlid']] = $content;
+ return $content;
+}
+
+/**
+ * Checks the existence of the directory specified in $form_element.
+ *
+ * This function is called from the system_settings form to check all core
+ * file directories (file_public_path, file_private_path, file_temporary_path).
+ *
+ * @param $form_element
+ * The form element containing the name of the directory to check.
+ */
+function system_check_directory($form_element) {
+ $directory = $form_element['#value'];
+ if (strlen($directory) == 0) {
+ return $form_element;
+ }
+
+ if (!is_dir($directory) && !drupal_mkdir($directory, NULL, TRUE)) {
+ // If the directory does not exists and cannot be created.
+ form_set_error($form_element['#parents'][0], t('The directory %directory does not exist and could not be created.', array('%directory' => $directory)));
+ watchdog('file system', 'The directory %directory does not exist and could not be created.', array('%directory' => $directory), WATCHDOG_ERROR);
+ }
+
+ if (is_dir($directory) && !is_writable($directory) && !drupal_chmod($directory)) {
+ // If the directory is not writable and cannot be made so.
+ form_set_error($form_element['#parents'][0], t('The directory %directory exists but is not writable and could not be made writable.', array('%directory' => $directory)));
+ watchdog('file system', 'The directory %directory exists but is not writable and could not be made writable.', array('%directory' => $directory), WATCHDOG_ERROR);
+ }
+ elseif (is_dir($directory)) {
+ if ($form_element['#name'] == 'file_public_path') {
+ // Create public .htaccess file.
+ file_create_htaccess($directory, FALSE);
+ }
+ else {
+ // Create private .htaccess file.
+ file_create_htaccess($directory);
+ }
+ }
+
+ return $form_element;
+}
+
+/**
+ * Retrieves the current status of an array of files in the system table.
+ *
+ * @param $files
+ * An array of files to check.
+ * @param $type
+ * The type of the files.
+ */
+function system_get_files_database(&$files, $type) {
+ // Extract current files from database.
+ $result = db_query("SELECT filename, name, type, status, schema_version, weight FROM {system} WHERE type = :type", array(':type' => $type));
+ foreach ($result as $file) {
+ if (isset($files[$file->name]) && is_object($files[$file->name])) {
+ $file->uri = $file->filename;
+ foreach ($file as $key => $value) {
+ if (!isset($files[$file->name]->$key)) {
+ $files[$file->name]->$key = $value;
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Updates the records in the system table based on the files array.
+ *
+ * @param $files
+ * An array of files.
+ * @param $type
+ * The type of the files.
+ */
+function system_update_files_database(&$files, $type) {
+ $result = db_query("SELECT * FROM {system} WHERE type = :type", array(':type' => $type));
+
+ // Add all files that need to be deleted to a DatabaseCondition.
+ $delete = db_or();
+ foreach ($result as $file) {
+ if (isset($files[$file->name]) && is_object($files[$file->name])) {
+ // Keep the old filename from the database in case the file has moved.
+ $old_filename = $file->filename;
+
+ $updated_fields = array();
+
+ // Handle info specially, compare the serialized value.
+ $serialized_info = serialize($files[$file->name]->info);
+ if ($serialized_info != $file->info) {
+ $updated_fields['info'] = $serialized_info;
+ }
+ unset($file->info);
+
+ // Scan remaining fields to find only the updated values.
+ foreach ($file as $key => $value) {
+ if (isset($files[$file->name]->$key) && $files[$file->name]->$key != $value) {
+ $updated_fields[$key] = $files[$file->name]->$key;
+ }
+ }
+
+ // Update the record.
+ if (count($updated_fields)) {
+ db_update('system')
+ ->fields($updated_fields)
+ ->condition('filename', $old_filename)
+ ->execute();
+ }
+
+ // Indicate that the file exists already.
+ $files[$file->name]->exists = TRUE;
+ }
+ else {
+ // File is not found in file system, so delete record from the system table.
+ $delete->condition('filename', $file->filename);
+ }
+ }
+
+ if (count($delete) > 0) {
+ // Delete all missing files from the system table, but only if the plugin
+ // has never been installed.
+ db_delete('system')
+ ->condition($delete)
+ ->condition('schema_version', -1)
+ ->execute();
+ }
+
+ // All remaining files are not in the system table, so we need to add them.
+ $query = db_insert('system')->fields(array('filename', 'name', 'type', 'owner', 'info'));
+ foreach ($files as &$file) {
+ if (isset($file->exists)) {
+ unset($file->exists);
+ }
+ else {
+ $query->values(array(
+ 'filename' => $file->uri,
+ 'name' => $file->name,
+ 'type' => $type,
+ 'owner' => isset($file->owner) ? $file->owner : '',
+ 'info' => serialize($file->info),
+ ));
+ $file->type = $type;
+ $file->status = 0;
+ $file->schema_version = -1;
+ }
+ }
+ $query->execute();
+
+ // If any module or theme was moved to a new location, we need to reset the
+ // system_list() cache or we will continue to load the old copy, look for
+ // schema updates in the wrong place, etc.
+ system_list_reset();
+}
+
+/**
+ * Returns an array of information about enabled modules or themes.
+ *
+ * This function returns the information from the {system} table corresponding
+ * to the cached contents of the .info file for each active module or theme.
+ *
+ * @param $type
+ * Either 'module' or 'theme'.
+ * @param $name
+ * (optional) The name of a module or theme whose information shall be
+ * returned. If omitted, all records for the provided $type will be returned.
+ * If $name does not exist in the provided $type or is not enabled, an empty
+ * array will be returned.
+ *
+ * @return
+ * An associative array of module or theme information keyed by name, or only
+ * information for $name, if given. If no records are available, an empty
+ * array is returned.
+ *
+ * @see system_rebuild_module_data()
+ * @see system_rebuild_theme_data()
+ */
+function system_get_info($type, $name = NULL) {
+ $info = array();
+ if ($type == 'module') {
+ $type = 'module_enabled';
+ }
+ $list = system_list($type);
+ foreach ($list as $shortname => $item) {
+ if (!empty($item->status)) {
+ $info[$shortname] = $item->info;
+ }
+ }
+ if (isset($name)) {
+ return isset($info[$name]) ? $info[$name] : array();
+ }
+ return $info;
+}
+
+/**
+ * Helper function to scan and collect module .info data.
+ *
+ * @return
+ * An associative array of module information.
+ */
+function _system_rebuild_module_data() {
+ // Find modules
+ $modules = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules', 'name', 0);
+
+ // Include the installation profile in modules that are loaded.
+ $profile = drupal_get_profile();
+ $modules[$profile] = new stdClass();
+ $modules[$profile]->name = $profile;
+ $modules[$profile]->uri = 'profiles/' . $profile . '/' . $profile . '.profile';
+ $modules[$profile]->filename = $profile . '.profile';
+
+ // Installation profile hooks are always executed last.
+ $modules[$profile]->weight = 1000;
+
+ // Set defaults for module info.
+ $defaults = array(
+ 'dependencies' => array(),
+ 'description' => '',
+ 'package' => 'Other',
+ 'version' => NULL,
+ 'php' => DRUPAL_MINIMUM_PHP,
+ 'files' => array(),
+ 'bootstrap' => 0,
+ );
+
+ // Read info files for each module.
+ foreach ($modules as $key => $module) {
+ // The module system uses the key 'filename' instead of 'uri' so copy the
+ // value so it will be used by the modules system.
+ $modules[$key]->filename = $module->uri;
+
+ // Look for the info file.
+ $module->info = drupal_parse_info_file(dirname($module->uri) . '/' . $module->name . '.info');
+
+ // Skip modules that don't provide info.
+ if (empty($module->info)) {
+ unset($modules[$key]);
+ continue;
+ }
+
+ // Merge in defaults and save.
+ $modules[$key]->info = $module->info + $defaults;
+
+ // Prefix stylesheets and scripts with module path.
+ $path = dirname($module->uri);
+ if (isset($module->info['stylesheets'])) {
+ $module->info['stylesheets'] = _system_info_add_path($module->info['stylesheets'], $path);
+ }
+ if (isset($module->info['scripts'])) {
+ $module->info['scripts'] = _system_info_add_path($module->info['scripts'], $path);
+ }
+
+ // Installation profiles are hidden by default, unless explicitly specified
+ // otherwise in the .info file.
+ if ($key == $profile && !isset($modules[$key]->info['hidden'])) {
+ $modules[$key]->info['hidden'] = TRUE;
+ }
+
+ // Invoke hook_system_info_alter() to give installed modules a chance to
+ // modify the data in the .info files if necessary.
+ $type = 'module';
+ drupal_alter('system_info', $modules[$key]->info, $modules[$key], $type);
+ }
+
+ if (isset($modules[$profile])) {
+ // The installation profile is required, if it's a valid module.
+ $modules[$profile]->info['required'] = TRUE;
+ // Add a default distribution name if the profile did not provide one. This
+ // matches the default value used in install_profile_info().
+ if (!isset($modules[$profile]->info['distribution_name'])) {
+ $modules[$profile]->info['distribution_name'] = 'Drupal';
+ }
+ }
+
+ return $modules;
+}
+
+/**
+ * Rebuild, save, and return data about all currently available modules.
+ *
+ * @return
+ * Array of all available modules and their data.
+ */
+function system_rebuild_module_data() {
+ $modules_cache = &drupal_static(__FUNCTION__);
+ // Only rebuild once per request. $modules and $modules_cache cannot be
+ // combined into one variable, because the $modules_cache variable is reset by
+ // reference from system_list_reset() during the rebuild.
+ if (!isset($modules_cache)) {
+ $modules = _system_rebuild_module_data();
+ ksort($modules);
+ system_get_files_database($modules, 'module');
+ system_update_files_database($modules, 'module');
+ $modules = _module_build_dependencies($modules);
+ $modules_cache = $modules;
+ }
+ return $modules_cache;
+}
+
+/**
+ * Refresh bootstrap column in the system table.
+ *
+ * This is called internally by module_enable/disable() to flag modules that
+ * implement hooks used during bootstrap, such as hook_boot(). These modules
+ * are loaded earlier to invoke the hooks.
+ */
+function _system_update_bootstrap_status() {
+ $bootstrap_modules = array();
+ foreach (bootstrap_hooks() as $hook) {
+ foreach (module_implements($hook) as $module) {
+ $bootstrap_modules[] = $module;
+ }
+ }
+ $query = db_update('system')->fields(array('bootstrap' => 0));
+ if ($bootstrap_modules) {
+ db_update('system')
+ ->fields(array('bootstrap' => 1))
+ ->condition('name', $bootstrap_modules, 'IN')
+ ->execute();
+ $query->condition('name', $bootstrap_modules, 'NOT IN');
+ }
+ $query->execute();
+ // Reset the cached list of bootstrap modules.
+ system_list_reset();
+}
+
+/**
+ * Helper function to scan and collect theme .info data and their engines.
+ *
+ * @return
+ * An associative array of themes information.
+ */
+function _system_rebuild_theme_data() {
+ // Find themes
+ $themes = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info$/', 'themes');
+ // Allow modules to add further themes.
+ if ($module_themes = module_invoke_all('system_theme_info')) {
+ foreach ($module_themes as $name => $uri) {
+ // @see file_scan_directory()
+ $themes[$name] = (object) array(
+ 'uri' => $uri,
+ 'filename' => pathinfo($uri, PATHINFO_FILENAME),
+ 'name' => $name,
+ );
+ }
+ }
+
+ // Find theme engines
+ $engines = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.engine$/', 'themes/engines');
+
+ // Set defaults for theme info.
+ $defaults = array(
+ 'engine' => 'phptemplate',
+ 'regions' => array(
+ 'sidebar_first' => 'Left sidebar',
+ 'sidebar_second' => 'Right sidebar',
+ 'content' => 'Content',
+ 'header' => 'Header',
+ 'footer' => 'Footer',
+ 'highlighted' => 'Highlighted',
+ 'help' => 'Help',
+ 'page_top' => 'Page top',
+ 'page_bottom' => 'Page bottom',
+ ),
+ 'description' => '',
+ 'features' => _system_default_theme_features(),
+ 'screenshot' => 'screenshot.png',
+ 'php' => DRUPAL_MINIMUM_PHP,
+ 'stylesheets' => array(),
+ 'scripts' => array(),
+ );
+
+ $sub_themes = array();
+ // Read info files for each theme
+ foreach ($themes as $key => $theme) {
+ $themes[$key]->filename = $theme->uri;
+ $themes[$key]->info = drupal_parse_info_file($theme->uri) + $defaults;
+
+ // Invoke hook_system_info_alter() to give installed modules a chance to
+ // modify the data in the .info files if necessary.
+ $type = 'theme';
+ drupal_alter('system_info', $themes[$key]->info, $themes[$key], $type);
+
+ if (!empty($themes[$key]->info['base theme'])) {
+ $sub_themes[] = $key;
+ }
+ if ($themes[$key]->info['engine'] == 'theme') {
+ $filename = dirname($themes[$key]->uri) . '/' . $themes[$key]->name . '.theme';
+ if (file_exists($filename)) {
+ $themes[$key]->owner = $filename;
+ $themes[$key]->prefix = $key;
+ }
+ }
+ else {
+ $engine = $themes[$key]->info['engine'];
+ if (isset($engines[$engine])) {
+ $themes[$key]->owner = $engines[$engine]->uri;
+ $themes[$key]->prefix = $engines[$engine]->name;
+ $themes[$key]->template = TRUE;
+ }
+ }
+
+ // Prefix stylesheets and scripts with module path.
+ $path = dirname($theme->uri);
+ $theme->info['stylesheets'] = _system_info_add_path($theme->info['stylesheets'], $path);
+ $theme->info['scripts'] = _system_info_add_path($theme->info['scripts'], $path);
+
+ // Give the screenshot proper path information.
+ if (!empty($themes[$key]->info['screenshot'])) {
+ $themes[$key]->info['screenshot'] = $path . '/' . $themes[$key]->info['screenshot'];
+ }
+ }
+
+ // Now that we've established all our master themes, go back and fill in data
+ // for subthemes.
+ foreach ($sub_themes as $key) {
+ $themes[$key]->base_themes = drupal_find_base_themes($themes, $key);
+ // Don't proceed if there was a problem with the root base theme.
+ if (!current($themes[$key]->base_themes)) {
+ continue;
+ }
+ $base_key = key($themes[$key]->base_themes);
+ foreach (array_keys($themes[$key]->base_themes) as $base_theme) {
+ $themes[$base_theme]->sub_themes[$key] = $themes[$key]->info['name'];
+ }
+ // Copy the 'owner' and 'engine' over if the top level theme uses a theme
+ // engine.
+ if (isset($themes[$base_key]->owner)) {
+ if (isset($themes[$base_key]->info['engine'])) {
+ $themes[$key]->info['engine'] = $themes[$base_key]->info['engine'];
+ $themes[$key]->owner = $themes[$base_key]->owner;
+ $themes[$key]->prefix = $themes[$base_key]->prefix;
+ }
+ else {
+ $themes[$key]->prefix = $key;
+ }
+ }
+ }
+
+ return $themes;
+}
+
+/**
+ * Rebuild, save, and return data about all currently available themes.
+ *
+ * @return
+ * Array of all available themes and their data.
+ */
+function system_rebuild_theme_data() {
+ $themes = _system_rebuild_theme_data();
+ ksort($themes);
+ system_get_files_database($themes, 'theme');
+ system_update_files_database($themes, 'theme');
+ return $themes;
+}
+
+/**
+ * Prefixes all values in an .info file array with a given path.
+ *
+ * This helper function is mainly used to prefix all array values of an .info
+ * file property with a single given path (to the module or theme); e.g., to
+ * prefix all values of the 'stylesheets' or 'scripts' properties with the file
+ * path to the defining module/theme.
+ *
+ * @param $info
+ * A nested array of data of an .info file to be processed.
+ * @param $path
+ * A file path to prepend to each value in $info.
+ *
+ * @return
+ * The $info array with prefixed values.
+ *
+ * @see _system_rebuild_module_data()
+ * @see _system_rebuild_theme_data()
+ */
+function _system_info_add_path($info, $path) {
+ foreach ($info as $key => $value) {
+ // Recurse into nested values until we reach the deepest level.
+ if (is_array($value)) {
+ $info[$key] = _system_info_add_path($info[$key], $path);
+ }
+ // Unset the original value's key and set the new value with prefix, using
+ // the original value as key, so original values can still be looked up.
+ else {
+ unset($info[$key]);
+ $info[$value] = $path . '/' . $value;
+ }
+ }
+ return $info;
+}
+
+/**
+ * Returns an array of default theme features.
+ */
+function _system_default_theme_features() {
+ return array(
+ 'logo',
+ 'favicon',
+ 'name',
+ 'slogan',
+ 'node_user_picture',
+ 'comment_user_picture',
+ 'comment_user_verification',
+ 'main_menu',
+ 'secondary_menu',
+ );
+}
+
+/**
+ * Find all the base themes for the specified theme.
+ *
+ * This function has been deprecated in favor of drupal_find_base_themes().
+ */
+function system_find_base_themes($themes, $key, $used_keys = array()) {
+ return drupal_find_base_themes($themes, $key, $used_keys);
+}
+
+/**
+ * Get a list of available regions from a specified theme.
+ *
+ * @param $theme_key
+ * The name of a theme.
+ * @param $show
+ * Possible values: REGIONS_ALL or REGIONS_VISIBLE. Visible excludes hidden
+ * regions.
+ * @return
+ * An array of regions in the form $region['name'] = 'description'.
+ */
+function system_region_list($theme_key, $show = REGIONS_ALL) {
+ $themes = list_themes();
+ if (!isset($themes[$theme_key])) {
+ return array();
+ }
+
+ $list = array();
+ $info = $themes[$theme_key]->info;
+ // If requested, suppress hidden regions. See block_admin_display_form().
+ foreach ($info['regions'] as $name => $label) {
+ if ($show == REGIONS_ALL || !isset($info['regions_hidden']) || !in_array($name, $info['regions_hidden'])) {
+ $list[$name] = t($label);
+ }
+ }
+
+ return $list;
+}
+
+/**
+ * Implements hook_system_info_alter().
+ */
+function system_system_info_alter(&$info, $file, $type) {
+ // Remove page-top and page-bottom from the blocks UI since they are reserved for
+ // modules to populate from outside the blocks system.
+ if ($type == 'theme') {
+ $info['regions_hidden'][] = 'page_top';
+ $info['regions_hidden'][] = 'page_bottom';
+ }
+}
+
+/**
+ * Get the name of the default region for a given theme.
+ *
+ * @param $theme
+ * The name of a theme.
+ * @return
+ * A string that is the region name.
+ */
+function system_default_region($theme) {
+ $regions = array_keys(system_region_list($theme, REGIONS_VISIBLE));
+ return isset($regions[0]) ? $regions[0] : '';
+}
+
+/**
+ * Sets up a form to save information automatically.
+ *
+ * This function adds a submit handler and a submit button to a form array. The
+ * submit function saves all the data in the form, using variable_set(), to
+ * variables named the same as the keys in the form array. Note that this means
+ * you should normally prefix your form array keys with your module name, so
+ * that they are unique when passed into variable_set().
+ *
+ * If you need to manipulate the data in a custom manner, you can either put
+ * your own submission handler in the form array before calling this function,
+ * or just use your own submission handler instead of calling this function.
+ *
+ * @param $form
+ * An associative array containing the structure of the form.
+ *
+ * @return
+ * The form structure.
+ *
+ * @see system_settings_form_submit()
+ *
+ * @ingroup forms
+ */
+function system_settings_form($form) {
+ $form['actions']['#type'] = 'actions';
+ $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
+
+ if (!empty($_POST) && form_get_errors()) {
+ drupal_set_message(t('The settings have not been saved because of the errors.'), 'error');
+ }
+ $form['#submit'][] = 'system_settings_form_submit';
+ // By default, render the form using theme_system_settings_form().
+ if (!isset($form['#theme'])) {
+ $form['#theme'] = 'system_settings_form';
+ }
+ return $form;
+}
+
+/**
+ * Form submission handler for system_settings_form().
+ *
+ * If you want node type configure style handling of your checkboxes,
+ * add an array_filter value to your form.
+ */
+function system_settings_form_submit($form, &$form_state) {
+ // Exclude unnecessary elements.
+ form_state_values_clean($form_state);
+
+ foreach ($form_state['values'] as $key => $value) {
+ if (is_array($value) && isset($form_state['values']['array_filter'])) {
+ $value = array_keys(array_filter($value));
+ }
+ variable_set($key, $value);
+ }
+
+ drupal_set_message(t('The configuration options have been saved.'));
+}
+
+/**
+ * Helper function to sort requirements.
+ */
+function _system_sort_requirements($a, $b) {
+ if (!isset($a['weight'])) {
+ if (!isset($b['weight'])) {
+ return strcmp($a['title'], $b['title']);
+ }
+ return -$b['weight'];
+ }
+ return isset($b['weight']) ? $a['weight'] - $b['weight'] : $a['weight'];
+}
+
+/**
+ * Generates a form array for a confirmation form.
+ *
+ * This function returns a complete form array for confirming an action. The
+ * form contains a confirm button as well as a cancellation link that allows a
+ * user to abort the action.
+ *
+ * If the submit handler for a form that implements confirm_form() is invoked,
+ * the user successfully confirmed the action. You should never directly
+ * inspect $_POST to see if an action was confirmed.
+ *
+ * Note - if the parameters $question, $description, $yes, or $no could contain
+ * any user input (such as node titles or taxonomy terms), it is the
+ * responsibility of the code calling confirm_form() to sanitize them first with
+ * a function like check_plain() or filter_xss().
+ *
+ * @param $form
+ * Additional elements to add to the form. These can be regular form elements,
+ * #value elements, etc., and their values will be available to the submit
+ * handler.
+ * @param $question
+ * The question to ask the user (e.g. "Are you sure you want to delete the
+ * block <em>foo</em>?"). The page title will be set to this value.
+ * @param $path
+ * The page to go to if the user cancels the action. This can be either:
+ * - A string containing a Drupal path.
+ * - An associative array with a 'path' key. Additional array values are
+ * passed as the $options parameter to l().
+ * If the 'destination' query parameter is set in the URL when viewing a
+ * confirmation form, that value will be used instead of $path.
+ * @param $description
+ * Additional text to display. Defaults to t('This action cannot be undone.').
+ * @param $yes
+ * A caption for the button that confirms the action (e.g. "Delete",
+ * "Replace", ...). Defaults to t('Confirm').
+ * @param $no
+ * A caption for the link which cancels the action (e.g. "Cancel"). Defaults
+ * to t('Cancel').
+ * @param $name
+ * The internal name used to refer to the confirmation item.
+ *
+ * @return
+ * The form array.
+ */
+function confirm_form($form, $question, $path, $description = NULL, $yes = NULL, $no = NULL, $name = 'confirm') {
+ $description = isset($description) ? $description : t('This action cannot be undone.');
+
+ // Prepare cancel link.
+ if (isset($_GET['destination'])) {
+ $options = drupal_parse_url(urldecode($_GET['destination']));
+ }
+ elseif (is_array($path)) {
+ $options = $path;
+ }
+ else {
+ $options = array('path' => $path);
+ }
+
+ drupal_set_title($question, PASS_THROUGH);
+
+ $form['#attributes']['class'][] = 'confirmation';
+ $form['description'] = array('#markup' => $description);
+ $form[$name] = array('#type' => 'hidden', '#value' => 1);
+
+ $form['actions'] = array('#type' => 'actions');
+ $form['actions']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => $yes ? $yes : t('Confirm'),
+ );
+ $form['actions']['cancel'] = array(
+ '#type' => 'link',
+ '#title' => $no ? $no : t('Cancel'),
+ '#href' => $options['path'],
+ '#options' => $options,
+ );
+ // By default, render the form using theme_confirm_form().
+ if (!isset($form['#theme'])) {
+ $form['#theme'] = 'confirm_form';
+ }
+ return $form;
+}
+
+/**
+ * Determines whether the current user is in compact mode.
+ *
+ * Compact mode shows certain administration pages with less description text,
+ * such as the configuration page and the permissions page.
+ *
+ * Whether the user is in compact mode is determined by a cookie, which is set
+ * for the user by system_admin_compact_page().
+ *
+ * If the user does not have the cookie, the default value is given by the
+ * system variable 'admin_compact_mode', which itself defaults to FALSE. This
+ * does not have a user interface to set it: it is a hidden variable which can
+ * be set in the settings.php file.
+ *
+ * @return
+ * TRUE when in compact mode, FALSE when in expanded mode.
+ */
+function system_admin_compact_mode() {
+ // PHP converts dots into underscores in cookie names to avoid problems with
+ // its parser, so we use a converted cookie name.
+ return isset($_COOKIE['Drupal_visitor_admin_compact_mode']) ? $_COOKIE['Drupal_visitor_admin_compact_mode'] : variable_get('admin_compact_mode', FALSE);
+}
+
+/**
+ * Menu callback; Sets whether the admin menu is in compact mode or not.
+ *
+ * @param $mode
+ * Valid values are 'on' and 'off'.
+ */
+function system_admin_compact_page($mode = 'off') {
+ user_cookie_save(array('admin_compact_mode' => ($mode == 'on')));
+ drupal_goto();
+}
+
+/**
+ * Generate a list of tasks offered by a specified module.
+ *
+ * @param $module
+ * Module name.
+ * @param $info
+ * The module's information, as provided by system_get_info().
+ *
+ * @return
+ * An array of task links.
+ */
+function system_get_module_admin_tasks($module, $info) {
+ $links = &drupal_static(__FUNCTION__);
+
+ if (!isset($links)) {
+ $links = array();
+ $query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC));
+ $query->join('menu_router', 'm', 'm.path = ml.router_path');
+ $query
+ ->fields('ml')
+ // Weight should be taken from {menu_links}, not {menu_router}.
+ ->fields('m', array_diff(drupal_schema_fields_sql('menu_router'), array('weight')))
+ ->condition('ml.link_path', 'admin/%', 'LIKE')
+ ->condition('ml.hidden', 0, '>=')
+ ->condition('ml.module', 'system')
+ ->condition('m.number_parts', 1, '>')
+ ->condition('m.page_callback', 'system_admin_menu_block_page', '<>');
+ foreach ($query->execute() as $link) {
+ _menu_link_translate($link);
+ if ($link['access']) {
+ $links[$link['router_path']] = $link;
+ }
+ }
+ }
+
+ $admin_tasks = array();
+ $titles = array();
+ if ($menu = module_invoke($module, 'menu')) {
+ foreach ($menu as $path => $item) {
+ if (isset($links[$path])) {
+ $task = $links[$path];
+ // The link description, either derived from 'description' in
+ // hook_menu() or customized via menu module is used as title attribute.
+ if (!empty($task['localized_options']['attributes']['title'])) {
+ $task['description'] = $task['localized_options']['attributes']['title'];
+ unset($task['localized_options']['attributes']['title']);
+ }
+
+ // Check the admin tasks for duplicate names. If one is found,
+ // append the parent menu item's title to differentiate.
+ $duplicate_path = array_search($task['title'], $titles);
+ if ($duplicate_path !== FALSE) {
+ if ($parent = menu_link_load($task['plid'])) {
+ // Append the parent item's title to this task's title.
+ $task['title'] = t('@original_title (@parent_title)', array('@original_title' => $task['title'], '@parent_title' => $parent['title']));
+ }
+ if ($parent = menu_link_load($admin_tasks[$duplicate_path]['plid'])) {
+ // Append the parent item's title to the duplicated task's title.
+ // We use $links[$duplicate_path] in case there are triplicates.
+ $admin_tasks[$duplicate_path]['title'] = t('@original_title (@parent_title)', array('@original_title' => $links[$duplicate_path]['title'], '@parent_title' => $parent['title']));
+ }
+ }
+ else {
+ $titles[$path] = $task['title'];
+ }
+
+ $admin_tasks[$path] = $task;
+ }
+ }
+ }
+
+ // Append link for permissions.
+ if (module_hook($module, 'permission')) {
+ $item = menu_get_item('admin/people/permissions');
+ if (!empty($item['access'])) {
+ $item['link_path'] = $item['href'];
+ $item['title'] = t('Configure @module permissions', array('@module' => $info['name']));
+ unset($item['description']);
+ $item['localized_options']['fragment'] = 'module-' . $module;
+ $admin_tasks["admin/people/permissions#module-$module"] = $item;
+ }
+ }
+
+ return $admin_tasks;
+}
+
+/**
+ * Implements hook_cron().
+ *
+ * Remove older rows from flood and batch table. Remove old temporary files.
+ */
+function system_cron() {
+ // Cleanup the flood.
+ db_delete('flood')
+ ->condition('expiration', REQUEST_TIME, '<')
+ ->execute();
+
+ // Remove temporary files that are older than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
+ // Use separate placeholders for the status to avoid a bug in some versions
+ // of PHP. See http://drupal.org/node/352956.
+ $result = db_query('SELECT fid FROM {file_managed} WHERE status <> :permanent AND timestamp < :timestamp', array(
+ ':permanent' => FILE_STATUS_PERMANENT,
+ ':timestamp' => REQUEST_TIME - DRUPAL_MAXIMUM_TEMP_FILE_AGE
+ ));
+ foreach ($result as $row) {
+ if ($file = file_load($row->fid)) {
+ $references = file_usage_list($file);
+ if (empty($references)) {
+ if (!file_delete($file)) {
+ watchdog('file system', 'Could not delete temporary file "%path" during garbage collection', array('%path' => $file->uri), WATCHDOG_ERROR);
+ }
+ }
+ else {
+ watchdog('file system', 'Did not delete temporary file "%path" during garbage collection, because it is in use by the following modules: %modules.', array('%path' => $file->uri, '%modules' => implode(', ', array_keys($references))), WATCHDOG_INFO);
+ }
+ }
+ }
+
+ $core = array('cache', 'cache_path', 'cache_filter', 'cache_page', 'cache_form', 'cache_menu');
+ $cache_tables = array_merge(module_invoke_all('flush_caches'), $core);
+ foreach ($cache_tables as $table) {
+ cache_clear_all(NULL, $table);
+ }
+
+ // Cleanup the batch table and the queue for failed batches.
+ db_delete('batch')
+ ->condition('timestamp', REQUEST_TIME - 864000, '<')
+ ->execute();
+ db_delete('queue')
+ ->condition('created', REQUEST_TIME - 864000, '<')
+ ->condition('name', 'drupal_batch:%', 'LIKE')
+ ->execute();
+
+ // Reset expired items in the default queue implementation table. If that's
+ // not used, this will simply be a no-op.
+ db_update('queue')
+ ->fields(array(
+ 'expire' => 0,
+ ))
+ ->condition('expire', 0, '<>')
+ ->condition('expire', REQUEST_TIME, '<')
+ ->execute();
+}
+
+/**
+ * Implements hook_flush_caches().
+ */
+function system_flush_caches() {
+ // Rebuild list of date formats.
+ system_date_formats_rebuild();
+ // Reset the menu static caches.
+ menu_reset_static_cache();
+}
+
+/**
+ * Implements hook_action_info().
+ */
+function system_action_info() {
+ return array(
+ 'system_message_action' => array(
+ 'type' => 'system',
+ 'label' => t('Display a message to the user'),
+ 'configurable' => TRUE,
+ 'triggers' => array('any'),
+ ),
+ 'system_send_email_action' => array(
+ 'type' => 'system',
+ 'label' => t('Send e-mail'),
+ 'configurable' => TRUE,
+ 'triggers' => array('any'),
+ ),
+ 'system_block_ip_action' => array(
+ 'type' => 'user',
+ 'label' => t('Ban IP address of current user'),
+ 'configurable' => FALSE,
+ 'triggers' => array('any'),
+ ),
+ 'system_goto_action' => array(
+ 'type' => 'system',
+ 'label' => t('Redirect to URL'),
+ 'configurable' => TRUE,
+ 'triggers' => array('any'),
+ ),
+ );
+}
+
+/**
+ * Return a form definition so the Send email action can be configured.
+ *
+ * @param $context
+ * Default values (if we are editing an existing action instance).
+ *
+ * @return
+ * Form definition.
+ *
+ * @see system_send_email_action_validate()
+ * @see system_send_email_action_submit()
+ */
+function system_send_email_action_form($context) {
+ // Set default values for form.
+ if (!isset($context['recipient'])) {
+ $context['recipient'] = '';
+ }
+ if (!isset($context['subject'])) {
+ $context['subject'] = '';
+ }
+ if (!isset($context['message'])) {
+ $context['message'] = '';
+ }
+
+ $form['recipient'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Recipient'),
+ '#default_value' => $context['recipient'],
+ '#maxlength' => '254',
+ '#description' => t('The email address to which the message should be sent OR enter [node:author:mail], [comment:author:mail], etc. if you would like to send an e-mail to the author of the original post.'),
+ );
+ $form['subject'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Subject'),
+ '#default_value' => $context['subject'],
+ '#maxlength' => '254',
+ '#description' => t('The subject of the message.'),
+ );
+ $form['message'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Message'),
+ '#default_value' => $context['message'],
+ '#cols' => '80',
+ '#rows' => '20',
+ '#description' => t('The message that should be sent. You may include placeholders like [node:title], [user:name], and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.'),
+ );
+ return $form;
+}
+
+/**
+ * Validate system_send_email_action form submissions.
+ */
+function system_send_email_action_validate($form, $form_state) {
+ $form_values = $form_state['values'];
+ // Validate the configuration form.
+ if (!valid_email_address($form_values['recipient']) && strpos($form_values['recipient'], ':mail') === FALSE) {
+ // We want the literal %author placeholder to be emphasized in the error message.
+ form_set_error('recipient', t('Enter a valid email address or use a token e-mail address such as %author.', array('%author' => '[node:author:mail]')));
+ }
+}
+
+/**
+ * Process system_send_email_action form submissions.
+ */
+function system_send_email_action_submit($form, $form_state) {
+ $form_values = $form_state['values'];
+ // Process the HTML form to store configuration. The keyed array that
+ // we return will be serialized to the database.
+ $params = array(
+ 'recipient' => $form_values['recipient'],
+ 'subject' => $form_values['subject'],
+ 'message' => $form_values['message'],
+ );
+ return $params;
+}
+
+/**
+ * Sends an e-mail message.
+ *
+ * @param object $entity
+ * An optional node object, which will be added as $context['node'] if
+ * provided.
+ * @param array $context
+ * Array with the following elements:
+ * - 'recipient': E-mail message recipient. This will be passed through
+ * token_replace().
+ * - 'subject': The subject of the message. This will be passed through
+ * token_replace().
+ * - 'message': The message to send. This will be passed through
+ * token_replace().
+ * - Other elements will be used as the data for token replacement.
+ *
+ * @ingroup actions
+ */
+function system_send_email_action($entity, $context) {
+ if (empty($context['node'])) {
+ $context['node'] = $entity;
+ }
+
+ $recipient = token_replace($context['recipient'], $context);
+
+ // If the recipient is a registered user with a language preference, use
+ // the recipient's preferred language. Otherwise, use the system default
+ // language.
+ $recipient_account = user_load_by_mail($recipient);
+ if ($recipient_account) {
+ $language = user_preferred_language($recipient_account);
+ }
+ else {
+ $language = language_default();
+ }
+ $params = array('context' => $context);
+
+ if (drupal_mail('system', 'action_send_email', $recipient, $language, $params)) {
+ watchdog('action', 'Sent email to %recipient', array('%recipient' => $recipient));
+ }
+ else {
+ watchdog('error', 'Unable to send email to %recipient', array('%recipient' => $recipient));
+ }
+}
+
+/**
+ * Implements hook_mail().
+ */
+function system_mail($key, &$message, $params) {
+ $context = $params['context'];
+
+ $subject = token_replace($context['subject'], $context);
+ $body = token_replace($context['message'], $context);
+
+ $message['subject'] .= str_replace(array("\r", "\n"), '', $subject);
+ $message['body'][] = $body;
+}
+
+function system_message_action_form($context) {
+ $form['message'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Message'),
+ '#default_value' => isset($context['message']) ? $context['message'] : '',
+ '#required' => TRUE,
+ '#rows' => '8',
+ '#description' => t('The message to be displayed to the current user. You may include placeholders like [node:title], [user:name], and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.'),
+ );
+ return $form;
+}
+
+function system_message_action_submit($form, $form_state) {
+ return array('message' => $form_state['values']['message']);
+}
+
+/**
+ * Sends a message to the current user's screen.
+ *
+ * @param object $entity
+ * An optional node object, which will be added as $context['node'] if
+ * provided.
+ * @param array $context
+ * Array with the following elements:
+ * - 'message': The message to send. This will be passed through
+ * token_replace().
+ * - Other elements will be used as the data for token replacement in
+ * the message.
+ *
+ * @ingroup actions
+ */
+function system_message_action(&$entity, $context = array()) {
+ if (empty($context['node'])) {
+ $context['node'] = $entity;
+ }
+
+ $context['message'] = token_replace(filter_xss_admin($context['message']), $context);
+ drupal_set_message($context['message']);
+}
+
+/**
+ * Settings form for system_goto_action().
+ */
+function system_goto_action_form($context) {
+ $form['url'] = array(
+ '#type' => 'textfield',
+ '#title' => t('URL'),
+ '#description' => t('The URL to which the user should be redirected. This can be an internal URL like node/1234 or an external URL like http://drupal.org.'),
+ '#default_value' => isset($context['url']) ? $context['url'] : '',
+ '#required' => TRUE,
+ );
+ return $form;
+}
+
+function system_goto_action_submit($form, $form_state) {
+ return array(
+ 'url' => $form_state['values']['url']
+ );
+}
+
+/**
+ * Redirects to a different URL.
+ *
+ * @param $entity
+ * Ignored.
+ * @param array $context
+ * Array with the following elements:
+ * - 'url': URL to redirect to. This will be passed through
+ * token_replace().
+ * - Other elements will be used as the data for token replacement.
+ *
+ * @ingroup actions
+ */
+function system_goto_action($entity, $context) {
+ drupal_goto(token_replace($context['url'], $context));
+}
+
+/**
+ * Blocks the current user's IP address.
+ *
+ * @ingroup actions
+ */
+function system_block_ip_action() {
+ $ip = ip_address();
+ db_insert('blocked_ips')
+ ->fields(array('ip' => $ip))
+ ->execute();
+ watchdog('action', 'Banned IP address %ip', array('%ip' => $ip));
+}
+
+/**
+ * Generate an array of time zones and their local time&date.
+ *
+ * @param $blank
+ * If evaluates true, prepend an empty time zone option to the array.
+ */
+function system_time_zones($blank = NULL) {
+ $zonelist = timezone_identifiers_list();
+ $zones = $blank ? array('' => t('- None selected -')) : array();
+ foreach ($zonelist as $zone) {
+ // Because many time zones exist in PHP only for backward compatibility
+ // reasons and should not be used, the list is filtered by a regular
+ // expression.
+ if (preg_match('!^((Africa|America|Antarctica|Arctic|Asia|Atlantic|Australia|Europe|Indian|Pacific)/|UTC$)!', $zone)) {
+ $zones[$zone] = t('@zone: @date', array('@zone' => t(str_replace('_', ' ', $zone)), '@date' => format_date(REQUEST_TIME, 'custom', variable_get('date_format_long', 'l, F j, Y - H:i') . ' O', $zone)));
+ }
+ }
+ // Sort the translated time zones alphabetically.
+ asort($zones);
+ return $zones;
+}
+
+/**