Skip to content
Commits on Source (1633)
# 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
......@@ -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>
......@@ -16,17 +16,11 @@ Options +FollowSymLinks
# 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.
......@@ -62,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
......@@ -84,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.
......@@ -135,3 +141,9 @@ DirectoryIndex index.php index.html index.htm
</FilesMatch>
</IfModule>
</IfModule>
# 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.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
......@@ -115,8 +1007,8 @@ Drupal 7.0, 2011-01-05
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
......@@ -300,7 +1192,7 @@ Drupal 7.0, 2011-01-05
requests.
Drupal 6.23-dev, xxxx-xx-xx (development release)
-----------------------
---------------------------
Drupal 6.22, 2011-05-25
-----------------------
......@@ -310,25 +1202,25 @@ Drupal 6.22, 2011-05-25
- 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
......@@ -337,24 +1229,24 @@ Drupal 6.17, 2010-06-02
- 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 install profiles and
- 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
......@@ -363,18 +1255,18 @@ Drupal 6.14, 2009-09-16
- 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
......@@ -382,7 +1274,7 @@ Drupal 6.11, 2009-04-29
- 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.
......@@ -461,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
......@@ -516,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.
......@@ -695,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
......@@ -755,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:
......@@ -778,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
......@@ -891,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
......@@ -1059,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
......@@ -21,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>
- Nathaniel Catchpole 'catch' <http://drupal.org/user/35733>
- 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' <http://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
- Károly Négyesi 'chx' <http://drupal.org/user/9446>
- 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
......@@ -151,144 +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' <http://drupal.org/user/395439>
- 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
- Vojtech Kusy 'wojtha' <http://drupal.org/user/56154>
- 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>
- Kristof De Jaeger 'swentel' <http://drupal.org/user/107403>
- 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
----------
......
......@@ -64,6 +64,9 @@ following the instructions in the INTRODUCTION section at the top of this file:
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
......@@ -74,6 +77,13 @@ following the instructions in the INTRODUCTION section at the top of this file:
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
into your Drupal directory.
......@@ -141,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. Log in as user ID 1 (the site maintenance user).
4. Go to Administer > Site configuration > Site maintenance. Select
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.
......@@ -158,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.
......@@ -191,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.
......@@ -214,17 +228,17 @@ 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.
......
......@@ -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,16 +21,17 @@
*/
/**
* 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');
......@@ -51,7 +52,7 @@ function authorize_access_denied_page() {
* 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);
......
......@@ -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.
*/
......@@ -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.
*
......@@ -167,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'
......@@ -188,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:
......@@ -210,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);
......@@ -222,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
......@@ -250,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]);
......@@ -262,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);
......@@ -293,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.
......@@ -308,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() {
......@@ -337,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;
......@@ -351,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);
}
/**
......@@ -368,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
......@@ -382,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;
}
}
......@@ -403,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'])) {
......@@ -421,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:
......@@ -464,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);
......@@ -554,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.
......@@ -577,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.
......@@ -596,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
......@@ -741,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.
......@@ -837,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(
......@@ -1211,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
......@@ -165,8 +172,6 @@ function _authorize_filetransfer_connection_settings($backend) {
* 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,9 +191,10 @@ 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
......@@ -218,9 +224,10 @@ 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;
......@@ -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
......@@ -370,7 +370,7 @@ 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.
......@@ -379,11 +379,14 @@ function _batch_process() {
* 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) {
......@@ -410,7 +413,7 @@ function _batch_api_percentage($total, $current) {
}
/**
* Return the batch set being currently processed.
* Returns the batch set being currently processed.
*/
function &_batch_current_set() {
$batch = &batch_get();
......@@ -418,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
......@@ -442,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.
......@@ -457,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));
}
}
}
......@@ -521,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()) {
......@@ -531,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.8');
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.
*
......@@ -43,9 +58,9 @@
* 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. PHP supplies predefined LOG_* constants
* 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
* 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."
*
......@@ -68,32 +83,32 @@
define('WATCHDOG_ALERT', 1);
/**
* Log message severity -- Critical: critical conditions.
* Log message severity -- Critical conditions.
*/
define('WATCHDOG_CRITICAL', 2);
/**
* Log message severity -- Error: error conditions.
* Log message severity -- Error conditions.
*/
define('WATCHDOG_ERROR', 3);
/**
* Log message severity -- Warning: warning conditions.
* Log message severity -- Warning conditions.
*/
define('WATCHDOG_WARNING', 4);
/**
* Log message severity -- Notice: normal but significant condition.
* Log message severity -- Normal but significant conditions.
*/
define('WATCHDOG_NOTICE', 5);
/**
* Log message severity -- Informational: informational messages.
* Log message severity -- Informational messages.
*/
define('WATCHDOG_INFO', 6);
/**
* Log message severity -- Debug: debug-level messages.
* Log message severity -- Debug-level messages.
*/
define('WATCHDOG_DEBUG', 7);
......@@ -137,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);
......@@ -153,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);
......@@ -191,20 +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.
*
* REQUEST_TIME is a float with microseconds since PHP 5.4.0, but float
* timestamps confuses most of the PHP functions (including date_create()).
* 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', (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);
......@@ -221,10 +244,19 @@
/**
* 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]*');
/**
* 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.
*
......@@ -255,7 +287,7 @@
* 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/en/arrayaccess.offsetget.php on
* http://php.net/manual/arrayaccess.offsetget.php on
* ArrayAccess::offsetGet().
*
* By default, the class accounts for caches where calling functions might
......@@ -275,7 +307,7 @@
* 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 behaviour, for
* procedural code. Extending classes may wish to alter this behavior, for
* example by overriding offsetSet() and adding an automatic call to persist().
*
* @see SchemaCache
......@@ -285,12 +317,12 @@ abstract class DrupalCacheArray implements ArrayAccess {
/**
* A cid to pass to cache_set() and cache_get().
*/
private $cid;
protected $cid;
/**
* A bin to pass to cache_set() and cache_get().
*/
private $bin;
protected $bin;
/**
* An array of keys to add to the cache at the end of the request.
......@@ -303,7 +335,7 @@ abstract class DrupalCacheArray implements ArrayAccess {
protected $storage = array();
/**
* Constructor.
* Constructs a DrupalCacheArray object.
*
* @param $cid
* The cid for the array being cached.
......@@ -319,10 +351,16 @@ public function __construct($cid, $bin) {
}
}
/**
* 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];
......@@ -332,10 +370,16 @@ public function offsetGet($offset) {
}
}
/**
* Implements ArrayAccess::offsetSet().
*/
public function offsetSet($offset, $value) {
$this->storage[$offset] = $value;
}
/**
* Implements ArrayAccess::offsetUnset().
*/
public function offsetUnset($offset) {
unset($this->storage[$offset]);
}
......@@ -349,11 +393,11 @@ public function offsetUnset($offset) {
* without necessarily writing back to the persistent cache at the end.
*
* @param $offset
* The array offset that was request.
* 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 written at the end of the request.
* 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;
......@@ -375,32 +419,31 @@ protected function persist($offset, $persist = TRUE) {
abstract protected function resolveCacheMiss($offset);
/**
* Immediately write a value to the persistent cache.
* Writes a value to the persistent cache immediately.
*
* @param $cid
* The cache ID.
* @param $bin
* The cache bin.
* @param $data
* The data to write to the persistent cache.
* @param $lock
* Whether to acquire a lock before writing to cache.
*/
protected function set($cid, $data, $bin, $lock = TRUE) {
protected function set($data, $lock = TRUE) {
// Lock cache writes to help avoid stampedes.
// To implement locking for cache misses, override __construct().
$lock_name = $cid . ':' . $bin;
$lock_name = $this->cid . ':' . $this->bin;
if (!$lock || lock_acquire($lock_name)) {
if ($cached = cache_get($cid, $bin)) {
if ($cached = cache_get($this->cid, $this->bin)) {
$data = $cached->data + $data;
}
cache_set($cid, $data, $bin);
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) {
......@@ -409,16 +452,18 @@ public function __destruct() {
}
}
if (!empty($data)) {
$this->set($this->cid, $data, $this->bin);
$this->set($data);
}
}
}
/**
* Start the timer with the specified name. If you start and stop the same
* timer multiple times, the measured intervals will be accumulated.
* Starts the timer with the specified name.
*
* If you start and stop the same timer multiple times, the measured intervals
* will be accumulated.
*
* @param name
* @param $name
* The name of the timer.
*/
function timer_start($name) {
......@@ -429,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.
*/
......@@ -452,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).
......@@ -479,56 +526,11 @@ function timer_stop($name) {
}
/**
* Finds the appropriate configuration directory.
*
* Finds 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 and the remaining ones will be ignored.
* If no configuration file is found, return a default value '$confdir/default'.
*
* With a site located at http://www.example.com:8080/mysite/test/, the file,
* settings.php, is searched for in the following directories:
* Returns the appropriate configuration directory.
*
* - $confdir/8080.www.example.com.mysite.test
* - $confdir/www.example.com.mysite.test
* - $confdir/example.com.mysite.test
* - $confdir/com.mysite.test
*
* - $confdir/8080.www.example.com.mysite
* - $confdir/www.example.com.mysite
* - $confdir/example.com.mysite
* - $confdir/com.mysite
*
* - $confdir/8080.www.example.com
* - $confdir/www.example.com
* - $confdir/example.com
* - $confdir/com
*
* - $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:
* @code
* $sites = array(
* 'The url to alias' => 'A directory within the sites directory'
* );
* @endcode
* For example:
* @code
* $sites = array(
* 'devexample.com' => 'example.com',
* 'localhost.example' => 'example.com',
* );
* @endcode
* 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.
* 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.
*
* @param bool $require_settings
* Only configuration directories with an existing settings.php file
......@@ -541,6 +543,8 @@ function timer_stop($name) {
*
* @return
* The path of the matching directory.
*
* @see default.settings.php
*/
function conf_path($require_settings = TRUE, $reset = FALSE) {
$conf = &drupal_static(__FUNCTION__, '');
......@@ -576,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.
......@@ -638,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'])) {
......@@ -687,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');
......@@ -697,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();
......@@ -725,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'] = '';
}
......@@ -734,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'];
......@@ -760,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().
......@@ -794,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
......@@ -811,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);
}
}
......@@ -890,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
......@@ -939,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()
......@@ -1001,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
......@@ -1033,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.
......@@ -1052,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.
......@@ -1074,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).
......@@ -1107,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).
......@@ -1143,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.
......@@ -1164,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).
*/
......@@ -1177,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);
......@@ -1207,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()
*/
......@@ -1249,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
......@@ -1269,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.
......@@ -1319,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
......@@ -1360,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');
......@@ -1400,6 +1653,7 @@ function drupal_unpack($obj, $field = 'data') {
* 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
......@@ -1413,40 +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()) {
......@@ -1480,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;
return format_string($string, $args);
}
}
case '%':
default:
// Escaped and placeholder.
$args[$key] = drupal_placeholder($value);
break;
/**
* 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 '!':
// Pass-through.
}
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
......@@ -1542,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.
*/
......@@ -1583,7 +1890,7 @@ function request_uri() {
}
/**
* Log an exception.
* Logs an exception.
*
* This is a wrapper function for watchdog() which automatically decodes an
* exception.
......@@ -1597,14 +1904,14 @@ function request_uri() {
* 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) {
......@@ -1624,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
......@@ -1640,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.
*
......@@ -1658,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,
......@@ -1666,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
......@@ -1684,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 to be displayed to the user. For consistency with other
* messages, it should begin with a capital letter and end 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();
}
......@@ -1720,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()) {
......@@ -1753,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.
......@@ -1770,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
......@@ -1795,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
......@@ -1804,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.
*/
......@@ -1829,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.
......@@ -1843,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()
......@@ -1884,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);
......@@ -1895,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.
......@@ -1956,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);
}
/**
......@@ -2007,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();
......@@ -2017,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.
*
* 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 $phase
* A constant. Allowed values are the DRUPAL_BOOTSTRAP_* constants.
* @param $new_phase
* @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.
......@@ -2051,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) {
......@@ -2109,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;
......@@ -2124,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.
......@@ -2135,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';
......@@ -2143,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
......@@ -2171,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.
......@@ -2186,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;
......@@ -2242,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
......@@ -2291,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;
......@@ -2308,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');
......@@ -2330,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
......@@ -2343,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;
......@@ -2357,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.
......@@ -2367,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);
......@@ -2403,7 +2858,35 @@ 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';
......@@ -2418,10 +2901,10 @@ function drupal_installation_attempted() {
*
* 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 the
* non-installation time, such as while installing the module from the
* module administration page.
*
* Example useage:
* Example usage:
* @code
* $t = get_t();
* $translated = $t('translate this');
......@@ -2446,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();
......@@ -2471,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
......@@ -2486,7 +2969,10 @@ 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
......@@ -2496,16 +2982,29 @@ function drupal_multilingual() {
}
/**
* 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
* (optional) The field to index the list with.
*
* @param $field 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__);
......@@ -2544,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' => ''));
......@@ -2578,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
......@@ -2614,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 '/'
......@@ -2663,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
......@@ -2697,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);
}
}
}
}
......@@ -2707,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.
......@@ -2748,11 +3262,17 @@ function drupal_get_schema($table = NULL, $rebuild = FALSE) {
*/
class SchemaCache extends DrupalCacheArray {
/**
* Constructs a SchemaCache object.
*/
public function __construct() {
// Cache by request method.
parent::__construct('schema:runtime:' . $_SERVER['REQUEST_METHOD'] == 'GET', 'cache');
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;
......@@ -2763,7 +3283,7 @@ protected function resolveCacheMiss($offset) {
}
/**
* Get the whole database schema.
* Gets the whole database schema.
*
* The returned schema will include any modifications made by any
* module that implements hook_schema_alter().
......@@ -2823,23 +3343,24 @@ function drupal_get_complete_schema($rebuild = FALSE) {
}
/**
* @} 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.
*/
......@@ -2848,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.
*/
......@@ -2863,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
......@@ -2872,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.
......@@ -2879,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;
}
......@@ -2912,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];
}
......@@ -2920,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.
......@@ -2934,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 {
......@@ -2943,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.
......@@ -2954,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
......@@ -3052,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.
*
......@@ -3078,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.
......@@ -3123,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)));
......@@ -3141,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).
......@@ -3154,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".
......@@ -3189,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();
......@@ -3212,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']));
}
}
......@@ -70,8 +70,7 @@
define('CSS_THEME', 100);
/**
* The default group for JavaScript libraries, settings or jQuery plugins added
* to the page.
* The default group for JavaScript and jQuery libraries added to the page.
*/
define('JS_LIBRARY', -100);
......@@ -86,20 +85,27 @@
define('JS_THEME', 100);
/**
* Error code indicating that the request made by drupal_http_request() exceeded
* the specified timeout.
* Error code indicating that the request exceeded the specified timeout.
*
* @see drupal_http_request()
*/
define('HTTP_REQUEST_TIMEOUT', -1);
/**
* Constants defining cache granularity for blocks and renderable arrays.
*
* Modules specify the caching patterns for their blocks using binary
* combinations of these constants in their hook_block_info():
* $block[delta]['cache'] = DRUPAL_CACHE_PER_ROLE | DRUPAL_CACHE_PER_PAGE;
* DRUPAL_CACHE_PER_ROLE is used as a default when no caching pattern is
* specified. Use DRUPAL_CACHE_CUSTOM to disable standard block cache and
* implement
* @defgroup block_caching Block Caching
* @{
* Constants that define each block's caching state.
*
* Modules specify how their blocks can be cached in their hook_block_info()
* implementations. Caching can be turned off (DRUPAL_NO_CACHE), managed by the
* module declaring the block (DRUPAL_CACHE_CUSTOM), or managed by the core
* Block module. If the Block module is managing the cache, you can specify that
* the block is the same for every page and user (DRUPAL_CACHE_GLOBAL), or that
* it can change depending on the page (DRUPAL_CACHE_PER_PAGE) or by user
* (DRUPAL_CACHE_PER_ROLE or DRUPAL_CACHE_PER_USER). Page and user settings can
* be combined with a bitwise-binary or operator; for example,
* DRUPAL_CACHE_PER_ROLE | DRUPAL_CACHE_PER_PAGE means that the block can change
* depending on the user role or page it is on.
*
* The block cache is cleared in cache_clear_all(), and uses the same clearing
* policy than page cache (node, comment, user, taxonomy added or updated...).
......@@ -110,31 +116,35 @@
*/
/**
* The block should not get cached. This setting should be used:
* - for simple blocks (notably those that do not perform any db query),
* where querying the db cache would be more expensive than directly generating
* the content.
* - for blocks that change too frequently.
* The block should not get cached.
*
* This setting should be used:
* - For simple blocks (notably those that do not perform any db query), where
* querying the db cache would be more expensive than directly generating the
* content.
* - For blocks that change too frequently.
*/
define('DRUPAL_NO_CACHE', -1);
/**
* The block is handling its own caching in its hook_block_view(). From the
* perspective of the block cache system, this is equivalent to DRUPAL_NO_CACHE.
* Useful when time based expiration is needed or a site uses a node access
* which invalidates standard block cache.
* The block is handling its own caching in its hook_block_view().
*
* This setting is useful when time based expiration is needed or a site uses a
* node access which invalidates standard block cache.
*/
define('DRUPAL_CACHE_CUSTOM', -2);
/**
* The block or element can change depending on the roles the user viewing the
* page belongs to. This is the default setting for blocks, used when the block
* does not specify anything.
* The block or element can change depending on the user's roles.
*
* This is the default setting for blocks, used when the block does not specify
* anything.
*/
define('DRUPAL_CACHE_PER_ROLE', 0x0001);
/**
* The block or element can change depending on the user viewing the page.
* The block or element can change depending on the user.
*
* This setting can be resource-consuming for sites with large number of users,
* and thus should only be used when DRUPAL_CACHE_PER_ROLE is not sufficient.
*/
......@@ -146,12 +156,16 @@
define('DRUPAL_CACHE_PER_PAGE', 0x0004);
/**
* The block or element is the same for every user on every page where it is visible.
* The block or element is the same for every user and page that it is visible.
*/
define('DRUPAL_CACHE_GLOBAL', 0x0008);
/**
* Add content to a specified region.
* @} End of "defgroup block_caching".
*/
/**
* Adds content to a specified region.
*
* @param $region
* Page region the content is added to.
......@@ -168,7 +182,7 @@ function drupal_add_region_content($region = NULL, $data = NULL) {
}
/**
* Get assigned content for a given region.
* Gets assigned content for a given region.
*
* @param $region
* A specified region to fetch content for. If NULL, all regions will be
......@@ -194,16 +208,16 @@ function drupal_get_region_content($region = NULL, $delimiter = ' ') {
}
/**
* Get the name of the currently active install profile.
* Gets the name of the currently active installation profile.
*
* When this function is called during Drupal's initial installation process,
* the name of the profile that's about to be installed is stored in the global
* installation state. At all other times, the standard Drupal systems variable
* table contains the name of the current profile, and we can call variable_get()
* to determine what one is active.
* table contains the name of the current profile, and we can call
* variable_get() to determine what one is active.
*
* @return $profile
* The name of the install profile.
* The name of the installation profile.
*/
function drupal_get_profile() {
global $install_state;
......@@ -220,7 +234,7 @@ function drupal_get_profile() {
/**
* Set the breadcrumb trail for the current page.
* Sets the breadcrumb trail for the current page.
*
* @param $breadcrumb
* Array of links, starting with "home" and proceeding up to but not including
......@@ -236,7 +250,7 @@ function drupal_set_breadcrumb($breadcrumb = NULL) {
}
/**
* Get the breadcrumb trail for the current page.
* Gets the breadcrumb trail for the current page.
*/
function drupal_get_breadcrumb() {
$breadcrumb = drupal_set_breadcrumb();
......@@ -265,9 +279,9 @@ function drupal_get_rdf_namespaces() {
}
/**
* Add output to the head tag of the HTML page.
* Adds output to the HEAD tag of the HTML page.
*
* This function can be called as long the headers aren't sent. Pass no
* This function can be called as long as the headers aren't sent. Pass no
* arguments (or NULL for both) to retrieve the currently stored elements.
*
* @param $data
......@@ -333,7 +347,7 @@ function _drupal_default_html_head() {
}
/**
* Retrieve output to be displayed in the HEAD tag of the HTML page.
* Retrieves output to be displayed in the HEAD tag of the HTML page.
*/
function drupal_get_html_head() {
$elements = drupal_add_html_head();
......@@ -342,7 +356,7 @@ function drupal_get_html_head() {
}
/**
* Add a feed URL for the current page.
* Adds a feed URL for the current page.
*
* This function can be called as long the HTML header hasn't been sent.
*
......@@ -370,7 +384,7 @@ function drupal_add_feed($url = NULL, $title = '') {
}
/**
* Get the feed URLs for the current page.
* Gets the feed URLs for the current page.
*
* @param $delimiter
* A delimiter to split feeds by.
......@@ -387,7 +401,7 @@ function drupal_get_feeds($delimiter = "\n") {
*/
/**
* Process a URL query parameter array to remove unwanted elements.
* Processes a URL query parameter array to remove unwanted elements.
*
* @param $query
* (optional) An array to be processed. Defaults to $_GET.
......@@ -432,19 +446,19 @@ function drupal_get_query_parameters(array $query = NULL, array $exclude = array
}
/**
* Split an URL-encoded query string into an array.
* Splits a URL-encoded query string into an array.
*
* @param $query
* The query string to split.
*
* @return
* An array of url decoded couples $param_name => $value.
* An array of URL decoded couples $param_name => $value.
*/
function drupal_get_query_array($query) {
$result = array();
if (!empty($query)) {
foreach (explode('&', $query) as $param) {
$param = explode('=', $param);
$param = explode('=', $param, 2);
$result[$param[0]] = isset($param[1]) ? rawurldecode($param[1]) : '';
}
}
......@@ -452,7 +466,7 @@ function drupal_get_query_array($query) {
}
/**
* Parse an array into a valid, rawurlencoded query string.
* Parses an array into a valid, rawurlencoded query string.
*
* This differs from http_build_query() as we need to rawurlencode() (instead of
* urlencode()) all query parameters.
......@@ -493,13 +507,19 @@ function drupal_http_build_query(array $query, $parent = '') {
}
/**
* Prepare a 'destination' URL query parameter for use in combination with drupal_goto().
* Prepares a 'destination' URL query parameter for use with drupal_goto().
*
* Used to direct the user back to the referring page after completing a form.
* By default the current URL is returned. If a destination exists in the
* previous request, that destination is returned. As such, a destination can
* persist across multiple pages.
*
* @return
* An associative array containing the key:
* - destination: The path provided via the destination query string or, if
* not available, the current path.
*
* @see current_path()
* @see drupal_goto()
*/
function drupal_get_destination() {
......@@ -524,37 +544,32 @@ function drupal_get_destination() {
}
/**
* Wrapper around parse_url() to parse a system URL string into an associative array, suitable for url().
*
* This function should only be used for URLs that have been generated by the
* system, resp. url(). It should not be used for URLs that come from external
* sources, or URLs that link to external resources.
* Parses a URL string into its path, query, and fragment components.
*
* The returned array contains a 'path' that may be passed separately to url().
* For example:
* @code
* $options = drupal_parse_url($_GET['destination']);
* $my_url = url($options['path'], $options);
* $my_link = l('Example link', $options['path'], $options);
* @endcode
* This function splits both internal paths like @code node?b=c#d @endcode and
* external URLs like @code https://example.com/a?b=c#d @endcode into their
* component parts. See
* @link http://tools.ietf.org/html/rfc3986#section-3 RFC 3986 @endlink for an
* explanation of what the component parts are.
*
* This is required, because url() does not support relative URLs containing a
* query string or fragment in its $path argument. Instead, any query string
* needs to be parsed into an associative query parameter array in
* $options['query'] and the fragment into $options['fragment'].
* Note that, unlike the RFC, when passed an external URL, this function
* groups the scheme, authority, and path together into the path component.
*
* @param $url
* The URL string to parse, f.e. $_GET['destination'].
* @param string $url
* The internal path or external URL string to parse.
*
* @return
* An associative array containing the keys:
* - 'path': The path of the URL. If the given $url is external, this includes
* the scheme and host.
* - 'query': An array of query parameters of $url, if existent.
* - 'fragment': The fragment of $url, if existent.
* @return array
* An associative array containing:
* - path: The path component of $url. If $url is an external URL, this
* includes the scheme, authority, and path.
* - query: An array of query parameters from $url, if they exist.
* - fragment: The fragment component from $url, if it exists.
*
* @see url()
* @see drupal_goto()
* @see l()
* @see url()
* @see http://tools.ietf.org/html/rfc3986
*
* @ingroup php_wrappers
*/
function drupal_parse_url($url) {
......@@ -621,7 +636,7 @@ function drupal_encode_path($path) {
}
/**
* Send the user to a different Drupal page.
* Sends the user to a different page.
*
* This issues an on-site HTTP redirect. The function makes sure the redirected
* URL is formatted correctly.
......@@ -642,20 +657,23 @@ function drupal_encode_path($path) {
* callback.
*
* @param $path
* A Drupal path or a full URL.
* (optional) A Drupal path or a full URL, which will be passed to url() to
* compute the redirect for the URL.
* @param $options
* An associative array of additional URL options to pass to url().
* (optional) An associative array of additional URL options to pass to url().
* @param $http_response_code
* Valid values for an actual "goto" as per RFC 2616 section 10.3 are:
* - 301 Moved Permanently (the recommended value for most redirects)
* - 302 Found (default in Drupal and PHP, sometimes used for spamming search
* engines)
* - 303 See Other
* - 304 Not Modified
* - 305 Use Proxy
* - 307 Temporary Redirect (alternative to "503 Site Down for Maintenance")
* Note: Other values are defined by RFC 2616, but are rarely used and poorly
* supported.
* (optional) The HTTP status code to use for the redirection, defaults to
* 302. The valid values for 3xx redirection status codes are defined in
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3 RFC 2616 @endlink
* and the
* @link http://tools.ietf.org/html/draft-reschke-http-status-308-07 draft for the new HTTP status codes: @endlink
* - 301: Moved Permanently (the recommended value for most redirects).
* - 302: Found (default in Drupal and PHP, sometimes used for spamming search
* engines).
* - 303: See Other.
* - 304: Not Modified.
* - 305: Use Proxy.
* - 307: Temporary Redirect.
*
* @see drupal_get_destination()
* @see url()
......@@ -670,6 +688,13 @@ function drupal_goto($path = '', array $options = array(), $http_response_code =
$options['fragment'] = $destination['fragment'];
}
// In some cases modules call drupal_goto(current_path()). We need to ensure
// that such a redirect is not to an external URL.
if ($path === current_path() && empty($options['external']) && url_is_external($path)) {
// Force url() to generate a non-external URL.
$options['external'] = FALSE;
}
drupal_alter('drupal_goto', $path, $options, $http_response_code);
// The 'Location' HTTP header must be absolute.
......@@ -686,7 +711,7 @@ function drupal_goto($path = '', array $options = array(), $http_response_code =
}
/**
* Deliver a "site is under maintenance" message to the browser.
* Delivers a "site is under maintenance" message to the browser.
*
* Page callback functions wanting to report a "site offline" message should
* return MENU_SITE_OFFLINE instead of calling drupal_site_offline(). However,
......@@ -698,7 +723,7 @@ function drupal_site_offline() {
}
/**
* Deliver a "page not found" error to the browser.
* Delivers a "page not found" error to the browser.
*
* Page callback functions wanting to report a "page not found" message should
* return MENU_NOT_FOUND instead of calling drupal_not_found(). However,
......@@ -710,19 +735,20 @@ function drupal_not_found() {
}
/**
* Deliver a "access denied" error to the browser.
* Delivers an "access denied" error to the browser.
*
* Page callback functions wanting to report an "access denied" message should
* return MENU_ACCESS_DENIED instead of calling drupal_access_denied(). However,
* functions that are invoked in contexts where that return value might not
* bubble up to menu_execute_active_handler() should call drupal_access_denied().
* bubble up to menu_execute_active_handler() should call
* drupal_access_denied().
*/
function drupal_access_denied() {
drupal_deliver_page(MENU_ACCESS_DENIED);
}
/**
* Perform an HTTP request.
* Performs an HTTP request.
*
* This is a flexible and powerful HTTP client implementation. Correctly
* handles GET, POST, PUT or any other HTTP requests. Handles redirects.
......@@ -734,7 +760,8 @@ function drupal_access_denied() {
* - headers: An array containing request headers to send as name/value pairs.
* - method: A string containing the request method. Defaults to 'GET'.
* - data: A string containing the request body, formatted as
* 'param=value&param=value&...'. Defaults to NULL.
* 'param=value&param=value&...'; to generate this, use http_build_query().
* Defaults to NULL.
* - max_redirects: An integer representing how many times a redirect
* may be followed. Defaults to 3.
* - timeout: A float representing the maximum number of seconds the function
......@@ -759,8 +786,17 @@ function drupal_access_denied() {
* HTTP header names are case-insensitive (RFC 2616, section 4.2), so for
* easy access the array keys are returned in lower case.
* - data: A string containing the response body that was received.
*
* @see http_build_query()
*/
function drupal_http_request($url, array $options = array()) {
// Allow an alternate HTTP client library to replace Drupal's default
// implementation.
$override_function = variable_get('drupal_http_request_function', FALSE);
if (!empty($override_function) && function_exists($override_function)) {
return $override_function($url, $options);
}
$result = new stdClass();
// Parse the URL and make sure we can handle the schema.
......@@ -789,10 +825,51 @@ function drupal_http_request($url, array $options = array()) {
'timeout' => 30.0,
'context' => NULL,
);
// Merge the default headers.
$options['headers'] += array(
'User-Agent' => 'Drupal (+http://drupal.org/)',
);
// stream_socket_client() requires timeout to be a float.
$options['timeout'] = (float) $options['timeout'];
// Use a proxy if one is defined and the host is not on the excluded list.
$proxy_server = variable_get('proxy_server', '');
if ($proxy_server && _drupal_http_use_proxy($uri['host'])) {
// Set the scheme so we open a socket to the proxy server.
$uri['scheme'] = 'proxy';
// Set the path to be the full URL.
$uri['path'] = $url;
// Since the URL is passed as the path, we won't use the parsed query.
unset($uri['query']);
// Add in username and password to Proxy-Authorization header if needed.
if ($proxy_username = variable_get('proxy_username', '')) {
$proxy_password = variable_get('proxy_password', '');
$options['headers']['Proxy-Authorization'] = 'Basic ' . base64_encode($proxy_username . (!empty($proxy_password) ? ":" . $proxy_password : ''));
}
// Some proxies reject requests with any User-Agent headers, while others
// require a specific one.
$proxy_user_agent = variable_get('proxy_user_agent', '');
// The default value matches neither condition.
if ($proxy_user_agent === NULL) {
unset($options['headers']['User-Agent']);
}
elseif ($proxy_user_agent) {
$options['headers']['User-Agent'] = $proxy_user_agent;
}
}
switch ($uri['scheme']) {
case 'proxy':
// Make the socket connection to a proxy server.
$socket = 'tcp://' . $proxy_server . ':' . variable_get('proxy_port', 8080);
// The Host header still needs to match the real request.
$options['headers']['Host'] = $uri['host'];
$options['headers']['Host'] .= isset($uri['port']) && $uri['port'] != 80 ? ':' . $uri['port'] : '';
break;
case 'http':
case 'feed':
$port = isset($uri['port']) ? $uri['port'] : 80;
......@@ -802,12 +879,14 @@ function drupal_http_request($url, array $options = array()) {
// checking the host that do not take into account the port number.
$options['headers']['Host'] = $uri['host'] . ($port != 80 ? ':' . $port : '');
break;
case 'https':
// Note: Only works when PHP is compiled with OpenSSL support.
$port = isset($uri['port']) ? $uri['port'] : 443;
$socket = 'ssl://' . $uri['host'] . ':' . $port;
$options['headers']['Host'] = $uri['host'] . ($port != 443 ? ':' . $port : '');
break;
default:
$result->error = 'invalid schema ' . $uri['scheme'];
$result->code = -1003;
......@@ -832,7 +911,7 @@ function drupal_http_request($url, array $options = array()) {
// Mark that this request failed. This will trigger a check of the web
// server's ability to make outgoing HTTP requests the next time that
// requirements checking is performed.
// See system_requirements()
// See system_requirements().
variable_set('drupal_http_request_fails', TRUE);
return $result;
......@@ -844,11 +923,6 @@ function drupal_http_request($url, array $options = array()) {
$path .= '?' . $uri['query'];
}
// Merge the default headers.
$options['headers'] += array(
'User-Agent' => 'Drupal (+http://drupal.org/)',
);
// Only add Content-Length if we actually have any content or if it is a POST
// or PUT request. Some non-standard servers get confused by Content-Length in
// at least HEAD/GET requests, and Squid always requires Content-Length in
......@@ -860,7 +934,7 @@ function drupal_http_request($url, array $options = array()) {
// If the server URL has a user then attempt to use basic authentication.
if (isset($uri['user'])) {
$options['headers']['Authorization'] = 'Basic ' . base64_encode($uri['user'] . (isset($uri['pass']) ? ':' . $uri['pass'] : ''));
$options['headers']['Authorization'] = 'Basic ' . base64_encode($uri['user'] . (isset($uri['pass']) ? ':' . $uri['pass'] : ':'));
}
// If the database prefix is being used by SimpleTest to run the tests in a copied
......@@ -921,9 +995,10 @@ function drupal_http_request($url, array $options = array()) {
$response = preg_split("/\r\n|\n|\r/", $response);
// Parse the response status line.
list($protocol, $code, $status_message) = explode(' ', trim(array_shift($response)), 3);
$result->protocol = $protocol;
$result->status_message = $status_message;
$response_status_array = _drupal_parse_response_status(trim(array_shift($response)));
$result->protocol = $response_status_array['http_version'];
$result->status_message = $response_status_array['reason_phrase'];
$code = $response_status_array['response_code'];
$result->headers = array();
......@@ -992,6 +1067,12 @@ function drupal_http_request($url, array $options = array()) {
switch ($code) {
case 200: // OK
case 201: // Created
case 202: // Accepted
case 203: // Non-Authoritative Information
case 204: // No Content
case 205: // Reset Content
case 206: // Partial Content
case 304: // Not modified
break;
case 301: // Moved permanently
......@@ -1014,15 +1095,66 @@ function drupal_http_request($url, array $options = array()) {
}
break;
default:
$result->error = $status_message;
$result->error = $result->status_message;
}
return $result;
}
/**
* Splits an HTTP response status line into components.
*
* See the @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html status line definition @endlink
* in RFC 2616.
*
* @param string $respone
* The response status line, for example 'HTTP/1.1 500 Internal Server Error'.
*
* @return array
* Keyed array containing the component parts. If the response is malformed,
* all possible parts will be extracted. 'reason_phrase' could be empty.
* Possible keys:
* - 'http_version'
* - 'response_code'
* - 'reason_phrase'
*/
function _drupal_parse_response_status($response) {
$response_array = explode(' ', trim($response), 3);
// Set up empty values.
$result = array(
'reason_phrase' => '',
);
$result['http_version'] = $response_array[0];
$result['response_code'] = $response_array[1];
if (isset($response_array[2])) {
$result['reason_phrase'] = $response_array[2];
}
return $result;
}
/**
* Helper function for determining hosts excluded from needing a proxy.
*
* @return
* TRUE if a proxy should be used for this host.
*/
function _drupal_http_use_proxy($host) {
$proxy_exceptions = variable_get('proxy_exceptions', array('localhost', '127.0.0.1'));
return !in_array(strtolower($host), $proxy_exceptions, TRUE);
}
/**
* @} End of "HTTP handling".
*/
/**
* Strips slashes from a string or array of strings.
*
* Callback for array_walk() within fix_gpx_magic().
*
* @param $item
* An individual string or array of strings from superglobals.
*/
function _fix_gpc_magic(&$item) {
if (is_array($item)) {
array_walk($item, '_fix_gpc_magic');
......@@ -1033,11 +1165,19 @@ function _fix_gpc_magic(&$item) {
}
/**
* Helper function to strip slashes from $_FILES skipping over the tmp_name keys
* since PHP generates single backslashes for file paths on Windows systems.
* Strips slashes from $_FILES items.
*
* tmp_name does not have backslashes added see
* http://php.net/manual/en/features.file-upload.php#42280
* Callback for array_walk() within fix_gpc_magic().
*
* The tmp_name key is skipped keys since PHP generates single backslashes for
* file paths on Windows systems.
*
* @param $item
* An item from $_FILES.
* @param $key
* The key for the item within $_FILES.
*
* @see http://php.net/manual/features.file-upload.php#42280
*/
function _fix_gpc_magic_files(&$item, $key) {
if ($key != 'tmp_name') {
......@@ -1051,7 +1191,10 @@ function _fix_gpc_magic_files(&$item, $key) {
}
/**
* Fix double-escaping problems caused by "magic quotes" in some PHP installations.
* Fixes double-escaping caused by "magic quotes" in some PHP installations.
*
* @see _fix_gpc_magic()
* @see _fix_gpc_magic_files()
*/
function fix_gpc_magic() {
static $fixed = FALSE;
......@@ -1072,12 +1215,14 @@ function fix_gpc_magic() {
*/
/**
* Verify the syntax of the given e-mail address.
* Verifies the syntax of the given e-mail address.
*
* Empty e-mail addresses are allowed. See RFC 2822 for details.
* This uses the
* @link http://php.net/manual/filter.filters.validate.php PHP e-mail validation filter. @endlink
*
* @param $mail
* A string containing an e-mail address.
*
* @return
* TRUE if the address is in a valid format.
*/
......@@ -1086,7 +1231,7 @@ function valid_email_address($mail) {
}
/**
* Verify the syntax of the given URL.
* Verifies the syntax of the given URL.
*
* This function should only be used on actual URLs. It should not be used for
* Drupal menu paths, which can contain arbitrary characters.
......@@ -1095,6 +1240,7 @@ function valid_email_address($mail) {
* The URL to verify.
* @param $absolute
* Whether the URL is absolute (beginning with a scheme such as "http:").
*
* @return
* TRUE if the URL is in a valid format.
*/
......@@ -1127,7 +1273,7 @@ function valid_url($url, $absolute = FALSE) {
*/
/**
* Register an event for the current visitor to the flood control mechanism.
* Registers an event for the current visitor to the flood control mechanism.
*
* @param $name
* The name of an event.
......@@ -1154,7 +1300,7 @@ function flood_register_event($name, $window = 3600, $identifier = NULL) {
}
/**
* Make the flood control mechanism forget about an event for the current visitor.
* Makes the flood control mechanism forget an event for the current visitor.
*
* @param $name
* The name of an event.
......@@ -1172,7 +1318,7 @@ function flood_clear_event($name, $identifier = NULL) {
}
/**
* Checks whether user is allowed to proceed with the specified event.
* Checks whether a user is allowed to proceed with the specified event.
*
* Events can have thresholds saying that each user can only do that event
* a certain number of times in a time window. This function verifies that the
......@@ -1266,7 +1412,7 @@ function drupal_strip_dangerous_protocols($uri) {
}
/**
* Strips dangerous protocols (e.g. 'javascript:') from a URI and encodes it for output to an HTML attribute value.
* Strips dangerous protocols from a URI and encodes it for output to HTML.
*
* @param $uri
* A plain-text URI that might contain dangerous protocols.
......@@ -1286,7 +1432,7 @@ function check_url($uri) {
}
/**
* Very permissive XSS/HTML filter for admin-only use.
* Applies a very permissive XSS/HTML filter for admin-only use.
*
* Use only for fields where it is impractical to use the
* whole filter system, but where some (mainly inline) mark-up
......@@ -1300,7 +1446,7 @@ function filter_xss_admin($string) {
}
/**
* Filters an HTML string to prevent cross-site-scripting (XSS) vulnerabilities.
* Filters HTML to prevent cross-site-scripting (XSS) vulnerabilities.
*
* Based on kses by Ulf Harnhammar, see http://sourceforge.net/projects/kses.
* For examples of various XSS attacks, see: http://ha.ckers.org/xss.html.
......@@ -1323,7 +1469,6 @@ function filter_xss_admin($string) {
* valid UTF-8.
*
* @see drupal_validate_utf8()
* @ingroup sanitization
*/
function filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite', 'blockquote', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd')) {
// Only operate on valid UTF-8 strings. This is necessary to prevent cross
......@@ -1331,21 +1476,21 @@ function filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite',
if (!drupal_validate_utf8($string)) {
return '';
}
// Store the text format
// Store the text format.
_filter_xss_split($allowed_tags, TRUE);
// Remove NULL characters (ignored by some browsers)
// Remove NULL characters (ignored by some browsers).
$string = str_replace(chr(0), '', $string);
// Remove Netscape 4 JS entities
// Remove Netscape 4 JS entities.
$string = preg_replace('%&\s*\{[^}]*(\}\s*;?|$)%', '', $string);
// Defuse all HTML entities
// Defuse all HTML entities.
$string = str_replace('&', '&amp;', $string);
// Change back only well-formed entities in our whitelist
// Decimal numeric entities
// Change back only well-formed entities in our whitelist:
// Decimal numeric entities.
$string = preg_replace('/&amp;#([0-9]+;)/', '&#\1', $string);
// Hexadecimal numeric entities
// Hexadecimal numeric entities.
$string = preg_replace('/&amp;#[Xx]0*((?:[0-9A-Fa-f]{2})+;)/', '&#x\1', $string);
// Named entities
// Named entities.
$string = preg_replace('/&amp;([A-Za-z][A-Za-z0-9]*;)/', '&\1', $string);
return preg_replace_callback('%
......@@ -1369,6 +1514,7 @@ function filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite',
* If $store is FALSE then the array has one element, the HTML tag to process.
* @param $store
* Whether to store $m.
*
* @return
* If the element isn't allowed, an empty string. Otherwise, the cleaned up
* version of the HTML element.
......@@ -1384,16 +1530,16 @@ function _filter_xss_split($m, $store = FALSE) {
$string = $m[1];
if (substr($string, 0, 1) != '<') {
// We matched a lone ">" character
// We matched a lone ">" character.
return '&gt;';
}
elseif (strlen($string) == 1) {
// We matched a lone "<" character
// We matched a lone "<" character.
return '&lt;';
}
if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?|(<!--.*?-->)$%', $string, $matches)) {
// Seriously malformed
if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9\-]+)([^>]*)>?|(<!--.*?-->)$%', $string, $matches)) {
// Seriously malformed.
return '';
}
......@@ -1407,7 +1553,7 @@ function _filter_xss_split($m, $store = FALSE) {
}
if (!isset($allowed_html[strtolower($elem)])) {
// Disallowed HTML element
// Disallowed HTML element.
return '';
}
......@@ -1423,7 +1569,7 @@ function _filter_xss_split($m, $store = FALSE) {
$attrlist = preg_replace('%(\s?)/\s*$%', '\1', $attrlist, -1, $count);
$xhtml_slash = $count ? ' /' : '';
// Clean up attributes
// Clean up attributes.
$attr2 = implode(' ', _filter_xss_attributes($attrlist));
$attr2 = preg_replace('/[<>]/', '', $attr2);
$attr2 = strlen($attr2) ? ' ' . $attr2 : '';
......@@ -1448,7 +1594,7 @@ function _filter_xss_attributes($attr) {
switch ($mode) {
case 0:
// Attribute name, href for instance
// Attribute name, href for instance.
if (preg_match('/^([-a-zA-Z]+)/', $attr, $match)) {
$attrname = strtolower($match[1]);
$skip = ($attrname == 'style' || substr($attrname, 0, 2) == 'on');
......@@ -1458,7 +1604,7 @@ function _filter_xss_attributes($attr) {
break;
case 1:
// Equals sign or valueless ("selected")
// Equals sign or valueless ("selected").
if (preg_match('/^\s*=\s*/', $attr)) {
$working = 1; $mode = 2;
$attr = preg_replace('/^\s*=\s*/', '', $attr);
......@@ -1475,7 +1621,7 @@ function _filter_xss_attributes($attr) {
break;
case 2:
// Attribute value, a URL after href= for instance
// Attribute value, a URL after href= for instance.
if (preg_match('/^"([^"]*)"(\s+|$)/', $attr, $match)) {
$thisval = filter_xss_bad_protocol($match[1]);
......@@ -1512,7 +1658,7 @@ function _filter_xss_attributes($attr) {
}
if ($working == 0) {
// not well formed, remove and try again
// Not well formed; remove and try again.
$attr = preg_replace('/
^
(
......@@ -1536,15 +1682,16 @@ function _filter_xss_attributes($attr) {
}
/**
* Processes an HTML attribute value and ensures it does not contain an URL with a disallowed protocol (e.g. javascript:).
* Processes an HTML attribute value and strips dangerous protocols from URLs.
*
* @param $string
* The string with the attribute value.
* @param $decode
* (Deprecated) Whether to decode entities in the $string. Set to FALSE if the
* (deprecated) Whether to decode entities in the $string. Set to FALSE if the
* $string is in plain text, TRUE otherwise. Defaults to TRUE. This parameter
* is deprecated and will be removed in Drupal 8. To process a plain-text URI,
* call drupal_strip_dangerous_protocols() or check_url() instead.
*
* @return
* Cleaned up and HTML-escaped version of $string.
*/
......@@ -1598,7 +1745,7 @@ function format_rss_channel($title, $link, $description, $items, $langcode = NUL
}
/**
* Format a single RSS item.
* Formats a single RSS item.
*
* Arbitrary elements may be added using the $args associative array.
*/
......@@ -1614,7 +1761,7 @@ function format_rss_item($title, $link, $description, $args = array()) {
}
/**
* Format XML elements.
* Formats XML elements.
*
* @param $array
* An array where each item represents an element and is either a:
......@@ -1623,9 +1770,15 @@ function format_rss_item($title, $link, $description, $args = array()) {
* - 'key': element name
* - 'value': element contents
* - 'attributes': associative array of element attributes
* - 'encoded': TRUE if 'value' is already encoded
*
* In both cases, 'value' can be a simple string, or it can be another array
* with the same format as $array itself for nesting.
*
* If 'encoded' is TRUE it is up to the caller to ensure that 'value' is either
* entity-encoded or CDATA-escaped. Using this option is not recommended when
* working with untrusted user input, since failing to escape the data
* correctly has security implications.
*/
function format_xml_elements($array) {
$output = '';
......@@ -1638,7 +1791,7 @@ function format_xml_elements($array) {
}
if (isset($value['value']) && $value['value'] != '') {
$output .= '>' . (is_array($value['value']) ? format_xml_elements($value['value']) : check_plain($value['value'])) . '</' . $value['key'] . ">\n";
$output .= '>' . (is_array($value['value']) ? format_xml_elements($value['value']) : (!empty($value['encoded']) ? $value['value'] : check_plain($value['value']))) . '</' . $value['key'] . ">\n";
}
else {
$output .= " />\n";
......@@ -1653,7 +1806,7 @@ function format_xml_elements($array) {
}
/**
* Format a string containing a count of items.
* Formats a string containing a count of items.
*
* This function ensures that the string is pluralized correctly. Since t() is
* called by this function, make sure not to pass already-localized strings to
......@@ -1669,37 +1822,33 @@ function format_xml_elements($array) {
* $output = format_plural($update_count,
* 'Changed the content type of 1 post from %old-type to %new-type.',
* 'Changed the content type of @count posts from %old-type to %new-type.',
* array('%old-type' => $info->old_type, '%new-type' => $info->new_type)));
* array('%old-type' => $info->old_type, '%new-type' => $info->new_type));
* @endcode
*
* @param $count
* The item count to display.
* @param $singular
* The string for the singular case. Please make sure it is clear this is
* singular, to ease translation (e.g. use "1 new comment" instead of "1 new").
* Do not use @count in the singular string.
* The string for the singular case. Make sure it is clear this is singular,
* to ease translation (e.g. use "1 new comment" instead of "1 new"). Do not
* use @count in the singular string.
* @param $plural
* The string for the plural case. Please make sure it is clear this is plural,
* to ease translation. Use @count in place of the item count, as in "@count
* new comments".
* The string for the plural case. Make sure it is clear this is plural, to
* ease translation. Use @count in place of the item count, as in
* "@count new comments".
* @param $args
* An associative array of replacements to make after translation. Incidences
* An associative array of replacements to make after translation. Instances
* of any key in this array are replaced with the corresponding value.
* Based on the first character of the key, the value is escaped and/or themed:
* - !variable: inserted as is
* - @variable: escape plain text to HTML (check_plain)
* - %variable: escape text and theme as a placeholder for user-submitted
* content (check_plain + drupal_placeholder)
* Note that you do not need to include @count in this array.
* This replacement is done automatically for the plural case.
* Based on the first character of the key, the value is escaped and/or
* themed. See format_string(). Note that you do not need to include @count
* in this array; this replacement is done automatically for the plural case.
* @param $options
* An associative array of additional options, with the following keys:
* - 'langcode' (default to the current language) The language code to
* translate to a language other than what is used to display the page.
* - 'context' (default to the empty context) The context the source string
* belongs to.
* An associative array of additional options. See t() for allowed keys.
*
* @return
* A translated string.
*
* @see t()
* @see format_string()
*/
function format_plural($count, $singular, $plural, array $args = array(), array $options = array()) {
$args['@count'] = $count;
......@@ -1709,7 +1858,8 @@ function format_plural($count, $singular, $plural, array $args = array(), array
// Get the plural index through the gettext formula.
$index = (function_exists('locale_get_plural')) ? locale_get_plural($count, isset($options['langcode']) ? $options['langcode'] : NULL) : -1;
// Backwards compatibility.
// If the index cannot be computed, use the plural as a fallback (which
// allows for most flexiblity with the replaceable @count value).
if ($index < 0) {
return t($plural, $args, $options);
}
......@@ -1728,11 +1878,12 @@ function format_plural($count, $singular, $plural, array $args = array(), array
}
/**
* Parse a given byte count.
* Parses a given byte count.
*
* @param $size
* A size expressed as a number of bytes with optional SI or IEC binary unit
* prefix (e.g. 2, 3K, 5MB, 10G, 6GiB, 8 bytes, 9mbytes).
*
* @return
* An integer representation of the size in bytes.
*/
......@@ -1749,13 +1900,14 @@ function parse_size($size) {
}
/**
* Generate a string representation for the given byte count.
* Generates a string representation for the given byte count.
*
* @param $size
* A size in bytes.
* @param $langcode
* Optional language code to translate to a language other than what is used
* to display the page.
*
* @return
* A translated string representation of the size.
*/
......@@ -1788,19 +1940,20 @@ function format_size($size, $langcode = NULL) {
}
/**
* Format a time interval with the requested granularity.
* Formats a time interval with the requested granularity.
*
* @param $timestamp
* @param $interval
* The length of the interval in seconds.
* @param $granularity
* How many different units to display in the string.
* @param $langcode
* Optional language code to translate to a language other than
* what is used to display the page.
*
* @return
* A translated string representation of the interval.
*/
function format_interval($timestamp, $granularity = 2, $langcode = NULL) {
function format_interval($interval, $granularity = 2, $langcode = NULL) {
$units = array(
'1 year|@count years' => 31536000,
'1 month|@count months' => 2592000,
......@@ -1813,9 +1966,9 @@ function format_interval($timestamp, $granularity = 2, $langcode = NULL) {
$output = '';
foreach ($units as $key => $value) {
$key = explode('|', $key);
if ($timestamp >= $value) {
$output .= ($output ? ' ' : '') . format_plural(floor($timestamp / $value), $key[0], $key[1], array(), array('langcode' => $langcode));
$timestamp %= $value;
if ($interval >= $value) {
$output .= ($output ? ' ' : '') . format_plural(floor($interval / $value), $key[0], $key[1], array(), array('langcode' => $langcode));
$interval %= $value;
$granularity--;
}
......@@ -1845,7 +1998,7 @@ function format_interval($timestamp, $granularity = 2, $langcode = NULL) {
* get interpreted as date format characters.
* @param $timezone
* (optional) Time zone identifier, as described at
* http://php.net/manual/en/timezones.php Defaults to the time zone used to
* http://php.net/manual/timezones.php Defaults to the time zone used to
* display the page.
* @param $langcode
* (optional) Language code to translate to. Defaults to the language used to
......@@ -1928,10 +2081,11 @@ function format_date($timestamp, $type = 'medium', $format = '', $timezone = NUL
/**
* Returns an ISO8601 formatted date based on the given date.
*
* Can be used as a callback for RDF mappings.
* Callback for use within hook_rdf_mapping() implementations.
*
* @param $date
* A UNIX timestamp.
*
* @return string
* An ISO8601 formatted date.
*/
......@@ -1942,7 +2096,9 @@ function date_iso8601($date) {
}
/**
* Callback function for preg_replace_callback().
* Translates a formatted date string.
*
* Callback for preg_replace_callback() within format_date().
*/
function _format_date_callback(array $matches = NULL, $new_langcode = NULL) {
// We cache translations to avoid redundant and rather costly calls to t().
......@@ -1978,6 +2134,9 @@ function _format_date_callback(array $matches = NULL, $new_langcode = NULL) {
/**
* Format a username.
*
* This is also the label callback implementation of
* callback_entity_info_label() for user_entity_info().
*
* By default, the passed-in object's 'name' property is used if it exists, or
* else, the site-defined value for the 'anonymous' variable. However, a module
* may override this by implementing hook_username_alter(&$name, $account).
......@@ -2009,8 +2168,9 @@ function format_username($account) {
* alternative than url().
*
* @param $path
* The internal path or external URL being linked to, such as "node/34" or
* "http://example.com/foo". A few notes:
* (optional) The internal path or external URL being linked to, such as
* "node/34" or "http://example.com/foo". The default value is equivalent to
* passing in '<front>'. A few notes:
* - If you provide a full URL, it will be considered an external URL.
* - If you provide only the path (e.g. "node/34"), it will be
* considered an internal link. In this case, it should be a system URL,
......@@ -2026,7 +2186,8 @@ function format_username($account) {
* include them in $path, or use $options['query'] to let this function
* URL encode them.
* @param $options
* An associative array of additional options, with the following elements:
* (optional) An associative array of additional options, with the following
* elements:
* - 'query': An array of query key/value-pairs (without any URL-encoding) to
* append to the URL.
* - 'fragment': A fragment identifier (named anchor) to append to the URL.
......@@ -2042,7 +2203,7 @@ function format_username($account) {
* for the URL. If $options['language'] is omitted, the global $language_url
* will be used.
* - 'https': Whether this URL should point to a secure location. If not
* defined, the current scheme is used, so the user stays on http or https
* defined, the current scheme is used, so the user stays on HTTP or HTTPS
* respectively. TRUE enforces HTTPS and FALSE enforces HTTP, but HTTPS can
* only be enforced when the variable 'https' is set to TRUE.
* - 'base_url': Only used internally, to modify the base URL when a language
......@@ -2057,8 +2218,8 @@ function format_username($account) {
* Drupal on a web server that cannot be configured to automatically find
* index.php, then hook_url_outbound_alter() can be implemented to force
* this value to 'index.php'.
* - 'entity_type': The entity type of the object that called url(). Only set if
* url() is invoked by entity_uri().
* - 'entity_type': The entity type of the object that called url(). Only
* set if url() is invoked by entity_uri().
* - 'entity': The entity object (such as a node) for which the URL is being
* generated. Only set if url() is invoked by entity_uri().
*
......@@ -2076,13 +2237,7 @@ function url($path = NULL, array $options = array()) {
);
if (!isset($options['external'])) {
// Return an external link if $path contains an allowed absolute URL. Only
// call the slow drupal_strip_dangerous_protocols() if $path contains a ':'
// before any / ? or #. Note: we could use url_is_external($path) here, but
// that would require another function call, and performance inside url() is
// critical.
$colonpos = strpos($path, ':');
$options['external'] = ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && drupal_strip_dangerous_protocols($path) == $path);
$options['external'] = url_is_external($path);
}
// Preserve the original path before altering or aliasing.
......@@ -2120,6 +2275,11 @@ function url($path = NULL, array $options = array()) {
return $path . $options['fragment'];
}
// Strip leading slashes from internal paths to prevent them becoming external
// URLs without protocol. /example.com should not be turned into
// //example.com.
$path = ltrim($path, '/');
global $base_url, $base_secure_url, $base_insecure_url;
// The base_url might be rewritten from the language rewrite in domain mode.
......@@ -2183,7 +2343,7 @@ function url($path = NULL, array $options = array()) {
}
/**
* Return TRUE if a path is external to Drupal (e.g. http://example.com).
* Returns TRUE if a path is external to Drupal (e.g. http://example.com).
*
* If a path cannot be assessed by Drupal's menu handler, then we must
* treat it as potentially insecure.
......@@ -2191,19 +2351,31 @@ function url($path = NULL, array $options = array()) {
* @param $path
* The internal path or external URL being linked to, such as "node/34" or
* "http://example.com/foo".
*
* @return
* Boolean TRUE or FALSE, where TRUE indicates an external path.
*/
function url_is_external($path) {
$colonpos = strpos($path, ':');
// Avoid calling drupal_strip_dangerous_protocols() if there is any
// slash (/), hash (#) or question_mark (?) before the colon (:)
// occurrence - if any - as this would clearly mean it is not a URL.
return $colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && drupal_strip_dangerous_protocols($path) == $path;
}
/**
* Format an attribute string for a HTTP header.
// Some browsers treat \ as / so normalize to forward slashes.
$path = str_replace('\\', '/', $path);
// If the path starts with 2 slashes then it is always considered an external
// URL without an explicit protocol part.
return (strpos($path, '//') === 0)
// Leading control characters may be ignored or mishandled by browsers, so
// assume such a path may lead to an external location. The \p{C} character
// class matches all UTF-8 control, unassigned, and private characters.
|| (preg_match('/^\p{C}/u', $path) !== 0)
// Avoid calling drupal_strip_dangerous_protocols() if there is any slash
// (/), hash (#) or question_mark (?) before the colon (:) occurrence - if
// any - as this would clearly mean it is not a URL.
|| ($colonpos !== FALSE
&& !preg_match('![/?#]!', substr($path, 0, $colonpos))
&& drupal_strip_dangerous_protocols($path) == $path);
}
/**
* Formats an attribute string for an HTTP header.
*
* @param $attributes
* An associative array of attributes such as 'rel'.
......@@ -2225,7 +2397,7 @@ function drupal_http_header_attributes(array $attributes = array()) {
}
/**
* Converts an associative array to an attribute string for use in XML/HTML tags.
* Converts an associative array to an XML/HTML tag attribute string.
*
* Each array key and its value will be formatted into an attribute string.
* If a value is itself an array, then its elements are concatenated to a single
......@@ -2271,21 +2443,30 @@ function drupal_attributes(array $attributes = array()) {
/**
* Formats an internal or external URL link as an HTML anchor tag.
*
* This function correctly handles aliased paths, and adds an 'active' class
* This function correctly handles aliased paths and adds an 'active' class
* attribute to links that point to the current page (for theming), so all
* internal links output by modules should be generated by this function if
* possible.
*
* @param $text
* The link text for the anchor tag.
* @param $path
* However, for links enclosed in translatable text you should use t() and
* embed the HTML anchor tag directly in the translated string. For example:
* @code
* t('Visit the <a href="@url">settings</a> page', array('@url' => url('admin')));
* @endcode
* This keeps the context of the link title ('settings' in the example) for
* translators.
*
* @param string $text
* The translated link text for the anchor tag.
* @param string $path
* The internal path or external URL being linked to, such as "node/34" or
* "http://example.com/foo". After the url() function is called to construct
* the URL from $path and $options, the resulting URL is passed through
* check_plain() before it is inserted into the HTML anchor tag, to ensure
* well-formed HTML. See url() for more information and notes.
* @param array $options
* An associative array of additional options, with the following elements:
* An associative array of additional options. Defaults to an empty array. It
* may contain the following elements.
* - 'attributes': An associative array of HTML attributes to apply to the
* anchor tag. If element 'class' is included, it must be an array; 'title'
* must be a string; other elements are more flexible, as they just need
......@@ -2301,8 +2482,10 @@ function drupal_attributes(array $attributes = array()) {
* well as the path must match). This element is also used by url().
* - Additional $options elements used by the url() function.
*
* @return
* @return string
* An HTML string containing a link to the given path.
*
* @see url()
*/
function l($text, $path, array $options = array()) {
global $language_url;
......@@ -2338,7 +2521,7 @@ function l($text, $path, array $options = array()) {
// rendering.
if (variable_get('theme_link', TRUE)) {
drupal_theme_initialize();
$registry = theme_get_registry();
$registry = theme_get_registry(FALSE);
// We don't want to duplicate functionality that's in theme(), so any
// hint of a module or theme doing anything at all special with the 'link'
// theme hook should simply result in theme() being called. This includes
......@@ -2446,7 +2629,7 @@ function drupal_deliver_page($page_callback_result, $default_delivery_callback =
}
/**
* Package and send the result of a page callback to the browser as HTML.
* Packages and sends the result of a page callback to the browser as HTML.
*
* @param $page_callback_result
* The result of a page callback. Can be one of:
......@@ -2466,6 +2649,19 @@ function drupal_deliver_html_page($page_callback_result) {
drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
}
// Send appropriate HTTP-Header for browsers and search engines.
global $language;
drupal_add_http_header('Content-Language', $language->language);
// By default, do not allow the site to be rendered in an iframe on another
// domain, but provide a variable to override this. If the code running for
// this page request already set the X-Frame-Options header earlier, don't
// overwrite it here.
$frame_options = variable_get('x_frame_options', 'SAMEORIGIN');
if ($frame_options && is_null(drupal_get_http_header('X-Frame-Options'))) {
drupal_add_http_header('X-Frame-Options', $frame_options);
}
// Menu status constants are integers; page content is a string or array.
if (is_int($page_callback_result)) {
// @todo: Break these up into separate functions?
......@@ -2476,9 +2672,15 @@ function drupal_deliver_html_page($page_callback_result) {
watchdog('page not found', check_plain($_GET['q']), NULL, WATCHDOG_WARNING);
// Check for and return a fast 404 page if configured.
drupal_fast_404();
// Keep old path for reference, and to allow forms to redirect to it.
if (!isset($_GET['destination'])) {
$_GET['destination'] = $_GET['q'];
// Make sure that the current path is not interpreted as external URL.
if (!url_is_external($_GET['q'])) {
$_GET['destination'] = $_GET['q'];
}
}
$path = drupal_get_normal_path(variable_get('site_404', ''));
......@@ -2492,7 +2694,7 @@ function drupal_deliver_html_page($page_callback_result) {
if (empty($return) || $return == MENU_NOT_FOUND || $return == MENU_ACCESS_DENIED) {
// Standard 404 handler.
drupal_set_title(t('Page not found'));
$return = t('The requested page could not be found.');
$return = t('The requested page "@path" could not be found.', array('@path' => request_uri()));
}
drupal_set_page_content($return);
......@@ -2507,7 +2709,10 @@ function drupal_deliver_html_page($page_callback_result) {
// Keep old path for reference, and to allow forms to redirect to it.
if (!isset($_GET['destination'])) {
$_GET['destination'] = $_GET['q'];
// Make sure that the current path is not interpreted as external URL.
if (!url_is_external($_GET['q'])) {
$_GET['destination'] = $_GET['q'];
}
}
$path = drupal_get_normal_path(variable_get('site_403', ''));
......@@ -2548,7 +2753,7 @@ function drupal_deliver_html_page($page_callback_result) {
}
/**
* Perform end-of-request tasks.
* Performs end-of-request tasks.
*
* This function sets the page cache if appropriate, and allows modules to
* react to the closing of the page by calling hook_exit().
......@@ -2571,11 +2776,12 @@ function drupal_page_footer() {
_registry_check_code(REGISTRY_WRITE_LOOKUP_CACHE);
drupal_cache_system_paths();
module_implements_write_cache();
drupal_file_scan_write_cache();
system_run_automated_cron();
}
/**
* Perform end-of-request tasks.
* Performs end-of-request tasks.
*
* In some cases page requests need to end without calling drupal_page_footer().
* In these cases, call drupal_exit() instead. There should rarely be a reason
......@@ -2597,7 +2803,7 @@ function drupal_exit($destination = NULL) {
}
/**
* Form an associative array from a linear array.
* Forms an associative array from a linear array.
*
* This function walks through the provided array and constructs an associative
* array out of it. The keys of the resulting array will be the values of the
......@@ -2632,11 +2838,11 @@ function drupal_map_assoc($array, $function = NULL) {
* into script execution a call such as set_time_limit(20) is made, the
* script will run for a total of 45 seconds before timing out.
*
* It also means that it is possible to decrease the total time limit if
* the sum of the new time limit and the current time spent running the
* script is inferior to the original time limit. It is inherent to the way
* set_time_limit() works, it should rather be called with an appropriate
* value every time you need to allocate a certain amount of time
* If the current time limit is not unlimited it is possible to decrease the
* total time limit if the sum of the new time limit and the current time spent
* running the script is inferior to the original time limit. It is inherent to
* the way set_time_limit() works, it should rather be called with an
* appropriate value every time you need to allocate a certain amount of time
* to execute a task than only once at the beginning of the script.
*
* Before calling set_time_limit(), we check if this function is available
......@@ -2653,7 +2859,11 @@ function drupal_map_assoc($array, $function = NULL) {
*/
function drupal_set_time_limit($time_limit) {
if (function_exists('set_time_limit')) {
@set_time_limit($time_limit);
$current = ini_get('max_execution_time');
// Do not set time limit if it is currently unlimited.
if ($current != 0) {
@set_time_limit($time_limit);
}
}
}
......@@ -2666,17 +2876,17 @@ function drupal_set_time_limit($time_limit) {
* The name of the item for which the path is requested.
*
* @return
* The path to the requested item.
* The path to the requested item or an empty string if the item is not found.
*/
function drupal_get_path($type, $name) {
return dirname(drupal_get_filename($type, $name));
}
/**
* Return the base URL path (i.e., directory) of the Drupal installation.
* Returns the base URL path (i.e., directory) of the Drupal installation.
*
* base_path() prefixes and suffixes a "/" onto the returned path if the path is
* not empty. At the very least, this will return "/".
* base_path() adds a "/" to the beginning and end of the returned path if the
* path is not empty. At the very least, this will return "/".
*
* Examples:
* - http://example.com returns "/" because the path is empty.
......@@ -2687,12 +2897,12 @@ function base_path() {
}
/**
* Add a LINK tag with a distinct 'rel' attribute to the page's HEAD.
* Adds a LINK tag with a distinct 'rel' attribute to the page's HEAD.
*
* This function can be called as long the HTML header hasn't been sent,
* which on normal pages is up through the preprocess step of theme('html').
* Adding a link will overwrite a prior link with the exact same 'rel' and
* 'href' attributes.
* This function can be called as long the HTML header hasn't been sent, which
* on normal pages is up through the preprocess step of theme('html'). Adding
* a link will overwrite a prior link with the exact same 'rel' and 'href'
* attributes.
*
* @param $attributes
* Associative array of element attributes including 'href' and 'rel'.
......@@ -2756,8 +2966,8 @@ function drupal_add_html_head_link($attributes, $header = FALSE) {
* See drupal_get_css() where the overrides are performed. Also, if the
* direction of the current language is right-to-left (Hebrew, Arabic,
* etc.), the function will also look for an RTL CSS file and append it to
* the list. The name of this file should have an '-rtl.css' suffix. For
* example a CSS file called 'mymodule-name.css' will have a
* the list. The name of this file should have an '-rtl.css' suffix. For
* example, a CSS file called 'mymodule-name.css' will have a
* 'mymodule-name-rtl.css' file added to the list, if exists in the same
* directory. This CSS file should contain overrides for properties which
* should be reversed or otherwise different in a right-to-left display.
......@@ -2782,7 +2992,7 @@ function drupal_add_html_head_link($attributes, $header = FALSE) {
* - 'group': A number identifying the group in which to add the stylesheet.
* Available constants are:
* - CSS_SYSTEM: Any system-layer CSS.
* - CSS_DEFAULT: Any module-layer CSS.
* - CSS_DEFAULT: (default) Any module-layer CSS.
* - CSS_THEME: Any theme-layer CSS.
* The group number serves as a weight: the markup for loading a stylesheet
* within a lower weight group is output to the page before the markup for
......@@ -2834,6 +3044,13 @@ function drupal_add_html_head_link($attributes, $header = FALSE) {
*/
function drupal_add_css($data = NULL, $options = NULL) {
$css = &drupal_static(__FUNCTION__, array());
$count = &drupal_static(__FUNCTION__ . '_count', 0);
// If the $css variable has been reset with drupal_static_reset(), there is
// no longer any CSS being tracked, so set the counter back to 0 also.
if (count($css) === 0) {
$count = 0;
}
// Construct the options, taking the defaults into consideration.
if (isset($options)) {
......@@ -2869,7 +3086,8 @@ function drupal_add_css($data = NULL, $options = NULL) {
}
// Always add a tiny value to the weight, to conserve the insertion order.
$options['weight'] += count($css) / 1000;
$options['weight'] += $count / 1000;
$count++;
// Add the data to the CSS array depending on the type.
switch ($options['type']) {
......@@ -2889,7 +3107,7 @@ function drupal_add_css($data = NULL, $options = NULL) {
}
/**
* Returns a themed representation of all stylesheets that should be attached to the page.
* Returns a themed representation of all stylesheets to attach to the page.
*
* It loads the CSS in order, with 'module' first, then 'theme' afterwards.
* This ensures proper cascading of styles so themes can easily override
......@@ -2930,12 +3148,24 @@ function drupal_get_css($css = NULL, $skip_alter = FALSE) {
// Sort CSS items, so that they appear in the correct order.
uasort($css, 'drupal_sort_css_js');
// Provide the page with information about the individual CSS files used,
// information not otherwise available when CSS aggregation is enabled. The
// setting is attached later in this function, but is set here, so that CSS
// files removed below are still considered "used" and prevented from being
// added in a later AJAX request.
// Skip if no files were added to the page or jQuery.extend() will overwrite
// the Drupal.settings.ajaxPageState.css object with an empty array.
if (!empty($css)) {
// Cast the array to an object to be on the safe side even if not empty.
$setting['ajaxPageState']['css'] = (object) array_fill_keys(array_keys($css), 1);
}
// Remove the overridden CSS files. Later CSS files override former ones.
$previous_item = array();
foreach ($css as $key => $item) {
if ($item['type'] == 'file') {
// If defined, force a unique basename for this file.
$basename = isset($item['basename']) ? $item['basename'] : basename($item['data']);
$basename = isset($item['basename']) ? $item['basename'] : drupal_basename($item['data']);
if (isset($previous_item[$basename])) {
// Remove the previous item that shared the same base name.
unset($css[$previous_item[$basename]]);
......@@ -2950,20 +3180,32 @@ function drupal_get_css($css = NULL, $skip_alter = FALSE) {
'#items' => $css,
);
// Provide the page with information about the individual CSS files used,
// information not otherwise available when CSS aggregation is enabled.
$setting['ajaxPageState']['css'] = array_fill_keys(array_keys($css), 1);
$styles['#attached']['js'][] = array('type' => 'setting', 'data' => $setting);
if (!empty($setting)) {
$styles['#attached']['js'][] = array('type' => 'setting', 'data' => $setting);
}
return drupal_render($styles);
}
/**
* Function used by uasort to sort the array structures returned by drupal_add_css() and drupal_add_js().
* Sorts CSS and JavaScript resources.
*
* Callback for uasort() within:
* - drupal_get_css()
* - drupal_get_js()
*
* This sort order helps optimize front-end performance while providing modules
* and themes with the necessary control for ordering the CSS and JavaScript
* appearing on a page.
*
* @param $a
* First item for comparison. The compared items should be associative arrays
* of member items from drupal_add_css() or drupal_add_js().
* @param $b
* Second item for comparison.
*
* @see drupal_add_css()
* @see drupal_add_js()
*/
function drupal_sort_css_js($a, $b) {
// First order by group, so that, for example, all items in the CSS_SYSTEM
......@@ -3016,7 +3258,7 @@ function drupal_sort_css_js($a, $b) {
* are always groupable, and items of the 'external' type are never groupable.
* This function also ensures that the process of grouping items does not change
* their relative order. This requirement may result in multiple groups for the
* same type, media, and browsers, if needed to accomodate other items in
* same type, media, and browsers, if needed to accommodate other items in
* between.
*
* @param $css
......@@ -3030,6 +3272,7 @@ function drupal_sort_css_js($a, $b) {
* 'items' key, which is the subset of items from $css that are in the group.
*
* @see drupal_pre_render_styles()
* @see system_element_info()
*/
function drupal_group_css($css) {
$groups = array();
......@@ -3112,6 +3355,7 @@ function drupal_group_css($css) {
*
* @see drupal_group_css()
* @see drupal_pre_render_styles()
* @see system_element_info()
*/
function drupal_aggregate_css(&$css_groups) {
$preprocess_css = (variable_get('preprocess_css', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update'));
......@@ -3290,7 +3534,11 @@ function drupal_pre_render_styles($elements) {
$import_batch = array_slice($import, 0, 31);
$import = array_slice($import, 31);
$element = $style_element_defaults;
$element['#value'] = implode("\n", $import_batch);
// This simplifies the JavaScript regex, allowing each line
// (separated by \n) to be treated as a completely different string.
// This means that we can use ^ and $ on one line at a time, and not
// worry about style tags since they'll never match the regex.
$element['#value'] = "\n" . implode("\n", $import_batch) . "\n";
$element['#attributes']['media'] = $group['media'];
$element['#browsers'] = $group['browsers'];
$elements[] = $element;
......@@ -3373,8 +3621,8 @@ function drupal_pre_render_styles($elements) {
* in $css while the value is the cache file name. The cache file is generated
* in two cases. First, if there is no file name value for the key, which will
* happen if a new file name has been added to $css or after the lookup
* variable is emptied to force a rebuild of the cache. Second, the cache
* file is generated if it is missing on disk. Old cache files are not deleted
* variable is emptied to force a rebuild of the cache. Second, the cache file
* is generated if it is missing on disk. Old cache files are not deleted
* immediately when the lookup variable is emptied, but are deleted after a set
* period by drupal_delete_file_if_stale(). This ensures that files referenced
* by a cached page will still be available.
......@@ -3389,7 +3637,13 @@ function drupal_build_css_cache($css) {
$data = '';
$uri = '';
$map = variable_get('drupal_css_cache_files', array());
$key = hash('sha256', serialize($css));
// Create a new array so that only the file names are used to create the hash.
// This prevents new aggregates from being created unnecessarily.
$css_data = array();
foreach ($css as $css_file) {
$css_data[] = $css_file['data'];
}
$key = hash('sha256', serialize($css_data));
if (isset($map[$key])) {
$uri = $map[$key];
}
......@@ -3452,9 +3706,7 @@ function drupal_build_css_cache($css) {
}
/**
* Helper function for drupal_build_css_cache().
*
* This function will prefix all paths within a CSS file.
* Prefixes all paths within a CSS file for drupal_build_css_cache().
*/
function _drupal_build_css_path($matches, $base = NULL) {
$_base = &drupal_static(__FUNCTION__);
......@@ -3511,33 +3763,40 @@ function drupal_load_stylesheet($file, $optimize = NULL, $reset_basepath = TRUE)
if ($basepath && !file_uri_scheme($file)) {
$file = $basepath . '/' . $file;
}
// Store the parent base path to restore it later.
$parent_base_path = $basepath;
// Set the current base path to process possible child imports.
$basepath = dirname($file);
// Load the CSS stylesheet. We suppress errors because themes may specify
// stylesheets in their .info file that don't exist in the theme's path,
// but are merely there to disable certain module CSS files.
$content = '';
if ($contents = @file_get_contents($file)) {
// Return the processed stylesheet.
return drupal_load_stylesheet_content($contents, $_optimize);
$content = drupal_load_stylesheet_content($contents, $_optimize);
}
return '';
// Restore the parent base path as the file and its childen are processed.
$basepath = $parent_base_path;
return $content;
}
/**
* Process the contents of a stylesheet for aggregation.
* Processes the contents of a stylesheet for aggregation.
*
* @param $contents
* The contents of the stylesheet.
* @param $optimize
* (optional) Boolean whether CSS contents should be minified. Defaults to
* FALSE.
*
* @return
* Contents of the stylesheet including the imported stylesheets.
*/
function drupal_load_stylesheet_content($contents, $optimize = FALSE) {
// Remove multiple charset declarations for standards compliance (and fixing Safari problems).
$contents = preg_replace('/^@charset\s+[\'"](\S*)\b[\'"];/i', '', $contents);
$contents = preg_replace('/^@charset\s+[\'"](\S*?)\b[\'"];/i', '', $contents);
if ($optimize) {
// Perform some safe CSS optimizations.
......@@ -3556,7 +3815,7 @@ function drupal_load_stylesheet_content($contents, $optimize = FALSE) {
// Remove certain whitespace.
// There are different conditions for removing leading and trailing
// whitespace.
// @see http://php.net/manual/en/regexp.reference.subpatterns.php
// @see http://php.net/manual/regexp.reference.subpatterns.php
$contents = preg_replace('<
# Strip leading and trailing whitespace.
\s*([@{};,])\s*
......@@ -3581,7 +3840,7 @@ function drupal_load_stylesheet_content($contents, $optimize = FALSE) {
// Replaces @import commands with the actual stylesheet content.
// This happens recursively but omits external files.
$contents = preg_replace_callback('/@import\s*(?:url\(\s*)?[\'"]?(?![a-z]+:)([^\'"\()]+)[\'"]?\s*\)?\s*;/', '_drupal_load_stylesheet', $contents);
$contents = preg_replace_callback('/@import\s*(?:url\(\s*)?[\'"]?(?![a-z]+:)(?!\/\/)([^\'"\()]+)[\'"]?\s*\)?\s*;/', '_drupal_load_stylesheet', $contents);
return $contents;
}
......@@ -3605,7 +3864,7 @@ function _drupal_load_stylesheet($matches) {
// Alter all internal url() paths. Leave external paths alone. We don't need
// to normalize absolute paths here (i.e. remove folder/... segments) because
// that will be done later.
return preg_replace('/url\(\s*([\'"]?)(?![a-z]+:|\/+)/i', 'url(\1'. $directory, $file);
return preg_replace('/url\(\s*([\'"]?)(?![a-z]+:|\/+)([^\'")]+)([\'"]?)\s*\)/i', 'url(\1' . $directory . '\2\3)', $file);
}
/**
......@@ -3627,7 +3886,7 @@ function drupal_delete_file_if_stale($uri) {
}
/**
* Prepare a string for use as a valid CSS identifier (element, class or ID name).
* Prepares a string for use as a CSS identifier (element, class, or ID name).
*
* http://www.w3.org/TR/CSS21/syndata.html#characters shows the syntax for valid
* CSS identifiers (including element names, classes, and IDs in selectors.)
......@@ -3636,10 +3895,26 @@ function drupal_delete_file_if_stale($uri) {
* The identifier to clean.
* @param $filter
* An array of string replacements to use on the identifier.
*
* @return
* The cleaned identifier.
*/
function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_' => '-', '/' => '-', '[' => '-', ']' => '')) {
// Use the advanced drupal_static() pattern, since this is called very often.
static $drupal_static_fast;
if (!isset($drupal_static_fast)) {
$drupal_static_fast['allow_css_double_underscores'] = &drupal_static(__FUNCTION__ . ':allow_css_double_underscores');
}
$allow_css_double_underscores = &$drupal_static_fast['allow_css_double_underscores'];
if (!isset($allow_css_double_underscores)) {
$allow_css_double_underscores = variable_get('allow_css_double_underscores', FALSE);
}
// Preserve BEM-style double-underscores depending on custom setting.
if ($allow_css_double_underscores) {
$filter['__'] = '__';
}
// By default, we filter using Drupal's coding standards.
$identifier = strtr($identifier, $filter);
......@@ -3657,22 +3932,30 @@ function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_
}
/**
* Prepare a string for use as a valid class name.
* Prepares a string for use as a valid class name.
*
* Do not pass one string containing multiple classes as they will be
* incorrectly concatenated with dashes, i.e. "one two" will become "one-two".
*
* @param $class
* The class name to clean.
*
* @return
* The cleaned class name.
*/
function drupal_html_class($class) {
return drupal_clean_css_identifier(drupal_strtolower($class));
// The output of this function will never change, so this uses a normal
// static instead of drupal_static().
static $classes = array();
if (!isset($classes[$class])) {
$classes[$class] = drupal_clean_css_identifier(drupal_strtolower($class));
}
return $classes[$class];
}
/**
* Prepare a string for use as a valid HTML ID and guarantee uniqueness.
* Prepares a string for use as a valid HTML ID and guarantees uniqueness.
*
* This function ensures that each passed HTML ID value only exists once on the
* page. By tracking the already returned ids, this function enables forms,
......@@ -3723,7 +4006,16 @@ function drupal_html_id($id) {
// requested id. $_POST['ajax_html_ids'] contains the ids as they were
// returned by this function, potentially with the appended counter, so
// we parse that to reconstruct the $seen_ids array.
foreach ($_POST['ajax_html_ids'] as $seen_id) {
if (isset($_POST['ajax_html_ids'][0]) && strpos($_POST['ajax_html_ids'][0], ',') === FALSE) {
$ajax_html_ids = $_POST['ajax_html_ids'];
}
else {
// jquery.form.js may send the server a comma-separated string as the
// first element of an array (see http://drupal.org/node/1575060), so
// we need to convert it to an array in that case.
$ajax_html_ids = explode(',', $_POST['ajax_html_ids'][0]);
}
foreach ($ajax_html_ids as $seen_id) {
// We rely on '--' being used solely for separating a base id from the
// counter, which this function ensures when returning an id.
$parts = explode('--', $seen_id, 2);
......@@ -3803,7 +4095,7 @@ function drupal_region_class($region) {
* to tell the user that a new message arrived, by opening a pop up, alert
* box, etc.). This should only be used for JavaScript that cannot be executed
* from a file. When adding inline code, make sure that you are not relying on
* $() being the jQuery function. Wrap your code in
* $() being the jQuery function. Wrap your code in
* @code (function ($) {... })(jQuery); @endcode
* or use jQuery() instead of $().
* - Add external JavaScript ('external'): Allows the inclusion of external
......@@ -3849,7 +4141,8 @@ function drupal_region_class($region) {
* actually needed.
*
* @param $data
* (optional) If given, the value depends on the $options parameter:
* (optional) If given, the value depends on the $options parameter, or
* $options['type'] if $options is passed as an associative array:
* - 'file': Path to the file relative to base_path().
* - 'inline': The JavaScript code that should be placed in the given scope.
* - 'external': The absolute path to an external JavaScript file that is not
......@@ -3922,8 +4215,15 @@ function drupal_region_class($region) {
* else being the same, JavaScript added by a call to drupal_add_js() that
* happened later in the page request gets added to the page after one for
* which drupal_add_js() happened earlier in the page request.
* - defer: If set to TRUE, the defer attribute is set on the &lt;script&gt;
* tag. Defaults to FALSE.
* - requires_jquery: Set this to FALSE if the JavaScript you are adding does
* not have a dependency on jQuery. Defaults to TRUE, except for JavaScript
* settings where it defaults to FALSE. This is used on sites that have the
* 'javascript_always_use_jquery' variable set to FALSE; on those sites, if
* all the JavaScript added to the page by drupal_add_js() does not have a
* dependency on jQuery, then for improved front-end performance Drupal
* will not add jQuery and related libraries and settings to the page.
* - defer: If set to TRUE, the defer attribute is set on the <script>
* tag. Defaults to FALSE.
* - cache: If set to FALSE, the JavaScript file is loaded anew on every page
* call; in other words, it is not cached. Used only when 'type' references
* a JavaScript file. Defaults to TRUE.
......@@ -3939,6 +4239,14 @@ function drupal_region_class($region) {
*/
function drupal_add_js($data = NULL, $options = NULL) {
$javascript = &drupal_static(__FUNCTION__, array());
$jquery_added = &drupal_static(__FUNCTION__ . ':jquery_added', FALSE);
// If the $javascript variable has been reset with drupal_static_reset(),
// jQuery and related files will have been removed from the list, so set the
// variable back to FALSE to indicate they have not yet been added.
if (empty($javascript)) {
$jquery_added = FALSE;
}
// Construct the options, taking the defaults into consideration.
if (isset($options)) {
......@@ -3949,6 +4257,9 @@ function drupal_add_js($data = NULL, $options = NULL) {
else {
$options = array();
}
if (isset($options['type']) && $options['type'] == 'setting') {
$options += array('requires_jquery' => FALSE);
}
$options += drupal_js_defaults($data);
// Preprocess can only be set if caching is enabled.
......@@ -3959,14 +4270,18 @@ function drupal_add_js($data = NULL, $options = NULL) {
$options['weight'] += count($javascript) / 1000;
if (isset($data)) {
// Add jquery.js and drupal.js, as well as the basePath setting, the
// first time a JavaScript file is added.
if (empty($javascript)) {
// Add jquery.js, drupal.js, and related files and settings if they have
// not been added yet. However, if the 'javascript_always_use_jquery'
// variable is set to FALSE (indicating that the site does not want jQuery
// automatically added on all pages) then only add it if a file or setting
// that requires jQuery is being added also.
if (!$jquery_added && (variable_get('javascript_always_use_jquery', TRUE) || $options['requires_jquery'])) {
$jquery_added = TRUE;
// url() generates the prefix using hook_url_outbound_alter(). Instead of
// running the hook_url_outbound_alter() again here, extract the prefix
// from url().
url('', array('prefix' => &$prefix));
$javascript = array(
$default_javascript = array(
'settings' => array(
'data' => array(
array('basePath' => base_path()),
......@@ -3985,11 +4300,13 @@ function drupal_add_js($data = NULL, $options = NULL) {
'group' => JS_LIBRARY,
'every_page' => TRUE,
'weight' => -1,
'requires_jquery' => TRUE,
'preprocess' => TRUE,
'cache' => TRUE,
'defer' => FALSE,
),
);
$javascript = drupal_array_merge_deep($javascript, $default_javascript);
// Register all required libraries.
drupal_add_library('system', 'jquery', TRUE);
drupal_add_library('system', 'jquery.once', TRUE);
......@@ -4020,6 +4337,7 @@ function drupal_add_js($data = NULL, $options = NULL) {
*
* @param $data
* (optional) The default data parameter for the JavaScript item array.
*
* @see drupal_get_js()
* @see drupal_add_js()
*/
......@@ -4029,6 +4347,7 @@ function drupal_js_defaults($data = NULL) {
'group' => JS_DEFAULT,
'every_page' => FALSE,
'weight' => 0,
'requires_jquery' => TRUE,
'scope' => 'header',
'cache' => TRUE,
'defer' => FALSE,
......@@ -4063,8 +4382,10 @@ function drupal_js_defaults($data = NULL) {
* (optional) If set to TRUE, this function skips calling drupal_alter() on
* $javascript, useful when the calling function passes a $javascript array
* that has already been altered.
*
* @return
* All JavaScript code segments and includes for the scope as HTML tags.
*
* @see drupal_add_js()
* @see locale_js_alter()
* @see drupal_js_defaults()
......@@ -4073,7 +4394,12 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
if (!isset($javascript)) {
$javascript = drupal_add_js();
}
if (empty($javascript)) {
// If no JavaScript items have been added, or if the only JavaScript items
// that have been added are JavaScript settings (which don't do anything
// without any JavaScript code to use them), then no JavaScript code should
// be added to the page.
if (empty($javascript) || (isset($javascript['settings']) && count($javascript) == 1)) {
return '';
}
......@@ -4227,8 +4553,8 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
*
* Libraries, JavaScript, CSS and other types of custom structures are attached
* to elements using the #attached property. The #attached property is an
* associative array, where the keys are the the attachment types and the values
* are the attached data. For example:
* associative array, where the keys are the attachment types and the values are
* the attached data. For example:
* @code
* $build['#attached'] = array(
* 'js' => array(drupal_get_path('module', 'taxonomy') . '/taxonomy.js'),
......@@ -4236,7 +4562,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
* );
* @endcode
*
* 'js', 'css', and 'library' are types that get special handling. For any
* 'js', 'css', and 'library' are types that get special handling. For any
* other kind of attached data, the array key must be the full name of the
* callback function and each value an array of arguments. For example:
* @code
......@@ -4587,16 +4913,16 @@ function drupal_get_library($module, $name = NULL) {
}
/**
* Assist in adding the tableDrag JavaScript behavior to a themed table.
* Assists in adding the tableDrag JavaScript behavior to a themed table.
*
* Draggable tables should be used wherever an outline or list of sortable items
* needs to be arranged by an end-user. Draggable tables are very flexible and
* can manipulate the value of form elements placed within individual columns.
*
* To set up a table to use drag and drop in place of weight select-lists or
* in place of a form that contains parent relationships, the form must be
* themed into a table. The table must have an id attribute set. If using
* theme_table(), the id may be set as such:
* To set up a table to use drag and drop in place of weight select-lists or in
* place of a form that contains parent relationships, the form must be themed
* into a table. The table must have an ID attribute set. If using
* theme_table(), the ID may be set as follows:
* @code
* $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'my-module-table')));
* return $output;
......@@ -4611,8 +4937,8 @@ function drupal_get_library($module, $name = NULL) {
* $form['my_elements'][$delta]['weight']['#attributes']['class'] = array('my-elements-weight');
* @endcode
*
* Each row of the table must also have a class of "draggable" in order to enable the
* drag handles:
* Each row of the table must also have a class of "draggable" in order to
* enable the drag handles:
* @code
* $row = array(...);
* $rows[] = array(
......@@ -4632,8 +4958,8 @@ function drupal_get_library($module, $name = NULL) {
* @endcode
*
* In a more complex case where there are several groups in one column (such as
* the block regions on the admin/structure/block page), a separate subgroup class
* must also be added to differentiate the groups.
* the block regions on the admin/structure/block page), a separate subgroup
* class must also be added to differentiate the groups.
* @code
* $form['my_elements'][$region][$delta]['weight']['#attributes']['class'] = array('my-elements-weight', 'my-elements-weight-' . $region);
* @endcode
......@@ -4650,14 +4976,14 @@ function drupal_get_library($module, $name = NULL) {
*
* In a situation where tree relationships are present, adding multiple
* subgroups is not necessary, because the table will contain indentations that
* provide enough information about the sibling and parent relationships.
* See theme_menu_overview_form() for an example creating a table containing
* parent relationships.
*
* Please note that this function should be called from the theme layer, such as
* in a .tpl.php file, theme_ function, or in a template_preprocess function,
* not in a form declaration. Though the same JavaScript could be added to the
* page using drupal_add_js() directly, this function helps keep template files
* provide enough information about the sibling and parent relationships. See
* theme_menu_overview_form() for an example creating a table containing parent
* relationships.
*
* Note that this function should be called from the theme layer, such as in a
* .tpl.php file, theme_ function, or in a template_preprocess function, not in
* a form declaration. Though the same JavaScript could be added to the page
* using drupal_add_js() directly, this function helps keep template files
* clean and readable. It also prevents tabledrag.js from being added twice
* accidentally.
*
......@@ -4730,8 +5056,8 @@ function drupal_add_tabledrag($table_id, $action, $relationship, $group, $subgro
* $files while the value is the cache file name. The cache file is generated
* in two cases. First, if there is no file name value for the key, which will
* happen if a new file name has been added to $files or after the lookup
* variable is emptied to force a rebuild of the cache. Second, the cache
* file is generated if it is missing on disk. Old cache files are not deleted
* variable is emptied to force a rebuild of the cache. Second, the cache file
* is generated if it is missing on disk. Old cache files are not deleted
* immediately when the lookup variable is emptied, but are deleted after a set
* period by drupal_delete_file_if_stale(). This ensures that files referenced
* by a cached page will still be available.
......@@ -4746,7 +5072,13 @@ function drupal_build_js_cache($files) {
$contents = '';
$uri = '';
$map = variable_get('drupal_js_cache_files', array());
$key = hash('sha256', serialize($files));
// Create a new array so that only the file names are used to create the hash.
// This prevents new aggregates from being created unnecessarily.
$js_data = array();
foreach ($files as $file) {
$js_data[] = $file['data'];
}
$key = hash('sha256', serialize($js_data));
if (isset($map[$key])) {
$uri = $map[$key];
}
......@@ -4797,14 +5129,29 @@ function drupal_clear_js_cache() {
/**
* Converts a PHP variable into its JavaScript equivalent.
*
* We use HTML-safe strings, i.e. with <, > and & escaped.
* We use HTML-safe strings, with several characters escaped.
*
* @see drupal_json_decode()
* @see drupal_json_encode_helper()
* @ingroup php_wrappers
*/
function drupal_json_encode($var) {
// json_encode() does not escape <, > and &, so we do it with str_replace().
return str_replace(array('<', '>', '&'), array('\u003c', '\u003e', '\u0026'), json_encode($var));
// The PHP version cannot change within a request.
static $php530;
if (!isset($php530)) {
$php530 = version_compare(PHP_VERSION, '5.3.0', '>=');
}
if ($php530) {
// Encode <, >, ', &, and " using the json_encode() options parameter.
return json_encode($var, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT);
}
// json_encode() escapes <, >, ', &, and " using its options parameter, but
// does not support this parameter prior to PHP 5.3.0. Use a helper instead.
include_once DRUPAL_ROOT . '/includes/json-encode.inc';
return drupal_json_encode_helper($var);
}
/**
......@@ -4818,7 +5165,7 @@ function drupal_json_decode($var) {
}
/**
* Return data in JSON format.
* Returns data in JSON format.
*
* This function should be used for JavaScript callback functions returning
* data in JSON format. It sets the header for JavaScript output.
......@@ -4836,44 +5183,43 @@ function drupal_json_output($var = NULL) {
}
/**
* Get 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;
}
/**
* Ensure the private key variable used to generate tokens is set.
* Ensures the private key variable used to generate tokens is set.
*
* @return
* The private key.
*/
function drupal_get_private_key() {
if (!($key = variable_get('drupal_private_key', 0))) {
$key = drupal_hash_base64(drupal_random_bytes(55));
$key = drupal_random_key();
variable_set('drupal_private_key', $key);
}
return $key;
}
/**
* Generate a token based on $value, the current user session and private key.
* Generates a token based on $value, the user session, and the private key.
*
* @param $value
* An additional value to base the token on.
*
* The generated token is based on the session ID of the current user. Normally,
* anonymous users do not have a session, so the generated token will be
* different on every page request. To generate a token for users without a
* session, manually start a session prior to calling this function.
*
* @return string
* A 43-character URL-safe token for validation, based on the user session ID,
* the hash salt provided from drupal_get_hash_salt(), and the
* 'drupal_private_key' configuration variable.
*
* @see drupal_get_hash_salt()
*/
function drupal_get_token($value = '') {
return drupal_hmac_base64($value, session_id() . drupal_get_private_key() . drupal_get_hash_salt());
}
/**
* Validate a token based on $value, the current user session and private key.
* Validates a token based on $value, the user session, and the private key.
*
* @param $token
* The token to be validated.
......@@ -4881,13 +5227,14 @@ function drupal_get_token($value = '') {
* An additional value to base the token on.
* @param $skip_anonymous
* Set to true to skip token validation for anonymous users.
*
* @return
* True for a valid token, false for an invalid token. When $skip_anonymous
* is true, the return value will always be true for anonymous users.
*/
function drupal_valid_token($token, $value = '', $skip_anonymous = FALSE) {
global $user;
return (($skip_anonymous && $user->uid == 0) || ($token == drupal_get_token($value)));
return (($skip_anonymous && $user->uid == 0) || ($token === drupal_get_token($value)));
}
function _drupal_bootstrap_full() {
......@@ -4918,8 +5265,17 @@ function _drupal_bootstrap_full() {
fix_gpc_magic();
// Load all enabled modules
module_load_all();
// Reset drupal_alter() and module_implements() static caches as these
// include implementations for vital modules only when called early on
// in the bootstrap.
drupal_static_reset('drupal_alter');
drupal_static_reset('module_implements');
// Make sure all stream wrappers are registered.
file_get_stream_wrappers();
// Ensure mt_rand is reseeded, to prevent random values from one page load
// being exploited to predict random values in subsequent page loads.
$seed = unpack("L", drupal_random_bytes(4));
mt_srand($seed[1]);
$test_info = &$GLOBALS['drupal_test_info'];
if (!empty($test_info['in_child_site'])) {
......@@ -4949,7 +5305,7 @@ function _drupal_bootstrap_full() {
}
/**
* Store the current page in the cache.
* Stores the current page in the cache.
*
* If page_compression is enabled, a gzipped version of the page is stored in
* the cache to avoid compressing the output on each request. The cache entry
......@@ -4957,7 +5313,7 @@ function _drupal_bootstrap_full() {
* client without gzip support.
*
* Page compression requires the PHP zlib extension
* (http://php.net/manual/en/ref.zlib.php).
* (http://php.net/manual/ref.zlib.php).
*
* @see drupal_page_header()
*/
......@@ -4965,6 +5321,10 @@ function drupal_page_set_cache() {
global $base_root;
if (drupal_page_is_cacheable()) {
// Check whether the current page might be compressed.
$page_compressed = variable_get('page_compression', TRUE) && extension_loaded('zlib');
$cache = (object) array(
'cid' => $base_root . request_uri(),
'data' => array(
......@@ -4972,6 +5332,9 @@ function drupal_page_set_cache() {
'body' => ob_get_clean(),
'title' => drupal_get_title(),
'headers' => array(),
// We need to store whether page was compressed or not,
// because by the time it is read, the configuration might change.
'page_compressed' => $page_compressed,
),
'expire' => CACHE_TEMPORARY,
'created' => REQUEST_TIME,
......@@ -4989,7 +5352,7 @@ function drupal_page_set_cache() {
}
if ($cache->data['body']) {
if (variable_get('page_compression', TRUE) && extension_loaded('zlib')) {
if ($page_compressed) {
$cache->data['body'] = gzencode($cache->data['body'], 9, FORCE_GZIP);
}
cache_set($cache->cid, $cache->data, 'cache_page', $cache->expire);
......@@ -5001,16 +5364,17 @@ function drupal_page_set_cache() {
/**
* Executes a cron run when called.
*
* Do not call this function from test, use $this->cronRun() instead.
* Do not call this function from a test. Use $this->cronRun() instead.
*
* @return
* Returns TRUE if ran successfully
* @return bool
* TRUE if cron ran successfully and FALSE if cron is already running.
*/
function drupal_cron_run() {
// Allow execution to continue even if the request gets canceled.
@ignore_user_abort(TRUE);
// Prevent session information from being saved while cron is running.
$original_session_saving = drupal_save_session();
drupal_save_session(FALSE);
// Force the current user to anonymous to ensure consistent permissions on
......@@ -5037,8 +5401,6 @@ function drupal_cron_run() {
foreach ($queues as $queue_name => $info) {
DrupalQueue::get($queue_name)->createQueue();
}
// Register shutdown callback
drupal_register_shutdown_function('drupal_cron_cleanup');
// Iterate through the modules calling their cron handlers (if any):
foreach (module_implements('cron') as $module) {
......@@ -5051,7 +5413,7 @@ function drupal_cron_run() {
}
}
// Record cron time
// Record cron time.
variable_set('cron_last', REQUEST_TIME);
watchdog('cron', 'Cron run completed.', array(), WATCHDOG_NOTICE);
......@@ -5063,30 +5425,47 @@ function drupal_cron_run() {
}
foreach ($queues as $queue_name => $info) {
$function = $info['worker callback'];
if (!empty($info['skip on cron'])) {
// Do not run if queue wants to skip.
continue;
}
$callback = $info['worker callback'];
$end = time() + (isset($info['time']) ? $info['time'] : 15);
$queue = DrupalQueue::get($queue_name);
while (time() < $end && ($item = $queue->claimItem())) {
$function($item->data);
$queue->deleteItem($item);
try {
call_user_func($callback, $item->data);
$queue->deleteItem($item);
}
catch (Exception $e) {
// In case of exception log it and leave the item in the queue
// to be processed again later.
watchdog_exception('cron', $e);
}
}
}
// Restore the user.
$GLOBALS['user'] = $original_user;
drupal_save_session(TRUE);
drupal_save_session($original_session_saving);
return $return;
}
/**
* Shutdown function for cron cleanup.
* DEPRECATED: Shutdown function: Performs cron cleanup.
*
* This function is deprecated because the 'cron_semaphore' variable it
* references no longer exists. It is therefore no longer used as a shutdown
* function by Drupal core.
*
* @deprecated
*/
function drupal_cron_cleanup() {
// See if the semaphore is still locked.
if (variable_get('cron_semaphore', FALSE)) {
watchdog('cron', 'Cron run exceeded the time limit and was aborted.', array(), WATCHDOG_WARNING);
// Release cron semaphore
// Release cron semaphore.
variable_del('cron_semaphore');
}
}
......@@ -5102,7 +5481,7 @@ function drupal_cron_cleanup() {
* drupal_system_listing("/\.module$/", "modules", 'name', 0);
* @endcode
* this function will search the site-wide modules directory (i.e., /modules/),
* your install profile's directory (i.e.,
* your installation profile's directory (i.e.,
* /profiles/your_site_profile/modules/), the all-sites directory (i.e.,
* /sites/all/modules/), and your site-specific directory (i.e.,
* /sites/your_site_dir/modules/), in that order, and return information about
......@@ -5142,8 +5521,6 @@ function drupal_cron_cleanup() {
function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1) {
$config = conf_path();
$profile = drupal_get_profile();
$searchdir = array($directory);
$files = array();
......@@ -5151,18 +5528,34 @@ function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1)
// themes as organized by a distribution. It is pristine in the same way
// that /modules is pristine for core; users should avoid changing anything
// there in favor of sites/all or sites/<domain> directories.
if (file_exists("profiles/$profile/$directory")) {
$searchdir[] = "profiles/$profile/$directory";
$profiles = array();
$profile = drupal_get_profile();
// For SimpleTest to be able to test modules packaged together with a
// distribution we need to include the profile of the parent site (in which
// test runs are triggered).
if (drupal_valid_test_ua()) {
$testing_profile = variable_get('simpletest_parent_profile', FALSE);
if ($testing_profile && $testing_profile != $profile) {
$profiles[] = $testing_profile;
}
}
// In case both profile directories contain the same extension, the actual
// profile always has precedence.
$profiles[] = $profile;
foreach ($profiles as $profile) {
if (file_exists("profiles/$profile/$directory")) {
$searchdir[] = "profiles/$profile/$directory";
}
}
// Always search sites/all/* as well as the global directories
// Always search sites/all/* as well as the global directories.
$searchdir[] = 'sites/all/' . $directory;
if (file_exists("$config/$directory")) {
$searchdir[] = "$config/$directory";
}
// Get current list of items
// Get current list of items.
if (!function_exists('file_scan_directory')) {
require_once DRUPAL_ROOT . '/includes/file.inc';
}
......@@ -5198,7 +5591,7 @@ function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1)
}
/**
* Set the main page content value for later use.
* Sets the main page content value for later use.
*
* Given the nature of the Drupal page handling, this will be called once with
* a string or array. We store that and return it later as the block is being
......@@ -5206,6 +5599,7 @@ function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1)
*
* @param $content
* A string or renderable array representing the body of the page.
*
* @return
* If called without $content, a renderable array representing the body of
* the page.
......@@ -5377,7 +5771,7 @@ function drupal_pre_render_link($element) {
* @code
* $node->content['links'] = array(
* '#theme' => 'links__node',
* '#pre_render' = array('drupal_pre_render_links'),
* '#pre_render' => array('drupal_pre_render_links'),
* 'comment' => array(
* '#theme' => 'links__node__comment',
* '#links' => array(
......@@ -5456,13 +5850,13 @@ function drupal_pre_render_links($element) {
* Note that if also a #theme is defined for the element, then the result of
* the theme callback will override #children.
*
* @see drupal_render()
*
* @param $elements
* A structured array using the #markup key.
*
* @return
* The passed-in elements, but #markup appended to #children.
*
* @see drupal_render()
*/
function drupal_pre_render_markup($elements) {
$elements['#children'] = $elements['#markup'];
......@@ -5475,8 +5869,10 @@ function drupal_pre_render_markup($elements) {
* @param $page
* A string or array representing the content of a page. The array consists of
* the following keys:
* - #type: Value is always 'page'. This pushes the theming through page.tpl.php (required).
* - #show_messages: Suppress drupal_get_message() items. Used by Batch API (optional).
* - #type: Value is always 'page'. This pushes the theming through
* page.tpl.php (required).
* - #show_messages: Suppress drupal_get_message() items. Used by Batch
* API (optional).
*
* @see hook_page_alter()
* @see element_info()
......@@ -5516,15 +5912,25 @@ function drupal_render_page($page) {
*
* Recursively iterates over each of the array elements, generating HTML code.
*
* HTML generation is controlled by two properties containing theme functions,
* #theme and #theme_wrappers.
* Renderable arrays have two kinds of key/value pairs: properties and
* children. Properties have keys starting with '#' and their values influence
* how the array will be rendered. Children are all elements whose keys do not
* start with a '#'. Their values should be renderable arrays themselves,
* which will be rendered during the rendering of the parent array. The markup
* provided by the children is typically inserted into the markup generated by
* the parent array.
*
* HTML generation for a renderable array, and the treatment of any children,
* is controlled by two properties containing theme functions, #theme and
* #theme_wrappers.
*
* #theme is the theme function called first. If it is set and the element has
* any children, they have to be rendered there. For elements that are not
* allowed to have any children, e.g. buttons or textfields, it can be used to
* render the element itself. If #theme is not present and the element has
* children, they are rendered and concatenated into a string by
* drupal_render_children().
* any children, it is the responsibility of the theme function to render
* these children. For elements that are not allowed to have any children,
* e.g. buttons or textfields, the theme function can be used to render the
* element itself. If #theme is not present and the element has children, each
* child is itself rendered by a call to drupal_render(), and the results are
* concatenated.
*
* The #theme_wrappers property contains an array of theme functions which will
* be called, in order, after #theme has run. These can be used to add further
......@@ -5544,20 +5950,20 @@ function drupal_render_page($page) {
* drupal_render() can optionally cache the rendered output of elements to
* improve performance. To use drupal_render() caching, set the element's #cache
* property to an associative array with one or several of the following keys:
* - 'keys': An array of one or more keys that identify the element. If 'keys'
* is set, the cache ID is created automatically from these keys. See
* drupal_render_cid_create().
* - 'granularity' (optional): Define the cache granularity using binary
* combinations of the cache granularity constants, e.g. DRUPAL_CACHE_PER_USER
* to cache for each user separately or
* DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE to cache separately for each
* page and role. If not specified the element is cached globally for each
* theme and language.
* - 'cid': Specify the cache ID directly. Either 'keys' or 'cid' is required.
* If 'cid' is set, 'keys' and 'granularity' are ignored. Use only if you
* have special requirements.
* - 'expire': Set to one of the cache lifetime constants.
* - 'bin': Specify a cache bin to cache the element in. Defaults to 'cache'.
* - 'keys': An array of one or more keys that identify the element. If 'keys'
* is set, the cache ID is created automatically from these keys. See
* drupal_render_cid_create().
* - 'granularity' (optional): Define the cache granularity using binary
* combinations of the cache granularity constants, e.g.
* DRUPAL_CACHE_PER_USER to cache for each user separately or
* DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE to cache separately for each
* page and role. If not specified the element is cached globally for each
* theme and language.
* - 'cid': Specify the cache ID directly. Either 'keys' or 'cid' is required.
* If 'cid' is set, 'keys' and 'granularity' are ignored. Use only if you
* have special requirements.
* - 'expire': Set to one of the cache lifetime constants.
* - 'bin': Specify a cache bin to cache the element in. Defaults to 'cache'.
*
* This function is usually called from within another function, like
* drupal_get_form() or a theme function. Elements are sorted internally
......@@ -5570,27 +5976,31 @@ function drupal_render_page($page) {
* array to be rendered independently and prevents them from being rendered
* more than once on subsequent calls to drupal_render() (e.g., as part of a
* larger array). If the same array or array element is passed more than once
* to drupal_render(), it simply returns a NULL value.
* to drupal_render(), it simply returns an empty string.
*
* @param $elements
* @param array $elements
* The structured array describing the data to be rendered.
* @return
*
* @return string
* The rendered HTML.
*/
function drupal_render(&$elements) {
// Early-return nothing if user does not have access.
if (empty($elements) || (isset($elements['#access']) && !$elements['#access'])) {
return;
return '';
}
// Do not print elements twice.
if (!empty($elements['#printed'])) {
return;
return '';
}
// Try to fetch the element's markup from cache and return.
if (isset($elements['#cache']) && $cached_output = drupal_render_cache_get($elements)) {
return $cached_output;
if (isset($elements['#cache'])) {
$cached_output = drupal_render_cache_get($elements);
if ($cached_output !== FALSE) {
return $cached_output;
}
}
// If #markup is set, ensure #type is set. This allows to specify just #markup
......@@ -5618,7 +6028,7 @@ function drupal_render(&$elements) {
// Allow #pre_render to abort rendering.
if (!empty($elements['#printed'])) {
return;
return '';
}
// Get the children of the element, sorted by weight.
......@@ -5687,16 +6097,18 @@ function drupal_render(&$elements) {
}
/**
* Render children of an element and concatenate them.
*
* This renders all children of an element using drupal_render() and then
* joins them together into a single string.
* Renders children of an element and concatenates them.
*
* @param $element
* @param array $element
* The structured array whose children shall be rendered.
* @param $children_keys
* If the keys of the element's children are already known, they can be passed
* in to save another run of element_children().
* @param array $children_keys
* (optional) If the keys of the element's children are already known, they
* can be passed in to save another run of element_children().
*
* @return string
* The rendered HTML of all children of the element.
* @see drupal_render()
*/
function drupal_render_children(&$element, $children_keys = NULL) {
if ($children_keys === NULL) {
......@@ -5712,7 +6124,7 @@ function drupal_render_children(&$element, $children_keys = NULL) {
}
/**
* Render an element.
* Renders an element.
*
* This function renders an element using drupal_render(). The top level
* element is shown with show() before rendering, so it will always be rendered
......@@ -5741,7 +6153,7 @@ function render(&$element) {
}
/**
* Hide an element from later rendering.
* Hides an element from later rendering.
*
* The first time render() or drupal_render() is called on an element tree,
* as each element in the tree is rendered, it is marked with a #printed flag
......@@ -5767,7 +6179,7 @@ function hide(&$element) {
}
/**
* Show a hidden element for later rendering.
* Shows a hidden element for later rendering.
*
* You can also use render($element), which shows the element while rendering
* it.
......@@ -5796,16 +6208,17 @@ function show(&$element) {
}
/**
* Get the rendered output of a renderable element from cache.
*
* @see drupal_render()
* @see drupal_render_cache_set()
* Gets the rendered output of a renderable element from the cache.
*
* @param $elements
* A renderable array.
*
* @return
* A markup string containing the rendered content of the element, or FALSE
* if no cached copy of the element is available.
*
* @see drupal_render()
* @see drupal_render_cache_set()
*/
function drupal_render_cache_get($elements) {
if (!in_array($_SERVER['REQUEST_METHOD'], array('GET', 'HEAD')) || !$cid = drupal_render_cid_create($elements)) {
......@@ -5826,17 +6239,17 @@ function drupal_render_cache_get($elements) {
}
/**
* Cache the rendered output of a renderable element.
* Caches the rendered output of a renderable element.
*
* This is called by drupal_render() if the #cache property is set on an element.
*
* @see drupal_render()
* @see drupal_render_cache_get()
* This is called by drupal_render() if the #cache property is set on an
* element.
*
* @param $markup
* The rendered output string of $elements.
* @param $elements
* A renderable array.
*
* @see drupal_render_cache_get()
*/
function drupal_render_cache_set(&$markup, $elements) {
// Create the cache ID for the element.
......@@ -5862,7 +6275,7 @@ function drupal_render_cache_set(&$markup, $elements) {
}
/**
* Collect #attached for an element and all child elements into a single array.
* Collects #attached for an element and its children into a single array.
*
* When caching elements, it is necessary to collect all libraries, JavaScript
* and CSS into a single array, from both the element itself and all child
......@@ -5905,9 +6318,10 @@ function drupal_render_collect_attached($elements, $return = FALSE) {
}
/**
* Prepare an element for caching based on a query. This smart caching strategy
* saves Drupal from querying and rendering to HTML when the underlying query is
* unchanged.
* Prepares an element for caching based on a query.
*
* This smart caching strategy saves Drupal from querying and rendering to HTML
* when the underlying query is unchanged.
*
* Expensive queries should use the query builder to create the query and then
* call this function. Executing the query and formatting results should happen
......@@ -5945,12 +6359,15 @@ function drupal_render_cache_by_query($query, $function, $expire = CACHE_TEMPORA
}
/**
* Helper function for building cache ids.
* Returns cache ID parts for building a cache ID.
*
* @param $granularity
* One or more cache granularity constants, e.g. DRUPAL_CACHE_PER_USER to cache
* for each user separately or DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE to
* cache separately for each page and role.
* One or more cache granularity constants. For example, to cache separately
* for each user, use DRUPAL_CACHE_PER_USER. To cache separately for each
* page and role, use the expression:
* @code
* DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE
* @endcode
*
* @return
* An array of cache ID parts, always containing the active theme. If the
......@@ -5970,13 +6387,21 @@ function drupal_render_cid_parts($granularity = NULL) {
}
if (!empty($granularity)) {
$cache_per_role = $granularity & DRUPAL_CACHE_PER_ROLE;
$cache_per_user = $granularity & DRUPAL_CACHE_PER_USER;
// User 1 has special permissions outside of the role system, so when
// caching per role is requested, it should cache per user instead.
if ($user->uid == 1 && $cache_per_role) {
$cache_per_user = TRUE;
$cache_per_role = FALSE;
}
// 'PER_ROLE' and 'PER_USER' are mutually exclusive. 'PER_USER' can be a
// resource drag for sites with many users, so when a module is being
// equivocal, we favor the less expensive 'PER_ROLE' pattern.
if ($granularity & DRUPAL_CACHE_PER_ROLE) {
if ($cache_per_role) {
$cid_parts[] = 'r.' . implode(',', array_keys($user->roles));
}
elseif ($granularity & DRUPAL_CACHE_PER_USER) {
elseif ($cache_per_user) {
$cid_parts[] = "u.$user->uid";
}
......@@ -5989,7 +6414,7 @@ function drupal_render_cid_parts($granularity = NULL) {
}
/**
* Create the cache ID for a renderable element.
* Creates the cache ID for a renderable element.
*
* This creates the cache ID string, either by returning the #cache['cid']
* property if present or by building the cache ID out of the #cache['keys']
......@@ -6036,7 +6461,7 @@ function element_sort_by_title($a, $b) {
}
/**
* Retrieve the default properties for the defined element type.
* Retrieves the default properties for the defined element type.
*
* @param $type
* An element type as defined by hook_element_info().
......@@ -6062,7 +6487,7 @@ function element_info($type) {
}
/**
* Retrieve a single property for the defined element type.
* Retrieves a single property for the defined element type.
*
* @param $type
* An element type as defined by hook_element_info().
......@@ -6077,7 +6502,19 @@ function element_info_property($type, $property_name, $default = NULL) {
}
/**
* Function used by uasort to sort structured arrays by weight, without the property weight prefix.
* Sorts a structured array by the 'weight' element.
*
* Note that the sorting is by the 'weight' array element, not by the render
* element property '#weight'.
*
* Callback for uasort() used in various functions.
*
* @param $a
* First item for comparison. The compared items should be associative arrays
* that optionally include a 'weight' element. For items without a 'weight'
* element, a default value of 0 will be used.
* @param $b
* Second item for comparison.
*/
function drupal_sort_weight($a, $b) {
$a_weight = (is_array($a) && isset($a['weight'])) ? $a['weight'] : 0;
......@@ -6102,33 +6539,37 @@ function drupal_sort_title($a, $b) {
}
/**
* Check if the key is a property.
* Checks if the key is a property.
*/
function element_property($key) {
return $key[0] == '#';
}
/**
* Get properties of a structured array element. Properties begin with '#'.
* Gets properties of a structured array element (keys beginning with '#').
*/
function element_properties($element) {
return array_filter(array_keys((array) $element), 'element_property');
}
/**
* Check if the key is a child.
* Checks if the key is a child.
*/
function element_child($key) {
return !isset($key[0]) || $key[0] != '#';
}
/**
* Return the children of an element, optionally sorted by weight.
* Identifies the children of an element array, optionally sorted by weight.
*
* The children of a element array are those key/value pairs whose key does
* not start with a '#'. See drupal_render() for details.
*
* @param $elements
* The element to be sorted.
* The element array whose children are to be identified.
* @param $sort
* Boolean to indicate whether the children should be sorted by weight.
*
* @return
* The array keys of the element's children.
*/
......@@ -6168,6 +6609,7 @@ function element_children(&$elements, $sort = FALSE) {
*
* @param $elements
* The parent element.
*
* @return
* The array keys of the element's visible children.
*/
......@@ -6218,6 +6660,44 @@ function element_set_attributes(array &$element, array $map) {
}
}
/**
* Recursively computes the difference of arrays with additional index check.
*
* This is a version of array_diff_assoc() that supports multidimensional
* arrays.
*
* @param array $array1
* The array to compare from.
* @param array $array2
* The array to compare to.
*
* @return array
* Returns an array containing all the values from array1 that are not present
* in array2.
*/
function drupal_array_diff_assoc_recursive($array1, $array2) {
$difference = array();
foreach ($array1 as $key => $value) {
if (is_array($value)) {
if (!array_key_exists($key, $array2) || !is_array($array2[$key])) {
$difference[$key] = $value;
}
else {
$new_diff = drupal_array_diff_assoc_recursive($value, $array2[$key]);
if (!empty($new_diff)) {
$difference[$key] = $new_diff;
}
}
}
elseif (!array_key_exists($key, $array2) || $array2[$key] !== $value) {
$difference[$key] = $value;
}
}
return $difference;
}
/**
* Sets a value in a nested array with variable depth.
*
......@@ -6311,10 +6791,10 @@ function drupal_array_set_nested_value(array &$array, array $parents, $value, $f
* $value = drupal_array_get_nested_value($form, $parents);
* @endcode
*
* The return value will be NULL, regardless of whether the actual value is NULL
* or whether the requested key does not exist. If it is required to know
* whether the nested array key actually exists, pass a third argument that is
* altered by reference:
* A return value of NULL is ambiguous, and can mean either that the requested
* key does not exist, or that the actual value is NULL. If it is required to
* know whether the nested array key actually exists, pass a third argument that
* is altered by reference:
* @code
* $key_exists = NULL;
* $value = drupal_array_get_nested_value($form, $parents, $key_exists);
......@@ -6346,7 +6826,7 @@ function drupal_array_set_nested_value(array &$array, array $parents, $value, $f
*
* @see drupal_array_set_nested_value()
*/
function drupal_array_get_nested_value(array &$array, array $parents, &$key_exists = NULL) {
function &drupal_array_get_nested_value(array &$array, array $parents, &$key_exists = NULL) {
$ref = &$array;
foreach ($parents as $parent) {
if (is_array($ref) && array_key_exists($parent, $ref)) {
......@@ -6354,7 +6834,8 @@ function drupal_array_get_nested_value(array &$array, array $parents, &$key_exis
}
else {
$key_exists = FALSE;
return NULL;
$null = NULL;
return $null;
}
}
$key_exists = TRUE;
......@@ -6362,7 +6843,7 @@ function drupal_array_get_nested_value(array &$array, array $parents, &$key_exis
}
/**
* Determines whether a nested array with variable depth contains all of the requested keys.
* Determines whether a nested array contains the requested keys.
*
* This helper function should be used when the depth of the array element to be
* checked may vary (that is, the number of parent keys is variable). See
......@@ -6398,11 +6879,11 @@ function drupal_array_nested_key_exists(array $array, array $parents) {
}
/**
* Provide theme registration for themes across .inc files.
* Provides theme registration for themes across .inc files.
*/
function drupal_common_theme() {
return array(
// theme.inc
// From theme.inc.
'html' => array(
'render element' => 'page',
'template' => 'html',
......@@ -6478,7 +6959,7 @@ function drupal_common_theme() {
'html_tag' => array(
'render element' => 'element',
),
// from theme.maintenance.inc
// From theme.maintenance.inc.
'maintenance_page' => array(
'variables' => array('content' => NULL, 'show_messages' => TRUE),
'template' => 'maintenance-page',
......@@ -6498,7 +6979,7 @@ function drupal_common_theme() {
'authorize_report' => array(
'variables' => array('messages' => array()),
),
// from pager.inc
// From pager.inc.
'pager' => array(
'variables' => array('tags' => array(), 'element' => 0, 'parameters' => array(), 'quantity' => 9),
),
......@@ -6517,7 +6998,7 @@ function drupal_common_theme() {
'pager_link' => array(
'variables' => array('text' => NULL, 'page_new' => NULL, 'element' => NULL, 'parameters' => array(), 'attributes' => array()),
),
// from menu.inc
// From menu.inc.
'menu_link' => array(
'render element' => 'element',
),
......@@ -6533,7 +7014,7 @@ function drupal_common_theme() {
'menu_local_tasks' => array(
'variables' => array('primary' => array(), 'secondary' => array()),
),
// from form.inc
// From form.inc.
'select' => array(
'render element' => 'element',
),
......@@ -6604,12 +7085,12 @@ function drupal_common_theme() {
}
/**
* @ingroup schemaapi
* @addtogroup schemaapi
* @{
*/
/**
* Creates all tables in a module's hook_schema() implementation.
* Creates all tables defined in a module's hook_schema().
*
* Note: This function does not pass the module's schema through
* hook_schema_alter(). The module's tables will be created exactly as the
......@@ -6628,7 +7109,7 @@ function drupal_install_schema($module) {
}
/**
* Remove all tables that a module defines in its hook_schema().
* Removes all tables defined in a module's hook_schema().
*
* Note: This function does not pass the module's schema through
* hook_schema_alter(). The module's tables will be created exactly as the
......@@ -6636,6 +7117,7 @@ function drupal_install_schema($module) {
*
* @param $module
* The module for which the tables will be removed.
*
* @return
* An array of arrays with the following key/value pairs:
* - success: a boolean indicating whether the query succeeded.
......@@ -6659,7 +7141,8 @@ function drupal_uninstall_schema($module) {
* specification of a schema, as it was defined in a module's
* hook_schema(). No additional default values will be set,
* hook_schema_alter() is not invoked and these unprocessed
* definitions won't be cached.
* definitions won't be cached. To retrieve the schema after
* hook_schema_alter() has been invoked use drupal_get_schema().
*
* This function can be used to retrieve a schema specification in
* hook_schema(), so it allows you to derive your tables from existing
......@@ -6691,7 +7174,7 @@ function drupal_get_schema_unprocessed($module, $table = NULL) {
}
/**
* Fill in required default values for table definitions returned by hook_schema().
* Fills in required default values for table definitions from hook_schema().
*
* @param $schema
* The schema definition array as it was returned by the module's
......@@ -6722,7 +7205,27 @@ function _drupal_schema_initialize(&$schema, $module, $remove_descriptions = TRU
}
/**
* Retrieve a list of fields from a table schema. The list is suitable for use in a SQL query.
* Retrieves the type for every field in a table schema.
*
* @param $table
* The name of the table from which to retrieve type information.
*
* @return
* An array of types, keyed by field name.
*/
function drupal_schema_field_types($table) {
$table_schema = drupal_get_schema($table);
$field_types = array();
foreach ($table_schema['fields'] as $field_name => $field_info) {
$field_types[$field_name] = isset($field_info['type']) ? $field_info['type'] : NULL;
}
return $field_types;
}
/**
* Retrieves a list of fields from a table schema.
*
* The returned list is suitable for use in an SQL query.
*
* @param $table
* The name of the table from which to retrieve fields.
......@@ -6730,7 +7233,7 @@ function _drupal_schema_initialize(&$schema, $module, $remove_descriptions = TRU
* An optional prefix to to all fields.
*
* @return An array of fields.
**/
*/
function drupal_schema_fields_sql($table, $prefix = NULL) {
$schema = drupal_get_schema($table);
$fields = array_keys($schema['fields']);
......@@ -6749,6 +7252,10 @@ function drupal_schema_fields_sql($table, $prefix = NULL) {
/**
* Saves (inserts or updates) a record to the database based upon the schema.
*
* Do not use drupal_write_record() within hook_update_N() functions, since the
* database schema cannot be relied upon when a user is running a series of
* updates. Instead, use db_insert() or db_update() to save the record.
*
* @param $table
* The name of the table; this must be defined by a hook_schema()
* implementation.
......@@ -6904,7 +7411,7 @@ function drupal_write_record($table, &$record, $primary_keys = array()) {
}
/**
* @} End of "ingroup schemaapi".
* @} End of "addtogroup schemaapi".
*/
/**
......@@ -6916,7 +7423,16 @@ function drupal_write_record($table, &$record, $primary_keys = array()) {
* Information stored in a module .info file:
* - name: The real name of the module for display purposes.
* - description: A brief description of the module.
* - dependencies: An array of shortnames of other modules this module requires.
* - dependencies: An array of dependency strings. Each is in the form
* 'project:module (versions)'; with the following meanings:
* - project: (optional) Project shortname, recommended to ensure uniqueness,
* if the module is part of a project hosted on drupal.org. If omitted,
* also omit the : that follows. The project name is currently ignored by
* Drupal core but is used for automated testing.
* - module: (required) Module shortname within the project.
* - (versions): Optional version information, consisting of one or more
* comma-separated operator/value pairs or simply version numbers, which
* can contain "x" as a wildcard. Examples: (>=7.22, <7.28), (7.x-3.x).
* - package: The name of the package of modules this module belongs to.
*
* See forum.info for an example of a module .info file.
......@@ -6958,7 +7474,7 @@ function drupal_parse_info_file($filename) {
}
/**
* Parse data in Drupal's .info format.
* Parses data in Drupal's .info format.
*
* Data should be in an .ini-like format to specify values. White-space
* generally doesn't matter, except inside values:
......@@ -6988,6 +7504,7 @@ function drupal_parse_info_file($filename) {
*
* @param $data
* A string to parse.
*
* @return
* The info array.
*
......@@ -6995,7 +7512,6 @@ function drupal_parse_info_file($filename) {
*/
function drupal_parse_info_format($data) {
$info = array();
$constants = get_defined_constants();
if (preg_match_all('
@^\s* # Start at the beginning of a line, ignoring leading whitespace
......@@ -7011,19 +7527,19 @@ function drupal_parse_info_format($data) {
)\s*$ # Stop at the next end of a line, ignoring trailing whitespace
@msx', $data, $matches, PREG_SET_ORDER)) {
foreach ($matches as $match) {
// Fetch the key and value string
// Fetch the key and value string.
$i = 0;
foreach (array('key', 'value1', 'value2', 'value3') as $var) {
$$var = isset($match[++$i]) ? $match[$i] : '';
}
$value = stripslashes(substr($value1, 1, -1)) . stripslashes(substr($value2, 1, -1)) . $value3;
// Parse array syntax
// Parse array syntax.
$keys = preg_split('/\]?\[/', rtrim($key, ']'));
$last = array_pop($keys);
$parent = &$info;
// Create nested arrays
// Create nested arrays.
foreach ($keys as $key) {
if ($key == '') {
$key = count($parent);
......@@ -7035,11 +7551,11 @@ function drupal_parse_info_format($data) {
}
// Handle PHP constants.
if (isset($constants[$value])) {
$value = $constants[$value];
if (preg_match('/^\w+$/i', $value) && defined($value)) {
$value = constant($value);
}
// Insert actual value
// Insert actual value.
if ($last == '') {
$last = count($parent);
}
......@@ -7051,11 +7567,12 @@ function drupal_parse_info_format($data) {
}
/**
* Severity levels, as defined in RFC 3164: http://www.ietf.org/rfc/rfc3164.txt.
* Returns a list of severity levels, as defined in RFC 3164.
*
* @return
* Array of the possible severity levels for log messages.
*
* @see http://www.ietf.org/rfc/rfc3164.txt
* @see watchdog()
* @ingroup logging_severity_levels
*/
......@@ -7074,7 +7591,7 @@ function watchdog_severity_levels() {
/**
* Explode a string of given tags into an array.
* Explodes a string of tags into an array.
*
* @see drupal_implode_tags()
*/
......@@ -7100,7 +7617,7 @@ function drupal_explode_tags($tags) {
}
/**
* Implode an array of tags into a string.
* Implodes an array of tags into a string.
*
* @see drupal_explode_tags()
*/
......@@ -7118,7 +7635,7 @@ function drupal_implode_tags($tags) {
}
/**
* Flush all cached data on the site.
* Flushes all cached data on the site.
*
* Empties cache tables, rebuilds the menu cache and theme registries, and
* invokes a hook so that other modules' cache data can be cleared as well.
......@@ -7136,6 +7653,7 @@ function drupal_flush_all_caches() {
system_rebuild_theme_data();
drupal_theme_rebuild();
entity_info_cache_clear();
node_types_rebuild();
// node_menu() defines menu items based on node types so it needs to come
// after node types are rebuilt.
......@@ -7159,10 +7677,10 @@ function drupal_flush_all_caches() {
}
/**
* Helper function to change query-strings on css/js files.
* Changes the dummy query string added to all CSS and JavaScript files.
*
* Changes the character added to all css/js files as dummy query-string, so
* that all browsers are forced to reload fresh files.
* Changing the dummy query string appended to CSS and JavaScript files forces
* all browsers to reload fresh files.
*/
function _drupal_flush_css_js() {
// The timestamp is converted to base 36 in order to make it more compact.
......@@ -7170,7 +7688,7 @@ function _drupal_flush_css_js() {
}
/**
* Debug function used for outputting debug information.
* Outputs debug information.
*
* The debug information is passed on to trigger_error() after being converted
* to a string using _drupal_debug_message().
......@@ -7195,10 +7713,16 @@ function debug($data, $label = NULL, $print_r = FALSE) {
}
/**
* Parse a dependency for comparison by drupal_check_incompatibility().
* Parses a dependency for comparison by drupal_check_incompatibility().
*
* @param $dependency
* A dependency string, for example 'foo (>=7.x-4.5-beta5, 3.x)'.
* A dependency string, which specifies a module dependency, and optionally
* the project it comes from and versions that are supported. Supported
* formats include:
* - 'module'
* - 'project:module'
* - 'project:module (>=version, version)'
*
* @return
* An associative array with three keys:
* - 'name' includes the name of the thing to depend on (e.g. 'foo').
......@@ -7212,6 +7736,12 @@ function debug($data, $label = NULL, $print_r = FALSE) {
* @see drupal_check_incompatibility()
*/
function drupal_parse_dependency($dependency) {
$value = array();
// Split out the optional project name.
if (strpos($dependency, ':')) {
list($project_name, $dependency) = explode(':', $dependency);
$value['project'] = $project_name;
}
// We use named subpatterns and support every op that version_compare
// supports. Also, op is optional and defaults to equals.
$p_op = '(?P<operation>!=|==|=|<|<=|>|>=|<>)?';
......@@ -7220,7 +7750,6 @@ function drupal_parse_dependency($dependency) {
$p_major = '(?P<major>\d+)';
// By setting the minor version to x, branches can be matched.
$p_minor = '(?P<minor>(?:\d+|x)(?:-[A-Za-z]+\d+)?)';
$value = array();
$parts = explode('(', $dependency, 2);
$value['name'] = trim($parts[0]);
if (isset($parts[1])) {
......@@ -7252,12 +7781,13 @@ function drupal_parse_dependency($dependency) {
}
/**
* Check whether a version is compatible with a given dependency.
* Checks whether a version is compatible with a given dependency.
*
* @param $v
* The parsed dependency structure from drupal_parse_dependency().
* @param $current_version
* The version to check against (like 4.2).
*
* @return
* NULL if compatible, otherwise the original dependency version string that
* caused the incompatibility.
......@@ -7277,12 +7807,12 @@ function drupal_check_incompatibility($v, $current_version) {
/**
* Get the entity info array of an entity type.
*
* @see hook_entity_info()
* @see hook_entity_info_alter()
*
* @param $entity_type
* The entity type, e.g. node, for which the info shall be returned, or NULL
* to return an array with info about all types.
*
* @see hook_entity_info()
* @see hook_entity_info_alter()
*/
function entity_get_info($entity_type = NULL) {
global $language;
......@@ -7334,6 +7864,7 @@ function entity_get_info($entity_type = NULL) {
// Prepare entity schema fields SQL info for
// DrupalEntityControllerInterface::buildQuery().
if (isset($entity_info[$name]['base table'])) {
$entity_info[$name]['base table field types'] = drupal_schema_field_types($entity_info[$name]['base table']);
$entity_info[$name]['schema_fields_sql']['base table'] = drupal_schema_fields_sql($entity_info[$name]['base table']);
if (isset($entity_info[$name]['revision table'])) {
$entity_info[$name]['schema_fields_sql']['revision table'] = drupal_schema_fields_sql($entity_info[$name]['revision table']);
......@@ -7370,12 +7901,13 @@ function entity_info_cache_clear() {
* The entity type; e.g. 'node' or 'user'.
* @param $entity
* The entity from which to extract values.
*
* @return
* A numerically indexed array (not a hash table) containing these
* elements:
* 0: primary id of the entity
* 1: revision id of the entity, or NULL if $entity_type is not versioned
* 2: bundle name of the entity
* - 0: Primary ID of the entity.
* - 1: Revision ID of the entity, or NULL if $entity_type is not versioned.
* - 2: Bundle name of the entity, or NULL if $entity_type has no bundles.
*/
function entity_extract_ids($entity_type, $entity) {
$info = entity_get_info($entity_type);
......@@ -7408,13 +7940,12 @@ function entity_extract_ids($entity_type, $entity) {
* @param $entity_type
* The entity type; e.g. 'node' or 'user'.
* @param $ids
* A numerically indexed array, as returned by entity_extract_ids(),
* containing these elements:
* 0: primary id of the entity
* 1: revision id of the entity, or NULL if $entity_type is not versioned
* 2: bundle name of the entity, or NULL if $entity_type has no bundles
* A numerically indexed array, as returned by entity_extract_ids().
*
* @return
* An entity structure, initialized with the ids provided.
*
* @see entity_extract_ids()
*/
function entity_create_stub_entity($entity_type, $ids) {
$entity = new stdClass();
......@@ -7444,11 +7975,6 @@ function entity_create_stub_entity($entity_type, $ids) {
* DrupalDefaultEntityController class. See node_entity_info() and the
* NodeController in node.module as an example.
*
* @see hook_entity_info()
* @see DrupalEntityControllerInterface
* @see DrupalDefaultEntityController
* @see EntityFieldQuery
*
* @param $entity_type
* The entity type to load, e.g. node or user.
* @param $ids
......@@ -7466,6 +7992,11 @@ function entity_create_stub_entity($entity_type, $ids) {
* found, an empty array is returned.
*
* @todo Remove $conditions in Drupal 8.
*
* @see hook_entity_info()
* @see DrupalEntityControllerInterface
* @see DrupalDefaultEntityController
* @see EntityFieldQuery
*/
function entity_load($entity_type, $ids = FALSE, $conditions = array(), $reset = FALSE) {
if ($reset) {
......@@ -7485,7 +8016,7 @@ function entity_load($entity_type, $ids = FALSE, $conditions = array(), $reset =
* @param $entity_type
* The entity type to load, e.g. node or user.
* @param $id
* The id of the entity to load.
* The ID of the entity to load.
*
* @return
* The unchanged entity, or FALSE if the entity cannot be loaded.
......@@ -7497,7 +8028,10 @@ function entity_load_unchanged($entity_type, $id) {
}
/**
* Get the entity controller class for an entity type.
* Gets the entity controller for an entity type.
*
* @return DrupalEntityControllerInterface
* The entity controller object for the specified entity type.
*/
function entity_get_controller($entity_type) {
$controllers = &drupal_static(__FUNCTION__, array());
......@@ -7522,7 +8056,6 @@ function entity_get_controller($entity_type) {
* recursion. By convention, entity_prepare_view() is called after
* field_attach_prepare_view() to allow entity level hooks to act on content
* loaded by field API.
* @see hook_entity_prepare_view()
*
* @param $entity_type
* The type of entity, i.e. 'node', 'user'.
......@@ -7531,6 +8064,8 @@ function entity_get_controller($entity_type) {
* @param $langcode
* (optional) A language code to be used for rendering. Defaults to the global
* content language of the current request.
*
* @see hook_entity_prepare_view()
*/
function entity_prepare_view($entity_type, $entities, $langcode = NULL) {
if (!isset($langcode)) {
......@@ -7557,56 +8092,92 @@ function entity_prepare_view($entity_type, $entities, $langcode = NULL) {
}
/**
* Returns the uri elements of an entity.
* Invoke hook_entity_view_mode_alter().
*
* If adding a new entity similar to nodes, comments or users, you should invoke
* this function during the ENTITY_build_content() or ENTITY_view_multiple()
* phases of rendering to allow other modules to alter the view mode during this
* phase. This function needs to be called before field_attach_prepare_view() to
* ensure that the correct content is loaded by field API.
*
* @param $entity_type
* The type of entity, i.e. 'node', 'user'.
* @param $entities
* The entity objects which are being prepared for view, keyed by object ID.
* @param $view_mode
* The original view mode e.g. 'full', 'teaser'...
* @param $langcode
* (optional) A language code to be used for rendering. Defaults to the global
* content language of the current request.
* @return
* An associative array with arrays of entities keyed by view mode.
*
* @see hook_entity_view_mode_alter()
*/
function entity_view_mode_prepare($entity_type, $entities, $view_mode, $langcode = NULL) {
if (!isset($langcode)) {
$langcode = $GLOBALS['language_content']->language;
}
// To ensure hooks are never run after field_attach_prepare_view() only
// process items without the entity_view_prepared flag.
$entities_by_view_mode = array();
foreach ($entities as $id => $entity) {
$entity_view_mode = $view_mode;
if (empty($entity->entity_view_prepared)) {
// Allow modules to change the view mode.
$context = array(
'entity_type' => $entity_type,
'entity' => $entity,
'langcode' => $langcode,
);
drupal_alter('entity_view_mode', $entity_view_mode, $context);
}
$entities_by_view_mode[$entity_view_mode][$id] = $entity;
}
return $entities_by_view_mode;
}
/**
* Returns the URI elements of an entity.
*
* @param $entity_type
* The entity type; e.g. 'node' or 'user'.
* @param $entity
* The entity for which to generate a path.
* @return
* An array containing the 'path' and 'options' keys used to build the uri of
* An array containing the 'path' and 'options' keys used to build the URI of
* the entity, and matching the signature of url(). NULL if the entity has no
* uri of its own.
* URI of its own.
*/
function entity_uri($entity_type, $entity) {
// This check enables the URI of an entity to be easily overridden from what
// the callback for the entity type or bundle would return, and it helps
// minimize performance overhead when entity_uri() is called multiple times
// for the same entity.
if (!isset($entity->uri)) {
$info = entity_get_info($entity_type);
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
// A bundle-specific callback takes precedence over the generic one for the
// entity type.
if (isset($info['bundles'][$bundle]['uri callback'])) {
$uri_callback = $info['bundles'][$bundle]['uri callback'];
}
elseif (isset($info['uri callback'])) {
$uri_callback = $info['uri callback'];
}
else {
$uri_callback = NULL;
}
$info = entity_get_info($entity_type);
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
// Invoke the callback to get the URI. If there is no callback, set the
// entity's 'uri' property to FALSE to indicate that it is known to not have
// a URI.
if (isset($uri_callback) && function_exists($uri_callback)) {
$entity->uri = $uri_callback($entity);
if (!isset($entity->uri['options'])) {
$entity->uri['options'] = array();
}
// Pass the entity data to url() so that alter functions do not need to
// lookup this entity again.
$entity->uri['options']['entity_type'] = $entity_type;
$entity->uri['options']['entity'] = $entity;
}
else {
$entity->uri = FALSE;
}
// A bundle-specific callback takes precedence over the generic one for the
// entity type.
if (isset($info['bundles'][$bundle]['uri callback'])) {
$uri_callback = $info['bundles'][$bundle]['uri callback'];
}
elseif (isset($info['uri callback'])) {
$uri_callback = $info['uri callback'];
}
else {
return NULL;
}
// Invoke the callback to get the URI. If there is no callback, return NULL.
if (isset($uri_callback) && function_exists($uri_callback)) {
$uri = $uri_callback($entity);
// Pass the entity data to url() so that alter functions do not need to
// lookup this entity again.
$uri['options']['entity_type'] = $entity_type;
$uri['options']['entity'] = $entity;
return $uri;
}
return $entity->uri ? $entity->uri : NULL;
}
/**
......@@ -7637,7 +8208,45 @@ function entity_label($entity_type, $entity) {
}
/**
* Helper function for attaching field API validation to entity forms.
* Returns the language of an entity.
*
* @param $entity_type
* The entity type; e.g., 'node' or 'user'.
* @param $entity
* The entity for which to get the language.
*
* @return
* A valid language code or NULL if the entity has no language support.
*/
function entity_language($entity_type, $entity) {
$info = entity_get_info($entity_type);
// Invoke the callback to get the language. If there is no callback, try to
// get it from a property of the entity, otherwise NULL.
if (isset($info['language callback']) && function_exists($info['language callback'])) {
$langcode = $info['language callback']($entity_type, $entity);
}
elseif (!empty($info['entity keys']['language']) && isset($entity->{$info['entity keys']['language']})) {
$langcode = $entity->{$info['entity keys']['language']};
}
else {
// The value returned in D8 would be LANGUAGE_NONE, we cannot use it here to
// preserve backward compatibility. In fact this function has been
// introduced very late in the D7 life cycle, mainly as the proper default
// for field_attach_form(). By returning LANGUAGE_NONE when no language
// information is available, we would introduce a potentially BC-breaking
// API change, since field_attach_form() defaults to the default language
// instead of LANGUAGE_NONE. Moreover this allows us to distinguish between
// entities that have no language specified from ones that do not have
// language support at all.
$langcode = NULL;
}
return $langcode;
}
/**
* Attaches field API validation to entity forms.
*/
function entity_form_field_validate($entity_type, $form, &$form_state) {
// All field attach API functions act on an entity object, but during form
......@@ -7650,7 +8259,7 @@ function entity_form_field_validate($entity_type, $form, &$form_state) {
}
/**
* Helper function for copying submitted values to entity properties for simple entity forms.
* Copies submitted values to entity properties for simple entity forms.
*
* During the submission handling of an entity form's "Save", "Preview", and
* possibly other buttons, the form state's entity needs to be updated with the
......@@ -7776,11 +8385,12 @@ function archiver_get_extensions() {
}
/**
* Create the appropriate archiver for the specified file.
* Creates the appropriate archiver for the specified file.
*
* @param $file
* The full path of the archive file. Note that stream wrapper
* paths are supported, but not remote ones.
* The full path of the archive file. Note that stream wrapper paths are
* supported, but not remote ones.
*
* @return
* A newly created instance of the archiver class appropriate
* for the specified file, already bound to that file.
......@@ -7809,14 +8419,14 @@ function archiver_get_archiver($file) {
}
/**
* Drupal Updater registry.
* Assembles the Drupal Updater registry.
*
* An Updater is a class that knows how to update various parts of the Drupal
* file system, for example to update modules that have newer releases, or to
* install a new theme.
*
* @return
* Returns the Drupal Updater class registry.
* The Drupal Updater class registry.
*
* @see hook_updater_info()
* @see hook_updater_info_alter()
......@@ -7832,10 +8442,10 @@ function drupal_get_updaters() {
}
/**
* Drupal FileTransfer registry.
* Assembles the Drupal FileTransfer registry.
*
* @return
* Returns the Drupal FileTransfer class registry.
* The Drupal FileTransfer class registry.
*
* @see FileTransfer
* @see hook_filetransfer_info()
......