From b27aabe359a2e66e1dd9646b69437c1b90c856d0 Mon Sep 17 00:00:00 2001 From: Bachir Soussi Chiadmi Date: Wed, 16 Mar 2016 16:41:44 +0100 Subject: [PATCH] updated drupal core to 7.43 --- CHANGELOG.txt | 157 +- MAINTAINERS.txt | 212 +- UPGRADE.txt | 10 + includes/batch.inc | 4 +- includes/bootstrap.inc | 43 +- includes/cache.inc | 1 + includes/common.inc | 106 +- includes/database/database.inc | 2 +- includes/database/mysql/database.inc | 13 +- includes/database/schema.inc | 5 +- includes/database/sqlite/install.inc | 2 - includes/file.inc | 2 +- includes/form.inc | 16 +- includes/install.core.inc | 6 +- includes/install.inc | 3 +- includes/locale.inc | 6 + includes/mail.inc | 2 +- includes/menu.inc | 38 +- includes/module.inc | 76 +- includes/path.inc | 3 +- includes/registry.inc | 2 +- includes/theme.inc | 30 +- includes/update.inc | 4 + includes/xmlrpcs.inc | 8 + misc/states.js | 6 +- modules/aggregator/aggregator.info | 6 +- modules/aggregator/tests/aggregator_test.info | 6 +- modules/block/block.api.php | 25 + modules/block/block.info | 6 +- modules/block/block.js | 6 +- modules/block/block.module | 45 +- modules/block/tests/block_test.info | 6 +- .../block_test_theme/block_test_theme.info | 6 +- modules/blog/blog.info | 6 +- modules/book/book.info | 6 +- modules/color/color.info | 6 +- modules/comment/comment.info | 6 +- modules/contact/contact.info | 6 +- modules/contextual/contextual.info | 6 +- modules/dashboard/dashboard.info | 6 +- modules/dblog/dblog.info | 6 +- modules/dblog/dblog.module | 9 +- modules/dblog/dblog.test | 6 +- modules/field/field.info | 6 +- modules/field/field.info.class.inc | 4 +- .../field_sql_storage/field_sql_storage.info | 6 +- modules/field/modules/list/list.info | 6 +- .../field/modules/list/tests/list_test.info | 6 +- modules/field/modules/number/number.info | 6 +- modules/field/modules/number/number.module | 4 +- modules/field/modules/options/options.info | 6 +- modules/field/modules/options/options.module | 10 +- modules/field/modules/options/options.test | 3 +- modules/field/modules/text/text.info | 6 +- modules/field/modules/text/text.module | 6 +- modules/field/tests/field_test.info | 6 +- modules/field_ui/field_ui.info | 6 +- modules/file/file.field.inc | 2 +- modules/file/file.info | 6 +- modules/file/file.module | 51 +- modules/file/tests/file.test | 166 + modules/file/tests/file_module_test.info | 6 +- modules/filter/filter.info | 6 +- modules/filter/filter.module | 33 +- modules/filter/filter.pages.inc | 7 +- modules/filter/filter.test | 48 +- modules/forum/forum.info | 6 +- modules/help/help.info | 6 +- modules/image/image.info | 6 +- modules/image/image.module | 8 +- modules/image/image.test | 86 + modules/image/tests/image_module_test.info | 6 +- modules/image/tests/image_module_test.module | 3 + modules/locale/locale.info | 6 +- modules/locale/tests/locale_test.info | 6 +- modules/menu/menu.info | 6 +- modules/menu/menu.test | 11 + modules/node/node.admin.inc | 15 +- modules/node/node.api.php | 23 +- modules/node/node.info | 6 +- modules/node/node.module | 11 +- modules/node/node.pages.inc | 1 - modules/node/node.test | 80 +- modules/node/tests/node_access_test.info | 6 +- modules/node/tests/node_test.info | 6 +- modules/node/tests/node_test_exception.info | 6 +- modules/openid/openid.info | 6 +- modules/openid/tests/openid_test.info | 6 +- modules/overlay/overlay-parent.js | 9 +- modules/overlay/overlay.info | 6 +- modules/path/path.info | 6 +- modules/path/path.module | 6 +- modules/php/php.info | 6 +- modules/poll/poll.info | 6 +- modules/poll/poll.module | 8 +- modules/profile/profile.info | 6 +- modules/profile/profile.test | 2 +- modules/rdf/rdf.info | 6 +- modules/rdf/tests/rdf_test.info | 6 +- modules/search/search.extender.inc | 8 +- modules/search/search.info | 6 +- modules/search/search.pages.inc | 2 +- modules/search/search.test | 32 +- .../search/tests/search_embedded_form.info | 6 +- modules/search/tests/search_extra_type.info | 6 +- modules/search/tests/search_node_tags.info | 6 +- modules/shortcut/shortcut.info | 6 +- modules/simpletest/drupal_web_test_case.php | 9 +- modules/simpletest/simpletest.info | 7 +- modules/simpletest/simpletest.module | 37 +- .../simpletest/tests/actions_loop_test.info | 6 +- modules/simpletest/tests/ajax_forms_test.info | 6 +- modules/simpletest/tests/ajax_test.info | 6 +- .../simpletest/tests/batch_test.callbacks.inc | 22 + modules/simpletest/tests/batch_test.info | 6 +- modules/simpletest/tests/boot.test | 38 + modules/simpletest/tests/boot_test_1.info | 12 + modules/simpletest/tests/boot_test_1.module | 21 + modules/simpletest/tests/boot_test_2.info | 12 + modules/simpletest/tests/boot_test_2.module | 13 + modules/simpletest/tests/bootstrap.test | 6 +- modules/simpletest/tests/common.test | 68 + modules/simpletest/tests/common_test.info | 6 +- modules/simpletest/tests/common_test.module | 9 + .../tests/common_test_cron_helper.info | 6 +- modules/simpletest/tests/database_test.info | 6 +- .../drupal_autoload_test.info | 6 +- .../drupal_autoload_test.module | 16 + .../drupal_autoload_test_trait.sh | 16 + ...drupal_system_listing_compatible_test.info | 6 +- ...upal_system_listing_incompatible_test.info | 6 +- .../simpletest/tests/entity_cache_test.info | 6 +- .../tests/entity_cache_test_dependency.info | 6 +- .../tests/entity_crud_hook_test.info | 6 +- .../tests/entity_query_access_test.info | 6 +- modules/simpletest/tests/error_test.info | 6 +- modules/simpletest/tests/file_test.info | 6 +- modules/simpletest/tests/filter_test.info | 6 +- modules/simpletest/tests/form_test.info | 6 +- modules/simpletest/tests/image_test.info | 6 +- modules/simpletest/tests/mail.test | 5 +- modules/simpletest/tests/menu_test.info | 6 +- modules/simpletest/tests/module.test | 42 + .../tests/module_test.implementations.inc | 10 + modules/simpletest/tests/module_test.info | 6 +- modules/simpletest/tests/module_test.module | 11 + modules/simpletest/tests/path_test.info | 6 +- .../tests/psr_0_test/psr_0_test.info | 6 +- .../tests/psr_4_test/psr_4_test.info | 6 +- .../simpletest/tests/requirements1_test.info | 6 +- .../simpletest/tests/requirements2_test.info | 6 +- modules/simpletest/tests/session_test.info | 6 +- .../tests/system_dependencies_test.info | 6 +- ...atible_core_version_dependencies_test.info | 6 +- ...system_incompatible_core_version_test.info | 6 +- ...ible_module_version_dependencies_test.info | 6 +- ...stem_incompatible_module_version_test.info | 6 +- .../tests/system_project_namespace_test.info | 13 + .../system_project_namespace_test.module | 1 + modules/simpletest/tests/system_test.info | 6 +- modules/simpletest/tests/system_test.module | 3 + modules/simpletest/tests/taxonomy_test.info | 6 +- modules/simpletest/tests/theme_test.info | 6 +- .../themes/test_basetheme/test_basetheme.info | 6 +- .../themes/test_subtheme/test_subtheme.info | 6 +- .../tests/themes/test_theme/test_theme.info | 6 +- .../simpletest/tests/update_script_test.info | 6 +- modules/simpletest/tests/update_test_1.info | 6 +- modules/simpletest/tests/update_test_2.info | 6 +- modules/simpletest/tests/update_test_3.info | 6 +- .../upgrade/drupal-6.upload.database.php | 99 +- .../tests/upgrade/upgrade.upload.test | 23 + modules/simpletest/tests/url_alter_test.info | 6 +- modules/simpletest/tests/xmlrpc.test | 34 + modules/simpletest/tests/xmlrpc_test.info | 6 +- modules/statistics/statistics.info | 6 +- modules/statistics/statistics.module | 7 +- modules/statistics/statistics.php | 26 +- modules/syslog/syslog.info | 6 +- modules/system/system.admin.inc | 8 +- modules/system/system.api.php | 16 +- modules/system/system.info | 6 +- modules/system/system.install | 35 + modules/system/system.js | 2 +- modules/system/system.module | 26 +- modules/system/system.tar.inc | 2901 ++++++++++------- modules/system/system.test | 70 +- modules/system/tests/cron_queue_test.info | 6 +- modules/system/tests/cron_queue_test.module | 12 + modules/system/tests/system_cron_test.info | 12 + modules/system/tests/system_cron_test.module | 15 + modules/taxonomy/taxonomy.info | 6 +- modules/taxonomy/taxonomy.module | 2 +- modules/toolbar/toolbar.info | 6 +- modules/tracker/tracker.info | 6 +- .../translation/tests/translation_test.info | 6 +- modules/translation/translation.info | 6 +- modules/trigger/tests/trigger_test.info | 6 +- modules/trigger/trigger.info | 6 +- modules/update/tests/aaa_update_test.info | 6 +- modules/update/tests/bbb_update_test.info | 6 +- modules/update/tests/ccc_update_test.info | 6 +- .../update_test_basetheme.info | 6 +- .../update_test_subtheme.info | 6 +- modules/update/tests/update_test.info | 6 +- modules/update/update.authorize.inc | 12 +- modules/update/update.fetch.inc | 8 +- modules/update/update.info | 6 +- modules/update/update.manager.inc | 6 +- modules/user/tests/user_form_test.info | 6 +- modules/user/user-picture.tpl.php | 2 +- modules/user/user.api.php | 22 +- modules/user/user.info | 6 +- modules/user/user.js | 2 + modules/user/user.module | 84 +- modules/user/user.pages.inc | 10 +- modules/user/user.test | 73 +- profiles/README.txt | 28 + profiles/minimal/minimal.info | 6 +- profiles/standard/standard.info | 6 +- ...drupal_system_listing_compatible_test.info | 6 +- ...upal_system_listing_incompatible_test.info | 6 +- profiles/testing/testing.info | 6 +- scripts/password-hash.sh | 2 +- themes/bartik/bartik.info | 6 +- themes/garland/garland.info | 6 +- themes/garland/template.php | 78 +- themes/seven/seven.info | 6 +- themes/stark/stark.info | 6 +- web.config | 2 +- 230 files changed, 4138 insertions(+), 2075 deletions(-) create mode 100644 modules/simpletest/tests/boot.test create mode 100644 modules/simpletest/tests/boot_test_1.info create mode 100644 modules/simpletest/tests/boot_test_1.module create mode 100644 modules/simpletest/tests/boot_test_2.info create mode 100644 modules/simpletest/tests/boot_test_2.module create mode 100644 modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test_trait.sh create mode 100644 modules/simpletest/tests/module_test.implementations.inc create mode 100644 modules/simpletest/tests/system_project_namespace_test.info create mode 100644 modules/simpletest/tests/system_project_namespace_test.module create mode 100644 modules/system/tests/system_cron_test.info create mode 100644 modules/system/tests/system_cron_test.module create mode 100644 profiles/README.txt diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 04cff864..72d9d8fc 100755 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,4 +1,101 @@ +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. @@ -86,11 +183,11 @@ Drupal 7.36, 2015-04-01 - 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 @@ -159,11 +256,11 @@ Drupal 7.33, 2014-11-07 - 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 @@ -178,7 +275,7 @@ Drupal 7.30, 2014-07-24 - 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 @@ -224,11 +321,11 @@ Drupal 7.28, 2014-05-08 - 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 @@ -294,7 +391,7 @@ Drupal 7.25, 2014-01-02 - 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 @@ -548,8 +645,8 @@ Drupal 7.15, 2012-08-01 - Numerous API documentation improvements. - Additional automated test coverage. -Drupal 7.14 2012-05-02 ----------------------- +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. @@ -597,12 +694,12 @@ Drupal 7.14 2012-05-02 - 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 ----------------------- +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. @@ -636,11 +733,11 @@ Drupal 7.12, 2012-02-01 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 @@ -993,7 +1090,7 @@ Drupal 7.0, 2011-01-05 requests. Drupal 6.23-dev, xxxx-xx-xx (development release) ------------------------ +--------------------------- Drupal 6.22, 2011-05-25 ----------------------- @@ -1003,25 +1100,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 @@ -1030,7 +1127,7 @@ 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. @@ -1042,12 +1139,12 @@ Drupal 6.16, 2010-03-03 - 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 @@ -1056,18 +1153,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 @@ -1075,7 +1172,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. @@ -1471,7 +1568,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 @@ -1584,7 +1681,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 diff --git a/MAINTAINERS.txt b/MAINTAINERS.txt index f5cf6f89..0d1e0d0c 100755 --- a/MAINTAINERS.txt +++ b/MAINTAINERS.txt @@ -1,7 +1,8 @@ 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 http://drupal.org/contribute to find out how. +contribute in other ways -- see https://www.drupal.org/contribute to find out +how. Branch maintainers ------------------ @@ -9,154 +10,153 @@ Branch maintainers The Drupal Core branch maintainers oversee the development of Drupal as a whole. The branch maintainers for Drupal 7 are: -- Dries Buytaert 'dries' http://drupal.org/user/1 -- Angela Byron 'webchick' http://drupal.org/user/24967 -- David Rothstein 'David_Rothstein' http://drupal.org/user/124982 +- Dries Buytaert 'dries' https://www.drupal.org/u/dries +- Angela Byron 'webchick' https://www.drupal.org/u/webchick +- David Rothstein 'David_Rothstein' https://www.drupal.org/u/david_rothstein Component maintainers --------------------- The Drupal Core component maintainers oversee the development of Drupal -subsystems. See http://drupal.org/contribute/core-maintainers for more +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 -- 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 -- 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 -- 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 + - Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud Database update system -- Ashok Modi 'BTMash' http://drupal.org/user/60422 +- Ashok Modi 'BTMash' https://www.drupal.org/u/btmash Entity system -- Wolfgang Ziegler 'fago' http://drupal.org/user/16747 -- 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 -- 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_' http://drupal.org/user/598310 -- Steve De Jonghe 'seutje' http://drupal.org/user/264148 -- Jesse Renée Beach 'jessebeach' http://drupal.org/user/748566 +- 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 +- 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-Johnson 'bowersox' 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 -- Jennifer Hodgdon 'jhodgdon' http://drupal.org/user/155601 +- 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' http://drupal.org/user/23 -- Ken Rickard 'agentrickard' http://drupal.org/user/20975 -- Jess Myrbo 'xjm' http://drupal.org/user/65776 +- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman +- Ken Rickard 'agentrickard' https://www.drupal.org/u/agentrickard +- Jess Myrbo 'xjm' https://www.drupal.org/u/xjm Security team ----------------- -To report a security issue, see: https://drupal.org/security-team/report-issue +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 http://drupal.org/security-team for more information. The -security team lead is: +documentation. See https://www.drupal.org/security-team for more information. +The security team lead is: -- Michael Hess 'mlhess' https://drupal.org/user/102818 +- Michael Hess 'mlhess' https://www.drupal.org/u/mlhess Module maintainers @@ -166,142 +166,142 @@ 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 -- 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' http://drupal.org/user/729614 +- Andrei Mateescu 'amateescu' https://www.drupal.org/u/amateescu Profile module - ? RDF module -- Stéphane Corlosquet 'scor' http://drupal.org/user/52142 +- Stéphane Corlosquet 'scor' https://www.drupal.org/u/scor Search module -- Doug Green 'douggreen' http://drupal.org/user/29191 +- Doug Green 'douggreen' https://www.drupal.org/u/douggreen Shortcut module -- David Rothstein 'David_Rothstein' http://drupal.org/user/124982 +- David Rothstein 'David_Rothstein' https://www.drupal.org/u/david_rothstein Simpletest module -- Jimmy Berry 'boombatower' http://drupal.org/user/214218 +- Jimmy Berry 'boombatower' https://www.drupal.org/u/boombatower Statistics module -- Tim Millwood 'timmillwood' http://drupal.org/user/227849 +- 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 -- Jess Myrbo 'xjm' http://drupal.org/user/65776 -- Nathaniel Catchpole 'catch' http://drupal.org/user/35733 -- Benjamin Doherty 'bangpound' http://drupal.org/user/100456 +- Jess Myrbo 'xjm' https://www.drupal.org/u/xjm +- 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 diff --git a/UPGRADE.txt b/UPGRADE.txt index e870ff0f..ae733ca1 100755 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -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. diff --git a/includes/batch.inc b/includes/batch.inc index 061acd4c..e89ab8de 100755 --- a/includes/batch.inc +++ b/includes/batch.inc @@ -460,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)); } } } diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index e14626ca..0428bd36 100755 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -8,7 +8,7 @@ /** * The current system version. */ -define('VERSION', '7.39'); +define('VERSION', '7.43'); /** * Core API compatibility. @@ -1055,7 +1055,7 @@ function drupal_page_get_cache($check_only = FALSE) { * 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. @@ -1262,6 +1262,10 @@ function drupal_page_header() { $default_headers = array( 'Expires' => 'Sun, 19 Nov 1978 05:00:00 GMT', 'Cache-Control' => 'no-cache, must-revalidate, post-check=0, pre-check=0', + // 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); } @@ -1776,7 +1780,7 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO * @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(); } @@ -2464,6 +2468,9 @@ 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'); + } } /** @@ -2779,10 +2786,14 @@ function language_list($field = 'language') { } /** - * Returns the 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' => '')); @@ -2952,7 +2963,9 @@ function ip_address() { * 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. @@ -3107,6 +3120,22 @@ function drupal_autoload_class($class) { return _registry_check_code('class', $class); } +/** + * 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. * @@ -3125,7 +3154,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; } diff --git a/includes/cache.inc b/includes/cache.inc index 207bf661..4c2bed32 100755 --- a/includes/cache.inc +++ b/includes/cache.inc @@ -14,6 +14,7 @@ * * @param $bin * The cache bin for which the cache object should be returned. + * * @return DrupalCacheInterface * The cache object associated with the specified bin. * diff --git a/includes/common.inc b/includes/common.inc index ceac115a..c6303efa 100755 --- a/includes/common.inc +++ b/includes/common.inc @@ -688,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. @@ -1057,6 +1064,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 @@ -2214,20 +2227,8 @@ function url($path = NULL, array $options = array()) { 'prefix' => '' ); - // A duplicate of the code from url_is_external() to avoid needing another - // function call, since performance inside url() is critical. if (!isset($options['external'])) { - // Return an external link if $path contains an allowed absolute URL. 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. If the path starts with 2 - // slashes then it is always considered an external URL without an explicit - // protocol part. - $colonpos = strpos($path, ':'); - $options['external'] = (strpos($path, '//') === 0) - || ($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. @@ -2347,12 +2348,18 @@ function url($path = NULL, array $options = array()) { */ 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. If the path starts with 2 slashes - // then it is always considered an external URL without an explicit protocol - // part. + // 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); @@ -2812,11 +2819,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 @@ -2833,7 +2840,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); + } } } @@ -5212,6 +5223,11 @@ 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 @@ -5308,8 +5324,8 @@ function drupal_page_set_cache() { * * Do not call this function from a test. Use $this->cronRun() instead. * - * @return - * TRUE if cron 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. @@ -5371,12 +5387,12 @@ function drupal_cron_run() { // Do not run if queue wants to skip. continue; } - $function = $info['worker callback']; + $callback = $info['worker callback']; $end = time() + (isset($info['time']) ? $info['time'] : 15); $queue = DrupalQueue::get($queue_name); while (time() < $end && ($item = $queue->claimItem())) { try { - $function($item->data); + call_user_func($callback, $item->data); $queue->deleteItem($item); } catch (Exception $e) { @@ -7083,7 +7099,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 @@ -7156,6 +7173,7 @@ function _drupal_schema_initialize(&$schema, $module, $remove_descriptions = TRU */ 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; } @@ -7363,7 +7381,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. @@ -7443,7 +7470,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 @@ -7483,8 +7509,8 @@ 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. @@ -7648,7 +7674,12 @@ function debug($data, $label = NULL, $print_r = FALSE) { * 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: @@ -7663,6 +7694,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!=|==|=|<|<=|>|>=|<>)?'; @@ -7671,7 +7708,6 @@ function drupal_parse_dependency($dependency) { $p_major = '(?P\d+)'; // By setting the minor version to x, branches can be matched. $p_minor = '(?P(?:\d+|x)(?:-[A-Za-z]+\d+)?)'; - $value = array(); $parts = explode('(', $dependency, 2); $value['name'] = trim($parts[0]); if (isset($parts[1])) { diff --git a/includes/database/database.inc b/includes/database/database.inc index 3d776b57..90a3f743 100755 --- a/includes/database/database.inc +++ b/includes/database/database.inc @@ -656,7 +656,7 @@ abstract class DatabaseConnection extends PDO { * @return DatabaseStatementInterface * This method will return one of: the executed statement, the number of * rows affected by the query (not the number matched), or the generated - * insert IT of the last query, depending on the value of + * insert ID of the last query, depending on the value of * $options['return']. Typically that value will be set by default or a * query builder and should not be set by a user. If there is an error, * this method will return NULL and may throw an exception if diff --git a/includes/database/mysql/database.inc b/includes/database/mysql/database.inc index 0b84f271..a96b053c 100755 --- a/includes/database/mysql/database.inc +++ b/includes/database/mysql/database.inc @@ -51,6 +51,11 @@ class DatabaseConnection_mysql extends DatabaseConnection { // Because MySQL's prepared statements skip the query cache, because it's dumb. PDO::ATTR_EMULATE_PREPARES => TRUE, ); + if (defined('PDO::MYSQL_ATTR_MULTI_STATEMENTS')) { + // An added connection option in PHP 5.5.21+ to optionally limit SQL to a + // single statement like mysqli. + $connection_options['pdo'] += array(PDO::MYSQL_ATTR_MULTI_STATEMENTS => FALSE); + } parent::__construct($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']); @@ -76,10 +81,12 @@ class DatabaseConnection_mysql extends DatabaseConnection { 'init_commands' => array(), ); $connection_options['init_commands'] += array( - 'sql_mode' => "SET sql_mode = 'ANSI,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER'", + 'sql_mode' => "SET sql_mode = 'REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER'", ); - // Set connection options. - $this->exec(implode('; ', $connection_options['init_commands'])); + // Execute initial commands. + foreach ($connection_options['init_commands'] as $sql) { + $this->exec($sql); + } } public function __destruct() { diff --git a/includes/database/schema.inc b/includes/database/schema.inc index 1fc92954..d8344c62 100755 --- a/includes/database/schema.inc +++ b/includes/database/schema.inc @@ -92,7 +92,8 @@ require_once dirname(__FILE__) . '/query.inc'; * specification). Each specification is an array containing the name of * the referenced table ('table'), and an array of column mappings * ('columns'). Column mappings are defined by key pairs ('source_column' => - * 'referenced_column'). + * 'referenced_column'). This key is for documentation purposes only; foreign + * keys are not created in the database, nor are they enforced by Drupal. * - 'indexes': An associative array of indexes ('indexname' => * specification). Each specification is an array of one or more * key column specifiers (see below) that form an index on the @@ -144,6 +145,8 @@ require_once dirname(__FILE__) . '/query.inc'; * 'unique keys' => array( * 'vid' => array('vid'), * ), + * // For documentation purposes only; foreign keys are not created in the + * // database. * 'foreign keys' => array( * 'node_revision' => array( * 'table' => 'node_revision', diff --git a/includes/database/sqlite/install.inc b/includes/database/sqlite/install.inc index 62cbac38..10884e2f 100755 --- a/includes/database/sqlite/install.inc +++ b/includes/database/sqlite/install.inc @@ -14,8 +14,6 @@ class DatabaseTasks_sqlite extends DatabaseTasks { /** * Minimum engine version. - * - * @todo: consider upping to 3.6.8 in Drupal 8 to get SAVEPOINT support. */ public function minimumVersion() { return '3.3.7'; diff --git a/includes/file.inc b/includes/file.inc index d3ac87ea..ba3da064 100755 --- a/includes/file.inc +++ b/includes/file.inc @@ -1785,7 +1785,7 @@ function file_validate_is_image(stdClass $file) { /** * Verifies that image dimensions are within the specified maximum and minimum. * - * Non-image files will be ignored. If a image toolkit is available the image + * Non-image files will be ignored. If an image toolkit is available the image * will be scaled to fit within the desired maximum dimensions. * * @param $file diff --git a/includes/form.inc b/includes/form.inc index f7671bed..baadcef2 100755 --- a/includes/form.inc +++ b/includes/form.inc @@ -3385,9 +3385,12 @@ function form_process_container($element, &$form_state) { /** * Returns HTML to wrap child elements in a container. * - * Used for grouped form items. Can also be used as a #theme_wrapper for any + * Used for grouped form items. Can also be used as a theme wrapper for any * renderable element, to surround it with a
and add attributes such as - * classes or an HTML id. + * classes or an HTML ID. + * + * See the @link forms_api_reference.html Form API reference @endlink for more + * information on the #theme_wrappers render array property. * * @param $variables * An associative array containing: @@ -3979,7 +3982,12 @@ function form_process_autocomplete($element) { // browser interpreting the path plus search string as an actual file. $current_clean_url = isset($GLOBALS['conf']['clean_url']) ? $GLOBALS['conf']['clean_url'] : NULL; $GLOBALS['conf']['clean_url'] = 0; - $element['#autocomplete_input']['#url_value'] = url($element['#autocomplete_path'], array('absolute' => TRUE)); + // Force the script path to 'index.php', in case the server is not + // configured to find it automatically. Normally it is the responsibility + // of the site to do this themselves using hook_url_outbound_alter() (see + // url()) but since this code is forcing non-clean URLs on sites that don't + // normally use them, it is done here instead. + $element['#autocomplete_input']['#url_value'] = url($element['#autocomplete_path'], array('absolute' => TRUE, 'script' => 'index.php')); $GLOBALS['conf']['clean_url'] = $current_clean_url; } return $element; @@ -4484,7 +4492,7 @@ function element_validate_number($element, &$form_state) { * * Sample callback_batch_finished(): * @code - * function batch_test_finished($success, $results, $operations) { + * function my_finished_callback($success, $results, $operations) { * // The 'success' parameter means no fatal PHP errors were detected. All * // other error management should be handled using 'results'. * if ($success) { diff --git a/includes/install.core.inc b/includes/install.core.inc index e5a65865..ad43b42a 100755 --- a/includes/install.core.inc +++ b/includes/install.core.inc @@ -1590,7 +1590,9 @@ function install_finished(&$install_state) { } /** - * Batch callback for batch installation of modules. + * Implements callback_batch_operation(). + * + * Performs batch installation of modules. */ function _install_module_batch($module, $module_name, &$context) { // Install and enable the module right away, so that the module will be @@ -1603,6 +1605,8 @@ function _install_module_batch($module, $module_name, &$context) { } /** + * Implements callback_batch_finished(). + * * 'Finished' callback for module installation batch. */ function _install_profile_modules_finished($success, $results, $operations) { diff --git a/includes/install.inc b/includes/install.inc index 2b55589f..5e1d3c63 100755 --- a/includes/install.inc +++ b/includes/install.inc @@ -750,7 +750,7 @@ function drupal_install_system() { /** * Uninstalls a given list of disabled modules. * - * @param array $module_list + * @param string[] $module_list * The modules to uninstall. It is the caller's responsibility to ensure that * all modules in this list have already been disabled before this function * is called. @@ -769,6 +769,7 @@ function drupal_install_system() { * included in $module_list). * * @see module_disable() + * @see module_enable() */ function drupal_uninstall_modules($module_list = array(), $uninstall_dependents = TRUE) { if ($uninstall_dependents) { diff --git a/includes/locale.inc b/includes/locale.inc index c7f95838..82c55e5c 100755 --- a/includes/locale.inc +++ b/includes/locale.inc @@ -2306,6 +2306,8 @@ function _locale_batch_build($files, $finished = NULL, $components = array()) { } /** + * Implements callback_batch_operation(). + * * Perform interface translation import as a batch step. * * @param $filepath @@ -2324,6 +2326,8 @@ function _locale_batch_import($filepath, &$context) { } /** + * Implements callback_batch_finished(). + * * Finished callback of system page locale import batch. * Inform the user of translation files imported. */ @@ -2334,6 +2338,8 @@ function _locale_batch_system_finished($success, $results) { } /** + * Implements callback_batch_finished(). + * * Finished callback of language addition locale import batch. * Inform the user of translation files imported. */ diff --git a/includes/mail.inc b/includes/mail.inc index 0275922b..0e5c1780 100755 --- a/includes/mail.inc +++ b/includes/mail.inc @@ -566,7 +566,7 @@ function _drupal_wrap_mail_line(&$line, $key, $values) { // Use soft-breaks only for purely quoted or unindented text. $line = wordwrap($line, 77 - $values['length'], $values['soft'] ? " \n" : "\n"); // Break really long words at the maximum width allowed. - $line = wordwrap($line, 996 - $values['length'], $values['soft'] ? " \n" : "\n"); + $line = wordwrap($line, 996 - $values['length'], $values['soft'] ? " \n" : "\n", TRUE); } /** diff --git a/includes/menu.inc b/includes/menu.inc index 0e9c977c..1fe5a647 100755 --- a/includes/menu.inc +++ b/includes/menu.inc @@ -229,12 +229,20 @@ define('MENU_CONTEXT_INLINE', 0x0002); define('MENU_FOUND', 1); /** - * Internal menu status code -- Menu item was not found. + * Menu status code -- Not found. + * + * This can be used as the return value from a page callback, although it is + * preferable to use a load function to accomplish this; see the hook_menu() + * documentation for details. */ define('MENU_NOT_FOUND', 2); /** - * Internal menu status code -- Menu item access is denied. + * Menu status code -- Access denied. + * + * This can be used as the return value from a page callback, although it is + * preferable to use an access callback to accomplish this; see the hook_menu() + * documentation for details. */ define('MENU_ACCESS_DENIED', 3); @@ -431,7 +439,7 @@ function menu_set_item($path, $router_item) { * * @param $path * The path; for example, 'node/5'. The function will find the corresponding - * node/% item and return that. + * node/% item and return that. Defaults to the current path. * @param $router_item * Internal use only. * @@ -2613,10 +2621,30 @@ function menu_get_active_breadcrumb() { */ function menu_get_active_title() { $active_trail = menu_get_active_trail(); + $local_task_title = NULL; foreach (array_reverse($active_trail) as $item) { - if (!(bool) ($item['type'] & MENU_IS_LOCAL_TASK)) { - return $item['title']; + // Local task titles are displayed as tabs and therefore should not be + // repeated as the page title. However, if the local task appears in a + // top-level menu, it is no longer a "local task" anymore (the front page + // of the site does not have tabs) so it is better to use the local task + // title in that case than to fall back on the front page link in the + // active trail (which is usually "Home" and would not make sense in this + // context). + if ((bool) ($item['type'] & MENU_IS_LOCAL_TASK)) { + // A local task title is being skipped; track it in case it needs to be + // used later. + $local_task_title = $item['title']; + } + else { + // This is not a local task, so use it for the page title (unless the + // conditions described above are met). + if (isset($local_task_title) && isset($item['href']) && $item['href'] == '') { + return $local_task_title; + } + else { + return $item['title']; + } } } } diff --git a/includes/module.inc b/includes/module.inc index 494924f5..68c8b8ef 100755 --- a/includes/module.inc +++ b/includes/module.inc @@ -320,16 +320,27 @@ function module_load_install($module) { * The name of the included file, if successful; FALSE otherwise. */ function module_load_include($type, $module, $name = NULL) { + static $files = array(); + if (!isset($name)) { $name = $module; } + $key = $type . ':' . $module . ':' . $name; + if (isset($files[$key])) { + return $files[$key]; + } + if (function_exists('drupal_get_path')) { $file = DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . "/$name.$type"; if (is_file($file)) { require_once $file; + $files[$key] = $file; return $file; } + else { + $files[$key] = FALSE; + } } return FALSE; } @@ -365,20 +376,22 @@ function module_load_all_includes($type, $name = NULL) { * - Invoke hook_modules_installed(). * - Invoke hook_modules_enabled(). * - * @param $module_list + * @param string[] $module_list * An array of module names. - * @param $enable_dependencies + * @param bool $enable_dependencies * If TRUE, dependencies will automatically be added and enabled in the * correct order. This incurs a significant performance cost, so use FALSE * if you know $module_list is already complete and in the correct order. * - * @return + * @return bool * FALSE if one or more dependencies are missing, TRUE otherwise. * * @see hook_install() * @see hook_enable() * @see hook_modules_installed() * @see hook_modules_enabled() + * @see module_disable() + * @see drupal_uninstall_modules() */ function module_enable($module_list, $enable_dependencies = TRUE) { if ($enable_dependencies) { @@ -505,12 +518,15 @@ function module_enable($module_list, $enable_dependencies = TRUE) { /** * Disables a given set of modules. * - * @param $module_list + * @param string[] $module_list * An array of module names. - * @param $disable_dependents + * @param bool $disable_dependents * If TRUE, dependent modules will automatically be added and disabled in the * correct order. This incurs a significant performance cost, so use FALSE * if you know $module_list is already complete and in the correct order. + * + * @see drupal_uninstall_modules() + * @see module_enable() */ function module_disable($module_list, $disable_dependents = TRUE) { if ($disable_dependents) { @@ -676,12 +692,16 @@ function module_hook($module, $hook) { /** * Determines which modules are implementing a hook. * - * @param $hook + * Lazy-loaded include files specified with "group" via hook_hook_info() or + * hook_module_implements_alter() will be automatically included by this + * function when necessary. + * + * @param string $hook * The name of the hook (e.g. "help" or "menu"). - * @param $sort + * @param bool $sort * By default, modules are ordered by weight and filename, settings this option * to TRUE, module list will be ordered by module name. - * @param $reset + * @param bool $reset * For internal use only: Whether to force the stored list of hook * implementations to be regenerated (such as after enabling a new module, * before processing hook_enable). @@ -696,8 +716,10 @@ function module_implements($hook, $sort = FALSE, $reset = FALSE) { static $drupal_static_fast; if (!isset($drupal_static_fast)) { $drupal_static_fast['implementations'] = &drupal_static(__FUNCTION__); + $drupal_static_fast['verified'] = &drupal_static(__FUNCTION__ . ':verified'); } $implementations = &$drupal_static_fast['implementations']; + $verified = &$drupal_static_fast['verified']; // We maintain a persistent cache of hook implementations in addition to the // static cache to avoid looping through every module and every hook on each @@ -711,14 +733,19 @@ function module_implements($hook, $sort = FALSE, $reset = FALSE) { // per request. if ($reset) { $implementations = array(); + $verified = array(); cache_set('module_implements', array(), 'cache_bootstrap'); drupal_static_reset('module_hook_info'); drupal_static_reset('drupal_alter'); cache_clear_all('hook_info', 'cache_bootstrap'); + cache_clear_all('system_cache_tables', 'cache'); return; } // Fetch implementations from cache. + // This happens on the first call to module_implements(*, *, FALSE) during a + // request, but also when $implementations have been reset, e.g. after + // module_enable(). if (empty($implementations)) { $implementations = cache_get('module_implements', 'cache_bootstrap'); if ($implementations === FALSE) { @@ -727,12 +754,17 @@ function module_implements($hook, $sort = FALSE, $reset = FALSE) { else { $implementations = $implementations->data; } + // Forget all previously "verified" hooks, in case that $implementations + // were cleared via drupal_static_reset('module_implements') instead of + // module_implements(*, *, TRUE). + $verified = array(); } if (!isset($implementations[$hook])) { // The hook is not cached, so ensure that whether or not it has // implementations, that the cache is updated at the end of the request. $implementations['#write_cache'] = TRUE; + // Discover implementations for this hook. $hook_info = module_hook_info(); $implementations[$hook] = array(); $list = module_list(FALSE, FALSE, $sort); @@ -744,13 +776,31 @@ function module_implements($hook, $sort = FALSE, $reset = FALSE) { $implementations[$hook][$module] = $include_file ? $hook_info[$hook]['group'] : FALSE; } } - // Allow modules to change the weight of specific implementations but avoid + // Allow modules to change the weight of specific implementations, but avoid // an infinite loop. if ($hook != 'module_implements_alter') { + // Remember the implementations before hook_module_implements_alter(). + $implementations_before = $implementations[$hook]; drupal_alter('module_implements', $implementations[$hook], $hook); + // Verify implementations that were added or modified. + foreach (array_diff_assoc($implementations[$hook], $implementations_before) as $module => $group) { + // If drupal_alter('module_implements') changed or added a $group, the + // respective file needs to be included. + if ($group) { + module_load_include('inc', $module, "$module.$group"); + } + // If a new implementation was added, verify that the function exists. + if (!function_exists($module . '_' . $hook)) { + unset($implementations[$hook][$module]); + } + } } + // Implementations for this hook are now "verified". + $verified[$hook] = TRUE; } - else { + elseif (!isset($verified[$hook])) { + // Implementations for this hook were in the cache, but they are not + // "verified" yet. foreach ($implementations[$hook] as $module => $group) { // If this hook implementation is stored in a lazy-loaded file, so include // that file first. @@ -769,6 +819,7 @@ function module_implements($hook, $sort = FALSE, $reset = FALSE) { $implementations['#write_cache'] = TRUE; } } + $verified[$hook] = TRUE; } return array_keys($implementations[$hook]); @@ -833,6 +884,11 @@ function module_hook_info() { * @see module_implements() */ function module_implements_write_cache() { + // The list of implementations includes vital modules only before full + // bootstrap, so do not write cache if we are not fully bootstrapped yet. + if (drupal_get_bootstrap_phase() != DRUPAL_BOOTSTRAP_FULL) { + return; + } $implementations = &drupal_static('module_implements'); if (isset($implementations['#write_cache'])) { unset($implementations['#write_cache']); diff --git a/includes/path.inc b/includes/path.inc index 2e357111..6bd48d30 100755 --- a/includes/path.inc +++ b/includes/path.inc @@ -347,7 +347,8 @@ function drupal_match_path($path, $patterns) { * drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL) makes this function available. * * @return - * The current Drupal URL path. + * The current Drupal URL path. The path is untrusted user input and must be + * treated as such. * * @see request_path() */ diff --git a/includes/registry.inc b/includes/registry.inc index 5fc76748..29a1fca8 100755 --- a/includes/registry.inc +++ b/includes/registry.inc @@ -164,7 +164,7 @@ function _registry_parse_files($files) { * (optional) Weight of the module. */ function _registry_parse_file($filename, $contents, $module = '', $weight = 0) { - if (preg_match_all('/^\s*(?:abstract|final)?\s*(class|interface)\s+([a-zA-Z0-9_]+)/m', $contents, $matches)) { + if (preg_match_all('/^\s*(?:abstract|final)?\s*(class|interface|trait)\s+([a-zA-Z0-9_]+)/m', $contents, $matches)) { foreach ($matches[2] as $key => $name) { db_merge('registry') ->key(array( diff --git a/includes/theme.inc b/includes/theme.inc index 8d5348df..ff54d6e2 100755 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -1710,11 +1710,29 @@ function theme_status_messages($variables) { * copy if none of the enabled modules or the active theme implement any * preprocess or process functions or override this theme implementation. * - * @param $variables - * An associative array containing the keys 'text', 'path', and 'options'. - * See the l() function for information about these variables. + * @param array $variables + * An associative array containing the keys: + * - text: The text of the link. + * - path: The internal path or external URL being linked to. It is used as + * the $path parameter of the url() function. + * - options: (optional) An array that defaults to empty, but can contain: + * - attributes: Can contain optional attributes: + * - class: must be declared in an array. Example: 'class' => + * array('class_name1','class_name2'). + * - title: must be a string. Example: 'title' => 'Example title' + * - Others are more flexible as long as they work with + * drupal_attributes($variables['options']['attributes]). + * - html: Boolean flag that tells whether text contains HTML or plain + * text. If set to TRUE, the text value will not be sanitized so the + calling function must ensure that it already contains safe HTML. + * The elements $variables['options']['attributes'] and + * $variables['options']['html'] are used in this function similarly to the + * way that $options['attributes'] and $options['html'] are used in l(). + * The link itself is built by the url() function, which takes + * $variables['path'] and $variables['options'] as arguments. * * @see l() + * @see url() */ function theme_link($variables) { return '' . ($variables['options']['html'] ? $variables['text'] : check_plain($variables['text'])) . ''; @@ -1791,7 +1809,8 @@ function theme_links($variables) { foreach ($links as $key => $link) { $class = array($key); - // Add first, last and active classes to the list of links to help out themers. + // Add first, last and active classes to the list of links to help out + // themers. if ($i == 1) { $class[] = 'first'; } @@ -1809,7 +1828,8 @@ function theme_links($variables) { $output .= l($link['title'], $link['href'], $link); } elseif (!empty($link['title'])) { - // Some links are actually not links, but we wrap these in for adding title and class attributes. + // Some links are actually not links, but we wrap these in for + // adding title and class attributes. if (empty($link['html'])) { $link['title'] = check_plain($link['title']); } diff --git a/includes/update.inc b/includes/update.inc index a17161c9..35a73c33 100755 --- a/includes/update.inc +++ b/includes/update.inc @@ -908,6 +908,8 @@ function update_get_d6_session_name() { } /** + * Implements callback_batch_operation(). + * * Performs one update and stores the results for display on the results page. * * If an update function completes successfully, it should return a message @@ -1078,6 +1080,8 @@ function update_batch($start, $redirect = NULL, $url = NULL, $batch = array(), $ } /** + * Implements callback_batch_finished(). + * * Finishes the update process and stores the results for eventual display. * * After the updates run, all caches are flushed. The update results are diff --git a/includes/xmlrpcs.inc b/includes/xmlrpcs.inc index 8655c05b..c334de15 100755 --- a/includes/xmlrpcs.inc +++ b/includes/xmlrpcs.inc @@ -264,6 +264,10 @@ function xmlrpc_server_call($xmlrpc_server, $methodname, $args) { */ function xmlrpc_server_multicall($methodcalls) { // See http://www.xmlrpc.com/discuss/msgReader$1208 + // To avoid multicall expansion attacks, limit the number of duplicate method + // calls allowed with a default of 1. Set to -1 for unlimited. + $duplicate_method_limit = variable_get('xmlrpc_multicall_duplicate_method_limit', 1); + $method_count = array(); $return = array(); $xmlrpc_server = xmlrpc_server_get(); foreach ($methodcalls as $call) { @@ -273,10 +277,14 @@ function xmlrpc_server_multicall($methodcalls) { $ok = FALSE; } $method = $call['methodName']; + $method_count[$method] = isset($method_count[$method]) ? $method_count[$method] + 1 : 1; $params = $call['params']; if ($method == 'system.multicall') { $result = xmlrpc_error(-32600, t('Recursive calls to system.multicall are forbidden.')); } + elseif ($duplicate_method_limit > 0 && $method_count[$method] > $duplicate_method_limit) { + $result = xmlrpc_error(-156579, t('Too many duplicate method calls in system.multicall.')); + } elseif ($ok) { $result = xmlrpc_server_call($xmlrpc_server, $method, $params); } diff --git a/misc/states.js b/misc/states.js index 6d98da81..5aac65d2 100755 --- a/misc/states.js +++ b/misc/states.js @@ -493,7 +493,11 @@ $(document).bind('state:disabled', function(e) { $(document).bind('state:required', function(e) { if (e.trigger) { if (e.value) { - $(e.target).closest('.form-item, .form-wrapper').find('label').append('*'); + var $label = $(e.target).closest('.form-item, .form-wrapper').find('label'); + // Avoids duplicate required markers on initialization. + if (!$label.find('.form-required').length) { + $label.append('*'); + } } else { $(e.target).closest('.form-item, .form-wrapper').find('label .form-required').remove(); diff --git a/modules/aggregator/aggregator.info b/modules/aggregator/aggregator.info index e7e8fa55..c181417a 100755 --- a/modules/aggregator/aggregator.info +++ b/modules/aggregator/aggregator.info @@ -7,8 +7,8 @@ files[] = aggregator.test configure = admin/config/services/aggregator/settings stylesheets[all][] = aggregator.css -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/aggregator/tests/aggregator_test.info b/modules/aggregator/tests/aggregator_test.info index e862225f..4579b77e 100755 --- a/modules/aggregator/tests/aggregator_test.info +++ b/modules/aggregator/tests/aggregator_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/block/block.api.php b/modules/block/block.api.php index d7453b24..e38f7d6e 100755 --- a/modules/block/block.api.php +++ b/modules/block/block.api.php @@ -363,6 +363,31 @@ function hook_block_list_alter(&$blocks) { } } +/** + * Act on block cache ID (cid) parts before the cid is generated. + * + * This hook allows you to add, remove or modify the custom keys used to + * generate a block cache ID (by default, these keys are set to the block + * module and delta). These keys will be combined with the standard ones + * provided by drupal_render_cid_parts() to generate the final block cache ID. + * + * To change the cache granularity used by drupal_render_cid_parts(), this hook + * cannot be used; instead, set the 'cache' key in the block's definition in + * hook_block_info(). + * + * @params $cid_parts + * An array of elements used to build the cid. + * @param $block + * The block object being acted on. + * + * @see _block_get_cache_id() + */ +function hook_block_cid_parts_alter(&$cid_parts, $block) { + global $user; + // This example shows how to cache a block based on the user's timezone. + $cid_parts[] = $user->timezone; +} + /** * @} End of "addtogroup hooks". */ diff --git a/modules/block/block.info b/modules/block/block.info index e5f66a7d..ad206a88 100755 --- a/modules/block/block.info +++ b/modules/block/block.info @@ -6,8 +6,8 @@ core = 7.x files[] = block.test configure = admin/structure/block -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/block/block.js b/modules/block/block.js index acd3f5ae..721dedf1 100755 --- a/modules/block/block.js +++ b/modules/block/block.js @@ -24,7 +24,7 @@ Drupal.behaviors.blockSettingsSummary = { $('fieldset#edit-node-type', context).drupalSetSummary(function (context) { var vals = []; $('input[type="checkbox"]:checked', context).each(function () { - vals.push($.trim($(this).next('label').text())); + vals.push($.trim($(this).next('label').html())); }); if (!vals.length) { vals.push(Drupal.t('Not restricted')); @@ -35,7 +35,7 @@ Drupal.behaviors.blockSettingsSummary = { $('fieldset#edit-role', context).drupalSetSummary(function (context) { var vals = []; $('input[type="checkbox"]:checked', context).each(function () { - vals.push($.trim($(this).next('label').text())); + vals.push($.trim($(this).next('label').html())); }); if (!vals.length) { vals.push(Drupal.t('Not restricted')); @@ -49,7 +49,7 @@ Drupal.behaviors.blockSettingsSummary = { return Drupal.t('Not customizable'); } else { - return $radio.next('label').text(); + return $radio.next('label').html(); } }); } diff --git a/modules/block/block.module b/modules/block/block.module index 48c80d76..ca41da71 100755 --- a/modules/block/block.module +++ b/modules/block/block.module @@ -16,7 +16,7 @@ define('BLOCK_REGION_NONE', -1); define('BLOCK_CUSTOM_FIXED', 0); /** - * Shows this block by default, but lets individual users hide it. + * Shows this block by default, but lets individual users hide it. */ define('BLOCK_CUSTOM_ENABLED', 1); @@ -59,6 +59,7 @@ function block_help($path, $arg) { $output .= '
' . t('Users with the Administer blocks permission can add custom blocks, which are then listed on the Blocks administration page. Once created, custom blocks behave just like default and module-generated blocks.', array('@blocks' => url('admin/structure/block'), '@block-add' => url('admin/structure/block/add'))) . '
'; $output .= ''; return $output; + case 'admin/structure/block/add': return '

