Skip to content
Commits on Source (2111)
# Drupal editor configuration normalization
# @see http://editorconfig.org/
# This is the top-most .editorconfig file; do not search in parent directories.
root = true
# All files.
[*]
end_of_line = LF
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
# Ignore configuration files that may contain sensitive information.
sites/*/settings*.php
# Ignore paths that contain user-generated content.
sites/*/files
sites/*/private
......@@ -3,7 +3,7 @@
#
# Protect files and directories from prying eyes.
<FilesMatch "\.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)$|^(\..*|Entries.*|Repository|Root|Tag|Template)$">
<FilesMatch "\.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\..*|Entries.*|Repository|Root|Tag|Template|composer\.(json|lock))$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig\.save)$">
Order allow,deny
</FilesMatch>
......@@ -13,23 +13,14 @@ Options -Indexes
# Follow symbolic links in this directory.
Options +FollowSymLinks
# Multiviews creates problems with aliased URLs and is not needed for Drupal.
Options -Multiviews
# Make Drupal handle any 404 errors.
ErrorDocument 404 /index.php
# Force simple error message for requests for non-existent favicon.ico.
<Files favicon.ico>
# There is no end quote below, for compatibility with Apache 1.3.
ErrorDocument 404 "The requested file favicon.ico was not found.
</Files>
# Set the default handler.
DirectoryIndex index.php index.html index.htm
# Override PHP settings that cannot be changed at runtime. See
# sites/default/default.settings.php and drupal_initialize_variables() in
# sites/default/default.settings.php and drupal_environment_initialize() in
# includes/bootstrap.inc for settings that can be changed at runtime.
# PHP 5, Apache 1 and 2.
......@@ -65,6 +56,17 @@ DirectoryIndex index.php index.html index.htm
<IfModule mod_rewrite.c>
RewriteEngine on
# Set "protossl" to "s" if we were accessed via https://. This is used later
# if you enable "www." stripping or enforcement, in order to ensure that
# you don't bounce between http and https.
RewriteRule ^ - [E=protossl]
RewriteCond %{HTTPS} on
RewriteRule ^ - [E=protossl:s]
# Make sure Authorization HTTP header is available to PHP
# even when running as CGI or FastCGI.
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Block access to "hidden" directories whose names begin with a period. This
# includes directories used by version control systems such as Subversion or
# Git to store control files. Files whose names begin with a period, as well
......@@ -87,14 +89,15 @@ DirectoryIndex index.php index.html index.htm
# To redirect all users to access the site WITH the 'www.' prefix,
# (http://example.com/... will be redirected to http://www.example.com/...)
# uncomment the following:
# RewriteCond %{HTTP_HOST} .
# RewriteCond %{HTTP_HOST} !^www\. [NC]
# RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# RewriteRule ^ http%{ENV:protossl}://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
#
# To redirect all users to access the site WITHOUT the 'www.' prefix,
# (http://www.example.com/... will be redirected to http://example.com/...)
# uncomment the following:
# RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
# RewriteRule ^ http://%1%{REQUEST_URI} [L,R=301]
# RewriteRule ^ http%{ENV:protossl}://%1%{REQUEST_URI} [L,R=301]
# Modify the RewriteBase if you are using Drupal in a subdirectory or in a
# VirtualDocumentRoot and the rewrite rules are not working properly.
......@@ -132,11 +135,15 @@ DirectoryIndex index.php index.html index.htm
<FilesMatch "(\.js\.gz|\.css\.gz)$">
# Serve correct encoding type.
Header append Content-Encoding gzip
Header set Content-Encoding gzip
# Force proxies to cache gzipped & non-gzipped css/js files separately.
Header append Vary Accept-Encoding
</FilesMatch>
</IfModule>
</IfModule>
# $Id$
# Add headers to all responses.
<IfModule mod_headers.c>
# Disable content sniffing, since it's an attack vector.
Header always set X-Content-Type-Options nosniff
</IfModule>
Drupal 7.0, xxxx-xx-xx (development version)
Drupal 7.52, 2016-11-16
-----------------------
- Fixed security issues (multiple vulnerabilities). See SA-CORE-2016-005.
Drupal 7.51, 2016-10-05
-----------------------
- The Update module now also checks for updates to a disabled theme that is
used as an admin theme.
- Exceptions thrown in dblog_watchdog() are now caught and ignored.
- Clarified the warning that appears when modules are missing or have moved.
- Log messages are now XSS filtered on display.
- Draggable tables now work on touch screen devices.
- Added a setting for allowing double underscores in CSS identifiers
(https://www.drupal.org/node/2810369).
- If a user navigates away from a page while an Ajax request is running they
will no longer get an error message saying "An Ajax HTTP request terminated
abnormally".
- The system_region_list() API function now takes an optional third parameter
which allows region name translations to be skipped when they are not needed
(API addition: https://www.drupal.org/node/2810365).
- Numerous performance improvements.
- Numerous bug fixes.
- Numerous API documentation improvements.
- Additional automated test coverage.
Drupal 7.50, 2016-07-07
-----------------------
- Added a new "administer fields" permission for trusted users, which is
required in addition to other permissions to use the field UI
(https://www.drupal.org/node/2483307).
- Added clickjacking protection to Drupal core by setting the X-Frame-Options
header to SAMEORIGIN by default (https://www.drupal.org/node/2735873).
- Added support for full UTF-8 (emojis, Asian symbols, mathematical symbols) on
MySQL and other database drivers when the site and database are configured to
allow it (https://www.drupal.org/node/2761183).
- Improved performance by avoiding a re-scan of directories when a file is
missing; instead, trigger a PHP warning (minor API change:
https://www.drupal.org/node/2581445).
- Made it possible to use any PHP callable in Ajax form callbacks, form API
form-building functions, and form API wrapper callbacks (API addition:
https://www.drupal.org/node/2761169).
- Fixed that following a password reset link while logged in leaves users unable
to change their password (minor user interface change:
https://www.drupal.org/node/2759023).
- Implemented various fixes for automated test failures on PHP 5.4+ and PHP 7.
Drupal core automated tests now pass in these environments.
- Improved support for PHP 7 by fixing various problems.
- Fixed various bugs with PHP 5.5+ imagerotate(), including when incorrect
color indices are passed in.
- Fixed a regression introduced in Drupal 7.43 that allowed files uploaded by
anonymous users to be lost after form validation errors, and that also caused
regressions with certain contributed modules.
- Fixed a regression introduced in Drupal 7.36 which caused the default value
of hidden textarea fields to be ignored.
- Fixed robots.txt to allow search engines to access CSS, JavaScript and image
files.
- Changed wording on the Update Manager settings page to clarify that the
option to check for disabled module updates also applies to uninstalled
modules (administrative-facing translatable string change).
- Changed the help text when editing menu links and configuring URL redirect
actions so that it does not reference "Drupal" or the drupal.org website
(administrative-facing translatable string change).
- Fixed the locale safety check that is used to ensure that translations are
safe to allow for tokens in the href/src attributes of translated strings.
- Fixed that URL generation only works on port 80 when using domain based
language negotation.
- Made method="get" forms work inside the administrative overlay. The fix adds
a new hidden field to these forms when they appear inside the overlay (minor
data structure change).
- Increased maxlength of menu link title input fields in the node form and
menu link form from 128 to 255 characters.
- Removed meaningless post-check=0 and pre-check=0 cache control headers from
Drupal HTTP responses.
- Added a .editorconfig file to auto-configure editors that support it.
- Added --directory option to run-tests.sh for easier test discovery of all
tests within a project.
- Made run-tests.sh exit with a failure code when there are test fails or
problems running the script.
- Fixed that cookies from previous tests are still present when a new test
starts in DrupalWebTestCase.
- Improved performance of queries on the {authmap} database table.
- Fixed handling of missing files and functions inside the registry.
- Fixed Ajax handling for tableselect form elements that use checkboxes.
- Fixed a bug which caused ip_address() to return nothing when the client IP
address and proxy IP address are the same.
- Added a new option to format_xml_elements() to allow for already encoded
values.
- Changed the {history} table's node ID field to be an unsigned integer, to
match the same field in the {node} table and to prevent errors with very
large node IDs.
- Added an explicit page callback to the "admin/people/create" menu item in the
User module (minor data structure change). Previously this automatically
inherited the page callback from the parent "admin/people" menu item, which
broke contributed modules that override the "admin/people" page.
- Numerous small bug fixes.
- Numerous API documentation improvements.
- Additional automated test coverage.
Drupal 7.44, 2016-06-15
-----------------------
- Fixed security issues (privilege escalation). See SA-CORE-2016-002.
Drupal 7.43, 2016-02-24
-----------------------
- Fixed security issues (multiple vulnerabilities). See SA-CORE-2016-001.
Drupal 7.42, 2016-02-03
-----------------------
- Stopped invoking hook_flush_caches() on every cron run, since some modules
use that hook for expensive operations that are only needed on cache clears.
- Changed the default .htaccess and web.config to block Composer-related files.
- Added static caching to module_load_include() to improve performance.
- Fixed double-encoding bugs in select field widgets provided by the Options
module. The fix deprecates the 'strip_tags' property on option widgets and
replaces it with a new 'strip_tags_and_unescape' property (minor data
structure change).
- Improved MySQL 5.7 support by changing the MySQL database driver to stop
using the ANSI SQL mode alias, which has different meanings for different
MySQL versions.
- Fixed a regression introduced in Drupal 7.39 which prevented autocomplete
functionality from working on servers that are not configured to
automatically recognize index.php.
- Updated the Archive_Tar PEAR package to the latest 1.4.0 release, to fix bugs
with tar file handling on various operating systems.
- Fixed fatal errors on node preview when a field is displayed in the node
teaser but hidden in the full node view. The fix removes a
field_attach_prepare_view() call from the node_preview() function since it is
redundant with one in the node preview theme layer.
- Improved the description of the "Trimmed" format option on text fields
(translatable string change, and minor UI and data structure change).
- Numerous small bug fixes.
- Numerous API documentation improvements.
- Additional automated test coverage.
Drupal 7.41, 2015-10-21
-----------------------
- Fixed security issues (open redirect). See SA-CORE-2015-004.
Drupal 7.40, 2015-10-14
-----------------------
- Made Drupal's code for parsing .info files run much faster and use much less
memory.
- Prevented drupal_http_request() from returning an error when it receives a
201 through 206 HTTP status code.
- Added support for autoloading traits via the registry on sites running PHP
5.4 or higher.
- Allowed the user-picture.tpl.php theme template to have HTML classes besides
the default "user-picture" class printed in it (markup change).
- Fixed the URL text filter to convert e-mail addresses with plus signs into
mailto: links.
- Added alternate text to file icons displayed by the File module, to improve
accessibility (string change, and minor API addition to theme_file_icon()).
- Changed one-time login link failure messages to be displayed as errors or
warnings as appropriate, rather than as regular status messages (minor UI
change and data structure change).
- Changed the default settings.php configuration to exclude private files from
the "404_fast_paths" behavior.
- Changed the page that displays filter tips for a particular text format, for
example filter/tips/full_html, to return "page not found" or "access denied"
if the format does not exist or the user does not have access to it. This
change adds a new menu item to the Filter module's hook_menu() entry (minor
data structure change).
- Added a new hook, hook_block_cid_parts_alter(), to allow modules to alter the
cache keys used for caching a particular block.
- Made drupal_set_message() display and return messages when "0" is passed in
as the message to set.
- Fixed non-functional "Files displayed by default" setting on file fields.
- The "worker callback" provided in hook_cron_queue_info() and the "finished"
callback specified during batch processing can now be any PHP callable
instead of just functions.
- Prevented drupal_set_time_limit() from decreasing the time limit in the case
where the PHP maximum execution time is already unlimited.
- Changed the default thousand marker for numeric fields from a space ("1 000")
to nothing ("1000") (minor UI change: https://www.drupal.org/node/1388376).
- Prevented malformed theme .info files (without a "name" key) from causing
exceptions during menu rebuilds. If an .info file without a "name" key is
found in a module or theme directory, Drupal will now use the module or
theme's machine name as the display name instead.
- Made the format column in the {date_format_locale} database table
case-sensitive, to match the equivalent column in the {date_formats} table.
- Fixed a bug in the Statistics module that caused JavaScript files attached to
a node while it is being viewed to be omitted from the page.
- Added an optional 'project:' prefix that can be added to dependencies in a
module's .info file to indicate which project the dependency resides in (API
addition: https://www.drupal.org/node/2299747).
- Fixed various bugs that occurred after hooks were invoked early in the Drupal
bootstrap and that caused module_implements() and drupal_alter() to cache an
incomplete set of hook implementations for later use.
- Set the X-Content-Type-Options header to "nosniff" when possible, to prevent
certain web browsers from picking an unsafe MIME type.
- Prevented the database API from executing multiple queries at once on MySQL,
if the site's PHP version is new enough to do so. This is a secondary defense
against SQL injection (API change: https://www.drupal.org/node/2463973).
- Fixed a bug in the Drupal 6 to Drupal 7 upgrade path which caused the upgrade
to fail when there were multiple file records pointing to the same file.
- Numerous small bug fixes.
- Numerous API documentation improvements.
- Additional automated test coverage.
Drupal 7.39, 2015-08-19
-----------------------
- Fixed security issues (multiple vulnerabilities). See SA-CORE-2015-003.
Drupal 7.38, 2015-06-17
-----------------------
- Fixed security issues (multiple vulnerabilities). See SA-CORE-2015-002.
Drupal 7.37, 2015-05-07
-----------------------
- Fixed a regression in Drupal 7.36 which caused certain kinds of content types
to become disabled if they were defined by a no-longer-enabled module.
- Removed a confusing description regarding automatic time zone detection from
the user account form (minor UI and data structure change).
- Allowed custom HTML tags with a dash in the name to pass through filter_xss()
when specified in the list of allowed tags.
- Allowed hook_field_schema() implementations to specify indexes for fields
based on a fixed-length column prefix (rather than the entire column), as was
already allowed in hook_schema() implementations.
- Fixed PDO exceptions on PostgreSQL when accessing invalid entity URLs.
- Added a sites/all/libraries folder to the codebase, with instructions for
using it.
- Added a description to the "Administer text formats and filters" permission
on the Permissions page (string change).
- Numerous small bug fixes.
- Numerous API documentation improvements.
- Additional automated test coverage.
Drupal 7.36, 2015-04-01
-----------------------
- Added a 'file_public_schema' variable which allows modules that define
publicly-accessible streams in hook_stream_wrappers() to bypass file download
access checks when processing managed file upload fields.
- Fixed a bug that caused database query tags not to be added to search-related
database queries under many circumstances, and which prevented the
corresponding hook_query_TAG_alter() implementations from being called.
- Fixed the "for" attribute on managed file upload field labels to improve
accessibility (minor markup change).
- Added a 'javascript_always_use_jquery' variable which can be set to FALSE by
sites that may not need jQuery loaded on all pages, and a 'requires_jquery'
option to drupal_add_js() which modules can set to FALSE when adding
JavaScript files that have no dependency on jQuery (API addition:
https://www.drupal.org/node/2462717).
- Fixed incorrect foreign keys in the User module's role_permission and
users_roles database tables.
- Changed permission descriptions throughout Drupal core to consistently link
to relevant administrative pages, regardless of whether the user viewing the
Permissions page can view the page being linked to (minor UI change).
- Fixed the drupal_add_region_content() function so that it actually adds
content to the page.
- Added an 'image_suppress_itok_output' variable to allow sites already using
the existing 'image_allow_insecure_derivatives' variable to also prevent
security tokens from appearing in image derivative URLs.
- Fixed double-escaping of theme names in the Block module administrative
interface (minor string change).
- Added basic support for Xdebug when running automated tests.
- Fixed a bug which caused previewing a node to remove elements from the node
being edited. With this fix, calling node_preview() will no longer modify the
passed-in node object (minor API change).
- Added a user_has_role() function to check whether a user has a particular
role (API addition: https://www.drupal.org/node/2462411).
- Fixed installation failures when an opcode cache is enabled.
- Fixed a bug in the Drupal 6 to Drupal 7 upgrade path which caused private
files to be inaccessible.
- Fixed a bug in the Drupal 6 to Drupal 7 upgrade path which caused user
pictures to be lost.
- Fixed missing language code in hook_field_attach_view_alter() when it is
invoked from field_view_field().
- Stopped sending ETag and Last-Modified headers for uncached page requests,
since they break caching for certain Varnish and Nginx configurations.
- Changed the Simpletest module to allow PSR-4 test classes to be used in
Drupal 7.
- Fixed a fatal error that occurred when using the Comment module's "Unpublish
comment containing keyword(s)" action.
- Changed the "lang" attribute on language links to "xml:lang" so it validates
as XHTML (minor markup change).
- Prevented the form API from allowing arrays to be submitted for various form
elements, such as textfields, textareas, and password fields (API change:
https://www.drupal.org/node/2462723).
- Fixed a bug in the Contact module which caused the global user object to have
the incorrect name and e-mail address during the remainder of the page
request after the contact form is submitted.
- Numerous small bug fixes.
- Numerous API documentation improvements.
- Additional automated test coverage.
Drupal 7.35, 2015-03-18
-----------------------
- Fixed security issues (multiple vulnerabilities). See SA-CORE-2015-001.
Drupal 7.34, 2014-11-19
-----------------------
- Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-006.
Drupal 7.33, 2014-11-07
-----------------------
- Began storing the file modification time of each module and theme in the
{system} database table so that contributed modules can use it to identify
recently changed modules and themes (minor data structure change to the
return value of system_get_info() and other related functions).
- Added a "Did you mean?" feature to the run-tests.sh script for running
automated tests from the command line, to help developers who are attempting
to run a particular test class or group.
- Changed the date format used in various HTTP headers output by Drupal core
from RFC 1123 format to RFC 7231 format.
- Added a "block_cache_bypass_node_grants" variable to allow sites which have
node access modules enabled to use the block cache if desired (API addition).
- Made image derivative generation HTTP requests return a 404 error (rather
than a 500 error) when the source image does not exist.
- Fixed a bug which caused user pictures to be removed from the user object
after saving, and resulted in data loss if the user account was subsequently
re-saved.
- Fixed a bug in which field_has_data() did not return TRUE for fields that
only had data in older entity revisions, leading to loss of the field's data
when the field configuration was edited.
- Fixed a bug which caused the Ajax progress throbber to appear misaligned in
many situatons (minor styling change).
- Prevented the Bartik theme from lower-casing the "Permalink" link on
comments, for improved multilingual support (minor UI change).
- Added a "preferred_menu_links" tag to the database query that is used by
menu_link_get_preferred() to find the preferred menu link for a given path,
to make it easier to alter.
- Increased the maximum allowed length of block titles to 255 characters
(database schema change to the {block} table).
- Removed the Field module's field_modules_uninstalled() function, since it did
not do anything when it was invoked.
- Added a "theme_hook_original" variable to templates and theme functions and
an optional sitewide theme debug mode, to provide contextual information in
the page's HTML to theme developers. The theme debug mode is based on the one
used with Twig in Drupal 8 and can be accessed by setting the "theme_debug"
variable to TRUE (API addition).
- Added an entity_view_mode_prepare() API function to allow entity-defining
modules to properly invoke hook_entity_view_mode_alter(), and used it
throughout Drupal core to fix bugs with the invocation of that hook (API
change: https://www.drupal.org/node/2369141).
- Security improvement: Made the database API's orderBy() method sanitize the
sort direction ("ASC" or "DESC") for queries built with db_select(), so that
calling code does not have to.
- Changed the RDF module to consistently output RDF metadata for nodes and
comments near where the node is rendered in the HTML (minor markup and data
structure change).
- Added an HTML class to RDFa metatags throughout Drupal to prevent them from
accidentally affecting the site appearance (minor markup change).
- Fixed a bug in the Unicode requirements check which prevented installing
Drupal on PHP 5.6.
- Fixed a bug which caused drupal_get_bootstrap_phase() to abort the bootstrap
when called early in the page request.
- Renamed the "Search result" view mode to "Search result highlighting input"
to better reflect how it is used (UI change).
- Improved database queries generated by EntityFieldQuery in the case where
delta or language condition groups are used, to reduce the number of INNER
JOINs (this is a minor data structure change affecting code which implements
hook_query_alter() on these queries).
- Removed special-case behavior for file uploads which allowed user #1 to
bypass maximum file size and user quota limits.
- Numerous small bug fixes.
- Numerous API documentation improvements.
- Additional automated test coverage.
Drupal 7.32, 2014-10-15
-----------------------
- Fixed security issues (SQL injection). See SA-CORE-2014-005.
Drupal 7.31, 2014-08-06
-----------------------
- Fixed security issues (denial of service). See SA-CORE-2014-004.
Drupal 7.30, 2014-07-24
-----------------------
- Fixed a regression introduced in Drupal 7.29 that caused files or images
attached to taxonomy terms to be deleted when the taxonomy term was edited
and resaved (and other related bugs with contributed and custom modules).
- Added a warning on the permissions page to recommend restricting access to
the "View site reports" permission to trusted administrators. See
DRUPAL-PSA-2014-002.
- Numerous API documentation improvements.
- Additional automated test coverage.
Drupal 7.29, 2014-07-16
-----------------------
- Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-003.
Drupal 7.28, 2014-05-08
-----------------------
- Fixed a regression introduced in Drupal 7.27 that caused JavaScript to break
on older browsers (such as Internet Explorer 8 and earlier) when Ajax was
used.
- Increased the timeout used by the Update Manager module when it fetches data
from drupal.org (from 5 seconds to 30 seconds), to work around a problem
which causes incomplete information about security updates to be presented to
site administrators. This fix may lead to a performance slowdown on the
Update Manager administration pages, when installing Drupal distributions,
and (for sites that use the automated cron feature) on occasional page loads
by site visitors.
- Fixed the behavior of the token system's "[node:summary]" token when the body
field does not have a manual summary.
- Changed the behavior of db_query_temporary() so that it works on SELECT
queries even when they have leading comments/whitespace. A side effect of
this fix is that db_query_temporary() will now fail with an error if it is
ever used on non-SELECT queries.
- Added a "node_admin_filter" tag to the database query used to build the list
of nodes on the content administration page, to make it easier to alter.
- Made the cron queue system log any exceptions that are thrown while an item
in the queue is being processed, rather than stopping the entire PHP request.
- Improved screen reader support by adding an aria-live HTML attribute to file
upload fields when there is an error uploading the file (minor markup
change).
- Made the pager on the Tracker module listing pages show the same number of
items as other pagers throughout Drupal core (minor UI change).
- Fixed a bug which caused caches not to be properly cleared when a file entity
was saved or deleted.
- Added several missing countries to the default list returned by
country_get_list() (string change).
- Replaced the term "weight" with "influence" in the content ranking settings
for search, and added help text for administrators (string change).
- Fixed untranslatable text strings in the administrative interface for the
"Crop" effect provided by the Image module (minor string change).
- Fixed a bug in the Taxonomy module update function introduced in Drupal 7.26
that caused memory and CPU problems on sites with very large numbers of
unpublished nodes.
- Numerous small bug fixes.
- Numerous API documentation improvements.
- Additional automated test coverage.
Drupal 7.27, 2014-04-16
-----------------------
- Fixed security issues (information disclosure). See SA-CORE-2014-002.
Drupal 7.26, 2014-01-15
-----------------------
- Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-001.
Drupal 7.25, 2014-01-02
-----------------------
- Fixed a bug in node_save() which prevented the saved node from being updated
in hook_node_insert() and other similar hooks.
- Added a meta tag to install.php to prevent it from being indexed by search
engines even when Drupal is installed in a subfolder (minor markup change).
- Fixed a bug in the database API that caused frequent deadlock errors when
running merge queries on some servers.
- Performance improvement: Prevented block rehashing from writing blocks to the
database on every cache clear and cron run when the blocks have not changed.
This fix results in an extra 'saved' key which is added and set to TRUE for
each block returned by _block_rehash() that actually is saved to the database
(data structure change).
- Added an optional 'skip on cron' parameter to hook_cron_queue_info() to allow
queues to avoid being automatically processed on cron runs (API addition).
- Fixed a bug which caused hook_block_view_MODULE_DELTA_alter() to never be
invoked if the block delta had a hyphen in it. To implement the hook when the
block delta has a hyphen, modules should now replace hyphens with underscores
when constructing the function name for the hook implementation.
- Fixed a bug which caused cached pages to sometimes be sent to the browser
with incorrect compression. The fix adds a new 'page_compressed' key to the
$cache->data array returned by drupal_page_get_cache() (minor data structure
change).
- Fixed broken tests on PHP 5.5.
- Made the File and Image modules more robust when saving entities that have
deleted files attached. The code in file_field_presave() will now remove the
record of the deleted file from the entity before saving (minor data
structure change).
- Standardized menu callback functions throughout Drupal core to return
MENU_NOT_FOUND and MENU_ACCESS_DENIED rather than printing their own "page
not found" or "access denied" pages (minor API change in the return value of
these functions under some circumstances).
- Fixed a bug in which caches were not properly cleared when a node was deleted
via the administrative interface.
- Changed the Bartik theme to render content contained in <pre>, <code> and
similar tags in a larger font size, so it is easier to read.
- Fixed a bug in the Search module that caused exceptions to be thrown during
searches if the server was not configured to represent decimal points as a
period.
- Fixed a regression in the Image module that made image_style_url() not work
when a relative path (rather than a complete file URI) was passed to it.
- Added an optional feature to the Statistics module to allow node views to be
tracked by Ajax requests rather than during the server-side generation of the
page. This allows the node counter to work on sites that use external page
caches (string change and new administrative option:
https://drupal.org/node/2164069).
- Added a link to the drupal.org documentation page for cron to the Cron
settings page (string change).
- Added a 'drupal_anonymous_user_object' variable to allow the anonymous user
object returned by drupal_anonymous_user() to be overridden with a classed
object (API addition).
- Changed the database API to allow inserts based on a SELECT * query to work
correctly.
- Changed the database schema of the {file_managed} table to allow Drupal to
manage files larger than 4 GB.
- Changed the File module's hook_field_load() implementation to prevent file
entity properties which have the same name as file or image field properties
from overwriting the field properties (minor API change).
- Numerous small bug fixes.
- Numerous API documentation improvements.
- Additional automated test coverage.
Drupal 7.24, 2013-11-20
-----------------------
- Fixed security issues (multiple vulnerabilities), see SA-CORE-2013-003.
Drupal 7.23, 2013-08-07
-----------------------
- Fixed a fatal error on PostgreSQL databases when updating the Taxonomy module
from Drupal 6 to Drupal 7.
- Fixed the default ordering of CSS files for sites using right-to-left
languages, to consistently place the right-to-left override file immediately
after the CSS it is overriding (API change: https://drupal.org/node/2058463).
- Added a drupal_check_memory_limit() API function to allow the memory limit to
be checked consistently (API addition).
- Changed the default web.config file for IIS servers to allow favicon.ico
files which are present in the filesystem to be accessed.
- Fixed inconsistent support for the 'tel' protocol in Drupal's URL filtering
functions.
- Performance improvement: Allowed all hooks to be included in the
module_implements() cache, even those that are only invoked on HTTP POST
requests.
- Made the database system replace truncate queries with delete queries when
inside a transaction, to fix issues with PostgreSQL and other databases.
- Fixed a bug which caused nested contextual links to display improperly.
- Fixed a bug which prevented cached image derivatives from being flushed for
private files and other non-default file schemes.
- Fixed drupal_render() to always return an empty string when there is no
output, rather than sometimes returning NULL (minor API change).
- Added protection to cache_clear_all() to ensure that non-cache tables cannot
be truncated (API addition: a new isValidBin() method has been added to the
default database cache implementation).
- Changed the default .htaccess file to support HTTP authorization in CGI
environments.
- Changed the password reset form to pre-fill the username when requested via a
URL query parameter, and used this in the error message that appears after a
failed login attempt (minor data structure and behavior change).
- Fixed broken support for foreign keys in the field API.
- Fixed "No active batch" error when a user cancels their own account.
- Added a description to the "access content overview" permission on the
permissions page (string change).
- Added a drupal_array_diff_assoc_recursive() function to allow associative
arrays to be compared recursively (API addition).
- Added human-readable labels to image styles, in addition to the existing
machine-readable name (API change: https://drupal.org/node/2058503).
- Moved the drupal_get_hash_salt() function to bootstrap.inc and used it in
additional places in the code, for added security in the case where there is
no hash salt in settings.php.
- Fixed a regression in Drupal 7.22 that caused internal server errors for
sites running on very old Apache 1.x web servers.
- Numerous small bug fixes.
- Numerous API documentation improvements.
- Additional automated test coverage.
Drupal 7.22, 2013-04-03
-----------------------
- Allowed the drupal_http_request() function to be overridden so that
additional HTTP request capabilities can be added by contributed modules.
- Changed the Simpletest module to allow PSR-0 test classes to be used in
Drupal 7.
- Removed an unnecessary "Content-Disposition" header from private file
downloads; it prevented many private files from being viewed inline in a web
browser.
- Changed various field API functions to allow them to optionally act on a
single field within an entity (API addition: http://drupal.org/node/1825844).
- Fixed a bug which prevented Drupal's file transfer functionality from working
on some PHP 5.4 systems.
- Fixed incorrect log message when theme() is called for a theme hook that does
not exist (minor string change).
- Fixed Drupal's token-replacement system to allow spaces in the token value.
- Changed the default behavior after a user creates a node they do not have
access to view. The user will now be redirected to the front page rather than
an access denied page.
- Fixed a bug which prevented empty HTTP headers (such as "0") from being set.
(Minor behavior change: Callers of drupal_add_http_header() must now set
FALSE explicitly to prevent a header from being sent at all; this was already
indicated in the function's documentation.)
- Fixed OpenID errors when more than one module implements hook_openid(). The
behavior is now changed so that if more than one module tries to set the same
parameter, the last module's change takes effect.
- Fixed a serious documentation bug: The $name variable in the
taxonomy-term.tpl.php theme template was incorrectly documented as being
sanitized when in fact it is not.
- Fixed a bug which prevented Drupal 6 to Drupal 7 upgrades on sites which had
duplicate permission names in the User module's database tables.
- Added an empty "datatype" attribute to taxonomy term and username links to
make the RDFa markup upward compatible with RDFa 1.1 (minor markup addition).
- Fixed a bug which caused the denial-of-service protection added in Drupal
7.20 to break certain valid image URLs that had an extra slash in them.
- Fixed a bug with update queries in the SQLite database driver that prevented
Drupal from being installed with SQLite on PHP 5.4.
- Fixed enforced dependencies errors updating to recent versions of Drupal 7 on
certain non-MySQL databases.
- Refactored the Field module's caching behavior to obtain large improvements
in memory usage for sites with many fields and instances (API addition:
http://drupal.org/node/1915646).
- Fixed entity argument not being passed to implementations of
hook_file_download_access_alter(). The fix adds an additional context
parameter that can be passed when calling drupal_alter() for any hook (API
change: http://drupal.org/node/1882722).
- Fixed broken support for translatable comment fields (API change:
http://drupal.org/node/1874724).
- Added an assertThemeOutput() method to Simpletest to allow tests to check
that themed output matches an expected HTML string (API addition).
- Added a link to "Install another module" after a module has been successfully
downloaded via the Update Manager (UI change).
- Added an optional "exclusive" flag to installation profile .info files which
allows Drupal distributions to force a profile to be selected during
installation (API addition: http://drupal.org/node/1961012).
- Fixed a bug which caused the database API to not properly close database
connections.
- Added a link to the URL for running cron from outside the site to the Cron
settings page (UI change).
- Fixed a bug which prevented image styles from being reverted on PHP 5.4.
- Made the default .htaccess rules protocol sensitive to improve security for
sites which use HTTPS and redirect between "www" and non-"www" versions of
the page.
- Numerous small bug fixes.
- Numerous API documentation improvements.
- Additional automated test coverage.
Drupal 7.21, 2013-03-06
-----------------------
- Allowed sites using the 'image_allow_insecure_derivatives' variable to still
have partial protection from the security issues fixed in Drupal 7.20.
Drupal 7.20, 2013-02-20
-----------------------
- Fixed security issues (denial of service). See SA-CORE-2013-002.
Drupal 7.19, 2013-01-16
-----------------------
- Fixed security issues (multiple vulnerabilities). See SA-CORE-2013-001.
Drupal 7.18, 2012-12-19
-----------------------
- Fixed security issues (multiple vulnerabilities). See SA-CORE-2012-004.
Drupal 7.17, 2012-11-07
-----------------------
- Changed the default value of the '404_fast_html' variable to have a DOCTYPE
declaration.
- Made it possible to use associative arrays for the 'items' variable in
theme_item_list().
- Fixed a bug which prevented required form elements without a title from being
given an "error" class when the form fails validation.
- Prevented duplicate HTML IDs from appearing when two forms are displayed on
the same page and one of them is submitted with invalid data (minor markup
change).
- Fixed a bug which prevented Drupal 6 to Drupal 7 upgrades on sites which had
stale data in the Upload module's database tables.
- Fixed a bug in the States API which prevented certain types of form elements
from being disabled when requested.
- Allowed aggregator feed items with author names longer than 255 characters to
have a truncated version saved to the database (rather than causing a fatal
error).
- Allowed aggregator feed items to have URLs longer than 255 characters
(schema change which results in several columns in the Aggregator module's
database tables changing from VARCHAR to TEXT fields).
- Added hook_taxonomy_term_view() and standardized the process for rendering
taxonomy terms to invoke hook_entity_view() and otherwise make it consistent
with other entities (API change: http://drupal.org/node/1808870).
- Added hook_entity_view_mode_alter() to allow modules to change entity view
modes on display (API addition: http://drupal.org/node/1833086).
- Fixed a bug which made database queries running a "LIKE" query on blob fields
fail on PostgreSQL databases. This caused errors during the Drupal 6 to
Drupal 7 upgrade.
- Changed the hook_menu() entry for Drupal's rss.xml page to prevent extra path
components from being accidentally passed to the page callback function (data
structure change).
- Removed a non-standard "name" attribute from Drupal's default Content-Type
header for file downloads.
- Fixed the theme settings form to properly clean up submitted values in
$form_state['values'] when the form is submitted (data structure change).
- Fixed an inconsistency by removing the colon from the end of the label on
multi-valued form fields (minor string change).
- Added support for 'weight' in hook_field_widget_info() to allow modules to
control the order in which widgets are displayed in the Field UI.
- Updated various tables in the OpenID and Book modules to use the default
"empty table" text pattern (string change).
- Added proxy server support to drupal_http_request().
- Added "lang" attributes to language links, to better support screen readers.
- Fixed double occurrence of a "ul" HTML tag on secondary local tasks in the
Seven theme (markup change).
- Fixed bugs which caused taxonomy vocabulary and shortcut set titles to be
double-escaped. The fix replaces the taxonomy vocabulary overview page and
"Edit shortcuts" menu items' title callback entries in hook_menu() with new
functions that do not escape HTML characters (data structure change).
- Modified the Update manager module to allow drupal.org to collect usage
statistics for individual modules and themes, rather than only for entire
projects.
- Modified the node listing database query on Drupal's default front page to
add table aliases for better query altering (this is a data structure change
affecting code which implements hook_query_alter() on this query).
- Improved the translatability of the "Field type(s) in use" message on the
modules page (admin-facing string change).
- Fixed a regression which caused a "call to undefined function
drupal_find_base_themes()" fatal error under rare circumstances.
- Numerous API documentation improvements.
- Additional automated test coverage.
Drupal 7.16, 2012-10-17
-----------------------
- Fixed security issues (Arbitrary PHP code execution and information
disclosure). See SA-CORE-2012-003.
Drupal 7.15, 2012-08-01
-----------------------
- Introduced a 'user_password_reset_timeout' variable to allow the 24-hour
expiration for user password reset links to be adjusted (API addition).
- Fixed database errors due to ambiguous column names that occurred when
EntityFieldQuery was used in certain situations.
- Changed the drupal_array_get_nested_value() function to return a reference
(API addition).
- Changed the System module's hook_block_info() implementation to assign the
"Main page content" and "System help" blocks to appropriate regions by
default and prevent error messages on the block administration page (data
structure change).
- Fixed regression: Non-node entities couldn't be accessed with
EntityFieldQuery.
- Fixed regression: Optional radio buttons with an empty, non-NULL default
value led to an illegal choice error when none were selected.
- Reorganized the testing framework to split setUp() into specific sub-methods
and fix several regressions in the process.
- Fixed bug which made it impossible to search for strings that have not been
translated into a particular language.
- Renamed the "Field" column on the Manage Fields screen to "Field type", since
the former was confusing and inaccurate (UI change).
- Performance improvement: Removed needless call to system_rebuild_module_data()
in field_sync_field_status(), greatly speeding up bulk module enable/disable.
- Fixed bug which prevented notifications from being sent when core, module, and
theme updates are available.
- Fixed bug which prevented sub-themes from inheriting the default values of
theme settings defined by the base theme.
- Fixed bug which prevented the jQuery UI Datepicker from being localized.
- Made Ajax alert dialogs respect error reporting settings.
- Fixed bug which prevented image styles from being deleted on PHP 5.4.
- Fixed bug: Language detection by domain only worked on port 80.
- Fixed regression: The first plural index on a page was not calculated
correctly.
- Introduced generic entity language support. Entities may now declare their
language property in hook_entity_info(), and modules working with entities
may access the language using entity_language() (API change:
http://drupal.org/node/1626346).
- Added EntityFieldQuery support for taxonomy bundles.
- Fixed issue where field form structure was incomplete if field_access()
returned FALSE. Instead of being incomplete, the form structure now has
#access set to FALSE and field form validation is skipped (data structure
change: http://drupal.org/node/1663020).
- Fixed data loss issue due to field_has_data() returning inconsistent results.
The fix adds an optional DANGEROUS_ACCESS_CHECK_OPT_OUT tag to entity field
queries which field storage engines can respond to (API addition:
http://drupal.org/node/1597378).
- Fixed notice: Undefined index: default_image in image_field_prepare_view()
- Numerous API documentation improvements.
- Additional automated test coverage.
Drupal 7.14, 2012-05-02
-----------------------
- Fixed "integrity constraint" fatal errors when rebuilding registry.
- Fixed custom logo and favicon functionality referencing incorrect paths.
- Fixed DB Case Sensitivity: Allow BINARY attribute in MySQL.
- Split field_bundle_settings out per bundle.
- Improve UX for machine names for fields (UI change).
- Fixed User pictures are not removed properly.
- Fixed HTTPS sessions not working in all cases.
- Fixed Regression: Required radios throw illegal choice error when none
selected.
- Fixed allow autocompletion requests to include slashes.
- Eliminate $user->cache and {session}.cache in favor of
$_SESSION['cache_expiration'][$bin] (Performance).
- Fixed focus jumps to tab when pressing enter on a form element within tab.
- Fixed race condition in locale() - duplicates in {locales_source}.
- Fixed Missing "Default image" per field instance.
- Quit clobbering people's work when they click the filter tips link
- Form API #states: Fix conditionals to allow OR and XOR constructions.
- Fixed Focus jumps to tab when pressing enter on a form element within tab.
(Accessibility)
- Improved performance of node_access queries.
- Fixed Fieldsets inside vertical tabs have no title and can't be collapsed.
- Reduce size of cache_menu table (Performance).
- Fixed unnecessary aggregation of CSS/JS (Performance).
- Fixed taxonomy_autocomplete() produces SQL error for nonexistent field.
- Fixed HTML filter is not run first by default, despite default weight.
- Fixed Overlay does not work with prefixed URL paths.
- Better debug info for field errors (string change).
- Fixed Data corruption in comment IDs (results in broken threading on
PostgreSQL).
- Fixed machine name not editable if every character is replaced.
- Fixed user picture not appearing in comment preview (Markup change).
- Added optional vid argument for taxonomy_get_term_by_name().
- Fixed Invalid Unicode code range in PREG_CLASS_UNICODE_WORD_BOUNDARY fails
with PCRE 8.30.
- Fixed {trigger_assignments()}.hook has only 32 characters, is too short.
- Numerous fixes to run-tests.sh.
- Fixed Tests in profiles/[name]/modules cannot be run and cannot use a
different profile for running tests.
- Numerous JavaScript performance fixes.
- Numerous documentation fixes.
- Fixed All pager links have an 'active' CSS class.
- Numerous upgrade path fixes; notably:
- system_update_7061() fails on inserting files with same name but different
case.
- system_update_7061() converts filepaths too aggressively.
- Trigger upgrade path: Node triggers removed when upgrading to 7-x from 6.25.
Drupal 7.13, 2012-05-02
-----------------------
- Fixed security issues (Multiple vulnerabilities), see SA-CORE-2012-002.
Drupal 7.12, 2012-02-01
-----------------------
- Fixed bug preventing custom menus from receiving an active trail.
- Fixed hook_field_delete() no longer invoked during field_purge_data().
- Fixed bug causing entity info cache to not be cleared with the rest of caches.
- Fixed file_unmanaged_copy() fails with Drupal 7.7+ and safe_mode() or
open_basedir().
- Fixed Nested transactions throw exceptions when they got out of scope.
- Fixed bugs with the Return-Path when sending mail on both Windows and
non-Windows systems.
- Fixed bug with DrupalCacheArray property visibility preventing others from
extending it (API change: http://drupal.org/node/1422264).
- Fixed bug with handling of non-ASCII characters in file names (API change:
http://drupal.org/node/1424840).
- Reconciled field maximum length with database column size in image and
aggregator modules.
- Fixes to various core JavaScript files to allow for minification and
aggregation.
- Fixed Prevent tests from deleting main installation's tables when
parent::setUp() is not called.
- Fixed several Poll module bugs.
- Fixed several Shortcut module bugs.
- Added new hook_system_theme_info() to provide ability for contributed modules
to test theme functionality.
- Added ability to cancel mail sending from hook_mail_alter().
- Added support for configurable PDO connection options, enabling master-master
database replication.
- Numerous improvements to tests and test runner to pave the way for faster test
runs.
- Expanded test coverage.
- Numerous API documentation improvements.
- Numerous performance improvements, including token replacement and render
cache.
Drupal 7.11, 2012-02-01
-----------------------
- Fixed security issues (Multiple vulnerabilities), see SA-CORE-2012-001.
Drupal 7.10, 2011-12-05
-----------------------
- Fixed Content-Language HTTP header to not cause issues with Drush 5.x.
- Reduce memory usage of theme registry (performance).
- Fixed PECL upload progress bar for FileField
- Fixed running update.php doesn't always clear the cache.
- Fixed PDO exceptions on long titles.
- Fixed Overlay redirect does not include query string.
- Fixed D6 modules satisfy D7 module dependencies.
- Fixed the ordering of module hooks when using module_implements_alter().
- Fixed "floating" submit buttons during AJAX requests.
- Fixed timezone selected on install not propogating to admin account.
- Added msgctx context to JS translation functions, for feature parity with t().
- Profiles' .install files now available during hook_install_tasks().
- Added test coverage of 7.0 -> 7.x upgrade path.
- Numerous notice fixes.
- Numerous documentation improvements.
- Additional automated test coverage.
Drupal 7.9, 2011-10-26
----------------------
- Critical fixes to OpenID to spec violations that could allow for
impersonation in certain scenarios. Existing OpenID users should see
http://drupal.org/node/1120290#comment-5092796 for more information on
transitioning.
- Fixed files getting lost when adding multiple files to multiple file fields
at the same time.
- Improved usability of the clean URL test screens.
- Restored height/width attributes on images run through the theme system.
- Fixed usability bug with first password field being pre-filled by certain
browser plugins.
- Fixed file_usage_list() so that it can return more than one result.
- Fixed bug preventing preview of private images on node form.
- Fixed PDO error when inserting an aggregator title longer than 255 characters.
- Spelled out what TRADITIONAL means in MySQL sql_mode.
- Deprecated "!=" operator for DBTNG; should be "<>".
- Added two new API functions (menu_tree_set_path()/menu_tree_get_path()) were
added in order to enable setting the active menu trail for dynamically
generated menu paths.
- Added new "fast 404" capability in settings.php to bypass Drupal bootstrap
when serving 404 pages for certain file types.
- Added format_string() function which can perform string munging ala the t()
function without the overhead of the translation system.
- Numerous #states system fixes.
- Numerous EntityFieldQuery, DBTNG, and SQLite fixes.
- Numerous Shortcut module fixes.
- Numerous language system fixes.
- Numerous token fixes.
- Numerous CSS fixes.
- Numerous upgrade path fixes.
- Numerous minor string fixes.
- Numerous notice fixes.
Drupal 7.8, 2011-08-31
----------------------
- Fixed critical upgrade path issue with multilingual sites, leading to lost
content.
- Numerous fixes to upgrade path, preventing fatal errors due to incorrect
dependencies.
- Fixed issue with saving files on hosts with open_basedir restrictions.
- Fixed Update manger error when used with Overlay.
- Fixed RTL support in Seven administration theme and Overlay.
- Fixes to nested transaction support.
- Introduced performance pattern to reduce Drupal core's RAM usage.
- Added support for HTML 5 tags to filter_xss_admin().
- Added exception handling to cron.
- Added new hook hook_field_widget_form_alter() for contribtued modules.
- element_validate_*() functions now available to contrib.
- Added new maintainers for several subsystems.
- Numerous testing system improvements.
- Numerous markup and CSS fixes.
- Numerous poll module fixes.
- Numerous notice/warning fixes.
- Numerous documentation fixes.
- Numerous token fixes.
Drupal 7.7, 2011-07-27
----------------------
- Fixed VERSION string.
Drupal 7.6, 2011-07-27
----------------------
- Fixed support for remote streamwrappers.
- AJAX now binds to 'click' instead of 'mousedown'.
- 'Translatable' flag on fields created in UI now defaults to FALSE, to match those created via the API.
- Performance enhancement to permissions page on large numbers of permissions.
- More secure password generation.
- Fix for temporary directory on Windows servers.
- run-tests.sh now uses proc_open() instead of pcntl_fork() for better Windows support.
- Numerous upgrade path fixes.
- Numerous documentation fixes.
- Numerous notice fixes.
- Numerous fixes to improve PHP 5.4 support.
- Numerous RTL improvements.
Drupal 7.5, 2011-07-27
----------------------
- Fixed security issue (Access bypass), see SA-CORE-2011-003.
Drupal 7.4, 2011-06-29
----------------------
- Rolled back patch that caused fatal errors in CTools, Feeds, and other modules using the class registry.
- Fixed critical bug with saving default images.
- Fixed fatal errors when uninstalling some modules.
- Added workaround for MySQL transaction support breaking on DDL statments.
- Improved page caching with external caching systems.
- Fix to Batch API, which was terminating too early.
- Numerous upgrade path fixes.
- Performance fixes.
- Additional test coverage.
- Numerous documentation fixes.
Drupal 7.3, 2011-06-29
----------------------
- Fixed security issue (Access bypass), see SA-CORE-2011-002.
Drupal 7.2, 2011-05-25
----------------------
- Added a default .gitignore file.
- Improved PostgreSQL and SQLite support.
- Numerous critical performance improvements.
- Numerous critical fixes to the upgrade path.
- Numerous fixes to language and translation systems.
- Numerous fixes to AJAX and #states systems.
- Improvements to the locking system.
- Numerous documentation fixes.
- Numerous styling and theme system fixes.
- Numerous fixes for schema mis-matches between Drupal 6 and 7.
- Minor internal API clean-ups.
Drupal 7.1, 2011-05-25
----------------------
- Fixed security issues (Cross site scripting, File access bypass), see SA-CORE-2011-001.
Drupal 7.0, 2011-01-05
----------------------
- Database:
* Fully rewritten database layer utilizing PHP 5's PDO abstraction layer.
......@@ -34,8 +1007,8 @@ Drupal 7.0, xxxx-xx-xx (development version)
order can now be customized using the Views module.
* Removed the 'related terms' feature from taxonomy module since this can
now be achieved with Field API.
* Added additional features to the default install profile, and implemented
a "slimmed down" install profile designed for developers.
* Added additional features to the default installation profile, and
implemented a "slimmed down" profile designed for developers.
* Added a built-in, automated cron run feature, which is triggered by site
visitors.
* Added an administrator role which is assigned all permissions for
......@@ -218,6 +1191,159 @@ Drupal 7.0, xxxx-xx-xx (development version)
* Added a locking framework to coordinate long-running operations across
requests.
Drupal 6.23-dev, xxxx-xx-xx (development release)
---------------------------
Drupal 6.22, 2011-05-25
-----------------------
- Made Drupal 6 work better with IIS and Internet Explorer.
- Fixed .po file imports to work better with custom textgroups.
- Improved code documentation at various places.
- Fixed a variety of other bugs.
Drupal 6.21, 2011-05-25
-----------------------
- Fixed security issues (Cross site scripting), see SA-CORE-2011-001.
Drupal 6.20, 2010-12-15
-----------------------
- Fixed a variety of small bugs, improved code documentation.
Drupal 6.19, 2010-08-11
-----------------------
- Fixed a variety of small bugs, improved code documentation.
Drupal 6.18, 2010-08-11
-----------------------
- Fixed security issues (OpenID authentication bypass, File download access
bypass, Comment unpublishing bypass, Actions cross site scripting),
see SA-CORE-2010-002.
Drupal 6.17, 2010-06-02
-----------------------
- Improved PostgreSQL compatibility
- Better PHP 5.3 and PHP 4 compatibility
- Better browser compatibility of CSS and JS aggregation
- Improved logging for login failures
- Fixed an incompatibility with some contributed modules and the locking system
- Fixed a variety of other bugs.
Drupal 6.16, 2010-03-03
-----------------------
- Fixed security issues (Installation cross site scripting, Open redirection,
Locale module cross site scripting, Blocked user session regeneration),
see SA-CORE-2010-001.
- Better support for updated jQuery versions.
- Reduced resource usage of update.module.
- Fixed several issues relating to support of installation profiles and
distributions.
- Added a locking framework to avoid data corruption on long operations.
- Fixed a variety of other bugs.
Drupal 6.15, 2009-12-16
-----------------------
- Fixed security issues (Cross site scripting), see SA-CORE-2009-009.
- Fixed a variety of other bugs.
Drupal 6.14, 2009-09-16
-----------------------
- Fixed security issues (OpenID association cross site request forgeries,
OpenID impersonation and File upload), see SA-CORE-2009-008.
- Changed the system modules page to not run all cache rebuilds; use the
button on the performance settings page to achieve the same effect.
- Added support for PHP 5.3.0 out of the box.
- Fixed a variety of small bugs.
Drupal 6.13, 2009-07-01
-----------------------
- Fixed security issues (Cross site scripting, Input format access bypass and
Password leakage in URL), see SA-CORE-2009-007.
- Fixed a variety of small bugs.
Drupal 6.12, 2009-05-13
-----------------------
- Fixed security issues (Cross site scripting), see SA-CORE-2009-006.
- Fixed a variety of small bugs.
Drupal 6.11, 2009-04-29
-----------------------
- Fixed security issues (Cross site scripting and limited information
disclosure), see SA-CORE-2009-005
- Fixed performance issues with the menu router cache, the update
status cache and improved cache invalidation
- Fixed a variety of small bugs.
Drupal 6.10, 2009-02-25
-----------------------
- Fixed a security issue, (Local file inclusion on Windows),
see SA-CORE-2009-003
- Fixed node_feed() so custom fields can show up in RSS feeds.
- Improved PostgreSQL compatibility.
- Fixed a variety of small bugs.
Drupal 6.9, 2009-01-14
----------------------
- Fixed security issues, (Access Bypass, Validation Bypass and Hardening
against SQL injection), see SA-CORE-2009-001
- Made HTTP request checking more robust and informative.
- Fixed HTTP_HOST checking to work again with HTTP 1.0 clients and
basic shell scripts.
- Removed t() calls from all schema documentation. Suggested best practice
changed for contributed modules, see http://drupal.org/node/322731.
- Fixed a variety of small bugs.
Drupal 6.8, 2008-12-11
----------------------
- Removed a previous change incompatible with PHP 5.1.x and lower.
Drupal 6.7, 2008-12-10
----------------------
- Fixed security issues, (Cross site request forgery and Cross site scripting), see SA-2008-073
- Updated robots.txt and .htaccess to match current file use.
- Fixed a variety of small bugs.
Drupal 6.6, 2008-10-22
----------------------
- Fixed security issues, (File inclusion, Cross site scripting), see SA-2008-067
- Fixed a variety of small bugs.
Drupal 6.5, 2008-10-08
----------------------
- Fixed security issues, (File upload access bypass, Access rules bypass,
BlogAPI access bypass), see SA-2008-060.
- Fixed a variety of small bugs.
Drupal 6.4, 2008-08-13
----------------------
- Fixed a security issue (Cross site scripting, Arbitrary file uploads via
BlogAPI, Cross site request forgeries and Various Upload module
vulnerabilities), see SA-2008-047.
- Improved error messages during installation.
- Fixed a bug that prevented AHAH handlers to be attached to radios widgets.
- Fixed a variety of small bugs.
Drupal 6.3, 2008-07-09
----------------------
- Fixed security issues, (Cross site scripting, cross site request forgery,
session fixation and SQL injection), see SA-2008-044.
- Slightly modified installation process to prevent file ownership issues on
shared hosts.
- Improved PostgreSQL compatibility (rewritten queries; custom blocks).
- Upgraded to jQuery 1.2.6.
- Performance improvements to search, menu handling and form API caches.
- Fixed Views compatibility issues (Views for Drupal 6 requires Drupal 6.3+).
- Fixed a variety of small bugs.
Drupal 6.2, 2008-04-09
----------------------
- Fixed a variety of small bugs.
- Fixed a security issue (Access bypasses), see SA-2008-026.
Drupal 6.1, 2008-02-27
----------------------
- Fixed a variety of small bugs.
- Fixed a security issue (Cross site scripting), see SA-2008-018.
Drupal 6.0, 2008-02-13
----------------------
- New, faster and better menu system.
......@@ -227,7 +1353,7 @@ Drupal 6.0, 2008-02-13
* Expands the severity levels from 3 (Error, Warning, Notice) to the 8
levels defined in RFC 3164.
* The watchdog module is now called dblog, and is optional, but enabled by
default in the default install profile.
default in the default installation profile.
* Extended the database log module so log messages can be filtered.
* Added syslog module: useful for monitoring large Drupal installations.
- Added optional e-mail notifications when users are approved, blocked, or
......@@ -282,7 +1408,7 @@ Drupal 6.0, 2008-02-13
* Themed the installer with the Garland theme.
* Added form to provide initial site information during installation.
* Added ability to provide extra installation steps programmatically.
* Made it possible to import interface translations at install time.
* Made it possible to import interface translations during installation.
- Added the HTML corrector filter:
* Fixes faulty and chopped off HTML in postings.
* Tags are now automatically closed at the end of the teaser.
......@@ -320,6 +1446,95 @@ Drupal 6.0, 2008-02-13
- Removed old system updates. Updates from Drupal versions prior to 5.x will
require upgrading to 5.x before upgrading to 6.x.
Drupal 5.23, 2010-08-11
-----------------------
- Fixed security issues (File download access bypass, Comment unpublishing
bypass), see SA-CORE-2010-002.
Drupal 5.22, 2010-03-03
-----------------------
- Fixed security issues (Open redirection, Locale module cross site scripting,
Blocked user session regeneration), see SA-CORE-2010-001.
Drupal 5.21, 2009-12-16
-----------------------
- Fixed a security issue (Cross site scripting), see SA-CORE-2009-009.
- Fixed a variety of small bugs.
Drupal 5.20, 2009-09-16
-----------------------
- Avoid security problems resulting from writing Drupal 6-style menu
declarations.
- Fixed security issues (session fixation), see SA-CORE-2009-008.
- Fixed a variety of small bugs.
Drupal 5.19, 2009-07-01
-----------------------
- Fixed security issues (Cross site scripting and Password leakage in URL), see
SA-CORE-2009-007.
- Fixed a variety of small bugs.
Drupal 5.18, 2009-05-13
-----------------------
- Fixed security issues (Cross site scripting), see SA-CORE-2009-006.
- Fixed a variety of small bugs.
Drupal 5.17, 2009-04-29
-----------------------
- Fixed security issues (Cross site scripting and limited information
disclosure) see SA-CORE-2009-005.
- Fixed a variety of small bugs.
Drupal 5.16, 2009-02-25
-----------------------
- Fixed a security issue, (Local file inclusion on Windows), see SA-CORE-2009-004.
- Fixed a variety of small bugs.
Drupal 5.15, 2009-01-14
-----------------------
- Fixed security issues, (Hardening against SQL injection), see
SA-CORE-2009-001
- Fixed HTTP_HOST checking to work again with HTTP 1.0 clients and basic shell
scripts.
- Fixed a variety of small bugs.
Drupal 5.14, 2008-12-11
-----------------------
- removed a previous change incompatible with PHP 5.1.x and lower.
Drupal 5.13, 2008-12-10
-----------------------
- fixed a variety of small bugs.
- fixed security issues, (Cross site request forgery and Cross site scripting), see SA-2008-073
- updated robots.txt and .htaccess to match current file use.
Drupal 5.12, 2008-10-22
-----------------------
- fixed security issues, (File inclusion), see SA-2008-067
Drupal 5.11, 2008-10-08
-----------------------
- fixed a variety of small bugs.
- fixed security issues, (File upload access bypass, Access rules bypass,
BlogAPI access bypass, Node validation bypass), see SA-2008-060
Drupal 5.10, 2008-08-13
-----------------------
- fixed a variety of small bugs.
- fixed security issues, (Cross site scripting, Arbitrary file uploads via
BlogAPI and Cross site request forgery), see SA-2008-047
Drupal 5.9, 2008-07-23
----------------------
- fixed a variety of small bugs.
- fixed security issues, (Session fixation), see SA-2008-046
Drupal 5.8, 2008-07-09
----------------------
- fixed a variety of small bugs.
- fixed security issues, (Cross site scripting, cross site request forgery, and
session fixation), see SA-2008-044
Drupal 5.7, 2008-01-28
----------------------
- fixed the input format configuration page.
......@@ -372,7 +1587,7 @@ Drupal 5.0, 2007-01-15
- Added web-based installer which can:
* Check installation and run-time requirements
* Automatically generate the database configuration file
* Install pre-made 'install profiles' or distributions
* Install pre-made installation profiles or distributions
* Import the database structure with automatic table prefixing
* Be localized
- Added new default Garland theme
......@@ -432,7 +1647,7 @@ Drupal 5.0, 2007-01-15
- Removed the archive module.
- Upgrade system:
* Created space for update branches.
- Forms API:
- Form API:
* Made it possible to programmatically submit forms.
* Improved api for multistep forms.
- Theme system:
......@@ -455,7 +1670,7 @@ Drupal 4.7.9, 2007-12-05
- fixed a security issue (SQL injection), see SA-2007-031
Drupal 4.7.8, 2007-10-17
----------------------
------------------------
- fixed a security issue (HTTP response splitting), see SA-2007-024
- fixed a security issue (Cross site scripting via uploads), see SA-2007-026
- fixed a security issue (API handling of unpublished comment), see SA-2007-030
......@@ -568,7 +1783,7 @@ Drupal 4.6.11, 2007-01-05
- Fixed security issue (DoS), see SA-2007-002
Drupal 4.6.10, 2006-10-18
------------------------
-------------------------
- Fixed security issue (XSS), see SA-2006-024
- Fixed security issue (CSRF), see SA-2006-025
- Fixed security issue (Form action attribute injection), see SA-2006-026
......@@ -736,7 +1951,7 @@ Drupal 4.5.0, 2004-10-18
- Filter system:
* Added support for using multiple input formats on the site
* Expanded the embedded PHP-code feature so it can be used everywhere
* Added support for role-dependant filtering, through input formats
* Added support for role-dependent filtering, through input formats
- UI translation:
* Managing translations is now completely done through the administration interface
* Added support for importing/exporting gettext .po files
......
All Drupal code is Copyright 2001 - 2010 by the original authors.
All Drupal code is Copyright 2001 - 2013 by the original authors.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation.
the Free Software Foundation; either version 2 of the License, or (at
your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
......@@ -20,5 +20,25 @@ Drupal includes works under other copyright notices and distributed
according to the terms of the GNU General Public License or a compatible
license, including:
jQuery - Copyright (c) 2008 - 2009 John Resig
Javascript
Farbtastic - Copyright (c) 2010 Matt Farina
jQuery - Copyright (c) 2010 John Resig
jQuery BBQ - Copyright (c) 2010 "Cowboy" Ben Alman
jQuery Cookie - Copyright (c) 2006 Klaus Hartl
jQuery Form - Copyright (c) 2010 Mike Alsup
jQuery Once - Copyright (c) 2009 Konstantin K�fer
jQuery UI - Copyright (c) 2010 by the original authors
(http://jqueryui.com/about)
Sizzle.js - Copyright (c) 2010 The Dojo Foundation (http://sizzlejs.com/)
PHP
ArchiveTar - Copyright (c) 1997 - 2008 Vincent Blavet
......@@ -18,20 +18,23 @@ initial database files. Next you must log in and set the access database rights:
mysql -u username -p
Again, you will be asked for the 'username' database password. At the MySQL
prompt, enter following command:
prompt, enter the following command:
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER
ON databasename.*
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER,
CREATE TEMPORARY TABLES ON databasename.*
TO 'username'@'localhost' IDENTIFIED BY 'password';
where
where:
'databasename' is the name of your database
'username@localhost' is the username of your MySQL account
'username' is the username of your MySQL account
'localhost' is the web server host where Drupal is installed
'password' is the password required for that username
Note: Unless your database user has all of the privileges listed above, you will
not be able to run Drupal.
Note: Unless the database user/host combination for your Drupal installation
has all of the privileges listed above (except possibly CREATE TEMPORARY TABLES,
which is currently only used by Drupal core automated tests and some
contributed modules), you will not be able to install or run Drupal.
If successful, MySQL will reply with:
......
......@@ -20,8 +20,10 @@ Drupal requires:
- MySQL 5.0.15 (or greater) (http://www.mysql.com/).
- MariaDB 5.1.44 (or greater) (http://mariadb.org/). MariaDB is a fully
compatible drop-in replacement for MySQL.
- Percona Server 5.1.70 (or greater) (http://www.percona.com/). Percona
Server is a backwards-compatible replacement for MySQL.
- PostgreSQL 8.3 (or greater) (http://www.postgresql.org/).
- SQLite 3.4.2 (or greater) (http://www.sqlite.org/).
- SQLite 3.3.7 (or greater) (http://www.sqlite.org/).
For more detailed information about Drupal requirements, including a list of
PHP extensions and configurations that are required, see "System requirements"
......@@ -89,8 +91,8 @@ INSTALLATION
- Download a translation file for the correct Drupal version and language
from the translation server: http://localize.drupal.org/translate/downloads
- Place the file into your installation profile's translations
directory. For instance, if you are using the Standard install profile,
- Place the file into your installation profile's translations directory.
For instance, if you are using the Standard installation profile,
move the .po file into the directory:
profiles/standard/translations/
......
Drupal core is maintained by the community. To participate, go to
http://drupal.org/contribute
The people listed here have agreed to do more quality assurance work for
particular areas of Drupal. All of them are subject to change.
Drupal core is built and maintained by the Drupal project community. Everyone is
encouraged to submit issues and changes (patches) to improve Drupal, and to
contribute in other ways -- see https://www.drupal.org/contribute to find out
how.
Branch maintainers
------------------
Drupal 7
- Dries Buytaert 'dries' <http://drupal.org/user/1>
- Angela Byron 'webchick' <http://drupal.org/user/24967>
The Drupal Core branch maintainers oversee the development of Drupal as a whole.
The branch maintainers for Drupal 7 are:
- Dries Buytaert 'dries' https://www.drupal.org/u/dries
- Angela Byron 'webchick' https://www.drupal.org/u/webchick
- Fabian Franz 'Fabianx' https://www.drupal.org/u/fabianx
- David Rothstein 'David_Rothstein' https://www.drupal.org/u/david_rothstein
- Stefan Ruijsenaars 'stefan.r' https://www.drupal.org/u/stefanr-0
Component maintainers
---------------------
The Drupal Core component maintainers oversee the development of Drupal
subsystems. See https://www.drupal.org/contribute/core-maintainers for more
information on their responsibilities, and to find out how to become a component
maintainer. Current component maintainers for Drupal 7:
Ajax system
- Alex Bronstein 'effulgentsia' <http://drupal.org/user/78040>
- Randy Fay 'rfay' <http://drupal.org/user/30906>
- Earl Miles 'merlinofchaos' <http://drupal.org/user/26979>
- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
- Earl Miles 'merlinofchaos' https://www.drupal.org/u/merlinofchaos
Base system
- Károly Négyesi 'chx' <http://drupal.org/user/9446>
- Damien Tournoud 'DamZ' <http://drupal.org/user/22211>
- Moshe Weitzman 'moshe weitzman' <http://drupal.org/user/23>
- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
Batch system
- Yves Chedemois 'yched' <http://drupal.org/user/39567>
- Yves Chedemois 'yched' https://www.drupal.org/u/yched
Cache system
- Damien Tournoud 'DamZ' <http://drupal.org/user/22211>
- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
Cron system
- Károly Négyesi 'chx' <http://drupal.org/user/9446>
- Derek Wright 'dww' <http://drupal.org/user/46549>
- Derek Wright 'dww' https://www.drupal.org/u/dww
Database system
- Larry Garfield 'Crell' <http://drupal.org/user/26398>
- Larry Garfield 'Crell' https://www.drupal.org/u/crell
- MySQL driver
- Larry Garfield 'Crell' <http://drupal.org/user/26398>
- David Strauss 'David Strauss' <hhttp://drupal.org/user/93254>
- Larry Garfield 'Crell' https://www.drupal.org/u/crell
- David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
- PostgreSQL driver
- Damien Tournoud 'DamZ' <http://drupal.org/user/22211>
- Josh Waihi 'fiasco' <http://drupal.org/user/188162>
- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
- Josh Waihi 'fiasco' https://www.drupal.org/u/josh-waihi
- Sqlite driver
- Damien Tournoud 'DamZ' <http://drupal.org/user/22211>
- Károly Négyesi 'chx' <http://drupal.org/user/9446>
- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
Database update system
- ?
- Ashok Modi 'BTMash' https://www.drupal.org/u/btmash
Entity system
- Nathaniel Catchpole 'catch' <http://drupal.org/user/35733>
- Franz Heinzmann 'Frando' <http://drupal.org/user/21850>
- Wolfgang Ziegler 'fago' https://www.drupal.org/u/fago
- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
- Franz Heinzmann 'Frando' https://www.drupal.org/u/frando
File system
- Andrew Morton 'drewish' <http://drupal.org/user/34869>
- Aaron Winborn 'aaron' <http://drupal.org/user/33420>
- Andrew Morton 'drewish' https://www.drupal.org/u/drewish
- Aaron Winborn 'aaron' https://www.drupal.org/u/aaron
Form system
- Károly Négyesi 'chx' <http://drupal.org/user/9446>
- Alex Bronstein 'effulgentsia' <http://drupal.org/user/78040>
- Wolfgang Ziegler 'fago' <http://drupal.org/user/16747>
- Daniel F. Kudwien 'sun' <http://drupal.org/user/54136>
- Franz Heinzmann 'Frando' <http://drupal.org/user/21850>
- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
- Wolfgang Ziegler 'fago' https://www.drupal.org/u/fago
- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
- Franz Heinzmann 'Frando' https://www.drupal.org/u/frando
Image system
- Andrew Morton 'drewish' <http://drupal.org/user/34869>
- Nathan Haug 'quicksketch' <http://drupal.org/user/35821>
- Andrew Morton 'drewish' https://www.drupal.org/u/drewish
- Nathan Haug 'quicksketch' https://www.drupal.org/u/quicksketch
Install system
- David Rothstein 'David_Rothstein' <http://drupal.org/user/124982>
- David Rothstein 'David_Rothstein' https://www.drupal.org/u/david_rothstein
JavaScript
- ?
- Théodore Biadala 'nod_' https://www.drupal.org/u/nod_
- Steve De Jonghe 'seutje' https://www.drupal.org/u/seutje
Language system
- Francesco Placella 'plach' <http://drupal.org/user/183211>
- Daniel F. Kudwien 'sun' <http://drupal.org/user/54136>
- Francesco Placella 'plach' https://www.drupal.org/u/plach
- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
Lock system
- Damien Tournoud 'DamZ' <http://drupal.org/user/22211>
- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
Mail system
- ?
Markup
- Jacine Luisi 'Jacine' <http://drupal.org/user/88931>
- Daniel F. Kudwien 'sun' <http://drupal.org/user/54136>
- Jacine Luisi 'Jacine' https://www.drupal.org/u/jacine
- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
Menu system
- Peter Wolanin 'pwolanin' <http://drupal.org/user/49851>
- Károly Négyesi 'chx' <http://drupal.org/user/9446>
- Peter Wolanin 'pwolanin' https://www.drupal.org/u/pwolanin
Path system
- Dave Reid 'davereid' <http://drupal.org/user/53892>
- Nathaniel Catchpole 'catch' <http://drupal.org/user/35733>
- Dave Reid 'davereid' https://www.drupal.org/u/dave-reid
- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
Render system
- Moshe Weitzman 'moshe weitzman' <http://drupal.org/user/23>
- Alex Bronstein 'effulgentsia' <http://drupal.org/user/78040>
- Franz Heinzmann 'Frando' <http://drupal.org/user/21850>
- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
- Franz Heinzmann 'Frando' https://www.drupal.org/u/frando
Theme system
- Earl Miles 'merlinofchaos' <http://drupal.org/user/26979>
- Alex Bronstein 'effulgentsia' <http://drupal.org/user/78040>
- Joon Park 'dvessel' <http://drupal.org/user/56782>
- John Albin Wilkins 'JohnAlbin' <http://drupal.org/user/32095>
- Earl Miles 'merlinofchaos' https://www.drupal.org/u/merlinofchaos
- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
- Joon Park 'dvessel' https://www.drupal.org/u/dvessel
- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
Token system
- Dave Reid 'davereid' <http://drupal.org/user/53892>
- Dave Reid 'davereid' https://www.drupal.org/u/dave-reid
XML-RPC system
- Frederic G. Marand 'fgm' <http://drupal.org/user/27985>
- Frederic G. Marand 'fgm' https://www.drupal.org/u/fgm
Topic coordinators
------------------
Accessibility
- Everett Zufelt 'Everett Zufelt' <http://drupal.org/user/406552>
- Brandon Bowersox 'brandonojc' <http://drupal.org/user/186415>
- Everett Zufelt 'Everett Zufelt' https://www.drupal.org/u/everett-zufelt
- Brandon Bowersox-Johnson 'bowersox' https://www.drupal.org/u/bowersox
Documentation
- Ariane Khachatourians 'arianek' <http://drupal.org/user/158886>
- Jennifer Hodgdon 'jhodgdon' <http://drupal.org/user/155601>
Security
- Heine Deelstra 'Heine' <http://drupal.org/user/17943>
- Jennifer Hodgdon 'jhodgdon' https://www.drupal.org/u/jhodgdon
Translations
- Gerhard Killesreiter 'killes' <http://drupal.org/user/83>
- Gerhard Killesreiter 'killes' https://www.drupal.org/u/gerhard-killesreiter
User experience and usability
- Roy Scholten 'yoroy' <http://drupal.org/user/41502>
- Bojhan Somers 'Bojhan' <http://drupal.org/user/87969>
- Roy Scholten 'yoroy' https://www.drupal.org/u/yoroy
- Bojhan Somers 'Bojhan' https://www.drupal.org/u/bojhan
Node Access
- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
- Ken Rickard 'agentrickard' https://www.drupal.org/u/agentrickard
Security team
-----------------
To report a security issue, see: https://www.drupal.org/security-team/report-issue
The Drupal security team provides Security Advisories for vulnerabilities,
assists developers in resolving security issues, and provides security
documentation. See https://www.drupal.org/security-team for more information.
The security team lead is:
- Michael Hess 'mlhess' https://www.drupal.org/u/mlhess
Module maintainers
......@@ -150,142 +167,141 @@ Aggregator module
- ?
Block module
- John Albin Wilkins 'JohnAlbin' <http://drupal.org/user/32095>
- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
Blog module
- ?
Book module
- Peter Wolanin 'pwolanin' <http://drupal.org/user/49851>
- Peter Wolanin 'pwolanin' https://www.drupal.org/u/pwolanin
Color module
- ?
Comment module
- Nathaniel Catchpole 'catch' <http://drupal.org/user/35733>
- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
Contact module
- Dave Reid 'davereid' <http://drupal.org/user/53892>
- Dave Reid 'davereid' https://www.drupal.org/u/dave-reid
Contextual module
- Daniel F. Kudwien 'sun' <http://drupal.org/user/54136>
- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
Dashboard module
- ?
Database logging module
- Khalid Baheyeldin 'kbahey' <http://drupal.org/user/4063>
- Khalid Baheyeldin 'kbahey' https://www.drupal.org/u/kbahey
Field module
- Yves Chedemois 'yched' <http://drupal.org/user/39567>
- Barry Jaspan 'bjaspan' <http://drupal.org/user/46413>
- Yves Chedemois 'yched' https://www.drupal.org/u/yched
- Barry Jaspan 'bjaspan' https://www.drupal.org/u/bjaspan
Field UI module
- Yves Chedemois 'yched' <http://drupal.org/user/39567>
- Yves Chedemois 'yched' https://www.drupal.org/u/yched
File module
- Aaron Winborn 'aaron' <http://drupal.org/user/33420>
- Aaron Winborn 'aaron' https://www.drupal.org/u/aaron
Filter module
- Daniel F. Kudwien 'sun' <http://drupal.org/user/54136>
- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
Forum module
- ?
- Lee Rowlands 'larowlan' https://www.drupal.org/u/larowlan
Help module
- ?
Image module
- Nathan Haug 'quicksketch' <http://drupal.org/user/35821>
- Nathan Haug 'quicksketch' https://www.drupal.org/u/quicksketch
Locale module
- Gábor Hojtsy 'Gábor Hojtsy' <http://drupal.org/user/4166>
- Gábor Hojtsy 'Gábor Hojtsy' https://www.drupal.org/u/gábor-hojtsy
Menu module
- ?
Node module
- Moshe Weitzman 'moshe weitzman' <http://drupal.org/user/23>
- David Strauss 'David Strauss' <http://drupal.org/user/93254>
- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
- David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
OpenID module
- Heine Deelstra 'Heine' <http://drupal.org/user/17943>
- Christian Schmidt 'c960657' <http://drupal.org/user/216078>
- Damien Tournoud 'DamZ' <http://drupal.org/user/22211>
- Vojtech Kusy 'wojtha' https://www.drupal.org/u/wojtha
- Christian Schmidt 'c960657' https://www.drupal.org/u/c960657
- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
Overlay module
- Katherine Senzee 'ksenzee' <http://drupal.org/user/139855>
- Katherine Senzee 'ksenzee' https://www.drupal.org/u/ksenzee
Path module
- Dave Reid 'davereid' <http://drupal.org/user/53892>
- Dave Reid 'davereid' https://www.drupal.org/u/dave-reid
PHP module
- ?
Poll module
- ?
- Andrei Mateescu 'amateescu' https://www.drupal.org/u/amateescu
Profile module
- ?
RDF module
- Stéphane Corlosquet 'scor' <http://drupal.org/user/52142>
- Stéphane Corlosquet 'scor' https://www.drupal.org/u/scor
Search module
- Doug Green 'douggreen' <http://drupal.org/user/29191>
- Doug Green 'douggreen' https://www.drupal.org/u/douggreen
Shortcut module
- David Rothstein 'David_Rothstein' <http://drupal.org/user/124982>
- David Rothstein 'David_Rothstein' https://www.drupal.org/u/david_rothstein
Simpletest module
- Jimmy Berry 'boombatower' <http://drupal.org/user/214218>
- Károly Négyesi 'chx' <http://drupal.org/user/9446>
- Jimmy Berry 'boombatower' https://www.drupal.org/u/boombatower
Statistics module
- Dave Reid 'davereid' <http://drupal.org/user/53892>
- Tim Millwood 'timmillwood' https://www.drupal.org/u/timmillwood
Syslog module
- Khalid Baheyeldin 'kbahey' <http://drupal.org/user/4063>
- Khalid Baheyeldin 'kbahey' https://www.drupal.org/u/kbahey
System module
- ?
Taxonomy module
- Nathaniel Catchpole 'catch' <http://drupal.org/user/35733>
- Benjamin Doherty 'bangpound' <http://drupal.org/user/100456>
- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
- Benjamin Doherty 'bangpound' https://www.drupal.org/u/bangpound
Toolbar module
- ?
Tracker module
- David Strauss 'David Strauss' <http://drupal.org/user/93254>
- David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
Translation module
- Francesco Placella 'plach' <http://drupal.org/user/183211>
- Francesco Placella 'plach' https://www.drupal.org/u/plach
Trigger module
- ?
Update module
- Derek Wright 'dww' <http://drupal.org/user/46549>
- Derek Wright 'dww' https://www.drupal.org/u/dww
User module
- Moshe Weitzman 'moshe weitzman' <http://drupal.org/user/23>
- David Strauss 'David Strauss' <http://drupal.org/user/93254>
- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
- David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
Theme maintainers
-----------------
Bartik theme
- Jen Simmons 'jensimmons' <http://drupal.org/user/140882>
- Jeff Burns 'Jeff Burnz' <http://drupal.org/user/61393>
- Jen Simmons 'jensimmons' https://www.drupal.org/u/jensimmons
- Jeff Burns 'Jeff Burnz' https://www.drupal.org/u/jeff-burnz
Garland theme
- John Albin Wilkins 'JohnAlbin' <http://drupal.org/user/32095>
- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
Seven theme
- Jeff Burns 'Jeff Burnz' <http://drupal.org/user/61393>
- Jeff Burns 'Jeff Burnz' https://www.drupal.org/u/jeff-burnz
Stark theme
- John Albin Wilkins 'JohnAlbin' <http://drupal.org/user/32095>
- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
......@@ -4,6 +4,7 @@ CONTENTS OF THIS FILE
* About Drupal
* Configuration and features
* Installation profiles
* Appearance
* Developing for Drupal
......@@ -43,6 +44,40 @@ More about configuration:
http://drupal.org/project/modules
* See also: "Developing for Drupal" for writing your own modules, below.
INSTALLATION PROFILES
---------------------
Installation profiles define additional steps (such as enabling modules,
defining content types, etc.) that run after the base installation provided
by core when Drupal is first installed. There are two basic installation
profiles provided with Drupal core.
Installation profiles from the Drupal community modify the installation process
to provide a website for a specific use case, such as a CMS for media
publishers, a web-based project tracking tool, or a full-fledged CRM for
non-profit organizations raising money and accepting donations. They can be
distributed as bare installation profiles or as "distributions". Distributions
include Drupal core, the installation profile, and all other required
extensions, such as contributed and custom modules, themes, and third-party
libraries. Bare installation profiles require you to download Drupal Core and
the required extensions separately; place the downloaded profile in the
/profiles directory before you start the installation process. Note that the
contents of this directory may be overwritten during updates of Drupal core;
it is advised to keep code backups or use a version control system.
Additionally, modules and themes may be placed inside subdirectories in a
specific installation profile such as profiles/your_site_profile/modules and
profiles/your_site_profile/themes respectively to restrict their usage to only
sites that were installed with that specific profile.
More about installation profiles and distributions:
* Read about the difference between installation profiles and distributions:
http://drupal.org/node/1089736
* Download contributed installation profiles and distributions:
http://drupal.org/project/distributions
* Develop your own installation profile or distribution:
http://drupal.org/developing/distributions
APPEARANCE
----------
......
INTRODUCTION
------------
This document describes how to:
......@@ -25,6 +24,11 @@ First steps and definitions:
applying it to your live site. Even minor updates can cause your site's
behavior to change.
* Each new release of Drupal has release notes, which explain the changes made
since the previous version and any special instructions needed to update or
upgrade to the new version. You can find a link to the release notes for the
version you are upgrading or updating to on the Drupal project page
(http://drupal.org/project/drupal).
UPGRADE PROBLEMS
----------------
......@@ -40,7 +44,6 @@ If you encounter errors during this process,
More in-depth information on upgrading can be found at http://drupal.org/upgrade
MINOR VERSION UPDATES
---------------------
To update from one minor 7.x version of Drupal to any later 7.x version, after
......@@ -58,11 +61,28 @@ following the instructions in the INTRODUCTION section at the top of this file:
If you made modifications to files like .htaccess or robots.txt, you will
need to re-apply them from your backup, after the new files are in place.
Sometimes an update includes changes to settings.php (this will be noted in
the release announcement). If that's the case, replace your old settings.php
with the new one, and copy the site-specific entries (especially the lines
giving the database name, user, and password) from the old settings.php to
the new settings.php.
Sometimes an update includes changes to default.settings.php (this will be
noted in the release notes). If that's the case, follow these steps:
- Locate your settings.php file in the /sites/* directory. (Typically
sites/default.)
- Make a backup copy of your settings.php file, with a different file name.
- Make a copy of the new default.settings.php file, and name the copy
settings.php (overwriting your previous settings.php file).
- Copy the custom and site-specific entries from the backup you made into the
new settings.php file. You will definitely need the lines giving the
database information, and you will also want to copy in any other
customizations you have added.
You can find the release notes for your version at
https://www.drupal.org/project/drupal. At bottom of the project page under
"Downloads" use the link for your version of Drupal to view the release
notes. If your version is not listed, use the 'View all releases' link. From
this page you can scroll down or use the filter to find your version and its
release notes.
4. Download the latest Drupal 7.x release from http://drupal.org to a
directory outside of your web root. Extract the archive and copy the files
......@@ -110,7 +130,6 @@ following the instructions in the INTRODUCTION section at the top of this file:
Disable the "Put site into maintenance mode" checkbox and save the
configuration.
MAJOR VERSION UPGRADE
---------------------
To upgrade from a previous major version of Drupal to Drupal 7.x, after
......@@ -132,15 +151,19 @@ following the instructions in the INTRODUCTION section at the top of this file:
download Drupal 6.x and follow the instructions in its UPGRADE.txt. This
document only applies for upgrades from 6.x to 7.x.
3. Log in as user ID 1 (the site maintenance user).
3. In addition to updating to the latest available version of Drupal 6.x core,
you must also upgrade all of your contributed modules for Drupal to their
latest Drupal 6.x versions.
4. Go to Administer > Site configuration > Site maintenance. Select
4. Log in as user ID 1 (the site maintenance user).
5. Go to Administer > Site configuration > Site maintenance. Select
"Off-line" and save the configuration.
5. Go to Administer > Site building > Themes. Enable "Garland" and select it as
6. Go to Administer > Site building > Themes. Enable "Garland" and select it as
the default theme.
6. Go to Administer > Site building > Modules. Disable all modules that are not
7. Go to Administer > Site building > Modules. Disable all modules that are not
listed under "Core - required" or "Core - optional". It is possible that some
modules cannot be disabled, because others depend on them. Repeat this step
until all non-core modules are disabled.
......@@ -149,21 +172,21 @@ following the instructions in the INTRODUCTION section at the top of this file:
no longer need their data, then you can uninstall them under the Uninstall
tab after disabling them.
7. On the command line or in your FTP client, remove the file
8. On the command line or in your FTP client, remove the file
sites/default/default.settings.php
8. Remove all old core files and directories, except for the 'sites' directory
9. Remove all old core files and directories, except for the 'sites' directory
and any custom files you added elsewhere.
If you made modifications to files like .htaccess or robots.txt, you will
need to re-apply them from your backup, after the new files are in place.
9. If you uninstalled any modules, remove them from the sites/all/modules and
10. If you uninstalled any modules, remove them from the sites/all/modules and
other sites/*/modules directories. Leave other modules in place, even though
they are incompatible with Drupal 7.x.
10. Download the latest Drupal 7.x release from http://drupal.org to a
11. Download the latest Drupal 7.x release from http://drupal.org to a
directory outside of your web root. Extract the archive and copy the files
into your Drupal directory.
......@@ -182,14 +205,14 @@ following the instructions in the INTRODUCTION section at the top of this file:
from http://drupal.org using your web browser, extract it, and then use an
FTP client to upload the files to your web root.
11. Re-apply any modifications to files such as .htaccess or robots.txt.
12. Re-apply any modifications to files such as .htaccess or robots.txt.
12. Make your settings.php file writeable, so that the update process can
13. Make your settings.php file writeable, so that the update process can
convert it to the format of Drupal 7.x. settings.php is usually located in
sites/default/settings.php
13. Run update.php by visiting http://www.example.com/update.php (replace
14. Run update.php by visiting http://www.example.com/update.php (replace
www.example.com with your domain name). This will update the core database
tables.
......@@ -205,20 +228,19 @@ following the instructions in the INTRODUCTION section at the top of this file:
- Once the upgrade is done, $update_free_access must be reverted to FALSE.
14. Backup your database after the core upgrade has run.
15. Backup your database after the core upgrade has run.
15. Replace and update your non-core modules and themes, following the
16. Replace and update your non-core modules and themes, following the
procedures at http://drupal.org/node/948216
16. Go to Administration > Reports > Status report. Verify that everything is
17. Go to Administration > Reports > Status report. Verify that everything is
working as expected.
17. Ensure that $update_free_access is FALSE in settings.php.
18. Ensure that $update_free_access is FALSE in settings.php.
18. Go to Administration > Configuration > Development > Maintenance mode.
19. Go to Administration > Configuration > Development > Maintenance mode.
Disable the "Put site into maintenance mode" checkbox and save the
configuration.
To get started with Drupal 7 administration, visit
http://drupal.org/getting-started/7/admin
......@@ -4,16 +4,16 @@
* @file
* Administrative script for running authorized file operations.
*
* Using this script, the site owner (the user actually owning the files on
* the webserver) can authorize certain file-related operations to proceed
* with elevated privileges, for example to deploy and upgrade modules or
* themes. Users should not visit this page directly, but instead use an
* administrative user interface which knows how to redirect the user to this
* script as part of a multistep process. This script actually performs the
* selected operations without loading all of Drupal, to be able to more
* gracefully recover from errors. Access to the script is controlled by a
* global killswitch in settings.php ('allow_authorize_operations') and via
* the 'administer software updates' permission.
* Using this script, the site owner (the user actually owning the files on the
* webserver) can authorize certain file-related operations to proceed with
* elevated privileges, for example to deploy and upgrade modules or themes.
* Users should not visit this page directly, but instead use an administrative
* user interface which knows how to redirect the user to this script as part of
* a multistep process. This script actually performs the selected operations
* without loading all of Drupal, to be able to more gracefully recover from
* errors. Access to the script is controlled by a global killswitch in
* settings.php ('allow_authorize_operations') and via the 'administer software
* updates' permission.
*
* There are helper functions for setting up an operation to run via this
* system in modules/system/system.module. For more information, see:
......@@ -21,21 +21,22 @@
*/
/**
* Root directory of Drupal installation.
* Defines the root directory of the Drupal installation.
*/
define('DRUPAL_ROOT', getcwd());
/**
* Global flag to identify update.php and authorize.php runs, and so
* avoid various unwanted operations, such as hook_init() and
* hook_exit() invokes, css/js preprocessing and translation, and
* solve some theming issues. This flag is checked on several places
* in Drupal code (not just authorize.php).
* Global flag to identify update.php and authorize.php runs.
*
* Identifies update.php and authorize.php runs, avoiding unwanted operations
* such as hook_init() and hook_exit() invokes, css/js preprocessing and
* translation, and solves some theming issues. The flag is checked in other
* places in Drupal code (not just authorize.php).
*/
define('MAINTENANCE_MODE', 'update');
/**
* Render a 403 access denied page for authorize.php
* Renders a 403 access denied page for authorize.php.
*/
function authorize_access_denied_page() {
drupal_add_http_header('Status', '403 Forbidden');
......@@ -45,13 +46,13 @@ function authorize_access_denied_page() {
}
/**
* Determine if the current user is allowed to run authorize.php.
* Determines if the current user is allowed to run authorize.php.
*
* The killswitch in settings.php overrides all else, otherwise, the user must
* have access to the 'administer software updates' permission.
*
* @return
* TRUE if the current user can run authorize.php, otherwise FALSE.
* TRUE if the current user can run authorize.php, and FALSE if not.
*/
function authorize_access_allowed() {
return variable_get('allow_authorize_operations', TRUE) && user_access('administer software updates');
......@@ -60,7 +61,6 @@ function authorize_access_allowed() {
// *** Real work of the script begins here. ***
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
require_once DRUPAL_ROOT . '/includes/session.inc';
require_once DRUPAL_ROOT . '/includes/common.inc';
require_once DRUPAL_ROOT . '/includes/file.inc';
require_once DRUPAL_ROOT . '/includes/module.inc';
......@@ -74,7 +74,7 @@ function authorize_access_allowed() {
global $conf;
// We have to enable the user and system modules, even to check access and
// display errors via the maintainence theme.
// display errors via the maintenance theme.
$module_list['system']['filename'] = 'modules/system/system.module';
$module_list['user']['filename'] = 'modules/user/user.module';
module_list(TRUE, FALSE, FALSE, $module_list);
......@@ -145,7 +145,7 @@ function authorize_access_allowed() {
l(t('Front page'), '<front>'),
));
}
$output .= theme('item_list', array('items' => $links, 'title' => t('Next steps')));
}
// If a batch is running, let it run.
......@@ -172,4 +172,3 @@ function authorize_access_allowed() {
if (!empty($output)) {
print theme('update_page', array('content' => $output, 'show_messages' => $show_messages));
}
......@@ -22,7 +22,7 @@
* - $a1, $a2: Optional additional information, which can be passed into
* actions_do() and will be passed along to the action function.
*
* @} End of "defgroup actions".
* @}
*/
/**
......@@ -48,6 +48,7 @@
* Passed along to the callback.
* @param $a2
* Passed along to the callback.
*
* @return
* An associative array containing the results of the functions that
* perform the actions, keyed on action ID.
......@@ -149,6 +150,7 @@ function actions_do($action_ids, $object = NULL, $context = NULL, $a1 = NULL, $a
*
* @param $reset
* Reset the action info static cache.
*
* @return
* An associative array keyed on action function name, with the same format
* as the return value of hook_action_info(), containing all
......@@ -176,9 +178,9 @@ function actions_list($reset = FALSE) {
* function and the actions returned by actions_list() are partially
* synchronized. Non-configurable actions from hook_action_info()
* implementations are put into the database when actions_synchronize() is
* called, which happens when admin/config/system/actions is visited. Configurable
* actions are not added to the database until they are configured in the
* user interface, in which case a database row is created for each
* called, which happens when admin/config/system/actions is visited.
* Configurable actions are not added to the database until they are configured
* in the user interface, in which case a database row is created for each
* configuration of each action.
*
* @return
......@@ -205,6 +207,7 @@ function actions_get_all_actions() {
* An associative array with function names or action IDs as keys
* and associative arrays with keys 'label', 'type', etc. as values.
* This is usually the output of actions_list() or actions_get_all_actions().
*
* @return
* An associative array whose keys are hashes of the input array keys, and
* whose corresponding values are associative arrays with components
......@@ -223,7 +226,7 @@ function actions_actions_map($actions) {
}
/**
* Given a hash of an action array key, returns the key (function or ID).
* Returns an action array key (function or ID), given its hash.
*
* Faster than actions_actions_map() when you only need the function name or ID.
*
......@@ -231,6 +234,7 @@ function actions_actions_map($actions) {
* Hash of a function name or action ID array key. The array key
* is a key into the return value of actions_list() (array key is the action
* function name) or actions_get_all_actions() (array key is the action ID).
*
* @return
* The corresponding array key, or FALSE if no match is found.
*/
......@@ -311,7 +315,7 @@ function actions_synchronize($delete_orphans = FALSE) {
$link = l(t('Remove orphaned actions'), 'admin/config/system/actions/orphan');
$count = count($actions_in_db);
$orphans = implode(', ', $orphaned);
watchdog('actions', '@count orphaned actions (%orphans) exist in the actions table. !link', array('@count' => $count, '%orphans' => $orphans, '!link' => $link), WATCHDOG_WARNING);
watchdog('actions', '@count orphaned actions (%orphans) exist in the actions table. !link', array('@count' => $count, '%orphans' => $orphans, '!link' => $link), WATCHDOG_INFO);
}
}
}
......@@ -332,6 +336,7 @@ function actions_synchronize($delete_orphans = FALSE) {
* to Jim'.
* @param $aid
* The ID of this action. If omitted, a new action is created.
*
* @return
* The ID of the action.
*/
......@@ -361,6 +366,7 @@ function actions_save($function, $type, $params, $label, $aid = NULL) {
*
* @param $aid
* The ID of the action to retrieve.
*
* @return
* The appropriate action row from the database as an object.
*/
......@@ -380,4 +386,3 @@ function actions_delete($aid) {
->execute();
module_invoke_all('actions_delete', $aid);
}
......@@ -24,7 +24,8 @@
* ajax_form_callback() and a defined #ajax['callback'] function.
* However, you may optionally specify a different path to request or a
* different callback function to invoke, which can return updated HTML or can
* also return a richer set of @link ajax_commands Ajax framework commands @endlink.
* also return a richer set of
* @link ajax_commands Ajax framework commands @endlink.
*
* Standard form handling is as follows:
* - A form element has a #ajax property that includes #ajax['callback'] and
......@@ -101,7 +102,7 @@
* In the above example, the 'changethis' element is Ajax-enabled. The default
* #ajax['event'] is 'change', so when the 'changethis' element changes,
* an Ajax call is made. The form is submitted and reprocessed, and then the
* callback is called. In this case, the form has been automatically
* callback is called. In this case, the form has been automatically
* built changing $form['replace_textfield']['#description'], so the callback
* just returns that part of the form.
*
......@@ -143,6 +144,21 @@
* - #ajax['event']: The JavaScript event to respond to. This is normally
* selected automatically for the type of form widget being used, and
* is only needed if you need to override the default behavior.
* - #ajax['prevent']: A JavaScript event to prevent when 'event' is triggered.
* Defaults to 'click' for #ajax on #type 'submit', 'button', and
* 'image_button'. Multiple events may be specified separated by spaces.
* For example, when binding #ajax behaviors to form buttons, pressing the
* ENTER key within a textfield triggers the 'click' event of the form's first
* submit button. Triggering Ajax in this situation leads to problems, like
* breaking autocomplete textfields. Because of that, Ajax behaviors are bound
* to the 'mousedown' event on form buttons by default. However, binding to
* 'mousedown' rather than 'click' means that it is possible to trigger a
* click by pressing the mouse, holding the mouse button down until the Ajax
* request is complete and the button is re-enabled, and then releasing the
* mouse button. For this case, 'prevent' can be set to 'click', so an
* additional event handler is bound to prevent such a click from triggering a
* non-Ajax form submission. This also prevents a textfield's ENTER press
* triggering a button's non-Ajax form submission behavior.
* - #ajax['method']: The jQuery method to use to place the new HTML.
* Defaults to 'replaceWith'. May be: 'replaceWith', 'append', 'prepend',
* 'before', 'after', or 'html'. See the
......@@ -152,7 +168,7 @@
* displayed while awaiting a response from the callback, and add an optional
* message. Possible keys: 'type', 'message', 'url', 'interval'.
* More information is available in the
* @link http://api.drupal.org/api/drupal/developer--topics--forms_api_reference.html/7 Form API Reference @endlink
* @link forms_api_reference.html Form API Reference @endlink
*
* In addition to using Form API for doing in-form modification, Ajax may be
* enabled by adding classes to buttons and links. By adding the 'use-ajax'
......@@ -173,11 +189,11 @@
* be converted to a JSON object and returned to the client, which will then
* iterate over the array and process it like a macro language.
*
* Each command item is an associative array which will be converted to a command
* object on the JavaScript side. $command_item['command'] is the type of
* command, e.g. 'alert' or 'replace', and will correspond to a method in the
* Drupal.ajax[command] space. The command array may contain any other data
* that the command needs to process, e.g. 'method', 'selector', 'settings', etc.
* Each command item is an associative array which will be converted to a
* command object on the JavaScript side. $command_item['command'] is the type
* of command, e.g. 'alert' or 'replace', and will correspond to a method in the
* Drupal.ajax[command] space. The command array may contain any other data that
* the command needs to process, e.g. 'method', 'selector', 'settings', etc.
*
* Commands are usually created with a couple of helper functions, so they
* look like this:
......@@ -195,7 +211,7 @@
*
* When returning an Ajax command array, it is often useful to have
* status messages rendered along with other tasks in the command array.
* In that case the the Ajax commands array may be constructed like this:
* In that case the Ajax commands array may be constructed like this:
* @code
* $commands = array();
* $commands[] = ajax_command_replace(NULL, $output);
......@@ -207,13 +223,17 @@
*/
/**
* Render a commands array into JSON.
* Renders a commands array into JSON.
*
* @param $commands
* A list of macro commands generated by the use of ajax_command_*()
* functions.
*/
function ajax_render($commands = array()) {
// Although ajax_deliver() does this, some contributed and custom modules
// render Ajax responses without using that delivery callback.
ajax_set_verification_header();
// Ajax responses aren't rendered with html.tpl.php, so we have to call
// drupal_get_css() and drupal_get_js() here, in order to have new files added
// during this request to be loaded by the page. We only want to send back
......@@ -235,8 +255,8 @@ function ajax_render($commands = array()) {
// reliably diffed with array_diff_key(), since the number can change
// due to factors unrelated to the inline content, so for now, we strip
// the inline items from Ajax responses, and can add support for them
// when drupal_add_css() and drupal_add_js() are changed to using md5()
// or some other hash of the inline content.
// when drupal_add_css() and drupal_add_js() are changed to use a hash
// of the inline content as the array key.
foreach ($items[$type] as $key => $item) {
if (is_numeric($key)) {
unset($items[$type][$key]);
......@@ -247,26 +267,20 @@ function ajax_render($commands = array()) {
}
}
// Settings are handled separately, later in this function, so that changes to
// the ajaxPageState setting that occur during drupal_get_css() and
// drupal_get_js() get included, and because the jQuery.extend() code produced
// by drupal_get_js() for adding settings isn't appropriate during an Ajax
// response, because it does not pass TRUE for the "deep" parameter, and
// therefore, can clobber existing settings on the page.
// Render the HTML to load these files, and add AJAX commands to insert this
// HTML in the page. We pass TRUE as the $skip_alter argument to prevent the
// data from being altered again, as we already altered it above. Settings are
// handled separately, afterwards.
if (isset($items['js']['settings'])) {
unset($items['js']['settings']);
}
// Render the HTML to load these files, and add Ajax commands to insert this
// HTML in the page. We pass TRUE as the $skip_alter argument to prevent the
// data from being altered again, as we already altered it above.
$styles = drupal_get_css($items['css'], TRUE);
$scripts_footer = drupal_get_js('footer', $items['js'], TRUE);
$scripts_header = drupal_get_js('header', $items['js'], TRUE);
$extra_commands = array();
if (!empty($styles)) {
$extra_commands[] = ajax_command_prepend('head', $styles);
$extra_commands[] = ajax_command_add_css($styles);
}
if (!empty($scripts_header)) {
$extra_commands[] = ajax_command_prepend('head', $scripts_header);
......@@ -278,12 +292,11 @@ function ajax_render($commands = array()) {
$commands = array_merge($extra_commands, $commands);
}
// Now add a command to merge changes and additions to Drupal.settings.
$scripts = drupal_add_js();
if (!empty($scripts['settings'])) {
$settings = $scripts['settings'];
// Automatically extract any settings added via drupal_add_js() and make
// them the first command.
array_unshift($commands, ajax_command_settings(call_user_func_array('array_merge_recursive', $settings['data']), TRUE));
array_unshift($commands, ajax_command_settings(drupal_array_merge_deep_array($settings['data']), TRUE));
}
// Allow modules to alter any Ajax response.
......@@ -293,16 +306,17 @@ function ajax_render($commands = array()) {
}
/**
* Get a form submitted via #ajax during an Ajax callback.
* Gets a form submitted via #ajax during an Ajax callback.
*
* This will load a form from the form cache used during Ajax operations. It
* pulls the form info from $_POST.
*
* @return
* An array containing the $form and $form_state. Use the list() function
* to break these apart:
* An array containing the $form, $form_state, $form_id, $form_build_id and an
* initial list of Ajax $commands. Use the list() function to break these
* apart:
* @code
* list($form, $form_state, $form_id, $form_build_id) = ajax_get_form();
* list($form, $form_state, $form_id, $form_build_id, $commands) = ajax_get_form();
* @endcode
*/
function ajax_get_form() {
......@@ -322,6 +336,17 @@ function ajax_get_form() {
drupal_exit();
}
// When a page level cache is enabled, the form-build id might have been
// replaced from within form_get_cache. If this is the case, it is also
// necessary to update it in the browser by issuing an appropriate Ajax
// command.
$commands = array();
if (isset($form['#build_id_old']) && $form['#build_id_old'] != $form['#build_id']) {
// If the form build ID has changed, issue an Ajax command to update it.
$commands[] = ajax_command_update_build_id($form);
$form_build_id = $form['#build_id'];
}
// Since some of the submit handlers are run, redirects need to be disabled.
$form_state['no_redirect'] = TRUE;
......@@ -336,7 +361,7 @@ function ajax_get_form() {
$form_state['input'] = $_POST;
$form_id = $form['#form_id'];
return array($form, $form_state, $form_id, $form_build_id);
return array($form, $form_state, $form_id, $form_build_id, $commands);
}
/**
......@@ -353,9 +378,11 @@ function ajax_get_form() {
* #ajax['path']. If processing is required that cannot be accomplished with
* a callback, re-implement this function and set #ajax['path'] to the
* enhanced function.
*
* @see system_menu()
*/
function ajax_form_callback() {
list($form, $form_state) = ajax_get_form();
list($form, $form_state, $form_id, $form_build_id, $commands) = ajax_get_form();
drupal_process_form($form['#form_id'], $form, $form_state);
// We need to return the part of the form (or some other content) that needs
......@@ -367,8 +394,20 @@ function ajax_form_callback() {
if (!empty($form_state['triggering_element'])) {
$callback = $form_state['triggering_element']['#ajax']['callback'];
}
if (!empty($callback) && function_exists($callback)) {
return $callback($form, $form_state);
if (!empty($callback) && is_callable($callback)) {
$result = $callback($form, $form_state);
if (!(is_array($result) && isset($result['#type']) && $result['#type'] == 'ajax')) {
// Turn the response into a #type=ajax array if it isn't one already.
$result = array(
'#type' => 'ajax',
'#commands' => ajax_prepare_response($result),
);
}
$result['#commands'] = array_merge($commands, $result['#commands']);
return $result;
}
}
......@@ -388,6 +427,9 @@ function ajax_form_callback() {
* of the page. Therefore, system_menu() sets the 'theme callback' for
* 'system/ajax' to this function, and it is recommended that modules
* implementing other generic Ajax paths do the same.
*
* @see system_menu()
* @see file_menu()
*/
function ajax_base_page_theme() {
if (!empty($_POST['ajax_page_state']['theme']) && !empty($_POST['ajax_page_state']['theme_token'])) {
......@@ -406,7 +448,7 @@ function ajax_base_page_theme() {
}
/**
* Package and send the result of a page callback to the browser as an Ajax response.
* Packages and sends the result of a page callback as an Ajax response.
*
* This function is the equivalent of drupal_deliver_html_page(), but for Ajax
* requests. Like that function, it:
......@@ -449,6 +491,9 @@ function ajax_deliver($page_callback_result) {
}
}
// Let ajax.js know that this response is safe to process.
ajax_set_verification_header();
// Print the response.
$commands = ajax_prepare_response($page_callback_result);
$json = ajax_render($commands);
......@@ -539,7 +584,30 @@ function ajax_prepare_response($page_callback_result) {
}
/**
* Perform end-of-Ajax-request tasks.
* Sets a response header for ajax.js to trust the response body.
*
* It is not safe to invoke Ajax commands within user-uploaded files, so this
* header protects against those being invoked.
*
* @see Drupal.ajax.options.success()
*/
function ajax_set_verification_header() {
$added = &drupal_static(__FUNCTION__);
// User-uploaded files cannot set any response headers, so a custom header is
// used to indicate to ajax.js that this response is safe. Note that most
// Ajax requests bound using the Form API will be protected by having the URL
// flagged as trusted in Drupal.settings, so this header is used only for
// things like custom markup that gets Ajax behaviors attached.
if (empty($added)) {
drupal_add_http_header('X-Drupal-Ajax-Token', '1');
// Avoid sending the header twice.
$added = TRUE;
}
}
/**
* Performs end-of-Ajax-request tasks.
*
* This function is the equivalent of drupal_page_footer(), but for Ajax
* requests.
......@@ -562,7 +630,7 @@ function ajax_footer() {
}
/**
* Form element process callback to handle #ajax.
* Form element processing handler for the #ajax form property.
*
* @param $element
* An associative array containing the properties of the element.
......@@ -581,7 +649,7 @@ function ajax_process_form($element, &$form_state) {
}
/**
* Add Ajax information about an element to the page to communicate with JavaScript.
* Adds Ajax information about an element to communicate with JavaScript.
*
* If #ajax['path'] is set on an element, this additional JavaScript is added
* to the page header to attach the Ajax behaviors. See ajax.js for more
......@@ -591,6 +659,7 @@ function ajax_process_form($element, &$form_state) {
* An associative array containing the properties of the element.
* Properties used:
* - #ajax['event']
* - #ajax['prevent']
* - #ajax['path']
* - #ajax['options']
* - #ajax['wrapper']
......@@ -619,13 +688,26 @@ function ajax_pre_render_element($element) {
case 'submit':
case 'button':
case 'image_button':
// Use the mousedown instead of the click event because form
// submission via pressing the enter key triggers a click event on
// submit inputs, inappropriately triggering Ajax behaviors.
// Pressing the ENTER key within a textfield triggers the click event of
// the form's first submit button. Triggering Ajax in this situation
// leads to problems, like breaking autocomplete textfields, so we bind
// to mousedown instead of click.
// @see http://drupal.org/node/216059
$element['#ajax']['event'] = 'mousedown';
// Attach an additional event handler so that Ajax behaviors
// can be triggered still via keyboard input.
// Retain keyboard accessibility by setting 'keypress'. This causes
// ajax.js to trigger 'event' when SPACE or ENTER are pressed while the
// button has focus.
$element['#ajax']['keypress'] = TRUE;
// Binding to mousedown rather than click means that it is possible to
// trigger a click by pressing the mouse, holding the mouse button down
// until the Ajax request is complete and the button is re-enabled, and
// then releasing the mouse button. Set 'prevent' so that ajax.js binds
// an additional handler to prevent such a click from triggering a
// non-Ajax form submission. This also prevents a textfield's ENTER
// press triggering this button's non-Ajax form submission behavior.
if (!isset($element['#ajax']['prevent'])) {
$element['#ajax']['prevent'] = 'click';
}
break;
case 'password':
......@@ -712,7 +794,12 @@ function ajax_pre_render_element($element) {
$element['#attached']['js'][] = array(
'type' => 'setting',
'data' => array('ajax' => array($element['#id'] => $settings)),
'data' => array(
'ajax' => array($element['#id'] => $settings),
'urlIsAjaxTrusted' => array(
$settings['url'] => TRUE,
),
),
);
// Indicate that Ajax processing was successful.
......@@ -808,7 +895,8 @@ function ajax_command_insert($selector, $html, $settings = NULL) {
* @return
* An array suitable for use with the ajax_render() function.
*
* See @link http://docs.jquery.com/Manipulation/replaceWith#content jQuery replaceWith command @endlink
* See
* @link http://docs.jquery.com/Manipulation/replaceWith#content jQuery replaceWith command @endlink
*/
function ajax_command_replace($selector, $html, $settings = NULL) {
return array(
......@@ -1182,3 +1270,48 @@ function ajax_command_restripe($selector) {
);
}
/**
* Creates a Drupal Ajax 'update_build_id' command.
*
* This command updates the value of a hidden form_build_id input element on a
* form. It requires the form passed in to have keys for both the old build ID
* in #build_id_old and the new build ID in #build_id.
*
* The primary use case for this Ajax command is to serve a new build ID to a
* form served from the cache to an anonymous user, preventing one anonymous
* user from accessing the form state of another anonymous users on Ajax enabled
* forms.
*
* @param $form
* The form array representing the form whose build ID should be updated.
*/
function ajax_command_update_build_id($form) {
return array(
'command' => 'updateBuildId',
'old' => $form['#build_id_old'],
'new' => $form['#build_id'],
);
}
/**
* Creates a Drupal Ajax 'add_css' command.
*
* This method will add css via ajax in a cross-browser compatible way.
*
* This command is implemented by Drupal.ajax.prototype.commands.add_css()
* defined in misc/ajax.js.
*
* @param $styles
* A string that contains the styles to be added.
*
* @return
* An array suitable for use with the ajax_render() function.
*
* @see misc/ajax.js
*/
function ajax_command_add_css($styles) {
return array(
'command' => 'add_css',
'data' => $styles,
);
}
......@@ -6,61 +6,63 @@
*/
/**
* Common interface for all Archiver classes.
* Defines the common interface for all Archiver classes.
*/
interface ArchiverInterface {
/**
* Constructor for a new archiver instance.
* Constructs a new archiver instance.
*
* @param $file_path
* The full system path of the archive to manipulate. Only local files
* are supported. If the file does not yet exist, it will be created if
* The full system path of the archive to manipulate. Only local files
* are supported. If the file does not yet exist, it will be created if
* appropriate.
*/
public function __construct($file_path);
/**
* Add the specified file or directory to the archive.
* Adds the specified file or directory to the archive.
*
* @param $file_path
* The full system path of the file or directory to add. Only local files
* and directories are supported.
*
* @return ArchiverInterface
* The called object.
*/
public function add($file_path);
/**
* Remove the specified file from the archive.
* Removes the specified file from the archive.
*
* @param $path
* The file name relative to the root of the archive to remove.
*
* @return ArchiverInterface
* The called object.
*/
public function remove($path);
/**
* Extract multiple files in the archive to the specified path.
* Extracts multiple files in the archive to the specified path.
*
* @param $path
* A full system path of the directory to which to extract files.
* @param $files
* Optionally specify a list of files to be extracted. Files are
* relative to the root of the archive. If not specified, all files
* in the archive will be extracted
* in the archive will be extracted.
*
* @return ArchiverInterface
* The called object.
*/
public function extract($path, Array $files = array());
public function extract($path, array $files = array());
/**
* List all files in the archive.
* Lists all files in the archive.
*
* @return
* An array of file names relative to the root of the archive.
*/
public function listContents();
}
......@@ -6,13 +6,19 @@
*/
/**
* Build the form for choosing a FileTransfer type and supplying credentials.
* Form constructor for the file transfer authorization form.
*
* Allows the user to choose a FileTransfer type and supply credentials.
*
* @see authorize_filetransfer_form_validate()
* @see authorize_filetransfer_form_submit()
* @ingroup forms
*/
function authorize_filetransfer_form($form, &$form_state) {
global $base_url, $is_https;
$form = array();
// If possible, we want to post this form securely via https.
// If possible, we want to post this form securely via HTTPS.
$form['#https'] = TRUE;
// CSS we depend on lives in modules/system/maintenance.css, which is loaded
......@@ -127,10 +133,11 @@ function authorize_filetransfer_form($form, &$form_state) {
}
/**
* Generate the Form API array for the settings for a given connection backend.
* Generates the Form API array for a given connection backend's settings.
*
* @param $backend
* The name of the backend (e.g. 'ftp', 'ssh', etc).
*
* @return
* Form API array of connection settings for the given backend.
*
......@@ -151,7 +158,7 @@ function _authorize_filetransfer_connection_settings($backend) {
}
/**
* Recursively fill in the default settings on a file transfer connection form.
* Sets the default settings on a file transfer connection form recursively.
*
* The default settings for the file transfer connection forms are saved in
* the database. The settings are stored as a nested array in the case of a
......@@ -159,14 +166,12 @@ function _authorize_filetransfer_connection_settings($backend) {
* Therefore, to properly add defaults, we need to walk through all the
* children form elements and process those defaults recursively.
*
* @param &$element
* @param $element
* Reference to the Form API form element we're operating on.
* @param $key
* The key for our current form element, if any.
* @param array $defaults
* The default settings for the file transfer backend we're operating on.
* @return
* Nothing, this function just sets $element['#default_value'] if needed.
*/
function _authorize_filetransfer_connection_settings_set_defaults(&$element, $key, array $defaults) {
// If we're operating on a form element which isn't a fieldset, and we have
......@@ -186,14 +191,15 @@ function _authorize_filetransfer_connection_settings_set_defaults(&$element, $ke
}
/**
* Validate callback for the filetransfer authorization form.
* Form validation handler for authorize_filetransfer_form().
*
* @see authorize_filetransfer_form()
* @see authorize_filetransfer_submit()
*/
function authorize_filetransfer_form_validate($form, &$form_state) {
// Only validate the form if we have collected all of the user input and are
// ready to proceed with updating or installing.
if ($form_state['clicked_button']['#name'] != 'process_updates') {
if ($form_state['triggering_element']['#name'] != 'process_updates') {
return;
}
......@@ -218,13 +224,14 @@ function authorize_filetransfer_form_validate($form, &$form_state) {
}
/**
* Submit callback when a file transfer is being authorized.
* Form submission handler for authorize_filetransfer_form().
*
* @see authorize_filetransfer_form()
* @see authorize_filetransfer_validate()
*/
function authorize_filetransfer_form_submit($form, &$form_state) {
global $base_url;
switch ($form_state['clicked_button']['#name']) {
switch ($form_state['triggering_element']['#name']) {
case 'process_updates':
// Save the connection settings to the DB.
......@@ -280,7 +287,7 @@ function authorize_filetransfer_form_submit($form, &$form_state) {
}
/**
* Run the operation specified in $_SESSION['authorize_operation']
* Runs the operation specified in $_SESSION['authorize_operation'].
*
* @param $filetransfer
* The FileTransfer object to use for running the operation.
......@@ -298,12 +305,13 @@ function authorize_run_operation($filetransfer) {
}
/**
* Get a FileTransfer class for a specific transfer method and settings.
* Gets a FileTransfer class for a specific transfer method and settings.
*
* @param $backend
* The FileTransfer backend to get the class for.
* @param $settings
* Array of settings for the FileTransfer.
*
* @return
* An instantiated FileTransfer object for the requested method and settings,
* or FALSE if there was an error finding or instantiating it.
......
<?php
/**
* @file
* Batch processing API for processes to run in multiple HTTP requests.
......@@ -21,6 +20,7 @@
* @param $id
* The ID of the batch to load. When a progressive batch is being processed,
* the relevant ID is found in $_REQUEST['id'].
*
* @return
* An array representing the batch, or FALSE if no batch was found.
*/
......@@ -36,7 +36,7 @@ function batch_load($id) {
}
/**
* State-based dispatcher for the batch processing page.
* Renders the batch processing page based on the current state of the batch.
*
* @see _batch_shutdown()
*/
......@@ -94,7 +94,7 @@ function _batch_page() {
}
/**
* Initialize the batch processing.
* Initializes the batch processing.
*
* JavaScript-enabled clients are identified by the 'has_js' cookie set in
* drupal.js. If no JavaScript-enabled page has been visited during the current
......@@ -110,7 +110,7 @@ function _batch_start() {
}
/**
* Output a batch processing page with JavaScript support.
* Outputs a batch processing page with JavaScript support.
*
* This initializes the batch and error messages. Note that in JavaScript-based
* processing, the batch processing page is displayed only once and updated via
......@@ -144,7 +144,7 @@ function _batch_progress_page_js() {
}
/**
* Do one execution pass in JavaScript-mode and return progress to the browser.
* Does one execution pass with JavaScript and returns progress to the browser.
*
* @see _batch_progress_page_js()
* @see _batch_process()
......@@ -164,7 +164,7 @@ function _batch_do() {
}
/**
* Output a batch processing page without JavaScript support.
* Outputs a batch processing page without JavaScript support.
*
* @see _batch_process()
*/
......@@ -228,7 +228,7 @@ function _batch_progress_page_nojs() {
}
/**
* Process sets in a batch.
* Processes sets in a batch.
*
* If the batch was marked for progressive execution (default), this executes as
* many operations in batch sets until an execution time of 1 second has been
......@@ -339,6 +339,8 @@ function _batch_process() {
$progress_message = $old_set['progress_message'];
}
// Total progress is the number of operations that have fully run plus the
// completion level of the current operation.
$current = $total - $remaining + $finished;
$percentage = _batch_api_percentage($total, $current);
$elapsed = isset($current_set['elapsed']) ? $current_set['elapsed'] : 0;
......@@ -368,17 +370,23 @@ function _batch_process() {
}
/**
* Helper function for _batch_process(): returns the formatted percentage.
* Formats the percent completion for a batch set.
*
* @param $total
* The total number of operations.
* @param $current
* The number of the current operation.
* The number of the current operation. This may be a floating point number
* rather than an integer in the case of a multi-step operation that is not
* yet complete; in that case, the fractional part of $current represents the
* fraction of the operation that has been completed.
*
* @return
* The properly formatted percentage, as a string. We output percentages
* using the correct number of decimal places so that we never print "100%"
* until we are finished, but we also never print more decimal places than
* are meaningful.
*
* @see _batch_process()
*/
function _batch_api_percentage($total, $current) {
if (!$total || $total == $current) {
......@@ -390,13 +398,22 @@ function _batch_api_percentage($total, $current) {
// We add a new digit at 200, 2000, etc. (since, for example, 199/200
// would round up to 100% if we didn't).
$decimal_places = max(0, floor(log10($total / 2.0)) - 1);
$percentage = sprintf('%01.' . $decimal_places . 'f', round($current / $total * 100, $decimal_places));
do {
// Calculate the percentage to the specified number of decimal places.
$percentage = sprintf('%01.' . $decimal_places . 'f', round($current / $total * 100, $decimal_places));
// When $current is an integer, the above calculation will always be
// correct. However, if $current is a floating point number (in the case
// of a multi-step batch operation that is not yet complete), $percentage
// may be erroneously rounded up to 100%. To prevent that, we add one
// more decimal place and try again.
$decimal_places++;
} while ($percentage == '100');
}
return $percentage;
}
/**
* Return the batch set being currently processed.
* Returns the batch set being currently processed.
*/
function &_batch_current_set() {
$batch = &batch_get();
......@@ -404,7 +421,7 @@ function &_batch_current_set() {
}
/**
* Retrieve the next set in a batch.
* Retrieves the next set in a batch.
*
* If there is a subsequent set in this batch, assign it as the new set to
* process and execute its form submit handler (if defined), which may add
......@@ -428,7 +445,7 @@ function _batch_next_set() {
}
/**
* End the batch processing.
* Ends the batch processing.
*
* Call the 'finished' callback of each batch set to allow custom handling of
* the results and resolve page redirection.
......@@ -443,10 +460,10 @@ function _batch_finished() {
if (isset($batch_set['file']) && is_file($batch_set['file'])) {
include_once DRUPAL_ROOT . '/' . $batch_set['file'];
}
if (function_exists($batch_set['finished'])) {
if (is_callable($batch_set['finished'])) {
$queue = _batch_queue($batch_set);
$operations = $queue->getAllItems();
$batch_set['finished']($batch_set['success'], $batch_set['results'], $operations, format_interval($batch_set['elapsed'] / 1000));
call_user_func($batch_set['finished'], $batch_set['success'], $batch_set['results'], $operations, format_interval($batch_set['elapsed'] / 1000));
}
}
}
......@@ -507,7 +524,10 @@ function _batch_finished() {
}
/**
* Shutdown function; store the current batch data for the next request.
* Shutdown function: Stores the current batch data for the next request.
*
* @see _batch_page()
* @see drupal_register_shutdown_function()
*/
function _batch_shutdown() {
if ($batch = batch_get()) {
......@@ -517,4 +537,3 @@ function _batch_shutdown() {
->execute();
}
}
<?php
/**
* @file
* Queue handlers used by the Batch API.
*
* Those implementations:
* - ensure FIFO ordering,
* - let an item be repeatedly claimed until it is actually deleted (no notion
* of lease time or 'expire' date), to allow multipass operations.
* These implementations:
* - Ensure FIFO ordering.
* - Allow an item to be repeatedly claimed until it is actually deleted (no
* notion of lease time or 'expire' date), to allow multipass operations.
*/
/**
* Batch queue implementation.
* Defines a batch queue.
*
* Stale items from failed batches are cleaned from the {queue} table on cron
* using the 'created' date.
*/
class BatchQueue extends SystemQueue {
/**
* Overrides SystemQueue::claimItem().
*
* Unlike SystemQueue::claimItem(), this method provides a default lease
* time of 0 (no expiration) instead of 30. This allows the item to be
* claimed repeatedly until it is deleted.
*/
public function claimItem($lease_time = 0) {
$item = db_query_range('SELECT data, item_id FROM {queue} q WHERE name = :name ORDER BY item_id ASC', 0, 1, array(':name' => $this->name))->fetchObject();
if ($item) {
......@@ -29,9 +35,9 @@ public function claimItem($lease_time = 0) {
}
/**
* Retrieve all remaining items in the queue.
* Retrieves all remaining items in the queue.
*
* This is specific to Batch API and is not part of the DrupalQueueInterface,
* This is specific to Batch API and is not part of the DrupalQueueInterface.
*/
public function getAllItems() {
$result = array();
......@@ -44,10 +50,17 @@ public function getAllItems() {
}
/**
* Batch queue implementation used for non-progressive batches.
* Defines a batch queue for non-progressive batches.
*/
class BatchMemoryQueue extends MemoryQueue {
/**
* Overrides MemoryQueue::claimItem().
*
* Unlike MemoryQueue::claimItem(), this method provides a default lease
* time of 0 (no expiration) instead of 30. This allows the item to be
* claimed repeatedly until it is deleted.
*/
public function claimItem($lease_time = 0) {
if (!empty($this->queue)) {
reset($this->queue);
......@@ -57,9 +70,9 @@ public function claimItem($lease_time = 0) {
}
/**
* Retrieve all remaining items in the queue.
* Retrieves all remaining items in the queue.
*
* This is specific to Batch API and is not part of the DrupalQueueInterface,
* This is specific to Batch API and is not part of the DrupalQueueInterface.
*/
public function getAllItems() {
$result = array();
......
......@@ -8,7 +8,7 @@
/**
* The current system version.
*/
define('VERSION', '7.0-dev');
define('VERSION', '7.52');
/**
* Core API compatibility.
......@@ -25,6 +25,21 @@
*/
define('DRUPAL_MINIMUM_PHP_MEMORY_LIMIT', '32M');
/**
* Error reporting level: display no errors.
*/
define('ERROR_REPORTING_HIDE', 0);
/**
* Error reporting level: display errors and warnings.
*/
define('ERROR_REPORTING_DISPLAY_SOME', 1);
/**
* Error reporting level: display all messages.
*/
define('ERROR_REPORTING_DISPLAY_ALL', 2);
/**
* Indicates that the item should never be removed unless explicitly selected.
*
......@@ -38,93 +53,69 @@
define('CACHE_TEMPORARY', -1);
/**
* Log message severity -- Emergency: system is unusable.
* @defgroup logging_severity_levels Logging severity levels
* @{
* Logging severity levels as defined in RFC 3164.
*
* The WATCHDOG_* constant definitions correspond to the logging severity levels
* defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
*
* defined in RFC 3164, section 4.1.1. PHP supplies predefined LOG_* constants
* for use in the syslog() function, but their values on Windows builds do not
* correspond to RFC 3164. The associated PHP bug report was closed with the
* comment, "And it's also not a bug, as Windows just have less log levels,"
* and "So the behavior you're seeing is perfectly normal."
*
* @see http://www.faqs.org/rfcs/rfc3164.html
* @see http://bugs.php.net/bug.php?id=18090
* @see http://php.net/manual/function.syslog.php
* @see http://php.net/manual/network.constants.php
* @see watchdog()
* @see watchdog_severity_levels()
*/
/**
* Log message severity -- Emergency: system is unusable.
*/
define('WATCHDOG_EMERGENCY', 0);
/**
* Log message severity -- Alert: action must be taken immediately.
*
* The WATCHDOG_* constant definitions correspond to the logging severity levels
* defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
*
* @see watchdog()
* @see watchdog_severity_levels()
*/
define('WATCHDOG_ALERT', 1);
/**
* Log message severity -- Critical: critical conditions.
*
* The WATCHDOG_* constant definitions correspond to the logging severity levels
* defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
*
* @see watchdog()
* @see watchdog_severity_levels()
* Log message severity -- Critical conditions.
*/
define('WATCHDOG_CRITICAL', 2);
/**
* Log message severity -- Error: error conditions.
*
* The WATCHDOG_* constant definitions correspond to the logging severity levels
* defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
*
* @see watchdog()
* @see watchdog_severity_levels()
* Log message severity -- Error conditions.
*/
define('WATCHDOG_ERROR', 3);
/**
* Log message severity -- Warning: warning conditions.
*
* The WATCHDOG_* constant definitions correspond to the logging severity levels
* defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
*
* @see watchdog()
* @see watchdog_severity_levels()
* Log message severity -- Warning conditions.
*/
define('WATCHDOG_WARNING', 4);
/**
* Log message severity -- Notice: normal but significant condition.
*
* The WATCHDOG_* constant definitions correspond to the logging severity levels
* defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
*
* @see watchdog()
* @see watchdog_severity_levels()
* Log message severity -- Normal but significant conditions.
*/
define('WATCHDOG_NOTICE', 5);
/**
* Log message severity -- Informational: informational messages.
*
* The WATCHDOG_* constant definitions correspond to the logging severity levels
* defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
*
* @see watchdog()
* @see watchdog_severity_levels()
* Log message severity -- Informational messages.
*/
define('WATCHDOG_INFO', 6);
/**
* Log message severity -- Debug: debug-level messages.
*
* The WATCHDOG_* constant definitions correspond to the logging severity levels
* defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
*
* @see watchdog()
* @see watchdog_severity_levels()
* Log message severity -- Debug-level messages.
*/
define('WATCHDOG_DEBUG', 7);
/**
* @} End of "defgroup logging_severity_levels".
*/
/**
* First bootstrap phase: initialize configuration.
*/
......@@ -161,8 +152,7 @@
define('DRUPAL_BOOTSTRAP_LANGUAGE', 6);
/**
* Final bootstrap phase: Drupal is fully loaded; validate and fix
* input data.
* Final bootstrap phase: Drupal is fully loaded; validate and fix input data.
*/
define('DRUPAL_BOOTSTRAP_FULL', 7);
......@@ -177,8 +167,9 @@
define('DRUPAL_AUTHENTICATED_RID', 2);
/**
* The number of bytes in a kilobyte. For more information, visit
* http://en.wikipedia.org/wiki/Kilobyte.
* The number of bytes in a kilobyte.
*
* For more information, visit http://en.wikipedia.org/wiki/Kilobyte.
*/
define('DRUPAL_KILOBYTE', 1024);
......@@ -215,17 +206,28 @@
define('LANGUAGE_RTL', 1);
/**
* For convenience, define a short form of the request time global.
* Time of the current request in seconds elapsed since the Unix Epoch.
*
* This differs from $_SERVER['REQUEST_TIME'], which is stored as a float
* since PHP 5.4.0. Float timestamps confuse most PHP functions
* (including date_create()).
*
* @see http://php.net/manual/reserved.variables.server.php
* @see http://php.net/manual/function.time.php
*/
define('REQUEST_TIME', $_SERVER['REQUEST_TIME']);
define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
/**
* Flag for drupal_set_title(); text is not sanitized, so run check_plain().
* Flag used to indicate that text is not sanitized, so run check_plain().
*
* @see drupal_set_title()
*/
define('CHECK_PLAIN', 0);
/**
* Flag for drupal_set_title(); text has already been sanitized.
* Flag used to indicate that text has already been sanitized.
*
* @see drupal_set_title()
*/
define('PASS_THROUGH', -1);
......@@ -242,15 +244,226 @@
/**
* Regular expression to match PHP function names.
*
* @see http://php.net/manual/en/language.functions.php
* @see http://php.net/manual/language.functions.php
*/
define('DRUPAL_PHP_FUNCTION_PATTERN', '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*');
/**
* Start the timer with the specified name. If you start and stop the same
* timer multiple times, the measured intervals will be accumulated.
* A RFC7231 Compliant date.
*
* http://tools.ietf.org/html/rfc7231#section-7.1.1.1
*
* Example: Sun, 06 Nov 1994 08:49:37 GMT
*/
define('DATE_RFC7231', 'D, d M Y H:i:s \G\M\T');
/**
* Provides a caching wrapper to be used in place of large array structures.
*
* This class should be extended by systems that need to cache large amounts
* of data and have it represented as an array to calling functions. These
* arrays can become very large, so ArrayAccess is used to allow different
* strategies to be used for caching internally (lazy loading, building caches
* over time etc.). This can dramatically reduce the amount of data that needs
* to be loaded from cache backends on each request, and memory usage from
* static caches of that same data.
*
* Note that array_* functions do not work with ArrayAccess. Systems using
* DrupalCacheArray should use this only internally. If providing API functions
* that return the full array, this can be cached separately or returned
* directly. However since DrupalCacheArray holds partial content by design, it
* should be a normal PHP array or otherwise contain the full structure.
*
* Note also that due to limitations in PHP prior to 5.3.4, it is impossible to
* write directly to the contents of nested arrays contained in this object.
* Only writes to the top-level array elements are possible. So if you
* previously had set $object['foo'] = array(1, 2, 'bar' => 'baz'), but later
* want to change the value of 'bar' from 'baz' to 'foobar', you cannot do so
* a targeted write like $object['foo']['bar'] = 'foobar'. Instead, you must
* overwrite the entire top-level 'foo' array with the entire set of new
* values: $object['foo'] = array(1, 2, 'bar' => 'foobar'). Due to this same
* limitation, attempts to create references to any contained data, nested or
* otherwise, will fail silently. So $var = &$object['foo'] will not throw an
* error, and $var will be populated with the contents of $object['foo'], but
* that data will be passed by value, not reference. For more information on
* the PHP limitation, see the note in the official PHP documentation at·
* http://php.net/manual/arrayaccess.offsetget.php on
* ArrayAccess::offsetGet().
*
* By default, the class accounts for caches where calling functions might
* request keys in the array that won't exist even after a cache rebuild. This
* prevents situations where a cache rebuild would be triggered over and over
* due to a 'missing' item. These cases are stored internally as a value of
* NULL. This means that the offsetGet() and offsetExists() methods
* must be overridden if caching an array where the top level values can
* legitimately be NULL, and where $object->offsetExists() needs to correctly
* return (equivalent to array_key_exists() vs. isset()). This should not
* be necessary in the majority of cases.
*
* Classes extending this class must override at least the
* resolveCacheMiss() method to have a working implementation.
*
* offsetSet() is not overridden by this class by default. In practice this
* means that assigning an offset via arrayAccess will only apply while the
* object is in scope and will not be written back to the persistent cache.
* This follows a similar pattern to static vs. persistent caching in
* procedural code. Extending classes may wish to alter this behavior, for
* example by overriding offsetSet() and adding an automatic call to persist().
*
* @see SchemaCache
*/
abstract class DrupalCacheArray implements ArrayAccess {
/**
* A cid to pass to cache_set() and cache_get().
*/
protected $cid;
/**
* A bin to pass to cache_set() and cache_get().
*/
protected $bin;
/**
* An array of keys to add to the cache at the end of the request.
*/
protected $keysToPersist = array();
/**
* Storage for the data itself.
*/
protected $storage = array();
/**
* Constructs a DrupalCacheArray object.
*
* @param $cid
* The cid for the array being cached.
* @param $bin
* The bin to cache the array.
*/
public function __construct($cid, $bin) {
$this->cid = $cid;
$this->bin = $bin;
if ($cached = cache_get($this->cid, $this->bin)) {
$this->storage = $cached->data;
}
}
/**
* Implements ArrayAccess::offsetExists().
*/
public function offsetExists($offset) {
return $this->offsetGet($offset) !== NULL;
}
/**
* Implements ArrayAccess::offsetGet().
*/
public function offsetGet($offset) {
if (isset($this->storage[$offset]) || array_key_exists($offset, $this->storage)) {
return $this->storage[$offset];
}
else {
return $this->resolveCacheMiss($offset);
}
}
/**
* Implements ArrayAccess::offsetSet().
*/
public function offsetSet($offset, $value) {
$this->storage[$offset] = $value;
}
/**
* Implements ArrayAccess::offsetUnset().
*/
public function offsetUnset($offset) {
unset($this->storage[$offset]);
}
/**
* Flags an offset value to be written to the persistent cache.
*
* If a value is assigned to a cache object with offsetSet(), by default it
* will not be written to the persistent cache unless it is flagged with this
* method. This allows items to be cached for the duration of a request,
* without necessarily writing back to the persistent cache at the end.
*
* @param $offset
* The array offset that was requested.
* @param $persist
* Optional boolean to specify whether the offset should be persisted or
* not, defaults to TRUE. When called with $persist = FALSE the offset will
* be unflagged so that it will not be written at the end of the request.
*/
protected function persist($offset, $persist = TRUE) {
$this->keysToPersist[$offset] = $persist;
}
/**
* Resolves a cache miss.
*
* When an offset is not found in the object, this is treated as a cache
* miss. This method allows classes implementing the interface to look up
* the actual value and allow it to be cached.
*
* @param $offset
* The offset that was requested.
*
* @return
* The value of the offset, or NULL if no value was found.
*/
abstract protected function resolveCacheMiss($offset);
/**
* Writes a value to the persistent cache immediately.
*
* @param $data
* The data to write to the persistent cache.
* @param $lock
* Whether to acquire a lock before writing to cache.
*/
protected function set($data, $lock = TRUE) {
// Lock cache writes to help avoid stampedes.
// To implement locking for cache misses, override __construct().
$lock_name = $this->cid . ':' . $this->bin;
if (!$lock || lock_acquire($lock_name)) {
if ($cached = cache_get($this->cid, $this->bin)) {
$data = $cached->data + $data;
}
cache_set($this->cid, $data, $this->bin);
if ($lock) {
lock_release($lock_name);
}
}
}
/**
* Destructs the DrupalCacheArray object.
*/
public function __destruct() {
$data = array();
foreach ($this->keysToPersist as $offset => $persist) {
if ($persist) {
$data[$offset] = $this->storage[$offset];
}
}
if (!empty($data)) {
$this->set($data);
}
}
}
/**
* Starts the timer with the specified name.
*
* @param name
* If you start and stop the same timer multiple times, the measured intervals
* will be accumulated.
*
* @param $name
* The name of the timer.
*/
function timer_start($name) {
......@@ -261,10 +474,11 @@ function timer_start($name) {
}
/**
* Read the current timer value without stopping the timer.
* Reads the current timer value without stopping the timer.
*
* @param name
* @param $name
* The name of the timer.
*
* @return
* The current timer value in ms.
*/
......@@ -284,10 +498,11 @@ function timer_read($name) {
}
/**
* Stop the timer with the specified name.
* Stops the timer with the specified name.
*
* @param name
* @param $name
* The name of the timer.
*
* @return
* A timer array. The array contains the number of times the timer has been
* started and stopped (count) and the accumulated timer value in ms (time).
......@@ -311,68 +526,25 @@ function timer_stop($name) {
}
/**
* Find the appropriate configuration directory.
* Returns the appropriate configuration directory.
*
* Try finding a matching configuration directory by stripping the website's
* hostname from left to right and pathname from right to left. The first
* configuration file found will be used; the remaining will ignored. If no
* configuration file is found, return a default value '$confdir/default'.
* Returns the configuration path based on the site's hostname, port, and
* pathname. See default.settings.php for examples on how the URL is converted
* to a directory.
*
* Example for a fictitious site installed at
* http://www.drupal.org:8080/mysite/test/ the 'settings.php' is searched in
* the following directories:
*
* 1. $confdir/8080.www.drupal.org.mysite.test
* 2. $confdir/www.drupal.org.mysite.test
* 3. $confdir/drupal.org.mysite.test
* 4. $confdir/org.mysite.test
*
* 5. $confdir/8080.www.drupal.org.mysite
* 6. $confdir/www.drupal.org.mysite
* 7. $confdir/drupal.org.mysite
* 8. $confdir/org.mysite
*
* 9. $confdir/8080.www.drupal.org
* 10. $confdir/www.drupal.org
* 11. $confdir/drupal.org
* 12. $confdir/org
*
* 13. $confdir/default
*
* If a file named sites.php is present in the $confdir, it will be loaded
* prior to scanning for directories. It should define an associative array
* named $sites, which maps domains to directories. It should be in the form
* of:
*
* $sites = array(
* 'The url to alias' => 'A directory within the sites directory'
* );
*
* For example:
*
* $sites = array(
* 'devexample.com' => 'example.com',
* 'localhost.example' => 'example.com',
* );
*
* The above array will cause Drupal to look for a directory named
* "example.com" in the sites directory whenever a request comes from
* "example.com", "devexample.com", or "localhost/example". That is useful
* on development servers, where the domain name may not be the same as the
* domain of the live server. Since Drupal stores file paths into the database
* (files, system table, etc.) this will ensure the paths are correct while
* accessed on development servers.
*
* @param $require_settings
* @param bool $require_settings
* Only configuration directories with an existing settings.php file
* will be recognized. Defaults to TRUE. During initial installation,
* this is set to FALSE so that Drupal can detect a matching directory,
* then create a new settings.php file in it.
* @param reset
* @param bool $reset
* Force a full search for matching directories even if one had been
* found previously.
* found previously. Defaults to FALSE.
*
* @return
* The path of the matching directory.
*
* @see default.settings.php
*/
function conf_path($require_settings = TRUE, $reset = FALSE) {
$conf = &drupal_static(__FUNCTION__, '');
......@@ -408,7 +580,7 @@ function conf_path($require_settings = TRUE, $reset = FALSE) {
}
/**
* Set appropriate server variables needed for command line scripts to work.
* Sets appropriate server variables needed for command line scripts to work.
*
* This function can be called by command line scripts before bootstrapping
* Drupal, to ensure that the page loads with the desired server parameters.
......@@ -470,7 +642,7 @@ function drupal_override_server_variables($variables = array()) {
}
/**
* Initialize PHP environment.
* Initializes the PHP environment.
*/
function drupal_environment_initialize() {
if (!isset($_SERVER['HTTP_REFERER'])) {
......@@ -519,7 +691,8 @@ function drupal_environment_initialize() {
ini_set('session.use_only_cookies', '1');
ini_set('session.use_trans_sid', '0');
// Don't send HTTP headers using PHP's session handler.
ini_set('session.cache_limiter', 'none');
// An empty string is used here to disable the cache limiter.
ini_set('session.cache_limiter', '');
// Use httponly session cookies.
ini_set('session.cookie_httponly', '1');
......@@ -529,23 +702,29 @@ function drupal_environment_initialize() {
}
/**
* Validate that a hostname (for example $_SERVER['HTTP_HOST']) is safe.
* Validates that a hostname (for example $_SERVER['HTTP_HOST']) is safe.
*
* @return
* TRUE if only containing valid characters, or FALSE otherwise.
*/
function drupal_valid_http_host($host) {
return preg_match('/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/', $host);
// Limit the length of the host name to 1000 bytes to prevent DoS attacks with
// long host names.
return strlen($host) <= 1000
// Limit the number of subdomains and port separators to prevent DoS attacks
// in conf_path().
&& substr_count($host, '.') <= 100
&& substr_count($host, ':') <= 100
&& preg_match('/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/', $host);
}
/**
* Loads the configuration and sets the base URL, cookie domain, and
* session name correctly.
* Sets the base URL, cookie domain, and session name from configuration.
*/
function drupal_settings_initialize() {
global $base_url, $base_path, $base_root;
// Export the following settings.php variables to the global namespace
// Export these settings.php variables to the global namespace.
global $databases, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url, $db_prefix, $drupal_hash_salt, $is_https, $base_secure_url, $base_insecure_url;
$conf = array();
......@@ -557,7 +736,6 @@ function drupal_settings_initialize() {
if (isset($base_url)) {
// Parse fixed base URL from settings.php.
$parts = parse_url($base_url);
$http_protocol = $parts['scheme'];
if (!isset($parts['path'])) {
$parts['path'] = '';
}
......@@ -566,7 +744,7 @@ function drupal_settings_initialize() {
$base_root = substr($base_url, 0, strlen($base_url) - strlen($parts['path']));
}
else {
// Create base URL
// Create base URL.
$http_protocol = $is_https ? 'https' : 'http';
$base_root = $http_protocol . '://' . $_SERVER['HTTP_HOST'];
......@@ -592,7 +770,7 @@ function drupal_settings_initialize() {
}
else {
// Otherwise use $base_url as session name, without the protocol
// to use the same session identifiers across http and https.
// to use the same session identifiers across HTTP and HTTPS.
list( , $session_name) = explode('://', $base_url, 2);
// HTTP_HOST can be modified by a visitor, but we already sanitized it
// in drupal_settings_initialize().
......@@ -626,13 +804,14 @@ function drupal_settings_initialize() {
}
/**
* Returns and optionally sets the filename for a system item (module,
* theme, etc.). The filename, whether provided, cached, or retrieved
* from the database, is only returned if the file exists.
* Returns and optionally sets the filename for a system resource.
*
* The filename, whether provided, cached, or retrieved from the database, is
* only returned if the file exists.
*
* This function plays a key role in allowing Drupal's resources (modules
* and themes) to be located in different places depending on a site's
* configuration. For example, a module 'foo' may legally be be located
* configuration. For example, a module 'foo' may legally be located
* in any of these three places:
*
* modules/foo/foo.module
......@@ -643,76 +822,73 @@ function drupal_settings_initialize() {
* the above, depending on where the module is located.
*
* @param $type
* The type of the item (i.e. theme, theme_engine, module, profile).
* The type of the item (theme, theme_engine, module, profile).
* @param $name
* The name of the item for which the filename is requested.
* @param $filename
* The filename of the item if it is to be set explicitly rather
* than by consulting the database.
* @param bool $trigger_error
* Whether to trigger an error when a file is missing or has unexpectedly
* moved. This defaults to TRUE, but can be set to FALSE by calling code that
* merely wants to check whether an item exists in the filesystem.
*
* @return
* The filename of the requested item.
* The filename of the requested item or NULL if the item is not found.
*/
function drupal_get_filename($type, $name, $filename = NULL) {
function drupal_get_filename($type, $name, $filename = NULL, $trigger_error = TRUE) {
// The $files static variable will hold the locations of all requested files.
// We can be sure that any file listed in this static variable actually
// exists as all additions have gone through a file_exists() check.
// The location of files will not change during the request, so do not use
// drupal_static().
static $files = array();
// Profiles are a special case: they have a fixed location and naming.
if ($type == 'profile') {
$profile_filename = "profiles/$name/$name.profile";
$files[$type][$name] = file_exists($profile_filename) ? $profile_filename : FALSE;
}
if (!isset($files[$type])) {
$files[$type] = array();
}
if (!empty($filename) && file_exists($filename)) {
// Prime the static cache with the provided filename.
$files[$type][$name] = $filename;
}
elseif (isset($files[$type][$name])) {
// nothing
// This item had already been found earlier in the request, either through
// priming of the static cache (for example, in system_list()), through a
// lookup in the {system} table, or through a file scan (cached or not). Do
// nothing.
}
// Verify that we have an active database connection, before querying
// the database. This is required because this function is called both
// before we have a database connection (i.e. during installation) and
// when a database connection fails.
else {
// Look for the filename listed in the {system} table. Verify that we have
// an active database connection before doing so, since this function is
// called both before we have a database connection (i.e. during
// installation) and when a database connection fails.
$database_unavailable = TRUE;
try {
if (function_exists('db_query')) {
$file = db_query("SELECT filename FROM {system} WHERE name = :name AND type = :type", array(':name' => $name, ':type' => $type))->fetchField();
if (file_exists(DRUPAL_ROOT . '/' . $file)) {
if ($file !== FALSE && file_exists(DRUPAL_ROOT . '/' . $file)) {
$files[$type][$name] = $file;
}
$database_unavailable = FALSE;
}
}
catch (Exception $e) {
// The database table may not exist because Drupal is not yet installed,
// or the database might be down. We have a fallback for this case so we
// hide the error completely.
// the database might be down, or we may have done a non-database cache
// flush while $conf['page_cache_without_database'] = TRUE and
// $conf['page_cache_invoke_hooks'] = TRUE. We have a fallback for these
// cases so we hide the error completely.
}
// Fallback to searching the filesystem if the database could not find the
// file or the file returned by the database is not found.
// Fall back to searching the filesystem if the database could not find the
// file or the file does not exist at the path returned by the database.
if (!isset($files[$type][$name])) {
// We have a consistent directory naming: modules, themes...
$dir = $type . 's';
if ($type == 'theme_engine') {
$dir = 'themes/engines';
$extension = 'engine';
}
elseif ($type == 'theme') {
$extension = 'info';
}
else {
$extension = $type;
}
if (!function_exists('drupal_system_listing')) {
require_once DRUPAL_ROOT . '/includes/common.inc';
}
// Scan the appropriate directories for all files with the requested
// extension, not just the file we are currently looking for. This
// prevents unnecessary scans from being repeated when this function is
// called more than once in the same page request.
$matches = drupal_system_listing("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir, 'name', 0);
foreach ($matches as $matched_name => $file) {
$files[$type][$matched_name] = $file->uri;
}
$files[$type][$name] = _drupal_get_filename_fallback($type, $name, $trigger_error, $database_unavailable);
}
}
......@@ -722,11 +898,261 @@ function drupal_get_filename($type, $name, $filename = NULL) {
}
/**
* Load the persistent variable table.
* Performs a cached file system scan as a fallback when searching for a file.
*
* This function looks for the requested file by triggering a file scan,
* caching the new location if the file has moved and caching the miss
* if the file is missing. If a file had been marked as missing in a previous
* file scan, or if it has been marked as moved and is still in the last known
* location, no new file scan will be performed.
*
* @param string $type
* The type of the item (theme, theme_engine, module, profile).
* @param string $name
* The name of the item for which the filename is requested.
* @param bool $trigger_error
* Whether to trigger an error when a file is missing or has unexpectedly
* moved.
* @param bool $database_unavailable
* Whether this function is being called because the Drupal database could
* not be queried for the file's location.
*
* @return
* The filename of the requested item or NULL if the item is not found.
*
* @see drupal_get_filename()
*/
function _drupal_get_filename_fallback($type, $name, $trigger_error, $database_unavailable) {
$file_scans = &_drupal_file_scan_cache();
$filename = NULL;
// If the cache indicates that the item is missing, or we can verify that the
// item exists in the location the cache says it exists in, use that.
if (isset($file_scans[$type][$name]) && ($file_scans[$type][$name] === FALSE || file_exists($file_scans[$type][$name]))) {
$filename = $file_scans[$type][$name];
}
// Otherwise, perform a new file scan to find the item.
else {
$filename = _drupal_get_filename_perform_file_scan($type, $name);
// Update the static cache, and mark the persistent cache for updating at
// the end of the page request. See drupal_file_scan_write_cache().
$file_scans[$type][$name] = $filename;
$file_scans['#write_cache'] = TRUE;
}
// If requested, trigger a user-level warning about the missing or
// unexpectedly moved file. If the database was unavailable, do not trigger a
// warning in the latter case, though, since if the {system} table could not
// be queried there is no way to know if the location found here was
// "unexpected" or not.
if ($trigger_error) {
$error_type = $filename === FALSE ? 'missing' : 'moved';
if ($error_type == 'missing' || !$database_unavailable) {
_drupal_get_filename_fallback_trigger_error($type, $name, $error_type);
}
}
// The cache stores FALSE for files that aren't found (to be able to
// distinguish them from files that have not yet been searched for), but
// drupal_get_filename() expects NULL for these instead, so convert to NULL
// before returning.
if ($filename === FALSE) {
$filename = NULL;
}
return $filename;
}
/**
* Returns the current list of cached file system scan results.
*
* @return
* An associative array tracking the most recent file scan results for all
* files that have had scans performed. The keys are the type and name of the
* item that was searched for, and the values can be either:
* - Boolean FALSE if the item was not found in the file system.
* - A string pointing to the location where the item was found.
*/
function &_drupal_file_scan_cache() {
$file_scans = &drupal_static(__FUNCTION__, array());
// The file scan results are stored in a persistent cache (in addition to the
// static cache) but because this function can be called before the
// persistent cache is available, we must merge any items that were found
// earlier in the page request into the results from the persistent cache.
if (!isset($file_scans['#cache_merge_done'])) {
try {
if (function_exists('cache_get')) {
$cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
if (!empty($cache->data)) {
// File scan results from the current request should take precedence
// over the results from the persistent cache, since they are newer.
$file_scans = drupal_array_merge_deep($cache->data, $file_scans);
}
// Set a flag to indicate that the persistent cache does not need to be
// merged again.
$file_scans['#cache_merge_done'] = TRUE;
}
}
catch (Exception $e) {
// Hide the error.
}
}
return $file_scans;
}
/**
* Performs a file system scan to search for a system resource.
*
* @param $type
* The type of the item (theme, theme_engine, module, profile).
* @param $name
* The name of the item for which the filename is requested.
*
* @return
* The filename of the requested item or FALSE if the item is not found.
*
* @see drupal_get_filename()
* @see _drupal_get_filename_fallback()
*/
function _drupal_get_filename_perform_file_scan($type, $name) {
// The location of files will not change during the request, so do not use
// drupal_static().
static $dirs = array(), $files = array();
// We have a consistent directory naming: modules, themes...
$dir = $type . 's';
if ($type == 'theme_engine') {
$dir = 'themes/engines';
$extension = 'engine';
}
elseif ($type == 'theme') {
$extension = 'info';
}
else {
$extension = $type;
}
// Check if we had already scanned this directory/extension combination.
if (!isset($dirs[$dir][$extension])) {
// Log that we have now scanned this directory/extension combination
// into a static variable so as to prevent unnecessary file scans.
$dirs[$dir][$extension] = TRUE;
if (!function_exists('drupal_system_listing')) {
require_once DRUPAL_ROOT . '/includes/common.inc';
}
// Scan the appropriate directories for all files with the requested
// extension, not just the file we are currently looking for. This
// prevents unnecessary scans from being repeated when this function is
// called more than once in the same page request.
$matches = drupal_system_listing("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir, 'name', 0);
foreach ($matches as $matched_name => $file) {
// Log the locations found in the file scan into a static variable.
$files[$type][$matched_name] = $file->uri;
}
}
// Return the results of the file system scan, or FALSE to indicate the file
// was not found.
return isset($files[$type][$name]) ? $files[$type][$name] : FALSE;
}
/**
* Triggers a user-level warning for missing or unexpectedly moved files.
*
* @param $type
* The type of the item (theme, theme_engine, module, profile).
* @param $name
* The name of the item for which the filename is requested.
* @param $error_type
* The type of the error ('missing' or 'moved').
*
* @see drupal_get_filename()
* @see _drupal_get_filename_fallback()
*/
function _drupal_get_filename_fallback_trigger_error($type, $name, $error_type) {
// Hide messages due to known bugs that will appear on a lot of sites.
// @todo Remove this in https://www.drupal.org/node/2383823
if (empty($name)) {
return;
}
// Make sure we only show any missing or moved file errors only once per
// request.
static $errors_triggered = array();
if (empty($errors_triggered[$type][$name][$error_type])) {
// Use _drupal_trigger_error_with_delayed_logging() here since these are
// triggered during low-level operations that cannot necessarily be
// interrupted by a watchdog() call.
if ($error_type == 'missing') {
_drupal_trigger_error_with_delayed_logging(format_string('The following @type is missing from the file system: %name. For information about how to fix this, see <a href="@documentation">the documentation page</a>.', array('@type' => $type, '%name' => $name, '@documentation' => 'https://www.drupal.org/node/2487215')), E_USER_WARNING);
}
elseif ($error_type == 'moved') {
_drupal_trigger_error_with_delayed_logging(format_string('The following @type has moved within the file system: %name. In order to fix this, clear caches or put the @type back in its original location. For more information, see <a href="@documentation">the documentation page</a>.', array('@type' => $type, '%name' => $name, '@documentation' => 'https://www.drupal.org/node/2487215')), E_USER_WARNING);
}
$errors_triggered[$type][$name][$error_type] = TRUE;
}
}
/**
* Invokes trigger_error() with logging delayed until the end of the request.
*
* This is an alternative to PHP's trigger_error() function which can be used
* during low-level Drupal core operations that need to avoid being interrupted
* by a watchdog() call.
*
* Normally, Drupal's error handler calls watchdog() in response to a
* trigger_error() call. However, this invokes hook_watchdog() which can run
* arbitrary code. If the trigger_error() happens in the middle of an
* operation such as a rebuild operation which should not be interrupted by
* arbitrary code, that could potentially break or trigger the rebuild again.
* This function protects against that by delaying the watchdog() call until
* the end of the current page request.
*
* This is an internal function which should only be called by low-level Drupal
* core functions. It may be removed in a future Drupal 7 release.
*
* @param string $error_msg
* The error message to trigger. As with trigger_error() itself, this is
* limited to 1024 bytes; additional characters beyond that will be removed.
* @param int $error_type
* (optional) The type of error. This should be one of the E_USER family of
* constants. As with trigger_error() itself, this defaults to E_USER_NOTICE
* if not provided.
*
* @see _drupal_log_error()
*/
function _drupal_trigger_error_with_delayed_logging($error_msg, $error_type = E_USER_NOTICE) {
$delay_logging = &drupal_static(__FUNCTION__, FALSE);
$delay_logging = TRUE;
trigger_error($error_msg, $error_type);
$delay_logging = FALSE;
}
/**
* Writes the file scan cache to the persistent cache.
*
* This cache stores all files marked as missing or moved after a file scan
* to prevent unnecessary file scans in subsequent requests. This cache is
* cleared in system_list_reset() (i.e. after a module/theme rebuild).
*/
function drupal_file_scan_write_cache() {
// Only write to the persistent cache if requested, and if we know that any
// data previously in the cache was successfully loaded and merged in by
// _drupal_file_scan_cache().
$file_scans = &_drupal_file_scan_cache();
if (isset($file_scans['#write_cache']) && isset($file_scans['#cache_merge_done'])) {
unset($file_scans['#write_cache']);
cache_set('_drupal_file_scan_cache', $file_scans, 'cache_bootstrap');
}
}
/**
* Loads the persistent variable table.
*
* The variable table is composed of values that have been saved in the table
* with variable_set() as well as those explicitly specified in the configuration
* file.
* with variable_set() as well as those explicitly specified in the
* configuration file.
*/
function variable_initialize($conf = array()) {
// NOTE: caching the variables improves performance by 20% when serving
......@@ -771,7 +1197,7 @@ function variable_initialize($conf = array()) {
* The default value to use if this variable has never been set.
*
* @return
* The value of the variable.
* The value of the variable. Unserialization is taken care of as necessary.
*
* @see variable_del()
* @see variable_set()
......@@ -833,7 +1259,7 @@ function variable_del($name) {
}
/**
* Retrieve the current page from the cache.
* Retrieves the current page from the cache.
*
* Note: we do not serve cached pages to authenticated users, or to anonymous
* users when $_SESSION is non-empty. $_SESSION may contain status messages
......@@ -865,10 +1291,10 @@ function drupal_page_get_cache($check_only = FALSE) {
}
/**
* Determine the cacheability of the current page.
* Determines the cacheability of the current page.
*
* @param $allow_caching
* Set to FALSE if you want to prevent this page to get cached.
* Set to FALSE if you want to prevent this page from being cached.
*
* @return
* TRUE if the current page can be cached, FALSE otherwise.
......@@ -884,7 +1310,7 @@ function drupal_page_is_cacheable($allow_caching = NULL) {
}
/**
* Invoke a bootstrap hook in all bootstrap modules that implement it.
* Invokes a bootstrap hook in all bootstrap modules that implement it.
*
* @param $hook
* The name of the bootstrap hook to invoke.
......@@ -906,8 +1332,9 @@ function bootstrap_invoke_all($hook) {
}
/**
* Includes a file with the provided type and name. This prevents
* including a theme, engine, module, etc., more than once.
* Includes a file with the provided type and name.
*
* This prevents including a theme, engine, module, etc., more than once.
*
* @param $type
* The type of item to load (i.e. theme, theme_engine, module).
......@@ -939,7 +1366,7 @@ function drupal_load($type, $name) {
}
/**
* Set an HTTP response header for the current page.
* Sets an HTTP response header for the current page.
*
* Note: When sending a Content-Type header, always include a 'charset' type,
* too. This is necessary to avoid security bugs (e.g. UTF-7 XSS).
......@@ -975,11 +1402,12 @@ function drupal_add_http_header($name, $value, $append = FALSE) {
}
/**
* Get the HTTP response headers for the current page.
* Gets the HTTP response headers for the current page.
*
* @param $name
* An HTTP header name. If omitted, all headers are returned as name/value
* pairs. If an array value is FALSE, the header has been unset.
*
* @return
* A string containing the header value, or FALSE if the header has been set,
* or NULL if the header has not been set.
......@@ -996,6 +1424,8 @@ function drupal_get_http_header($name = NULL) {
}
/**
* Sets the preferred name for the HTTP header.
*
* Header names are case-insensitive, but for maximum compatibility they should
* follow "common form" (see RFC 2617, section 4.2).
*/
......@@ -1009,14 +1439,16 @@ function _drupal_set_preferred_header_name($name = NULL) {
}
/**
* Send the HTTP response headers previously set using drupal_add_http_header().
* Add default headers, unless they have been replaced or unset using
* drupal_add_http_header().
* Sends the HTTP response headers that were previously set, adding defaults.
*
* @param $default_headers
* An array of headers as name/value pairs.
* @param $single
* If TRUE and headers have already be sent, send only the specified header.
* Headers are set in drupal_add_http_header(). Default headers are not set
* if they have been replaced or unset using drupal_add_http_header().
*
* @param array $default_headers
* (optional) An array of headers as name/value pairs.
* @param bool $only_default
* (optional) If TRUE and headers have already been sent, send only the
* specified headers.
*/
function drupal_send_headers($default_headers = array(), $only_default = FALSE) {
$headers_sent = &drupal_static(__FUNCTION__, FALSE);
......@@ -1039,36 +1471,23 @@ function drupal_send_headers($default_headers = array(), $only_default = FALSE)
header($_SERVER['SERVER_PROTOCOL'] . ' ' . $value);
}
// Skip headers that have been unset.
elseif ($value) {
elseif ($value !== FALSE) {
header($header_names[$name_lower] . ': ' . $value);
}
}
}
/**
* Set HTTP headers in preparation for a page response.
* Sets HTTP headers in preparation for a page response.
*
* Authenticated users are always given a 'no-cache' header, and will fetch a
* fresh page on every request. This prevents authenticated users from seeing
* locally cached pages.
*
* Also give each page a unique ETag. This will force clients to include both
* an If-Modified-Since header and an If-None-Match header when doing
* conditional requests for the page (required by RFC 2616, section 13.3.4),
* making the validation more robust. This is a workaround for a bug in Mozilla
* Firefox that is triggered when Drupal's caching is enabled and the user
* accesses Drupal via an HTTP proxy (see
* https://bugzilla.mozilla.org/show_bug.cgi?id=269303): When an authenticated
* user requests a page, and then logs out and requests the same page again,
* Firefox may send a conditional request based on the page that was cached
* locally when the user was logged in. If this page did not have an ETag
* header, the request only contains an If-Modified-Since header. The date will
* be recent, because with authenticated users the Last-Modified header always
* refers to the time of the request. If the user accesses Drupal via a proxy
* server, and the proxy already has a cached copy of the anonymous page with an
* older Last-Modified date, the proxy may respond with 304 Not Modified, making
* the client think that the anonymous and authenticated pageviews are
* identical.
* ETag and Last-Modified headers are not set per default for authenticated
* users so that browsers do not send If-Modified-Since headers from
* authenticated user pages. drupal_serve_page_from_cache() will set appropriate
* ETag and Last-Modified headers for cached pages.
*
* @see drupal_page_set_cache()
*/
......@@ -1081,15 +1500,17 @@ function drupal_page_header() {
$default_headers = array(
'Expires' => 'Sun, 19 Nov 1978 05:00:00 GMT',
'Last-Modified' => gmdate(DATE_RFC1123, REQUEST_TIME),
'Cache-Control' => 'no-cache, must-revalidate, post-check=0, pre-check=0',
'ETag' => '"' . REQUEST_TIME . '"',
'Cache-Control' => 'no-cache, must-revalidate',
// Prevent browsers from sniffing a response and picking a MIME type
// different from the declared content-type, since that can lead to
// XSS and other vulnerabilities.
'X-Content-Type-Options' => 'nosniff',
);
drupal_send_headers($default_headers);
}
/**
* Set HTTP headers in preparation for a cached page response.
* Sets HTTP headers in preparation for a cached page response.
*
* The headers allow as much as possible in proxies and browsers without any
* particular knowledge about the pages. Modules can override these headers
......@@ -1101,7 +1522,7 @@ function drupal_page_header() {
*/
function drupal_serve_page_from_cache(stdClass $cache) {
// Negotiate whether to use compression.
$page_compression = variable_get('page_compression', TRUE) && extension_loaded('zlib');
$page_compression = !empty($cache->data['page_compressed']);
$return_compressed = $page_compression && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE;
// Get headers set in hook_boot(). Keys are lower-case.
......@@ -1122,13 +1543,12 @@ function drupal_serve_page_from_cache(stdClass $cache) {
}
}
// If a cache is served from a HTTP proxy without hitting the web server,
// the boot and exit hooks cannot be fired, so only allow caching in
// proxies if boot hooks are disabled. If the client send a session cookie,
// do not bother caching the page in a public proxy, because the cached copy
// will only be served to that particular user due to Vary: Cookie, unless
// the Vary header has been replaced or unset in hook_boot() (see below).
$max_age = !variable_get('page_cache_invoke_hooks', TRUE) && (!isset($_COOKIE[session_name()]) || isset($hook_boot_headers['vary'])) ? variable_get('page_cache_maximum_age', 0) : 0;
// If the client sent a session cookie, a cached copy will only be served
// to that one particular client due to Vary: Cookie. Thus, do not set
// max-age > 0, allowing the page to be cached by external proxies, when a
// session cookie is present unless the Vary header has been replaced or
// unset in hook_boot().
$max_age = !isset($_COOKIE[session_name()]) || isset($hook_boot_headers['vary']) ? variable_get('page_cache_maximum_age', 0) : 0;
$default_headers['Cache-Control'] = 'public, max-age=' . $max_age;
// Entity tag should change if the output changes.
......@@ -1152,7 +1572,7 @@ function drupal_serve_page_from_cache(stdClass $cache) {
drupal_add_http_header($name, $value);
}
$default_headers['Last-Modified'] = gmdate(DATE_RFC1123, $cache->created);
$default_headers['Last-Modified'] = gmdate(DATE_RFC7231, $cache->created);
// HTTP/1.0 proxies does not support the Vary header, so prevent any caching
// by sending an Expires date in the past. HTTP/1.1 clients ignores the
......@@ -1193,7 +1613,7 @@ function drupal_serve_page_from_cache(stdClass $cache) {
}
/**
* Define the critical hooks that force modules to always be loaded.
* Defines the critical hooks that force modules to always be loaded.
*/
function bootstrap_hooks() {
return array('boot', 'exit', 'watchdog', 'language_init');
......@@ -1224,14 +1644,16 @@ function drupal_unpack($obj, $field = 'data') {
* The t() function serves two purposes. First, at run-time it translates
* user-visible text into the appropriate language. Second, various mechanisms
* that figure out what text needs to be translated work off t() -- the text
* inside t() calls is added to the database of strings to be translated. So,
* to enable a fully-translatable site, it is important that all human-readable
* text that will be displayed on the site or sent to a user is passed through
* the t() function, or a related function. See the
* inside t() calls is added to the database of strings to be translated.
* These strings are expected to be in English, so the first argument should
* always be in English. To enable a fully-translatable site, it is important
* that all human-readable text that will be displayed on the site or sent to
* a user is passed through the t() function, or a related function. See the
* @link http://drupal.org/node/322729 Localization API @endlink pages for
* more information, including recommendations on how to break up or not
* break up strings for translation.
*
* @section sec_translating_vars Translating Variables
* You should never use t() to translate variables, such as calling
* @code t($text); @endcode, unless the text that the variable holds has been
* passed through t() elsewhere (e.g., $text is one of several translated
......@@ -1245,34 +1667,54 @@ function drupal_unpack($obj, $field = 'data') {
* $text = t("@name's blog", array('@name' => format_username($account)));
* @endcode
* Basically, you can put variables like @name into your string, and t() will
* substitute their sanitized values at translation time (see $args below or
* the Localization API pages referenced above for details). Translators can
* then rearrange the string as necessary for the language (e.g., in Spanish,
* it might be "blog de @name").
* substitute their sanitized values at translation time. (See the
* Localization API pages referenced above and the documentation of
* format_string() for details about how to define variables in your string.)
* Translators can then rearrange the string as necessary for the language
* (e.g., in Spanish, it might be "blog de @name").
*
* @section sec_alt_funcs_install Use During Installation Phase
* During the Drupal installation phase, some resources used by t() wil not be
* available to code that needs localization. See st() and get_t() for
* alternatives.
*
* @section sec_context String context
* Matching source strings are normally only translated once, and the same
* translation is used everywhere that has a matching string. However, in some
* cases, a certain English source string needs to have multiple translations.
* One example of this is the string "May", which could be used as either a
* full month name or a 3-letter abbreviated month. In other languages where
* the month name for May has more than 3 letters, you would need to provide
* two different translations (one for the full name and one abbreviated), and
* the correct form would need to be chosen, depending on how "May" is being
* used. To facilitate this, the "May" string should be provided with two
* different contexts in the $options parameter when calling t(). For example:
* @code
* t('May', array(), array('context' => 'Long month name')
* t('May', array(), array('context' => 'Abbreviated month name')
* @endcode
* See https://localize.drupal.org/node/2109 for more information.
*
* @param $string
* A string containing the English string to translate.
* @param $args
* An associative array of replacements to make after translation.
* Occurrences in $string of any key in $args are replaced with the
* corresponding value, after sanitization. The sanitization function depends
* on the first character of the key:
* - !variable: Inserted as is. Use this for text that has already been
* sanitized.
* - @variable: Escaped to HTML using check_plain(). Use this for anything
* displayed on a page on the site.
* - %variable: Escaped as a placeholder for user-submitted content using
* drupal_placeholder(), which shows up as <em>emphasized</em> text.
* An associative array of replacements to make after translation. Based
* on the first character of the key, the value is escaped and/or themed.
* See format_string() for details.
* @param $options
* An associative array of additional options, with the following elements:
* - 'langcode' (defaults to the current language): The language code to
* translate to a language other than what is used to display the page.
* - 'context' (defaults to the empty context): The context the source string
* belongs to.
* - 'context' (defaults to the empty context): A string giving the context
* that the source string belongs to. See @ref sec_context above for more
* information.
*
* @return
* The translated string.
*
* @see st()
* @see get_t()
* @see format_string()
* @ingroup sanitization
*/
function t($string, array $args = array(), array $options = array()) {
......@@ -1306,40 +1748,78 @@ function t($string, array $args = array(), array $options = array()) {
return $string;
}
else {
// Transform arguments before inserting them.
foreach ($args as $key => $value) {
switch ($key[0]) {
case '@':
// Escaped only.
$args[$key] = check_plain($value);
break;
case '%':
default:
// Escaped and placeholder.
$args[$key] = drupal_placeholder($value);
break;
return format_string($string, $args);
}
}
case '!':
// Pass-through.
}
/**
* Formats a string for HTML display by replacing variable placeholders.
*
* This function replaces variable placeholders in a string with the requested
* values and escapes the values so they can be safely displayed as HTML. It
* should be used on any unknown text that is intended to be printed to an HTML
* page (especially text that may have come from untrusted users, since in that
* case it prevents cross-site scripting and other security problems).
*
* In most cases, you should use t() rather than calling this function
* directly, since it will translate the text (on non-English-only sites) in
* addition to formatting it.
*
* @param $string
* A string containing placeholders.
* @param $args
* An associative array of replacements to make. Occurrences in $string of
* any key in $args are replaced with the corresponding value, after optional
* sanitization and formatting. The type of sanitization and formatting
* depends on the first character of the key:
* - @variable: Escaped to HTML using check_plain(). Use this as the default
* choice for anything displayed on a page on the site.
* - %variable: Escaped to HTML and formatted using drupal_placeholder(),
* which makes it display as <em>emphasized</em> text.
* - !variable: Inserted as is, with no sanitization or formatting. Only use
* this for text that has already been prepared for HTML display (for
* example, user-supplied text that has already been run through
* check_plain() previously, or is expected to contain some limited HTML
* tags and has already been run through filter_xss() previously).
*
* @see t()
* @ingroup sanitization
*/
function format_string($string, array $args = array()) {
// Transform arguments before inserting them.
foreach ($args as $key => $value) {
switch ($key[0]) {
case '@':
// Escaped only.
$args[$key] = check_plain($value);
break;
case '%':
default:
// Escaped and placeholder.
$args[$key] = drupal_placeholder($value);
break;
case '!':
// Pass-through.
}
return strtr($string, $args);
}
return strtr($string, $args);
}
/**
* Encode special characters in a plain-text string for display as HTML.
* Encodes special characters in a plain-text string for display as HTML.
*
* Also validates strings as UTF-8 to prevent cross site scripting attacks on
* Internet Explorer 6.
*
* @param $text
* @param string $text
* The text to be checked or processed.
*
* @return
* An HTML safe version of $text, or an empty string if $text is not
* valid UTF-8.
* @return string
* An HTML safe version of $text. If $text is not valid UTF-8, an empty string
* is returned and, on PHP < 5.4, a warning may be issued depending on server
* configuration (see @link https://bugs.php.net/bug.php?id=47494 @endlink).
*
* @see drupal_validate_utf8()
* @ingroup sanitization
......@@ -1368,6 +1848,7 @@ function check_plain($text) {
*
* @param $text
* The text to check.
*
* @return
* TRUE if the text is valid UTF-8, FALSE if not.
*/
......@@ -1382,11 +1863,12 @@ function drupal_validate_utf8($text) {
}
/**
* Since $_SERVER['REQUEST_URI'] is only available on Apache, we
* generate an equivalent using other environment variables.
* Returns the equivalent of Apache's $_SERVER['REQUEST_URI'] variable.
*
* Because $_SERVER['REQUEST_URI'] is only available on Apache, we generate an
* equivalent using other environment variables.
*/
function request_uri() {
if (isset($_SERVER['REQUEST_URI'])) {
$uri = $_SERVER['REQUEST_URI'];
}
......@@ -1408,7 +1890,7 @@ function request_uri() {
}
/**
* Log an exception.
* Logs an exception.
*
* This is a wrapper function for watchdog() which automatically decodes an
* exception.
......@@ -1419,17 +1901,17 @@ function request_uri() {
* The exception that is going to be logged.
* @param $message
* The message to store in the log. If empty, a text that contains all useful
* information about the passed in exception is used.
* information about the passed-in exception is used.
* @param $variables
* Array of variables to replace in the message on display. Defaults to the
* return value of drupal_decode_exception().
* return value of _drupal_decode_exception().
* @param $severity
* The severity of the message, as per RFC 3164.
* @param $link
* A link to associate with the message.
*
* @see watchdog()
* @see drupal_decode_exception()
* @see _drupal_decode_exception()
*/
function watchdog_exception($type, Exception $exception, $message = NULL, $variables = array(), $severity = WATCHDOG_ERROR, $link = NULL) {
......@@ -1449,7 +1931,7 @@ function watchdog_exception($type, Exception $exception, $message = NULL, $varia
}
/**
* Log a system message.
* Logs a system message.
*
* @param $type
* The category to which this message belongs. Can be any string, but the
......@@ -1465,8 +1947,16 @@ function watchdog_exception($type, Exception $exception, $message = NULL, $varia
* NULL if message is already translated or not possible to
* translate.
* @param $severity
* The severity of the message, as per RFC 3164. Possible values are
* WATCHDOG_ERROR, WATCHDOG_WARNING, etc.
* 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: (default) Normal but significant conditions.
* - WATCHDOG_INFO: Informational messages.
* - WATCHDOG_DEBUG: Debug-level messages.
* @param $link
* A link to associate with the message.
*
......@@ -1483,6 +1973,9 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO
if (!$in_error_state && function_exists('module_implements')) {
$in_error_state = TRUE;
// The user object may not exist in all conditions, so 0 is substituted if needed.
$user_uid = isset($user->uid) ? $user->uid : 0;
// Prepare the fields to be logged
$log_entry = array(
'type' => $type,
......@@ -1491,10 +1984,12 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO
'severity' => $severity,
'link' => $link,
'user' => $user,
'uid' => $user_uid,
'request_uri' => $base_root . request_uri(),
'referer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '',
'ip' => ip_address(),
'timestamp' => REQUEST_TIME,
// Request time isn't accurate for long processes, use time() instead.
'timestamp' => time(),
);
// Call the logging hooks to log/process the message
......@@ -1509,25 +2004,40 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO
}
/**
* Set a message which reflects the status of the performed operation.
* Sets a message to display to the user.
*
* If the function is called with no arguments, this function returns all set
* messages without clearing them.
* Messages are stored in a session variable and displayed in page.tpl.php via
* the $messages theme variable.
*
* @param $message
* The message should begin with a capital letter and always ends with a
* period '.'.
* @param $type
* The type of the message. One of the following values are possible:
* Example usage:
* @code
* drupal_set_message(t('An error occurred and processing did not complete.'), 'error');
* @endcode
*
* @param string $message
* (optional) The translated message to be displayed to the user. For
* consistency with other messages, it should begin with a capital letter and
* end with a period.
* @param string $type
* (optional) The message's type. Defaults to 'status'. These values are
* supported:
* - 'status'
* - 'warning'
* - 'error'
* @param $repeat
* If this is FALSE and the message is already set, then the message won't
* be repeated.
* @param bool $repeat
* (optional) If this is FALSE and the message is already set, then the
* message won't be repeated. Defaults to TRUE.
*
* @return array|null
* A multidimensional array with keys corresponding to the set message types.
* The indexed array values of each contain the set messages for that type.
* Or, if there are no messages set, the function returns NULL.
*
* @see drupal_get_messages()
* @see theme_status_messages()
*/
function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) {
if ($message) {
if ($message || $message === '0' || $message === 0) {
if (!isset($_SESSION['messages'][$type])) {
$_SESSION['messages'][$type] = array();
}
......@@ -1545,17 +2055,29 @@ function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) {
}
/**
* Return all messages that have been set.
* Returns all messages that have been set with drupal_set_message().
*
* @param $type
* (optional) Only return messages of this type.
* @param $clear_queue
* (optional) Set to FALSE if you do not want to clear the messages queue
* @return
* An associative array, the key is the message type, the value an array
* of messages. If the $type parameter is passed, you get only that type,
* or an empty array if there are no such messages. If $type is not passed,
* all message types are returned, or an empty array if none exist.
* @param string $type
* (optional) Limit the messages returned by type. Defaults to NULL, meaning
* all types. These values are supported:
* - NULL
* - 'status'
* - 'warning'
* - 'error'
* @param bool $clear_queue
* (optional) If this is TRUE, the queue will be cleared of messages of the
* type specified in the $type parameter. Otherwise the queue will be left
* intact. Defaults to TRUE.
*
* @return array
* A multidimensional array with keys corresponding to the set message types.
* The indexed array values of each contain the set messages for that type.
* The messages returned are limited to the type specified in the $type
* parameter. If there are no messages of the specified type, an empty array
* is returned.
*
* @see drupal_set_message()
* @see theme_status_messages()
*/
function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
if ($messages = drupal_set_message()) {
......@@ -1578,7 +2100,9 @@ function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
}
/**
* Get the title of the current page, for display on the page and in the title bar.
* Gets the title of the current page.
*
* The title is displayed on the page and in the title bar.
*
* @return
* The current page's title.
......@@ -1595,7 +2119,9 @@ function drupal_get_title() {
}
/**
* Set the title of the current page, for display on the page and in the title bar.
* Sets the title of the current page.
*
* The title is displayed on the page and in the title bar.
*
* @param $title
* Optional string value to assign to the page title; or if set to NULL
......@@ -1620,7 +2146,7 @@ function drupal_set_title($title = NULL, $output = CHECK_PLAIN) {
}
/**
* Check to see if an IP address has been blocked.
* Checks to see if an IP address has been blocked.
*
* Blocked IP addresses are stored in the database by default. However for
* performance reasons we allow an override in settings.php. This allows us
......@@ -1629,6 +2155,7 @@ function drupal_set_title($title = NULL, $output = CHECK_PLAIN) {
*
* @param $ip
* IP address to check.
*
* @return bool
* TRUE if access is denied, FALSE if access is allowed.
*/
......@@ -1654,7 +2181,7 @@ function drupal_is_denied($ip) {
}
/**
* Handle denied users.
* Handles denied users.
*
* @param $ip
* IP address to check. Prints a message and exits if access is denied.
......@@ -1668,40 +2195,74 @@ function drupal_block_denied($ip) {
}
}
/**
* Returns a URL-safe, base64 encoded string of highly randomized bytes (over the full 8-bit range).
*
* @param $byte_count
* The number of random bytes to fetch and base64 encode.
*
* @return string
* The base64 encoded result will have a length of up to 4 * $byte_count.
*/
function drupal_random_key($byte_count = 32) {
return drupal_base64_encode(drupal_random_bytes($byte_count));
}
/**
* Returns a URL-safe, base64 encoded version of the supplied string.
*
* @param $string
* The string to convert to base64.
*
* @return string
*/
function drupal_base64_encode($string) {
$data = base64_encode($string);
// Modify the output so it's safe to use in URLs.
return strtr($data, array('+' => '-', '/' => '_', '=' => ''));
}
/**
* Returns a string of highly randomized bytes (over the full 8-bit range).
*
* This function is better than simply calling mt_rand() or any other built-in
* PHP function because it can return a long string of bytes (compared to < 4
* bytes normally from mt_rand()) and uses the best available pseudo-random source.
* bytes normally from mt_rand()) and uses the best available pseudo-random
* source.
*
* @param $count
* The number of characters (bytes) to return in the string.
*/
function drupal_random_bytes($count) {
// $random_state does not use drupal_static as it stores random bytes.
static $random_state, $bytes;
// Initialize on the first call. The contents of $_SERVER includes a mix of
// user-specific and system information that varies a little with each page.
if (!isset($random_state)) {
$random_state = print_r($_SERVER, TRUE);
if (function_exists('getmypid')) {
// Further initialize with the somewhat random PHP process ID.
$random_state .= getmypid();
}
$bytes = '';
}
if (strlen($bytes) < $count) {
// /dev/urandom is available on many *nix systems and is considered the
// best commonly available pseudo-random source.
if ($fh = @fopen('/dev/urandom', 'rb')) {
static $random_state, $bytes, $has_openssl;
$missing_bytes = $count - strlen($bytes);
if ($missing_bytes > 0) {
// PHP versions prior 5.3.4 experienced openssl_random_pseudo_bytes()
// locking on Windows and rendered it unusable.
if (!isset($has_openssl)) {
$has_openssl = version_compare(PHP_VERSION, '5.3.4', '>=') && function_exists('openssl_random_pseudo_bytes');
}
// openssl_random_pseudo_bytes() will find entropy in a system-dependent
// way.
if ($has_openssl) {
$bytes .= openssl_random_pseudo_bytes($missing_bytes);
}
// Else, read directly from /dev/urandom, which is available on many *nix
// systems and is considered cryptographically secure.
elseif ($fh = @fopen('/dev/urandom', 'rb')) {
// PHP only performs buffered reads, so in reality it will always read
// at least 4096 bytes. Thus, it costs nothing extra to read and store
// that much so as to speed any additional invocations.
$bytes .= fread($fh, max(4096, $count));
$bytes .= fread($fh, max(4096, $missing_bytes));
fclose($fh);
}
// If /dev/urandom is not available or returns no bytes, this loop will
// If we couldn't get enough entropy, this simple hash-based PRNG will
// generate a good set of pseudo-random bytes on any system.
// Note that it may be important that our $random_state is passed
// through hash() prior to being rolled into $output, that the two hash()
......@@ -1709,9 +2270,23 @@ function drupal_random_bytes($count) {
// the microtime() - is prepended rather than appended. This is to avoid
// directly leaking $random_state via the $output stream, which could
// allow for trivial prediction of further "random" numbers.
while (strlen($bytes) < $count) {
$random_state = hash('sha256', microtime() . mt_rand() . $random_state);
$bytes .= hash('sha256', mt_rand() . $random_state, TRUE);
if (strlen($bytes) < $count) {
// Initialize on the first call. The contents of $_SERVER includes a mix of
// user-specific and system information that varies a little with each page.
if (!isset($random_state)) {
$random_state = print_r($_SERVER, TRUE);
if (function_exists('getmypid')) {
// Further initialize with the somewhat random PHP process ID.
$random_state .= getmypid();
}
$bytes = '';
}
do {
$random_state = hash('sha256', microtime() . mt_rand() . $random_state);
$bytes .= hash('sha256', mt_rand() . $random_state, TRUE);
}
while (strlen($bytes) < $count);
}
}
$output = substr($bytes, 0, $count);
......@@ -1720,25 +2295,29 @@ function drupal_random_bytes($count) {
}
/**
* Calculate a base-64 encoded, URL-safe sha-256 hmac.
* Calculates a base-64 encoded, URL-safe sha-256 hmac.
*
* @param $data
* @param string $data
* String to be validated with the hmac.
* @param $key
* @param string $key
* A secret string key.
*
* @return
* @return string
* A base-64 encoded sha-256 hmac, with + replaced with -, / with _ and
* any = padding characters removed.
*/
function drupal_hmac_base64($data, $key) {
$hmac = base64_encode(hash_hmac('sha256', $data, $key, TRUE));
// Casting $data and $key to strings here is necessary to avoid empty string
// results of the hash function if they are not scalar values. As this
// function is used in security-critical contexts like token validation it is
// important that it never returns an empty string.
$hmac = base64_encode(hash_hmac('sha256', (string) $data, (string) $key, TRUE));
// Modify the hmac so it's safe to use in URLs.
return strtr($hmac, array('+' => '-', '/' => '_', '=' => ''));
}
/**
* Calculate a base-64 encoded, URL-safe sha-256 hash.
* Calculates a base-64 encoded, URL-safe sha-256 hash.
*
* @param $data
* String to be hashed.
......@@ -1781,7 +2360,8 @@ function drupal_hash_base64($data) {
* @see drupal_array_merge_deep_array()
*/
function drupal_array_merge_deep() {
return drupal_array_merge_deep_array(func_get_args());
$args = func_get_args();
return drupal_array_merge_deep_array($args);
}
/**
......@@ -1832,7 +2412,7 @@ function drupal_array_merge_deep_array($arrays) {
* @return Object - the user object.
*/
function drupal_anonymous_user() {
$user = new stdClass();
$user = variable_get('drupal_anonymous_user_object', new stdClass);
$user->uid = 0;
$user->hostname = ip_address();
$user->roles = array();
......@@ -1842,20 +2422,34 @@ function drupal_anonymous_user() {
}
/**
* A string describing a phase of Drupal to load. Each phase adds to the
* previous one, so invoking a later phase automatically runs the earlier
* phases too. The most important usage is that if you want to access the
* Drupal database from a script without loading anything else, you can
* include bootstrap.inc, and call drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE).
* Ensures Drupal is bootstrapped to the specified phase.
*
* @param $phase
* A constant. Allowed values are the DRUPAL_BOOTSTRAP_* constants.
* @param $new_phase
* In order to bootstrap Drupal from another PHP script, you can use this code:
* @code
* define('DRUPAL_ROOT', '/path/to/drupal');
* require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
* drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
* @endcode
*
* @param int $phase
* A constant telling which phase to bootstrap to. When you bootstrap to a
* particular phase, all earlier phases are run automatically. Possible
* values:
* - DRUPAL_BOOTSTRAP_CONFIGURATION: Initializes configuration.
* - DRUPAL_BOOTSTRAP_PAGE_CACHE: Tries to serve a cached page.
* - DRUPAL_BOOTSTRAP_DATABASE: Initializes the database layer.
* - DRUPAL_BOOTSTRAP_VARIABLES: Initializes the variable system.
* - DRUPAL_BOOTSTRAP_SESSION: Initializes session handling.
* - DRUPAL_BOOTSTRAP_PAGE_HEADER: Sets up the page header.
* - DRUPAL_BOOTSTRAP_LANGUAGE: Finds out the language of the page.
* - DRUPAL_BOOTSTRAP_FULL: Fully loads Drupal. Validates and fixes input
* data.
* @param boolean $new_phase
* A boolean, set to FALSE if calling drupal_bootstrap from inside a
* function called from drupal_bootstrap (recursion).
* @return
* The most recently completed phase.
*
* @return int
* The most recently completed phase.
*/
function drupal_bootstrap($phase = NULL, $new_phase = TRUE) {
// Not drupal_static(), because does not depend on any run-time information.
......@@ -1876,12 +2470,13 @@ function drupal_bootstrap($phase = NULL, $new_phase = TRUE) {
// bootstrap state.
static $stored_phase = -1;
// When not recursing, store the phase name so it's not forgotten while
// recursing.
if ($new_phase) {
$final_phase = $phase;
}
if (isset($phase)) {
// When not recursing, store the phase name so it's not forgotten while
// recursing but take care of not going backwards.
if ($new_phase && $phase >= $stored_phase) {
$final_phase = $phase;
}
// Call a phase if it has not been called before and is below the requested
// phase.
while ($phases && $phase > $stored_phase && $final_phase > $stored_phase) {
......@@ -1934,7 +2529,7 @@ function drupal_bootstrap($phase = NULL, $new_phase = TRUE) {
}
/**
* Return the time zone of the current user.
* Returns the time zone of the current user.
*/
function drupal_get_user_timezone() {
global $user;
......@@ -1949,7 +2544,20 @@ function drupal_get_user_timezone() {
}
/**
* Custom PHP error handler.
* Gets a salt useful for hardening against SQL injection.
*
* @return
* A salt based on information in settings.php, not in the database.
*/
function drupal_get_hash_salt() {
global $drupal_hash_salt, $databases;
// If the $drupal_hash_salt variable is empty, a hash of the serialized
// database credentials is used as a fallback salt.
return empty($drupal_hash_salt) ? hash('sha256', serialize($databases)) : $drupal_hash_salt;
}
/**
* Provides custom PHP error handling.
*
* @param $error_level
* The level of the error raised.
......@@ -1960,7 +2568,8 @@ function drupal_get_user_timezone() {
* @param $line
* The line number the error was raised at.
* @param $context
* An array that points to the active symbol table at the point the error occurred.
* An array that points to the active symbol table at the point the error
* occurred.
*/
function _drupal_error_handler($error_level, $message, $filename, $line, $context) {
require_once DRUPAL_ROOT . '/includes/errors.inc';
......@@ -1968,7 +2577,7 @@ function _drupal_error_handler($error_level, $message, $filename, $line, $contex
}
/**
* Custom PHP exception handler.
* Provides custom PHP exception handling.
*
* Uncaught exceptions are those not enclosed in a try/catch block. They are
* always fatal: the execution of the script will stop as soon as the exception
......@@ -1996,7 +2605,7 @@ function _drupal_exception_handler($exception) {
}
/**
* Bootstrap configuration: Setup script environment and load settings.php.
* Sets up the script environment and loads settings.php.
*/
function _drupal_bootstrap_configuration() {
// Set the Drupal custom error handler.
......@@ -2011,7 +2620,7 @@ function _drupal_bootstrap_configuration() {
}
/**
* Bootstrap page cache: Try to serve a page from cache.
* Attempts to serve a page from the cache.
*/
function _drupal_bootstrap_page_cache() {
global $user;
......@@ -2067,7 +2676,7 @@ function _drupal_bootstrap_page_cache() {
}
/**
* Bootstrap database: Initialize database system and register autoload functions.
* Initializes the database system and registers autoload functions.
*/
function _drupal_bootstrap_database() {
// Redirect the user to the installation script if Drupal has not been
......@@ -2116,10 +2725,13 @@ function _drupal_bootstrap_database() {
// the install or upgrade process.
spl_autoload_register('drupal_autoload_class');
spl_autoload_register('drupal_autoload_interface');
if (version_compare(PHP_VERSION, '5.4') >= 0) {
spl_autoload_register('drupal_autoload_trait');
}
}
/**
* Bootstrap variables: Load system variables and all enabled bootstrap modules.
* Loads system variables and all enabled bootstrap modules.
*/
function _drupal_bootstrap_variables() {
global $conf;
......@@ -2133,10 +2745,30 @@ function _drupal_bootstrap_variables() {
// Load bootstrap modules.
require_once DRUPAL_ROOT . '/includes/module.inc';
module_load_all(TRUE);
// Sanitize the destination parameter (which is often used for redirects) to
// prevent open redirect attacks leading to other domains. Sanitize both
// $_GET['destination'] and $_REQUEST['destination'] to protect code that
// relies on either, but do not sanitize $_POST to avoid interfering with
// unrelated form submissions. The sanitization happens here because
// url_is_external() requires the variable system to be available.
if (isset($_GET['destination']) || isset($_REQUEST['destination'])) {
require_once DRUPAL_ROOT . '/includes/common.inc';
// If the destination is an external URL, remove it.
if (isset($_GET['destination']) && url_is_external($_GET['destination'])) {
unset($_GET['destination']);
unset($_REQUEST['destination']);
}
// If there's still something in $_REQUEST['destination'] that didn't come
// from $_GET, check it too.
if (isset($_REQUEST['destination']) && (!isset($_GET['destination']) || $_REQUEST['destination'] != $_GET['destination']) && url_is_external($_REQUEST['destination'])) {
unset($_REQUEST['destination']);
}
}
}
/**
* Bootstrap page header: Invoke hook_boot(), initialize locking system, and send default HTTP headers.
* Invokes hook_boot(), initializes locking system, and sends HTTP headers.
*/
function _drupal_bootstrap_page_header() {
bootstrap_invoke_all('boot');
......@@ -2155,12 +2787,11 @@ function _drupal_bootstrap_page_header() {
* @see drupal_bootstrap()
*/
function drupal_get_bootstrap_phase() {
return drupal_bootstrap();
return drupal_bootstrap(NULL, FALSE);
}
/**
* Checks the current User-Agent string to see if this is an internal request
* from SimpleTest. If so, returns the test prefix for this test.
* Returns the test prefix if this is an internal request from SimpleTest.
*
* @return
* Either the simpletest prefix (the string "simpletest" followed by any
......@@ -2168,7 +2799,6 @@ function drupal_get_bootstrap_phase() {
* HMAC and timestamp.
*/
function drupal_valid_test_ua() {
global $drupal_hash_salt;
// No reason to reset this.
static $test_prefix;
......@@ -2182,7 +2812,7 @@ function drupal_valid_test_ua() {
// We use the salt from settings.php to make the HMAC key, since
// the database is not yet initialized and we can't access any Drupal variables.
// The file properties add more entropy not easily accessible to others.
$key = $drupal_hash_salt . filectime(__FILE__) . fileinode(__FILE__);
$key = drupal_get_hash_salt() . filectime(__FILE__) . fileinode(__FILE__);
$time_diff = REQUEST_TIME - $time;
// Since we are making a local request a 5 second time window is allowed,
// and the HMAC must match.
......@@ -2192,21 +2822,21 @@ function drupal_valid_test_ua() {
}
}
return FALSE;
$test_prefix = FALSE;
return $test_prefix;
}
/**
* Generate a user agent string with a HMAC and timestamp for simpletest.
* Generates a user agent string with a HMAC and timestamp for simpletest.
*/
function drupal_generate_test_ua($prefix) {
global $drupal_hash_salt;
static $key;
if (!isset($key)) {
// We use the salt from settings.php to make the HMAC key, since
// the database is not yet initialized and we can't access any Drupal variables.
// The file properties add more entropy not easily accessible to others.
$key = $drupal_hash_salt . filectime(__FILE__) . fileinode(__FILE__);
$key = drupal_get_hash_salt() . filectime(__FILE__) . fileinode(__FILE__);
}
// Generate a moderately secure HMAC based on the database credentials.
$salt = uniqid('', TRUE);
......@@ -2228,15 +2858,65 @@ function drupal_maintenance_theme() {
}
/**
* Return TRUE if a Drupal installation is currently being attempted.
* Returns a simple 404 Not Found page.
*
* If fast 404 pages are enabled, and this is a matching page then print a
* simple 404 page and exit.
*
* This function is called from drupal_deliver_html_page() at the time when a
* a normal 404 page is generated, but it can also optionally be called directly
* from settings.php to prevent a Drupal bootstrap on these pages. See
* documentation in settings.php for the benefits and drawbacks of using this.
*
* Paths to dynamically-generated content, such as image styles, should also be
* accounted for in this function.
*/
function drupal_fast_404() {
$exclude_paths = variable_get('404_fast_paths_exclude', FALSE);
if ($exclude_paths && !preg_match($exclude_paths, $_GET['q'])) {
$fast_paths = variable_get('404_fast_paths', FALSE);
if ($fast_paths && preg_match($fast_paths, $_GET['q'])) {
drupal_add_http_header('Status', '404 Not Found');
$fast_404_html = variable_get('404_fast_html', '<!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"><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>');
// Replace @path in the variable with the page path.
print strtr($fast_404_html, array('@path' => check_plain(request_uri())));
exit;
}
}
}
/**
* Returns TRUE if a Drupal installation is currently being attempted.
*/
function drupal_installation_attempted() {
return defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install';
}
/**
* Return the name of the localization function. Use in code that needs to
* run both during installation and normal operation.
* Returns the name of the proper localization function.
*
* get_t() exists to support localization for code that might run during
* the installation phase, when some elements of the system might not have
* loaded.
*
* This would include implementations of hook_install(), which could run
* during the Drupal installation phase, and might also be run during
* non-installation time, such as while installing the module from the
* module administration page.
*
* Example usage:
* @code
* $t = get_t();
* $translated = $t('translate this');
* @endcode
*
* Use t() if your code will never run during the Drupal installation phase.
* Use st() if your code will only run during installation and never any other
* time. Use get_t() if your code could run in either circumstance.
*
* @see t()
* @see st()
* @ingroup sanitization
*/
function get_t() {
static $t;
......@@ -2249,7 +2929,7 @@ function get_t() {
}
/**
* Initialize all the defined language types.
* Initializes all the defined language types.
*/
function drupal_language_initialize() {
$types = language_types();
......@@ -2274,7 +2954,7 @@ function drupal_language_initialize() {
}
/**
* The built-in language types.
* Returns a list of the built-in language types.
*
* @return
* An array of key-values pairs where the key is the language type and the
......@@ -2289,23 +2969,42 @@ function drupal_language_types() {
}
/**
* Return true if there is more than one language enabled.
* Returns TRUE if there is more than one language enabled.
*
* @return
* TRUE if more than one language is enabled.
*/
function drupal_multilingual() {
// The "language_count" variable stores the number of enabled languages to
// avoid unnecessarily querying the database when building the list of
// enabled languages on monolingual sites.
return variable_get('language_count', 1) > 1;
}
/**
* Return an array of the available language types.
* Returns an array of the available language types.
*
* @return
* An array of all language types where the keys of each are the language type
* name and its value is its configurability (TRUE/FALSE).
*/
function language_types() {
return array_keys(variable_get('language_types', drupal_language_types()));
}
/**
* Get a list of languages set up indexed by the specified key
* Returns a list of installed languages, indexed by the specified key.
*
* @param $field The field to index the list with.
* @param $field
* (optional) The field to index the list with.
*
* @return
* An associative array, keyed on the values of $field.
* - If $field is 'weight' or 'enabled', the array is nested, with the outer
* array's values each being associative arrays with language codes as
* keys and language objects as values.
* - For all other values of $field, the array is only one level deep, and
* the array's values are language objects.
*/
function language_list($field = 'language') {
$languages = &drupal_static(__FUNCTION__);
......@@ -2344,10 +3043,14 @@ function language_list($field = 'language') {
}
/**
* Default language used on the site
* Returns the default language, as an object, or one of its properties.
*
* @param $property
* Optional property of the language object to return
* (optional) The property of the language object to return.
*
* @return
* Either the language object for the default language used on the site,
* or the property of that object named in the $property parameter.
*/
function language_default($property = NULL) {
$language = variable_get('language_default', (object) array('language' => 'en', 'name' => 'English', 'native' => 'English', 'direction' => 0, 'enabled' => 1, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => '', 'weight' => 0, 'javascript' => ''));
......@@ -2363,6 +3066,8 @@ function language_default($property = NULL) {
* base_path() returns "/drupalfolder/".
* - http://example.com/path/alias (which is a path alias for node/306) returns
* "path/alias" as opposed to the internal path.
* - http://example.com/index.php returns an empty string (meaning: front page).
* - http://example.com/index.php?page=1 returns an empty string.
*
* @return
* The requested Drupal URL path.
......@@ -2376,7 +3081,7 @@ function request_path() {
return $path;
}
if (isset($_GET['q'])) {
if (isset($_GET['q']) && is_string($_GET['q'])) {
// This is a request with a ?q=foo/bar query string. $_GET['q'] is
// overwritten in drupal_path_initialize(), but request_path() is called
// very early in the bootstrap process, so the original value is saved in
......@@ -2384,11 +3089,19 @@ function request_path() {
$path = $_GET['q'];
}
elseif (isset($_SERVER['REQUEST_URI'])) {
// This is a request using a clean URL. Extract the path from REQUEST_URI.
// This request is either a clean URL, or 'index.php', or nonsense.
// Extract the path from REQUEST_URI.
$request_path = strtok($_SERVER['REQUEST_URI'], '?');
$base_path_len = strlen(rtrim(dirname($_SERVER['SCRIPT_NAME']), '\/'));
// Unescape and strip $base_path prefix, leaving q without a leading slash.
$path = substr(urldecode($request_path), $base_path_len + 1);
// If the path equals the script filename, either because 'index.php' was
// explicitly provided in the URL, or because the server added it to
// $_SERVER['REQUEST_URI'] even when it wasn't provided in the URL (some
// versions of Microsoft IIS do this), the front page should be served.
if ($path == basename($_SERVER['PHP_SELF'])) {
$path = '';
}
}
else {
// This is the front page.
......@@ -2404,16 +3117,16 @@ function request_path() {
}
/**
* Return a component of the current Drupal path.
* Returns a component of the current Drupal path.
*
* When viewing a page at the path "admin/structure/types", for example, arg(0)
* returns "admin", arg(1) returns "structure", and arg(2) returns "types".
*
* Avoid use of this function where possible, as resulting code is hard to read.
* In menu callback functions, attempt to use named arguments. See the explanation
* in menu.inc for how to construct callbacks that take arguments. When attempting
* to use this function to load an element from the current path, e.g. loading the
* node on a node page, please use menu_get_object() instead.
* Avoid use of this function where possible, as resulting code is hard to
* read. In menu callback functions, attempt to use named arguments. See the
* explanation in menu.inc for how to construct callbacks that take arguments.
* When attempting to use this function to load an element from the current
* path, e.g. loading the node on a node page, use menu_get_object() instead.
*
* @param $index
* The index of the component, where each component is separated by a '/'
......@@ -2453,6 +3166,8 @@ function arg($index = NULL, $path = NULL) {
}
/**
* Returns the IP address of the client machine.
*
* If Drupal is behind a reverse proxy, we use the X-Forwarded-For header
* instead of $_SERVER['REMOTE_ADDR'], which would be the IP address of
* the proxy server, and not the client's. The actual header name can be
......@@ -2487,8 +3202,15 @@ function ip_address() {
// Eliminate all trusted IPs.
$untrusted = array_diff($forwarded, $reverse_proxy_addresses);
// The right-most IP is the most specific we can trust.
$ip_address = array_pop($untrusted);
if (!empty($untrusted)) {
// The right-most IP is the most specific we can trust.
$ip_address = array_pop($untrusted);
}
else {
// All IP addresses in the forwarded array are configured proxy IPs
// (and thus trusted). We take the leftmost IP.
$ip_address = array_shift($forwarded);
}
}
}
}
......@@ -2497,15 +3219,17 @@ function ip_address() {
}
/**
* @ingroup schemaapi
* @addtogroup schemaapi
* @{
*/
/**
* Get the schema definition of a table, or the whole database schema.
* Gets the schema definition of a table, or the whole database schema.
*
* The returned schema will include any modifications made by any
* module that implements hook_schema_alter().
* module that implements hook_schema_alter(). To get the schema without
* modifications, use drupal_get_schema_unprocessed().
*
*
* @param $table
* The name of the table. If not given, the schema of all tables is returned.
......@@ -2513,6 +3237,61 @@ function ip_address() {
* If true, the schema will be rebuilt instead of retrieved from the cache.
*/
function drupal_get_schema($table = NULL, $rebuild = FALSE) {
static $schema;
if ($rebuild || !isset($table)) {
$schema = drupal_get_complete_schema($rebuild);
}
elseif (!isset($schema)) {
$schema = new SchemaCache();
}
if (!isset($table)) {
return $schema;
}
if (isset($schema[$table])) {
return $schema[$table];
}
else {
return FALSE;
}
}
/**
* Extends DrupalCacheArray to allow for dynamic building of the schema cache.
*/
class SchemaCache extends DrupalCacheArray {
/**
* Constructs a SchemaCache object.
*/
public function __construct() {
// Cache by request method.
parent::__construct('schema:runtime:' . ($_SERVER['REQUEST_METHOD'] == 'GET'), 'cache');
}
/**
* Overrides DrupalCacheArray::resolveCacheMiss().
*/
protected function resolveCacheMiss($offset) {
$complete_schema = drupal_get_complete_schema();
$value = isset($complete_schema[$offset]) ? $complete_schema[$offset] : NULL;
$this->storage[$offset] = $value;
$this->persist($offset);
return $value;
}
}
/**
* Gets the whole database schema.
*
* The returned schema will include any modifications made by any
* module that implements hook_schema_alter().
*
* @param $rebuild
* If true, the schema will be rebuilt instead of retrieved from the cache.
*/
function drupal_get_complete_schema($rebuild = FALSE) {
static $schema = array();
if (empty($schema) || $rebuild) {
......@@ -2554,38 +3333,34 @@ function drupal_get_schema($table = NULL, $rebuild = FALSE) {
if (!empty($schema) && (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL)) {
cache_set('schema', $schema);
}
if ($rebuild) {
cache_clear_all('schema:', 'cache', TRUE);
}
}
}
if (!isset($table)) {
return $schema;
}
elseif (isset($schema[$table])) {
return $schema[$table];
}
else {
return FALSE;
}
return $schema;
}
/**
* @} End of "ingroup schemaapi".
* @} End of "addtogroup schemaapi".
*/
/**
* @ingroup registry
* @addtogroup registry
* @{
*/
/**
* Confirm that an interface is available.
* Confirms that an interface is available.
*
* This function is rarely called directly. Instead, it is registered as an
* spl_autoload() handler, and PHP calls it for us when necessary.
*
* @param $interface
* The name of the interface to check or load.
*
* @return
* TRUE if the interface is currently available, FALSE otherwise.
*/
......@@ -2594,13 +3369,14 @@ function drupal_autoload_interface($interface) {
}
/**
* Confirm that a class is available.
* Confirms that a class is available.
*
* This function is rarely called directly. Instead, it is registered as an
* spl_autoload() handler, and PHP calls it for us when necessary.
*
* @param $class
* The name of the class to check or load.
*
* @return
* TRUE if the class is currently available, FALSE otherwise.
*/
......@@ -2609,7 +3385,23 @@ function drupal_autoload_class($class) {
}
/**
* Helper to check for a resource in the registry.
* Confirms that a trait is available.
*
* This function is rarely called directly. Instead, it is registered as an
* spl_autoload() handler, and PHP calls it for us when necessary.
*
* @param string $trait
* The name of the trait to check or load.
*
* @return bool
* TRUE if the trait is currently available, FALSE otherwise.
*/
function drupal_autoload_trait($trait) {
return _registry_check_code('trait', $trait);
}
/**
* Checks for a resource in the registry.
*
* @param $type
* The type of resource we are looking up, or one of the constants
......@@ -2618,6 +3410,7 @@ function drupal_autoload_class($class) {
* @param $name
* The name of the resource, or NULL if either of the REGISTRY_* constants
* is passed in.
*
* @return
* TRUE if the resource was found, FALSE if not.
* NULL if either of the REGISTRY_* constants is passed in as $type.
......@@ -2625,7 +3418,7 @@ function drupal_autoload_class($class) {
function _registry_check_code($type, $name = NULL) {
static $lookup_cache, $cache_update_needed;
if ($type == 'class' && class_exists($name) || $type == 'interface' && interface_exists($name)) {
if ($type == 'class' && class_exists($name) || $type == 'interface' && interface_exists($name) || $type == 'trait' && trait_exists($name)) {
return TRUE;
}
......@@ -2658,7 +3451,7 @@ function _registry_check_code($type, $name = NULL) {
$cache_key = $type[0] . $name;
if (isset($lookup_cache[$cache_key])) {
if ($lookup_cache[$cache_key]) {
require_once DRUPAL_ROOT . '/' . $lookup_cache[$cache_key];
include_once DRUPAL_ROOT . '/' . $lookup_cache[$cache_key];
}
return (bool) $lookup_cache[$cache_key];
}
......@@ -2666,10 +3459,13 @@ function _registry_check_code($type, $name = NULL) {
// This function may get called when the default database is not active, but
// there is no reason we'd ever want to not use the default database for
// this query.
$file = Database::getConnection('default', 'default')->query("SELECT filename FROM {registry} WHERE name = :name AND type = :type", array(
':name' => $name,
':type' => $type,
))
$file = Database::getConnection('default', 'default')
->select('registry', 'r', array('target' => 'default'))
->fields('r', array('filename'))
// Use LIKE here to make the query case-insensitive.
->condition('r.name', db_like($name), 'LIKE')
->condition('r.type', $type)
->execute()
->fetchField();
// Flag that we've run a lookup query and need to update the cache.
......@@ -2680,7 +3476,7 @@ function _registry_check_code($type, $name = NULL) {
$lookup_cache[$cache_key] = $file;
if ($file) {
require_once DRUPAL_ROOT . '/' . $file;
include_once DRUPAL_ROOT . '/' . $file;
return TRUE;
}
else {
......@@ -2689,7 +3485,7 @@ function _registry_check_code($type, $name = NULL) {
}
/**
* Rescan all enabled modules and rebuild the registry.
* Rescans all enabled modules and rebuilds the registry.
*
* Rescans all code in modules or includes directories, storing the location of
* each interface or class in the database.
......@@ -2700,25 +3496,44 @@ function registry_rebuild() {
}
/**
* Update the registry based on the latest files listed in the database.
* Updates the registry based on the latest files listed in the database.
*
* This function should be used when system_rebuild_module_data() does not need
* to be called, because it is already known that the list of files in the
* {system} table matches those in the file system.
*
* @return
* TRUE if the registry was rebuilt, FALSE if another thread was rebuilding
* in parallel and the current thread just waited for completion.
*
* @see registry_rebuild()
*/
function registry_update() {
// install_system_module() calls module_enable() which calls into this
// function during initial system installation, so the lock system is neither
// loaded nor does its storage exist yet.
$in_installer = drupal_installation_attempted();
if (!$in_installer && !lock_acquire(__FUNCTION__)) {
// Another request got the lock, wait for it to finish.
lock_wait(__FUNCTION__);
return FALSE;
}
require_once DRUPAL_ROOT . '/includes/registry.inc';
_registry_update();
if (!$in_installer) {
lock_release(__FUNCTION__);
}
return TRUE;
}
/**
* @} End of "ingroup registry".
* @} End of "addtogroup registry".
*/
/**
* Central static variable storage.
* Provides central static variable storage.
*
* All functions requiring a static variable to persist or cache data within
* a single page request are encouraged to use this function unless it is
......@@ -2798,8 +3613,8 @@ function registry_update() {
* However, the above line of code does not work, because PHP only allows static
* variables to be initializied by literal values, and does not allow static
* variables to be assigned to references.
* - http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static
* - http://php.net/manual/en/language.variables.scope.php#language.variables.scope.references
* - http://php.net/manual/language.variables.scope.php#language.variables.scope.static
* - http://php.net/manual/language.variables.scope.php#language.variables.scope.references
* The example below shows the syntax needed to work around both limitations.
* For benchmarks and more information, see http://drupal.org/node/619666.
*
......@@ -2824,11 +3639,9 @@ function registry_update() {
* @param $default_value
* Optional default value.
* @param $reset
* TRUE to reset a specific named variable, or all variables if $name is NULL.
* Resetting every variable should only be used, for example, for running
* unit tests with a clean environment. Should be used only though via
* function drupal_static_reset() and the return value should not be used in
* this case.
* TRUE to reset one or all variables(s). This parameter is only used
* internally and should not be passed in; use drupal_static_reset() instead.
* (This function's return value should not be used when TRUE is passed in.)
*
* @return
* Returns a variable by reference.
......@@ -2869,17 +3682,19 @@ function &drupal_static($name, $default_value = NULL, $reset = FALSE) {
}
/**
* Reset one or all centrally stored static variable(s).
* Resets one or all centrally stored static variable(s).
*
* @param $name
* Name of the static variable to reset. Omit to reset all variables.
* Resetting all variables should only be used, for example, for running unit
* tests with a clean environment.
*/
function drupal_static_reset($name = NULL) {
drupal_static($name, NULL, TRUE);
}
/**
* Detect whether the current script is running in a command-line environment.
* Detects whether the current script is running in a command-line environment.
*/
function drupal_is_cli() {
return (!isset($_SERVER['SERVER_SOFTWARE']) && (php_sapi_name() == 'cli' || (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0)));
......@@ -2887,7 +3702,8 @@ function drupal_is_cli() {
/**
* Formats text for emphasized display in a placeholder inside a sentence.
* Used automatically by t().
*
* Used automatically by format_string().
*
* @param $text
* The text to format (plain-text).
......@@ -2900,7 +3716,7 @@ function drupal_placeholder($text) {
}
/**
* Register a function for execution on shutdown.
* Registers a function for execution on shutdown.
*
* Wrapper for register_shutdown_function() that catches thrown exceptions to
* avoid "Exception thrown without a stack frame in Unknown".
......@@ -2935,7 +3751,7 @@ function &drupal_register_shutdown_function($callback = NULL) {
}
/**
* Internal function used to execute registered shutdown functions.
* Executes registered shutdown functions.
*/
function _drupal_shutdown_function() {
$callbacks = &drupal_register_shutdown_function();
......@@ -2958,3 +3774,63 @@ function _drupal_shutdown_function() {
}
}
}
/**
* Compares the memory required for an operation to the available memory.
*
* @param $required
* The memory required for the operation, expressed as a number of bytes with
* optional SI or IEC binary unit prefix (e.g. 2, 3K, 5MB, 10G, 6GiB, 8bytes,
* 9mbytes).
* @param $memory_limit
* (optional) The memory limit for the operation, expressed as a number of
* bytes with optional SI or IEC binary unit prefix (e.g. 2, 3K, 5MB, 10G,
* 6GiB, 8bytes, 9mbytes). If no value is passed, the current PHP
* memory_limit will be used. Defaults to NULL.
*
* @return
* TRUE if there is sufficient memory to allow the operation, or FALSE
* otherwise.
*/
function drupal_check_memory_limit($required, $memory_limit = NULL) {
if (!isset($memory_limit)) {
$memory_limit = ini_get('memory_limit');
}
// There is sufficient memory if:
// - No memory limit is set.
// - The memory limit is set to unlimited (-1).
// - The memory limit is greater than the memory required for the operation.
return ((!$memory_limit) || ($memory_limit == -1) || (parse_size($memory_limit) >= parse_size($required)));
}
/**
* Invalidates a PHP file from any active opcode caches.
*
* If the opcode cache does not support the invalidation of individual files,
* the entire cache will be flushed.
*
* @param string $filepath
* The absolute path of the PHP file to invalidate.
*/
function drupal_clear_opcode_cache($filepath) {
if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 50300) {
// Below PHP 5.3, clearstatcache does not accept any function parameters.
clearstatcache();
}
else {
clearstatcache(TRUE, $filepath);
}
// Zend OPcache.
if (function_exists('opcache_invalidate')) {
opcache_invalidate($filepath, TRUE);
}
// APC.
if (function_exists('apc_delete_file')) {
// apc_delete_file() throws a PHP warning in case the specified file was
// not compiled yet.
// @see http://php.net/apc-delete-file
@apc_delete_file($filepath);
}
}
......@@ -6,7 +6,7 @@
*/
/**
* A stub cache implementation to be used during the installation process.
* Defines a stub cache implementation to be used during installation.
*
* The stub implementation is needed when database access is not yet available.
* Because Drupal's caching system never requires that cached data be present,
......@@ -15,17 +15,30 @@
* normal operations would have a negative impact on performance.
*/
class DrupalFakeCache extends DrupalDatabaseCache implements DrupalCacheInterface {
/**
* Overrides DrupalDatabaseCache::get().
*/
function get($cid) {
return FALSE;
}
/**
* Overrides DrupalDatabaseCache::getMultiple().
*/
function getMultiple(&$cids) {
return array();
}
/**
* Overrides DrupalDatabaseCache::set().
*/
function set($cid, $data, $expire = CACHE_PERMANENT) {
}
/**
* Overrides DrupalDatabaseCache::clear().
*/
function clear($cid = NULL, $wildcard = FALSE) {
// If there is a database cache, attempt to clear it whenever possible. The
// reason for doing this is that the database cache can accumulate data
......@@ -52,6 +65,9 @@ function clear($cid = NULL, $wildcard = FALSE) {
}
}
/**
* Overrides DrupalDatabaseCache::isEmpty().
*/
function isEmpty() {
return TRUE;
}
......
<?php
/**
* Get the cache object for a cache bin.
* @file
* Functions and interfaces for cache handling.
*/
/**
* Gets the cache object for a cache bin.
*
* By default, this returns an instance of the DrupalDatabaseCache class.
* Classes implementing DrupalCacheInterface can register themselves both as a
* default implementation and for specific bins.
*
* @see DrupalCacheInterface
*
* @param $bin
* The cache bin for which the cache object should be returned.
*
* @return DrupalCacheInterface
* The cache object associated with the specified bin.
*
* @see DrupalCacheInterface
*/
function _cache_get_object($bin) {
// We do not use drupal_static() here because we do not want to change the
......@@ -29,7 +35,7 @@ function _cache_get_object($bin) {
}
/**
* Return data from the persistent cache
* Returns data from the persistent cache.
*
* Data may be stored as either plain text or as serialized data. cache_get
* will automatically return unserialized objects and arrays.
......@@ -44,19 +50,22 @@ function _cache_get_object($bin) {
*
* @return
* The cache or FALSE on failure.
*
* @see cache_set()
*/
function cache_get($cid, $bin = 'cache') {
return _cache_get_object($bin)->get($cid);
}
/**
* Return data from the persistent cache when given an array of cache IDs.
* Returns data from the persistent cache when given an array of cache IDs.
*
* @param $cids
* An array of cache IDs for the data to retrieve. This is passed by
* reference, and will have the IDs successfully returned from cache removed.
* @param $bin
* The cache bin where the data is stored.
*
* @return
* An array of the items successfully returned from cache indexed by cid.
*/
......@@ -65,50 +74,22 @@ function cache_get_multiple(array &$cids, $bin = 'cache') {
}
/**
* Store data in the persistent cache.
* Stores data in the persistent cache.
*
* The persistent cache is split up into several cache bins. In the default
* cache implementation, each cache bin corresponds to a database table by the
* same name. Other implementations might want to store several bins in data
* structures that get flushed together. While it is not a problem for most
* cache bins if the entries in them are flushed before their expire time, some
* might break functionality or are extremely expensive to recalculate. These
* will be marked with a (*). The other bins expired automatically by core.
* Contributed modules can add additional bins and get them expired
* automatically by implementing hook_flush_caches().
*
* - cache: Generic cache storage bin (used for variables, theme registry,
* locale date, list of simpletest tests etc).
*
* - cache_block: Stores the content of various blocks.
*
* - cache field: Stores the field data belonging to a given object.
*
* - cache_filter: Stores filtered pieces of content.
*
* - cache_form(*): Stores multistep forms. Flushing this bin means that some
* forms displayed to users lose their state and the data already submitted
* to them.
*
* - cache_menu: Stores the structure of visible navigation menus per page.
*
* - cache_page: Stores generated pages for anonymous users. It is flushed
* very often, whenever a page changes, at least for every ode and comment
* submission. This is the only bin affected by the page cache setting on
* the administrator panel.
*
* - cache path: Stores the system paths that have an alias.
*
* - cache update(*): Stores available releases. The update server (for
* example, drupal.org) needs to produce the relevant XML for every project
* installed on the current site. As this is different for (almost) every
* site, it's very expensive to recalculate for the update server.
* might break functionality or are extremely expensive to recalculate. The
* other bins are expired automatically by core. Contributed modules can add
* additional bins and get them expired automatically by implementing
* hook_flush_caches().
*
* The reasons for having several bins are as follows:
*
* - smaller bins mean smaller database tables and allow for faster selects and
* inserts
* - we try to put fast changing cache items and rather static ones into
* - Smaller bins mean smaller database tables and allow for faster selects and
* inserts.
* - We try to put fast changing cache items and rather static ones into
* different bins. The effect is that only the fast changing bins will need a
* lot of writes to disk. The more static bins will also be better cacheable
* with MySQL's query cache.
......@@ -117,44 +98,62 @@ function cache_get_multiple(array &$cids, $bin = 'cache') {
* The cache ID of the data to store.
* @param $data
* The data to store in the cache. Complex data types will be automatically
* serialized before insertion.
* Strings will be stored as plain text and not serialized.
* serialized before insertion. Strings will be stored as plain text and are
* not serialized. Some storage engines only allow objects up to a maximum of
* 1MB in size to be stored by default. When caching large arrays or similar,
* take care to ensure $data does not exceed this size.
* @param $bin
* The cache bin to store the data in. Valid core values are 'cache_block',
* 'cache_bootstrap', 'cache_field', 'cache_filter', 'cache_form',
* 'cache_menu', 'cache_page', 'cache_update' or 'cache' for the default
* cache.
* (optional) The cache bin to store the data in. Valid core values are:
* - cache: (default) Generic cache storage bin (used for theme registry,
* locale date, list of simpletest tests, etc.).
* - cache_block: Stores the content of various blocks.
* - cache_bootstrap: Stores the class registry, the system list of modules,
* the list of which modules implement which hooks, and the Drupal variable
* list.
* - cache_field: Stores the field data belonging to a given object.
* - cache_filter: Stores filtered pieces of content.
* - cache_form: Stores multistep forms. Flushing this bin means that some
* forms displayed to users lose their state and the data already submitted
* to them. This bin should not be flushed before its expired time.
* - cache_menu: Stores the structure of visible navigation menus per page.
* - cache_page: Stores generated pages for anonymous users. It is flushed
* very often, whenever a page changes, at least for every node and comment
* submission. This is the only bin affected by the page cache setting on
* the administrator panel.
* - cache_path: Stores the system paths that have an alias.
* @param $expire
* One of the following values:
* (optional) One of the following values:
* - CACHE_PERMANENT: Indicates that the item should never be removed unless
* explicitly told to using cache_clear_all() with a cache ID.
* - CACHE_TEMPORARY: Indicates that the item should be removed at the next
* general cache wipe.
* - A Unix timestamp: Indicates that the item should be kept at least until
* the given time, after which it behaves like CACHE_TEMPORARY.
*
* @see _update_cache_set()
* @see cache_get()
*/
function cache_set($cid, $data, $bin = 'cache', $expire = CACHE_PERMANENT) {
return _cache_get_object($bin)->set($cid, $data, $expire);
}
/**
* Expire data from the cache.
* Expires data from the cache.
*
* If called without arguments, expirable entries will be cleared from the
* cache_page and cache_block bins.
* If called with the arguments $cid and $bin set to NULL or omitted, then
* expirable entries will be cleared from the cache_page and cache_block bins,
* and the $wildcard argument is ignored.
*
* @param $cid
* If set, the cache ID to delete. Otherwise, all cache entries that can
* expire are deleted.
*
* If set, the cache ID or an array of cache IDs. Otherwise, all cache entries
* that can expire are deleted. The $wildcard argument will be ignored if set
* to NULL.
* @param $bin
* If set, the bin $bin to delete from. Mandatory
* argument if $cid is set.
*
* If set, the cache bin to delete from. Mandatory argument if $cid is set.
* @param $wildcard
* If $wildcard is TRUE, cache IDs starting with $cid are deleted in
* addition to the exact cache ID specified by $cid. If $wildcard is
* TRUE and $cid is '*' then the entire bin $bin is emptied.
* If TRUE, the $cid argument must contain a string value and cache IDs
* starting with $cid are deleted in addition to the exact cache ID specified
* by $cid. If $wildcard is TRUE and $cid is '*', the entire cache is emptied.
*/
function cache_clear_all($cid = NULL, $bin = NULL, $wildcard = FALSE) {
if (!isset($cid) && !isset($bin)) {
......@@ -170,13 +169,14 @@ function cache_clear_all($cid = NULL, $bin = NULL, $wildcard = FALSE) {
}
/**
* Check if a cache bin is empty.
* Checks if a cache bin is empty.
*
* A cache bin is considered empty if it does not contain any valid data for any
* cache ID.
*
* @param $bin
* The cache bin to check.
*
* @return
* TRUE if the cache bin specified is empty.
*/
......@@ -185,7 +185,7 @@ function cache_is_empty($bin) {
}
/**
* Interface for cache implementations.
* Defines an interface for cache implementations.
*
* All cache implementations have to implement this interface.
* DrupalDatabaseCache provides the default implementation, which can be
......@@ -222,49 +222,47 @@ function cache_is_empty($bin) {
* @see DrupalDatabaseCache
*/
interface DrupalCacheInterface {
/**
* Constructor.
*
* @param $bin
* The cache bin for which the object is created.
*/
function __construct($bin);
/**
* Return data from the persistent cache. Data may be stored as either plain
* text or as serialized data. cache_get will automatically return
* unserialized objects and arrays.
* Returns data from the persistent cache.
*
* Data may be stored as either plain text or as serialized data. cache_get()
* will automatically return unserialized objects and arrays.
*
* @param $cid
* The cache ID of the data to retrieve.
*
* @return
* The cache or FALSE on failure.
*/
function get($cid);
/**
* Return data from the persistent cache when given an array of cache IDs.
* Returns data from the persistent cache when given an array of cache IDs.
*
* @param $cids
* An array of cache IDs for the data to retrieve. This is passed by
* reference, and will have the IDs successfully returned from cache
* removed.
*
* @return
* An array of the items successfully returned from cache indexed by cid.
*/
function getMultiple(&$cids);
/**
* Store data in the persistent cache.
* Stores data in the persistent cache.
*
* @param $cid
* The cache ID of the data to store.
* @param $data
* The data to store in the cache. Complex data types will be automatically
* serialized before insertion.
* Strings will be stored as plain text and not serialized.
* serialized before insertion. Strings will be stored as plain text and not
* serialized. Some storage engines only allow objects up to a maximum of
* 1MB in size to be stored by default. When caching large arrays or
* similar, take care to ensure $data does not exceed this size.
* @param $expire
* One of the following values:
* (optional) One of the following values:
* - CACHE_PERMANENT: Indicates that the item should never be removed unless
* explicitly told to using cache_clear_all() with a cache ID.
* - CACHE_TEMPORARY: Indicates that the item should be removed at the next
......@@ -276,21 +274,25 @@ function set($cid, $data, $expire = CACHE_PERMANENT);
/**
* Expire data from the cache. If called without arguments, expirable
* entries will be cleared from the cache_page and cache_block bins.
* Expires data from the cache.
*
* If called without arguments, expirable entries will be cleared from the
* cache_page and cache_block bins.
*
* @param $cid
* If set, the cache ID to delete. Otherwise, all cache entries that can
* expire are deleted.
* If set, the cache ID or an array of cache IDs. Otherwise, all cache
* entries that can expire are deleted. The $wildcard argument will be
* ignored if set to NULL.
* @param $wildcard
* If set to TRUE, the $cid is treated as a substring
* to match rather than a complete ID. The match is a right hand
* match. If '*' is given as $cid, the bin $bin will be emptied.
* If TRUE, the $cid argument must contain a string value and cache IDs
* starting with $cid are deleted in addition to the exact cache ID
* specified by $cid. If $wildcard is TRUE and $cid is '*', the entire
* cache is emptied.
*/
function clear($cid = NULL, $wildcard = FALSE);
/**
* Check if a cache bin is empty.
* Checks if a cache bin is empty.
*
* A cache bin is considered empty if it does not contain any valid data for
* any cache ID.
......@@ -302,7 +304,7 @@ function isEmpty();
}
/**
* Default cache implementation.
* Defines a default cache implementation.
*
* This is Drupal's default cache implementation. It uses the database to store
* cached data. Each cache bin corresponds to a database table by the same name.
......@@ -310,16 +312,28 @@ function isEmpty();
class DrupalDatabaseCache implements DrupalCacheInterface {
protected $bin;
/**
* Constructs a DrupalDatabaseCache object.
*
* @param $bin
* The cache bin for which the object is created.
*/
function __construct($bin) {
$this->bin = $bin;
}
/**
* Implements DrupalCacheInterface::get().
*/
function get($cid) {
$cids = array($cid);
$cache = $this->getMultiple($cids);
return reset($cache);
}
/**
* Implements DrupalCacheInterface::getMultiple().
*/
function getMultiple(&$cids) {
try {
// Garbage collection necessary when enforcing a minimum cache lifetime.
......@@ -357,11 +371,31 @@ function getMultiple(&$cids) {
* The bin being requested.
*/
protected function garbageCollection() {
global $user;
$cache_lifetime = variable_get('cache_lifetime', 0);
// Clean-up the per-user cache expiration session data, so that the session
// handler can properly clean-up the session data for anonymous users.
if (isset($_SESSION['cache_expiration'])) {
$expire = REQUEST_TIME - $cache_lifetime;
foreach ($_SESSION['cache_expiration'] as $bin => $timestamp) {
if ($timestamp < $expire) {
unset($_SESSION['cache_expiration'][$bin]);
}
}
if (!$_SESSION['cache_expiration']) {
unset($_SESSION['cache_expiration']);
}
}
// Garbage collection necessary when enforcing a minimum cache lifetime.
// Garbage collection of temporary items is only necessary when enforcing
// a minimum cache lifetime.
if (!$cache_lifetime) {
return;
}
// When cache lifetime is in force, avoid running garbage collection too
// often since this will remove temporary cache items indiscriminately.
$cache_flush = variable_get('cache_flush_' . $this->bin, 0);
if ($cache_flush && ($cache_flush + variable_get('cache_lifetime', 0) <= REQUEST_TIME)) {
if ($cache_flush && ($cache_flush + $cache_lifetime <= REQUEST_TIME)) {
// Reset the variable immediately to prevent a meltdown in heavy load situations.
variable_set('cache_flush_' . $this->bin, 0);
// Time to flush old cache data
......@@ -373,13 +407,14 @@ protected function garbageCollection() {
}
/**
* Prepare a cached item.
* Prepares a cached item.
*
* Checks that items are either permanent or did not expire, and unserializes
* data as appropriate.
*
* @param $cache
* An item loaded from cache_get() or cache_get_multiple().
*
* @return
* The item with data unserialized as appropriate or FALSE if there is no
* valid item to load.
......@@ -390,17 +425,16 @@ protected function prepareItem($cache) {
if (!isset($cache->data)) {
return FALSE;
}
// If enforcing a minimum cache lifetime, validate that the data is
// currently valid for this user before we return it by making sure the cache
// entry was created before the timestamp in the current session's cache
// timer. The cache variable is loaded into the $user object by _drupal_session_read()
// in session.inc. If the data is permanent or we're not enforcing a minimum
// cache lifetime always return the cached data.
if ($cache->expire != CACHE_PERMANENT && variable_get('cache_lifetime', 0) && $user->cache > $cache->created) {
// This cache data is too old and thus not valid for us, ignore it.
// If the cached data is temporary and subject to a per-user minimum
// lifetime, compare the cache entry timestamp with the user session
// cache_expiration timestamp. If the cache entry is too old, ignore it.
if ($cache->expire != CACHE_PERMANENT && variable_get('cache_lifetime', 0) && isset($_SESSION['cache_expiration'][$this->bin]) && $_SESSION['cache_expiration'][$this->bin] > $cache->created) {
// Ignore cache data that is too old and thus not valid for this user.
return FALSE;
}
// If the data is permanent or not subject to a minimum cache lifetime,
// unserialize and return the cached data.
if ($cache->serialized) {
$cache->data = unserialize($cache->data);
}
......@@ -408,6 +442,9 @@ protected function prepareItem($cache) {
return $cache;
}
/**
* Implements DrupalCacheInterface::set().
*/
function set($cid, $data, $expire = CACHE_PERMANENT) {
$fields = array(
'serialized' => 0,
......@@ -434,16 +471,18 @@ function set($cid, $data, $expire = CACHE_PERMANENT) {
}
}
/**
* Implements DrupalCacheInterface::clear().
*/
function clear($cid = NULL, $wildcard = FALSE) {
global $user;
if (empty($cid)) {
if (variable_get('cache_lifetime', 0)) {
// We store the time in the current user's $user->cache variable which
// will be saved into the sessions bin by _drupal_session_write(). We then
// simulate that the cache was flushed for this user by not returning
// cached data that was cached before the timestamp.
$user->cache = REQUEST_TIME;
// We store the time in the current user's session. We then simulate
// that the cache was flushed for this user by not returning cached
// data that was cached before the timestamp.
$_SESSION['cache_expiration'][$this->bin] = REQUEST_TIME;
$cache_flush = variable_get('cache_flush_' . $this->bin, 0);
if ($cache_flush == 0) {
......@@ -471,7 +510,16 @@ function clear($cid = NULL, $wildcard = FALSE) {
else {
if ($wildcard) {
if ($cid == '*') {
db_truncate($this->bin)->execute();
// Check if $this->bin is a cache table before truncating. Other
// cache_clear_all() operations throw a PDO error in this situation,
// so we don't need to verify them first. This ensures that non-cache
// tables cannot be truncated accidentally.
if ($this->isValidBin()) {
db_truncate($this->bin)->execute();
}
else {
throw new Exception(t('Invalid or missing cache bin specified: %bin', array('%bin' => $this->bin)));
}
}
else {
db_delete($this->bin)
......@@ -496,6 +544,9 @@ function clear($cid = NULL, $wildcard = FALSE) {
}
}
/**
* Implements DrupalCacheInterface::isEmpty().
*/
function isEmpty() {
$this->garbageCollection();
$query = db_select($this->bin);
......@@ -505,4 +556,25 @@ function isEmpty() {
->fetchField();
return empty($result);
}
/**
* Checks if $this->bin represents a valid cache table.
*
* This check is required to ensure that non-cache tables are not truncated
* accidentally when calling cache_clear_all().
*
* @return boolean
*/
function isValidBin() {
if ($this->bin == 'cache' || substr($this->bin, 0, 6) == 'cache_') {
// Skip schema check for bins with standard table names.
return TRUE;
}
// These fields are required for any cache table.
$fields = array('cid', 'data', 'expire', 'created', 'serialized');
// Load the table schema.
$schema = drupal_get_schema($this->bin);
// Confirm that all fields are present.
return isset($schema['fields']) && !array_diff($fields, array_keys($schema['fields']));
}
}