' . t('Use this page to create a new custom block.') . '

'; } @@ -189,6 +190,7 @@ function _block_themes_access($theme) { * @param $theme * The theme whose blocks are being configured. If not set, the default theme * is assumed. + * * @return * The theme that should be used for the block configuration page, or NULL * to indicate that the default theme should be used. @@ -343,14 +345,17 @@ function _block_get_renderable_array($list = array()) { // to perform contextual actions on the help block, and the links needlessly // draw attention on it. if ($key != 'system_main' && $key != 'system_help') { - $build[$key]['#contextual_links']['block'] = array('admin/structure/block/manage', array($block->module, $block->delta)); + $build[$key]['#contextual_links']['block'] = array( + 'admin/structure/block/manage', + array($block->module, $block->delta), + ); } $build[$key] += array( '#block' => $block, '#weight' => ++$weight, ); - $build[$key]['#theme_wrappers'][] ='block'; + $build[$key]['#theme_wrappers'][] = 'block'; } $build['#sorted'] = TRUE; return $build; @@ -386,18 +391,20 @@ function _block_rehash($theme = NULL) { // Gather the blocks defined by modules. foreach (module_implements('block_info') as $module) { $module_blocks = module_invoke($module, 'block_info'); + $delta_list = array(); foreach ($module_blocks as $delta => $block) { // Compile a condition to retrieve this block from the database. - $condition = db_and() - ->condition('module', $module) - ->condition('delta', $delta); - $or->condition($condition); // Add identifiers. + $delta_list[] = $delta; $block['module'] = $module; - $block['delta'] = $delta; - $block['theme'] = $theme; + $block['delta'] = $delta; + $block['theme'] = $theme; $current_blocks[$module][$delta] = $block; } + if (!empty($delta_list)) { + $condition = db_and()->condition('module', $module)->condition('delta', $delta_list); + $or->condition($condition); + } } // Save the blocks defined in code for alter context. $code_blocks = $current_blocks; @@ -644,7 +651,8 @@ function block_theme_initialize($theme) { $regions = system_region_list($theme, REGIONS_VISIBLE); $result = db_query("SELECT * FROM {block} WHERE theme = :theme", array(':theme' => $default_theme), array('fetch' => PDO::FETCH_ASSOC)); foreach ($result as $block) { - // If the region isn't supported by the theme, assign the block to the theme's default region. + // If the region isn't supported by the theme, assign the block to the + // theme's default region. if ($block['status'] && !isset($regions[$block['region']])) { $block['region'] = system_default_region($theme); } @@ -812,17 +820,18 @@ function block_block_list_alter(&$blocks) { // with different case. Ex: /Page, /page, /PAGE. $pages = drupal_strtolower($block->pages); if ($block->visibility < BLOCK_VISIBILITY_PHP) { - // Convert the Drupal path to lowercase + // Convert the Drupal path to lowercase. $path = drupal_strtolower(drupal_get_path_alias($_GET['q'])); // Compare the lowercase internal and lowercase path alias (if any). $page_match = drupal_match_path($path, $pages); if ($path != $_GET['q']) { $page_match = $page_match || drupal_match_path($_GET['q'], $pages); } - // When $block->visibility has a value of 0 (BLOCK_VISIBILITY_NOTLISTED), - // the block is displayed on all pages except those listed in $block->pages. - // When set to 1 (BLOCK_VISIBILITY_LISTED), it is displayed only on those - // pages listed in $block->pages. + // When $block->visibility has a value of 0 + // (BLOCK_VISIBILITY_NOTLISTED), the block is displayed on all pages + // except those listed in $block->pages. When set to 1 + // (BLOCK_VISIBILITY_LISTED), it is displayed only on those pages + // listed in $block->pages. $page_match = !($block->visibility xor $page_match); } elseif (module_exists('php')) { @@ -845,7 +854,8 @@ function block_block_list_alter(&$blocks) { * Render the content and subject for a set of blocks. * * @param $region_blocks - * An array of block objects such as returned for one region by _block_load_blocks(). + * An array of block objects such as returned for one region by + * _block_load_blocks(). * * @return * An array of visible blocks as expected by drupal_render(). @@ -953,6 +963,8 @@ function _block_render_blocks($region_blocks) { * Theme and language contexts are automatically differentiated. * * @param $block + * The block to get the cache_id from. + * * @return * The string used as cache_id for the block. */ @@ -967,6 +979,7 @@ function _block_get_cache_id($block) { // Start with common sub-patterns: block identification, theme, language. $cid_parts[] = $block->module; $cid_parts[] = $block->delta; + drupal_alter('block_cid_parts', $cid_parts, $block); $cid_parts = array_merge($cid_parts, drupal_render_cid_parts($block->cache)); return implode(':', $cid_parts); diff --git a/modules/block/tests/block_test.info b/modules/block/tests/block_test.info index 59a1c5b8..1e7e4e92 100755 --- a/modules/block/tests/block_test.info +++ b/modules/block/tests/block_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/block/tests/themes/block_test_theme/block_test_theme.info b/modules/block/tests/themes/block_test_theme/block_test_theme.info index 0dc755e5..e49025ab 100755 --- a/modules/block/tests/themes/block_test_theme/block_test_theme.info +++ b/modules/block/tests/themes/block_test_theme/block_test_theme.info @@ -13,8 +13,8 @@ regions[footer] = Footer regions[highlighted] = Highlighted regions[help] = Help -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/blog/blog.info b/modules/blog/blog.info index c876ba60..13294f93 100755 --- a/modules/blog/blog.info +++ b/modules/blog/blog.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = blog.test -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/book/book.info b/modules/book/book.info index fd33b32b..fa5b99ba 100755 --- a/modules/book/book.info +++ b/modules/book/book.info @@ -7,8 +7,8 @@ files[] = book.test configure = admin/content/book/settings stylesheets[all][] = book.css -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/color/color.info b/modules/color/color.info index 994091b3..47ae442e 100755 --- a/modules/color/color.info +++ b/modules/color/color.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = color.test -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/comment/comment.info b/modules/comment/comment.info index 77cf16aa..fa206f98 100755 --- a/modules/comment/comment.info +++ b/modules/comment/comment.info @@ -9,8 +9,8 @@ files[] = comment.test configure = admin/content/comment stylesheets[all][] = comment.css -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/contact/contact.info b/modules/contact/contact.info index 08e50f99..f8d1a97d 100755 --- a/modules/contact/contact.info +++ b/modules/contact/contact.info @@ -6,8 +6,8 @@ core = 7.x files[] = contact.test configure = admin/structure/contact -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/contextual/contextual.info b/modules/contextual/contextual.info index c7df84a0..8e82e2d3 100755 --- a/modules/contextual/contextual.info +++ b/modules/contextual/contextual.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = contextual.test -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/dashboard/dashboard.info b/modules/dashboard/dashboard.info index 201d71eb..55b454ef 100755 --- a/modules/dashboard/dashboard.info +++ b/modules/dashboard/dashboard.info @@ -7,8 +7,8 @@ files[] = dashboard.test dependencies[] = block configure = admin/dashboard/customize -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/dblog/dblog.info b/modules/dblog/dblog.info index 9b5f5d74..89de733f 100755 --- a/modules/dblog/dblog.info +++ b/modules/dblog/dblog.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = dblog.test -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/dblog/dblog.module b/modules/dblog/dblog.module index 9183eed6..eb79faff 100755 --- a/modules/dblog/dblog.module +++ b/modules/dblog/dblog.module @@ -144,17 +144,20 @@ function _dblog_get_message_types() { * Note: Some values may be truncated to meet database column size restrictions. */ function dblog_watchdog(array $log_entry) { + if (!function_exists('drupal_substr')) { + require_once DRUPAL_ROOT . '/includes/unicode.inc'; + } Database::getConnection('default', 'default')->insert('watchdog') ->fields(array( 'uid' => $log_entry['uid'], - 'type' => substr($log_entry['type'], 0, 64), + 'type' => drupal_substr($log_entry['type'], 0, 64), 'message' => $log_entry['message'], 'variables' => serialize($log_entry['variables']), 'severity' => $log_entry['severity'], - 'link' => substr($log_entry['link'], 0, 255), + 'link' => drupal_substr($log_entry['link'], 0, 255), 'location' => $log_entry['request_uri'], 'referer' => $log_entry['referer'], - 'hostname' => substr($log_entry['ip'], 0, 128), + 'hostname' => drupal_substr($log_entry['ip'], 0, 128), 'timestamp' => $log_entry['timestamp'], )) ->execute(); diff --git a/modules/dblog/dblog.test b/modules/dblog/dblog.test index bf409c94..03308aff 100755 --- a/modules/dblog/dblog.test +++ b/modules/dblog/dblog.test @@ -119,13 +119,16 @@ class DBLogTestCase extends DrupalWebTestCase { private function generateLogEntries($count, $type = 'custom', $severity = WATCHDOG_NOTICE) { global $base_root; + // Make it just a little bit harder to pass the link part of the test. + $link = urldecode('/content/xo%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A-lake-isabelle'); + // Prepare the fields to be logged $log = array( 'type' => $type, 'message' => 'Log entry added to test the dblog row limit.', 'variables' => array(), 'severity' => $severity, - 'link' => NULL, + 'link' => $link, 'user' => $this->big_user, 'uid' => isset($this->big_user->uid) ? $this->big_user->uid : 0, 'request_uri' => $base_root . request_uri(), @@ -634,4 +637,3 @@ class DBLogTestCase extends DrupalWebTestCase { $this->assertLink(html_entity_decode($message_text), 0, $message); } } - diff --git a/modules/field/field.info b/modules/field/field.info index c154828c..11015a75 100755 --- a/modules/field/field.info +++ b/modules/field/field.info @@ -11,8 +11,8 @@ dependencies[] = field_sql_storage required = TRUE stylesheets[all][] = theme/field.css -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/field/field.info.class.inc b/modules/field/field.info.class.inc index f4f1f630..772cd451 100755 --- a/modules/field/field.info.class.inc +++ b/modules/field/field.info.class.inc @@ -612,10 +612,12 @@ class FieldInfo { // Fill in default values. $display += array( 'label' => 'above', - 'type' => $field_type_info['default_formatter'], 'settings' => array(), 'weight' => 0, ); + if (empty($display['type'])) { + $display['type'] = $field_type_info['default_formatter']; + } if ($display['type'] != 'hidden') { $formatter_type_info = field_info_formatter_types($display['type']); // Fall back to default formatter if formatter type is not available. diff --git a/modules/field/modules/field_sql_storage/field_sql_storage.info b/modules/field/modules/field_sql_storage/field_sql_storage.info index dc1631f9..0828c20a 100755 --- a/modules/field/modules/field_sql_storage/field_sql_storage.info +++ b/modules/field/modules/field_sql_storage/field_sql_storage.info @@ -7,8 +7,8 @@ dependencies[] = field files[] = field_sql_storage.test required = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/field/modules/list/list.info b/modules/field/modules/list/list.info index 49e7814f..7b0c9c23 100755 --- a/modules/field/modules/list/list.info +++ b/modules/field/modules/list/list.info @@ -7,8 +7,8 @@ dependencies[] = field dependencies[] = options files[] = tests/list.test -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/field/modules/list/tests/list_test.info b/modules/field/modules/list/tests/list_test.info index 912d7bf6..9b3e4d6b 100755 --- a/modules/field/modules/list/tests/list_test.info +++ b/modules/field/modules/list/tests/list_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/field/modules/number/number.info b/modules/field/modules/number/number.info index ce55ee33..52553fd7 100755 --- a/modules/field/modules/number/number.info +++ b/modules/field/modules/number/number.info @@ -6,8 +6,8 @@ core = 7.x dependencies[] = field files[] = number.test -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/field/modules/number/number.module b/modules/field/modules/number/number.module index 60465442..d00c55f0 100755 --- a/modules/field/modules/number/number.module +++ b/modules/field/modules/number/number.module @@ -188,7 +188,7 @@ function number_field_formatter_info() { 'label' => t('Default'), 'field types' => array('number_integer'), 'settings' => array( - 'thousand_separator' => ' ', + 'thousand_separator' => '', // The 'decimal_separator' and 'scale' settings are not configurable // through the UI, and will therefore keep their default values. They // are only present so that the 'number_integer' and 'number_decimal' @@ -202,7 +202,7 @@ function number_field_formatter_info() { 'label' => t('Default'), 'field types' => array('number_decimal', 'number_float'), 'settings' => array( - 'thousand_separator' => ' ', + 'thousand_separator' => '', 'decimal_separator' => '.', 'scale' => 2, 'prefix_suffix' => TRUE, diff --git a/modules/field/modules/options/options.info b/modules/field/modules/options/options.info index 1a5ba124..f07ea90b 100755 --- a/modules/field/modules/options/options.info +++ b/modules/field/modules/options/options.info @@ -6,8 +6,8 @@ core = 7.x dependencies[] = field files[] = options.test -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/field/modules/options/options.module b/modules/field/modules/options/options.module index 3862ba77..041b84a6 100755 --- a/modules/field/modules/options/options.module +++ b/modules/field/modules/options/options.module @@ -185,6 +185,7 @@ function _options_properties($type, $multiple, $required, $has_value) { $base = array( 'filter_xss' => FALSE, 'strip_tags' => FALSE, + 'strip_tags_and_unescape' => FALSE, 'empty_option' => FALSE, 'optgroups' => FALSE, ); @@ -195,7 +196,7 @@ function _options_properties($type, $multiple, $required, $has_value) { case 'select': $properties = array( // Select boxes do not support any HTML tag. - 'strip_tags' => TRUE, + 'strip_tags_and_unescape' => TRUE, 'optgroups' => TRUE, ); if ($multiple) { @@ -271,9 +272,16 @@ function _options_prepare_options(&$options, $properties) { _options_prepare_options($options[$value], $properties); } else { + // The 'strip_tags' option is deprecated. Use 'strip_tags_and_unescape' + // when plain text is required (and where the output will be run through + // check_plain() before being inserted back into HTML) or 'filter_xss' + // when HTML is required. if ($properties['strip_tags']) { $options[$value] = strip_tags($label); } + if ($properties['strip_tags_and_unescape']) { + $options[$value] = decode_entities(strip_tags($label)); + } if ($properties['filter_xss']) { $options[$value] = field_filter_xss($label); } diff --git a/modules/field/modules/options/options.test b/modules/field/modules/options/options.test index 7183311b..0e19f52f 100755 --- a/modules/field/modules/options/options.test +++ b/modules/field/modules/options/options.test @@ -24,7 +24,7 @@ class OptionsWidgetsTestCase extends FieldTestCase { 'cardinality' => 1, 'settings' => array( // Make sure that 0 works as an option. - 'allowed_values' => array(0 => 'Zero', 1 => 'One', 2 => 'Some & unescaped markup'), + 'allowed_values' => array(0 => 'Zero', 1 => 'One', 2 => 'Some & unescaped markup', 3 => 'Some HTML encoded markup with < & >'), ), ); $this->card_1 = field_create_field($this->card_1); @@ -233,6 +233,7 @@ class OptionsWidgetsTestCase extends FieldTestCase { $this->assertNoOptionSelected("edit-card-1-$langcode", 1); $this->assertNoOptionSelected("edit-card-1-$langcode", 2); $this->assertRaw('Some dangerous & unescaped markup', 'Option text was properly filtered.'); + $this->assertRaw('Some HTML encoded markup with < & >', 'HTML entities in option text were properly handled and not double-encoded'); // Submit form: select invalid 'none' option. $edit = array("card_1[$langcode]" => '_none'); diff --git a/modules/field/modules/text/text.info b/modules/field/modules/text/text.info index e8db6294..b3cf7cf0 100755 --- a/modules/field/modules/text/text.info +++ b/modules/field/modules/text/text.info @@ -7,8 +7,8 @@ dependencies[] = field files[] = text.test required = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/field/modules/text/text.module b/modules/field/modules/text/text.module index 68fc3cb4..bf0d29d5 100755 --- a/modules/field/modules/text/text.module +++ b/modules/field/modules/text/text.module @@ -223,11 +223,13 @@ function text_field_formatter_settings_form($field, $instance, $view_mode, $form if (strpos($display['type'], '_trimmed') !== FALSE) { $element['trim_length'] = array( - '#title' => t('Trim length'), + '#title' => t('Trimmed limit'), '#type' => 'textfield', + '#field_suffix' => t('characters'), '#size' => 10, '#default_value' => $settings['trim_length'], '#element_validate' => array('element_validate_integer_positive'), + '#description' => t('If the summary is not set, the trimmed %label field will be shorter than this character limit.', array('%label' => $instance['label'])), '#required' => TRUE, ); } @@ -245,7 +247,7 @@ function text_field_formatter_settings_summary($field, $instance, $view_mode) { $summary = ''; if (strpos($display['type'], '_trimmed') !== FALSE) { - $summary = t('Trim length') . ': ' . check_plain($settings['trim_length']); + $summary = t('Trimmed limit: @trim_length characters', array('@trim_length' => $settings['trim_length'])); } return $summary; diff --git a/modules/field/tests/field_test.info b/modules/field/tests/field_test.info index 0e66b280..8bfe1719 100755 --- a/modules/field/tests/field_test.info +++ b/modules/field/tests/field_test.info @@ -6,8 +6,8 @@ files[] = field_test.entity.inc version = VERSION hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/field_ui/field_ui.info b/modules/field_ui/field_ui.info index aa1e46b7..ef904d2e 100755 --- a/modules/field_ui/field_ui.info +++ b/modules/field_ui/field_ui.info @@ -6,8 +6,8 @@ core = 7.x dependencies[] = field files[] = field_ui.test -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/file/file.field.inc b/modules/file/file.field.inc index 794f16e6..d592381b 100755 --- a/modules/file/file.field.inc +++ b/modules/file/file.field.inc @@ -632,7 +632,7 @@ function file_field_widget_process($element, &$form_state, $form) { $element['#theme'] = 'file_widget'; // Add the display field if enabled. - if (!empty($field['settings']['display_field']) && $item['fid']) { + if (!empty($field['settings']['display_field'])) { $element['display'] = array( '#type' => empty($item['fid']) ? 'hidden' : 'checkbox', '#title' => t('Include file in display'), diff --git a/modules/file/file.info b/modules/file/file.info index edb2d2de..aebd7f92 100755 --- a/modules/file/file.info +++ b/modules/file/file.info @@ -6,8 +6,8 @@ core = 7.x dependencies[] = field files[] = tests/file.test -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/file/file.module b/modules/file/file.module index ae452a68..9e091af0 100755 --- a/modules/file/file.module +++ b/modules/file/file.module @@ -92,7 +92,7 @@ function file_theme() { 'variables' => array('file' => NULL, 'icon_directory' => NULL), ), 'file_icon' => array( - 'variables' => array('file' => NULL, 'icon_directory' => NULL), + 'variables' => array('file' => NULL, 'icon_directory' => NULL, 'alt' => ''), ), 'file_managed_file' => array( 'render element' => 'element', @@ -529,14 +529,19 @@ function file_managed_file_value(&$element, $input = FALSE, $form_state = NULL) // publicly accessible, with no download restrictions; for security // reasons all other schemes must go through the file_download_access() // check. - if (in_array(file_uri_scheme($file->uri), variable_get('file_public_schema', array('public'))) || file_download_access($file->uri)) { - $fid = $file->fid; - } - // If the current user doesn't have access, don't let the file be - // changed. - else { + if (!in_array(file_uri_scheme($file->uri), variable_get('file_public_schema', array('public'))) && !file_download_access($file->uri)) { $force_default = TRUE; } + // Temporary files that belong to other users should never be allowed. + // Since file ownership can't be determined for anonymous users, they + // are not allowed to reuse temporary files at all. + elseif ($file->status != FILE_STATUS_PERMANENT && (!$GLOBALS['user']->uid || $file->uid != $GLOBALS['user']->uid)) { + $force_default = TRUE; + } + // If all checks pass, allow the file to be changed. + else { + $fid = $file->fid; + } } } } @@ -749,7 +754,32 @@ function theme_file_link($variables) { $icon_directory = $variables['icon_directory']; $url = file_create_url($file->uri); - $icon = theme('file_icon', array('file' => $file, 'icon_directory' => $icon_directory)); + + // Human-readable names, for use as text-alternatives to icons. + $mime_name = array( + 'application/msword' => t('Microsoft Office document icon'), + 'application/vnd.ms-excel' => t('Office spreadsheet icon'), + 'application/vnd.ms-powerpoint' => t('Office presentation icon'), + 'application/pdf' => t('PDF icon'), + 'video/quicktime' => t('Movie icon'), + 'audio/mpeg' => t('Audio icon'), + 'audio/wav' => t('Audio icon'), + 'image/jpeg' => t('Image icon'), + 'image/png' => t('Image icon'), + 'image/gif' => t('Image icon'), + 'application/zip' => t('Package icon'), + 'text/html' => t('HTML icon'), + 'text/plain' => t('Plain text icon'), + 'application/octet-stream' => t('Binary Data'), + ); + + $mimetype = file_get_mimetype($file->uri); + + $icon = theme('file_icon', array( + 'file' => $file, + 'icon_directory' => $icon_directory, + 'alt' => !empty($mime_name[$mimetype]) ? $mime_name[$mimetype] : t('File'), + )); // Set options as per anchor format described at // http://microformats.org/wiki/file-format-examples @@ -779,16 +809,19 @@ function theme_file_link($variables) { * - file: A file object for which to make an icon. * - icon_directory: (optional) A path to a directory of icons to be used for * files. Defaults to the value of the "file_icon_directory" variable. + * - alt: (optional) The alternative text to represent the icon in text-based + * browsers. Defaults to an empty string. * * @ingroup themeable */ function theme_file_icon($variables) { $file = $variables['file']; + $alt = $variables['alt']; $icon_directory = $variables['icon_directory']; $mime = check_plain($file->filemime); $icon_url = file_icon_url($file, $icon_directory); - return ''; + return '' . check_plain($alt) . ''; } /** diff --git a/modules/file/tests/file.test b/modules/file/tests/file.test index 5c19d001..6d7cb4bc 100755 --- a/modules/file/tests/file.test +++ b/modules/file/tests/file.test @@ -218,6 +218,30 @@ class FileFieldTestCase extends DrupalWebTestCase { $message = isset($message) ? $message : format_string('File %file is permanent.', array('%file' => $file->uri)); $this->assertTrue($file->status == FILE_STATUS_PERMANENT, $message); } + + /** + * Creates a temporary file, for a specific user. + * + * @param string $data + * A string containing the contents of the file. + * @param int $uid + * The user ID of the file owner. + * + * @return object + * A file object, or FALSE on error. + */ + function createTemporaryFile($data, $uid = NULL) { + $file = file_save_data($data, NULL, NULL); + + if ($file) { + $file->uid = isset($uid) ? $uid : $this->admin_user->uid; + // Change the file status to be temporary. + $file->status = NULL; + return file_save($file); + } + + return $file; + } } /** @@ -526,6 +550,120 @@ class FileFieldWidgetTestCase extends FileFieldTestCase { } } + /** + * Tests exploiting the temporary file removal of another user using fid. + */ + function testTemporaryFileRemovalExploit() { + // Create a victim user. + $victim_user = $this->drupalCreateUser(); + + // Create an attacker user. + $attacker_user = $this->drupalCreateUser(array( + 'access content', + 'create page content', + 'edit any page content', + )); + + // Log in as the attacker user. + $this->drupalLogin($attacker_user); + + // Perform tests using the newly created users. + $this->doTestTemporaryFileRemovalExploit($victim_user->uid, $attacker_user->uid); + } + + /** + * Tests exploiting the temporary file removal for anonymous users using fid. + */ + public function testTemporaryFileRemovalExploitAnonymous() { + // Set up an anonymous victim user. + $victim_uid = 0; + + // Set up an anonymous attacker user. + $attacker_uid = 0; + + // Set up permissions for anonymous attacker user. + user_role_change_permissions(DRUPAL_ANONYMOUS_RID, array( + 'access content' => TRUE, + 'create page content' => TRUE, + 'edit any page content' => TRUE, + )); + + // In order to simulate being the anonymous attacker user, we need to log + // out here since setUp() has logged in the admin. + $this->drupalLogout(); + + // Perform tests using the newly set up users. + $this->doTestTemporaryFileRemovalExploit($victim_uid, $attacker_uid); + } + + /** + * Helper for testing exploiting the temporary file removal using fid. + * + * @param int $victim_uid + * The victim user ID. + * @param int $attacker_uid + * The attacker user ID. + */ + protected function doTestTemporaryFileRemovalExploit($victim_uid, $attacker_uid) { + // Use 'page' instead of 'article', so that the 'article' image field does + // not conflict with this test. If in the future the 'page' type gets its + // own default file or image field, this test can be made more robust by + // using a custom node type. + $type_name = 'page'; + $field_name = 'test_file_field'; + $this->createFileField($field_name, $type_name); + + $test_file = $this->getTestFile('text'); + foreach (array('nojs', 'js') as $type) { + // Create a temporary file owned by the anonymous victim user. This will be + // as if they had uploaded the file, but not saved the node they were + // editing or creating. + $victim_tmp_file = $this->createTemporaryFile('some text', $victim_uid); + $victim_tmp_file = file_load($victim_tmp_file->fid); + $this->assertTrue($victim_tmp_file->status != FILE_STATUS_PERMANENT, 'New file saved to disk is temporary.'); + $this->assertFalse(empty($victim_tmp_file->fid), 'New file has a fid'); + $this->assertEqual($victim_uid, $victim_tmp_file->uid, 'New file belongs to the victim user'); + + // Have attacker create a new node with a different uploaded file and + // ensure it got uploaded successfully. + // @todo Can we test AJAX? See https://www.drupal.org/node/2538260 + $edit = array( + 'title' => $type . '-title', + ); + + // Attach a file to a node. + $langcode = LANGUAGE_NONE; + $edit['files[' . $field_name . '_' . $langcode . '_0]'] = drupal_realpath($test_file->uri); + $this->drupalPost("node/add/$type_name", $edit, 'Save'); + $node = $this->drupalGetNodeByTitle($edit['title']); + $node_file = file_load($node->{$field_name}[$langcode][0]['fid']); + $this->assertFileExists($node_file, 'New file saved to disk on node creation.'); + $this->assertEqual($attacker_uid, $node_file->uid, 'New file belongs to the attacker.'); + + // Ensure the file can be downloaded. + $this->drupalGet(file_create_url($node_file->uri)); + $this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the shipped file.'); + + // "Click" the remove button (emulating either a nojs or js submission). + // In this POST request, the attacker "guesses" the fid of the victim's + // temporary file and uses that to remove this file. + $this->drupalGet('node/' . $node->nid . '/edit'); + switch ($type) { + case 'nojs': + $this->drupalPost(NULL, array("{$field_name}[$langcode][0][fid]" => (string) $victim_tmp_file->fid), 'Remove'); + break; + case 'js': + $button = $this->xpath('//input[@type="submit" and @value="Remove"]'); + $this->drupalPostAJAX(NULL, array("{$field_name}[$langcode][0][fid]" => (string) $victim_tmp_file->fid), array((string) $button[0]['name'] => (string) $button[0]['value'])); + break; + } + + // The victim's temporary file should not be removed by the attacker's + // POST request. + $this->assertFileExists($victim_tmp_file); + } + } + /** * Tests upload and remove buttons for multiple multi-valued File fields. */ @@ -951,6 +1089,34 @@ class FileFieldDisplayTestCase extends FileFieldTestCase { $this->assertRaw($field_name . '[' . LANGUAGE_NONE . '][0][display]', 'First file appears as expected.'); $this->assertRaw($field_name . '[' . LANGUAGE_NONE . '][1][display]', 'Second file appears as expected.'); } + + /** + * Tests default display of File Field. + */ + function testDefaultFileFieldDisplay() { + $field_name = strtolower($this->randomName()); + $type_name = 'article'; + $field_settings = array( + 'display_field' => '1', + 'display_default' => '0', + ); + $instance_settings = array( + 'description_field' => '1', + ); + $widget_settings = array(); + $this->createFileField($field_name, $type_name, $field_settings, $instance_settings, $widget_settings); + $field = field_info_field($field_name); + $instance = field_info_instance('node', $field_name, $type_name); + + $test_file = $this->getTestFile('text'); + + // Create a new node with the uploaded file. + $nid = $this->uploadNodeFile($test_file, $field_name, $type_name); + + $this->drupalGet('node/' . $nid . '/edit'); + $this->assertFieldByXPath('//input[@type="checkbox" and @name="' . $field_name . '[und][0][display]"]', NULL, 'Default file display checkbox field exists.'); + $this->assertFieldByXPath('//input[@type="checkbox" and @name="' . $field_name . '[und][0][display]" and not(@checked)]', NULL, 'Default file display is off.'); + } } /** diff --git a/modules/file/tests/file_module_test.info b/modules/file/tests/file_module_test.info index 368a7a61..a9675621 100755 --- a/modules/file/tests/file_module_test.info +++ b/modules/file/tests/file_module_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/filter/filter.info b/modules/filter/filter.info index d0779b50..7584d6ed 100755 --- a/modules/filter/filter.info +++ b/modules/filter/filter.info @@ -7,8 +7,8 @@ files[] = filter.test required = TRUE configure = admin/config/content/formats -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/filter/filter.module b/modules/filter/filter.module index f4bab9e6..c710ee70 100755 --- a/modules/filter/filter.module +++ b/modules/filter/filter.module @@ -93,6 +93,14 @@ function filter_menu() { 'type' => MENU_SUGGESTED_ITEM, 'file' => 'filter.pages.inc', ); + $items['filter/tips/%filter_format'] = array( + 'title' => 'Compose tips', + 'page callback' => 'filter_tips_long', + 'page arguments' => array(2), + 'access callback' => 'filter_access', + 'access arguments' => array(2), + 'file' => 'filter.pages.inc', + ); $items['admin/config/content/formats'] = array( 'title' => 'Text formats', 'description' => 'Configure how content input by users is filtered, including allowed HTML tags. Also allows enabling of module-provided filters.', @@ -1119,18 +1127,23 @@ function filter_dom_serialize($dom_document) { $body_node = $dom_document->getElementsByTagName('body')->item(0); $body_content = ''; - foreach ($body_node->getElementsByTagName('script') as $node) { - filter_dom_serialize_escape_cdata_element($dom_document, $node); - } + if ($body_node !== NULL) { + foreach ($body_node->getElementsByTagName('script') as $node) { + filter_dom_serialize_escape_cdata_element($dom_document, $node); + } - foreach ($body_node->getElementsByTagName('style') as $node) { - filter_dom_serialize_escape_cdata_element($dom_document, $node, '/*', '*/'); - } + foreach ($body_node->getElementsByTagName('style') as $node) { + filter_dom_serialize_escape_cdata_element($dom_document, $node, '/*', '*/'); + } - foreach ($body_node->childNodes as $child_node) { - $body_content .= $dom_document->saveXML($child_node); + foreach ($body_node->childNodes as $child_node) { + $body_content .= $dom_document->saveXML($child_node); + } + return preg_replace('|<([^> ]*)/>|i', '<$1 />', $body_content); + } + else { + return $body_content; } - return preg_replace('|<([^> ]*)/>|i', '<$1 />', $body_content); } /** @@ -1484,7 +1497,7 @@ function _filter_url($text, $filter) { $tasks['_filter_url_parse_full_links'] = $pattern; // Match e-mail addresses. - $url_pattern = "[A-Za-z0-9._-]{1,254}@(?:$domain)"; + $url_pattern = "[A-Za-z0-9._+-]{1,254}@(?:$domain)"; $pattern = "`($url_pattern)`"; $tasks['_filter_url_parse_email_links'] = $pattern; diff --git a/modules/filter/filter.pages.inc b/modules/filter/filter.pages.inc index e602bcef..0f13da84 100755 --- a/modules/filter/filter.pages.inc +++ b/modules/filter/filter.pages.inc @@ -14,10 +14,9 @@ * @see filter_menu() * @see theme_filter_tips() */ -function filter_tips_long() { - $format_id = arg(2); - if ($format_id) { - $output = theme('filter_tips', array('tips' => _filter_tips($format_id, TRUE), 'long' => TRUE)); +function filter_tips_long($format = NULL) { + if (!empty($format)) { + $output = theme('filter_tips', array('tips' => _filter_tips($format->format, TRUE), 'long' => TRUE)); } else { $output = theme('filter_tips', array('tips' => _filter_tips(-1, TRUE), 'long' => TRUE)); diff --git a/modules/filter/filter.test b/modules/filter/filter.test index ddea6afb..d558fa3b 100755 --- a/modules/filter/filter.test +++ b/modules/filter/filter.test @@ -555,6 +555,27 @@ class FilterFormatAccessTestCase extends DrupalWebTestCase { $this->assertTrue(isset($options[$this->allowed_format->format]), 'The allowed text format appears as an option when adding a new node.'); $this->assertFalse(isset($options[$this->disallowed_format->format]), 'The disallowed text format does not appear as an option when adding a new node.'); $this->assertTrue(isset($options[filter_fallback_format()]), 'The fallback format appears as an option when adding a new node.'); + + // Check regular user access to the filter tips pages. + $this->drupalGet('filter/tips/' . $this->allowed_format->format); + $this->assertResponse(200); + $this->drupalGet('filter/tips/' . $this->disallowed_format->format); + $this->assertResponse(403); + $this->drupalGet('filter/tips/' . filter_fallback_format()); + $this->assertResponse(200); + $this->drupalGet('filter/tips/invalid-format'); + $this->assertResponse(404); + + // Check admin user access to the filter tips pages. + $this->drupalLogin($this->admin_user); + $this->drupalGet('filter/tips/' . $this->allowed_format->format); + $this->assertResponse(200); + $this->drupalGet('filter/tips/' . $this->disallowed_format->format); + $this->assertResponse(200); + $this->drupalGet('filter/tips/' . filter_fallback_format()); + $this->assertResponse(200); + $this->drupalGet('filter/tips/invalid-format'); + $this->assertResponse(404); } /** @@ -1273,6 +1294,7 @@ class FilterUnitTestCase extends DrupalUnitTestCase { // Create a e-mail that is too long. $long_email = str_repeat('a', 254) . '@example.com'; $too_long_email = str_repeat('b', 255) . '@example.com'; + $email_with_plus_sign = 'one+two@example.com'; // Filter selection/pattern matching. @@ -1286,12 +1308,13 @@ http://example.com or www.example.com ), // MAILTO URLs. ' -person@example.com or mailto:person2@example.com or ' . $long_email . ' but not ' . $too_long_email . ' +person@example.com or mailto:person2@example.com or ' . $email_with_plus_sign . ' or ' . $long_email . ' but not ' . $too_long_email . ' ' => array( 'person@example.com' => TRUE, 'mailto:person2@example.com' => TRUE, '' . $long_email . '' => TRUE, '' . $too_long_email . '' => FALSE, + '' . $email_with_plus_sign . '' => TRUE, ), // URI parts and special characters. ' @@ -1983,3 +2006,26 @@ class FilterSettingsTestCase extends DrupalWebTestCase { } } } + +/** + * Tests DOMDocument serialization. + */ +class FilterDOMSerializeTestCase extends DrupalWebTestCase { + + public static function getInfo() { + return array( + 'name' => 'Serialization', + 'description' => 'Test serialization of DOMDocument objects.', + 'group' => 'Filter', + ); + } + + /** + * Tests empty DOMDocument object. + */ + function testFilterEmptyDOMSerialization() { + $document = new DOMDocument(); + $result = filter_dom_serialize($document); + $this->assertEqual('', $result); + } +} diff --git a/modules/forum/forum.info b/modules/forum/forum.info index 5312ec02..2f10e395 100755 --- a/modules/forum/forum.info +++ b/modules/forum/forum.info @@ -9,8 +9,8 @@ files[] = forum.test configure = admin/structure/forum stylesheets[all][] = forum.css -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/help/help.info b/modules/help/help.info index f51b9062..accbca80 100755 --- a/modules/help/help.info +++ b/modules/help/help.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = help.test -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/image/image.info b/modules/image/image.info index 3e9ef6be..b6bd514d 100755 --- a/modules/image/image.info +++ b/modules/image/image.info @@ -7,8 +7,8 @@ dependencies[] = file files[] = image.test configure = admin/config/media/image-styles -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/image/image.module b/modules/image/image.module index fac8de95..dab88361 100755 --- a/modules/image/image.module +++ b/modules/image/image.module @@ -64,7 +64,7 @@ function image_help($path, $arg) { $effect = image_effect_definition_load($arg[7]); return isset($effect['help']) ? ('

' . $effect['help'] . '

') : NULL; case 'admin/config/media/image-styles/edit/%/effects/%': - $effect = ($arg[5] == 'add') ? image_effect_definition_load($arg[6]) : image_effect_load($arg[6], $arg[4]); + $effect = ($arg[5] == 'add') ? image_effect_definition_load($arg[6]) : image_effect_load($arg[7], $arg[5]); return isset($effect['help']) ? ('

' . $effect['help'] . '

') : NULL; } } @@ -801,6 +801,8 @@ function image_style_options($include_empty = TRUE, $output = CHECK_PLAIN) { * * @param $style * The image style + * @param $scheme + * The file scheme, for example 'public' for public files. */ function image_style_deliver($style, $scheme) { $args = func_get_args(); @@ -833,8 +835,8 @@ function image_style_deliver($style, $scheme) { file_download($scheme, file_uri_target($derivative_uri)); } else { - $headers = module_invoke_all('file_download', $image_uri); - if (in_array(-1, $headers) || empty($headers)) { + $headers = file_download_headers($image_uri); + if (empty($headers)) { return MENU_ACCESS_DENIED; } if (count($headers)) { diff --git a/modules/image/image.test b/modules/image/image.test index 35919794..42f8d8bc 100755 --- a/modules/image/image.test +++ b/modules/image/image.test @@ -77,6 +77,24 @@ class ImageFieldTestCase extends DrupalWebTestCase { return field_create_instance($instance); } + /** + * Create a random style. + * + * @return array + * A list containing the details of the generated image style. + */ + function createRandomStyle() { + $style_name = strtolower($this->randomName(10)); + $style_label = $this->randomString(); + image_style_save(array('name' => $style_name, 'label' => $style_label)); + $style_path = 'admin/config/media/image-styles/edit/' . $style_name; + return array( + 'name' => $style_name, + 'label' => $style_label, + 'path' => $style_path, + ); + } + /** * Upload an image to a node. * @@ -183,6 +201,22 @@ class ImageStylesPathAndUrlTestCase extends DrupalWebTestCase { $this->assertResponse(404, 'Accessing an image style URL with a source image that does not exist provides a 404 error response.'); } + /** + * Test that we do not pass an array to drupal_add_http_header. + */ + function testImageContentTypeHeaders() { + $files = $this->drupalGetTestFiles('image'); + $file = array_shift($files); + // Copy the test file to private folder. + $private_file = file_copy($file, 'private://', FILE_EXISTS_RENAME); + // Tell image_module_test module to return the headers we want to test. + variable_set('image_module_test_invalid_headers', $private_file->uri); + // Invoke image_style_deliver so it will try to set headers. + $generated_url = image_style_url($this->style_name, $private_file->uri); + $this->drupalGet($generated_url); + variable_del('image_module_test_invalid_headers'); + } + /** * Test image_style_url(). */ @@ -469,6 +503,58 @@ class ImageEffectsUnitTest extends ImageToolkitTestCase { } } +/** + * Tests the administrative user interface. + */ +class ImageAdminUiTestCase extends ImageFieldTestCase { + public static function getInfo() { + return array( + 'name' => 'Administrative user interface', + 'description' => 'Tests the forms used in the administrative user interface.', + 'group' => 'Image', + ); + } + + function setUp() { + parent::setUp(array('image')); + } + + /** + * Test if the help text is available on the add effect form. + */ + function testAddEffectHelpText() { + // Create a random image style. + $style = $this->createRandomStyle(); + + // Open the add effect form and check for the help text. + $this->drupalGet($style['path'] . '/add/image_crop'); + $this->assertText(t('Cropping will remove portions of an image to make it the specified dimensions.'), 'The image style effect help text was displayed on the add effect page.'); + } + + /** + * Test if the help text is available on the edit effect form. + */ + function testEditEffectHelpText() { + // Create a random image style. + $random_style = $this->createRandomStyle(); + + // Add the crop effect to the image style. + $edit = array(); + $edit['data[width]'] = 20; + $edit['data[height]'] = 20; + $this->drupalPost($random_style['path'] . '/add/image_crop', $edit, t('Add effect')); + + // Open the edit effect form and check for the help text. + drupal_static_reset('image_styles'); + $style = image_style_load($random_style['name']); + + foreach ($style['effects'] as $ieid => $effect) { + $this->drupalGet($random_style['path'] . '/effects/' . $ieid); + $this->assertText(t('Cropping will remove portions of an image to make it the specified dimensions.'), 'The image style effect help text was displayed on the edit effect page.'); + } + } +} + /** * Tests creation, deletion, and editing of image styles and effects. */ diff --git a/modules/image/tests/image_module_test.info b/modules/image/tests/image_module_test.info index 702e8901..85a47e21 100755 --- a/modules/image/tests/image_module_test.info +++ b/modules/image/tests/image_module_test.info @@ -6,8 +6,8 @@ core = 7.x files[] = image_module_test.module hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/image/tests/image_module_test.module b/modules/image/tests/image_module_test.module index 8a322fb9..fc66d9b8 100755 --- a/modules/image/tests/image_module_test.module +++ b/modules/image/tests/image_module_test.module @@ -9,6 +9,9 @@ function image_module_test_file_download($uri) { if (variable_get('image_module_test_file_download', FALSE) == $uri) { return array('X-Image-Owned-By' => 'image_module_test'); } + if (variable_get('image_module_test_invalid_headers', FALSE) == $uri) { + return array('Content-Type' => 'image/png'); + } } /** diff --git a/modules/locale/locale.info b/modules/locale/locale.info index 61abe8a6..b2208f7a 100755 --- a/modules/locale/locale.info +++ b/modules/locale/locale.info @@ -6,8 +6,8 @@ core = 7.x files[] = locale.test configure = admin/config/regional/language -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/locale/tests/locale_test.info b/modules/locale/tests/locale_test.info index 5e8d50f7..23493fe3 100755 --- a/modules/locale/tests/locale_test.info +++ b/modules/locale/tests/locale_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/menu/menu.info b/modules/menu/menu.info index 7212d9ab..b135e395 100755 --- a/modules/menu/menu.info +++ b/modules/menu/menu.info @@ -6,8 +6,8 @@ core = 7.x files[] = menu.test configure = admin/structure/menu -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/menu/menu.test b/modules/menu/menu.test index a9bdb5f2..8e69efe5 100755 --- a/modules/menu/menu.test +++ b/modules/menu/menu.test @@ -72,6 +72,17 @@ class MenuTestCase extends DrupalWebTestCase { $saved_item = menu_link_load($item['mlid']); $this->assertEqual($description, $saved_item['options']['attributes']['title'], 'Saving an existing link updates the description (title attribute)'); $this->resetMenuLink($item, $old_title); + + // Test that the page title is correct when a local task appears in a + // top-level menu item. See https://www.drupal.org/node/1973262. + $item = $this->addMenuLink(0, 'user/register', 'user-menu'); + $this->drupalGet('user/password'); + $this->assertNoTitle('Home | Drupal'); + $this->drupalLogout(); + $this->drupalGet('user/register'); + $this->assertTitle($item['link_title'] . ' | Drupal'); + $this->drupalGet('user'); + $this->assertNoTitle('Home | Drupal'); } /** diff --git a/modules/node/node.admin.inc b/modules/node/node.admin.inc index 35f4c1d5..eead4ea9 100755 --- a/modules/node/node.admin.inc +++ b/modules/node/node.admin.inc @@ -329,6 +329,8 @@ function _node_mass_update_helper($nid, $updates) { } /** + * Implements callback_batch_operation(). + * * Executes a batch operation for node_mass_update(). * * @param array $nodes @@ -367,7 +369,9 @@ function _node_mass_update_batch_process($nodes, $updates, &$context) { } /** - * Menu callback: Reports the status of batch operation for node_mass_update(). + * Implements callback_batch_finished(). + * + * Reports the status of batch operation for node_mass_update(). * * @param bool $success * A boolean indicating whether the batch mass update operation successfully @@ -504,14 +508,17 @@ function node_admin_nodes() { $options = array(); foreach ($nodes as $node) { $langcode = entity_language('node', $node); - $l_options = $langcode != LANGUAGE_NONE && isset($languages[$langcode]) ? array('language' => $languages[$langcode]) : array(); + $uri = entity_uri('node', $node); + if ($langcode != LANGUAGE_NONE && isset($languages[$langcode])) { + $uri['options']['language'] = $languages[$langcode]; + } $options[$node->nid] = array( 'title' => array( 'data' => array( '#type' => 'link', '#title' => $node->title, - '#href' => 'node/' . $node->nid, - '#options' => $l_options, + '#href' => $uri['path'], + '#options' => $uri['options'], '#suffix' => ' ' . theme('mark', array('type' => node_mark($node->nid, $node->changed))), ), ), diff --git a/modules/node/node.api.php b/modules/node/node.api.php index 9a4d0959..c8176a7d 100755 --- a/modules/node/node.api.php +++ b/modules/node/node.api.php @@ -950,7 +950,7 @@ function hook_node_info() { * 'recent', or 'comments'. The values should be arrays themselves, with the * following keys available: * - title: (required) The human readable name of the ranking mechanism. - * - join: (optional) The part of a query string to join to any additional + * - join: (optional) An array with information to join any additional * necessary table. This is not necessary if the table required is already * joined to by the base query, such as for the {node} table. Other tables * should use the full table name as an alias to avoid naming collisions. @@ -974,7 +974,12 @@ function hook_ranking() { 'title' => t('Average vote'), // Note that we use i.sid, the search index's search item id, rather than // n.nid. - 'join' => 'LEFT JOIN {vote_node_data} vote_node_data ON vote_node_data.nid = i.sid', + 'join' => array( + 'type' => 'LEFT', + 'table' => 'vote_node_data', + 'alias' => 'vote_node_data', + 'on' => 'vote_node_data.nid = i.sid', + ), // The highest possible score should be 1, and the lowest possible score, // always 0, should be 0. 'score' => 'vote_node_data.average / CAST(%f AS DECIMAL)', @@ -1079,19 +1084,9 @@ function hook_delete($node) { * @ingroup node_api_hooks */ function hook_prepare($node) { - $file = file_save_upload($field_name, _image_filename($file->filename, NULL, TRUE)); - if ($file) { - if (!image_get_info($file->uri)) { - form_set_error($field_name, t('Uploaded file is not a valid image')); - return; - } + if (!isset($node->mymodule_value)) { + $node->mymodule_value = 'foo'; } - else { - return; - } - $node->images['_original'] = $file->uri; - _image_build_derivatives($node, TRUE); - $node->new_file = TRUE; } /** diff --git a/modules/node/node.info b/modules/node/node.info index 758179dc..58f56696 100755 --- a/modules/node/node.info +++ b/modules/node/node.info @@ -9,8 +9,8 @@ required = TRUE configure = admin/structure/types stylesheets[all][] = node.css -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/node/node.module b/modules/node/node.module index 7a6246d5..1d88834c 100755 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -2953,7 +2953,10 @@ function node_search_validate($form, &$form_state) { * system. When adding a node listing to your module, be sure to use a dynamic * query created by db_select() and add a tag of "node_access". This will allow * modules dealing with node access to ensure only nodes to which the user has - * access are retrieved, through the use of hook_query_TAG_alter(). + * access are retrieved, through the use of hook_query_TAG_alter(). Tagging a + * query with "node_access" does not check the published/unpublished status of + * nodes, so the base query is responsible for ensuring that unpublished nodes + * are not displayed to inappropriate users. * * Note: Even a single module returning NODE_ACCESS_DENY from hook_node_access() * will block access to the node. Therefore, implementers should take care to @@ -3669,6 +3672,8 @@ function node_access_rebuild($batch_mode = FALSE) { } /** + * Implements callback_batch_operation(). + * * Performs batch operation for node_access_rebuild(). * * This is a multistep operation: we go through all nodes by packs of 20. The @@ -3683,7 +3688,7 @@ function _node_access_rebuild_batch_operation(&$context) { // Initiate multistep processing. $context['sandbox']['progress'] = 0; $context['sandbox']['current_node'] = 0; - $context['sandbox']['max'] = db_query('SELECT COUNT(DISTINCT nid) FROM {node}')->fetchField(); + $context['sandbox']['max'] = db_query('SELECT COUNT(nid) FROM {node}')->fetchField(); } // Process the next 20 nodes. @@ -3707,6 +3712,8 @@ function _node_access_rebuild_batch_operation(&$context) { } /** + * Implements callback_batch_finished(). + * * Performs post-processing for node_access_rebuild(). * * @param bool $success diff --git a/modules/node/node.pages.inc b/modules/node/node.pages.inc index cc3908e3..72b0ea7c 100755 --- a/modules/node/node.pages.inc +++ b/modules/node/node.pages.inc @@ -396,7 +396,6 @@ function node_preview($node) { $cloned_node->changed = REQUEST_TIME; $nodes = array($cloned_node->nid => $cloned_node); - field_attach_prepare_view('node', $nodes, 'full'); // Display a preview of the node. if (!form_get_errors()) { diff --git a/modules/node/node.test b/modules/node/node.test index 5c9118eb..4ffc88e8 100755 --- a/modules/node/node.test +++ b/modules/node/node.test @@ -457,10 +457,70 @@ class PagePreviewTestCase extends DrupalWebTestCase { } function setUp() { - parent::setUp(); + parent::setUp(array('taxonomy', 'node')); $web_user = $this->drupalCreateUser(array('edit own page content', 'create page content')); $this->drupalLogin($web_user); + + // Add a vocabulary so we can test different view modes. + $vocabulary = (object) array( + 'name' => $this->randomName(), + 'description' => $this->randomName(), + 'machine_name' => drupal_strtolower($this->randomName()), + 'help' => '', + 'nodes' => array('page' => 'page'), + ); + taxonomy_vocabulary_save($vocabulary); + + $this->vocabulary = $vocabulary; + + // Add a term to the vocabulary. + $term = (object) array( + 'name' => $this->randomName(), + 'description' => $this->randomName(), + // Use the first available text format. + 'format' => db_query_range('SELECT format FROM {filter_format}', 0, 1)->fetchField(), + 'vid' => $this->vocabulary->vid, + 'vocabulary_machine_name' => $vocabulary->machine_name, + ); + taxonomy_term_save($term); + + $this->term = $term; + + // Set up a field and instance. + $this->field_name = drupal_strtolower($this->randomName()); + $this->field = array( + 'field_name' => $this->field_name, + 'type' => 'taxonomy_term_reference', + 'settings' => array( + 'allowed_values' => array( + array( + 'vocabulary' => $this->vocabulary->machine_name, + 'parent' => '0', + ), + ), + ) + ); + + field_create_field($this->field); + $this->instance = array( + 'field_name' => $this->field_name, + 'entity_type' => 'node', + 'bundle' => 'page', + 'widget' => array( + 'type' => 'options_select', + ), + // Hide on full display but render on teaser. + 'display' => array( + 'default' => array( + 'type' => 'hidden', + ), + 'teaser' => array( + 'type' => 'taxonomy_term_reference_link', + ), + ), + ); + field_create_instance($this->instance); } /** @@ -470,21 +530,26 @@ class PagePreviewTestCase extends DrupalWebTestCase { $langcode = LANGUAGE_NONE; $title_key = "title"; $body_key = "body[$langcode][0][value]"; + $term_key = "{$this->field_name}[$langcode]"; // Fill in node creation form and preview node. $edit = array(); $edit[$title_key] = $this->randomName(8); $edit[$body_key] = $this->randomName(16); + $edit[$term_key] = $this->term->tid; $this->drupalPost('node/add/page', $edit, t('Preview')); - // Check that the preview is displaying the title and body. + // Check that the preview is displaying the title, body, and term. $this->assertTitle(t('Preview | Drupal'), 'Basic page title is preview.'); $this->assertText($edit[$title_key], 'Title displayed.'); $this->assertText($edit[$body_key], 'Body displayed.'); + $this->assertText($this->term->name, 'Term displayed.'); - // Check that the title and body fields are displayed with the correct values. + // Check that the title, body, and term fields are displayed with the + // correct values. $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.'); $this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.'); + $this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.'); } /** @@ -494,6 +559,7 @@ class PagePreviewTestCase extends DrupalWebTestCase { $langcode = LANGUAGE_NONE; $title_key = "title"; $body_key = "body[$langcode][0][value]"; + $term_key = "{$this->field_name}[$langcode]"; // Force revision on "Basic page" content. variable_set('node_options_page', array('status', 'revision')); @@ -501,17 +567,21 @@ class PagePreviewTestCase extends DrupalWebTestCase { $edit = array(); $edit[$title_key] = $this->randomName(8); $edit[$body_key] = $this->randomName(16); + $edit[$term_key] = $this->term->tid; $edit['log'] = $this->randomName(32); $this->drupalPost('node/add/page', $edit, t('Preview')); - // Check that the preview is displaying the title and body. + // Check that the preview is displaying the title, body, and term. $this->assertTitle(t('Preview | Drupal'), 'Basic page title is preview.'); $this->assertText($edit[$title_key], 'Title displayed.'); $this->assertText($edit[$body_key], 'Body displayed.'); + $this->assertText($this->term->name, 'Term displayed.'); - // Check that the title and body fields are displayed with the correct values. + // Check that the title, body, and term fields are displayed with the + // correct values. $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.'); $this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.'); + $this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.'); // Check that the log field has the correct value. $this->assertFieldByName('log', $edit['log'], 'Log field displayed.'); diff --git a/modules/node/tests/node_access_test.info b/modules/node/tests/node_access_test.info index 87683bdb..faa0c5b3 100755 --- a/modules/node/tests/node_access_test.info +++ b/modules/node/tests/node_access_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/node/tests/node_test.info b/modules/node/tests/node_test.info index c91df020..fe528f7a 100755 --- a/modules/node/tests/node_test.info +++ b/modules/node/tests/node_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/node/tests/node_test_exception.info b/modules/node/tests/node_test_exception.info index 3a805b39..59941f03 100755 --- a/modules/node/tests/node_test_exception.info +++ b/modules/node/tests/node_test_exception.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/openid/openid.info b/modules/openid/openid.info index 62800a65..8f265925 100755 --- a/modules/openid/openid.info +++ b/modules/openid/openid.info @@ -5,8 +5,8 @@ package = Core core = 7.x files[] = openid.test -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/openid/tests/openid_test.info b/modules/openid/tests/openid_test.info index 3c66f075..c123b952 100755 --- a/modules/openid/tests/openid_test.info +++ b/modules/openid/tests/openid_test.info @@ -6,8 +6,8 @@ core = 7.x dependencies[] = openid hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/overlay/overlay-parent.js b/modules/overlay/overlay-parent.js index 7859821b..efb26370 100755 --- a/modules/overlay/overlay-parent.js +++ b/modules/overlay/overlay-parent.js @@ -350,7 +350,7 @@ Drupal.overlay.setFocusBefore = function ($element, document) { * TRUE if the URL represents an administrative link, FALSE otherwise. */ Drupal.overlay.isAdminLink = function (url) { - if (Drupal.overlay.isExternalLink(url)) { + if (!Drupal.urlIsLocal(url)) { return false; } @@ -378,6 +378,8 @@ Drupal.overlay.isAdminLink = function (url) { /** * Determine whether a link is external to the site. * + * Deprecated. Use Drupal.urlIsLocal() instead. + * * @param url * The URL to be tested. * @@ -385,8 +387,7 @@ Drupal.overlay.isAdminLink = function (url) { * TRUE if the URL is external to the site, FALSE otherwise. */ Drupal.overlay.isExternalLink = function (url) { - var re = RegExp('^((f|ht)tps?:)?//(?!' + window.location.host + ')'); - return re.test(url); + return !Drupal.urlIsLocal(url); }; /** @@ -405,7 +406,7 @@ Drupal.overlay.isExternalLink = function (url) { */ Drupal.overlay.getInternalUrl = function (path) { var url = Drupal.settings.basePath + path; - if (!this.isExternalLink(url)) { + if (Drupal.urlIsLocal(url)) { return url; } }; diff --git a/modules/overlay/overlay.info b/modules/overlay/overlay.info index ec0cdf4d..d415a0d5 100755 --- a/modules/overlay/overlay.info +++ b/modules/overlay/overlay.info @@ -4,8 +4,8 @@ package = Core version = VERSION core = 7.x -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/path/path.info b/modules/path/path.info index a70da447..7ef6ad34 100755 --- a/modules/path/path.info +++ b/modules/path/path.info @@ -6,8 +6,8 @@ core = 7.x files[] = path.test configure = admin/config/search/path -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/path/path.module b/modules/path/path.module index 81c7bb2c..4614b0fa 100755 --- a/modules/path/path.module +++ b/modules/path/path.module @@ -185,7 +185,7 @@ function path_form_element_validate($element, &$form_state, $complete_form) { * Implements hook_node_insert(). */ function path_node_insert($node) { - if (isset($node->path)) { + if (isset($node->path) && isset($node->path['alias'])) { $path = $node->path; $path['alias'] = trim($path['alias']); // Only save a non-empty alias. @@ -205,9 +205,9 @@ function path_node_insert($node) { function path_node_update($node) { if (isset($node->path)) { $path = $node->path; - $path['alias'] = trim($path['alias']); + $path['alias'] = isset($path['alias']) ? trim($path['alias']) : ''; // Delete old alias if user erased it. - if (!empty($path['pid']) && empty($path['alias'])) { + if (!empty($path['pid']) && !$path['alias']) { path_delete($path['pid']); } path_node_insert($node); diff --git a/modules/php/php.info b/modules/php/php.info index 93aed895..fb788c62 100755 --- a/modules/php/php.info +++ b/modules/php/php.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = php.test -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/poll/poll.info b/modules/poll/poll.info index aa203f9b..8d9d91af 100755 --- a/modules/poll/poll.info +++ b/modules/poll/poll.info @@ -6,8 +6,8 @@ core = 7.x files[] = poll.test stylesheets[all][] = poll.css -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/poll/poll.module b/modules/poll/poll.module index bfc72bf9..336e4456 100755 --- a/modules/poll/poll.module +++ b/modules/poll/poll.module @@ -631,9 +631,6 @@ function poll_delete($node) { * The node object to load. */ function poll_block_latest_poll_view($node) { - global $user; - $output = ''; - // This is necessary for shared objects because PHP doesn't copy objects, but // passes them by reference. So when the objects are cached it can result in // the wrong output being displayed on subsequent calls. The cloning and @@ -674,9 +671,6 @@ function poll_block_latest_poll_view($node) { * Implements hook_view(). */ function poll_view($node, $view_mode) { - global $user; - $output = ''; - if (!empty($node->allowvotes) && empty($node->show_results)) { $node->content['poll_view_voting'] = drupal_get_form('poll_view_voting', $node); } @@ -694,7 +688,7 @@ function poll_view($node, $view_mode) { function poll_teaser($node) { $teaser = NULL; if (is_array($node->choice)) { - foreach ($node->choice as $k => $choice) { + foreach ($node->choice as $choice) { if ($choice['chtext'] != '') { $teaser .= '* ' . check_plain($choice['chtext']) . "\n"; } diff --git a/modules/profile/profile.info b/modules/profile/profile.info index 4aa11a86..db6e7b1b 100755 --- a/modules/profile/profile.info +++ b/modules/profile/profile.info @@ -11,8 +11,8 @@ configure = admin/config/people/profile ; See user_system_info_alter(). hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/profile/profile.test b/modules/profile/profile.test index 6cb07391..18924714 100755 --- a/modules/profile/profile.test +++ b/modules/profile/profile.test @@ -342,7 +342,7 @@ class ProfileTestAutocomplete extends ProfileTestCase { // Autocomplete always uses non-clean URLs. $current_clean_url = isset($GLOBALS['conf']['clean_url']) ? $GLOBALS['conf']['clean_url'] : NULL; $GLOBALS['conf']['clean_url'] = 0; - $autocomplete_url = url('profile/autocomplete/' . $field['fid'], array('absolute' => TRUE)); + $autocomplete_url = url('profile/autocomplete/' . $field['fid'], array('absolute' => TRUE, 'script' => 'index.php')); $GLOBALS['conf']['clean_url'] = $current_clean_url; $autocomplete_id = drupal_html_id('edit-' . $field['form_name'] . '-autocomplete'); $autocomplete_html = ''; diff --git a/modules/rdf/rdf.info b/modules/rdf/rdf.info index af080ce4..a58be463 100755 --- a/modules/rdf/rdf.info +++ b/modules/rdf/rdf.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = rdf.test -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/rdf/tests/rdf_test.info b/modules/rdf/tests/rdf_test.info index 3b80f351..836ddf63 100755 --- a/modules/rdf/tests/rdf_test.info +++ b/modules/rdf/tests/rdf_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/search/search.extender.inc b/modules/search/search.extender.inc index 72cea647..40742569 100755 --- a/modules/search/search.extender.inc +++ b/modules/search/search.extender.inc @@ -409,10 +409,10 @@ class SearchQuery extends SelectQueryExtender { * used. However, if at least one call to addScore() has taken place, the * keyword relevance score is not automatically added. * - * Also note that if you call orderBy() directly on the query, search scores - * will not automatically be used to order search results. Your orderBy() - * expression can reference 'calculated_score', which will be the total - * calculated score value. + * Note that you must use this method to add ordering to your searches, and + * not call orderBy() directly, when using the SearchQuery extender. This is + * because of the two-pass system the SearchQuery class uses to normalize + * scores. * * @param $score * The score expression, which should evaluate to a number between 0 and 1. diff --git a/modules/search/search.info b/modules/search/search.info index 5743fd51..33fed95d 100755 --- a/modules/search/search.info +++ b/modules/search/search.info @@ -8,8 +8,8 @@ files[] = search.test configure = admin/config/search/settings stylesheets[all][] = search.css -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/search/search.pages.inc b/modules/search/search.pages.inc index 9dd00a6d..2123dd75 100755 --- a/modules/search/search.pages.inc +++ b/modules/search/search.pages.inc @@ -49,7 +49,7 @@ function search_view($module = NULL, $keys = '') { // which will get us back to this page callback. In other words, the search // form submits with POST but redirects to GET. This way we can keep // the search query URL clean as a whistle. - if (empty($_POST['form_id']) || $_POST['form_id'] != 'search_form') { + if (empty($_POST['form_id']) || ($_POST['form_id'] != 'search_form' && $_POST['form_id'] != 'search_block_form')) { $conditions = NULL; if (isset($info['conditions_callback']) && function_exists($info['conditions_callback'])) { // Build an optional array of more search conditions. diff --git a/modules/search/search.test b/modules/search/search.test index 5f16db3f..913d1989 100755 --- a/modules/search/search.test +++ b/modules/search/search.test @@ -666,6 +666,24 @@ class SearchBlockTestCase extends DrupalWebTestCase { url('search/node/', array('absolute' => TRUE)), 'Redirected to correct url.' ); + + // Test that after entering a too-short keyword in the form, you can then + // search again with a longer keyword. First test using the block form. + $terms = array('search_block_form' => 'a'); + $this->drupalPost('node', $terms, t('Search')); + $this->assertText('You must include at least one positive keyword with 3 characters or more'); + $terms = array('search_block_form' => 'foo'); + $this->drupalPost(NULL, $terms, t('Search')); + $this->assertNoText('You must include at least one positive keyword with 3 characters or more'); + $this->assertText('Your search yielded no results'); + + // Same test again, using the search page form for the second search this time. + $terms = array('search_block_form' => 'a'); + $this->drupalPost('node', $terms, t('Search')); + $terms = array('keys' => 'foo'); + $this->drupalPost(NULL, $terms, t('Search')); + $this->assertNoText('You must include at least one positive keyword with 3 characters or more'); + $this->assertText('Your search yielded no results'); } } @@ -2029,10 +2047,11 @@ class SearchNodeAccessTest extends DrupalWebTestCase { } /** - * Tests that search returns results with punctuation in the search phrase. + * Tests that search works with punctuation and HTML entities. */ function testPhraseSearchPunctuation() { $node = $this->drupalCreateNode(array('body' => array(LANGUAGE_NONE => array(array('value' => "The bunny's ears were fuzzy."))))); + $node2 = $this->drupalCreateNode(array('body' => array(LANGUAGE_NONE => array(array('value' => 'Dignissim Aliquam & Quieligo meus natu quae quia te. Damnum© erat— neo pneum. Facilisi feugiat ibidem ratis.'))))); // Update the search index. module_invoke_all('update_index'); @@ -2045,6 +2064,17 @@ class SearchNodeAccessTest extends DrupalWebTestCase { $edit = array('keys' => '"bunny\'s"'); $this->drupalPost('search/node', $edit, t('Search')); $this->assertText($node->title); + + // Search for "&" and verify entities are not broken up in the output. + $edit = array('keys' => '&'); + $this->drupalPost('search/node', $edit, t('Search')); + $this->assertNoRaw('&amp;'); + $this->assertText('You must include at least one positive keyword'); + + $edit = array('keys' => '&'); + $this->drupalPost('search/node', $edit, t('Search')); + $this->assertNoRaw('&amp;'); + $this->assertText('You must include at least one positive keyword'); } } diff --git a/modules/search/tests/search_embedded_form.info b/modules/search/tests/search_embedded_form.info index 2e671e39..d8b237ff 100755 --- a/modules/search/tests/search_embedded_form.info +++ b/modules/search/tests/search_embedded_form.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/search/tests/search_extra_type.info b/modules/search/tests/search_extra_type.info index e83c32fa..306cc8cb 100755 --- a/modules/search/tests/search_extra_type.info +++ b/modules/search/tests/search_extra_type.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/search/tests/search_node_tags.info b/modules/search/tests/search_node_tags.info index 266778d6..78b1f397 100644 --- a/modules/search/tests/search_node_tags.info +++ b/modules/search/tests/search_node_tags.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/shortcut/shortcut.info b/modules/shortcut/shortcut.info index 60aa6ae2..d6aa11c0 100755 --- a/modules/shortcut/shortcut.info +++ b/modules/shortcut/shortcut.info @@ -6,8 +6,8 @@ core = 7.x files[] = shortcut.test configure = admin/config/user-interface/shortcut -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php index b67c478a..aed66fa2 100755 --- a/modules/simpletest/drupal_web_test_case.php +++ b/modules/simpletest/drupal_web_test_case.php @@ -1015,9 +1015,7 @@ class DrupalWebTestCase extends DrupalTestCase { 'description' => '', 'help' => '', 'title_label' => 'Title', - 'body_label' => 'Body', 'has_title' => 1, - 'has_body' => 1, ); // Imposed values for a custom type. $forced = array( @@ -1067,7 +1065,7 @@ class DrupalWebTestCase extends DrupalTestCase { $lines = array(16, 256, 1024, 2048, 20480); $count = 0; foreach ($lines as $line) { - simpletest_generate_file('text-' . $count++, 64, $line); + simpletest_generate_file('text-' . $count++, 64, $line, 'text'); } // Copy other test files from simpletest. @@ -2586,6 +2584,11 @@ class DrupalWebTestCase extends DrupalTestCase { * * @param $xpath * The xpath string to use in the search. + * @param array $arguments + * An array of arguments with keys in the form ':name' matching the + * placeholders in the query. The values may be either strings or numeric + * values. + * * @return * The return value of the xpath search. For details on the xpath string * format and return values see the SimpleXML documentation, diff --git a/modules/simpletest/simpletest.info b/modules/simpletest/simpletest.info index 723736b6..1063ed66 100755 --- a/modules/simpletest/simpletest.info +++ b/modules/simpletest/simpletest.info @@ -11,6 +11,7 @@ configure = admin/config/development/testing/settings files[] = tests/actions.test files[] = tests/ajax.test files[] = tests/batch.test +files[] = tests/boot.test files[] = tests/bootstrap.test files[] = tests/cache.test files[] = tests/common.test @@ -56,8 +57,8 @@ files[] = tests/upgrade/update.trigger.test files[] = tests/upgrade/update.field.test files[] = tests/upgrade/update.user.test -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/simpletest.module b/modules/simpletest/simpletest.module index 91f0f906..29a20bb4 100755 --- a/modules/simpletest/simpletest.module +++ b/modules/simpletest/simpletest.module @@ -154,7 +154,7 @@ function simpletest_run_tests($test_list, $reporter = 'drupal') { } /** - * Batch operation callback. + * Implements callback_batch_operation(). */ function _simpletest_batch_operation($test_list_init, $test_id, &$context) { simpletest_classloader_register(); @@ -205,6 +205,9 @@ function _simpletest_batch_operation($test_list_init, $test_id, &$context) { $context['finished'] = 1 - $size / $max; } +/** + * Implements callback_batch_finished(). + */ function _simpletest_batch_finished($success, $results, $operations, $elapsed) { if ($success) { drupal_set_message(t('The test run finished in @elapsed.', array('@elapsed' => $elapsed))); @@ -509,25 +512,25 @@ function simpletest_registry_files_alter(&$files, $modules) { * Generate test file. */ function simpletest_generate_file($filename, $width, $lines, $type = 'binary-text') { - $size = $width * $lines - $lines; - - // Generate random text $text = ''; - for ($i = 0; $i < $size; $i++) { - switch ($type) { - case 'text': - $text .= chr(rand(32, 126)); - break; - case 'binary': - $text .= chr(rand(0, 31)); - break; - case 'binary-text': - default: - $text .= rand(0, 1); - break; + for ($i = 0; $i < $lines; $i++) { + // Generate $width - 1 characters to leave space for the "\n" character. + for ($j = 0; $j < $width - 1; $j++) { + switch ($type) { + case 'text': + $text .= chr(rand(32, 126)); + break; + case 'binary': + $text .= chr(rand(0, 31)); + break; + case 'binary-text': + default: + $text .= rand(0, 1); + break; + } } + $text .= "\n"; } - $text = wordwrap($text, $width - 1, "\n", TRUE) . "\n"; // Add \n for symmetrical file. // Create filename. file_put_contents('public://' . $filename . '.txt', $text); diff --git a/modules/simpletest/tests/actions_loop_test.info b/modules/simpletest/tests/actions_loop_test.info index b880d5d7..8054ae3e 100755 --- a/modules/simpletest/tests/actions_loop_test.info +++ b/modules/simpletest/tests/actions_loop_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/ajax_forms_test.info b/modules/simpletest/tests/ajax_forms_test.info index f0353069..9a036b55 100755 --- a/modules/simpletest/tests/ajax_forms_test.info +++ b/modules/simpletest/tests/ajax_forms_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/ajax_test.info b/modules/simpletest/tests/ajax_test.info index a09870c8..a987a3fa 100755 --- a/modules/simpletest/tests/ajax_test.info +++ b/modules/simpletest/tests/ajax_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/batch_test.callbacks.inc b/modules/simpletest/tests/batch_test.callbacks.inc index 75e66553..6564413d 100755 --- a/modules/simpletest/tests/batch_test.callbacks.inc +++ b/modules/simpletest/tests/batch_test.callbacks.inc @@ -7,6 +7,8 @@ */ /** + * Implements callback_batch_operation(). + * * Simple batch operation. */ function _batch_test_callback_1($id, $sleep, &$context) { @@ -20,6 +22,8 @@ function _batch_test_callback_1($id, $sleep, &$context) { } /** + * Implements callback_batch_operation(). + * * Multistep batch operation. */ function _batch_test_callback_2($start, $total, $sleep, &$context) { @@ -53,6 +57,8 @@ function _batch_test_callback_2($start, $total, $sleep, &$context) { } /** + * Implements callback_batch_operation(). + * * Simple batch operation. */ function _batch_test_callback_5($id, $sleep, &$context) { @@ -68,6 +74,8 @@ function _batch_test_callback_5($id, $sleep, &$context) { } /** + * Implements callback_batch_operation(). + * * Batch operation setting up its own batch. */ function _batch_test_nested_batch_callback() { @@ -76,6 +84,8 @@ function _batch_test_nested_batch_callback() { } /** + * Implements callback_batch_finished(). + * * Common 'finished' callbacks for batches 1 to 4. */ function _batch_test_finished_helper($batch_id, $success, $results, $operations) { @@ -99,6 +109,8 @@ function _batch_test_finished_helper($batch_id, $success, $results, $operations) } /** + * Implements callback_batch_finished(). + * * 'finished' callback for batch 0. */ function _batch_test_finished_0($success, $results, $operations) { @@ -106,6 +118,8 @@ function _batch_test_finished_0($success, $results, $operations) { } /** + * Implements callback_batch_finished(). + * * 'finished' callback for batch 1. */ function _batch_test_finished_1($success, $results, $operations) { @@ -113,6 +127,8 @@ function _batch_test_finished_1($success, $results, $operations) { } /** + * Implements callback_batch_finished(). + * * 'finished' callback for batch 2. */ function _batch_test_finished_2($success, $results, $operations) { @@ -120,6 +136,8 @@ function _batch_test_finished_2($success, $results, $operations) { } /** + * Implements callback_batch_finished(). + * * 'finished' callback for batch 3. */ function _batch_test_finished_3($success, $results, $operations) { @@ -127,6 +145,8 @@ function _batch_test_finished_3($success, $results, $operations) { } /** + * Implements callback_batch_finished(). + * * 'finished' callback for batch 4. */ function _batch_test_finished_4($success, $results, $operations) { @@ -134,6 +154,8 @@ function _batch_test_finished_4($success, $results, $operations) { } /** + * Implements callback_batch_finished(). + * * 'finished' callback for batch 5. */ function _batch_test_finished_5($success, $results, $operations) { diff --git a/modules/simpletest/tests/batch_test.info b/modules/simpletest/tests/batch_test.info index be057e33..04db1c8c 100755 --- a/modules/simpletest/tests/batch_test.info +++ b/modules/simpletest/tests/batch_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/boot.test b/modules/simpletest/tests/boot.test new file mode 100644 index 00000000..562b082e --- /dev/null +++ b/modules/simpletest/tests/boot.test @@ -0,0 +1,38 @@ + 'Early bootstrap test', + 'description' => 'Confirm that calling module_implements() during early bootstrap does not pollute the module_implements() cache.', + 'group' => 'System', + ); + } + + function setUp() { + parent::setUp('boot_test_1', 'boot_test_2'); + } + + /** + * Test hook_boot() on both regular and "early exit" pages. + */ + public function testHookBoot() { + $paths = array('', 'early_exit'); + foreach ($paths as $path) { + // Empty the module_implements() caches. + module_implements(NULL, FALSE, TRUE); + // Do a request to the front page, which will call module_implements() + // during hook_boot(). + $this->drupalGet($path); + // Reset the static cache so we get implementation data from the persistent + // cache. + drupal_static_reset(); + // Make sure we get a full list of all modules implementing hook_help(). + $modules = module_implements('help'); + $this->assertTrue(in_array('boot_test_2', $modules)); + } + } +} diff --git a/modules/simpletest/tests/boot_test_1.info b/modules/simpletest/tests/boot_test_1.info new file mode 100644 index 00000000..16c7bc48 --- /dev/null +++ b/modules/simpletest/tests/boot_test_1.info @@ -0,0 +1,12 @@ +name = Early bootstrap tests +description = A support module for hook_boot testing. +core = 7.x +package = Testing +version = VERSION +hidden = TRUE + +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" +project = "drupal" +datestamp = "1456343506" + diff --git a/modules/simpletest/tests/boot_test_1.module b/modules/simpletest/tests/boot_test_1.module new file mode 100644 index 00000000..a452e289 --- /dev/null +++ b/modules/simpletest/tests/boot_test_1.module @@ -0,0 +1,21 @@ +drupalLogin($user); $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag)); $this->assertResponse(200, 'Conditional request returned 200 OK for authenticated user.'); - $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Absense of Page was not cached.'); + $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Absence of Page was not cached.'); $this->assertFalse($this->drupalGetHeader('ETag'), 'ETag HTTP headers are not present for logged in users.'); $this->assertFalse($this->drupalGetHeader('Last-Modified'), 'Last-Modified HTTP headers are not present for logged in users.'); } @@ -313,6 +313,10 @@ class BootstrapAutoloadTestCase extends DrupalWebTestCase { $this->assertTrue(drupal_autoload_interface('drupalautoloadtestinterface'), 'drupal_autoload_interface() recognizes DrupalAutoloadTestInterface in lower case.'); // Test class autoloader. $this->assertTrue(drupal_autoload_class('drupalautoloadtestclass'), 'drupal_autoload_class() recognizes DrupalAutoloadTestClass in lower case.'); + // Test trait autoloader. + if (version_compare(PHP_VERSION, '5.4') >= 0) { + $this->assertTrue(drupal_autoload_trait('drupalautoloadtesttrait'), 'drupal_autoload_trait() recognizes DrupalAutoloadTestTrait in lower case.'); + } } } diff --git a/modules/simpletest/tests/common.test b/modules/simpletest/tests/common.test index bf855761..92aefe48 100755 --- a/modules/simpletest/tests/common.test +++ b/modules/simpletest/tests/common.test @@ -372,6 +372,65 @@ class CommonURLUnitTest extends DrupalWebTestCase { } } +/** + * Tests url_is_external(). + */ +class UrlIsExternalUnitTest extends DrupalUnitTestCase { + + public static function getInfo() { + return array( + 'name' => 'External URL checking', + 'description' => 'Performs tests on url_is_external().', + 'group' => 'System', + ); + } + + /** + * Tests if each URL is external or not. + */ + function testUrlIsExternal() { + foreach ($this->examples() as $path => $expected) { + $this->assertIdentical(url_is_external($path), $expected, $path); + } + } + + /** + * Provides data for testUrlIsExternal(). + * + * @return array + * An array of test data, keyed by a path, with the expected value where + * TRUE is external, and FALSE is not external. + */ + protected function examples() { + return array( + // Simple external URLs. + 'http://example.com' => TRUE, + 'https://example.com' => TRUE, + 'http://drupal.org/foo/bar?foo=bar&bar=baz&baz#foo' => TRUE, + '//drupal.org' => TRUE, + // Some browsers ignore or strip leading control characters. + "\x00//www.example.com" => TRUE, + "\x08//www.example.com" => TRUE, + "\x1F//www.example.com" => TRUE, + "\n//www.example.com" => TRUE, + // JSON supports decoding directly from UTF-8 code points. + json_decode('"\u00AD"') . "//www.example.com" => TRUE, + json_decode('"\u200E"') . "//www.example.com" => TRUE, + json_decode('"\uE0020"') . "//www.example.com" => TRUE, + json_decode('"\uE000"') . "//www.example.com" => TRUE, + // Backslashes should be normalized to forward. + '\\\\example.com' => TRUE, + // Local URLs. + 'node' => FALSE, + '/system/ajax' => FALSE, + '?q=foo:bar' => FALSE, + 'node/edit:me' => FALSE, + '/drupal.org' => FALSE, + '' => FALSE, + ); + } +} + /** * Tests for check_plain(), filter_xss(), format_string(), and check_url(). */ @@ -1256,6 +1315,15 @@ class DrupalGotoTest extends DrupalWebTestCase { $this->assertText('drupal_goto', 'Drupal goto redirect succeeded.'); $this->assertEqual($this->getUrl(), url('common-test/drupal_goto', array('query' => array('foo' => '123'), 'absolute' => TRUE)), 'Drupal goto redirected to expected URL.'); + // Test that calling drupal_goto() on the current path is not dangerous. + variable_set('common_test_redirect_current_path', TRUE); + $this->drupalGet('', array('query' => array('q' => 'http://www.example.com/'))); + $headers = $this->drupalGetHeaders(TRUE); + list(, $status) = explode(' ', $headers[0][':status'], 3); + $this->assertEqual($status, 302, 'Expected response code was sent.'); + $this->assertNotEqual($this->getUrl(), 'http://www.example.com/', 'Drupal goto did not redirect to external URL.'); + $this->assertTrue(strpos($this->getUrl(), url('', array('absolute' => TRUE))) === 0, 'Drupal redirected to itself.'); + variable_del('common_test_redirect_current_path'); // Test that drupal_goto() respects ?destination=xxx. Use an complicated URL // to test that the path is encoded and decoded properly. $destination = 'common-test/drupal_goto/destination?foo=%2525&bar=123'; diff --git a/modules/simpletest/tests/common_test.info b/modules/simpletest/tests/common_test.info index 1719db7a..e484a8c2 100755 --- a/modules/simpletest/tests/common_test.info +++ b/modules/simpletest/tests/common_test.info @@ -7,8 +7,8 @@ stylesheets[all][] = common_test.css stylesheets[print][] = common_test.print.css hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/common_test.module b/modules/simpletest/tests/common_test.module index 674a4944..2eb8cd5d 100755 --- a/modules/simpletest/tests/common_test.module +++ b/modules/simpletest/tests/common_test.module @@ -92,6 +92,15 @@ function common_test_drupal_goto_alter(&$path, &$options, &$http_response_code) } } +/** + * Implements hook_init(). + */ +function common_test_init() { + if (variable_get('common_test_redirect_current_path', FALSE)) { + drupal_goto(current_path()); + } +} + /** * Print destination query parameter. */ diff --git a/modules/simpletest/tests/common_test_cron_helper.info b/modules/simpletest/tests/common_test_cron_helper.info index e00bd241..a11c2fc5 100755 --- a/modules/simpletest/tests/common_test_cron_helper.info +++ b/modules/simpletest/tests/common_test_cron_helper.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/database_test.info b/modules/simpletest/tests/database_test.info index e5022598..c8c2aa80 100755 --- a/modules/simpletest/tests/database_test.info +++ b/modules/simpletest/tests/database_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test.info b/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test.info index f78833af..520e8d59 100644 --- a/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test.info +++ b/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test.info @@ -7,8 +7,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test.module b/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test.module index 37aa94eb..edd5d77c 100644 --- a/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test.module +++ b/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test.module @@ -4,3 +4,19 @@ * @file * Test module to check code registry. */ + +/** + * Implements hook_registry_files_alter(). + */ +function drupal_autoload_test_registry_files_alter(&$files, $modules) { + foreach ($modules as $module) { + // Add the drupal_autoload_test_trait.sh file to the registry when PHP 5.4+ + // is being used. + if ($module->name == 'drupal_autoload_test' && version_compare(PHP_VERSION, '5.4') >= 0) { + $files["$module->dir/drupal_autoload_test_trait.sh"] = array( + 'module' => $module->name, + 'weight' => $module->weight, + ); + } + } +} diff --git a/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test_trait.sh b/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test_trait.sh new file mode 100644 index 00000000..69ce9ec0 --- /dev/null +++ b/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test_trait.sh @@ -0,0 +1,16 @@ + is 1000 characters." */ function testVeryLongLineWrap() { - $input = 'Drupal

' . str_repeat('x', 2100) . '
Drupal'; + $input = 'Drupal

' . str_repeat('x', 2100) . '


Drupal'; $output = drupal_html_to_text($input); // This awkward construct comes from includes/mail.inc lines 8-13. $eol = variable_get('mail_line_endings', MAIL_LINE_ENDINGS); @@ -455,7 +455,6 @@ class DrupalHtmlToTextTestCase extends DrupalWebTestCase { $maximum_line_length = max($maximum_line_length, strlen($line . $eol)); } $verbose = 'Maximum line length found was ' . $maximum_line_length . ' octets.'; - // @todo This should assert that $maximum_line_length <= 1000. - $this->pass($verbose); + $this->assertTrue($maximum_line_length <= 1000, $verbose); } } diff --git a/modules/simpletest/tests/menu_test.info b/modules/simpletest/tests/menu_test.info index 162e912a..88007a1e 100755 --- a/modules/simpletest/tests/menu_test.info +++ b/modules/simpletest/tests/menu_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/module.test b/modules/simpletest/tests/module.test index 371339f3..eea3b51e 100755 --- a/modules/simpletest/tests/module.test +++ b/modules/simpletest/tests/module.test @@ -302,3 +302,45 @@ class ModuleUninstallTestCase extends DrupalWebTestCase { $this->assertEqual(0, $count, 'Permissions were all removed.'); } } + +class ModuleImplementsAlterTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Module implements alter', + 'description' => 'Tests hook_module_implements_alter().', + 'group' => 'Module', + ); + } + + /** + * Tests hook_module_implements_alter() adding an implementation. + */ + function testModuleImplementsAlter() { + module_enable(array('module_test'), FALSE); + $this->assertTrue(module_exists('module_test'), 'Test module is enabled.'); + + // Assert that module_test.module is now included. + $this->assertTrue(function_exists('module_test_permission'), + 'The file module_test.module was successfully included.'); + + $modules = module_implements('permission'); + $this->assertTrue(in_array('module_test', $modules), 'module_test implements hook_permission.'); + + $modules = module_implements('module_implements_alter'); + $this->assertTrue(in_array('module_test', $modules), 'module_test implements hook_module_implements_alter().'); + + // Assert that module_test.implementations.inc is not included yet. + $this->assertFalse(function_exists('module_test_altered_test_hook'), + 'The file module_test.implementations.inc is not included yet.'); + + // Assert that module_test_module_implements_alter(*, 'altered_test_hook') + // has added an implementation + $this->assertTrue(in_array('module_test', module_implements('altered_test_hook')), + 'module_test implements hook_altered_test_hook().'); + + // Assert that module_test.implementations.inc was included as part of the process. + $this->assertTrue(function_exists('module_test_altered_test_hook'), + 'The file module_test.implementations.inc was included.'); + } + +} diff --git a/modules/simpletest/tests/module_test.implementations.inc b/modules/simpletest/tests/module_test.implementations.inc new file mode 100644 index 00000000..63c866ea --- /dev/null +++ b/modules/simpletest/tests/module_test.implementations.inc @@ -0,0 +1,10 @@ +2.0) -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/system_incompatible_module_version_test.info b/modules/simpletest/tests/system_incompatible_module_version_test.info index f469bd7f..0bdef7cd 100755 --- a/modules/simpletest/tests/system_incompatible_module_version_test.info +++ b/modules/simpletest/tests/system_incompatible_module_version_test.info @@ -5,8 +5,8 @@ version = 1.0 core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/system_project_namespace_test.info b/modules/simpletest/tests/system_project_namespace_test.info new file mode 100644 index 00000000..2bfeb881 --- /dev/null +++ b/modules/simpletest/tests/system_project_namespace_test.info @@ -0,0 +1,13 @@ +name = "System project namespace test" +description = "Support module for testing project namespace dependencies." +package = Testing +version = VERSION +core = 7.x +hidden = TRUE +dependencies[] = drupal:filter + +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" +project = "drupal" +datestamp = "1456343506" + diff --git a/modules/simpletest/tests/system_project_namespace_test.module b/modules/simpletest/tests/system_project_namespace_test.module new file mode 100644 index 00000000..b3d9bbc7 --- /dev/null +++ b/modules/simpletest/tests/system_project_namespace_test.module @@ -0,0 +1 @@ +name == 'system_project_namespace_test') { + $info['hidden'] = FALSE; + } // Make the system_dependencies_test visible by default. if ($file->name == 'system_dependencies_test') { $info['hidden'] = FALSE; diff --git a/modules/simpletest/tests/taxonomy_test.info b/modules/simpletest/tests/taxonomy_test.info index ebb752e3..1e2d7221 100755 --- a/modules/simpletest/tests/taxonomy_test.info +++ b/modules/simpletest/tests/taxonomy_test.info @@ -6,8 +6,8 @@ core = 7.x hidden = TRUE dependencies[] = taxonomy -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/theme_test.info b/modules/simpletest/tests/theme_test.info index 9df1b617..adb04d76 100755 --- a/modules/simpletest/tests/theme_test.info +++ b/modules/simpletest/tests/theme_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/themes/test_basetheme/test_basetheme.info b/modules/simpletest/tests/themes/test_basetheme/test_basetheme.info index acba3d5f..b82f86a4 100755 --- a/modules/simpletest/tests/themes/test_basetheme/test_basetheme.info +++ b/modules/simpletest/tests/themes/test_basetheme/test_basetheme.info @@ -6,8 +6,8 @@ hidden = TRUE settings[basetheme_only] = base theme value settings[subtheme_override] = base theme value -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/themes/test_subtheme/test_subtheme.info b/modules/simpletest/tests/themes/test_subtheme/test_subtheme.info index 120fc3a6..5059593b 100755 --- a/modules/simpletest/tests/themes/test_subtheme/test_subtheme.info +++ b/modules/simpletest/tests/themes/test_subtheme/test_subtheme.info @@ -6,8 +6,8 @@ hidden = TRUE settings[subtheme_override] = subtheme value -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/themes/test_theme/test_theme.info b/modules/simpletest/tests/themes/test_theme/test_theme.info index 4679f32e..98050c40 100755 --- a/modules/simpletest/tests/themes/test_theme/test_theme.info +++ b/modules/simpletest/tests/themes/test_theme/test_theme.info @@ -17,8 +17,8 @@ stylesheets[all][] = system.base.css settings[theme_test_setting] = default value -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/update_script_test.info b/modules/simpletest/tests/update_script_test.info index 5a174fc6..fb5d12bb 100755 --- a/modules/simpletest/tests/update_script_test.info +++ b/modules/simpletest/tests/update_script_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/update_test_1.info b/modules/simpletest/tests/update_test_1.info index 4484ad46..4dddc493 100755 --- a/modules/simpletest/tests/update_test_1.info +++ b/modules/simpletest/tests/update_test_1.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/update_test_2.info b/modules/simpletest/tests/update_test_2.info index 4484ad46..4dddc493 100755 --- a/modules/simpletest/tests/update_test_2.info +++ b/modules/simpletest/tests/update_test_2.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/update_test_3.info b/modules/simpletest/tests/update_test_3.info index 4484ad46..4dddc493 100755 --- a/modules/simpletest/tests/update_test_3.info +++ b/modules/simpletest/tests/update_test_3.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/upgrade/drupal-6.upload.database.php b/modules/simpletest/tests/upgrade/drupal-6.upload.database.php index 46ebe2cb..3fd602af 100755 --- a/modules/simpletest/tests/upgrade/drupal-6.upload.database.php +++ b/modules/simpletest/tests/upgrade/drupal-6.upload.database.php @@ -127,6 +127,38 @@ db_insert('files')->fields(array( 'status' => '1', 'timestamp' => '1285708958', )) +// On some Drupal 6 sites, more than one file can have the same filepath. See +// https://www.drupal.org/node/1260938. +->values(array( + 'fid' => '12', + 'uid' => '1', + 'filename' => 'duplicate-name.png', + 'filepath' => 'sites/default/files/duplicate-name.png', + 'filemime' => 'image/png', + 'filesize' => '314', + 'status' => '1', + 'timestamp' => '1285708958', +)) +->values(array( + 'fid' => '13', + 'uid' => '1', + 'filename' => 'duplicate-name.png', + 'filepath' => 'sites/default/files/duplicate-name.png', + 'filemime' => 'image/png', + 'filesize' => '315', + 'status' => '1', + 'timestamp' => '1285708958', +)) +->values(array( + 'fid' => '14', + 'uid' => '1', + 'filename' => 'duplicate-name.png', + 'filepath' => 'sites/default/files/duplicate-name.png', + 'filemime' => 'image/png', + 'filesize' => '316', + 'status' => '1', + 'timestamp' => '1285708958', +)) ->execute(); db_insert('node')->fields(array( @@ -196,6 +228,23 @@ db_insert('node')->fields(array( 'sticky' => '0', 'tnid' => '0', 'translate' => '0', +)) +->values(array( + 'nid' => '41', + 'vid' => '55', + 'type' => 'page', + 'language' => '', + 'title' => 'node title 41 revision 55', + 'uid' => '1', + 'status' => '1', + 'created' => '1285709012', + 'changed' => '1285709012', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', )) ->execute(); @@ -253,6 +302,28 @@ db_insert('node_revisions')->fields(array( 'log' => '', 'timestamp' => '1285709012', 'format' => '1', +)) +->values(array( + 'nid' => '41', + 'vid' => '54', + 'uid' => '1', + 'title' => 'node title 41 revision 54', + 'body' => "Attachments:\r\nduplicate-name.png", + 'teaser' => "Attachments:\r\nduplicate-name.png", + 'log' => '', + 'timestamp' => '1285709012', + 'format' => '1', +)) +->values(array( + 'nid' => '41', + 'vid' => '55', + 'uid' => '1', + 'title' => 'node title 41 revision 55', + 'body' => "Attachments:\r\nduplicate-name.png\r\nduplicate-name.png", + 'teaser' => "Attachments:\r\nduplicate-name.png\r\nduplicate-name.png", + 'log' => '', + 'timestamp' => '1285709012', + 'format' => '1', )) ->execute(); @@ -415,6 +486,30 @@ db_insert('upload')->fields(array( 'list' => '1', 'weight' => '0', )) +->values(array( + 'fid' => '12', + 'nid' => '41', + 'vid' => '54', + 'description' => 'duplicate-name.png', + 'list' => '1', + 'weight' => '0', +)) +->values(array( + 'fid' => '13', + 'nid' => '41', + 'vid' => '55', + 'description' => 'first description', + 'list' => '0', + 'weight' => '0', +)) +->values(array( + 'fid' => '14', + 'nid' => '41', + 'vid' => '55', + 'description' => 'second description', + 'list' => '1', + 'weight' => '0', +)) ->execute(); // Add series of entries for invalid node vids to the {upload} table. @@ -431,7 +526,7 @@ for ($i = 30; $i < 250; $i += 2) { ->values(array( 'fid' => $i, 'nid' => '40', - 'vid' => 24 + $i, + 'vid' => 26 + $i, 'description' => 'crazy-basename.png', 'list' => '1', 'weight' => '0', @@ -440,7 +535,7 @@ for ($i = 30; $i < 250; $i += 2) { ->values(array( 'fid' => 2, 'nid' => '40', - 'vid' => 24 + $i + 1, + 'vid' => 26 + $i + 1, 'description' => 'crazy-basename.png', 'list' => '1', 'weight' => '0', diff --git a/modules/simpletest/tests/upgrade/upgrade.upload.test b/modules/simpletest/tests/upgrade/upgrade.upload.test index be352bd4..dfa94a00 100755 --- a/modules/simpletest/tests/upgrade/upgrade.upload.test +++ b/modules/simpletest/tests/upgrade/upgrade.upload.test @@ -64,12 +64,35 @@ class UploadUpgradePathTestCase extends UpgradePathTestCase { } $this->assertIdentical($filenames, $recorded_filenames, 'The uploaded files are present in the same order after the upgrade.'); } + // Test for the file with repeating basename to only have the streaming // path replaced. $node = node_load(40, 53); $repeated_basename_file = $node->upload[LANGUAGE_NONE][4]; $this->assertEqual($repeated_basename_file['uri'], 'private://drupal-6/file/directory/path/crazy-basename.png', "The file with the repeated basename path only had the stream portion replaced"); + // Ensure that filepaths are deduplicated. + $node0 = node_load(41, 54); + $node1 = node_load(41, 55); + // Ensure that both revisions point to the same file ID. + $items0 = field_get_items('node', $node0, 'upload'); + $this->assertEqual(count($items0), 1); + $items1 = field_get_items('node', $node1, 'upload'); + $this->assertEqual(count($items1), 2); + $this->assertEqual($items0[0]['fid'], $items1[0]['fid']); + $this->assertEqual($items0[0]['fid'], $items1[1]['fid']); + // The revision with more than one reference to the same file should retain + // the original settings for each reference. + $this->assertEqual($items1[0]['description'], 'first description'); + $this->assertEqual($items1[0]['display'], 0); + $this->assertEqual($items1[1]['description'], 'second description'); + $this->assertEqual($items1[1]['display'], 1); + // Ensure that the latest version of the files are used. + $this->assertEqual($items1[0]['filesize'], 316); + $this->assertEqual($items1[1]['filesize'], 316); + // No duplicate files should remain on the Drupal 7 site. + $this->assertEqual(0, db_query("SELECT COUNT(*) FROM {file_managed} GROUP BY uri HAVING COUNT(fid) > 1")->fetchField()); + // Make sure the file settings were properly migrated. $d6_file_directory_temp = '/drupal-6/file/directory/temp'; $d6_file_directory_path = '/drupal-6/file/directory/path'; diff --git a/modules/simpletest/tests/url_alter_test.info b/modules/simpletest/tests/url_alter_test.info index 8af9f248..641b673f 100755 --- a/modules/simpletest/tests/url_alter_test.info +++ b/modules/simpletest/tests/url_alter_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/simpletest/tests/xmlrpc.test b/modules/simpletest/tests/xmlrpc.test index 1a9ef234..bb74f059 100755 --- a/modules/simpletest/tests/xmlrpc.test +++ b/modules/simpletest/tests/xmlrpc.test @@ -246,4 +246,38 @@ class XMLRPCMessagesTestCase extends DrupalWebTestCase { $this->assertEqual($removed, 'system.methodSignature', 'Hiding builting system.methodSignature with hook_xmlrpc_alter works'); } + /** + * Test limits on system.multicall that can prevent brute-force attacks. + */ + function testMulticallLimit() { + $url = url(NULL, array('absolute' => TRUE)) . 'xmlrpc.php'; + $multicall_args = array(); + $num_method_calls = 10; + for ($i = 0; $i < $num_method_calls; $i++) { + $struct = array('i' => $i); + $multicall_args[] = array('methodName' => 'validator1.echoStructTest', 'params' => array($struct)); + } + // Test limits of 1, 5, 9, 13. + for ($limit = 1; $limit < $num_method_calls + 4; $limit += 4) { + variable_set('xmlrpc_multicall_duplicate_method_limit', $limit); + $results = xmlrpc($url, array('system.multicall' => array($multicall_args))); + $this->assertEqual($num_method_calls, count($results)); + for ($i = 0; $i < min($limit, $num_method_calls); $i++) { + $x = array_shift($results); + $this->assertTrue(empty($x->is_error), "Result $i is not an error"); + $this->assertEqual($multicall_args[$i]['params'][0], $x); + } + for (; $i < $num_method_calls; $i++) { + $x = array_shift($results); + $this->assertFalse(empty($x->is_error), "Result $i is an error"); + $this->assertEqual(-156579, $x->code); + } + } + variable_set('xmlrpc_multicall_duplicate_method_limit', -1); + $results = xmlrpc($url, array('system.multicall' => array($multicall_args))); + $this->assertEqual($num_method_calls, count($results)); + foreach ($results as $i => $x) { + $this->assertTrue(empty($x->is_error), "Result $i is not an error"); + } + } } diff --git a/modules/simpletest/tests/xmlrpc_test.info b/modules/simpletest/tests/xmlrpc_test.info index 0cf5bbac..857b1a51 100755 --- a/modules/simpletest/tests/xmlrpc_test.info +++ b/modules/simpletest/tests/xmlrpc_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/statistics/statistics.info b/modules/statistics/statistics.info index 92756356..25a6ecc2 100755 --- a/modules/statistics/statistics.info +++ b/modules/statistics/statistics.info @@ -6,8 +6,8 @@ core = 7.x files[] = statistics.test configure = admin/config/system/statistics -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/statistics/statistics.module b/modules/statistics/statistics.module index f665a14f..b2561d24 100755 --- a/modules/statistics/statistics.module +++ b/modules/statistics/statistics.module @@ -118,10 +118,9 @@ function statistics_node_view($node, $view_mode) { // Attach Ajax node count statistics if configured. if (variable_get('statistics_count_content_views', 0) && variable_get('statistics_count_content_views_ajax', 0)) { if (!empty($node->nid) && $view_mode == 'full' && node_is_page($node) && empty($node->in_preview)) { - $node->content['#attached']['js'] = array( - drupal_get_path('module', 'statistics') . '/statistics.js' => array( - 'scope' => 'footer' - ), + $statistics = drupal_get_path('module', 'statistics') . '/statistics.js'; + $node->content['#attached']['js'][$statistics] = array( + 'scope' => 'footer', ); $settings = array('data' => array('nid' => $node->nid), 'url' => url(drupal_get_path('module', 'statistics') . '/statistics.php')); $node->content['#attached']['js'][] = array( diff --git a/modules/statistics/statistics.php b/modules/statistics/statistics.php index f00e0397..48340c89 100644 --- a/modules/statistics/statistics.php +++ b/modules/statistics/statistics.php @@ -15,17 +15,19 @@ chdir(DRUPAL_ROOT); include_once DRUPAL_ROOT . '/includes/bootstrap.inc'; drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES); if (variable_get('statistics_count_content_views', 0) && variable_get('statistics_count_content_views_ajax', 0)) { - $nid = $_POST['nid']; - if (is_numeric($nid)) { - db_merge('node_counter') - ->key(array('nid' => $nid)) - ->fields(array( - 'daycount' => 1, - 'totalcount' => 1, - 'timestamp' => REQUEST_TIME, - )) - ->expression('daycount', 'daycount + 1') - ->expression('totalcount', 'totalcount + 1') - ->execute(); + if (isset($_POST['nid'])) { + $nid = $_POST['nid']; + if (is_numeric($nid)) { + db_merge('node_counter') + ->key(array('nid' => $nid)) + ->fields(array( + 'daycount' => 1, + 'totalcount' => 1, + 'timestamp' => REQUEST_TIME, + )) + ->expression('daycount', 'daycount + 1') + ->expression('totalcount', 'totalcount + 1') + ->execute(); + } } } diff --git a/modules/syslog/syslog.info b/modules/syslog/syslog.info index e0b8a44a..39afcb72 100755 --- a/modules/syslog/syslog.info +++ b/modules/syslog/syslog.info @@ -6,8 +6,8 @@ core = 7.x files[] = syslog.test configure = admin/config/development/logging -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc index 0f525c6c..16c40d4d 100755 --- a/modules/system/system.admin.inc +++ b/modules/system/system.admin.inc @@ -2202,6 +2202,11 @@ function system_add_date_format_type_form_submit($form, &$form_state) { * Return the date for a given format string via Ajax. */ function system_date_time_lookup() { + // This callback is protected with a CSRF token because user input from the + // query string is reflected in the output. + if (!isset($_GET['token']) || !drupal_valid_token($_GET['token'], 'admin/config/regional/date-time/formats/lookup')) { + return MENU_ACCESS_DENIED; + } $result = format_date(REQUEST_TIME, 'custom', $_GET['format']); drupal_json_output($result); } @@ -2875,13 +2880,14 @@ function system_date_time_formats() { * Allow users to add additional date formats. */ function system_configure_date_formats_form($form, &$form_state, $dfid = 0) { + $ajax_path = 'admin/config/regional/date-time/formats/lookup'; $js_settings = array( 'type' => 'setting', 'data' => array( 'dateTime' => array( 'date-format' => array( 'text' => t('Displayed as'), - 'lookup' => url('admin/config/regional/date-time/formats/lookup'), + 'lookup' => url($ajax_path, array('query' => array('token' => drupal_get_token($ajax_path)))), ), ), ), diff --git a/modules/system/system.api.php b/modules/system/system.api.php index d6cbc769..4576f819 100755 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -113,21 +113,21 @@ function hook_hook_info_alter(&$hooks) { * translation handlers. Array keys are the module names, array values * can be any data structure the module uses to provide field translation. * Any empty value disallows the module to appear as a translation handler. - * - entity keys: An array describing how the Field API can extract the - * information it needs from the objects of the type. Elements: + * - entity keys: (optional) An array describing how the Field API can extract + * the information it needs from the objects of the type. Elements: * - id: The name of the property that contains the primary id of the * entity. Every entity object passed to the Field API must have this * property and its value must be numeric. * - revision: The name of the property that contains the revision id of * the entity. The Field API assumes that all revision ids are unique * across all entities of a type. This entry can be omitted if the - * entities of this type are not versionable. + * entities of this type are not versionable. Defaults to an empty string. * - bundle: The name of the property that contains the bundle name for the * entity. The bundle name defines which set of fields are attached to * the entity (e.g. what nodes call "content type"). This entry can be * omitted if this entity type exposes a single bundle (all entities have * the same collection of fields). The name of this single bundle will be - * the same as the entity type. + * the same as the entity type. Defaults to an empty string. * - label: The name of the property that contains the entity label. For * example, if the entity's label is located in $entity->subject, then * 'subject' should be specified here. If complex logic is required to @@ -2632,6 +2632,8 @@ function hook_flush_caches() { * module_enable() for a detailed description of the order in which install and * enable hooks are invoked. * + * This hook should be implemented in a .module file, not in an .install file. + * * @param $modules * An array of the modules that were installed. * @@ -3173,7 +3175,9 @@ function hook_requirements($phase) { * creation and alteration of the supported database engines. * * See the Schema API Handbook at http://drupal.org/node/146843 for details on - * schema definition structures. + * schema definition structures. Note that foreign key definitions are for + * documentation purposes only; foreign keys are not created in the database, + * nor are they enforced by Drupal. * * @return array * A schema definition structure array. For each element of the @@ -3225,6 +3229,8 @@ function hook_schema() { 'nid_vid' => array('nid', 'vid'), 'vid' => array('vid'), ), + // For documentation purposes only; foreign keys are not created in the + // database. 'foreign keys' => array( 'node_revision' => array( 'table' => 'node_revision', diff --git a/modules/system/system.info b/modules/system/system.info index 97f3bdf3..850f7cee 100755 --- a/modules/system/system.info +++ b/modules/system/system.info @@ -12,8 +12,8 @@ files[] = system.test required = TRUE configure = admin/config/system -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/system/system.install b/modules/system/system.install index 64c989a6..323b7b35 100755 --- a/modules/system/system.install +++ b/modules/system/system.install @@ -800,6 +800,7 @@ function system_schema() { 'type' => 'varchar', 'length' => 100, 'not null' => TRUE, + 'binary' => TRUE, ), 'type' => array( 'description' => 'The date format type, e.g. medium.', @@ -2803,6 +2804,16 @@ function system_update_7061(&$sandbox) { ->from($query) ->execute(); + // Retrieve a list of duplicate files with the same filepath. Only the + // most-recently uploaded of these will be moved to the new {file_managed} + // table (and all references will be updated to point to it), since + // duplicate file URIs are not allowed in Drupal 7. + // Since the Drupal 6 to 7 upgrade path leaves the {files} table behind + // after it's done, custom or contributed modules which need to migrate + // file references of their own can use a similar query to determine the + // file IDs that duplicate filepaths were mapped to. + $sandbox['duplicate_filepath_fids_to_use'] = db_query("SELECT filepath, MAX(fid) FROM {files} GROUP BY filepath HAVING COUNT(*) > 1")->fetchAllKeyed(); + // Initialize batch update information. $sandbox['progress'] = 0; $sandbox['last_vid_processed'] = -1; @@ -2832,6 +2843,16 @@ function system_update_7061(&$sandbox) { continue; } + // If this file has a duplicate filepath, replace it with the + // most-recently uploaded file that has the same filepath. + if (isset($sandbox['duplicate_filepath_fids_to_use'][$file['filepath']]) && $record->fid != $sandbox['duplicate_filepath_fids_to_use'][$file['filepath']]) { + $file = db_select('files', 'f') + ->fields('f', array('fid', 'uid', 'filename', 'filepath', 'filemime', 'filesize', 'status', 'timestamp')) + ->condition('f.fid', $sandbox['duplicate_filepath_fids_to_use'][$file['filepath']]) + ->execute() + ->fetchAssoc(); + } + // Add in the file information from the upload table. $file['description'] = $record->description; $file['display'] = $record->list; @@ -3157,6 +3178,20 @@ function system_update_7079() { db_change_field('file_managed', 'filesize', 'filesize', $spec); } +/** + * Convert the 'format' column in {date_format_locale} to case sensitive varchar. + */ +function system_update_7080() { + $spec = array( + 'description' => 'The date format string.', + 'type' => 'varchar', + 'length' => 100, + 'not null' => TRUE, + 'binary' => TRUE, + ); + db_change_field('date_format_locale', 'format', 'format', $spec); +} + /** * @} End of "defgroup updates-7.x-extra". * The next series of updates should start at 8000. diff --git a/modules/system/system.js b/modules/system/system.js index 910fb5d3..c0e76d38 100755 --- a/modules/system/system.js +++ b/modules/system/system.js @@ -105,7 +105,7 @@ Drupal.behaviors.dateTime = { // Attach keyup handler to custom format inputs. $('input' + source, context).once('date-time').keyup(function () { var input = $(this); - var url = fieldSettings.lookup + (/\?q=/.test(fieldSettings.lookup) ? '&format=' : '?format=') + encodeURIComponent(input.val()); + var url = fieldSettings.lookup + (/\?/.test(fieldSettings.lookup) ? '&format=' : '?format=') + encodeURIComponent(input.val()); $.getJSON(url, function (data) { $(suffix).empty().append(' ' + fieldSettings.text + ': ' + data + ''); }); diff --git a/modules/system/system.module b/modules/system/system.module index c2aa9e07..362bdd44 100755 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -2411,6 +2411,10 @@ function _system_rebuild_module_data() { // Merge in defaults and save. $modules[$key]->info = $module->info + $defaults; + // The "name" key is required, but to avoid a fatal error in the menu system + // we set a reasonable default if it is not provided. + $modules[$key]->info += array('name' => $key); + // Prefix stylesheets and scripts with module path. $path = dirname($module->uri); if (isset($module->info['stylesheets'])) { @@ -2546,6 +2550,10 @@ function _system_rebuild_theme_data() { $themes[$key]->filename = $theme->uri; $themes[$key]->info = drupal_parse_info_file($theme->uri) + $defaults; + // The "name" key is required, but to avoid a fatal error in the menu system + // we set a reasonable default if it is not provided. + $themes[$key]->info += array('name' => $key); + // Add the info file modification time, so it becomes available for // contributed modules to use for ordering theme lists. $themes[$key]->info['mtime'] = filemtime($theme->uri); @@ -2807,7 +2815,7 @@ function system_settings_form_submit($form, &$form_state) { function _system_sort_requirements($a, $b) { if (!isset($a['weight'])) { if (!isset($b['weight'])) { - return strcmp($a['title'], $b['title']); + return strcasecmp($a['title'], $b['title']); } return -$b['weight']; } @@ -3048,8 +3056,20 @@ function system_cron() { } } - $core = array('cache', 'cache_path', 'cache_filter', 'cache_page', 'cache_form', 'cache_menu'); - $cache_tables = array_merge(module_invoke_all('flush_caches'), $core); + // Delete expired cache entries. + // Avoid invoking hook_flush_cashes() on every cron run because some modules + // use this hook to perform expensive rebuilding operations (which are only + // designed to happen on full cache clears), rather than just returning a + // list of cache tables to be cleared. + $cache_object = cache_get('system_cache_tables'); + if (empty($cache_object)) { + $core = array('cache', 'cache_path', 'cache_filter', 'cache_page', 'cache_form', 'cache_menu'); + $cache_tables = array_merge(module_invoke_all('flush_caches'), $core); + cache_set('system_cache_tables', $cache_tables); + } + else { + $cache_tables = $cache_object->data; + } foreach ($cache_tables as $table) { cache_clear_all(NULL, $table); } diff --git a/modules/system/system.tar.inc b/modules/system/system.tar.inc index 32bf7f06..86e4e3de 100755 --- a/modules/system/system.tar.inc +++ b/modules/system/system.tar.inc @@ -30,81 +30,148 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * - * @category File_Formats - * @package Archive_Tar - * @author Vincent Blavet - * @copyright 1997-2008 The Authors - * @license http://www.opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: Id: Tar.php,v 1.43 2008/10/30 17:58:42 dufuz Exp - * @link http://pear.php.net/package/Archive_Tar + * @category File_Formats + * @package Archive_Tar + * @author Vincent Blavet + * @copyright 1997-2010 The Authors + * @license http://www.opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/Archive_Tar */ -//require_once 'PEAR.php'; -// -// -define ('ARCHIVE_TAR_ATT_SEPARATOR', 90001); -define ('ARCHIVE_TAR_END_BLOCK', pack("a512", '')); + /** + * Note on Drupal 8 porting. + * This file origin is Tar.php, release 1.4.0 (stable) with some code + * from PEAR.php, release 1.9.5 (stable) both at http://pear.php.net. + * To simplify future porting from pear of this file, you should not + * do cosmetic or other non significant changes to this file. + * The following changes have been done: + * Added namespace Drupal\Core\Archiver. + * Removed require_once 'PEAR.php'. + * Added defintion of OS_WINDOWS taken from PEAR.php. + * Renamed class to ArchiveTar. + * Removed extends PEAR from class. + * Removed call parent:: __construct(). + * Changed PEAR::loadExtension($extname) to this->loadExtension($extname). + * Added function loadExtension() taken from PEAR.php. + * Changed all calls of unlink() to drupal_unlink(). + * Changed $this->error_object = &$this->raiseError($p_message) + * to throw new \Exception($p_message). + */ + + /** + * Note on Drupal 7 backporting from Drupal 8. + * File origin is core/lib/Drupal/Core/Archiver/ArchiveTar.php from Drupal 8. + * The following changes have been done: + * Removed namespace Drupal\Core\Archiver. + * Renamed class to Archive_Tar. + * Changed \Exception to Exception. + */ + + +// Drupal removal require_once 'PEAR.php'. + +// Drupal addition OS_WINDOWS as defined in PEAR.php. +if (substr(PHP_OS, 0, 3) == 'WIN') { + define('OS_WINDOWS', true); +} else { + define('OS_WINDOWS', false); +} + +define('ARCHIVE_TAR_ATT_SEPARATOR', 90001); +define('ARCHIVE_TAR_END_BLOCK', pack("a512", '')); + +if (!function_exists('gzopen') && function_exists('gzopen64')) { + function gzopen($filename, $mode, $use_include_path = 0) + { + return gzopen64($filename, $mode, $use_include_path); + } +} + +if (!function_exists('gztell') && function_exists('gztell64')) { + function gztell($zp) + { + return gztell64($zp); + } +} + +if (!function_exists('gzseek') && function_exists('gzseek64')) { + function gzseek($zp, $offset, $whence = SEEK_SET) + { + return gzseek64($zp, $offset, $whence); + } +} /** -* Creates a (compressed) Tar archive -* -* @author Vincent Blavet -* @version Revision: 1.43 -* @license http://www.opensource.org/licenses/bsd-license.php New BSD License -* @package Archive_Tar -*/ -class Archive_Tar // extends PEAR + * Creates a (compressed) Tar archive + * + * @package Archive_Tar + * @author Vincent Blavet + * @license http://www.opensource.org/licenses/bsd-license.php New BSD License + * @version $Revision$ + */ +// Drupal change class Archive_Tar extends PEAR. +class Archive_Tar { /** - * @var string Name of the Tar - */ - var $_tarname=''; + * @var string Name of the Tar + */ + public $_tarname = ''; /** - * @var boolean if true, the Tar file will be gzipped - */ - var $_compress=false; + * @var boolean if true, the Tar file will be gzipped + */ + public $_compress = false; /** - * @var string Type of compression : 'none', 'gz' or 'bz2' - */ - var $_compress_type='none'; + * @var string Type of compression : 'none', 'gz', 'bz2' or 'lzma2' + */ + public $_compress_type = 'none'; /** - * @var string Explode separator - */ - var $_separator=' '; + * @var string Explode separator + */ + public $_separator = ' '; /** - * @var file descriptor - */ - var $_file=0; + * @var file descriptor + */ + public $_file = 0; /** - * @var string Local Tar name of a remote Tar (http:// or ftp://) - */ - var $_temp_tarname=''; + * @var string Local Tar name of a remote Tar (http:// or ftp://) + */ + public $_temp_tarname = ''; - // {{{ constructor /** - * Archive_Tar Class constructor. This flavour of the constructor only - * declare a new Archive_Tar object, identifying it by the name of the - * tar file. - * If the compress argument is set the tar will be read or created as a - * gzip or bz2 compressed TAR file. - * - * @param string $p_tarname The name of the tar archive to create - * @param string $p_compress can be null, 'gz' or 'bz2'. This - * parameter indicates if gzip or bz2 compression - * is required. For compatibility reason the - * boolean value 'true' means 'gz'. - * @access public - */ -// function Archive_Tar($p_tarname, $p_compress = null) - function __construct($p_tarname, $p_compress = null) + * @var string regular expression for ignoring files or directories + */ + public $_ignore_regexp = ''; + + /** + * @var object PEAR_Error object + */ + public $error_object = null; + + /** + * Archive_Tar Class constructor. This flavour of the constructor only + * declare a new Archive_Tar object, identifying it by the name of the + * tar file. + * If the compress argument is set the tar will be read or created as a + * gzip or bz2 compressed TAR file. + * + * @param string $p_tarname The name of the tar archive to create + * @param string $p_compress can be null, 'gz', 'bz2' or 'lzma2'. This + * parameter indicates if gzip, bz2 or lzma2 compression + * is required. For compatibility reason the + * boolean value 'true' means 'gz'. + * + * @return bool + */ + public function __construct($p_tarname, $p_compress = null) { -// $this->PEAR(); + // Drupal removal parent::__construct(). + $this->_compress = false; $this->_compress_type = 'none'; if (($p_compress === null) || ($p_compress == '')) { @@ -116,10 +183,13 @@ class Archive_Tar // extends PEAR if ($data == "\37\213") { $this->_compress = true; $this->_compress_type = 'gz'; - // No sure it's enought for a magic code .... + // No sure it's enought for a magic code .... } elseif ($data == "BZ") { $this->_compress = true; $this->_compress_type = 'bz2'; + } elseif (file_get_contents($p_tarname, false, null, 1, 4) == '7zXZ') { + $this->_compress = true; + $this->_compress_type = 'lzma2'; } } } else { @@ -129,151 +199,177 @@ class Archive_Tar // extends PEAR $this->_compress = true; $this->_compress_type = 'gz'; } elseif ((substr($p_tarname, -3) == 'bz2') || - (substr($p_tarname, -2) == 'bz')) { + (substr($p_tarname, -2) == 'bz') + ) { $this->_compress = true; $this->_compress_type = 'bz2'; + } else { + if (substr($p_tarname, -2) == 'xz') { + $this->_compress = true; + $this->_compress_type = 'lzma2'; + } } } } else { if (($p_compress === true) || ($p_compress == 'gz')) { $this->_compress = true; $this->_compress_type = 'gz'; - } else if ($p_compress == 'bz2') { - $this->_compress = true; - $this->_compress_type = 'bz2'; } else { - die("Unsupported compression type '$p_compress'\n". - "Supported types are 'gz' and 'bz2'.\n"); - return false; + if ($p_compress == 'bz2') { + $this->_compress = true; + $this->_compress_type = 'bz2'; + } else { + if ($p_compress == 'lzma2') { + $this->_compress = true; + $this->_compress_type = 'lzma2'; + } else { + $this->_error( + "Unsupported compression type '$p_compress'\n" . + "Supported types are 'gz', 'bz2' and 'lzma2'.\n" + ); + return false; + } + } } } $this->_tarname = $p_tarname; - if ($this->_compress) { // assert zlib or bz2 extension support - if ($this->_compress_type == 'gz') + if ($this->_compress) { // assert zlib or bz2 or xz extension support + if ($this->_compress_type == 'gz') { $extname = 'zlib'; - else if ($this->_compress_type == 'bz2') - $extname = 'bz2'; + } else { + if ($this->_compress_type == 'bz2') { + $extname = 'bz2'; + } else { + if ($this->_compress_type == 'lzma2') { + $extname = 'xz'; + } + } + } if (!extension_loaded($extname)) { -// PEAR::loadExtension($extname); + // Drupal change PEAR::loadExtension($extname). $this->loadExtension($extname); } if (!extension_loaded($extname)) { - die("The extension '$extname' couldn't be found.\n". - "Please make sure your version of PHP was built ". - "with '$extname' support.\n"); + $this->_error( + "The extension '$extname' couldn't be found.\n" . + "Please make sure your version of PHP was built " . + "with '$extname' support.\n" + ); return false; } } } - // }}} + public function __destruct() + { + $this->_close(); + // ----- Look for a local copy to delete + if ($this->_temp_tarname != '') { + @drupal_unlink($this->_temp_tarname); + } + } + + // Drupal addition from PEAR.php. /** * OS independent PHP extension load. Remember to take care * on the correct extension name for case sensitive OSes. - * The function is the copy of PEAR::loadExtension(). * * @param string $ext The extension name * @return bool Success or not on the dl() call */ function loadExtension($ext) { - if (!extension_loaded($ext)) { - // if either returns true dl() will produce a FATAL error, stop that - if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) { - return false; - } - - if (OS_WINDOWS) { - $suffix = '.dll'; - } elseif (PHP_OS == 'HP-UX') { - $suffix = '.sl'; - } elseif (PHP_OS == 'AIX') { - $suffix = '.a'; - } elseif (PHP_OS == 'OSX') { - $suffix = '.bundle'; - } else { - $suffix = '.so'; - } - - return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix); + if (extension_loaded($ext)) { + return true; } - return true; + // if either returns true dl() will produce a FATAL error, stop that + if ( + function_exists('dl') === false || + ini_get('enable_dl') != 1 || + ini_get('safe_mode') == 1 + ) { + return false; + } + + if (OS_WINDOWS) { + $suffix = '.dll'; + } elseif (PHP_OS == 'HP-UX') { + $suffix = '.sl'; + } elseif (PHP_OS == 'AIX') { + $suffix = '.a'; + } elseif (PHP_OS == 'OSX') { + $suffix = '.bundle'; + } else { + $suffix = '.so'; + } + + return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix); } - // {{{ destructor -// function _Archive_Tar() - function __destruct() - { - $this->_close(); - // ----- Look for a local copy to delete - if ($this->_temp_tarname != '') - @drupal_unlink($this->_temp_tarname); -// $this->_PEAR(); - } - // }}} - - // {{{ create() /** - * This method creates the archive file and add the files / directories - * that are listed in $p_filelist. - * If a file with the same name exist and is writable, it is replaced - * by the new tar. - * The method return false and a PEAR error text. - * The $p_filelist parameter can be an array of string, each string - * representing a filename or a directory name with their path if - * needed. It can also be a single string with names separated by a - * single blank. - * For each directory added in the archive, the files and - * sub-directories are also added. - * See also createModify() method for more details. - * - * @param array $p_filelist An array of filenames and directory names, or a - * single string with names separated by a single - * blank space. - * @return true on success, false on error. - * @see createModify() - * @access public - */ - function create($p_filelist) + * This method creates the archive file and add the files / directories + * that are listed in $p_filelist. + * If a file with the same name exist and is writable, it is replaced + * by the new tar. + * The method return false and a PEAR error text. + * The $p_filelist parameter can be an array of string, each string + * representing a filename or a directory name with their path if + * needed. It can also be a single string with names separated by a + * single blank. + * For each directory added in the archive, the files and + * sub-directories are also added. + * See also createModify() method for more details. + * + * @param array $p_filelist An array of filenames and directory names, or a + * single string with names separated by a single + * blank space. + * + * @return true on success, false on error. + * @see createModify() + */ + public function create($p_filelist) { return $this->createModify($p_filelist, '', ''); } - // }}} - // {{{ add() /** - * This method add the files / directories that are listed in $p_filelist in - * the archive. If the archive does not exist it is created. - * The method return false and a PEAR error text. - * The files and directories listed are only added at the end of the archive, - * even if a file with the same name is already archived. - * See also createModify() method for more details. - * - * @param array $p_filelist An array of filenames and directory names, or a - * single string with names separated by a single - * blank space. - * @return true on success, false on error. - * @see createModify() - * @access public - */ - function add($p_filelist) + * This method add the files / directories that are listed in $p_filelist in + * the archive. If the archive does not exist it is created. + * The method return false and a PEAR error text. + * The files and directories listed are only added at the end of the archive, + * even if a file with the same name is already archived. + * See also createModify() method for more details. + * + * @param array $p_filelist An array of filenames and directory names, or a + * single string with names separated by a single + * blank space. + * + * @return true on success, false on error. + * @see createModify() + * @access public + */ + public function add($p_filelist) { return $this->addModify($p_filelist, '', ''); } - // }}} - // {{{ extract() - function extract($p_path='') + /** + * @param string $p_path + * @param bool $p_preserve + * @return bool + */ + public function extract($p_path = '', $p_preserve = false) { - return $this->extractModify($p_path, ''); + return $this->extractModify($p_path, '', $p_preserve); } - // }}} - // {{{ listContent() - function listContent() + /** + * @return array|int + */ + public function listContent() { $v_list_detail = array(); @@ -287,57 +383,56 @@ class Archive_Tar // extends PEAR return $v_list_detail; } - // }}} - // {{{ createModify() /** - * This method creates the archive file and add the files / directories - * that are listed in $p_filelist. - * If the file already exists and is writable, it is replaced by the - * new tar. It is a create and not an add. If the file exists and is - * read-only or is a directory it is not replaced. The method return - * false and a PEAR error text. - * The $p_filelist parameter can be an array of string, each string - * representing a filename or a directory name with their path if - * needed. It can also be a single string with names separated by a - * single blank. - * The path indicated in $p_remove_dir will be removed from the - * memorized path of each file / directory listed when this path - * exists. By default nothing is removed (empty path '') - * The path indicated in $p_add_dir will be added at the beginning of - * the memorized path of each file / directory listed. However it can - * be set to empty ''. The adding of a path is done after the removing - * of path. - * The path add/remove ability enables the user to prepare an archive - * for extraction in a different path than the origin files are. - * See also addModify() method for file adding properties. - * - * @param array $p_filelist An array of filenames and directory names, - * or a single string with names separated by - * a single blank space. - * @param string $p_add_dir A string which contains a path to be added - * to the memorized path of each element in - * the list. - * @param string $p_remove_dir A string which contains a path to be - * removed from the memorized path of each - * element in the list, when relevant. - * @return boolean true on success, false on error. - * @access public - * @see addModify() - */ - function createModify($p_filelist, $p_add_dir, $p_remove_dir='') + * This method creates the archive file and add the files / directories + * that are listed in $p_filelist. + * If the file already exists and is writable, it is replaced by the + * new tar. It is a create and not an add. If the file exists and is + * read-only or is a directory it is not replaced. The method return + * false and a PEAR error text. + * The $p_filelist parameter can be an array of string, each string + * representing a filename or a directory name with their path if + * needed. It can also be a single string with names separated by a + * single blank. + * The path indicated in $p_remove_dir will be removed from the + * memorized path of each file / directory listed when this path + * exists. By default nothing is removed (empty path '') + * The path indicated in $p_add_dir will be added at the beginning of + * the memorized path of each file / directory listed. However it can + * be set to empty ''. The adding of a path is done after the removing + * of path. + * The path add/remove ability enables the user to prepare an archive + * for extraction in a different path than the origin files are. + * See also addModify() method for file adding properties. + * + * @param array $p_filelist An array of filenames and directory names, + * or a single string with names separated by + * a single blank space. + * @param string $p_add_dir A string which contains a path to be added + * to the memorized path of each element in + * the list. + * @param string $p_remove_dir A string which contains a path to be + * removed from the memorized path of each + * element in the list, when relevant. + * + * @return boolean true on success, false on error. + * @see addModify() + */ + public function createModify($p_filelist, $p_add_dir, $p_remove_dir = '') { $v_result = true; - if (!$this->_openWrite()) + if (!$this->_openWrite()) { return false; + } if ($p_filelist != '') { - if (is_array($p_filelist)) + if (is_array($p_filelist)) { $v_list = $p_filelist; - elseif (is_string($p_filelist)) + } elseif (is_string($p_filelist)) { $v_list = explode($this->_separator, $p_filelist); - else { + } else { $this->_cleanFile(); $this->_error('Invalid file list'); return false; @@ -349,67 +444,69 @@ class Archive_Tar // extends PEAR if ($v_result) { $this->_writeFooter(); $this->_close(); - } else + } else { $this->_cleanFile(); + } return $v_result; } - // }}} - // {{{ addModify() /** - * This method add the files / directories listed in $p_filelist at the - * end of the existing archive. If the archive does not yet exists it - * is created. - * The $p_filelist parameter can be an array of string, each string - * representing a filename or a directory name with their path if - * needed. It can also be a single string with names separated by a - * single blank. - * The path indicated in $p_remove_dir will be removed from the - * memorized path of each file / directory listed when this path - * exists. By default nothing is removed (empty path '') - * The path indicated in $p_add_dir will be added at the beginning of - * the memorized path of each file / directory listed. However it can - * be set to empty ''. The adding of a path is done after the removing - * of path. - * The path add/remove ability enables the user to prepare an archive - * for extraction in a different path than the origin files are. - * If a file/dir is already in the archive it will only be added at the - * end of the archive. There is no update of the existing archived - * file/dir. However while extracting the archive, the last file will - * replace the first one. This results in a none optimization of the - * archive size. - * If a file/dir does not exist the file/dir is ignored. However an - * error text is send to PEAR error. - * If a file/dir is not readable the file/dir is ignored. However an - * error text is send to PEAR error. - * - * @param array $p_filelist An array of filenames and directory - * names, or a single string with names - * separated by a single blank space. - * @param string $p_add_dir A string which contains a path to be - * added to the memorized path of each - * element in the list. - * @param string $p_remove_dir A string which contains a path to be - * removed from the memorized path of - * each element in the list, when - * relevant. - * @return true on success, false on error. - * @access public - */ - function addModify($p_filelist, $p_add_dir, $p_remove_dir='') + * This method add the files / directories listed in $p_filelist at the + * end of the existing archive. If the archive does not yet exists it + * is created. + * The $p_filelist parameter can be an array of string, each string + * representing a filename or a directory name with their path if + * needed. It can also be a single string with names separated by a + * single blank. + * The path indicated in $p_remove_dir will be removed from the + * memorized path of each file / directory listed when this path + * exists. By default nothing is removed (empty path '') + * The path indicated in $p_add_dir will be added at the beginning of + * the memorized path of each file / directory listed. However it can + * be set to empty ''. The adding of a path is done after the removing + * of path. + * The path add/remove ability enables the user to prepare an archive + * for extraction in a different path than the origin files are. + * If a file/dir is already in the archive it will only be added at the + * end of the archive. There is no update of the existing archived + * file/dir. However while extracting the archive, the last file will + * replace the first one. This results in a none optimization of the + * archive size. + * If a file/dir does not exist the file/dir is ignored. However an + * error text is send to PEAR error. + * If a file/dir is not readable the file/dir is ignored. However an + * error text is send to PEAR error. + * + * @param array $p_filelist An array of filenames and directory + * names, or a single string with names + * separated by a single blank space. + * @param string $p_add_dir A string which contains a path to be + * added to the memorized path of each + * element in the list. + * @param string $p_remove_dir A string which contains a path to be + * removed from the memorized path of + * each element in the list, when + * relevant. + * + * @return true on success, false on error. + */ + public function addModify($p_filelist, $p_add_dir, $p_remove_dir = '') { $v_result = true; - if (!$this->_isArchive()) - $v_result = $this->createModify($p_filelist, $p_add_dir, - $p_remove_dir); - else { - if (is_array($p_filelist)) + if (!$this->_isArchive()) { + $v_result = $this->createModify( + $p_filelist, + $p_add_dir, + $p_remove_dir + ); + } else { + if (is_array($p_filelist)) { $v_list = $p_filelist; - elseif (is_string($p_filelist)) + } elseif (is_string($p_filelist)) { $v_list = explode($this->_separator, $p_filelist); - else { + } else { $this->_error('Invalid file list'); return false; } @@ -419,24 +516,41 @@ class Archive_Tar // extends PEAR return $v_result; } - // }}} - // {{{ addString() /** - * This method add a single string as a file at the - * end of the existing archive. If the archive does not yet exists it - * is created. - * - * @param string $p_filename A string which contains the full - * filename path that will be associated - * with the string. - * @param string $p_string The content of the file added in - * the archive. - * @return true on success, false on error. - * @access public - */ - function addString($p_filename, $p_string) + * This method add a single string as a file at the + * end of the existing archive. If the archive does not yet exists it + * is created. + * + * @param string $p_filename A string which contains the full + * filename path that will be associated + * with the string. + * @param string $p_string The content of the file added in + * the archive. + * @param bool|int $p_datetime A custom date/time (unix timestamp) + * for the file (optional). + * @param array $p_params An array of optional params: + * stamp => the datetime (replaces + * datetime above if it exists) + * mode => the permissions on the + * file (600 by default) + * type => is this a link? See the + * tar specification for details. + * (default = regular file) + * uid => the user ID of the file + * (default = 0 = root) + * gid => the group ID of the file + * (default = 0 = root) + * + * @return true on success, false on error. + */ + public function addString($p_filename, $p_string, $p_datetime = false, $p_params = array()) { + $p_stamp = @$p_params["stamp"] ? $p_params["stamp"] : ($p_datetime ? $p_datetime : time()); + $p_mode = @$p_params["mode"] ? $p_params["mode"] : 0600; + $p_type = @$p_params["type"] ? $p_params["type"] : ""; + $p_uid = @$p_params["uid"] ? $p_params["uid"] : ""; + $p_gid = @$p_params["gid"] ? $p_params["gid"] : ""; $v_result = true; if (!$this->_isArchive()) { @@ -446,11 +560,12 @@ class Archive_Tar // extends PEAR $this->_close(); } - if (!$this->_openAppend()) + if (!$this->_openAppend()) { return false; + } // Need to check the get back to the temporary file ? .... - $v_result = $this->_addString($p_filename, $p_string); + $v_result = $this->_addString($p_filename, $p_string, $p_datetime, $p_params); $this->_writeFooter(); @@ -458,131 +573,138 @@ class Archive_Tar // extends PEAR return $v_result; } - // }}} - // {{{ extractModify() /** - * This method extract all the content of the archive in the directory - * indicated by $p_path. When relevant the memorized path of the - * files/dir can be modified by removing the $p_remove_path path at the - * beginning of the file/dir path. - * While extracting a file, if the directory path does not exists it is - * created. - * While extracting a file, if the file already exists it is replaced - * without looking for last modification date. - * While extracting a file, if the file already exists and is write - * protected, the extraction is aborted. - * While extracting a file, if a directory with the same name already - * exists, the extraction is aborted. - * While extracting a directory, if a file with the same name already - * exists, the extraction is aborted. - * While extracting a file/directory if the destination directory exist - * and is write protected, or does not exist but can not be created, - * the extraction is aborted. - * If after extraction an extracted file does not show the correct - * stored file size, the extraction is aborted. - * When the extraction is aborted, a PEAR error text is set and false - * is returned. However the result can be a partial extraction that may - * need to be manually cleaned. - * - * @param string $p_path The path of the directory where the - * files/dir need to by extracted. - * @param string $p_remove_path Part of the memorized path that can be - * removed if present at the beginning of - * the file/dir path. - * @return boolean true on success, false on error. - * @access public - * @see extractList() - */ - function extractModify($p_path, $p_remove_path) + * This method extract all the content of the archive in the directory + * indicated by $p_path. When relevant the memorized path of the + * files/dir can be modified by removing the $p_remove_path path at the + * beginning of the file/dir path. + * While extracting a file, if the directory path does not exists it is + * created. + * While extracting a file, if the file already exists it is replaced + * without looking for last modification date. + * While extracting a file, if the file already exists and is write + * protected, the extraction is aborted. + * While extracting a file, if a directory with the same name already + * exists, the extraction is aborted. + * While extracting a directory, if a file with the same name already + * exists, the extraction is aborted. + * While extracting a file/directory if the destination directory exist + * and is write protected, or does not exist but can not be created, + * the extraction is aborted. + * If after extraction an extracted file does not show the correct + * stored file size, the extraction is aborted. + * When the extraction is aborted, a PEAR error text is set and false + * is returned. However the result can be a partial extraction that may + * need to be manually cleaned. + * + * @param string $p_path The path of the directory where the + * files/dir need to by extracted. + * @param string $p_remove_path Part of the memorized path that can be + * removed if present at the beginning of + * the file/dir path. + * @param boolean $p_preserve Preserve user/group ownership of files + * + * @return boolean true on success, false on error. + * @see extractList() + */ + public function extractModify($p_path, $p_remove_path, $p_preserve = false) { $v_result = true; $v_list_detail = array(); if ($v_result = $this->_openRead()) { - $v_result = $this->_extractList($p_path, $v_list_detail, - "complete", 0, $p_remove_path); + $v_result = $this->_extractList( + $p_path, + $v_list_detail, + "complete", + 0, + $p_remove_path, + $p_preserve + ); $this->_close(); } return $v_result; } - // }}} - // {{{ extractInString() /** - * This method extract from the archive one file identified by $p_filename. - * The return value is a string with the file content, or NULL on error. - * @param string $p_filename The path of the file to extract in a string. - * @return a string with the file content or NULL. - * @access public - */ - function extractInString($p_filename) + * This method extract from the archive one file identified by $p_filename. + * The return value is a string with the file content, or NULL on error. + * + * @param string $p_filename The path of the file to extract in a string. + * + * @return a string with the file content or NULL. + */ + public function extractInString($p_filename) { if ($this->_openRead()) { $v_result = $this->_extractInString($p_filename); $this->_close(); } else { - $v_result = NULL; + $v_result = null; } return $v_result; } - // }}} - // {{{ extractList() /** - * This method extract from the archive only the files indicated in the - * $p_filelist. These files are extracted in the current directory or - * in the directory indicated by the optional $p_path parameter. - * If indicated the $p_remove_path can be used in the same way as it is - * used in extractModify() method. - * @param array $p_filelist An array of filenames and directory names, - * or a single string with names separated - * by a single blank space. - * @param string $p_path The path of the directory where the - * files/dir need to by extracted. - * @param string $p_remove_path Part of the memorized path that can be - * removed if present at the beginning of - * the file/dir path. - * @return true on success, false on error. - * @access public - * @see extractModify() - */ - function extractList($p_filelist, $p_path='', $p_remove_path='') + * This method extract from the archive only the files indicated in the + * $p_filelist. These files are extracted in the current directory or + * in the directory indicated by the optional $p_path parameter. + * If indicated the $p_remove_path can be used in the same way as it is + * used in extractModify() method. + * + * @param array $p_filelist An array of filenames and directory names, + * or a single string with names separated + * by a single blank space. + * @param string $p_path The path of the directory where the + * files/dir need to by extracted. + * @param string $p_remove_path Part of the memorized path that can be + * removed if present at the beginning of + * the file/dir path. + * @param boolean $p_preserve Preserve user/group ownership of files + * + * @return true on success, false on error. + * @see extractModify() + */ + public function extractList($p_filelist, $p_path = '', $p_remove_path = '', $p_preserve = false) { $v_result = true; $v_list_detail = array(); - if (is_array($p_filelist)) + if (is_array($p_filelist)) { $v_list = $p_filelist; - elseif (is_string($p_filelist)) + } elseif (is_string($p_filelist)) { $v_list = explode($this->_separator, $p_filelist); - else { + } else { $this->_error('Invalid string list'); return false; } if ($v_result = $this->_openRead()) { - $v_result = $this->_extractList($p_path, $v_list_detail, "partial", - $v_list, $p_remove_path); + $v_result = $this->_extractList( + $p_path, + $v_list_detail, + "partial", + $v_list, + $p_remove_path, + $p_preserve + ); $this->_close(); } return $v_result; } - // }}} - // {{{ setAttribute() /** - * This method set specific attributes of the archive. It uses a variable - * list of parameters, in the format attribute code + attribute values : - * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ','); - * @param mixed $argv variable list of attributes and values - * @return true on success, false on error. - * @access public - */ - function setAttribute() + * This method set specific attributes of the archive. It uses a variable + * list of parameters, in the format attribute code + attribute values : + * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ','); + * + * @return true on success, false on error. + */ + public function setAttribute() { $v_result = true; @@ -592,30 +714,32 @@ class Archive_Tar // extends PEAR } // ----- Get the arguments - $v_att_list = &func_get_args(); + $v_att_list = & func_get_args(); // ----- Read the attributes - $i=0; - while ($i<$v_size) { + $i = 0; + while ($i < $v_size) { // ----- Look for next option switch ($v_att_list[$i]) { // ----- Look for options that request a string value case ARCHIVE_TAR_ATT_SEPARATOR : // ----- Check the number of parameters - if (($i+1) >= $v_size) { - $this->_error('Invalid number of parameters for ' - .'attribute ARCHIVE_TAR_ATT_SEPARATOR'); + if (($i + 1) >= $v_size) { + $this->_error( + 'Invalid number of parameters for ' + . 'attribute ARCHIVE_TAR_ATT_SEPARATOR' + ); return false; } // ----- Get the value - $this->_separator = $v_att_list[$i+1]; + $this->_separator = $v_att_list[$i + 1]; $i++; - break; + break; default : - $this->_error('Unknow attribute code '.$v_att_list[$i].''); + $this->_error('Unknown attribute code ' . $v_att_list[$i] . ''); return false; } @@ -625,151 +749,248 @@ class Archive_Tar // extends PEAR return $v_result; } - // }}} - // {{{ _error() - function _error($p_message) + /** + * This method sets the regular expression for ignoring files and directories + * at import, for example: + * $arch->setIgnoreRegexp("#CVS|\.svn#"); + * + * @param string $regexp regular expression defining which files or directories to ignore + */ + public function setIgnoreRegexp($regexp) { - // ----- To be completed -// $this->raiseError($p_message); + $this->_ignore_regexp = $regexp; + } + + /** + * This method sets the regular expression for ignoring all files and directories + * matching the filenames in the array list at import, for example: + * $arch->setIgnoreList(array('CVS', '.svn', 'bin/tool')); + * + * @param array $list a list of file or directory names to ignore + * + * @access public + */ + public function setIgnoreList($list) + { + $regexp = str_replace(array('#', '.', '^', '$'), array('\#', '\.', '\^', '\$'), $list); + $regexp = '#/' . join('$|/', $list) . '#'; + $this->setIgnoreRegexp($regexp); + } + + /** + * @param string $p_message + */ + public function _error($p_message) + { + // Drupal change $this->error_object = $this->raiseError($p_message). throw new Exception($p_message); } - // }}} - // {{{ _warning() - function _warning($p_message) + /** + * @param string $p_message + */ + public function _warning($p_message) { - // ----- To be completed -// $this->raiseError($p_message); + // Drupal change $this->error_object = $this->raiseError($p_message). throw new Exception($p_message); } - // }}} - // {{{ _isArchive() - function _isArchive($p_filename=NULL) + /** + * @param string $p_filename + * @return bool + */ + public function _isArchive($p_filename = null) { - if ($p_filename == NULL) { + if ($p_filename == null) { $p_filename = $this->_tarname; } clearstatcache(); return @is_file($p_filename) && !@is_link($p_filename); } - // }}} - // {{{ _openWrite() - function _openWrite() + /** + * @return bool + */ + public function _openWrite() { - if ($this->_compress_type == 'gz') + if ($this->_compress_type == 'gz' && function_exists('gzopen')) { $this->_file = @gzopen($this->_tarname, "wb9"); - else if ($this->_compress_type == 'bz2') - $this->_file = @bzopen($this->_tarname, "w"); - else if ($this->_compress_type == 'none') - $this->_file = @fopen($this->_tarname, "wb"); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); + } else { + if ($this->_compress_type == 'bz2' && function_exists('bzopen')) { + $this->_file = @bzopen($this->_tarname, "w"); + } else { + if ($this->_compress_type == 'lzma2' && function_exists('xzopen')) { + $this->_file = @xzopen($this->_tarname, 'w'); + } else { + if ($this->_compress_type == 'none') { + $this->_file = @fopen($this->_tarname, "wb"); + } else { + $this->_error( + 'Unknown or missing compression type (' + . $this->_compress_type . ')' + ); + return false; + } + } + } + } if ($this->_file == 0) { - $this->_error('Unable to open in write mode \'' - .$this->_tarname.'\''); + $this->_error( + 'Unable to open in write mode \'' + . $this->_tarname . '\'' + ); return false; } return true; } - // }}} - // {{{ _openRead() - function _openRead() + /** + * @return bool + */ + public function _openRead() { if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') { - // ----- Look if a local copy need to be done - if ($this->_temp_tarname == '') { - $this->_temp_tarname = uniqid('tar').'.tmp'; - if (!$v_file_from = @fopen($this->_tarname, 'rb')) { - $this->_error('Unable to open in read mode \'' - .$this->_tarname.'\''); - $this->_temp_tarname = ''; - return false; - } - if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) { - $this->_error('Unable to open in write mode \'' - .$this->_temp_tarname.'\''); - $this->_temp_tarname = ''; - return false; - } - while ($v_data = @fread($v_file_from, 1024)) - @fwrite($v_file_to, $v_data); - @fclose($v_file_from); - @fclose($v_file_to); - } + // ----- Look if a local copy need to be done + if ($this->_temp_tarname == '') { + $this->_temp_tarname = uniqid('tar') . '.tmp'; + if (!$v_file_from = @fopen($this->_tarname, 'rb')) { + $this->_error( + 'Unable to open in read mode \'' + . $this->_tarname . '\'' + ); + $this->_temp_tarname = ''; + return false; + } + if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) { + $this->_error( + 'Unable to open in write mode \'' + . $this->_temp_tarname . '\'' + ); + $this->_temp_tarname = ''; + return false; + } + while ($v_data = @fread($v_file_from, 1024)) { + @fwrite($v_file_to, $v_data); + } + @fclose($v_file_from); + @fclose($v_file_to); + } - // ----- File to open if the local copy - $v_filename = $this->_temp_tarname; + // ----- File to open if the local copy + $v_filename = $this->_temp_tarname; + } else { + // ----- File to open if the normal Tar file - } else - // ----- File to open if the normal Tar file - $v_filename = $this->_tarname; + $v_filename = $this->_tarname; + } - if ($this->_compress_type == 'gz') + if ($this->_compress_type == 'gz' && function_exists('gzopen')) { $this->_file = @gzopen($v_filename, "rb"); - else if ($this->_compress_type == 'bz2') - $this->_file = @bzopen($v_filename, "r"); - else if ($this->_compress_type == 'none') - $this->_file = @fopen($v_filename, "rb"); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); + } else { + if ($this->_compress_type == 'bz2' && function_exists('bzopen')) { + $this->_file = @bzopen($v_filename, "r"); + } else { + if ($this->_compress_type == 'lzma2' && function_exists('xzopen')) { + $this->_file = @xzopen($v_filename, "r"); + } else { + if ($this->_compress_type == 'none') { + $this->_file = @fopen($v_filename, "rb"); + } else { + $this->_error( + 'Unknown or missing compression type (' + . $this->_compress_type . ')' + ); + return false; + } + } + } + } if ($this->_file == 0) { - $this->_error('Unable to open in read mode \''.$v_filename.'\''); + $this->_error('Unable to open in read mode \'' . $v_filename . '\''); return false; } return true; } - // }}} - // {{{ _openReadWrite() - function _openReadWrite() + /** + * @return bool + */ + public function _openReadWrite() { - if ($this->_compress_type == 'gz') + if ($this->_compress_type == 'gz') { $this->_file = @gzopen($this->_tarname, "r+b"); - else if ($this->_compress_type == 'bz2') { - $this->_error('Unable to open bz2 in read/write mode \'' - .$this->_tarname.'\' (limitation of bz2 extension)'); - return false; - } else if ($this->_compress_type == 'none') - $this->_file = @fopen($this->_tarname, "r+b"); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); + } else { + if ($this->_compress_type == 'bz2') { + $this->_error( + 'Unable to open bz2 in read/write mode \'' + . $this->_tarname . '\' (limitation of bz2 extension)' + ); + return false; + } else { + if ($this->_compress_type == 'lzma2') { + $this->_error( + 'Unable to open lzma2 in read/write mode \'' + . $this->_tarname . '\' (limitation of lzma2 extension)' + ); + return false; + } else { + if ($this->_compress_type == 'none') { + $this->_file = @fopen($this->_tarname, "r+b"); + } else { + $this->_error( + 'Unknown or missing compression type (' + . $this->_compress_type . ')' + ); + return false; + } + } + } + } if ($this->_file == 0) { - $this->_error('Unable to open in read/write mode \'' - .$this->_tarname.'\''); + $this->_error( + 'Unable to open in read/write mode \'' + . $this->_tarname . '\'' + ); return false; } return true; } - // }}} - // {{{ _close() - function _close() + /** + * @return bool + */ + public function _close() { //if (isset($this->_file)) { if (is_resource($this->_file)) { - if ($this->_compress_type == 'gz') + if ($this->_compress_type == 'gz') { @gzclose($this->_file); - else if ($this->_compress_type == 'bz2') - @bzclose($this->_file); - else if ($this->_compress_type == 'none') - @fclose($this->_file); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); + } else { + if ($this->_compress_type == 'bz2') { + @bzclose($this->_file); + } else { + if ($this->_compress_type == 'lzma2') { + @xzclose($this->_file); + } else { + if ($this->_compress_type == 'none') { + @fclose($this->_file); + } else { + $this->_error( + 'Unknown or missing compression type (' + . $this->_compress_type . ')' + ); + } + } + } + } $this->_file = 0; } @@ -783,10 +1004,11 @@ class Archive_Tar // extends PEAR return true; } - // }}} - // {{{ _cleanFile() - function _cleanFile() + /** + * @return bool + */ + public function _cleanFile() { $this->_close(); @@ -803,296 +1025,419 @@ class Archive_Tar // extends PEAR return true; } - // }}} - // {{{ _writeBlock() - function _writeBlock($p_binary_data, $p_len=null) + /** + * @param mixed $p_binary_data + * @param integer $p_len + * @return bool + */ + public function _writeBlock($p_binary_data, $p_len = null) { - if (is_resource($this->_file)) { - if ($p_len === null) { - if ($this->_compress_type == 'gz') - @gzputs($this->_file, $p_binary_data); - else if ($this->_compress_type == 'bz2') - @bzwrite($this->_file, $p_binary_data); - else if ($this->_compress_type == 'none') - @fputs($this->_file, $p_binary_data); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - } else { - if ($this->_compress_type == 'gz') - @gzputs($this->_file, $p_binary_data, $p_len); - else if ($this->_compress_type == 'bz2') - @bzwrite($this->_file, $p_binary_data, $p_len); - else if ($this->_compress_type == 'none') - @fputs($this->_file, $p_binary_data, $p_len); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - - } - } - return true; + if (is_resource($this->_file)) { + if ($p_len === null) { + if ($this->_compress_type == 'gz') { + @gzputs($this->_file, $p_binary_data); + } else { + if ($this->_compress_type == 'bz2') { + @bzwrite($this->_file, $p_binary_data); + } else { + if ($this->_compress_type == 'lzma2') { + @xzwrite($this->_file, $p_binary_data); + } else { + if ($this->_compress_type == 'none') { + @fputs($this->_file, $p_binary_data); + } else { + $this->_error( + 'Unknown or missing compression type (' + . $this->_compress_type . ')' + ); + } + } + } + } + } else { + if ($this->_compress_type == 'gz') { + @gzputs($this->_file, $p_binary_data, $p_len); + } else { + if ($this->_compress_type == 'bz2') { + @bzwrite($this->_file, $p_binary_data, $p_len); + } else { + if ($this->_compress_type == 'lzma2') { + @xzwrite($this->_file, $p_binary_data, $p_len); + } else { + if ($this->_compress_type == 'none') { + @fputs($this->_file, $p_binary_data, $p_len); + } else { + $this->_error( + 'Unknown or missing compression type (' + . $this->_compress_type . ')' + ); + } + } + } + } + } + } + return true; } - // }}} - // {{{ _readBlock() - function _readBlock() + /** + * @return null|string + */ + public function _readBlock() { - $v_block = null; - if (is_resource($this->_file)) { - if ($this->_compress_type == 'gz') - $v_block = @gzread($this->_file, 512); - else if ($this->_compress_type == 'bz2') - $v_block = @bzread($this->_file, 512); - else if ($this->_compress_type == 'none') - $v_block = @fread($this->_file, 512); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - } - return $v_block; + $v_block = null; + if (is_resource($this->_file)) { + if ($this->_compress_type == 'gz') { + $v_block = @gzread($this->_file, 512); + } else { + if ($this->_compress_type == 'bz2') { + $v_block = @bzread($this->_file, 512); + } else { + if ($this->_compress_type == 'lzma2') { + $v_block = @xzread($this->_file, 512); + } else { + if ($this->_compress_type == 'none') { + $v_block = @fread($this->_file, 512); + } else { + $this->_error( + 'Unknown or missing compression type (' + . $this->_compress_type . ')' + ); + } + } + } + } + } + return $v_block; } - // }}} - // {{{ _jumpBlock() - function _jumpBlock($p_len=null) + /** + * @param null $p_len + * @return bool + */ + public function _jumpBlock($p_len = null) { - if (is_resource($this->_file)) { - if ($p_len === null) - $p_len = 1; + if (is_resource($this->_file)) { + if ($p_len === null) { + $p_len = 1; + } - if ($this->_compress_type == 'gz') { - @gzseek($this->_file, gztell($this->_file)+($p_len*512)); - } - else if ($this->_compress_type == 'bz2') { - // ----- Replace missing bztell() and bzseek() - for ($i=0; $i<$p_len; $i++) - $this->_readBlock(); - } else if ($this->_compress_type == 'none') - @fseek($this->_file, ftell($this->_file)+($p_len*512)); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - - } - return true; + if ($this->_compress_type == 'gz') { + @gzseek($this->_file, gztell($this->_file) + ($p_len * 512)); + } else { + if ($this->_compress_type == 'bz2') { + // ----- Replace missing bztell() and bzseek() + for ($i = 0; $i < $p_len; $i++) { + $this->_readBlock(); + } + } else { + if ($this->_compress_type == 'lzma2') { + // ----- Replace missing xztell() and xzseek() + for ($i = 0; $i < $p_len; $i++) { + $this->_readBlock(); + } + } else { + if ($this->_compress_type == 'none') { + @fseek($this->_file, $p_len * 512, SEEK_CUR); + } else { + $this->_error( + 'Unknown or missing compression type (' + . $this->_compress_type . ')' + ); + } + } + } + } + } + return true; } - // }}} - // {{{ _writeFooter() - function _writeFooter() + /** + * @return bool + */ + public function _writeFooter() { - if (is_resource($this->_file)) { - // ----- Write the last 0 filled block for end of archive - $v_binary_data = pack('a1024', ''); - $this->_writeBlock($v_binary_data); - } - return true; + if (is_resource($this->_file)) { + // ----- Write the last 0 filled block for end of archive + $v_binary_data = pack('a1024', ''); + $this->_writeBlock($v_binary_data); + } + return true; } - // }}} - // {{{ _addList() - function _addList($p_list, $p_add_dir, $p_remove_dir) + /** + * @param array $p_list + * @param string $p_add_dir + * @param string $p_remove_dir + * @return bool + */ + public function _addList($p_list, $p_add_dir, $p_remove_dir) { - $v_result=true; - $v_header = array(); + $v_result = true; + $v_header = array(); - // ----- Remove potential windows directory separator - $p_add_dir = $this->_translateWinPath($p_add_dir); - $p_remove_dir = $this->_translateWinPath($p_remove_dir, false); + // ----- Remove potential windows directory separator + $p_add_dir = $this->_translateWinPath($p_add_dir); + $p_remove_dir = $this->_translateWinPath($p_remove_dir, false); - if (!$this->_file) { - $this->_error('Invalid file descriptor'); - return false; - } - - if (sizeof($p_list) == 0) - return true; - - foreach ($p_list as $v_filename) { - if (!$v_result) { - break; - } - - // ----- Skip the current tar name - if ($v_filename == $this->_tarname) - continue; - - if ($v_filename == '') - continue; - - if (!file_exists($v_filename)) { - $this->_warning("File '$v_filename' does not exist"); - continue; + if (!$this->_file) { + $this->_error('Invalid file descriptor'); + return false; } - // ----- Add the file or directory header - if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir)) - return false; + if (sizeof($p_list) == 0) { + return true; + } - if (@is_dir($v_filename) && !@is_link($v_filename)) { - if (!($p_hdir = opendir($v_filename))) { - $this->_warning("Directory '$v_filename' can not be read"); + foreach ($p_list as $v_filename) { + if (!$v_result) { + break; + } + + // ----- Skip the current tar name + if ($v_filename == $this->_tarname) { continue; } - while (false !== ($p_hitem = readdir($p_hdir))) { - if (($p_hitem != '.') && ($p_hitem != '..')) { - if ($v_filename != ".") - $p_temp_list[0] = $v_filename.'/'.$p_hitem; - else - $p_temp_list[0] = $p_hitem; - $v_result = $this->_addList($p_temp_list, - $p_add_dir, - $p_remove_dir); + if ($v_filename == '') { + continue; + } + + // ----- ignore files and directories matching the ignore regular expression + if ($this->_ignore_regexp && preg_match($this->_ignore_regexp, '/' . $v_filename)) { + $this->_warning("File '$v_filename' ignored"); + continue; + } + + if (!file_exists($v_filename) && !is_link($v_filename)) { + $this->_warning("File '$v_filename' does not exist"); + continue; + } + + // ----- Add the file or directory header + if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir)) { + return false; + } + + if (@is_dir($v_filename) && !@is_link($v_filename)) { + if (!($p_hdir = opendir($v_filename))) { + $this->_warning("Directory '$v_filename' can not be read"); + continue; + } + while (false !== ($p_hitem = readdir($p_hdir))) { + if (($p_hitem != '.') && ($p_hitem != '..')) { + if ($v_filename != ".") { + $p_temp_list[0] = $v_filename . '/' . $p_hitem; + } else { + $p_temp_list[0] = $p_hitem; + } + + $v_result = $this->_addList( + $p_temp_list, + $p_add_dir, + $p_remove_dir + ); + } + } + + unset($p_temp_list); + unset($p_hdir); + unset($p_hitem); + } + } + + return $v_result; + } + + /** + * @param string $p_filename + * @param mixed $p_header + * @param string $p_add_dir + * @param string $p_remove_dir + * @param null $v_stored_filename + * @return bool + */ + public function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir, $v_stored_filename = null) + { + if (!$this->_file) { + $this->_error('Invalid file descriptor'); + return false; + } + + if ($p_filename == '') { + $this->_error('Invalid file name'); + return false; + } + + if (is_null($v_stored_filename)) { + // ----- Calculate the stored filename + $p_filename = $this->_translateWinPath($p_filename, false); + $v_stored_filename = $p_filename; + + if (strcmp($p_filename, $p_remove_dir) == 0) { + return true; + } + + if ($p_remove_dir != '') { + if (substr($p_remove_dir, -1) != '/') { + $p_remove_dir .= '/'; + } + + if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir) { + $v_stored_filename = substr($p_filename, strlen($p_remove_dir)); } } - unset($p_temp_list); - unset($p_hdir); - unset($p_hitem); + $v_stored_filename = $this->_translateWinPath($v_stored_filename); + if ($p_add_dir != '') { + if (substr($p_add_dir, -1) == '/') { + $v_stored_filename = $p_add_dir . $v_stored_filename; + } else { + $v_stored_filename = $p_add_dir . '/' . $v_stored_filename; + } + } + + $v_stored_filename = $this->_pathReduction($v_stored_filename); } - } - return $v_result; + if ($this->_isArchive($p_filename)) { + if (($v_file = @fopen($p_filename, "rb")) == 0) { + $this->_warning( + "Unable to open file '" . $p_filename + . "' in binary read mode" + ); + return true; + } + + if (!$this->_writeHeader($p_filename, $v_stored_filename)) { + return false; + } + + while (($v_buffer = fread($v_file, 512)) != '') { + $v_binary_data = pack("a512", "$v_buffer"); + $this->_writeBlock($v_binary_data); + } + + fclose($v_file); + } else { + // ----- Only header for dir + if (!$this->_writeHeader($p_filename, $v_stored_filename)) { + return false; + } + } + + return true; } - // }}} - // {{{ _addFile() - function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir) + /** + * @param string $p_filename + * @param string $p_string + * @param bool $p_datetime + * @param array $p_params + * @return bool + */ + public function _addString($p_filename, $p_string, $p_datetime = false, $p_params = array()) { - if (!$this->_file) { - $this->_error('Invalid file descriptor'); - return false; - } - - if ($p_filename == '') { - $this->_error('Invalid file name'); - return false; - } - - // ----- Calculate the stored filename - $p_filename = $this->_translateWinPath($p_filename, false);; - $v_stored_filename = $p_filename; - if (strcmp($p_filename, $p_remove_dir) == 0) { - return true; - } - if ($p_remove_dir != '') { - if (substr($p_remove_dir, -1) != '/') - $p_remove_dir .= '/'; - - if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir) - $v_stored_filename = substr($p_filename, strlen($p_remove_dir)); - } - $v_stored_filename = $this->_translateWinPath($v_stored_filename); - if ($p_add_dir != '') { - if (substr($p_add_dir, -1) == '/') - $v_stored_filename = $p_add_dir.$v_stored_filename; - else - $v_stored_filename = $p_add_dir.'/'.$v_stored_filename; - } - - $v_stored_filename = $this->_pathReduction($v_stored_filename); - - if ($this->_isArchive($p_filename)) { - if (($v_file = @fopen($p_filename, "rb")) == 0) { - $this->_warning("Unable to open file '".$p_filename - ."' in binary read mode"); - return true; - } - - if (!$this->_writeHeader($p_filename, $v_stored_filename)) - return false; - - while (($v_buffer = fread($v_file, 512)) != '') { - $v_binary_data = pack("a512", "$v_buffer"); - $this->_writeBlock($v_binary_data); - } - - fclose($v_file); - - } else { - // ----- Only header for dir - if (!$this->_writeHeader($p_filename, $v_stored_filename)) - return false; - } - - return true; - } - // }}} - - // {{{ _addString() - function _addString($p_filename, $p_string) - { - if (!$this->_file) { - $this->_error('Invalid file descriptor'); - return false; - } - - if ($p_filename == '') { - $this->_error('Invalid file name'); - return false; - } - - // ----- Calculate the stored filename - $p_filename = $this->_translateWinPath($p_filename, false);; - - if (!$this->_writeHeaderBlock($p_filename, strlen($p_string), - time(), 384, "", 0, 0)) - return false; - - $i=0; - while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') { - $v_binary_data = pack("a512", $v_buffer); - $this->_writeBlock($v_binary_data); - } - - return true; - } - // }}} - - // {{{ _writeHeader() - function _writeHeader($p_filename, $p_stored_filename) - { - if ($p_stored_filename == '') - $p_stored_filename = $p_filename; - $v_reduce_filename = $this->_pathReduction($p_stored_filename); - - if (strlen($v_reduce_filename) > 99) { - if (!$this->_writeLongHeader($v_reduce_filename)) + $p_stamp = @$p_params["stamp"] ? $p_params["stamp"] : ($p_datetime ? $p_datetime : time()); + $p_mode = @$p_params["mode"] ? $p_params["mode"] : 0600; + $p_type = @$p_params["type"] ? $p_params["type"] : ""; + $p_uid = @$p_params["uid"] ? $p_params["uid"] : 0; + $p_gid = @$p_params["gid"] ? $p_params["gid"] : 0; + if (!$this->_file) { + $this->_error('Invalid file descriptor'); return false; } - $v_info = lstat($p_filename); - $v_uid = sprintf("%6s ", DecOct($v_info[4])); - $v_gid = sprintf("%6s ", DecOct($v_info[5])); - $v_perms = sprintf("%6s ", DecOct($v_info['mode'])); + if ($p_filename == '') { + $this->_error('Invalid file name'); + return false; + } - $v_mtime = sprintf("%11s", DecOct($v_info['mode'])); + // ----- Calculate the stored filename + $p_filename = $this->_translateWinPath($p_filename, false); + + // ----- If datetime is not specified, set current time + if ($p_datetime === false) { + $p_datetime = time(); + } + + if (!$this->_writeHeaderBlock( + $p_filename, + strlen($p_string), + $p_stamp, + $p_mode, + $p_type, + $p_uid, + $p_gid + ) + ) { + return false; + } + + $i = 0; + while (($v_buffer = substr($p_string, (($i++) * 512), 512)) != '') { + $v_binary_data = pack("a512", $v_buffer); + $this->_writeBlock($v_binary_data); + } + + return true; + } + + /** + * @param string $p_filename + * @param string $p_stored_filename + * @return bool + */ + public function _writeHeader($p_filename, $p_stored_filename) + { + if ($p_stored_filename == '') { + $p_stored_filename = $p_filename; + } + $v_reduce_filename = $this->_pathReduction($p_stored_filename); + + if (strlen($v_reduce_filename) > 99) { + if (!$this->_writeLongHeader($v_reduce_filename)) { + return false; + } + } + + $v_info = lstat($p_filename); + $v_uid = sprintf("%07s", DecOct($v_info[4])); + $v_gid = sprintf("%07s", DecOct($v_info[5])); + $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777)); + + $v_mtime = sprintf("%011s", DecOct($v_info['mtime'])); $v_linkname = ''; if (@is_link($p_filename)) { - $v_typeflag = '2'; - $v_linkname = readlink($p_filename); - $v_size = sprintf("%11s ", DecOct(0)); + $v_typeflag = '2'; + $v_linkname = readlink($p_filename); + $v_size = sprintf("%011s", DecOct(0)); } elseif (@is_dir($p_filename)) { - $v_typeflag = "5"; - $v_size = sprintf("%11s ", DecOct(0)); + $v_typeflag = "5"; + $v_size = sprintf("%011s", DecOct(0)); } else { - $v_typeflag = ''; - clearstatcache(); - $v_size = sprintf("%11s ", DecOct($v_info['size'])); + $v_typeflag = '0'; + clearstatcache(); + $v_size = sprintf("%011s", DecOct($v_info['size'])); } - $v_magic = ''; + $v_magic = 'ustar '; - $v_version = ''; + $v_version = ' '; - $v_uname = ''; + if (function_exists('posix_getpwuid')) { + $userinfo = posix_getpwuid($v_info[4]); + $groupinfo = posix_getgrgid($v_info[5]); - $v_gname = ''; + $v_uname = $userinfo['name']; + $v_gname = $groupinfo['name']; + } else { + $v_uname = ''; + $v_gname = ''; + } $v_devmajor = ''; @@ -1100,31 +1445,49 @@ class Archive_Tar // extends PEAR $v_prefix = ''; - $v_binary_data_first = pack("a100a8a8a8a12A12", - $v_reduce_filename, $v_perms, $v_uid, - $v_gid, $v_size, $v_mtime); - $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", - $v_typeflag, $v_linkname, $v_magic, - $v_version, $v_uname, $v_gname, - $v_devmajor, $v_devminor, $v_prefix, ''); + $v_binary_data_first = pack( + "a100a8a8a8a12a12", + $v_reduce_filename, + $v_perms, + $v_uid, + $v_gid, + $v_size, + $v_mtime + ); + $v_binary_data_last = pack( + "a1a100a6a2a32a32a8a8a155a12", + $v_typeflag, + $v_linkname, + $v_magic, + $v_version, + $v_uname, + $v_gname, + $v_devmajor, + $v_devminor, + $v_prefix, + '' + ); // ----- Calculate the checksum $v_checksum = 0; // ..... First part of the header - for ($i=0; $i<148; $i++) - $v_checksum += ord(substr($v_binary_data_first,$i,1)); + for ($i = 0; $i < 148; $i++) { + $v_checksum += ord(substr($v_binary_data_first, $i, 1)); + } // ..... Ignore the checksum value and replace it by ' ' (space) - for ($i=148; $i<156; $i++) + for ($i = 148; $i < 156; $i++) { $v_checksum += ord(' '); + } // ..... Last part of the header - for ($i=156, $j=0; $i<512; $i++, $j++) - $v_checksum += ord(substr($v_binary_data_last,$j,1)); + for ($i = 156, $j = 0; $i < 512; $i++, $j++) { + $v_checksum += ord(substr($v_binary_data_last, $j, 1)); + } // ----- Write the first 148 bytes of the header in the archive $this->_writeBlock($v_binary_data_first, 148); // ----- Write the calculated checksum - $v_checksum = sprintf("%6s ", DecOct($v_checksum)); + $v_checksum = sprintf("%06s ", DecOct($v_checksum)); $v_binary_data = pack("a8", $v_checksum); $this->_writeBlock($v_binary_data, 8); @@ -1133,40 +1496,62 @@ class Archive_Tar // extends PEAR return true; } - // }}} - // {{{ _writeHeaderBlock() - function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0, - $p_type='', $p_uid=0, $p_gid=0) - { + /** + * @param string $p_filename + * @param int $p_size + * @param int $p_mtime + * @param int $p_perms + * @param string $p_type + * @param int $p_uid + * @param int $p_gid + * @return bool + */ + public function _writeHeaderBlock( + $p_filename, + $p_size, + $p_mtime = 0, + $p_perms = 0, + $p_type = '', + $p_uid = 0, + $p_gid = 0 + ) { $p_filename = $this->_pathReduction($p_filename); if (strlen($p_filename) > 99) { - if (!$this->_writeLongHeader($p_filename)) - return false; + if (!$this->_writeLongHeader($p_filename)) { + return false; + } } if ($p_type == "5") { - $v_size = sprintf("%11s ", DecOct(0)); + $v_size = sprintf("%011s", DecOct(0)); } else { - $v_size = sprintf("%11s ", DecOct($p_size)); + $v_size = sprintf("%011s", DecOct($p_size)); } - $v_uid = sprintf("%6s ", DecOct($p_uid)); - $v_gid = sprintf("%6s ", DecOct($p_gid)); - $v_perms = sprintf("%6s ", DecOct($p_perms)); + $v_uid = sprintf("%07s", DecOct($p_uid)); + $v_gid = sprintf("%07s", DecOct($p_gid)); + $v_perms = sprintf("%07s", DecOct($p_perms & 000777)); $v_mtime = sprintf("%11s", DecOct($p_mtime)); $v_linkname = ''; - $v_magic = ''; + $v_magic = 'ustar '; - $v_version = ''; + $v_version = ' '; - $v_uname = ''; + if (function_exists('posix_getpwuid')) { + $userinfo = posix_getpwuid($p_uid); + $groupinfo = posix_getgrgid($p_gid); - $v_gname = ''; + $v_uname = $userinfo['name']; + $v_gname = $groupinfo['name']; + } else { + $v_uname = ''; + $v_gname = ''; + } $v_devmajor = ''; @@ -1174,31 +1559,49 @@ class Archive_Tar // extends PEAR $v_prefix = ''; - $v_binary_data_first = pack("a100a8a8a8a12A12", - $p_filename, $v_perms, $v_uid, $v_gid, - $v_size, $v_mtime); - $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", - $p_type, $v_linkname, $v_magic, - $v_version, $v_uname, $v_gname, - $v_devmajor, $v_devminor, $v_prefix, ''); + $v_binary_data_first = pack( + "a100a8a8a8a12A12", + $p_filename, + $v_perms, + $v_uid, + $v_gid, + $v_size, + $v_mtime + ); + $v_binary_data_last = pack( + "a1a100a6a2a32a32a8a8a155a12", + $p_type, + $v_linkname, + $v_magic, + $v_version, + $v_uname, + $v_gname, + $v_devmajor, + $v_devminor, + $v_prefix, + '' + ); // ----- Calculate the checksum $v_checksum = 0; // ..... First part of the header - for ($i=0; $i<148; $i++) - $v_checksum += ord(substr($v_binary_data_first,$i,1)); + for ($i = 0; $i < 148; $i++) { + $v_checksum += ord(substr($v_binary_data_first, $i, 1)); + } // ..... Ignore the checksum value and replace it by ' ' (space) - for ($i=148; $i<156; $i++) + for ($i = 148; $i < 156; $i++) { $v_checksum += ord(' '); + } // ..... Last part of the header - for ($i=156, $j=0; $i<512; $i++, $j++) - $v_checksum += ord(substr($v_binary_data_last,$j,1)); + for ($i = 156, $j = 0; $i < 512; $i++, $j++) { + $v_checksum += ord(substr($v_binary_data_last, $j, 1)); + } // ----- Write the first 148 bytes of the header in the archive $this->_writeBlock($v_binary_data_first, 148); // ----- Write the calculated checksum - $v_checksum = sprintf("%6s ", DecOct($v_checksum)); + $v_checksum = sprintf("%06s ", DecOct($v_checksum)); $v_binary_data = pack("a8", $v_checksum); $this->_writeBlock($v_binary_data, 8); @@ -1207,10 +1610,12 @@ class Archive_Tar // extends PEAR return true; } - // }}} - // {{{ _writeLongHeader() - function _writeLongHeader($p_filename) + /** + * @param string $p_filename + * @return bool + */ + public function _writeLongHeader($p_filename) { $v_size = sprintf("%11s ", DecOct(strlen($p_filename))); @@ -1232,30 +1637,49 @@ class Archive_Tar // extends PEAR $v_prefix = ''; - $v_binary_data_first = pack("a100a8a8a8a12A12", - '././@LongLink', 0, 0, 0, $v_size, 0); - $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", - $v_typeflag, $v_linkname, $v_magic, - $v_version, $v_uname, $v_gname, - $v_devmajor, $v_devminor, $v_prefix, ''); + $v_binary_data_first = pack( + "a100a8a8a8a12a12", + '././@LongLink', + 0, + 0, + 0, + $v_size, + 0 + ); + $v_binary_data_last = pack( + "a1a100a6a2a32a32a8a8a155a12", + $v_typeflag, + $v_linkname, + $v_magic, + $v_version, + $v_uname, + $v_gname, + $v_devmajor, + $v_devminor, + $v_prefix, + '' + ); // ----- Calculate the checksum $v_checksum = 0; // ..... First part of the header - for ($i=0; $i<148; $i++) - $v_checksum += ord(substr($v_binary_data_first,$i,1)); + for ($i = 0; $i < 148; $i++) { + $v_checksum += ord(substr($v_binary_data_first, $i, 1)); + } // ..... Ignore the checksum value and replace it by ' ' (space) - for ($i=148; $i<156; $i++) + for ($i = 148; $i < 156; $i++) { $v_checksum += ord(' '); + } // ..... Last part of the header - for ($i=156, $j=0; $i<512; $i++, $j++) - $v_checksum += ord(substr($v_binary_data_last,$j,1)); + for ($i = 156, $j = 0; $i < 512; $i++, $j++) { + $v_checksum += ord(substr($v_binary_data_last, $j, 1)); + } // ----- Write the first 148 bytes of the header in the archive $this->_writeBlock($v_binary_data_first, 148); // ----- Write the calculated checksum - $v_checksum = sprintf("%6s ", DecOct($v_checksum)); + $v_checksum = sprintf("%06s ", DecOct($v_checksum)); $v_binary_data = pack("a8", $v_checksum); $this->_writeBlock($v_binary_data, 8); @@ -1263,27 +1687,30 @@ class Archive_Tar // extends PEAR $this->_writeBlock($v_binary_data_last, 356); // ----- Write the filename as content of the block - $i=0; - while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') { + $i = 0; + while (($v_buffer = substr($p_filename, (($i++) * 512), 512)) != '') { $v_binary_data = pack("a512", "$v_buffer"); $this->_writeBlock($v_binary_data); } return true; } - // }}} - // {{{ _readHeader() - function _readHeader($v_binary_data, &$v_header) + /** + * @param mixed $v_binary_data + * @param mixed $v_header + * @return bool + */ + public function _readHeader($v_binary_data, &$v_header) { - if (strlen($v_binary_data)==0) { + if (strlen($v_binary_data) == 0) { $v_header['filename'] = ''; return true; } if (strlen($v_binary_data) != 512) { $v_header['filename'] = ''; - $this->_error('Invalid block size : '.strlen($v_binary_data)); + $this->_error('Invalid block size : ' . strlen($v_binary_data)); return false; } @@ -1293,19 +1720,32 @@ class Archive_Tar // extends PEAR // ----- Calculate the checksum $v_checksum = 0; // ..... First part of the header - for ($i=0; $i<148; $i++) - $v_checksum+=ord(substr($v_binary_data,$i,1)); + for ($i = 0; $i < 148; $i++) { + $v_checksum += ord(substr($v_binary_data, $i, 1)); + } // ..... Ignore the checksum value and replace it by ' ' (space) - for ($i=148; $i<156; $i++) + for ($i = 148; $i < 156; $i++) { $v_checksum += ord(' '); + } // ..... Last part of the header - for ($i=156; $i<512; $i++) - $v_checksum+=ord(substr($v_binary_data,$i,1)); + for ($i = 156; $i < 512; $i++) { + $v_checksum += ord(substr($v_binary_data, $i, 1)); + } - $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" - ."a8checksum/a1typeflag/a100link/a6magic/a2version/" - ."a32uname/a32gname/a8devmajor/a8devminor", - $v_binary_data); + if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) { + $fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" . + "a8checksum/a1typeflag/a100link/a6magic/a2version/" . + "a32uname/a32gname/a8devmajor/a8devminor/a131prefix"; + } else { + $fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" . + "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" . + "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix"; + } + $v_data = unpack($fmt, $v_binary_data); + + if (strlen($v_data["prefix"]) > 0) { + $v_data["filename"] = "$v_data[prefix]/$v_data[filename]"; + } // ----- Extract the checksum $v_header['checksum'] = OctDec(trim($v_data['checksum'])); @@ -1313,20 +1753,25 @@ class Archive_Tar // extends PEAR $v_header['filename'] = ''; // ----- Look for last block (empty block) - if (($v_checksum == 256) && ($v_header['checksum'] == 0)) + if (($v_checksum == 256) && ($v_header['checksum'] == 0)) { return true; + } - $this->_error('Invalid checksum for file "'.$v_data['filename'] - .'" : '.$v_checksum.' calculated, ' - .$v_header['checksum'].' expected'); + $this->_error( + 'Invalid checksum for file "' . $v_data['filename'] + . '" : ' . $v_checksum . ' calculated, ' + . $v_header['checksum'] . ' expected' + ); return false; } // ----- Extract the properties - $v_header['filename'] = trim($v_data['filename']); + $v_header['filename'] = rtrim($v_data['filename'], "\0"); if ($this->_maliciousFilename($v_header['filename'])) { - $this->_error('Malicious .tar detected, file "' . $v_header['filename'] . - '" will not install in desired directory tree'); + $this->_error( + 'Malicious .tar detected, file "' . $v_header['filename'] . + '" will not install in desired directory tree' + ); return false; } $v_header['mode'] = OctDec(trim($v_data['mode'])); @@ -1335,11 +1780,11 @@ class Archive_Tar // extends PEAR $v_header['size'] = OctDec(trim($v_data['size'])); $v_header['mtime'] = OctDec(trim($v_data['mtime'])); if (($v_header['typeflag'] = $v_data['typeflag']) == "5") { - $v_header['size'] = 0; + $v_header['size'] = 0; } $v_header['link'] = trim($v_data['link']); /* ----- All these fields are removed form the header because - they do not carry interesting info + they do not carry interesting info $v_header[magic] = trim($v_data[magic]); $v_header[version] = trim($v_data[version]); $v_header[uname] = trim($v_data[uname]); @@ -1350,17 +1795,15 @@ class Archive_Tar // extends PEAR return true; } - // }}} - // {{{ _maliciousFilename() /** * Detect and report a malicious file name * * @param string $file + * * @return bool - * @access private */ - function _maliciousFilename($file) + private function _maliciousFilename($file) { if (strpos($file, '/../') !== false) { return true; @@ -1370,386 +1813,507 @@ class Archive_Tar // extends PEAR } return false; } - // }}} - // {{{ _readLongHeader() - function _readLongHeader(&$v_header) - { - $v_filename = ''; - $n = floor($v_header['size']/512); - for ($i=0; $i<$n; $i++) { - $v_content = $this->_readBlock(); - $v_filename .= $v_content; - } - if (($v_header['size'] % 512) != 0) { - $v_content = $this->_readBlock(); - $v_filename .= $v_content; - } - - // ----- Read the next header - $v_binary_data = $this->_readBlock(); - - if (!$this->_readHeader($v_binary_data, $v_header)) - return false; - - $v_filename = trim($v_filename); - $v_header['filename'] = $v_filename; - if ($this->_maliciousFilename($v_filename)) { - $this->_error('Malicious .tar detected, file "' . $v_filename . - '" will not install in desired directory tree'); - return false; - } - - return true; - } - // }}} - - // {{{ _extractInString() /** - * This method extract from the archive one file identified by $p_filename. - * The return value is a string with the file content, or NULL on error. - * @param string $p_filename The path of the file to extract in a string. - * @return a string with the file content or NULL. - * @access private - */ - function _extractInString($p_filename) + * @param $v_header + * @return bool + */ + public function _readLongHeader(&$v_header) { - $v_result_str = ""; - - While (strlen($v_binary_data = $this->_readBlock()) != 0) - { - if (!$this->_readHeader($v_binary_data, $v_header)) - return NULL; - - if ($v_header['filename'] == '') - continue; - - // ----- Look for long filename - if ($v_header['typeflag'] == 'L') { - if (!$this->_readLongHeader($v_header)) - return NULL; - } - - if ($v_header['filename'] == $p_filename) { - if ($v_header['typeflag'] == "5") { - $this->_error('Unable to extract in string a directory ' - .'entry {'.$v_header['filename'].'}'); - return NULL; - } else { - $n = floor($v_header['size']/512); - for ($i=0; $i<$n; $i++) { - $v_result_str .= $this->_readBlock(); - } - if (($v_header['size'] % 512) != 0) { - $v_content = $this->_readBlock(); - $v_result_str .= substr($v_content, 0, - ($v_header['size'] % 512)); - } - return $v_result_str; - } - } else { - $this->_jumpBlock(ceil(($v_header['size']/512))); - } + $v_filename = ''; + $v_filesize = $v_header['size']; + $n = floor($v_header['size'] / 512); + for ($i = 0; $i < $n; $i++) { + $v_content = $this->_readBlock(); + $v_filename .= $v_content; + } + if (($v_header['size'] % 512) != 0) { + $v_content = $this->_readBlock(); + $v_filename .= $v_content; } - return NULL; - } - // }}} + // ----- Read the next header + $v_binary_data = $this->_readBlock(); - // {{{ _extractList() - function _extractList($p_path, &$p_list_detail, $p_mode, - $p_file_list, $p_remove_path) - { - $v_result=true; - $v_nb = 0; - $v_extract_all = true; - $v_listing = false; - - $p_path = $this->_translateWinPath($p_path, false); - if ($p_path == '' || (substr($p_path, 0, 1) != '/' - && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) { - $p_path = "./".$p_path; - } - $p_remove_path = $this->_translateWinPath($p_remove_path); - - // ----- Look for path to remove format (should end by /) - if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/')) - $p_remove_path .= '/'; - $p_remove_path_size = strlen($p_remove_path); - - switch ($p_mode) { - case "complete" : - $v_extract_all = TRUE; - $v_listing = FALSE; - break; - case "partial" : - $v_extract_all = FALSE; - $v_listing = FALSE; - break; - case "list" : - $v_extract_all = FALSE; - $v_listing = TRUE; - break; - default : - $this->_error('Invalid extract mode ('.$p_mode.')'); - return false; - } - - clearstatcache(); - - while (strlen($v_binary_data = $this->_readBlock()) != 0) - { - $v_extract_file = FALSE; - $v_extraction_stopped = 0; - - if (!$this->_readHeader($v_binary_data, $v_header)) - return false; - - if ($v_header['filename'] == '') { - continue; - } - - // ----- Look for long filename - if ($v_header['typeflag'] == 'L') { - if (!$this->_readLongHeader($v_header)) - return false; - } - - if ((!$v_extract_all) && (is_array($p_file_list))) { - // ----- By default no unzip if the file is not found - $v_extract_file = false; - - for ($i=0; $i strlen($p_file_list[$i])) - && (substr($v_header['filename'], 0, strlen($p_file_list[$i])) - == $p_file_list[$i])) { - $v_extract_file = TRUE; - break; - } - } - - // ----- It is a file, so compare the file names - elseif ($p_file_list[$i] == $v_header['filename']) { - $v_extract_file = TRUE; - break; - } - } - } else { - $v_extract_file = TRUE; - } - - // ----- Look if this file need to be extracted - if (($v_extract_file) && (!$v_listing)) - { - if (($p_remove_path != '') - && (substr($v_header['filename'], 0, $p_remove_path_size) - == $p_remove_path)) - $v_header['filename'] = substr($v_header['filename'], - $p_remove_path_size); - if (($p_path != './') && ($p_path != '/')) { - while (substr($p_path, -1) == '/') - $p_path = substr($p_path, 0, strlen($p_path)-1); - - if (substr($v_header['filename'], 0, 1) == '/') - $v_header['filename'] = $p_path.$v_header['filename']; - else - $v_header['filename'] = $p_path.'/'.$v_header['filename']; - } - if (file_exists($v_header['filename'])) { - if ( (@is_dir($v_header['filename'])) - && ($v_header['typeflag'] == '')) { - $this->_error('File '.$v_header['filename'] - .' already exists as a directory'); - return false; - } - if ( ($this->_isArchive($v_header['filename'])) - && ($v_header['typeflag'] == "5")) { - $this->_error('Directory '.$v_header['filename'] - .' already exists as a file'); - return false; - } - if (!is_writeable($v_header['filename'])) { - $this->_error('File '.$v_header['filename'] - .' already exists and is write protected'); - return false; - } - if (filemtime($v_header['filename']) > $v_header['mtime']) { - // To be completed : An error or silent no replace ? - } - } - - // ----- Check the directory availability and create it if necessary - elseif (($v_result - = $this->_dirCheck(($v_header['typeflag'] == "5" - ?$v_header['filename'] - :dirname($v_header['filename'])))) != 1) { - $this->_error('Unable to create path for '.$v_header['filename']); + if (!$this->_readHeader($v_binary_data, $v_header)) { return false; } - if ($v_extract_file) { - if ($v_header['typeflag'] == "5") { - if (!@file_exists($v_header['filename'])) { - // Drupal integration. - // Changed the code to use drupal_mkdir() instead of mkdir(). - if (!@drupal_mkdir($v_header['filename'], 0777)) { - $this->_error('Unable to create directory {' - .$v_header['filename'].'}'); - return false; - } - } - } elseif ($v_header['typeflag'] == "2") { - if (@file_exists($v_header['filename'])) { - @drupal_unlink($v_header['filename']); - } - if (!@symlink($v_header['link'], $v_header['filename'])) { - $this->_error('Unable to extract symbolic link {' - .$v_header['filename'].'}'); - return false; - } - } else { - if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) { - $this->_error('Error while opening {'.$v_header['filename'] - .'} in write binary mode'); - return false; - } else { - $n = floor($v_header['size']/512); - for ($i=0; $i<$n; $i++) { - $v_content = $this->_readBlock(); - fwrite($v_dest_file, $v_content, 512); - } - if (($v_header['size'] % 512) != 0) { - $v_content = $this->_readBlock(); - fwrite($v_dest_file, $v_content, ($v_header['size'] % 512)); - } - - @fclose($v_dest_file); - - // ----- Change the file mode, mtime - @touch($v_header['filename'], $v_header['mtime']); - if ($v_header['mode'] & 0111) { - // make file executable, obey umask - $mode = fileperms($v_header['filename']) | (~umask() & 0111); - @chmod($v_header['filename'], $mode); - } - } - - // ----- Check the file size - clearstatcache(); - if (filesize($v_header['filename']) != $v_header['size']) { - $this->_error('Extracted file '.$v_header['filename'] - .' does not have the correct file size \'' - .filesize($v_header['filename']) - .'\' ('.$v_header['size'] - .' expected). Archive may be corrupted.'); - return false; - } - } - } else { - $this->_jumpBlock(ceil(($v_header['size']/512))); + $v_filename = rtrim(substr($v_filename, 0, $v_filesize), "\0"); + $v_header['filename'] = $v_filename; + if ($this->_maliciousFilename($v_filename)) { + $this->_error( + 'Malicious .tar detected, file "' . $v_filename . + '" will not install in desired directory tree' + ); + return false; } - } else { - $this->_jumpBlock(ceil(($v_header['size']/512))); - } - - /* TBC : Seems to be unused ... - if ($this->_compress) - $v_end_of_file = @gzeof($this->_file); - else - $v_end_of_file = @feof($this->_file); - */ - - if ($v_listing || $v_extract_file || $v_extraction_stopped) { - // ----- Log extracted files - if (($v_file_dir = dirname($v_header['filename'])) - == $v_header['filename']) - $v_file_dir = ''; - if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == '')) - $v_file_dir = '/'; - - $p_list_detail[$v_nb++] = $v_header; - if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) { - return true; - } - } - } return true; } - // }}} - // {{{ _openAppend() - function _openAppend() + /** + * This method extract from the archive one file identified by $p_filename. + * The return value is a string with the file content, or null on error. + * + * @param string $p_filename The path of the file to extract in a string. + * + * @return a string with the file content or null. + */ + private function _extractInString($p_filename) { - if (filesize($this->_tarname) == 0) - return $this->_openWrite(); + $v_result_str = ""; + + while (strlen($v_binary_data = $this->_readBlock()) != 0) { + if (!$this->_readHeader($v_binary_data, $v_header)) { + return null; + } + + if ($v_header['filename'] == '') { + continue; + } + + // ----- Look for long filename + if ($v_header['typeflag'] == 'L') { + if (!$this->_readLongHeader($v_header)) { + return null; + } + } + + if ($v_header['filename'] == $p_filename) { + if ($v_header['typeflag'] == "5") { + $this->_error( + 'Unable to extract in string a directory ' + . 'entry {' . $v_header['filename'] . '}' + ); + return null; + } else { + $n = floor($v_header['size'] / 512); + for ($i = 0; $i < $n; $i++) { + $v_result_str .= $this->_readBlock(); + } + if (($v_header['size'] % 512) != 0) { + $v_content = $this->_readBlock(); + $v_result_str .= substr( + $v_content, + 0, + ($v_header['size'] % 512) + ); + } + return $v_result_str; + } + } else { + $this->_jumpBlock(ceil(($v_header['size'] / 512))); + } + } + + return null; + } + + /** + * @param string $p_path + * @param string $p_list_detail + * @param string $p_mode + * @param string $p_file_list + * @param string $p_remove_path + * @param bool $p_preserve + * @return bool + */ + public function _extractList( + $p_path, + &$p_list_detail, + $p_mode, + $p_file_list, + $p_remove_path, + $p_preserve = false + ) { + $v_result = true; + $v_nb = 0; + $v_extract_all = true; + $v_listing = false; + + $p_path = $this->_translateWinPath($p_path, false); + if ($p_path == '' || (substr($p_path, 0, 1) != '/' + && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':')) + ) { + $p_path = "./" . $p_path; + } + $p_remove_path = $this->_translateWinPath($p_remove_path); + + // ----- Look for path to remove format (should end by /) + if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/')) { + $p_remove_path .= '/'; + } + $p_remove_path_size = strlen($p_remove_path); + + switch ($p_mode) { + case "complete" : + $v_extract_all = true; + $v_listing = false; + break; + case "partial" : + $v_extract_all = false; + $v_listing = false; + break; + case "list" : + $v_extract_all = false; + $v_listing = true; + break; + default : + $this->_error('Invalid extract mode (' . $p_mode . ')'); + return false; + } + + clearstatcache(); + + while (strlen($v_binary_data = $this->_readBlock()) != 0) { + $v_extract_file = false; + $v_extraction_stopped = 0; + + if (!$this->_readHeader($v_binary_data, $v_header)) { + return false; + } + + if ($v_header['filename'] == '') { + continue; + } + + // ----- Look for long filename + if ($v_header['typeflag'] == 'L') { + if (!$this->_readLongHeader($v_header)) { + return false; + } + } + + // ignore extended / pax headers + if ($v_header['typeflag'] == 'x' || $v_header['typeflag'] == 'g') { + $this->_jumpBlock(ceil(($v_header['size'] / 512))); + continue; + } + + if ((!$v_extract_all) && (is_array($p_file_list))) { + // ----- By default no unzip if the file is not found + $v_extract_file = false; + + for ($i = 0; $i < sizeof($p_file_list); $i++) { + // ----- Look if it is a directory + if (substr($p_file_list[$i], -1) == '/') { + // ----- Look if the directory is in the filename path + if ((strlen($v_header['filename']) > strlen($p_file_list[$i])) + && (substr($v_header['filename'], 0, strlen($p_file_list[$i])) + == $p_file_list[$i]) + ) { + $v_extract_file = true; + break; + } + } // ----- It is a file, so compare the file names + elseif ($p_file_list[$i] == $v_header['filename']) { + $v_extract_file = true; + break; + } + } + } else { + $v_extract_file = true; + } + + // ----- Look if this file need to be extracted + if (($v_extract_file) && (!$v_listing)) { + if (($p_remove_path != '') + && (substr($v_header['filename'] . '/', 0, $p_remove_path_size) + == $p_remove_path) + ) { + $v_header['filename'] = substr( + $v_header['filename'], + $p_remove_path_size + ); + if ($v_header['filename'] == '') { + continue; + } + } + if (($p_path != './') && ($p_path != '/')) { + while (substr($p_path, -1) == '/') { + $p_path = substr($p_path, 0, strlen($p_path) - 1); + } + + if (substr($v_header['filename'], 0, 1) == '/') { + $v_header['filename'] = $p_path . $v_header['filename']; + } else { + $v_header['filename'] = $p_path . '/' . $v_header['filename']; + } + } + if (file_exists($v_header['filename'])) { + if ((@is_dir($v_header['filename'])) + && ($v_header['typeflag'] == '') + ) { + $this->_error( + 'File ' . $v_header['filename'] + . ' already exists as a directory' + ); + return false; + } + if (($this->_isArchive($v_header['filename'])) + && ($v_header['typeflag'] == "5") + ) { + $this->_error( + 'Directory ' . $v_header['filename'] + . ' already exists as a file' + ); + return false; + } + if (!is_writeable($v_header['filename'])) { + $this->_error( + 'File ' . $v_header['filename'] + . ' already exists and is write protected' + ); + return false; + } + if (filemtime($v_header['filename']) > $v_header['mtime']) { + // To be completed : An error or silent no replace ? + } + } // ----- Check the directory availability and create it if necessary + elseif (($v_result + = $this->_dirCheck( + ($v_header['typeflag'] == "5" + ? $v_header['filename'] + : dirname($v_header['filename'])) + )) != 1 + ) { + $this->_error('Unable to create path for ' . $v_header['filename']); + return false; + } + + if ($v_extract_file) { + if ($v_header['typeflag'] == "5") { + if (!@file_exists($v_header['filename'])) { + if (!@mkdir($v_header['filename'], 0777)) { + $this->_error( + 'Unable to create directory {' + . $v_header['filename'] . '}' + ); + return false; + } + } + } elseif ($v_header['typeflag'] == "2") { + if (@file_exists($v_header['filename'])) { + @drupal_unlink($v_header['filename']); + } + if (!@symlink($v_header['link'], $v_header['filename'])) { + $this->_error( + 'Unable to extract symbolic link {' + . $v_header['filename'] . '}' + ); + return false; + } + } else { + if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) { + $this->_error( + 'Error while opening {' . $v_header['filename'] + . '} in write binary mode' + ); + return false; + } else { + $n = floor($v_header['size'] / 512); + for ($i = 0; $i < $n; $i++) { + $v_content = $this->_readBlock(); + fwrite($v_dest_file, $v_content, 512); + } + if (($v_header['size'] % 512) != 0) { + $v_content = $this->_readBlock(); + fwrite($v_dest_file, $v_content, ($v_header['size'] % 512)); + } + + @fclose($v_dest_file); + + if ($p_preserve) { + @chown($v_header['filename'], $v_header['uid']); + @chgrp($v_header['filename'], $v_header['gid']); + } + + // ----- Change the file mode, mtime + @touch($v_header['filename'], $v_header['mtime']); + if ($v_header['mode'] & 0111) { + // make file executable, obey umask + $mode = fileperms($v_header['filename']) | (~umask() & 0111); + @chmod($v_header['filename'], $mode); + } + } + + // ----- Check the file size + clearstatcache(); + if (!is_file($v_header['filename'])) { + $this->_error( + 'Extracted file ' . $v_header['filename'] + . 'does not exist. Archive may be corrupted.' + ); + return false; + } + + $filesize = filesize($v_header['filename']); + if ($filesize != $v_header['size']) { + $this->_error( + 'Extracted file ' . $v_header['filename'] + . ' does not have the correct file size \'' + . $filesize + . '\' (' . $v_header['size'] + . ' expected). Archive may be corrupted.' + ); + return false; + } + } + } else { + $this->_jumpBlock(ceil(($v_header['size'] / 512))); + } + } else { + $this->_jumpBlock(ceil(($v_header['size'] / 512))); + } + + /* TBC : Seems to be unused ... + if ($this->_compress) + $v_end_of_file = @gzeof($this->_file); + else + $v_end_of_file = @feof($this->_file); + */ + + if ($v_listing || $v_extract_file || $v_extraction_stopped) { + // ----- Log extracted files + if (($v_file_dir = dirname($v_header['filename'])) + == $v_header['filename'] + ) { + $v_file_dir = ''; + } + if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == '')) { + $v_file_dir = '/'; + } + + $p_list_detail[$v_nb++] = $v_header; + if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) { + return true; + } + } + } + + return true; + } + + /** + * @return bool + */ + public function _openAppend() + { + if (filesize($this->_tarname) == 0) { + return $this->_openWrite(); + } if ($this->_compress) { $this->_close(); - if (!@rename($this->_tarname, $this->_tarname.".tmp")) { - $this->_error('Error while renaming \''.$this->_tarname - .'\' to temporary file \''.$this->_tarname - .'.tmp\''); - return false; - } - - if ($this->_compress_type == 'gz') - $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb"); - elseif ($this->_compress_type == 'bz2') - $v_temp_tar = @bzopen($this->_tarname.".tmp", "r"); - - if ($v_temp_tar == 0) { - $this->_error('Unable to open file \''.$this->_tarname - .'.tmp\' in binary read mode'); - @rename($this->_tarname.".tmp", $this->_tarname); - return false; - } - - if (!$this->_openWrite()) { - @rename($this->_tarname.".tmp", $this->_tarname); + if (!@rename($this->_tarname, $this->_tarname . ".tmp")) { + $this->_error( + 'Error while renaming \'' . $this->_tarname + . '\' to temporary file \'' . $this->_tarname + . '.tmp\'' + ); return false; } if ($this->_compress_type == 'gz') { + $v_temp_tar = @gzopen($this->_tarname . ".tmp", "rb"); + } elseif ($this->_compress_type == 'bz2') { + $v_temp_tar = @bzopen($this->_tarname . ".tmp", "r"); + } elseif ($this->_compress_type == 'lzma2') { + $v_temp_tar = @xzopen($this->_tarname . ".tmp", "r"); + } + + + if ($v_temp_tar == 0) { + $this->_error( + 'Unable to open file \'' . $this->_tarname + . '.tmp\' in binary read mode' + ); + @rename($this->_tarname . ".tmp", $this->_tarname); + return false; + } + + if (!$this->_openWrite()) { + @rename($this->_tarname . ".tmp", $this->_tarname); + return false; + } + + if ($this->_compress_type == 'gz') { + $end_blocks = 0; + while (!@gzeof($v_temp_tar)) { $v_buffer = @gzread($v_temp_tar, 512); - if ($v_buffer == ARCHIVE_TAR_END_BLOCK) { + if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) { + $end_blocks++; // do not copy end blocks, we will re-make them // after appending continue; + } elseif ($end_blocks > 0) { + for ($i = 0; $i < $end_blocks; $i++) { + $this->_writeBlock(ARCHIVE_TAR_END_BLOCK); + } + $end_blocks = 0; } $v_binary_data = pack("a512", $v_buffer); $this->_writeBlock($v_binary_data); } @gzclose($v_temp_tar); - } - elseif ($this->_compress_type == 'bz2') { + } elseif ($this->_compress_type == 'bz2') { + $end_blocks = 0; + while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) { - if ($v_buffer == ARCHIVE_TAR_END_BLOCK) { + if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) { + $end_blocks++; + // do not copy end blocks, we will re-make them + // after appending continue; + } elseif ($end_blocks > 0) { + for ($i = 0; $i < $end_blocks; $i++) { + $this->_writeBlock(ARCHIVE_TAR_END_BLOCK); + } + $end_blocks = 0; } $v_binary_data = pack("a512", $v_buffer); $this->_writeBlock($v_binary_data); } @bzclose($v_temp_tar); + } elseif ($this->_compress_type == 'lzma2') { + $end_blocks = 0; + + while (strlen($v_buffer = @xzread($v_temp_tar, 512)) > 0) { + if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) { + $end_blocks++; + // do not copy end blocks, we will re-make them + // after appending + continue; + } elseif ($end_blocks > 0) { + for ($i = 0; $i < $end_blocks; $i++) { + $this->_writeBlock(ARCHIVE_TAR_END_BLOCK); + } + $end_blocks = 0; + } + $v_binary_data = pack("a512", $v_buffer); + $this->_writeBlock($v_binary_data); + } + + @xzclose($v_temp_tar); } - if (!@drupal_unlink($this->_tarname.".tmp")) { - $this->_error('Error while deleting temporary file \'' - .$this->_tarname.'.tmp\''); + if (!@drupal_unlink($this->_tarname . ".tmp")) { + $this->_error( + 'Error while deleting temporary file \'' + . $this->_tarname . '.tmp\'' + ); } - } else { // ----- For not compressed tar, just add files before the last - // one or two 512 bytes block - if (!$this->_openReadWrite()) - return false; + // one or two 512 bytes block + if (!$this->_openReadWrite()) { + return false; + } clearstatcache(); $v_size = filesize($this->_tarname); @@ -1760,32 +2324,34 @@ class Archive_Tar // extends PEAR fseek($this->_file, $v_size - 1024); if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) { fseek($this->_file, $v_size - 1024); - } - elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) { + } elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) { fseek($this->_file, $v_size - 512); } } return true; } - // }}} - // {{{ _append() - function _append($p_filelist, $p_add_dir='', $p_remove_dir='') + /** + * @param $p_filelist + * @param string $p_add_dir + * @param string $p_remove_dir + * @return bool + */ + public function _append($p_filelist, $p_add_dir = '', $p_remove_dir = '') { - if (!$this->_openAppend()) + if (!$this->_openAppend()) { return false; + } - if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir)) - $this->_writeFooter(); + if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir)) { + $this->_writeFooter(); + } $this->_close(); return true; } - // }}} - - // {{{ _dirCheck() /** * Check if a directory exists and create it (including parent @@ -1793,24 +2359,25 @@ class Archive_Tar // extends PEAR * * @param string $p_dir directory to check * - * @return bool TRUE if the directory exists or was created + * @return bool true if the directory exists or was created */ - function _dirCheck($p_dir) + public function _dirCheck($p_dir) { clearstatcache(); - if ((@is_dir($p_dir)) || ($p_dir == '')) + if ((@is_dir($p_dir)) || ($p_dir == '')) { return true; + } $p_parent_dir = dirname($p_dir); if (($p_parent_dir != $p_dir) && ($p_parent_dir != '') && - (!$this->_dirCheck($p_parent_dir))) - return false; + (!$this->_dirCheck($p_parent_dir)) + ) { + return false; + } - // Drupal integration. - // Changed the code to use drupal_mkdir() instead of mkdir(). - if (!@drupal_mkdir($p_dir, 0777)) { + if (!@mkdir($p_dir, 0777)) { $this->_error("Unable to create directory '$p_dir'"); return false; } @@ -1818,10 +2385,6 @@ class Archive_Tar // extends PEAR return true; } - // }}} - - // {{{ _pathReduction() - /** * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar", * rand emove double slashes. @@ -1829,11 +2392,8 @@ class Archive_Tar // extends PEAR * @param string $p_dir path to reduce * * @return string reduced path - * - * @access private - * */ - function _pathReduction($p_dir) + private function _pathReduction($p_dir) { $v_result = ''; @@ -1843,50 +2403,57 @@ class Archive_Tar // extends PEAR $v_list = explode('/', $p_dir); // ----- Study directories from last to first - for ($i=sizeof($v_list)-1; $i>=0; $i--) { + for ($i = sizeof($v_list) - 1; $i >= 0; $i--) { // ----- Look for current path if ($v_list[$i] == ".") { // ----- Ignore this directory // Should be the first $i=0, but no check is done - } - else if ($v_list[$i] == "..") { - // ----- Ignore it and ignore the $i-1 - $i--; - } - else if ( ($v_list[$i] == '') - && ($i!=(sizeof($v_list)-1)) - && ($i!=0)) { - // ----- Ignore only the double '//' in path, - // but not the first and last / } else { - $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/' - .$v_result:''); + if ($v_list[$i] == "..") { + // ----- Ignore it and ignore the $i-1 + $i--; + } else { + if (($v_list[$i] == '') + && ($i != (sizeof($v_list) - 1)) + && ($i != 0) + ) { + // ----- Ignore only the double '//' in path, + // but not the first and last / + } else { + $v_result = $v_list[$i] . ($i != (sizeof($v_list) - 1) ? '/' + . $v_result : ''); + } + } } } } - $v_result = strtr($v_result, '\\', '/'); + + if (defined('OS_WINDOWS') && OS_WINDOWS) { + $v_result = strtr($v_result, '\\', '/'); + } + return $v_result; } - // }}} - - // {{{ _translateWinPath() - function _translateWinPath($p_path, $p_remove_disk_letter=true) + /** + * @param $p_path + * @param bool $p_remove_disk_letter + * @return string + */ + public function _translateWinPath($p_path, $p_remove_disk_letter = true) { - if (defined('OS_WINDOWS') && OS_WINDOWS) { - // ----- Look for potential disk letter - if ( ($p_remove_disk_letter) - && (($v_position = strpos($p_path, ':')) != false)) { - $p_path = substr($p_path, $v_position+1); - } - // ----- Change potential windows directory separator - if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) { - $p_path = strtr($p_path, '\\', '/'); - } - } - return $p_path; + if (defined('OS_WINDOWS') && OS_WINDOWS) { + // ----- Look for potential disk letter + if (($p_remove_disk_letter) + && (($v_position = strpos($p_path, ':')) != false) + ) { + $p_path = substr($p_path, $v_position + 1); + } + // ----- Change potential windows directory separator + if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0, 1) == '\\')) { + $p_path = strtr($p_path, '\\', '/'); + } + } + return $p_path; } - // }}} - } -?> diff --git a/modules/system/system.test b/modules/system/system.test index d4c98f0a..95b43538 100755 --- a/modules/system/system.test +++ b/modules/system/system.test @@ -389,6 +389,18 @@ class ModuleDependencyTestCase extends ModuleTestCase { ); } + /** + * Checks functionality of project namespaces for dependencies. + */ + function testProjectNamespaceForDependencies() { + // Enable module with project namespace to ensure nothing breaks. + $edit = array( + 'modules[Testing][system_project_namespace_test][enable]' => TRUE, + ); + $this->drupalPost('admin/modules', $edit, t('Save configuration')); + $this->assertModules(array('system_project_namespace_test'), TRUE); + } + /** * Attempt to enable translation module without locale enabled. */ @@ -893,6 +905,29 @@ class CronRunTestCase extends DrupalWebTestCase { $result = variable_get('common_test_cron'); $this->assertEqual($result, 'success', 'Cron correctly handles exceptions thrown during hook_cron() invocations.'); } + + /** + * Tests that hook_flush_caches() is not invoked on every single cron run. + * + * @see system_cron() + */ + public function testCronCacheExpiration() { + module_enable(array('system_cron_test')); + variable_del('system_cron_test_flush_caches'); + + // Invoke cron the first time: hook_flush_caches() should be called and then + // get cached. + drupal_cron_run(); + $this->assertEqual(variable_get('system_cron_test_flush_caches'), 1, 'hook_flush_caches() was invoked the first time.'); + $cache = cache_get('system_cache_tables'); + $this->assertEqual(empty($cache), FALSE, 'Cache is filled with cache table data.'); + + // Run cron again and ensure that hook_flush_caches() is not called. + variable_del('system_cron_test_flush_caches'); + drupal_cron_run(); + $this->assertNull(variable_get('system_cron_test_flush_caches'), 'hook_flush_caches() was not invoked the second time.'); + } + } /** @@ -911,7 +946,7 @@ class CronQueueTestCase extends DrupalWebTestCase { } function setUp() { - parent::setUp(array('common_test', 'common_test_cron_helper')); + parent::setUp(array('common_test', 'common_test_cron_helper', 'cron_queue_test')); } /** @@ -931,6 +966,23 @@ class CronQueueTestCase extends DrupalWebTestCase { $this->assertEqual($queue->numberOfItems(), 1, 'Failing item still in the queue after throwing an exception.'); } + /** + * Tests worker defined as a class method callable. + */ + function testCallable() { + $queue = DrupalQueue::get('cron_queue_test_callback'); + + // Enqueue an item for processing. + $queue->createItem(array($this->randomName() => $this->randomName())); + + // Run cron; the worker should perform the task and delete the item from the + // queue. + $this->cronRun(); + + // The queue should be empty. + $this->assertEqual($queue->numberOfItems(), 0); + } + } class AdminMetaTagTestCase extends DrupalWebTestCase { @@ -1298,7 +1350,23 @@ class DateTimeFunctionalTest extends DrupalWebTestCase { $this->assertEqual($this->getUrl(), url('admin/config/regional/date-time/formats', array('absolute' => TRUE)), 'Correct page redirection.'); $this->assertText(t('Custom date format updated.'), 'Custom date format successfully updated.'); + // Check that ajax callback is protected by CSRF token. + $this->drupalGet('admin/config/regional/date-time/formats/lookup', array('query' => array('format' => 'Y m d'))); + $this->assertResponse(403, 'Access denied with no token'); + $this->drupalGet('admin/config/regional/date-time/formats/lookup', array('query' => array('token' => 'invalid', 'format' => 'Y m d'))); + $this->assertResponse(403, 'Access denied with invalid token'); + $this->drupalGet('admin/config/regional/date-time/formats'); + $this->clickLink(t('edit')); + $settings = $this->drupalGetSettings(); + $lookup_url = $settings['dateTime']['date-format']['lookup']; + preg_match('/token=([^&]+)/', $lookup_url, $matches); + $this->assertFalse(empty($matches[1]), 'Found token value'); + $this->drupalGet('admin/config/regional/date-time/formats/lookup', array('query' => array('token' => $matches[1], 'format' => 'Y m d'))); + $this->assertResponse(200, 'Access allowed with valid token'); + $this->assertText(format_date(time(), 'custom', 'Y m d')); + // Delete custom date format. + $this->drupalGet('admin/config/regional/date-time/formats'); $this->clickLink(t('delete')); $this->drupalPost($this->getUrl(), array(), t('Remove')); $this->assertEqual($this->getUrl(), url('admin/config/regional/date-time/formats', array('absolute' => TRUE)), 'Correct page redirection.'); diff --git a/modules/system/tests/cron_queue_test.info b/modules/system/tests/cron_queue_test.info index 09f8a323..0042f854 100644 --- a/modules/system/tests/cron_queue_test.info +++ b/modules/system/tests/cron_queue_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/system/tests/cron_queue_test.module b/modules/system/tests/cron_queue_test.module index e95c6b6a..0df6396a 100644 --- a/modules/system/tests/cron_queue_test.module +++ b/modules/system/tests/cron_queue_test.module @@ -7,9 +7,21 @@ function cron_queue_test_cron_queue_info() { $queues['cron_queue_test_exception'] = array( 'worker callback' => 'cron_queue_test_exception', ); + $queues['cron_queue_test_callback'] = array( + 'worker callback' => array('CronQueueTestCallbackClass', 'foo'), + ); + return $queues; } function cron_queue_test_exception($item) { throw new Exception('That is not supposed to happen.'); } + +class CronQueueTestCallbackClass { + + static public function foo() { + // Do nothing. + } + +} diff --git a/modules/system/tests/system_cron_test.info b/modules/system/tests/system_cron_test.info new file mode 100644 index 00000000..8a82231a --- /dev/null +++ b/modules/system/tests/system_cron_test.info @@ -0,0 +1,12 @@ +name = System Cron Test +description = 'Support module for testing the system_cron().' +package = Testing +version = VERSION +core = 7.x +hidden = TRUE + +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" +project = "drupal" +datestamp = "1456343506" + diff --git a/modules/system/tests/system_cron_test.module b/modules/system/tests/system_cron_test.module new file mode 100644 index 00000000..9ef80e23 --- /dev/null +++ b/modules/system/tests/system_cron_test.module @@ -0,0 +1,15 @@ +' . t('Uses') . ''; $output .= '
'; $output .= '
' . t('Creating vocabularies') . '
'; - $output .= '
' . t('Users with sufficient permissions can create vocabularies and terms through the Taxonomy page. The page listing the terms provides a drag-and-drop interface for controlling the order of the terms and sub-terms within a vocabulary, in a hierarchical fashion. A controlled vocabulary classifying music by genre with terms and sub-terms could look as follows:', array('@taxo' => url('admin/structure/taxonomy'), '@perm' => url('admin/people/permissions', array('fragment'=>'module-taxonomy')))); + $output .= '
' . t('Users with sufficient permissions can create vocabularies and terms through the Taxonomy page. The page listing the terms provides a drag-and-drop interface for controlling the order of the terms and sub-terms within a vocabulary, in a hierarchical fashion. A controlled vocabulary classifying music by genre with terms and sub-terms could look as follows:', array('@taxo' => url('admin/structure/taxonomy'), '@perm' => url('admin/people/permissions', array('fragment' => 'module-taxonomy')))); $output .= '
  • ' . t('vocabulary: Music') . '
  • '; $output .= '
    • ' . t('term: Jazz') . '
    • '; $output .= '
      • ' . t('sub-term: Swing') . '
      • '; diff --git a/modules/toolbar/toolbar.info b/modules/toolbar/toolbar.info index 75a670ea..7edd8eda 100755 --- a/modules/toolbar/toolbar.info +++ b/modules/toolbar/toolbar.info @@ -4,8 +4,8 @@ core = 7.x package = Core version = VERSION -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/tracker/tracker.info b/modules/tracker/tracker.info index 43ddc511..2917302b 100755 --- a/modules/tracker/tracker.info +++ b/modules/tracker/tracker.info @@ -6,8 +6,8 @@ version = VERSION core = 7.x files[] = tracker.test -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/translation/tests/translation_test.info b/modules/translation/tests/translation_test.info index 14da1a48..692b4ad8 100755 --- a/modules/translation/tests/translation_test.info +++ b/modules/translation/tests/translation_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/translation/translation.info b/modules/translation/translation.info index 533412ce..2f38d2ba 100755 --- a/modules/translation/translation.info +++ b/modules/translation/translation.info @@ -6,8 +6,8 @@ version = VERSION core = 7.x files[] = translation.test -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/trigger/tests/trigger_test.info b/modules/trigger/tests/trigger_test.info index b4f052fc..0fe583a3 100755 --- a/modules/trigger/tests/trigger_test.info +++ b/modules/trigger/tests/trigger_test.info @@ -4,8 +4,8 @@ package = Testing core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/trigger/trigger.info b/modules/trigger/trigger.info index 2b088436..85175379 100755 --- a/modules/trigger/trigger.info +++ b/modules/trigger/trigger.info @@ -6,8 +6,8 @@ core = 7.x files[] = trigger.test configure = admin/structure/trigger -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/update/tests/aaa_update_test.info b/modules/update/tests/aaa_update_test.info index e0bbebd5..23756e02 100755 --- a/modules/update/tests/aaa_update_test.info +++ b/modules/update/tests/aaa_update_test.info @@ -4,8 +4,8 @@ package = Testing core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/update/tests/bbb_update_test.info b/modules/update/tests/bbb_update_test.info index 94c8adea..3fa1422d 100755 --- a/modules/update/tests/bbb_update_test.info +++ b/modules/update/tests/bbb_update_test.info @@ -4,8 +4,8 @@ package = Testing core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/update/tests/ccc_update_test.info b/modules/update/tests/ccc_update_test.info index 09598697..50327549 100755 --- a/modules/update/tests/ccc_update_test.info +++ b/modules/update/tests/ccc_update_test.info @@ -4,8 +4,8 @@ package = Testing core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/update/tests/themes/update_test_basetheme/update_test_basetheme.info b/modules/update/tests/themes/update_test_basetheme/update_test_basetheme.info index 6954b3d4..427e9e33 100755 --- a/modules/update/tests/themes/update_test_basetheme/update_test_basetheme.info +++ b/modules/update/tests/themes/update_test_basetheme/update_test_basetheme.info @@ -3,8 +3,8 @@ description = Test theme which acts as a base theme for other test subthemes. core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/update/tests/themes/update_test_subtheme/update_test_subtheme.info b/modules/update/tests/themes/update_test_subtheme/update_test_subtheme.info index 81c222ef..3e6b0ba3 100755 --- a/modules/update/tests/themes/update_test_subtheme/update_test_subtheme.info +++ b/modules/update/tests/themes/update_test_subtheme/update_test_subtheme.info @@ -4,8 +4,8 @@ core = 7.x base theme = update_test_basetheme hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/update/tests/update_test.info b/modules/update/tests/update_test.info index f899512e..1c868d13 100755 --- a/modules/update/tests/update_test.info +++ b/modules/update/tests/update_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/update/update.authorize.inc b/modules/update/update.authorize.inc index 6ddd2c53..03d37049 100755 --- a/modules/update/update.authorize.inc +++ b/modules/update/update.authorize.inc @@ -97,7 +97,9 @@ function update_authorize_run_install($filetransfer, $project, $updater_name, $l } /** - * Batch callback: Copies project to its proper place when authorized to do so. + * Implements callback_batch_operation(). + * + * Copies project to its proper place when authorized to do so. * * @param string $project * The canonical short name of the project being installed. @@ -168,7 +170,9 @@ function update_authorize_batch_copy_project($project, $updater_name, $local_url } /** - * Batch callback: Performs actions when the authorized update batch is done. + * Implements callback_batch_finished(). + * + * Performs actions when the authorized update batch is done. * * This processes the results and stashes them into SESSION such that * authorize.php will render a report. Also responsible for putting the site @@ -235,7 +239,9 @@ function update_authorize_update_batch_finished($success, $results) { } /** - * Batch callback: Performs actions when the authorized install batch is done. + * Implements callback_batch_finished(). + * + * Performs actions when the authorized install batch is done. * * This processes the results and stashes them into SESSION such that * authorize.php will render a report. Also responsible for putting the site diff --git a/modules/update/update.fetch.inc b/modules/update/update.fetch.inc index 9dd2f0ba..428cace6 100755 --- a/modules/update/update.fetch.inc +++ b/modules/update/update.fetch.inc @@ -29,7 +29,9 @@ function update_manual_status() { } /** - * Batch callback: Processes a step in batch for fetching available update data. + * Implements callback_batch_operation(). + * + * Processes a step in batch for fetching available update data. * * @param $context * Reference to an array used for Batch API storage. @@ -77,7 +79,9 @@ function update_fetch_data_batch(&$context) { } /** - * Batch callback: Performs actions when all fetch tasks have been completed. + * Implements callback_batch_finished(). + * + * Performs actions when all fetch tasks have been completed. * * @param $success * TRUE if the batch operation was successful; FALSE if there were errors. diff --git a/modules/update/update.info b/modules/update/update.info index ddd1bde6..f55538f7 100755 --- a/modules/update/update.info +++ b/modules/update/update.info @@ -6,8 +6,8 @@ core = 7.x files[] = update.test configure = admin/reports/updates/settings -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/update/update.manager.inc b/modules/update/update.manager.inc index 85b587de..0b33a5f7 100755 --- a/modules/update/update.manager.inc +++ b/modules/update/update.manager.inc @@ -335,6 +335,8 @@ function update_manager_update_form_submit($form, &$form_state) { } /** + * Implements callback_batch_finished(). + * * Batch callback: Performs actions when the download batch is completed. * * @param $success @@ -847,7 +849,9 @@ function update_manager_file_get($url) { } /** - * Batch callback: Downloads, unpacks, and verifies a project. + * Implements callback_batch_operation(). + * + * Downloads, unpacks, and verifies a project. * * This function assumes that the provided URL points to a file archive of some * sort. The URL can have any scheme that we have a file stream wrapper to diff --git a/modules/user/tests/user_form_test.info b/modules/user/tests/user_form_test.info index c8eeeabe..3f4862d8 100755 --- a/modules/user/tests/user_form_test.info +++ b/modules/user/tests/user_form_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/user/user-picture.tpl.php b/modules/user/user-picture.tpl.php index ee821878..11d92cc5 100755 --- a/modules/user/user-picture.tpl.php +++ b/modules/user/user-picture.tpl.php @@ -17,7 +17,7 @@ */ ?> -
        +
        diff --git a/modules/user/user.api.php b/modules/user/user.api.php index edc61bd3..f205a85b 100755 --- a/modules/user/user.api.php +++ b/modules/user/user.api.php @@ -123,8 +123,8 @@ function hook_user_cancel($edit, $account, $method) { * description is NOT used for the radio button, but instead should provide * additional explanation to the user seeking to cancel their account. * - access: (optional) A boolean value indicating whether the user can access - * a method. If #access is defined, the method cannot be configured as default - * method. + * a method. If access is defined, the method cannot be configured as the + * default method. * * @param $methods * An array containing user account cancellation methods, keyed by method id. @@ -183,7 +183,23 @@ function hook_user_operations() { } /** - * Retrieve a list of user setting or profile information categories. + * Define a list of user settings or profile information categories. + * + * There are two steps to using hook_user_categories(): + * - Create the category with hook_user_categories(). + * - Display that category on the form ID of "user_profile_form" with + * hook_form_FORM_ID_alter(). + * + * Step one builds out the category but it won't be visible on your form until + * you explicitly tell it to do so. + * + * The function in step two should contain the following code in order to + * display your new category: + * @code + * if ($form['#user_category'] == 'mycategory') { + * // Return your form here. + * } + * @endcode * * @return * An array of associative arrays. Each inner array has elements: diff --git a/modules/user/user.info b/modules/user/user.info index aaebd74b..03be9cd5 100755 --- a/modules/user/user.info +++ b/modules/user/user.info @@ -9,8 +9,8 @@ required = TRUE configure = admin/config/people stylesheets[all][] = user.css -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/modules/user/user.js b/modules/user/user.js index d182066a..4cf98161 100755 --- a/modules/user/user.js +++ b/modules/user/user.js @@ -93,6 +93,8 @@ Drupal.behaviors.password = { * Returns the estimated strength and the relevant output message. */ Drupal.evaluatePasswordStrength = function (password, translate) { + password = $.trim(password); + var weaknesses = 0, strength = 100, msg = []; var hasLowercase = /[a-z]+/.test(password); diff --git a/modules/user/user.module b/modules/user/user.module index 0941926a..d38de69b 100644 --- a/modules/user/user.module +++ b/modules/user/user.module @@ -958,6 +958,8 @@ function user_search_access() { */ function user_search_execute($keys = NULL, $conditions = NULL) { $find = array(); + // Escape for LIKE matching. + $keys = db_like($keys); // Replace wildcards with MySQL/PostgreSQL wildcards. $keys = preg_replace('!\*+!', '%', $keys); $query = db_select('users')->extend('PagerDefault'); @@ -967,13 +969,13 @@ function user_search_execute($keys = NULL, $conditions = NULL) { // and they don't need to be restricted to only active users. $query->fields('users', array('mail')); $query->condition(db_or()-> - condition('name', '%' . db_like($keys) . '%', 'LIKE')-> - condition('mail', '%' . db_like($keys) . '%', 'LIKE')); + condition('name', '%' . $keys . '%', 'LIKE')-> + condition('mail', '%' . $keys . '%', 'LIKE')); } else { // Regular users can only search via usernames, and we do not show them // blocked accounts. - $query->condition('name', '%' . db_like($keys) . '%', 'LIKE') + $query->condition('name', '%' . $keys . '%', 'LIKE') ->condition('status', 1); } $uids = $query @@ -1306,10 +1308,12 @@ function user_user_presave(&$edit, $account, $category) { elseif (!empty($edit['picture_delete'])) { $edit['picture'] = NULL; } - // Prepare user roles. - if (isset($edit['roles'])) { - $edit['roles'] = array_filter($edit['roles']); - } + } + + // Filter out roles with empty values to avoid granting extra roles when + // processing custom form submissions. + if (isset($edit['roles'])) { + $edit['roles'] = array_filter($edit['roles']); } // Move account cancellation information into $user->data. @@ -1911,13 +1915,13 @@ function user_menu_link_alter(&$link) { // for authenticated users. Authenticated users should see "My account", but // anonymous users should not see it at all. Therefore, invoke // user_translated_menu_link_alter() to conditionally hide the link. - if ($link['link_path'] == 'user' && $link['module'] == 'system') { + if ($link['link_path'] == 'user' && isset($link['module']) && $link['module'] == 'system') { $link['options']['alter'] = TRUE; } // Force the Logout link to appear on the top-level of 'user-menu' menu by // default (i.e., unless it has been customized). - if ($link['link_path'] == 'user/logout' && $link['module'] == 'system' && empty($link['customized'])) { + if ($link['link_path'] == 'user/logout' && isset($link['module']) && $link['module'] == 'system' && empty($link['customized'])) { $link['plid'] = 0; } } @@ -2225,7 +2229,11 @@ function user_login_final_validate($form, &$form_state) { } } else { - form_set_error('name', t('Sorry, unrecognized username or password. Have you forgotten your password?', array('@password' => url('user/password', array('query' => array('name' => $form_state['values']['name'])))))); + // Use $form_state['input']['name'] here to guarantee that we send + // exactly what the user typed in. $form_state['values']['name'] may have + // been modified by validation handlers that ran earlier than this one. + $query = isset($form_state['input']['name']) ? array('name' => $form_state['input']['name']) : array(); + form_set_error('name', t('Sorry, unrecognized username or password. Have you forgotten your password?', array('@password' => url('user/password', array('query' => $query))))); watchdog('user', 'Login attempt failed for %user.', array('%user' => $form_state['values']['name'])); } } @@ -2348,26 +2356,14 @@ function user_external_login_register($name, $module) { * following properties: * - uid: The user ID number. * - login: The UNIX timestamp of the user's last login. - * @param array $options - * (optional) A keyed array of settings. Supported options are: - * - langcode: A language code to be used when generating locale-sensitive - * urls. If langcode is NULL the users preferred language is used. * * @return * A unique URL that provides a one-time log in for the user, from which * they can change their password. */ -function user_pass_reset_url($account, $options = array()) { +function user_pass_reset_url($account) { $timestamp = REQUEST_TIME; - $url_options = array('absolute' => TRUE); - if (isset($options['langcode'])) { - $languages = language_list(); - $url_options['language'] = $languages[$options['langcode']]; - } - else { - $url_options['language'] = user_preferred_language($account); - } - return url("user/reset/$account->uid/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid), $url_options); + return url("user/reset/$account->uid/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid), array('absolute' => TRUE)); } /** @@ -2379,10 +2375,6 @@ function user_pass_reset_url($account, $options = array()) { * - uid: The user ID number. * - pass: The hashed user password string. * - login: The UNIX timestamp of the user's last login. - * @param array $options - * (optional) A keyed array of settings. Supported options are: - * - langcode: A language code to be used when generating locale-sensitive - * urls. If langcode is NULL the users preferred language is used. * * @return * A unique URL that may be used to confirm the cancellation of the user @@ -2391,17 +2383,9 @@ function user_pass_reset_url($account, $options = array()) { * @see user_mail_tokens() * @see user_cancel_confirm() */ -function user_cancel_url($account, $options = array()) { +function user_cancel_url($account) { $timestamp = REQUEST_TIME; - $url_options = array('absolute' => TRUE); - if (isset($options['langcode'])) { - $languages = language_list(); - $url_options['language'] = $languages[$options['langcode']]; - } - else { - $url_options['language'] = user_preferred_language($account); - } - return url("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid), $url_options); + return url("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid), array('absolute' => TRUE)); } /** @@ -2512,7 +2496,9 @@ function user_cancel($edit, $uid, $method) { } /** - * Last batch processing step for cancelling a user account. + * Implements callback_batch_operation(). + * + * Last step for cancelling a user account. * * Since batch and session API require a valid user account, the actual * cancellation of a user account needs to happen last. @@ -2560,6 +2546,8 @@ function _user_cancel($edit, $account, $method) { } /** + * Implements callback_batch_finished(). + * * Finished batch processing callback for cancelling a user account. * * @see user_cancel() @@ -2887,7 +2875,7 @@ Your account on [site:name] has been canceled. if ($replace) { // We do not sanitize the token replacement, since the output of this // replacement is intended for an e-mail message, not a web browser. - return token_replace($text, $variables, array('language' => $language, 'langcode' => $langcode, 'callback' => 'user_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE)); + return token_replace($text, $variables, array('language' => $language, 'callback' => 'user_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE)); } return $text; @@ -2914,8 +2902,8 @@ Your account on [site:name] has been canceled. */ function user_mail_tokens(&$replacements, $data, $options) { if (isset($data['user'])) { - $replacements['[user:one-time-login-url]'] = user_pass_reset_url($data['user'], $options); - $replacements['[user:cancel-url]'] = user_cancel_url($data['user'], $options); + $replacements['[user:one-time-login-url]'] = user_pass_reset_url($data['user']); + $replacements['[user:cancel-url]'] = user_cancel_url($data['user']); } } @@ -3063,6 +3051,11 @@ function user_role_delete($role) { $role = user_role_load_by_name($role); } + // If this is the administrator role, delete the user_admin_role variable. + if ($role->rid == variable_get('user_admin_role')) { + variable_del('user_admin_role'); + } + db_delete('role') ->condition('rid', $role->rid) ->execute(); @@ -3678,12 +3671,7 @@ function user_form_process_password_confirm($element) { ); $element['#attached']['js'][] = drupal_get_path('module', 'user') . '/user.js'; - // Ensure settings are only added once per page. - static $already_added = FALSE; - if (!$already_added) { - $already_added = TRUE; - $element['#attached']['js'][] = array('data' => $js_settings, 'type' => 'setting'); - } + $element['#attached']['js'][] = array('data' => $js_settings, 'type' => 'setting'); return $element; } diff --git a/modules/user/user.pages.inc b/modules/user/user.pages.inc index f21bd134..2d3c13d0 100755 --- a/modules/user/user.pages.inc +++ b/modules/user/user.pages.inc @@ -105,10 +105,10 @@ function user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $a $reset_link_account = user_load($uid); if (!empty($reset_link_account)) { drupal_set_message(t('Another user (%other_user) is already logged into the site on this computer, but you tried to use a one-time link for user %resetting_user. Please logout and try using the link again.', - array('%other_user' => $user->name, '%resetting_user' => $reset_link_account->name, '!logout' => url('user/logout')))); + array('%other_user' => $user->name, '%resetting_user' => $reset_link_account->name, '!logout' => url('user/logout'))), 'warning'); } else { // Invalid one-time link specifies an unknown user. - drupal_set_message(t('The one-time login link you clicked is invalid.')); + drupal_set_message(t('The one-time login link you clicked is invalid.'), 'error'); } } drupal_goto(); @@ -123,7 +123,7 @@ function user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $a if ($timestamp <= $current && $account = reset($users)) { // No time out for first time login. if ($account->login && $current - $timestamp > $timeout) { - drupal_set_message(t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.')); + drupal_set_message(t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'), 'error'); drupal_goto('user/password'); } elseif ($account->uid && $timestamp >= $account->login && $timestamp <= $current && $hashed_pass == user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid)) { @@ -151,7 +151,7 @@ function user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $a } } else { - drupal_set_message(t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.')); + drupal_set_message(t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'), 'error'); drupal_goto('user/password'); } } @@ -533,7 +533,7 @@ function user_cancel_confirm($account, $timestamp = 0, $hashed_pass = '') { batch_process(''); } else { - drupal_set_message(t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.')); + drupal_set_message(t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.'), 'error'); drupal_goto("user/$account->uid/cancel"); } } diff --git a/modules/user/user.test b/modules/user/user.test index 4536e0c2..b9729c50 100644 --- a/modules/user/user.test +++ b/modules/user/user.test @@ -2095,12 +2095,16 @@ class UserRoleAdminTestCase extends DrupalWebTestCase { $this->assertFalse(user_role_load_by_name($old_name), 'The role can no longer be retrieved from the database using its old name.'); $this->assertTrue(is_object(user_role_load_by_name($role_name)), 'The role can be retrieved from the database using its new name.'); - // Test deleting a role. + // Test deleting the default administrator role. + $role_name = 'administrator'; + $role = user_role_load_by_name($role_name); $this->drupalPost("admin/people/permissions/roles/edit/{$role->rid}", NULL, t('Delete role')); $this->drupalPost(NULL, NULL, t('Delete')); $this->assertText(t('The role has been deleted.'), 'The role has been deleted'); $this->assertNoLinkByHref("admin/people/permissions/roles/edit/{$role->rid}", 'Role edit link removed.'); $this->assertFalse(user_role_load_by_name($role_name), 'A deleted role can no longer be loaded.'); + // Make sure this role is no longer configured as the administrator role. + $this->assertNull(variable_get('user_admin_role'), 'The administrator role is no longer configured as the administrator role.'); // Make sure that the system-defined roles cannot be edited via the user // interface. @@ -2145,26 +2149,6 @@ class UserTokenReplaceTestCase extends DrupalWebTestCase { ); } - public function setUp() { - parent::setUp('locale'); - - $account = $this->drupalCreateUser(array('access administration pages', 'administer languages')); - $this->drupalLogin($account); - - // Add language. - $edit = array('langcode' => 'de'); - $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); - - // Enable URL language detection and selection. - $edit = array('language[enabled][locale-url]' => 1); - $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); - - // Reset static caching. - drupal_static_reset('language_list'); - drupal_static_reset('locale_url_outbound_alter'); - drupal_static_reset('locale_language_url_rewrite_url'); - } - /** * Creates a user, then tests the tokens generated from it. */ @@ -2215,39 +2199,6 @@ class UserTokenReplaceTestCase extends DrupalWebTestCase { $output = token_replace($input, array('user' => $account), array('language' => $language, 'sanitize' => FALSE)); $this->assertEqual($output, $expected, format_string('Unsanitized user token %token replaced.', array('%token' => $input))); } - - $languages = language_list(); - - // Generate login and cancel link. - $tests = array(); - $tests['[user:one-time-login-url]'] = user_pass_reset_url($account); - $tests['[user:cancel-url]'] = user_cancel_url($account); - - // Generate tokens with interface language. - $link = url('user', array('absolute' => TRUE)); - foreach ($tests as $input => $expected) { - $output = token_replace($input, array('user' => $account), array('langcode' => $language->language, 'callback' => 'user_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE)); - $this->assertTrue(strpos($output, $link) === 0, 'Generated URL is in interface language.'); - } - - // Generate tokens with the user's preferred language. - $edit['language'] = 'de'; - $account = user_save($account, $edit); - $link = url('user', array('language' => $languages[$account->language], 'absolute' => TRUE)); - foreach ($tests as $input => $expected) { - $output = token_replace($input, array('user' => $account), array('callback' => 'user_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE)); - $this->assertTrue(strpos($output, $link) === 0, "Generated URL is in the user's preferred language."); - } - - // Generate tokens with one specific language. - $link = url('user', array('language' => $languages['de'], 'absolute' => TRUE)); - foreach ($tests as $input => $expected) { - foreach (array($user1, $user2) as $account) { - $output = token_replace($input, array('user' => $account), array('langcode' => 'de', 'callback' => 'user_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE)); - $this->assertTrue(strpos($output, $link) === 0, "Generated URL in in the requested language."); - } - } - } } @@ -2279,6 +2230,20 @@ class UserUserSearchTestCase extends DrupalWebTestCase { $this->drupalPost('search/user/', $edit, t('Search')); $this->assertText($keys); + // Verify that wildcard search works. + $keys = $user1->name; + $keys = substr($keys, 0, 2) . '*' . substr($keys, 4, 2); + $edit = array('keys' => $keys); + $this->drupalPost('search/user/', $edit, t('Search')); + $this->assertText($user1->name, 'Search for username wildcard resulted in user name on page for administrative user.'); + + // Verify that wildcard search works for email. + $keys = $user1->mail; + $keys = substr($keys, 0, 2) . '*' . substr($keys, 4, 2); + $edit = array('keys' => $keys); + $this->drupalPost('search/user/', $edit, t('Search')); + $this->assertText($user1->name, 'Search for email wildcard resulted in user name on page for administrative user.'); + // Create a blocked user. $blocked_user = $this->drupalCreateUser(); $edit = array('status' => 0); diff --git a/profiles/README.txt b/profiles/README.txt new file mode 100644 index 00000000..91d012ba --- /dev/null +++ b/profiles/README.txt @@ -0,0 +1,28 @@ +Installation profiles define additional steps that run after the base +installation provided by Drupal core when Drupal is first installed. + +WHAT TO PLACE IN THIS DIRECTORY? +-------------------------------- + +Place downloaded and custom installation profiles in this directory. +Installation profiles are generally provided as part of a Drupal distribution. +They only impact the installation of your site. They do not have any effect on +an already running site. + +DOWNLOAD ADDITIONAL DISTRIBUTIONS +--------------------------------- + +Contributed distributions from the Drupal community may be downloaded at +https://www.drupal.org/project/project_distribution. + +MULTISITE CONFIGURATION +----------------------- + +In multisite configurations, installation profiles found in this directory are +available to all sites during their initial site installation. + +MORE INFORMATION +---------------- + +Refer to the "Installation profiles" section of the README.txt in the Drupal +root directory for further information on extending Drupal with custom profiles. diff --git a/profiles/minimal/minimal.info b/profiles/minimal/minimal.info index 551e6f8f..f52b0f96 100755 --- a/profiles/minimal/minimal.info +++ b/profiles/minimal/minimal.info @@ -5,8 +5,8 @@ core = 7.x dependencies[] = block dependencies[] = dblog -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/profiles/standard/standard.info b/profiles/standard/standard.info index 8ad6b6d9..9840da32 100755 --- a/profiles/standard/standard.info +++ b/profiles/standard/standard.info @@ -24,8 +24,8 @@ dependencies[] = field_ui dependencies[] = file dependencies[] = rdf -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info b/profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info index 0dc4ff5a..1a6c0083 100755 --- a/profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info +++ b/profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info @@ -6,8 +6,8 @@ core = 7.x hidden = TRUE files[] = drupal_system_listing_compatible_test.test -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info b/profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info index 43f79b76..d6353f5d 100755 --- a/profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info +++ b/profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info @@ -8,8 +8,8 @@ version = VERSION core = 6.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/profiles/testing/testing.info b/profiles/testing/testing.info index 48f6ba22..4342ddab 100755 --- a/profiles/testing/testing.info +++ b/profiles/testing/testing.info @@ -4,8 +4,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/scripts/password-hash.sh b/scripts/password-hash.sh index 004421a8..1afe4387 100755 --- a/scripts/password-hash.sh +++ b/scripts/password-hash.sh @@ -1,4 +1,4 @@ -#!/usr/bin/php +#!/usr/bin/env php CSS_THEME, 'browsers' => array('IE' => 'lt IE 7', '!IE' => FALSE), 'preprocess' => FALSE)); @@ -43,27 +43,27 @@ function garland_preprocess_html(&$vars) { /** * Override or insert variables into the html template. */ -function garland_process_html(&$vars) { +function garland_process_html(&$variables) { // Hook into color.module if (module_exists('color')) { - _color_html_alter($vars); + _color_html_alter($variables); } } /** * Override or insert variables into the page template. */ -function garland_preprocess_page(&$vars) { +function garland_preprocess_page(&$variables) { // Move secondary tabs into a separate variable. - $vars['tabs2'] = array( + $variables['tabs2'] = array( '#theme' => 'menu_local_tasks', - '#secondary' => $vars['tabs']['#secondary'], + '#secondary' => $variables['tabs']['#secondary'], ); - unset($vars['tabs']['#secondary']); + unset($variables['tabs']['#secondary']); - if (isset($vars['main_menu'])) { - $vars['primary_nav'] = theme('links__system_main_menu', array( - 'links' => $vars['main_menu'], + if (isset($variables['main_menu'])) { + $variables['primary_nav'] = theme('links__system_main_menu', array( + 'links' => $variables['main_menu'], 'attributes' => array( 'class' => array('links', 'inline', 'main-menu'), ), @@ -75,11 +75,11 @@ function garland_preprocess_page(&$vars) { )); } else { - $vars['primary_nav'] = FALSE; + $variables['primary_nav'] = FALSE; } - if (isset($vars['secondary_menu'])) { - $vars['secondary_nav'] = theme('links__system_secondary_menu', array( - 'links' => $vars['secondary_menu'], + if (isset($variables['secondary_menu'])) { + $variables['secondary_nav'] = theme('links__system_secondary_menu', array( + 'links' => $variables['secondary_menu'], 'attributes' => array( 'class' => array('links', 'inline', 'secondary-menu'), ), @@ -91,66 +91,66 @@ function garland_preprocess_page(&$vars) { )); } else { - $vars['secondary_nav'] = FALSE; + $variables['secondary_nav'] = FALSE; } // Prepare header. $site_fields = array(); - if (!empty($vars['site_name'])) { - $site_fields[] = $vars['site_name']; + if (!empty($variables['site_name'])) { + $site_fields[] = $variables['site_name']; } - if (!empty($vars['site_slogan'])) { - $site_fields[] = $vars['site_slogan']; + if (!empty($variables['site_slogan'])) { + $site_fields[] = $variables['site_slogan']; } - $vars['site_title'] = implode(' ', $site_fields); + $variables['site_title'] = implode(' ', $site_fields); if (!empty($site_fields)) { $site_fields[0] = '' . $site_fields[0] . ''; } - $vars['site_html'] = implode(' ', $site_fields); + $variables['site_html'] = implode(' ', $site_fields); // Set a variable for the site name title and logo alt attributes text. - $slogan_text = $vars['site_slogan']; - $site_name_text = $vars['site_name']; - $vars['site_name_and_slogan'] = $site_name_text . ' ' . $slogan_text; + $slogan_text = $variables['site_slogan']; + $site_name_text = $variables['site_name']; + $variables['site_name_and_slogan'] = $site_name_text . ' ' . $slogan_text; } /** * Override or insert variables into the node template. */ -function garland_preprocess_node(&$vars) { - $vars['submitted'] = $vars['date'] . ' — ' . $vars['name']; +function garland_preprocess_node(&$variables) { + $variables['submitted'] = $variables['date'] . ' — ' . $variables['name']; } /** * Override or insert variables into the comment template. */ -function garland_preprocess_comment(&$vars) { - $vars['submitted'] = $vars['created'] . ' — ' . $vars['author']; +function garland_preprocess_comment(&$variables) { + $variables['submitted'] = $variables['created'] . ' — ' . $variables['author']; } /** * Override or insert variables into the block template. */ -function garland_preprocess_block(&$vars) { - $vars['title_attributes_array']['class'][] = 'title'; - $vars['classes_array'][] = 'clearfix'; +function garland_preprocess_block(&$variables) { + $variables['title_attributes_array']['class'][] = 'title'; + $variables['classes_array'][] = 'clearfix'; } /** * Override or insert variables into the page template. */ -function garland_process_page(&$vars) { +function garland_process_page(&$variables) { // Hook into color.module if (module_exists('color')) { - _color_page_alter($vars); + _color_page_alter($variables); } } /** * Override or insert variables into the region template. */ -function garland_preprocess_region(&$vars) { - if ($vars['region'] == 'header') { - $vars['classes_array'][] = 'clearfix'; +function garland_preprocess_region(&$variables) { + if ($variables['region'] == 'header') { + $variables['classes_array'][] = 'clearfix'; } } diff --git a/themes/seven/seven.info b/themes/seven/seven.info index 502000c8..3866ed2e 100755 --- a/themes/seven/seven.info +++ b/themes/seven/seven.info @@ -13,8 +13,8 @@ regions[page_bottom] = Page bottom regions[sidebar_first] = First sidebar regions_hidden[] = sidebar_first -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/themes/stark/stark.info b/themes/stark/stark.info index 49257c32..4b2f7db5 100755 --- a/themes/stark/stark.info +++ b/themes/stark/stark.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x stylesheets[all][] = layout.css -; Information added by Drupal.org packaging script on 2015-08-19 -version = "7.39" +; Information added by Drupal.org packaging script on 2016-02-24 +version = "7.43" project = "drupal" -datestamp = "1440020197" +datestamp = "1456343506" diff --git a/web.config b/web.config index 09983d92..c6fb5c84 100755 --- a/web.config +++ b/web.config @@ -6,7 +6,7 @@ - +