Browse Source

security update for contrib modules

Bachir Soussi Chiadmi 5 years ago
parent
commit
868629cbb2
100 changed files with 5141 additions and 2118 deletions
  1. 240 0
      sites/all/modules/contrib/admin/backup_migrate/CHANGELOG.txt
  2. 0 0
      sites/all/modules/contrib/admin/backup_migrate/LICENSE.txt
  3. 161 58
      sites/all/modules/contrib/admin/backup_migrate/README.txt
  4. 7 4
      sites/all/modules/contrib/admin/backup_migrate/backup_migrate.css
  5. 36 5
      sites/all/modules/contrib/admin/backup_migrate/backup_migrate.info
  6. 361 93
      sites/all/modules/contrib/admin/backup_migrate/backup_migrate.install
  7. 29 17
      sites/all/modules/contrib/admin/backup_migrate/backup_migrate.js
  8. 1 7
      sites/all/modules/contrib/admin/backup_migrate/backup_migrate.module
  9. 83 23
      sites/all/modules/contrib/admin/backup_migrate/includes/backup_migrate.drush.inc
  10. 132 138
      sites/all/modules/contrib/admin/backup_migrate/includes/crud.inc
  11. 33 12
      sites/all/modules/contrib/admin/backup_migrate/includes/destinations.browser.inc
  12. 81 75
      sites/all/modules/contrib/admin/backup_migrate/includes/destinations.db.inc
  13. 154 93
      sites/all/modules/contrib/admin/backup_migrate/includes/destinations.db.mysql.inc
  14. 37 28
      sites/all/modules/contrib/admin/backup_migrate/includes/destinations.email.inc
  15. 50 43
      sites/all/modules/contrib/admin/backup_migrate/includes/destinations.file.inc
  16. 55 61
      sites/all/modules/contrib/admin/backup_migrate/includes/destinations.ftp.inc
  17. 115 121
      sites/all/modules/contrib/admin/backup_migrate/includes/destinations.inc
  18. 22 11
      sites/all/modules/contrib/admin/backup_migrate/includes/destinations.nodesquirrel.inc
  19. 28 26
      sites/all/modules/contrib/admin/backup_migrate/includes/destinations.s3.inc
  20. 56 52
      sites/all/modules/contrib/admin/backup_migrate/includes/files.inc
  21. 43 33
      sites/all/modules/contrib/admin/backup_migrate/includes/filters.backup_restore.inc
  22. 19 21
      sites/all/modules/contrib/admin/backup_migrate/includes/filters.compression.inc
  23. 31 29
      sites/all/modules/contrib/admin/backup_migrate/includes/filters.encryption.inc
  24. 47 49
      sites/all/modules/contrib/admin/backup_migrate/includes/filters.inc
  25. 9 12
      sites/all/modules/contrib/admin/backup_migrate/includes/filters.statusnotify.inc
  26. 43 32
      sites/all/modules/contrib/admin/backup_migrate/includes/filters.utils.inc
  27. 67 106
      sites/all/modules/contrib/admin/backup_migrate/includes/locations.inc
  28. 62 44
      sites/all/modules/contrib/admin/backup_migrate/includes/profiles.inc
  29. 95 94
      sites/all/modules/contrib/admin/backup_migrate/includes/schedules.inc
  30. 75 49
      sites/all/modules/contrib/admin/backup_migrate/includes/sources.archivesource.inc
  31. 86 76
      sites/all/modules/contrib/admin/backup_migrate/includes/sources.db.inc
  32. 157 98
      sites/all/modules/contrib/admin/backup_migrate/includes/sources.db.mysql.inc
  33. 91 60
      sites/all/modules/contrib/admin/backup_migrate/includes/sources.filesource.inc
  34. 32 27
      sites/all/modules/contrib/admin/backup_migrate/includes/sources.inc
  35. 246 0
      sites/all/modules/contrib/admin/backup_migrate/tests/BmTestBase.test
  36. 111 0
      sites/all/modules/contrib/admin/backup_migrate/tests/BmTestBasics.test
  37. 168 0
      sites/all/modules/contrib/admin/backup_migrate/tests/BmTestProfiles.test
  38. 21 5
      sites/all/modules/contrib/admin/context/context.core.inc
  39. 3 4
      sites/all/modules/contrib/admin/context/context.info
  40. 1 1
      sites/all/modules/contrib/admin/context/context.install
  41. 60 5
      sites/all/modules/contrib/admin/context/context.module
  42. 2 2
      sites/all/modules/contrib/admin/context/context.plugins.inc
  43. 3 4
      sites/all/modules/contrib/admin/context/context_layouts/context_layouts.info
  44. 3 4
      sites/all/modules/contrib/admin/context/context_ui/context_ui.info
  45. 2 2
      sites/all/modules/contrib/admin/context/context_ui/context_ui.js
  46. 8 12
      sites/all/modules/contrib/admin/context/context_ui/context_ui.module
  47. 2 2
      sites/all/modules/contrib/admin/context/context_ui/context_ui_dialog.js
  48. 11 11
      sites/all/modules/contrib/admin/context/context_ui/export_ui/context_export_ui.class.php
  49. 1 1
      sites/all/modules/contrib/admin/context/context_ui/tests/context_ui.test
  50. 1 1
      sites/all/modules/contrib/admin/context/plugins/context_condition_bookroot.inc
  51. 1 1
      sites/all/modules/contrib/admin/context/plugins/context_condition_menu.inc
  52. 9 5
      sites/all/modules/contrib/admin/context/plugins/context_condition_node_taxonomy.inc
  53. 1 1
      sites/all/modules/contrib/admin/context/plugins/context_condition_query_string.inc
  54. 11 10
      sites/all/modules/contrib/admin/context/plugins/context_reaction_block.inc
  55. 5 6
      sites/all/modules/contrib/admin/context/plugins/context_reaction_block.js
  56. 1 0
      sites/all/modules/contrib/admin/context/plugins/context_reaction_breadcrumb.inc
  57. 3 1
      sites/all/modules/contrib/admin/context/plugins/context_reaction_menu.inc
  58. 5 1
      sites/all/modules/contrib/admin/context/plugins/context_reaction_region.inc
  59. 1 1
      sites/all/modules/contrib/admin/context/plugins/context_reaction_template_suggestions.inc
  60. 0 22
      sites/all/modules/contrib/admin/context/tests/context.conditions.test
  61. 3 4
      sites/all/modules/contrib/admin/context/theme/context_reaction_block.theme.inc
  62. 101 34
      sites/all/modules/contrib/admin/features/features.admin.inc
  63. 24 3
      sites/all/modules/contrib/admin/features/features.api.php
  64. 151 22
      sites/all/modules/contrib/admin/features/features.drush.inc
  65. 172 16
      sites/all/modules/contrib/admin/features/features.export.inc
  66. 7 4
      sites/all/modules/contrib/admin/features/features.info
  67. 27 4
      sites/all/modules/contrib/admin/features/features.install
  68. 7 5
      sites/all/modules/contrib/admin/features/features.js
  69. 112 19
      sites/all/modules/contrib/admin/features/features.module
  70. 115 0
      sites/all/modules/contrib/admin/features/includes/features.contact.inc
  71. 58 11
      sites/all/modules/contrib/admin/features/includes/features.field.inc
  72. 28 13
      sites/all/modules/contrib/admin/features/includes/features.image.inc
  73. 3 2
      sites/all/modules/contrib/admin/features/includes/features.locale.inc
  74. 11 7
      sites/all/modules/contrib/admin/features/includes/features.menu.inc
  75. 15 4
      sites/all/modules/contrib/admin/features/includes/features.node.inc
  76. 44 1
      sites/all/modules/contrib/admin/features/tests/features.test
  77. 0 8
      sites/all/modules/contrib/admin/features/tests/features_test/features_test.features.inc
  78. 3 4
      sites/all/modules/contrib/admin/features/tests/features_test/features_test.info
  79. 44 19
      sites/all/modules/contrib/admin/google_analytics/README.txt
  80. 1 1
      sites/all/modules/contrib/admin/google_analytics/googleanalytics.admin.inc
  81. 9 0
      sites/all/modules/contrib/admin/google_analytics/googleanalytics.admin.js
  82. 50 20
      sites/all/modules/contrib/admin/google_analytics/googleanalytics.debug.js
  83. 3 4
      sites/all/modules/contrib/admin/google_analytics/googleanalytics.info
  84. 22 15
      sites/all/modules/contrib/admin/google_analytics/googleanalytics.install
  85. 47 17
      sites/all/modules/contrib/admin/google_analytics/googleanalytics.js
  86. 61 20
      sites/all/modules/contrib/admin/google_analytics/googleanalytics.module
  87. 117 19
      sites/all/modules/contrib/admin/google_analytics/googleanalytics.test
  88. 8 6
      sites/all/modules/contrib/admin/google_analytics/googleanalytics.test.js
  89. 0 0
      sites/all/modules/contrib/admin/google_analytics/googleanalytics.variable.inc
  90. 184 41
      sites/all/modules/contrib/admin/module_filter/CHANGELOG.txt
  91. 0 0
      sites/all/modules/contrib/admin/module_filter/LICENSE.txt
  92. 107 0
      sites/all/modules/contrib/admin/module_filter/README.txt
  93. 23 0
      sites/all/modules/contrib/admin/module_filter/css/dynamic_position.css
  94. 23 1
      sites/all/modules/contrib/admin/module_filter/css/module_filter.css
  95. 54 0
      sites/all/modules/contrib/admin/module_filter/css/module_filter_tab-rtl.css
  96. 218 95
      sites/all/modules/contrib/admin/module_filter/css/module_filter_tab.css
  97. 53 0
      sites/all/modules/contrib/admin/module_filter/css/modules.css
  98. 18 0
      sites/all/modules/contrib/admin/module_filter/css/update_status.css
  99. BIN
      sites/all/modules/contrib/admin/module_filter/images/collapsed.png
  100. BIN
      sites/all/modules/contrib/admin/module_filter/images/expanded.png

+ 240 - 0
sites/all/modules/contrib/admin/backup_migrate/CHANGELOG.txt

@@ -0,0 +1,240 @@
+Backup and Migrate 7.x-3.6, 2018-12-15
+--------------------------------------
+#2293601 by KimNyholm, fastturtle: Errors shown because directory handles are
+  not closed when no longer needed.
+#2937023 by DamienMcKenna: Add a test for the basic UI functionality.
+#2945704 by DamienMcKenna: Write tests for each backup filename option.
+#2939277 by DamienMcKenna, baronmunchowsen: File mode change on
+  sources.archivesource.inc.
+#2935403 by DamienMcKenna, jacob.embree: Improve coding standards compliance.
+#2962822 by laryn: Spelling mistake: "frequecy".
+#2917959 by DamienMcKenna: Updated backup_migrate_update_7305() to fix schema
+  problems.
+#2996191 by mwnciau, DamienMcKenna: Special characters in database password
+  causes PHP notifications.
+#2949211 by gturnbull, DamienMcKenna, Alex Bukach, Eli.Stone, joelpittet:
+  Optional memory limit setting to work around out-of-memory errors during
+  backups.
+#2494757 by pbuyle, DamienMcKenna, Ives, Calystod: Allow running a scheduled
+  backup with Drush.
+#2404113 by DamienMcKenna, John Cook, danharper, salmino: Different host for
+  AWS type server.
+#2829492 by axel.rutz, RickJ, DamienMcKenna, devad, Kris77, pietrocap,
+  couturier: Big file backup breaks with "MySQL server has gone away".
+#3001702 by DamienMcKenna: Code cleanup on 7.x-3.x branch.
+#2831470 by snehal.addweb, bfodeke, jigish.addweb: Delete query in
+  hook_uninstall() can potentially remove unintended variables.
+#3004759 by Manthan.addweb, echoz: Notice: Undefined index: exclude_filepaths...
+  visiting Advanced Backup tab.
+#3013355 by jacob.embree: Syntax error in backup_migrate.js.
+#2415421 by ron_s, coredumperror, blake.thompson, John Bickar: Backup and
+  Migrate module and private file system.
+#3002975 by DamienMcKenna: Improve logging around the cron and backup processes.
+#2747197 by RickJ: Network errors writing to Nodesquirrel handled badly.
+#3014594 by DamienMcKenna: backup_migrate_uninstall() doesn't delete all.
+  variables; renamed "backup_migrate_backup_memory_limit" variable to
+  "backup_migrate_memory_limit".
+#3014601 by DamienMcKenna: Re-enable all tests.
+#3014143 by DamienMcKenna: Add master switch to turn off hook_cron.
+#3014603 by DamienMcKenna: Add a CHANGELOG.txt file.
+#3014596 by DamienMcKenna: Require PHP 5.4.
+#2943719 by DamienMcKenna: Reduce memory usage during MySQL database generation.
+#2338841 by catch, dasha_v, DamienMcKenna: Incorrect translated string +
+  menu_rebuild() performance issue.
+#1104012 by DamienMcKenna, axel.rutz, EmanueleQuinto, gisle, jacob.embree, Pere
+  Orga, frenkx: On restore, provide option to drop all tables.
+#2891272 by DamienMcKenna, webservant316: phpsnif 7.1 says Method name
+  \backup_migrate_destination_nodesquirrel::__xmlrpc\" is discouraged; PHP has
+  reserved all method names with a double underscore prefix for future use. 
+#3014597 by DamienMcKenna: Document the advanced backup options.
+#2382541 by sano, DamienMcKenna, couturier, dman, modulist: Updated
+  instructions for installing the S3 library.
+#3016278 by DamienMcKenna: Some debug messages aren't hidden behind
+  backup_migrate_verbose.
+#3016282 by DamienMcKenna: "Cannot add field
+  backup_migrate_profiles.machine_name: field already exists" error.
+#3017042 by DamienMcKenna, freelylw, gisle: Settings form field dependency
+  broken.
+#3018002 by DamienMcKenna: Improve the module's documentation.
+By DamienMcKenna: "server" not "sever" :-)
+#3018853 by RickJ: Temporary files not deleted.
+#2735337 by RickJ: Settings objects only offer Revert, not Delete.
+
+
+Backup and Migrate 7.x-3.5, 2018-02-05
+--------------------------------------
+#2941981 by DamienMcKenna, Rick J, camhoward: Backup files being overwritten
+  because of a change to the append_timestamp setting.
+#2940451 by Darren Oh: Unlimited max_execution_time treated as 0 seconds.
+
+
+Backup and Migrate 7.x-3.4, 2018-01-24
+--------------------------------------
+By DamienMcKenna, pere-orga, ikit-claw, cashwilliams: Make all permissions
+  restricted.
+#1058820 by kbasarab, sobi3ch, gbirch, avatxus: Add option to overwrite file.
+#2498191 by DamienMcKenna: Unable to backup to NodeSquirrel using PHP 5.6.9
+  (on Windows) due to changes in SSL validation rules.
+#2742855 by hanoii: Caching profile objects led to unwanted behaviors because of
+  stale data.
+#2703351 by DamienMcKenna, ultrabob: Redundant methods in
+  backup_migrate_location_remote.
+#2728345 by Rick J: Nodesquirrel destination class doesn't return its name.
+#2728331 by Rick J: Incorrect paging count in saved backup list.
+#2498179 by junaidpv: Use drupal_mail() for system notifications.
+#2920311 by DamienMcKenna: Check backup directory permissions / web
+  accessibility.
+#2912459 by DamienMcKenna: Add an initial test.
+#2880434 by DamienMcKenna, benqwerty: Minor changes to drush help output.
+#2914644 by DamienMcKenna, jacob.embree: List all files which contain classes in
+  the info file so other modules can use the classes.
+#2663928 by DamienMcKenna, fietserwin: public://languages should also be
+  excluded by default.
+#2388347 by DamienMcKenna: Nodequirrel destination class doesn't define $errno,
+  $errstr but assumes they're available.
+#2899586 by juankvillegas, DamienMcKenna: Ensure the page variable is numeric in
+  backup_migrate_nodesquirrel_status_form().
+
+
+Backup and Migrate 7.x-3.3, 2017-10-18
+--------------------------------------
+#2290707 by DamienMcKenna, mitsuroseba, Darren Oh: Exclude the data from the
+  following tables - does not work.
+#2830554 by axel.rutz: Support Ultimate Cron.
+#2728349 by Rick J: Disabled schedules still run under Elysia cron.
+
+
+Backup and Migrate 7.x-3.2, 2017-09-27
+--------------------------------------
+#2702229 by DamienMcKenna: Automatically add all cache tables to the 'nodata'
+  list, exclude simpletest tables.
+#2618516 by rocketeerbkw: Fatal error when restoring "Entire Site".
+#2839264 by wizonesolutions: Error: [] operator not supported for strings in
+  backup_migrate_crud_ui_list_all().
+#2633304 by adamelleston: 'clone' is a reserved keyword introduced in PHP
+  version 5.0 and cannot be invoked as a function.
+#2547065 by snehi: Description for filesource location is inaccurate.
+#2672478 by serverofworld, lklimek: Wrong destination when running multiple
+  schedules using the same profile.
+#1542274 by dale42, LGLC, AjitS: Restore fails with foreign key constraint error
+  (due to incorrect comment detection).
+#2623598 by SylvainM, joelstein: Deprecated: Methods with the same name as their
+  class will not be constructors in a future version of PHP.
+#2513720 by rrfegade: Code Spell errors.
+By ronan: Cleaned up use of manage url generator.
+#2495681 by DamienMcKenna: Secure all endpoint connections to NodeSquirrel
+  destination service.
+#2495677 by DamienMcKenna: Inconsistent use of https for manage.nodesquirrel.com
+  hostname.
+#2277383 by hswong3i: Fix schema mismatch after upgrade 7.x-3.x.
+
+
+Backup and Migrate 7.x-3.1, 2015-05-11
+--------------------------------------
+By ronan: Added pantheon notice and free notice to NodeSquirrel page.
+#2347637: Add a Note to Backup doesn't work with Jquery > 1.7.
+By ronan: Set Nodesquirrel schedule to use smart delete by default.
+#2477503: Error returned, but exit status is 0.
+#2369461: Scheduled Backups stopped working after 7.x-3.0?.
+#2390057: Site doesn't come out of maintenance mode when backing up entire site.
+#2455265: Update Version Number in README.TXT.
+By ronan: Updated NS messaging slightly.
+#2393615: Compression not running (only when backing up entire site).
+#2324949: Update #7303 Failing.
+#2421183: Stream URI - Not ignored (Recognized?) during backups.
+#2378739: "Exclude the data from the following tables" not respected when "Use
+  cli commands" is checked.
+#2307655 Fixed adding of full system path to site archive.
+#2290707 by marvoey Advanced Backup link on quick backup page goes to wrong
+  place.
+#2290707 Exclude the data from the following tables - does not work.
+By ronan: Moved run time check before scheduled backup attempt to fix too-
+  frequent schedule issue.
+#2280743: Files restore duplicating directory structure
+  (sites/default/files/sites/default/files).
+#2276663 Permissions - Access backup files.
+#2283701 by alarcombe. Restore not possible from archive.
+#2286835 by Fernando Vesga. Advanced backup page link error.
+#2287157 by topsitemakers. Links to "Create new destination" and "Create new
+  schedule" are invalid.
+#2287239 by topsitemakers. Minor typo in an error message.
+#2278865 by topsitemakers Undefined index: files in
+  theme_backup_migrate_file_list.
+
+
+Backup and Migrate 7.x-3.0, 2014-05-27
+--------------------------------------
+By ronan: Fixed comment on install hook.
+#2266381 Warning: Invalid argument supplied for foreach() in
+  backup_migrate_schedules_cron() on line 48.
+#1001654 Fixed ctools exportables.
+By ronan: Added nodesquirrel scheduling back. Fixed issue with restore from
+  previous radios. Added function to retrieve the most recent backup from the
+  given destination. This may be used in a drush command.
+By ronan: Removed link to help text when help module is off.
+By ronan: Removed confusing 'Quick Schedule' tab.
+#2225335 Drush commands not working.
+By ronan: Increased machine_name length in ui for compatibility with migrated
+  items.
+By ronan: Fixed a couple of php errors.
+By ronan: Minor wording tweaks.
+By ronan: Added better status for NodeSquirrel.
+By ronan: Removed dpm.
+By ronan: Added dependency to offline message on restore.
+By ronan: Removed stray console.log from the js.
+By ronan: Added most recent saved backups to the restore tab.
+By ronan: Ported MySQL views support.
+By ronan: Fixed some issues saving profiles.
+By ronan: Added a @TODO to remind me to fix saving on the advance backup page.
+#1974740 allow backup of codebase & files to skip unreadable files.
+By ronan: Merge branch 'refs/heads/NodeSquirrel-Language' into 7.x-3.x.
+By ronan: Fixed up some language and links to NodeSquirrel.
+By ronan: Cleaned up directory handling to support more directory structures.
+By ronan: Added some description language for NodeSquirrel.
+#2166813 Files backup fails when PEAR is not installed.
+#2155381 by JulienD Remove useless files[] directive from .info files.
+#1947206 by Les Lim. "No data" tables are exported even when they're in the
+  "Exclude the following tables altogether" list.
+#2031393 A debugging function call left behind.
+#2031777 Wrong function signature on form callback.
+#2065573 wrong default file token.
+#2039951 settings page gone for schedules.
+#2009392 Settings don't save for file or entire site sources.
+#1998788 missing i in NodeSquirrel.
+By ronan: Moved NodeSquirrel tab to match the 2.x branch.
+By ronan: Some d7 style cleanup.
+By ronan: Fixed nodesquirrel destination with new id structure.
+By ronan: Added checkplain to description on listing.
+By ronan: Fixed an issue with multiselect js invading other pages.
+#1564408 Gzip backups are compressed twice when downloaded.
+#1991686 thx morningtime. Undefined property: backup_migrate_schedule::$last_run
+By ronan: Switch to hourly default to align with smart delete default.
+#839254 Switched to using drupal_reapath to allow for stream wrapper schemas.
+#839254 Checking absolute paths within the docroot.
+#839254 check for access restriction ignores custom destination path.
+#1503202 Suhosin refuses sending mail because of to many new lines in mail
+  header (possible attacker).
+#1529174 Fatal Error when database name is longer than 21 characters.
+By ronan: Fixed 'Restore again' link.
+#1974720 error message on attempting to restore manual backup.
+#1974744 warning on creation of new settings profile.
+By ronan: Fixed inconsistent use of 'source' and 'destination'.
+By ronan: Changed destination_id to machine_name to fix issue with default file
+  destinations.
+By ronan: Fixed error noise caused by ftp not connecting.
+By ronan: Fixed error on file delete form.
+By ronan: Fixed fatal error in metadata util when a backup fails.
+#1968210 error message on attempting backup of entire site.
+By ronan: Improved error reporting when a non-existant file (such as a broken
+  symlink) cannot be backed up.
+#1968196 Error message on install in php 5.4.
+By ronan: Fixed checkboxes.
+By ronan: Fixed fatal error with token replace.
+By ronan: Created full upgrade path from 2.x. Fixed source settings.
+By ronan: Fixed db scheme issue. Fixed file source realpath issue.
+By ronan: Switched type to subtype for ctools compatibility.
+By ronan: Added machine_name js and ctools exportables.
+By ronan: Fixed source specific settings in backup.
+By ronan: Fixed issue with edit screen for source settings in profiles.
+By ronan: Fixed export.
+By ronan: Cleaned up more fatal errors. Better handling of translatable titles.

+ 0 - 0
sites/all/modules/contrib/admin/backup_migrate/LICENSE.txt


+ 161 - 58
sites/all/modules/contrib/admin/backup_migrate/README.txt

@@ -1,94 +1,197 @@
+Backup and Migrate
+------------------
+This module makes the task of backing up a site's Drupal database, code and
+all uploaded files and of migrating data from one Drupal install to another
+easier.
 
--------------------------------------------------------------------------------
-Backup and Migrate 2 for Drupal 7.x
-  by Ronan Dowling, Gorton Studios - ronan (at) gortonstudios (dot) com
--------------------------------------------------------------------------------
-
-DESCRIPTION:
-This module makes the task of backing up your Drupal database and migrating data
-from one Drupal install to another easier. It provides a function to backup the
-entire database to file or download, and to restore from a previous backup. You
-can also schedule the backup operation. Compression of backup files is also
-supported.
+Database backup files are a list of SQL statements which can be executed with a
+tool such as phpMyAdmin or the command-line mysql client. File and code backup
+files are tarballs which can be restored by extracting them to the desired
+directory.
 
-There are options to exclude the data from certain tables (such as cache or
-search index tables) to increase efficiency by ignoring data that does not need
-to be backed up or migrated.
-
-The backup files are a list of SQL statements which can be executed with a tool
-such as phpMyAdmin or the command-line mysql client.
 
+Installation
 -------------------------------------------------------------------------------
-
-INSTALLATION:
-* Put the module in your Drupal modules directory and enable it in 
-  admin/modules. 
-* Go to admin/people/permissions and grant permission to any roles that need to be 
-  able to backup or restore the database.
-* Configure and use the module at admin/config/system/backup_migrate
+* Put the module in the Drupal modules directory and enable it via
+  admin/modules.
+* Go to admin/people/permissions and grant permission to any roles that need to
+  be able to backup or restore the database.
+* Configure and use the module at admin/config/system/backup_migrate.
 
 OPTIONAL:
+* To drop all tables before import, expand "Advanced options" panel under the
+  "Restore" and "Saved backups" tabs and tick the option.
 * Enable token.module to allow token replacement in backup file names.
 * To Backup to Amazon S3:
-    - Download the S3 library from http://undesigned.org.za/2007/10/22/amazon-s3-php-class
-      and place the file 'S3.php' in the includes directory in this module.
-      The stable version (0.4.0 – 20th Jul 2009) works best with Backup and Migrate.
+  - Download the most recent version from:
+    https://github.com/tpyo/amazon-s3-php-class
+  - Or clone it with command:
+    git clone https://github.com/tpyo/amazon-s3-php-class.git s3-php5-curl
+  - Rename the unzipped folder to s3-php5-curl
 
-LIGHTTPD USERS:
-Add the following code to your lighttp.conf to secure your backup directories:
+The most recent version of the library known to work is 0.5.1.
+
+
+Additional requirements for LigHTTPd
+-------------------------------------------------------------------------------
+Add the following code to the lighttp.conf to secure the backup directories:
   $HTTP["url"] =~ "^/sites/default/files/backup_migrate/" {
-       url.access-deny = ( "" )
+    url.access-deny = ( "" )
   }
-You may need to adjust the path to reflect the actual path to the files.
+It may be necessary to adjust the path to reflect the actual path to the files.
 
-IIS 7 USERS:
-Add the following code to your web.config code to secure your backup directories:
+
+Additional requirements for IIS 7
+-------------------------------------------------------------------------------
+Add the following code to the web.config code to secure the backup
+directories:
 <rule name="postinst-redirect" stopProcessing="true">
    <match url="sites/default/files/backup_migrate" />
    <action type="Rewrite" url=""/>
 </rule>
-You may need to adjust the path to reflect the actual path to the files.
+It may also be necessary to adjust the path to reflect the actual path to the
+files.
 
--------------------------------------------------------------------------------
 
-VERY IMPORTANT SECURITY NOTE:
-Backup files may contain sensitive data and by default, are saved to your web
+VERY IMPORTANT SECURITY NOTE
+-------------------------------------------------------------------------------
+Backup files may contain sensitive data and, by default, are saved to the web
 server in a directory normally accessible by the public. This could lead to a
 very serious security vulnerability. Backup and Migrate attempts to protect
 backup files using a .htaccess file, but this is not guaranteed to work on all
-environments (and is guaranteed to fail on web servers that are not apache). You
-should test to see if your backup files are publicly accessible, and if in doubt
-do not save backups to the server, or use the destinations feature to save to a 
-folder outside of your webroot.
+environments (and is guaranteed to fail on web servers that are not Apache). It
+is imperitive to test to see if the site's backup files are publicly
+accessible, and if in doubt do not save backups to the server, or use the
+destinations feature to save to a folder outside of the site's webroot.
+
 
-OTHER WARNINGS:
-A failed restore can destroy your database and therefore your entire Drupal
+Other warnings
+-------------------------------------------------------------------------------
+A failed restore can destroy the database and therefore the entire Drupal
 installation. ALWAYS TEST BACKUP FILES ON A TEST ENVIRONMENT FIRST. If in doubt
 do not use this module.
 
-This module has only been tested with MySQL and does not work with any other dbms. 
-If you have experiences with Postgres or any other dbms and are willing to help 
-test and modify the module to work with it, please contact the developer at 
-ronan (at) gortonstudios (dot) com.
+This module has only been tested with MySQL and does not work with any other
+dbms. Assistance in adding support for other database systems would be
+appreciated, see the issue queue for further details.
 
-Make sure your php timeout is set high enough to complete a backup or restore
+Make sure the PHP timeout is set high enough to complete a backup or restore
 operation. Larger databases require more time. Also, while the module attempts
 to keep memory needs to a minimum, a backup or restore will require
-significantly more memory then most Drupal operations.
+significantly more memory than most Drupal operations.
 
-If your backup file contains the 'sessions' table all other users will be logged
-out after you run a restore. To avoid this, exclude the sessions table when 
-creating your backups. Be aware though that you will need to recreate the 
-sessions table if you use this backup on an empty database.
+If the backup file contains the 'sessions' table all other users will be logged
+out after running a restore. To avoid this, exclude the sessions table when
+creating the backups. Be aware though that this table is still required to run
+Drupal, so it will need to be recreated if the backup is restored onto an empty
+database.
 
 Do not change the file extension of backup files or the restore function will be
-unable to determine the compression type the file and will not function
+unable to determine the compression type of the file and will not function
 correctly.
 
-IF A RESTORE FAILS:
-Don't panic, the restore file should work with phpMyAdmin's import function, or
-with the mysql command line tool. If it does not, then it is likely corrupt; you
-may panic now. MAKE SURE THAT THIS MODULE IS NOT YOUR ONLY FORM OF BACKUP.
+The module's permissions should only be given to trusted users due to the
+inherent security vulnerabilities in allowing people access to a site's database
+and/or files backups.
+
 
+If a restore fails
 -------------------------------------------------------------------------------
+Don't panic!
 
+The restore file should still work with phpMyAdmin's import function or with
+the mysql command line tool.
+
+If the backup does not restore using either a graphical tool or the command line tools, then it is likely corrupt; you may panic now.
+
+MAKE SURE THAT THIS MODULE IS NOT THE ONLY FORM OF BACKUP.
+
+
+Known problems and workarounds
+-------------------------------------------------------------------------------
+* If backups fail due to an out-of-memory, try adjusting the memory limit using
+  the "backup_migrate_memory_limit" variable by adding one of these lines
+  to the site's settings.php file:
+
+  // Backup & Migrate: Use 512MB when generating backups.
+  $conf['backup_migrate_memory_limit'] = '512M';
+
+  // Backup & Migrate: Use 1GB when generating backups.
+  $conf['backup_migrate_memory_limit'] = '1G';
+
+* If backups fail due to a PHP timeout error, especially an error saying "MySQL
+  server has gone away", use the "backup_migrate_backup_max_time" variable to
+  adjust the timeout. Before doing this, check to see what PHP's
+  "max_execution_time" is set to, then set the "backup_migrate_backup_max_time"
+  variale to a higher number, e.g. if max_execution_time is 180 (seconds) try
+  setting backup_migrate_backup_max_time to 240 seconds / 4 minutes, or 300
+  seconds / 5 minutes
+
+  // Backup & Migrate: Adjust the PHP timeout to 5 minutes / 300 seconds.
+  $conf['backup_migrate_backup_max_time'] = 300;
+
+* A variable has been added which may help with problems. Setting the variable
+  'backup_migrate_verbose' to TRUE will make the module log additional messages
+  to watchdog as the module performs certain actions.
+
+  // Backup & Migrate: Log extra messages as the module is working.
+  $conf['backup_migrate_verbose'] = TRUE;
+
+* It can be frustrating working from a production database backup on non-prod
+  servers as schduled backups will automatically run via cron the same as they
+  run on production. The custom cron tasks may be disabled using the
+  "backup_migrate_disable_cron" variable. Note: this doesn't prevent people
+  from manually running backups via the UI or from the Drush commands, so it is
+  safe to hardcode to TRUE on all site instances and then hardcode to FALSE on
+  production environments.
+
+  // Backup & Migrate: Don't run backups via cron.
+  $conf['backup_migrate_disable_cron'] = TRUE;
+
+* There are three different variables that control how MySQL data is processed.
+  Should a site have problems with memory limits, it is worth testing these to
+  see which ones work the best.
+
+  - backup_migrate_data_rows_per_query
+    Controls how many records are loaded from the database at once. Defaults to
+    "1000", i.e. 1,000 rows. Note that setting this to a high number can cause
+    problems when exporting large data sets, e.g. cache tables can have huge
+    volumes of data per record.
+
+    // Backup & Migrate: Load 10,000 rows at once.
+    $conf['backup_migrate_data_rows_per_query'] = 10000;
+
+  - backup_migrate_data_rows_per_line
+    Controls how many records are included in a single INSERT statement.
+    Defaults to "30", i.e. 30 records.
+
+    // Backup & Migrate: Combine no more than five records in a single row.
+    $conf['backup_migrate_data_rows_per_line'] = 5;
+
+  - backup_migrate_data_bytes_per_line
+    Controls how much data will be inserted at once using a single INSERT
+    statement. This works with the "backup_migrate_data_rows_per_line" variable
+    to ensure that each INSERT statement doesn't end up being too large.
+    Defaults to "2000", i.e. 2,000 bytes.
+
+    // Backup & Migrate: Limit the output to 1000 bytes at a time.
+    $conf['backup_migrate_data_bytes_per_line'] = 1000;
+
+
+Credits / contact
+--------------------------------------------------------------------------------
+Currently maintained by Alex Andrascu [1], Damien McKenna [1] and Daniel Pickering [3]. All original development up through 2015 was by Ronan Dowling [4]
+with help by Drew Gorton [5].
+
+The best way to contact the authors is to submit an issue, be it a support
+request, a feature request or a bug report, in the project issue queue:
+  https://www.drupal.org/project/issues/backup_migrate
+
+
+References
+--------------------------------------------------------------------------------
+1: https://www.drupal.org/u/damienmckenna
+2: https://www.drupal.org/u/alex-andrascu
+3: https://www.drupal.org/u/ikit-claw
+4: https://www.drupal.org/u/ronan
+5: https://www.drupal.org/u/dgorton

+ 7 - 4
sites/all/modules/contrib/admin/backup_migrate/backup_migrate.css

@@ -1,5 +1,9 @@
-.schedule-list-disabled
-{
+/**
+ * @file
+ * Custom CSS for the Backup Migrate module.
+ */
+
+.schedule-list-disabled {
   filter:alpha(opacity=50);
   -moz-opacity: .50;
   opacity: .50;
@@ -27,8 +31,7 @@ div.backup-migrate-tables-checkboxes {
 }
 div.backup-migrate-tags,
 div.backup-migrate-description,
-div.backup-migrate-date
-{
+div.backup-migrate-date {
   font-size: smaller;
 }
 span.backup-migrate-label {

+ 36 - 5
sites/all/modules/contrib/admin/backup_migrate/backup_migrate.info

@@ -1,16 +1,47 @@
 name = Backup and Migrate
 description = "Backup the Drupal database and files or migrate them to another environment."
 core = 7.x
+configure = admin/config/system/backup_migrate
+
+; Require PHP 5.4 so that some slightly more advanced PHP logic can be used.
+; This is for future usage; removing this will allow the module to continue
+; working fine for now.
+php = 5.4
 
+; All of the class files used within the module.
+files[] = includes/crud.inc
 files[] = includes/destinations.inc
+files[] = includes/destinations.browser.inc
+files[] = includes/destinations.db.inc
+files[] = includes/destinations.db.mysql.inc
+files[] = includes/destinations.email.inc
+files[] = includes/destinations.file.inc
+files[] = includes/destinations.ftp.inc
+files[] = includes/destinations.nodesquirrel.inc
+files[] = includes/destinations.s3.inc
+files[] = includes/files.inc
+files[] = includes/filters.inc
+files[] = includes/filters.backup_restore.inc
+files[] = includes/filters.compression.inc
+files[] = includes/filters.encryption.inc
+files[] = includes/filters.statusnotify.inc
+files[] = includes/filters.utils.inc
+files[] = includes/locations.inc
 files[] = includes/profiles.inc
 files[] = includes/schedules.inc
+files[] = includes/sources.inc
+files[] = includes/sources.archivesource.inc
+files[] = includes/sources.db.inc
+files[] = includes/sources.db.mysql.inc
+files[] = includes/sources.filesource.inc
 
-configure = admin/config/system/backup_migrate
+; Test suite.
+files[] = tests/BmTestBase.test
+files[] = tests/BmTestBasics.test
+files[] = tests/BmTestProfiles.test
 
-; Information added by Drupal.org packaging script on 2014-05-27
-version = "7.x-3.0"
+; Information added by Drupal.org packaging script on 2018-12-16
+version = "7.x-3.6"
 core = "7.x"
 project = "backup_migrate"
-datestamp = "1401209057"
-
+datestamp = "1544925486"

+ 361 - 93
sites/all/modules/contrib/admin/backup_migrate/backup_migrate.install

@@ -1,22 +1,138 @@
 <?php
 
-
 /**
  * @file
  * Install hooks for Backup and Migrate.
  */
 
-
 /**
- * Implementation of hook_requirements().
+ * Implements hook_requirements().
  */
 function backup_migrate_requirements($phase) {
   $requirements = array();
+
+  // Ensure translations don't break during installation.
+  $t = get_t();
+
+  if ($phase == 'runtime') {
+    // Get a list of all destinations, make sure none of them are publicly
+    // accessible.
+    // @todo Expand the API to add methods to specifically check this.
+    backup_migrate_include('destinations');
+    foreach (backup_migrate_get_destinations() as $dest_name => $destination) {
+      if (method_exists($destination, 'get_display_location')) {
+        $dest_path = $destination->get_display_location();
+        if (!empty($dest_path) && file_valid_uri($dest_path)) {
+          $scheme = file_uri_scheme($dest_path);
+          // Support public and private storage and raw server paths.
+          if ($scheme === 'private' || $scheme === 'public' || substr($dest_path, 0, 1) == '/') {
+            // Check if the path exists.
+            $path_exists = file_prepare_directory($dest_path, FILE_CREATE_DIRECTORY);
+            if ($path_exists) {
+              $real_path = drupal_realpath($dest_path);
+              // See if the private path is somewhere inside the main Drupal
+              // directory structure.
+              if (strpos($real_path, DRUPAL_ROOT) === 0) {
+                // Extract the relative path from the Drupal root path, and
+                // then add the base URL, theoretically creating a fully
+                // qualified URL to the storage directory.
+                $url = substr($real_path, strlen(DRUPAL_ROOT) + 1);
+                $url = url($url, array('absolute' => TRUE));
+                $result = drupal_http_request($url);
+
+                // If the HTTP request comes back as a status 200 that means
+                // there is a directory listing of some sort; directory paths
+                // should return a 503 error.
+                if (!empty($result->code) && $result->code == 200) {
+                  // Get the human readable information for this destination.
+                  $dest_spec = $destination->get_list_row();
+
+                  // Display a warning message.
+                  $requirements['bmdest_' . $dest_name] = array(
+                    'severity' => REQUIREMENT_ERROR,
+                    'title' => 'Backup Migrate',
+                    'value' => $t('Backup destination "%dest" is publicly accessible!', array('%dest' => $dest_spec['name'])),
+                    'description' => $t('The backup destination, "%dest", stores its files in the "%path" directory. This directory is publicly available from the web server and urgently needs to be secured! Please see the Drupal manual on <a href="@manual">configuring the private directory path</a> on how to fix this problem.',
+                      array(
+                        '%dest' => $dest_spec['name'],
+                        '%path'  => $real_path,
+                        '@manual' => 'https://www.drupal.org/docs/7/core/modules/file/overview',
+                      )),
+                  );
+                }
+
+                // Check an individual file.
+                else {
+                  $files = scandir($real_path);
+                  if (!empty($files)) {
+                    foreach ($files as $file) {
+                      // Skip the base field pointers.
+                      if ($file == '.' || $file == '..') {
+                        continue;
+                      }
+
+                      $result = drupal_http_request($url . '/' . $file);
+
+                      // If the HTTP request comes back as a status 200 that
+                      // means the file is accessible.
+                      if (!empty($result->code) && $result->code == 200) {
+                        // Get the human readable information for this
+                        // destination.
+                        $dest_spec = $destination->get_list_row();
+
+                        // Display a warning message.
+                        $requirements['bmdest_' . $dest_name] = array(
+                          'severity' => REQUIREMENT_ERROR,
+                          'title' => 'Backup Migrate',
+                          'value' => $t('Files in "%dest" are publicly accessible!', array('%dest' => $dest_spec['name'])),
+                          'description' => $t('The backup destination, "%dest", stores its files in the "%path" directory. These file(s) are publicly available from the web server and urgently need to be secured! Please see the Drupal manual on <a href="@manual">configuring the private directory path</a> on how to fix this problem.',
+                            array(
+                              '%dest' => $dest_spec['name'],
+                              '%path'  => $real_path,
+                              '@manual' => 'https://www.drupal.org/docs/7/core/modules/file/overview',
+                            )),
+                        );
+                      }
+
+                      // Only need to check one file.
+                      break;
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+    // Leave a note if there were no problems.
+    // @todo No point in displaying this until the API has been expanded.
+    // @code
+    // if (empty($requirements)) {
+    //   $requirements['bmdest_' . $dest_name] = array(
+    //     'severity' => REQUIREMENT_INFO,
+    //     'title' => 'Backup Migrate',
+    //     'value' => $t('Backup destinations are safe'),
+    //     'description' => $t('The backup destinations were all checked and none of them were exposing files to the public. This is a good thing.'),
+    //   );
+    // }
+    // @endcode
+
+    if (variable_get('backup_migrate_disable_cron', FALSE)) {
+      $requirements['bm_disable_cron'] = array(
+        'severity' => REQUIREMENT_INFO,
+        'title' => 'Backup Migrate',
+        'value' => $t('Cron tasks are disabled'),
+        'description' => $t('The cron tasks have been disabled, so scheduled backups will not run. See the Backup & Migrate module\'s README.txt file for further details.'),
+      );
+    }
+  }
+
   return $requirements;
 }
 
 /**
- * Implementation of hook_schema().
+ * Implements hook_schema().
  */
 function backup_migrate_schema() {
   $schema['backup_migrate_profiles'] = array(
@@ -25,11 +141,14 @@ function backup_migrate_schema() {
       'key name' => 'Profile ID',
       'admin_title' => 'name',
       'primary key' => 'profile_id',
-      'identifier' => 'item', // Exports will be defined as $preset
-      'default hook' => 'exportables_backup_migrate_profiles',  // Function hook name.
+      // Exports will be defined as $preset.
+      'identifier' => 'item',
+      // Function hook name.
+      'default hook' => 'exportables_backup_migrate_profiles',
       'api' => array(
         'owner' => 'backup_migrate',
-        'api' => 'backup_migrate_exportables',  // Base name for api include files.
+        // Base name for api include files.
+        'api' => 'backup_migrate_exportables',
         'minimum_version' => 1,
         'current_version' => 1,
       ),
@@ -40,7 +159,8 @@ function backup_migrate_schema() {
         'unsigned' => TRUE,
         'not null' => TRUE,
         'description' => 'Primary ID field for the table. Not used for anything except internal lookups.',
-        'no export' => TRUE, // Do not export database-only keys.
+        // Do not export database-only keys.
+        'no export' => TRUE,
       ),
       'machine_name' => array(
         'type' => 'varchar',
@@ -53,13 +173,13 @@ function backup_migrate_schema() {
         'description' => 'The name of the profile.',
         'type' => 'varchar',
         'length' => 255,
-        'not null' => TRUE
+        'not null' => TRUE,
       ),
       'filename' => array(
         'description' => 'The name of the profile.',
         'type' => 'varchar',
         'length' => 255,
-        'not null' => TRUE
+        'not null' => TRUE,
       ),
       'append_timestamp' => array(
         'description' => 'Append a timestamp to the filename.',
@@ -67,13 +187,13 @@ function backup_migrate_schema() {
         'size' => 'tiny',
         'unsigned' => TRUE,
         'not null' => TRUE,
-        'default' => 0
+        'default' => 0,
       ),
       'timestamp_format' => array(
         'description' => 'The format of the timestamp.',
         'type' => 'varchar',
         'length' => 14,
-        'not null' => TRUE
+        'not null' => TRUE,
       ),
       'filters' => array(
         'description' => 'The filter settings for the profile.',
@@ -91,11 +211,14 @@ function backup_migrate_schema() {
       'key name' => 'Destination ID',
       'admin_title' => 'name',
       'primary key' => 'destination_id',
-      'identifier' => 'item', // Exports will be defined as $preset
-      'default hook' => 'exportables_backup_migrate_destinations',  // Function hook name.
+      // Exports will be defined as $preset.
+      'identifier' => 'item',
+      // Function hook name.
+      'default hook' => 'exportables_backup_migrate_destinations',
       'api' => array(
         'owner' => 'backup_migrate',
-        'api' => 'backup_migrate_exportables',  // Base name for api include files.
+        // Base name for api include files.
+        'api' => 'backup_migrate_exportables',
         'minimum_version' => 1,
         'current_version' => 1,
       ),
@@ -106,7 +229,8 @@ function backup_migrate_schema() {
         'unsigned' => TRUE,
         'not null' => TRUE,
         'description' => 'Primary ID field for the table. Not used for anything except internal lookups.',
-        'no export' => TRUE, // Do not export database-only keys.
+        // Do not export database-only keys.
+        'no export' => TRUE,
       ),
       'machine_name' => array(
         'type' => 'varchar',
@@ -119,18 +243,18 @@ function backup_migrate_schema() {
         'description' => 'The name of the destination.',
         'type' => 'varchar',
         'length' => 255,
-        'not null' => TRUE
+        'not null' => TRUE,
       ),
       'subtype' => array(
         'description' => 'The type of the destination.',
         'type' => 'varchar',
         'length' => 32,
-        'not null' => TRUE
+        'not null' => TRUE,
       ),
       'location' => array(
         'description' => 'The the location string of the destination.',
         'type' => 'text',
-        'not null' => TRUE
+        'not null' => TRUE,
       ),
       'settings' => array(
         'description' => 'Other settings for the destination.',
@@ -138,7 +262,7 @@ function backup_migrate_schema() {
         'not null' => TRUE,
         'serialize' => TRUE,
         'serialized default' => 'a:0:{}',
-     ),
+      ),
     ),
     'primary key' => array('destination_id'),
   );
@@ -148,11 +272,14 @@ function backup_migrate_schema() {
       'key name' => 'Source ID',
       'admin_title' => 'name',
       'primary key' => 'source_id',
-      'identifier' => 'item', // Exports will be defined as $preset
-      'default hook' => 'exportables_backup_migrate_sources',  // Function hook name.
+      // Exports will be defined as $preset.
+      'identifier' => 'item',
+      // Function hook name.
+      'default hook' => 'exportables_backup_migrate_sources',
       'api' => array(
         'owner' => 'backup_migrate',
-        'api' => 'backup_migrate_exportables',  // Base name for api include files.
+        // Base name for api include files.
+        'api' => 'backup_migrate_exportables',
         'minimum_version' => 1,
         'current_version' => 1,
       ),
@@ -163,11 +290,12 @@ function backup_migrate_schema() {
         'unsigned' => TRUE,
         'not null' => TRUE,
         'description' => 'Primary ID field for the table. Not used for anything except internal lookups.',
-        'no export' => TRUE, // Do not export database-only keys.
+        // Do not export database-only keys.
+        'no export' => TRUE,
       ),
       'machine_name' => array(
         'type' => 'varchar',
-        'length' => 32,
+        'length' => 255,
         'not null' => TRUE,
         'default' => '0',
         'description' => 'The primary identifier for a source.',
@@ -176,18 +304,18 @@ function backup_migrate_schema() {
         'description' => 'The name of the source.',
         'type' => 'varchar',
         'length' => 255,
-        'not null' => TRUE
+        'not null' => TRUE,
       ),
       'subtype' => array(
         'description' => 'The type of the source.',
         'type' => 'varchar',
         'length' => 32,
-        'not null' => TRUE
+        'not null' => TRUE,
       ),
       'location' => array(
         'description' => 'The the location string of the source.',
         'type' => 'text',
-        'not null' => TRUE
+        'not null' => TRUE,
       ),
       'settings' => array(
         'description' => 'Other settings for the source.',
@@ -195,7 +323,7 @@ function backup_migrate_schema() {
         'not null' => TRUE,
         'serialize' => TRUE,
         'serialized default' => 'a:0:{}',
-     ),
+      ),
     ),
     'primary key' => array('source_id'),
   );
@@ -206,11 +334,14 @@ function backup_migrate_schema() {
       'key name' => 'Source ID',
       'admin_title' => 'name',
       'primary key' => 'schedule_id',
-      'identifier' => 'item', // Exports will be defined as $preset
-      'default hook' => 'exportables_backup_migrate_schedules',  // Function hook name.
+      // Exports will be defined as $preset.
+      'identifier' => 'item',
+      // Function hook name.
+      'default hook' => 'exportables_backup_migrate_schedules',
       'api' => array(
         'owner' => 'backup_migrate',
-        'api' => 'backup_migrate_exportables',  // Base name for api include files.
+        // Base name for api include files.
+        'api' => 'backup_migrate_exportables',
         'minimum_version' => 1,
         'current_version' => 1,
       ),
@@ -221,7 +352,8 @@ function backup_migrate_schema() {
         'unsigned' => TRUE,
         'not null' => TRUE,
         'description' => 'Primary ID field for the table. Not used for anything except internal lookups.',
-        'no export' => TRUE, // Do not export database-only keys.
+        // Do not export database-only keys.
+        'no export' => TRUE,
       ),
       'machine_name' => array(
         'type' => 'varchar',
@@ -234,14 +366,14 @@ function backup_migrate_schema() {
         'description' => 'The name of the profile.',
         'type' => 'varchar',
         'length' => 255,
-        'not null' => TRUE
+        'not null' => TRUE,
       ),
       'source_id' => array(
         'description' => 'The {backup_migrate_destination}.destination_id of the source to backup from.',
         'type' => 'varchar',
         'length' => 255,
         'default' => 'db',
-        'not null' => TRUE
+        'not null' => TRUE,
       ),
       'destination_id' => array(
         'type' => 'varchar',
@@ -282,7 +414,7 @@ function backup_migrate_schema() {
         'size' => 'tiny',
         'unsigned' => TRUE,
         'not null' => TRUE,
-        'default' => 0
+        'default' => 0,
       ),
       'cron' => array(
         'description' => 'Whether the schedule should be run during cron.',
@@ -306,18 +438,27 @@ function backup_migrate_schema() {
 }
 
 /**
- * Implementation of hook_install().
- */
-function backup_migrate_install() {
-}
-
-/**
- * Remove variables on uninstall.
+ * Implements hook_uninstall().
  */
 function backup_migrate_uninstall() {
-  db_query("DELETE FROM {variable} WHERE name LIKE 'backup_migrate_%'");
-  db_query("DELETE FROM {variable} WHERE name LIKE 'nodesquirrel_%'");
-  cache_clear_all('variables', 'cache');
+  variable_del('backup_migrate_backup_max_time');
+  variable_del('backup_migrate_copy_destination_id');
+  variable_del('backup_migrate_data_bytes_per_line');
+  variable_del('backup_migrate_data_rows_per_line');
+  variable_del('backup_migrate_data_rows_per_query');
+  variable_del('backup_migrate_destination_id');
+  variable_del('backup_migrate_disable_cron');
+  variable_del('backup_migrate_memory_limit');
+  variable_del('backup_migrate_profile_id');
+  variable_del('backup_migrate_schedule_last_run_');
+  variable_del('backup_migrate_source_id');
+  variable_del('backup_migrate_timeout_buffer');
+  variable_del('backup_migrate_verbose');
+  variable_del('nodesquirrel_endpoint_urls');
+  variable_del('nodesquirrel_schedule');
+  variable_del('nodesquirrel_schedule_enabled');
+  variable_del('nodesquirrel_schedule_source_id');
+  variable_del('nodesquirrel_secret_key');
 }
 
 /**
@@ -329,7 +470,7 @@ function backup_migrate_update_2000() {
 }
 
 /**
- * Adding filter field for dev release of 2009-01-28
+ * Adding filter field for dev release of 2009-01-28.
  */
 function backup_migrate_update_2001() {
   $ret = array();
@@ -337,11 +478,21 @@ function backup_migrate_update_2001() {
 
   // Add the filters field to the db.
   if (!db_field_exists('backup_migrate_profiles', 'filters')) {
-    db_add_field('backup_migrate_profiles', 'filters', array('description' => t('The filter settings for the profile.'),'type' => 'text', 'not null' => TRUE));
+    db_add_field('backup_migrate_profiles', 'filters', array(
+      'description' => t('The filter settings for the profile.'),
+      'type' => 'text',
+      'not null' => TRUE,
+    ));
   }
-  // Add the source field
+  // Add the source field.
   if (!db_field_exists('backup_migrate_profiles', 'source_id')) {
-    db_add_field('backup_migrate_profiles', 'source_id', array('description' => t('The {backup_migrate_destination}.destination_id of the source to backup from.'), 'type' => 'varchar', 'length' => 255, 'default' => 'db', 'not null' => TRUE));
+    db_add_field('backup_migrate_profiles', 'source_id', array(
+      'description' => t('The {backup_migrate_destination}.destination_id of the source to backup from.'),
+      'type' => 'varchar',
+      'length' => 255,
+      'default' => 'db',
+      'not null' => TRUE,
+    ));
   }
   // Remove the compression field.
   if (db_field_exists('backup_migrate_profiles', 'compression')) {
@@ -351,7 +502,7 @@ function backup_migrate_update_2001() {
 }
 
 /**
- * Clearing the cache because there was a menu structure change in the dev of 2009-05-31
+ * Clear the cache because there was a menu structure change.
  */
 function backup_migrate_update_2002() {
   // Cache should clear automatically. Nothing to do here.
@@ -359,7 +510,7 @@ function backup_migrate_update_2002() {
 }
 
 /**
- * Allowing non-int profile ids in schedules 2009-05-31
+ * Allowing non-int profile ids in schedules 2009-05-31.
  */
 function backup_migrate_update_2003() {
   $ret = array();
@@ -376,7 +527,7 @@ function backup_migrate_update_2003() {
 }
 
 /**
- * Allowing non-int profile ids 2009-07-01
+ * Allowing non-int profile ids 2009-07-01.
  */
 function backup_migrate_update_2004() {
   $ret = array();
@@ -408,16 +559,16 @@ function backup_migrate_update_2004() {
 }
 
 /**
- * Change the default database id to something friendlier 2009-08-08
+ * Change the default database id to something friendlier 2009-08-08.
  */
 function backup_migrate_update_2005() {
-  require_once './'. drupal_get_path('module', 'backup_migrate') .'/includes/crud.inc';
-  require_once './'. drupal_get_path('module', 'backup_migrate') .'/includes/profiles.inc';
+  require_once './' . drupal_get_path('module', 'backup_migrate') . '/includes/crud.inc';
+  require_once './' . drupal_get_path('module', 'backup_migrate') . '/includes/profiles.inc';
 
   $ret = array();
-  // Change the destination ids of the defined database sources mostly to make using them with drush friendlier.
-
-  // Change the db_url:default id to simply 'db'
+  // Change the destination ids of the defined database sources mostly to make
+  // using them with drush friendlier.
+  // Change the db_url:default id to simply 'db'.
   $ret[] = db_query("UPDATE {backup_migrate_profiles} SET source_id = 'db' WHERE source_id = 'db_url:default'");
   $ret[] = db_query("UPDATE {backup_migrate_schedules} SET destination_id = 'db' WHERE destination_id = 'db_url:default'");
 
@@ -425,9 +576,15 @@ function backup_migrate_update_2005() {
   $ret[] = db_query("UPDATE {backup_migrate_profiles} SET source_id = REPLACE(source_id, 'db_url:', 'db:')");
   $ret[] = db_query("UPDATE {backup_migrate_schedules} SET destination_id = REPLACE(destination_id, 'db_url:', 'db:')");
 
-  // Add the source field to the schedule
+  // Add the source field to the schedule.
   if (!db_field_exists('backup_migrate_schedules', 'source_id')) {
-    db_add_field('backup_migrate_schedules', 'source_id', array('description' => t('The db source to backup from.'), 'type' => 'varchar', 'length' => 255, 'default' => 'db', 'not null' => TRUE));
+    db_add_field('backup_migrate_schedules', 'source_id', array(
+      'description' => t('The db source to backup from.'),
+      'type' => 'varchar',
+      'length' => 255,
+      'default' => 'db',
+      'not null' => TRUE,
+    ));
   }
 
   // Copy source data from profiles to schedules.
@@ -436,7 +593,7 @@ function backup_migrate_update_2005() {
     if (!$schedule['source_id']) {
       $schedule['source_id'] = 'db';
     }
-    $ret[] = db_query("UPDATE {backup_migrate_schedules} SET source_id = '". $schedule['source_id'] ."' WHERE schedule_id = '". $schedule['profile_id'] ."'");
+    $ret[] = db_query("UPDATE {backup_migrate_schedules} SET source_id = '" . $schedule['source_id'] . "' WHERE schedule_id = '" . $schedule['profile_id'] . "'");
   }
 
   if (db_field_exists('backup_migrate_profiles', 'source_id')) {
@@ -476,19 +633,21 @@ function backup_migrate_update_7200() {
   }
 }
 
-
 /**
  * Change the filename field to support 255 characters.
  */
 function backup_migrate_update_7202() {
   $ret = array();
-  db_change_field('backup_migrate_profiles', 'filename', 'filename', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE));
+  db_change_field('backup_migrate_profiles', 'filename', 'filename', array(
+    'type' => 'varchar',
+    'length' => 255,
+    'not null' => TRUE,
+  ));
   return $ret;
 }
 
-
 /**
- * Update the schedule last run times to use variables instead of saving with the schedule.
+ * Schedule last run times to use variables instead of saving with the schedule.
  */
 function backup_migrate_update_7203() {
   $result = db_query('SELECT * FROM {backup_migrate_schedules}', array(), array('fetch' => PDO::FETCH_ASSOC));
@@ -503,20 +662,20 @@ function backup_migrate_update_7203() {
 }
 
 /**
- * Uninstall backup migrate files if it's installed
+ * Uninstall backup migrate files if it's installed.
  */
 function backup_migrate_update_7300() {
   if (module_exists('backup_migrate_files')) {
     module_disable(array('backup_migrate_files'));
     $ret[] = array(
-      'success' => true,
+      'success' => TRUE,
       'query' => 'Disabled the Backup and Migrate Files module',
     );
   }
   if (module_exists('nodesquirrel')) {
     module_disable(array('nodesquirrel'));
     $ret[] = array(
-      'success' => true,
+      'success' => TRUE,
       'query' => 'Disabled the NodeSquirrel module',
     );
   }
@@ -535,18 +694,18 @@ function backup_migrate_update_7300() {
         'description' => t('The name of the source.'),
         'type' => 'varchar',
         'length' => 255,
-        'not null' => TRUE
+        'not null' => TRUE,
       ),
       'type' => array(
         'description' => t('The type of the source.'),
         'type' => 'varchar',
         'length' => 32,
-        'not null' => TRUE
+        'not null' => TRUE,
       ),
       'location' => array(
         'description' => t('The the location string of the source.'),
         'type' => 'text',
-        'not null' => TRUE
+        'not null' => TRUE,
       ),
       'settings' => array(
         'description' => t('Other settings for the source.'),
@@ -554,7 +713,7 @@ function backup_migrate_update_7300() {
         'not null' => TRUE,
         'serialize' => TRUE,
         'serialized default' => 'a:0:{}',
-     ),
+      ),
     ),
     'primary key' => array('source_id'),
   );
@@ -570,7 +729,7 @@ function backup_migrate_update_7300() {
     drupal_write_record('backup_migrate_source', $item);
   }
 
-  // Change 'destination' settings to 'source' settings
+  // Change 'destination' settings to 'source' settings.
   $result = db_query('SELECT * FROM {backup_migrate_profiles}', array(), array('fetch' => PDO::FETCH_ASSOC));
   foreach ($result as $item) {
     $item['filters'] = unserialize($item['filters']);
@@ -584,22 +743,33 @@ function backup_migrate_update_7300() {
  * Switch the cron switch to text.
  */
 function backup_migrate_update_7301() {
-  db_change_field('backup_migrate_schedules', 'cron', 'cron', array('type' => 'varchar', 'length' => 32, 'not null' => TRUE));
-  db_add_field('backup_migrate_schedules', 'cron_schedule', array('description' => 'The cron schedule to run on.', 'type' => 'varchar', 'length' => 255, 'default' => '0 4 * * *', 'not null' => TRUE));
+  db_change_field('backup_migrate_schedules', 'cron', 'cron', array(
+    'type' => 'varchar',
+    'length' => 32,
+    'not null' => TRUE,
+    'default' => 'builtin',
+  ));
+  db_add_field('backup_migrate_schedules', 'cron_schedule', array(
+    'description' => 'The cron schedule to run on.',
+    'type' => 'varchar',
+    'length' => 255,
+    'default' => '0 4 * * *',
+    'not null' => TRUE,
+  ));
 }
 
 /**
  * Add a second destination to schedules.
  */
 function backup_migrate_update_7302() {
-  db_add_field('backup_migrate_schedules', 'copy_destination_id', 
+  db_add_field('backup_migrate_schedules', 'copy_destination_id',
     array(
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '0',
-        'description' => 'A second {backup_migrate_destination}.destination_id of the destination to copy the backup to.',
-      )
+      'type' => 'varchar',
+      'length' => 32,
+      'not null' => TRUE,
+      'default' => '0',
+      'description' => 'A second {backup_migrate_destination}.destination_id of the destination to copy the backup to.',
+    )
   );
 }
 
@@ -607,22 +777,120 @@ function backup_migrate_update_7302() {
  * Add a serial id field to all tables to allow them to be ctools exportable.
  */
 function backup_migrate_update_7303() {
-  foreach (array('backup_migrate_sources' => 'source_id', 'backup_migrate_destinations' => 'destination_id', 'backup_migrate_schedules' => 'schedule_id', 'backup_migrate_profiles' => 'profile_id') as $table => $id) {
+  foreach (array(
+    'backup_migrate_sources' => 'source_id',
+    'backup_migrate_destinations' => 'destination_id',
+    'backup_migrate_schedules' => 'schedule_id',
+    'backup_migrate_profiles' => 'profile_id',
+  ) as $table => $id) {
+    // Take the primary key status from the machine name so it can be renamed
+    // A serial field must be defined as a key, so make a temporary index.
+    // See: https://www.drupal.org/node/190027
+    db_add_index($table, 'temp', array($id));
     db_drop_primary_key($table);
-    db_change_field($table, $id, 'machine_name', array('type' => 'varchar', 'length' => 32, 'not null' => TRUE));
-    db_add_field($table, $id, 
+    // Drop our temporary index.
+    db_drop_index($table, 'temp');
+
+    // Switch the name of the id to 'machine_name' to be more ctools standard.
+    db_change_field($table, $id, 'machine_name', array(
+      'type' => 'varchar',
+      'length' => 32,
+      'not null' => TRUE,
+    ));
+
+    // Add a serial ID.
+    db_add_field($table, $id,
       array(
-          'type' => 'serial',
-          'unsigned' => TRUE,
-          'not null' => TRUE,
-          'description' => 'Primary ID field for the table. Not used for anything except internal lookups.',
-          'no export' => TRUE, // Do not export database-only keys.
-        ),
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'Primary ID field for the table. Not used for anything except internal lookups.',
+        // Do not export database-only keys.
+        'no export' => TRUE,
+      ),
         array('primary key' => array($id))
     );
   }
   foreach (array('backup_migrate_sources', 'backup_migrate_destinations') as $table) {
-    db_change_field($table, 'type', 'subtype', array('type' => 'varchar', 'length' => 32, 'not null' => TRUE));
+    db_change_field($table, 'type', 'subtype', array(
+      'type' => 'varchar',
+      'length' => 32,
+      'not null' => TRUE,
+    ));
+  }
+}
+
+/**
+ * Update all schedules to use the built in cron if none is specified.
+ */
+function backup_migrate_update_7304() {
+  db_query("UPDATE {backup_migrate_schedules} SET cron = 'builtin' WHERE cron = '0'");
+}
+
+/**
+ * Fix schema mismatch after upgrade.
+ */
+function backup_migrate_update_7305() {
+  // Fix the 'machine_name' table fields.
+  $field_spec = array(
+    'type' => 'varchar',
+    'length' => 255,
+    'not null' => TRUE,
+    'default' => '0',
+  );
+  foreach (array(
+    'backup_migrate_profiles',
+    'backup_migrate_destinations',
+    'backup_migrate_sources',
+    'backup_migrate_schedules',
+  ) as $table) {
+    if (!db_field_exists($table, $machine_name)) {
+      try {
+        db_add_field($table, 'machine_name', $field_spec);
+      }
+      catch (\Exception $e) {
+        db_change_field($table, 'machine_name', 'machine_name', $field_spec);
+      }
+    }
+    else {
+      db_change_field($table, 'machine_name', 'machine_name', $field_spec);
+    }
   }
+
+  // Fix the 'cron' table field.
+  $field_spec = array(
+    'type' => 'varchar',
+    'length' => 32,
+    'not null' => TRUE,
+    'default' => 'builtin',
+  );
+  if (!db_field_exists('backup_migrate_schedules', 'cron')) {
+    try {
+      db_add_field('backup_migrate_schedules', 'cron', $field_spec);
+    }
+    catch (\Exception $e) {
+      db_change_field('backup_migrate_schedules', 'cron', 'cron', $field_spec);
+    }
+  }
+  else {
+    db_change_field('backup_migrate_schedules', 'cron', 'cron', $field_spec);
+  }
+}
+
+/**
+ * Leave a message to explain the mixup over the backup option.
+ */
+function backup_migrate_update_7306() {
+  drupal_set_message(t('Please note that release 7.x-3.4 had a bug which caused all backups to be overwritten instead of having a timestamp added. Please review all backup settings to ensure they work as intended.'), 'warning');
 }
 
+/**
+ * 'backup_migrate_backup_memory_limit' vs 'backup_migrate_memory_limit'.
+ */
+function backup_migrate_update_7307() {
+  $limit = variable_get('backup_migrate_backup_memory_limit');
+  if (!empty($limit)) {
+    variable_set('backup_migrate_memory_limit', $limit);
+    variable_del('backup_migrate_backup_memory_limit');
+  }
+}

+ 29 - 17
sites/all/modules/contrib/admin/backup_migrate/backup_migrate.js

@@ -1,23 +1,33 @@
+/**
+ * @file
+ * Custom JS for the Backup and Migrate module.
+ */
+
 (function($) {
+  'use strict';
+
   Drupal.behaviors.backupMigrate = {
-    attach: function(context) {
-      if (Drupal.settings.backup_migrate !== undefined) { 
+    attach: function(context, settings) {
+      if (Drupal.settings.backup_migrate !== undefined) {
         if (Drupal.settings.backup_migrate.dependents !== undefined) {
+          var key;
           for (key in Drupal.settings.backup_migrate.dependents) {
             info = Drupal.settings.backup_migrate.dependents[key];
-            dependent = $('#edit-' + info['dependent']);
+            var dependent = $('#edit-' + info['dependent']);
             for (key in info['dependencies']) {
               $('[name="' + key + '"]').each(function() {
                 var dependentval = info['dependencies'][key];
                 var dependency = $(this);
                 (function(dependent, dependency) {
                   var checkval = function(inval) {
-                    if (dependency.attr('type') == 'radio') {
+                    // Do loose comparisons to support things like "true", "1",
+                    // etc.
+                    if (dependency.attr('type') === 'radio') {
                       var val = $('[name="' + dependency.attr('name') + '"]:checked').val();
                       return val == inval;
                     }
-                    else if (dependency.attr('type') == 'checkbox') {
-                      return dependency.attr('checked') && inval == dependency.val();
+                    else if (dependency.attr('type') === 'checkbox') {
+                      return dependency.is(':checked') && inval == dependency.val();
                     }
                     else {
                       return dependency.val() == inval;
@@ -53,12 +63,13 @@
                 var label = $(this).attr('label');
                 copy_selector_options[label] = [];
                 $(this).find('option').each(function() {
-                  copy_selector_options[label].push(this); 
+                  copy_selector_options[label].push(this);
                 });
                 $(this).remove();
               })
 
-              // Assign an action to the main selector to modify the secondary selector
+              // Assign an action to the main selector to modify the secondary
+              // selector.
               selector.each(function() {
                 $(this).bind('load change click keypress focus', function() {
                   var group = $(this).find('option[value=' + $(this).val() + ']').parents('optgroup').attr('label');
@@ -75,27 +86,28 @@
               });
             })(info);
           }
+
           // Add the convert to checkboxes functionality to all multiselects.
           $('#backup-migrate-ui-manual-backup-form select[multiple], #backup-migrate-crud-edit-form select[multiple]').each(function() {
             var self = this;
             $(self).after(
               $('<div class="description backup-migrate-checkbox-link"></div>').append(
-                $('<a>'+ Drupal.settings.backup_migrate.checkboxLinkText +'</a>').click(function() {
+                $('<a>' + Drupal.settings.backup_migrate.checkboxLinkText + '</a>').click(function() {
                   var $select = $(self);
                   var $checkboxes = $('<div></div>').addClass('backup-migrate-tables-checkboxes');
                   $('option', $select).each(function(i) {
                     $checkboxes.append(
                       $('<div class="form-item"></div>').append(
                         $('<label class="option backup-migrate-table-select">' + this.value + '</label>').prepend(
-                          $('<input type="checkbox" class="backup-migrate-tables-checkbox" name="'+ $select.attr('name') +'"'+ (this.selected ? 'checked="checked"' : '') +' value="'+ this.value +'"/>')
+                          $('<input type="checkbox" class="backup-migrate-tables-checkbox" name="' + $select.attr('name') + '"' + (this.selected ? 'checked="checked"' : '') + ' value="' + this.value + '"/>')
                             .bind('click change load', function() {
-                                if (this.checked) {
-                                  $(this).parent().addClass('checked');
-                                }
-                                else {
-                                  $(this).parent().removeClass('checked');
-                                }
-                              }).load()
+                              if (this.checked) {
+                                $(this).parent().addClass('checked');
+                              }
+                              else {
+                                $(this).parent().removeClass('checked');
+                              }
+                            }).load()
                         )
                       )
                     );

File diff suppressed because it is too large
+ 1 - 7
sites/all/modules/contrib/admin/backup_migrate/backup_migrate.module


+ 83 - 23
sites/all/modules/contrib/admin/backup_migrate/includes/backup_migrate.drush.inc

@@ -1,11 +1,10 @@
 <?php
 
-
 /**
  * @file
  * Drush commands for backup and migrate.
  */
- 
+
 /**
  * Implementation of hook_drush_command().
  */
@@ -15,9 +14,9 @@ function backup_migrate_drush_command() {
     'description' => dt('Backup the site\'s database with Backup and Migrate.'),
     'aliases' => array('bb'),
     'examples' => array(
-      'drush bam-backup' => 'Backup the default databse to the manual backup directory using the default settings.', 
-      'drush bam-backup db scheduled mysettings' => 'Backup the database to the scheduled directory using a settings profile called "mysettings"', 
-      'drush bam-backup files' => 'Backup the files directory to the manual directory using the default settings. The Backup and Migrate Files module is required for files backups.', 
+      'drush bam-backup' => 'Backup the default database to the manual backup directory using the default settings.',
+      'drush bam-backup db scheduled mysettings' => 'Backup the database to the scheduled directory using a settings profile called "mysettings"',
+      'drush bam-backup files' => 'Backup the files directory to the manual directory using the default settings.',
     ),
     'arguments' => array(
       'source'        => "Optional. The id of the source (usually a database) to backup. Use 'drush bam-sources' to get a list of sources. Defaults to 'db'",
@@ -41,7 +40,6 @@ function backup_migrate_drush_command() {
     'callback' => 'backup_migrate_drush_destinations',
     'description' => dt('Get a list of available destinations.'),
   );
-
   $items['bam-sources'] = array(
     'callback' => 'backup_migrate_drush_sources',
     'description' => dt('Get a list of available sources.'),
@@ -57,6 +55,17 @@ function backup_migrate_drush_command() {
       'destination'   => "Optional. The id of destination to list backups from. Use 'drush bam-destinations' to get a list of destinations.",
     ),
   );
+  $items['bam-schedule'] = array(
+    'callback' => 'backup_migrate_drush_schedule',
+    'description' => dt('Backup using a specific schedule.'),
+    'arguments' => array(
+      'schedule_id' => dt('The ID of the schedule to run.'),
+    ),
+  );
+  $items['bam-schedules'] = array(
+    'callback' => 'backup_migrate_drush_schedules',
+    'description' => dt('Get a list of available schedules.'),
+  );
   return $items;
 }
 
@@ -67,12 +76,16 @@ function backup_migrate_drush_help($section) {
   switch ($section) {
     case 'drush:bam-backup':
       return dt("Backup the site's database using default settings.");
+
     case 'drush:bam-restore':
       return dt('Restore the site\'s database with Backup and Migrate.');
+
     case 'drush:bam-destinations':
       return dt('Get a list of available destinations.');
+
     case 'drush:bam-profiles':
       return dt('Get a list of available settings profiles.');
+
     case 'drush:bam-backups':
       return dt('Get a list of previously created backup files.');
   }
@@ -96,7 +109,7 @@ function backup_migrate_drush_backup($source_id = 'db', $destination_id = 'manua
     return;
   }
   $settings = backup_migrate_get_profile($profile_id);
-  if(!$settings) {
+  if (!$settings) {
     _backup_migrate_message("Could not find the profile '@profile'. Try using 'drush bam-profiles' to get a list of available profiles.", array('@profile' => $profile_id), 'error');
     return;
   }
@@ -107,11 +120,49 @@ function backup_migrate_drush_backup($source_id = 'db', $destination_id = 'manua
   backup_migrate_perform_backup($settings);
 }
 
+/**
+ * Backup using schedule.
+ */
+function backup_migrate_drush_schedule($schedule_id = '') {
+  backup_migrate_include('schedules');
+
+  // Set the message mode to drush output.
+  _backup_migrate_message_callback('_backup_migrate_message_drush');
+
+  if (!($schedule = backup_migrate_get_schedule($schedule_id))) {
+    _backup_migrate_message("Could not find the schedule '@schedule'. Try using 'drush bam-schedules' to get a list of available schedules.", array('@schedule' => $schedule_id), 'error');
+    return;
+  }
+
+  if (!$schedule->enabled) {
+    _backup_migrate_message("Nothing to do, the schedule '@schedule' is disabled.", array('@schedule' => $schedule_id), 'warning');
+    return;
+  }
+
+  _backup_migrate_message("Starting schedule '$schedule_id'...");
+
+  backup_migrate_schedule_run($schedule_id);
+}
+
+/**
+ * Get a list of available destinations.
+ */
+function backup_migrate_drush_schedules() {
+  backup_migrate_include('schedules');
+  $rows = array(array(dt('ID'), dt('Name')));
+  foreach (backup_migrate_get_schedules() as $schedule) {
+    $rows[] = array(
+      $schedule->get_id(),
+      $schedule->get_name(),
+    );
+  }
+  drush_print_table($rows, TRUE, array(32, 32));
+}
+
 /**
  * Restore to the default database.
  */
 function backup_migrate_drush_restore($source_id = '', $destination_id = '', $file_id = '') {
-
   backup_migrate_include('profiles', 'destinations', 'sources');
 
   // Set the message mode to drush output.
@@ -125,7 +176,7 @@ function backup_migrate_drush_restore($source_id = '', $destination_id = '', $fi
     _backup_migrate_message("Could not find the destination '@destination'. Try using 'drush bam-destinations' to get a list of available destinations.", array('@destination' => $destination_id), 'error');
     return;
   }
-  else if (!$file_id || !$file = backup_migrate_destination_get_file($destination_id, $file_id)) {
+  elseif (!$file_id || !$file = backup_migrate_destination_get_file($destination_id, $file_id)) {
     _backup_migrate_message("Could not find the file '@file'. Try using 'drush bam-backups @destination' to get a list of available backup files in this destination destinations.", array('@destination' => $destination_id, '@file' => $file_id), 'error');
     return;
   }
@@ -136,7 +187,7 @@ function backup_migrate_drush_restore($source_id = '', $destination_id = '', $fi
   }
   _backup_migrate_message('Starting restore...');
   $settings = array('source_id' => $source_id);
-  backup_migrate_perform_restore($destination_id, $file_id);
+  backup_migrate_perform_restore($destination_id, $file_id, $settings);
 }
 
 /**
@@ -164,7 +215,7 @@ function _backup_migrate_drush_destinations($op = NULL) {
     $rows[] = array(
       $destination->get_id(),
       $destination->get_name(),
-      implode (', ', $destination->ops()),
+      implode(', ', $destination->ops()),
     );
   }
   drush_print_table($rows, TRUE, array(32, 32));
@@ -181,7 +232,7 @@ function _backup_migrate_drush_sources($op = NULL) {
     $rows[] = array(
       $destination->get_id(),
       $destination->get_name(),
-      implode (', ', $destination->ops()),
+      implode(', ', $destination->ops()),
     );
   }
   drush_print_table($rows, TRUE, array(32, 32));
@@ -203,7 +254,7 @@ function backup_migrate_drush_profiles() {
 }
 
 /**
- * Get a list of files in a given destination
+ * Get a list of files in a given destination.
  */
 function backup_migrate_drush_destination_files($destination_id = NULL) {
   backup_migrate_include('destinations');
@@ -220,7 +271,7 @@ function backup_migrate_drush_destination_files($destination_id = NULL) {
   if ($destination) {
     $destinations = array($destination);
   }
-  // List all destinations
+  // List all destinations.
   else {
     $destinations = backup_migrate_get_destinations('list files');
   }
@@ -243,13 +294,15 @@ function backup_migrate_drush_destination_files($destination_id = NULL) {
     }
   }
 
-  $headers = array(array(
-    dt('Filename'),
-    dt('Destination'),
-    dt('Date'),
-    dt('Age'),
-    dt('Size'),
-  ));
+  $headers = array(
+    array(
+      dt('Filename'),
+      dt('Destination'),
+      dt('Date'),
+      dt('Age'),
+      dt('Size'),
+    ),
+  );
 
   if (count($rows)) {
     array_multisort($sort, SORT_DESC, $rows);
@@ -264,8 +317,15 @@ function backup_migrate_drush_destination_files($destination_id = NULL) {
  * Send a message to the drush log.
  */
 function _backup_migrate_message_drush($message, $replace, $type) {
-  // Use drush_log to display to the user.
-  drush_log(strip_tags(dt($message, $replace)), str_replace('status', 'notice', $type));
+  // If this is an error use drush_set_error to notify the end user and set the
+  // exit status.
+  if ($type == 'error') {
+    drush_set_error(strip_tags(dt($message, $replace)));
+  }
+  else {
+    // Use drush_log to display to the user.
+    drush_log(strip_tags(dt($message, $replace)), str_replace('status', 'notice', $type));
+  }
   // Watchdog log the message as well for admins.
   _backup_migrate_message_log($message, $replace, $type);
 }

+ 132 - 138
sites/all/modules/contrib/admin/backup_migrate/includes/crud.inc

@@ -56,14 +56,15 @@ function backup_migrate_crud_subtypes($type) {
       backup_migrate_include($info['include']);
     }
 
-    // Allow modules (including this one) to declare backup and migrate subtypes.
-    // We don't use module_invoke_all so we can avoid the side-effects of array_merge_recursive.
+    // Allow modules (including this one) to declare backup and migrate
+    // subtypes. We don't use module_invoke_all so we can avoid the
+    // side-effects of array_merge_recursive.
     $out = array();
     foreach (module_implements('backup_migrate_' . $type . '_subtypes') as $module) {
-      $function =  $module . '_backup_migrate_' . $type . '_subtypes';
+      $function = $module . '_backup_migrate_' . $type . '_subtypes';
       $result = $function();
       if (isset($result) && is_array($result)) {
-      foreach ($result as $key => $val) {
+        foreach ($result as $key => $val) {
           $out[$key] = $val;
         }
       }
@@ -87,7 +88,8 @@ function backup_migrate_crud_subtype_info($type, $subtype) {
 /**
  * Get a generic object of the given type to be used for static-like functions.
  *
- * I'm not using actual static method calls since they don't work on variables prior to PHP 5.3.0
+ * I'm not using actual static method calls since they don't work on variables
+ * prior to PHP 5.3.0.
  */
 function backup_migrate_crud_type_load($type, $subtype = NULL) {
   $out = $info = NULL;
@@ -104,7 +106,7 @@ function backup_migrate_crud_type_load($type, $subtype = NULL) {
       backup_migrate_include($info['include']);
     }
     if (!empty($info['file'])) {
-      include_once './'. (isset($info['path']) ? $info['path'] : '') . $info['file'];
+      include_once './' . (isset($info['path']) ? $info['path'] : '') . $info['file'];
     }
 
     if (class_exists($info['class'])) {
@@ -132,10 +134,10 @@ function backup_migrate_crud_menu() {
   $items = array();
   foreach (backup_migrate_crud_types() as $type => $info) {
     $item = backup_migrate_crud_type_load($type);
-    $items += (array)$item->get_menu_items();
+    $items += (array) $item->get_menu_items();
     foreach (backup_migrate_crud_subtypes($type) as $subtype => $info) {
       $subitem = backup_migrate_crud_type_load($type, $subtype);
-      $items += (array)$subitem->get_menu_items();
+      $items += (array) $subitem->get_menu_items();
     }
   }
   return $items;
@@ -166,7 +168,7 @@ function backup_migrate_crud_ui_list($type) {
  * Page callback to list all items.
  */
 function backup_migrate_crud_ui_list_all() {
-  $out = '';
+  $out = array();
   foreach (backup_migrate_crud_types() as $type => $info) {
     $type = backup_migrate_crud_type_load($type);
     $out[] = theme('backup_migrate_group', array('title' => t($type->title_plural), 'body' => $type->get_list()));
@@ -189,7 +191,7 @@ function backup_migrate_crud_ui_edit($type, $item_id = NULL) {
 
 /**
  * Does a crud item with the given name exist.
- * 
+ *
  * Callback for the 'machine_name' form type.
  */
 function backup_migrate_crud_item_exists($machine_name, $element, $form_state) {
@@ -232,7 +234,6 @@ function backup_migrate_crud_edit_form_submit($form, &$form_state) {
   }
 }
 
-
 /**
  * Page callback to delete an item.
  */
@@ -256,10 +257,11 @@ function backup_migrate_crud_delete_confirm_form($form, &$form_state, $item) {
   if ($item->storage == BACKUP_MIGRATE_STORAGE_OVERRIDEN) {
     $message = $item->revert_confirm_message();
     return confirm_form($form, t('Are you sure?'), $item->get_settings_path(), $message, t('Revert'), t('Cancel'));
-  } else {
+  }
+  else {
     $message = $item->delete_confirm_message();
     return confirm_form($form, t('Are you sure?'), $item->get_settings_path(), $message, t('Delete'), t('Cancel'));
-  }  
+  }
 }
 
 /**
@@ -322,7 +324,7 @@ function backup_migrate_crud_import_form($form, &$form_state) {
 }
 
 /**
- * Validate handler to import a view
+ * Validate handler to import a view.
  */
 function backup_migrate_crud_import_form_validate($form, &$form_state) {
   $item = backup_migrate_crud_create_from_import($form_state['values']['code']);
@@ -330,12 +332,12 @@ function backup_migrate_crud_import_form_validate($form, &$form_state) {
     $form_state['values']['item'] = $item;
   }
   else {
-   form_set_error('code', t('Unable to import item.'));
+    form_set_error('code', t('Unable to import item.'));
   }
 }
 
 /**
- * import a item after confirmation.
+ * Import a item after confirmation.
  */
 function backup_migrate_crud_import_form_submit($form, &$form_state) {
   $item = $form_state['values']['item'];
@@ -373,7 +375,6 @@ function backup_migrate_crud_get_items($type) {
   }
 }
 
-
 /**
  * Get an item of the specified type.
  */
@@ -393,24 +394,26 @@ function backup_migrate_crud_create_item($type, $params) {
 }
 
 /**
- * A base class for items which can be stored in the database, listed, edited, deleted etc.
+ * A base class for items which can be stored in the database.
  */
 class backup_migrate_item {
-  var $show_in_list = TRUE;
-  var $settings_path = '/settings/';
-  var $db_table = '';
-  var $type_name = '';
-  var $storage = FALSE;
-  var $default_values = array();
-  var $singular = 'item';
-  var $plural = 'items';
-  var $title_plural = 'Items';
-  var $title_singular = 'Item';
-
-  /**
-   * This function is not supposed to be called. It is just here to help the po extractor out.
+  public $show_in_list = TRUE;
+  public $settings_path = '/settings/';
+  public $db_table = '';
+  public $type_name = '';
+  public $storage = FALSE;
+  public $default_values = array();
+  public $singular = 'item';
+  public $plural = 'items';
+  public $title_plural = 'Items';
+  public $title_singular = 'Item';
+
+  /**
+   * This function is not supposed to be called.
+   *
+   * It is just here to help the po extractor out.
    */
-  function strings() {
+  public function strings() {
     // Help the pot extractor find these strings.
     t('item');
     t('items');
@@ -425,25 +428,25 @@ class backup_migrate_item {
     t('Export !type');
   }
 
-
   /**
-   * Constructor, set the basic info pulled from the db or generated programatically.
+   * Set the basic info pulled from the db or generated programatically.
    */
-  function __construct($params = array()) {
-    $this->from_array($this->_merge_defaults((array)$params, (array)$this->get_default_values()));
+  public function __construct($params = array()) {
+    $this->from_array($this->_merge_defaults((array) $params, (array) $this->get_default_values()));
   }
 
   /**
    * Merge parameters with the given defaults.
    *
-   * Works like array_merge_recursive, but it doesn't turn scalar values into arrays.
+   * Works like array_merge_recursive, but it doesn't turn scalar values into
+   * arrays.
    */
-  function _merge_defaults($params, $defaults) {
+  public function _merge_defaults($params, $defaults) {
     foreach ($defaults as $key => $val) {
       if (!isset($params[$key])) {
         $params[$key] = $val;
       }
-      else if (is_array($params[$key])) {
+      elseif (is_array($params[$key])) {
         $params[$key] = $this->_merge_defaults($params[$key], $val);
       }
     }
@@ -453,14 +456,14 @@ class backup_migrate_item {
   /**
    * Get the default values for standard parameters.
    */
-  function get_default_values() {
+  public function get_default_values() {
     return $this->default_values;
   }
 
   /**
    * Save the item to the database.
-   */  
-  function save() {
+   */
+  public function save() {
     if (!$this->get_id()) {
       $this->unique_id();
     }
@@ -470,20 +473,19 @@ class backup_migrate_item {
 
   /**
    * Delete the item from the database.
-   */  
-  function delete() {
-    $keys = (array)$this->get_machine_name_field();
+   */
+  public function delete() {
+    $keys = (array) $this->get_machine_name_field();
     db_query('DELETE FROM {' . $this->db_table . '} WHERE ' . $keys[0] . ' = :id', array(':id' => $this->get_id()));
   }
 
-
   /**
    * Load an existing item from an array.
    */
-  function from_array($params) {
+  public function from_array($params) {
     foreach ($params as $key => $value) {
-      if (method_exists($this, 'set_'. $key)) {
-        $this->{'set_'. $key}($value);
+      if (method_exists($this, 'set_' . $key)) {
+        $this->{'set_' . $key}($value);
       }
       else {
         $this->{$key} = $value;
@@ -494,7 +496,7 @@ class backup_migrate_item {
   /**
    * Return as an array of values.
    */
-  function to_array() {
+  public function to_array() {
     $out = array();
     // Return fields as specified in the schema.
     $schema = $this->get_schema();
@@ -509,7 +511,7 @@ class backup_migrate_item {
   /**
    * Return as an exported array of values.
    */
-  function export() {
+  public function export() {
     $out = $this->to_array();
     $out['type_name'] = $this->type_name;
 
@@ -523,7 +525,7 @@ class backup_migrate_item {
   /**
    * Load an existing item from an database (serialized) array.
    */
-  function load_row($data) {
+  public function load_row($data) {
     $params = array();
     $schema = $this->get_schema();
     // Load fields as specified in the schema.
@@ -533,11 +535,10 @@ class backup_migrate_item {
     $this->from_array($params);
   }
 
-
   /**
    * Decode a loaded db row (unserialize necessary fields).
    */
-  function decode_db_row($data) {
+  public function decode_db_row($data) {
     $params = array();
     $schema = $this->get_schema();
     // Load fields as specified in the schema.
@@ -550,7 +551,7 @@ class backup_migrate_item {
   /**
    * Return the fields which must be serialized before saving to the db.
    */
-  function get_serialized_fields() {
+  public function get_serialized_fields() {
     $out = array();
     $schema = $this->get_schema();
     foreach ($schema['fields'] as $field => $info) {
@@ -564,7 +565,7 @@ class backup_migrate_item {
   /**
    * Get the primary key field title from the schema.
    */
-  function get_primary_key() {
+  public function get_primary_key() {
     $schema = $this->get_schema();
     return @$schema['primary key'];
   }
@@ -572,7 +573,7 @@ class backup_migrate_item {
   /**
    * Get the machine name field name from the schema.
    */
-  function get_machine_name_field() {
+  public function get_machine_name_field() {
     $schema = $this->get_schema();
     if (isset($schema['export']['key'])) {
       return $schema['export']['key'];
@@ -583,7 +584,7 @@ class backup_migrate_item {
   /**
    * Get the schema for the item type.
    */
-  function get_schema() {
+  public function get_schema() {
     return drupal_get_schema($this->db_table);
   }
 
@@ -592,16 +593,16 @@ class backup_migrate_item {
    *
    * We only handle single field keys since that's all we need.
    */
-  function get_id() {
-    $keys = (array)$this->get_machine_name_field();
-    return !empty($keys[0]) && !empty($this->{$keys[0]}) ? (string)$this->{$keys[0]} : '';
+  public function get_id() {
+    $keys = (array) $this->get_machine_name_field();
+    return !empty($keys[0]) && !empty($this->{$keys[0]}) ? (string) $this->{$keys[0]} : '';
   }
 
   /**
    * Set the primary id for this item (if any is set).
    */
-  function set_id($id) {
-    $keys = (array)$this->get_machine_name_field();
+  public function set_id($id) {
+    $keys = (array) $this->get_machine_name_field();
     if (!empty($keys[0])) {
       return $this->{$keys[0]} = $id;
     }
@@ -611,8 +612,8 @@ class backup_migrate_item {
   /**
    * Return a random (very very likely unique) string id for a new item.
    */
-  function generate_id() {
-    $id = md5(uniqid(mt_rand(), true));
+  public function generate_id() {
+    $id = md5(uniqid(mt_rand(), TRUE));
 
     // Find the shortest possible unique id from (min 4 chars).
     for ($i = 4; $i < 32; $i++) {
@@ -621,23 +622,27 @@ class backup_migrate_item {
         return $new_id;
       }
     }
-    // If we get here, then all 28 increasingly complex ids were already taken so we'll try again.
-    // this could theoretially lead to an infinite loop, but the odds are incredibly low.
+    // If we get here, then all 28 increasingly complex ids were already taken
+    // so we'll try again; this could theoretially lead to an infinite loop,
+    // but the odds are incredibly low.
     return $this->generate_id();
   }
 
   /**
-   * Make sure this item has a unique id. Should only be called for new items or the item will collide with itself.
+   * Make sure this item has a unique id.
+   *
+   * Should only be called for new items or the item will collide with itself.
    */
-  function unique_id() {
+  public function unique_id() {
     $id = $this->get_id();
 
     // Unset the autoincrement field so it can be regenerated.
-    foreach ((array)$this->get_primary_key() as $key) {
-      $this->{$key} = NULL;    
+    foreach ((array) $this->get_primary_key() as $key) {
+      $this->{$key} = NULL;
     }
 
-    // If the item doesn't have an ID or if it's id is already taken, generate random one.
+    // If the item doesn't have an ID or if it's id is already taken, generate
+    // random one.
     if (!$id || $this->item($id)) {
       $this->set_id($this->generate_id());
     }
@@ -646,26 +651,24 @@ class backup_migrate_item {
   /**
    * Get the name of the item.
    */
-  function get_name() {
+  public function get_name() {
     return @$this->name;
   }
 
   /**
    * Get the member with the given key.
-   */  
-  function get($key) {
-    if (method_exists($this, 'get_'. $key)) {
-      return $this->{'get_'. $key}();
+   */
+  public function get($key) {
+    if (method_exists($this, 'get_' . $key)) {
+      return $this->{'get_' . $key}();
     }
     return @$this->{$key};
   }
 
-  /* UI Stuff */
-
   /**
    * Get the action links for a destination.
    */
-  function get_action_links() {
+  public function get_action_links() {
     $out = array();
 
     $item_id = $this->get_id();
@@ -675,13 +678,13 @@ class backup_migrate_item {
     if (@$this->storage == BACKUP_MIGRATE_STORAGE_DB || @$this->storage == BACKUP_MIGRATE_STORAGE_OVERRIDEN) {
       $out['edit'] = l(t("edit"), $path . "/edit/$item_id");
     }
-    else if (@$this->storage == BACKUP_MIGRATE_STORAGE_NONE) {
+    elseif (@$this->storage == BACKUP_MIGRATE_STORAGE_NONE) {
       $out['edit'] = l(t("override"), $path . "/edit/$item_id");
     }
     if (@$this->storage == BACKUP_MIGRATE_STORAGE_DB) {
       $out['delete'] = l(t("delete"), $path . "/delete/$item_id");
     }
-    else if (@$this->storage == BACKUP_MIGRATE_STORAGE_OVERRIDEN) {
+    elseif (@$this->storage == BACKUP_MIGRATE_STORAGE_OVERRIDEN) {
       $out['delete'] = l(t("revert"), $path . "/delete/$item_id");
     }
     $out['export'] = l(t("export"), $path . "/export/$item_id");
@@ -691,11 +694,11 @@ class backup_migrate_item {
 
   /**
    * Get a table of all items of this type.
-   */  
-  function get_list() {
+   */
+  public function get_list() {
     $items = $this->all_items();
     $rows = array();
-    foreach ((array)$items as $item) {
+    foreach ((array) $items as $item) {
       if ($item->show_in_list()) {
         if ($row = $item->get_list_row()) {
           $rows[] = $row;
@@ -709,30 +712,29 @@ class backup_migrate_item {
       $out = t('There are no !items to display.', array('!items' => $this->plural));
     }
     if (user_access('administer backup and migrate')) {
-      $out .= ' '. l(t('Create a new !item', array('!item' => $this->singular)), $this->get_settings_path() .'/add');
+      $out .= ' ' . l(t('Create a new !item', array('!item' => $this->singular)), $this->get_settings_path() . '/add');
     }
     return $out;
   }
 
   /**
    * Get the columns needed to list the type.
-   */  
-  function show_in_list() {
+   */
+  public function show_in_list() {
     return $this->show_in_list;
   }
 
   /**
    * Get the columns needed to list the type.
-   */  
-  function get_settings_path() {
+   */
+  public function get_settings_path() {
     return BACKUP_MIGRATE_MENU_PATH . $this->settings_path . $this->type_name;
   }
 
-
   /**
    * Get the columns needed to list the type.
-   */  
-  function get_list_column_info() {
+   */
+  public function get_list_column_info() {
     return array(
       'actions' => array('title' => t('Operations'), 'html' => TRUE),
     );
@@ -740,8 +742,8 @@ class backup_migrate_item {
 
   /**
    * Get header for a lost of this type.
-   */  
-  function get_list_header() {
+   */
+  public function get_list_header() {
     $out = array();
     foreach ($this->get_list_column_info() as $key => $col) {
       $out[] = $col['title'];
@@ -751,8 +753,8 @@ class backup_migrate_item {
 
   /**
    * Get a row of data to be used in a list of items of this type.
-   */  
-  function get_list_row() {
+   */
+  public function get_list_row() {
     $out = array();
     foreach ($this->get_list_column_info() as $key => $col) {
       $out[$key] = empty($col['html']) ? check_plain($this->get($key)) : $this->get($key);
@@ -766,7 +768,7 @@ class backup_migrate_item {
   /**
    * Get the rendered action links for a destination.
    */
-  function get_actions() {
+  public function get_actions() {
     $links = $this->get_action_links();
     return implode(" &nbsp; ", $links);
   }
@@ -774,7 +776,7 @@ class backup_migrate_item {
   /**
    * Get the edit form for the item.
    */
-  function edit_form() {
+  public function edit_form() {
     $form = array();
     $form['item'] = array(
       '#type' => 'value',
@@ -811,37 +813,36 @@ class backup_migrate_item {
   /**
    * Validate the edit form for the item.
    */
-  function edit_form_validate($form, &$form_state) {
+  public function edit_form_validate($form, &$form_state) {
   }
 
   /**
    * Submit the edit form for the item.
    */
-  function edit_form_submit($form, &$form_state) {
+  public function edit_form_submit($form, &$form_state) {
     $this->from_array($form_state['values']);
     $this->save();
     _backup_migrate_message('Your !type was saved', array('!type' => t($this->singular)));
   }
 
   /**
-   * Get the message to send to the user when confirming the deletion of the item.
+   * The message to send to the user when confirming the deletion of the item.
    */
-  function delete_confirm_message() {
+  public function delete_confirm_message() {
     return t('Are you sure you want to delete the !type %name?', array('!type' => t($this->singular), '%name' => $this->get('name')));
   }
 
   /**
-   * Get the message to send to the user when confirming the deletion of the item.
+   * The message to send to the user when confirming the deletion of the item.
    */
-  function revert_confirm_message() {
+  public function revert_confirm_message() {
     return t('Are you sure you want to revert the !type %name back to the default settings?', array('!type' => t($this->singular), '%name' => $this->get('name')));
   }
 
-  /* Static Functions */ 
   /**
    * Get the menu items for manipulating this type.
    */
-  function get_menu_items() {
+  public function get_menu_items() {
     $path = $this->get_settings_path();
 
     $type = $this->type_name;
@@ -853,13 +854,13 @@ class backup_migrate_item {
       'weight' => 2,
       'type' => MENU_LOCAL_TASK,
     );
-    $items[$path .'/list'] = array(
+    $items[$path . '/list'] = array(
       'title' => 'List !type',
       'title arguments' => array('!type' => t($this->title_plural)),
       'weight' => 1,
       'type' => MENU_DEFAULT_LOCAL_TASK,
     );
-    $items[$path .'/add'] = array(
+    $items[$path . '/add'] = array(
       'title' => 'Add !type',
       'title arguments' => array('!type' => t($this->title_singular)),
       'page callback' => 'backup_migrate_menu_callback',
@@ -868,7 +869,7 @@ class backup_migrate_item {
       'weight' => 2,
       'type' => MENU_LOCAL_ACTION,
     );
-    $items[$path .'/delete'] = array(
+    $items[$path . '/delete'] = array(
       'title' => 'Delete !type',
       'title arguments' => array('!type' => t($this->title_singular)),
       'page callback' => 'backup_migrate_menu_callback',
@@ -876,7 +877,7 @@ class backup_migrate_item {
       'access arguments' => array('administer backup and migrate'),
       'type' => MENU_CALLBACK,
     );
-    $items[$path .'/edit'] = array(
+    $items[$path . '/edit'] = array(
       'title' => 'Edit !type',
       'title arguments' => array('!type' => t($this->title_singular)),
       'page callback' => 'backup_migrate_menu_callback',
@@ -884,7 +885,7 @@ class backup_migrate_item {
       'access arguments' => array('administer backup and migrate'),
       'type' => MENU_CALLBACK,
     );
-    $items[$path .'/export'] = array(
+    $items[$path . '/export'] = array(
       'title' => 'Export !type',
       'title arguments' => array('!type' => t($this->title_singular)),
       'page callback' => 'backup_migrate_menu_callback',
@@ -895,11 +896,13 @@ class backup_migrate_item {
     return $items;
   }
 
-
   /**
-   * Create a new items with the given input. Doesn't load the parameters, but could use them to determine what type to create.
+   * Create a new items with the given input.
+   *
+   * Doesn't load the parameters, but could use them to determine what type to
+   * create.
    */
-  function create($params = array()) {
+  public function create($params = array()) {
     $type = get_class($this);
     return new $type($params);
   }
@@ -907,12 +910,12 @@ class backup_migrate_item {
   /**
    * Get all of the given items.
    */
-  function all_items() {
-    static $cache = array();
+  public function all_items() {
     $items = array();
 
-    // Get any items stored as a variable. This allows destinations to be defined in settings.php
-    $defaults = (array)variable_get($this->db_table . '_defaults', array());
+    // Get any items stored as a variable. This allows destinations to be
+    // defined in settings.php
+    $defaults = (array) variable_get($this->db_table . '_defaults', array());
     foreach ($defaults as $info) {
       if (is_array($info) && $item = $this->create($info)) {
         $items[$item->get_id()] = $item;
@@ -932,27 +935,16 @@ class backup_migrate_item {
     // Allow other modules to declare destinations programatically.
     $default_items = module_invoke_all($this->db_table);
 
-    // Get CTools exported versions.
-    if (function_exists('ctools_include')) {
-      ctools_include('export');
-      $defaults = ctools_export_load_object($this->db_table);
-      foreach ($defaults as $info) {
-        $info = (array)$info;
-        if (!empty($info) && $item = $this->create($info)) {
-          $default_items[$item->get_id()] = $item;
-        }
-      }
-    }
-
     // Get any items stored as a variable again to correctly mark overrides.
-    $defaults = (array)variable_get($this->db_table . '_defaults', array());
+    $defaults = (array) variable_get($this->db_table . '_defaults', array());
     foreach ($defaults as $info) {
       if (is_array($info) && $item = $this->create($info)) {
         $default_items[] = $item;
       }
     }
 
-    // Add the default items to the array or set the storage flag if they've already been overridden.
+    // Add the default items to the array or set the storage flag if they've
+    // already been overridden.
     foreach ($default_items as $item) {
       if (isset($items[$item->get_id()])) {
         $items[$item->get_id()]->storage = BACKUP_MIGRATE_STORAGE_OVERRIDEN;
@@ -963,9 +955,10 @@ class backup_migrate_item {
       }
     }
 
-    // Allow other modules to alter the items. This should maybe be before the db override code above
-    // but then the filters are not able to set defaults for missing values. Other modules should just
-    // be careful not to overwrite the user's UI changes in an unexpected way.
+    // Allow other modules to alter the items. This should maybe be before the
+    // db override code above but then the filters are not able to set defaults
+    // for missing values. Other modules should just be careful not to
+    // overwrite the user's UI changes in an unexpected way.
     drupal_alter($this->db_table, $items);
 
     return $items;
@@ -974,7 +967,7 @@ class backup_migrate_item {
   /**
    * A particular item.
    */
-  function item($item_id) {
+  public function item($item_id) {
     $items = $this->all_items();
     return !empty($items[$item_id]) ? $items[$item_id] : NULL;
   }
@@ -982,8 +975,9 @@ class backup_migrate_item {
   /**
    * A particular item.
    */
-  function item_exists($item_id) {
+  public function item_exists($item_id) {
     $items = $this->all_items();
     return !empty($items[$item_id]);
   }
+
 }

+ 33 - 12
sites/all/modules/contrib/admin/backup_migrate/includes/destinations.browser.inc

@@ -1,6 +1,5 @@
 <?php
 
-
 /**
  * @file
  * Functions to handle the browser upload/download backup destination.
@@ -12,13 +11,15 @@
  * @ingroup backup_migrate_destinations
  */
 class backup_migrate_destination_browser extends backup_migrate_destination {
+
   /**
    * Get a row of data to be used in a list of items of this type.
-   */  
-  function get_list_row() {
+   */
+  public function get_list_row() {
     // Return none as this type should not be displayed.
     return array();
   }
+
 }
 
 /**
@@ -27,8 +28,16 @@ class backup_migrate_destination_browser extends backup_migrate_destination {
  * @ingroup backup_migrate_destinations
  */
 class backup_migrate_destination_browser_upload extends backup_migrate_destination_browser {
-  var $supported_ops = array('restore');
-  function __construct() {
+
+  /**
+   * {@inheritdoc}
+   */
+  public $supported_ops = array('restore');
+
+  /**
+   * Constructor.
+   */
+  public function __construct() {
     $params = array();
     $params['name'] = "Upload";
     $params['machine_name'] = 'upload';
@@ -38,7 +47,7 @@ class backup_migrate_destination_browser_upload extends backup_migrate_destinati
   /**
    * File load destination callback.
    */
-  function load_file($file_id) {
+  public function load_file($file_id) {
     if ($file = file_save_upload('backup_migrate_restore_upload')) {
       $out = new backup_file(array('filepath' => $file->uri));
       backup_migrate_temp_files_add($file->uri);
@@ -46,6 +55,7 @@ class backup_migrate_destination_browser_upload extends backup_migrate_destinati
     }
     return NULL;
   }
+
 }
 
 /**
@@ -54,11 +64,22 @@ class backup_migrate_destination_browser_upload extends backup_migrate_destinati
  * @ingroup backup_migrate_destinations
  */
 class backup_migrate_destination_browser_download extends backup_migrate_destination_browser {
-  var $supported_ops = array('manual backup');
-  // Browser downloads must always be the last destination as they must end the current process when they are done.
-  var $weight = 1000;
 
-  function __construct() {
+  /**
+   * {@inheritdoc}
+   */
+  public $supported_ops = array('manual backup');
+
+  /**
+   * Browser downloads must always be the last destination as they must end the
+   * current process when they are done.
+   */
+  public $weight = 1000;
+
+  /**
+   * Constructor.
+   */
+  public function __construct() {
     $params = array();
     $params['name'] = "Download";
     $params['machine_name'] = 'download';
@@ -68,9 +89,9 @@ class backup_migrate_destination_browser_download extends backup_migrate_destina
   /**
    * File save destination callback.
    */
-  function save_file($file, $settings) {
+  public function save_file($file, $settings) {
     backup_migrate_include('files');
     $file->transfer();
   }
-}
 
+}

+ 81 - 75
sites/all/modules/contrib/admin/backup_migrate/includes/destinations.db.inc

@@ -1,6 +1,5 @@
 <?php
 
-
 /**
  * @file
  * Functions to handle the direct to database destination.
@@ -12,22 +11,22 @@
  * @ingroup backup_migrate_destinations
  */
 class backup_migrate_destination_db extends backup_migrate_destination_remote {
-  var $supported_ops = array('scheduled backup', 'manual backup', 'configure', 'source');
-  var $db_target = 'default';
-  var $connection = null;
+  public $supported_ops = array('scheduled backup', 'manual backup', 'configure', 'source');
+  public $db_target = 'default';
+  public $connection = NULL;
 
 
-  function type_name() {
+  public function type_name() {
     return t("Database");
   }
 
   /**
    * Save the info by importing it into the database.
    */
-  function save_file($file, $settings) {
+  public function save_file($file, $settings) {
     backup_migrate_include('files');
 
-    // Set the source_id to the destination_id in the settings since for a restore, the source_id is the 
+    // Set the source_id to the destination_id in the settings since for a restore, the source_id is the
     // database that gets restored to.
     $settings->set_source($this->get_id());
 
@@ -40,10 +39,10 @@ class backup_migrate_destination_db extends backup_migrate_destination_remote {
   /**
    * Destination configuration callback.
    */
-  function edit_form() {
+  public function edit_form() {
     $form = parent::edit_form();
     $form['scheme']['#title'] = t('Database type');
-//    $form['scheme']['#options'] = array($GLOBALS['db_type'] => $GLOBALS['db_type']);
+    //    $form['scheme']['#options'] = array($GLOBALS['db_type'] => $GLOBALS['db_type']);
     $form['scheme']['#description'] = t('The type of the database. Drupal only supports one database type at a time, so this must be the same as the current database type.');
     $form['path']['#title'] = t('Database name');
     $form['path']['#description'] = t('The name of the database. The database must exist, it will not be created for you.');
@@ -54,7 +53,7 @@ class backup_migrate_destination_db extends backup_migrate_destination_remote {
   /**
    * Validate the configuration form. Make sure the db info is valid.
    */
-  function edit_form_validate($form, &$form_state) {
+  public function edit_form_validate($form, &$form_state) {
     if (!preg_match('/[a-zA-Z0-9_\$]+/', $form_state['values']['path'])) {
       form_set_error('path', t('The database name is not valid.'));
     }
@@ -62,56 +61,63 @@ class backup_migrate_destination_db extends backup_migrate_destination_remote {
   }
 
   /**
-   * Get the form for the settings for this destination.
+   * Get the default settings for this object.
    *
-   * Return the default tables whose data can be ignored. These tables mostly contain
-   *  info which can be easily reproducted (such as cache or search index)
-   *  but also tables which can become quite bloated but are not necessarily extremely
-   *  important to back up or migrate during development (such ass access log and watchdog)
+   * @return array
+   *   The default tables whose data can be ignored. These tables mostly
+   *   contain info which can be easily reproducted (such as cache or search
+   *   index) but also tables which can become quite bloated but are not
+   *   necessarily extremely important to back up or migrate during development
+   *   (such as access log and watchdog).
    */
-  function backup_settings_default() {
-    $core = array(
-        'cache',
-        'cache_admin_menu',
-        'cache_browscap',
-        'cache_content',
-        'cache_filter',
-        'cache_calendar_ical',
-        'cache_location',
-        'cache_menu',
-        'cache_page',
-        'cache_reptag',
-        'cache_views',
-        'cache_views_data',
-        'cache_block',
-        'cache_update',
-        'cache_form',
-        'cache_bootstrap',
-        'cache_field',
-        'cache_image',
-        'cache_path',
-        'sessions',
-        'search_dataset',
-        'search_index',
-        'search_keywords_log',
-        'search_total',
-        'watchdog',
-        'accesslog',
-        'devel_queries',
-        'devel_times',
-      );
-    $nodata_tables = array_merge($core, module_invoke_all('devel_caches'));
-     return array(
-      'nodata_tables' => $nodata_tables,
-      'exclude_tables' => array(),
+  public function backup_settings_default() {
+    $all_tables = $this->_get_table_names();
+
+    // Basic modules that should be excluded.
+    $basic = array(
+      // Default core tables.
+      'accesslog',
+      'sessions',
+      'watchdog',
+      // Search module.
+      'search_dataset',
+      'search_index',
+      'search_keywords_log',
+      'search_total',
+      // Devel module.
+      'devel_queries',
+      'devel_times',
+    );
+
+    // Identify all cache tables.
+    $cache = array('cache');
+    foreach ($all_tables as $table_name) {
+      if (strpos($table_name, 'cache_') === 0) {
+        $cache[] = $table_name;
+      }
+    }
+
+    // Simpletest can create a lot of tables that do not need to be backed up,
+    // but all of them start with the string 'simpletest' so they can be easily
+    // excluded.
+    $simpletest = array();
+    foreach ($all_tables as $table_name) {
+      if (strpos($table_name, 'simpletest') === 0) {
+        $simpletest[] = $table_name;
+      }
+    }
+
+    return array(
+      'nodata_tables' => array_merge($basic, $cache, module_invoke_all('devel_caches')),
+      'exclude_tables' => $simpletest,
       'utils_lock_tables' => FALSE,
-   );
+    );
   }
 
   /**
    * Get the form for the backup settings for this destination.
    */
-  function backup_settings_form($settings) {
+  public function backup_settings_form($settings) {
     $objects  = $this->get_object_names();
     $form['#description'] = t("You may omit specific tables, or specific table data from the backup file. Only omit data that you know you will not need such as cache data, or tables from other applications. Excluding tables can break your Drupal install, so <strong>do not change these settings unless you know what you're doing</strong>.");
     $form['exclude_tables'] = array(
@@ -143,16 +149,15 @@ class backup_migrate_destination_db extends backup_migrate_destination_remote {
   /**
    * Backup from this source.
    */
-  function backup_to_file($file, $settings) {
+  public function backup_to_file($file, $settings) {
     $file->push_type($this->get_file_type_id());
 
     backup_migrate_filters_invoke_all('pre_backup', $this, $file, $settings);
-    //$this->lock_tables($settings);
-
+    // $this->lock_tables($settings);
     // Switch to a different db if specified.
     $success = $this->_backup_db_to_file($file, $settings);
 
-    //$this->unlock_tables($settings);
+    // $this->unlock_tables($settings);
     backup_migrate_filters_invoke_all('post_backup', $this, $file, $settings, $success);
 
     return $success ? $file : FALSE;
@@ -161,7 +166,7 @@ class backup_migrate_destination_db extends backup_migrate_destination_remote {
   /**
    * Restore to this source.
    */
-  function restore_from_file($file, &$settings) {
+  public function restore_from_file($file, &$settings) {
     $num = 0;
     $type = $this->get_file_type_id();
     // Open the file using the file wrapper. Check that the dump is of the right type (allow .sql for legacy reasons).
@@ -183,7 +188,7 @@ class backup_migrate_destination_db extends backup_migrate_destination_remote {
   /**
    * Get the db connection for the specified db.
    */
-  function _get_db_connection() {
+  public function _get_db_connection() {
     if (!$this->connection) {
       $target = $key = '';
       $parts = explode(':', $this->get_id());
@@ -197,12 +202,12 @@ class backup_migrate_destination_db extends backup_migrate_destination_remote {
         // If the url is specified build it into a connection info array.
         if (!empty($this->dest_url)) {
           $info = array(
-            'driver'    => empty($this->dest_url['scheme'])   ? NULL : $this->dest_url['scheme'],
-            'host'      => empty($this->dest_url['host'])     ? NULL : $this->dest_url['host'],
-            'port'      => empty($this->dest_url['port'])     ? NULL : $this->dest_url['port'],
-            'username'  => empty($this->dest_url['user'])     ? NULL : $this->dest_url['user'],
-            'password'  => empty($this->dest_url['pass'])     ? NULL : $this->dest_url['pass'],
-            'database'  => empty($this->dest_url['path'])     ? NULL : $this->dest_url['path'], 
+            'driver'    => empty($this->dest_url['scheme']) ? NULL : $this->dest_url['scheme'],
+            'host'      => empty($this->dest_url['host']) ? NULL : $this->dest_url['host'],
+            'port'      => empty($this->dest_url['port']) ? NULL : $this->dest_url['port'],
+            'username'  => empty($this->dest_url['user']) ? NULL : $this->dest_url['user'],
+            'password'  => empty($this->dest_url['pass']) ? NULL : $this->dest_url['pass'],
+            'database'  => empty($this->dest_url['path']) ? NULL : $this->dest_url['path'],
           );
           $key    = uniqid('backup_migrate_tmp_');
           $target = 'default';
@@ -223,21 +228,21 @@ class backup_migrate_destination_db extends backup_migrate_destination_remote {
   /**
    * Backup the databases to a file.
    */
-  function _backup_db_to_file($file, $settings) {
+  public function _backup_db_to_file($file, $settings) {
     // Must be overridden.
   }
 
   /**
    * Backup the databases to a file.
    */
-  function _restore_db_from_file($file, $settings) {
+  public function _restore_db_from_file($file, $settings) {
     // Must be overridden.
   }
 
   /**
    * Get a list of objects in the database.
    */
-  function get_object_names() {
+  public function get_object_names() {
     // Must be overridden.
     $out = $this->_get_table_names();
     if (method_exists($this, '_get_view_names')) {
@@ -249,7 +254,7 @@ class backup_migrate_destination_db extends backup_migrate_destination_remote {
   /**
    * Get a list of tables in the database.
    */
-  function get_table_names() {
+  public function get_table_names() {
     // Must be overridden.
     $out = $this->_get_table_names();
     return $out;
@@ -258,7 +263,7 @@ class backup_migrate_destination_db extends backup_migrate_destination_remote {
   /**
    * Get a list of tables in the database.
    */
-  function _get_table_names() {
+  public function _get_table_names() {
     // Must be overridden.
     return array();
   }
@@ -266,12 +271,12 @@ class backup_migrate_destination_db extends backup_migrate_destination_remote {
   /**
    * Lock the database in anticipation of a backup.
    */
-  function lock_tables($settings) {
+  public function lock_tables($settings) {
     if ($settings->filters['utils_lock_tables']) {
       $tables = array();
       foreach ($this->get_table_names() as $table) {
         // There's no need to lock excluded or structure only tables because it doesn't matter if they change.
-        if (empty($settings->filters['exclude_tables']) || !in_array($table, (array)$settings->filters['exclude_tables'])) {
+        if (empty($settings->filters['exclude_tables']) || !in_array($table, (array) $settings->filters['exclude_tables'])) {
           $tables[] = $table;
         }
       }
@@ -282,14 +287,14 @@ class backup_migrate_destination_db extends backup_migrate_destination_remote {
   /**
    * Lock the list of given tables in the database.
    */
-  function _lock_tables($tables) {
+  public function _lock_tables($tables) {
     // Must be overridden.
   }
 
   /**
    * Unlock any tables that have been locked.
    */
-  function unlock_tables($settings) {
+  public function unlock_tables($settings) {
     if ($settings->filters['utils_lock_tables']) {
       $this->_unlock_tables();
     }
@@ -298,14 +303,15 @@ class backup_migrate_destination_db extends backup_migrate_destination_remote {
   /**
    * Unlock the list of given tables in the database.
    */
-  function _unlock_tables($tables) {
+  public function _unlock_tables($tables) {
     // Must be overridden.
   }
 
   /**
    * Get the file type for to backup this destination to.
    */
-  function get_file_type_id() {
+  public function get_file_type_id() {
     return 'sql';
   }
+
 }

+ 154 - 93
sites/all/modules/contrib/admin/backup_migrate/includes/destinations.db.mysql.inc

@@ -1,5 +1,9 @@
 <?php
 
+/**
+ * @file
+ */
+
 backup_migrate_include('destinations.db');
 
 /**
@@ -12,16 +16,15 @@ backup_migrate_include('destinations.db');
  *
  * @ingroup backup_migrate_destinations
  */
-
 class backup_migrate_destination_db_mysql extends backup_migrate_destination_db {
-  function type_name() {
+  public function type_name() {
     return t("MySQL Database");
   }
 
   /**
    * Return a list of backup filetypes.
    */
-  function file_types() {
+  public function file_types() {
     return array(
       "sql" => array(
         "extension" => "sql",
@@ -41,17 +44,27 @@ class backup_migrate_destination_db_mysql extends backup_migrate_destination_db
   /**
    * Declare any mysql databases defined in the settings.php file as a possible destination.
    */
-  function destinations() {
+  public function destinations() {
     $out = array();
     global $databases;
-    foreach ((array)$databases as $db_key => $target) {
-      foreach ((array)$target as $tgt_key => $info) {
+    foreach ((array) $databases as $db_key => $target) {
+      foreach ((array) $target as $tgt_key => $info) {
         // Only mysql/mysqli supported by this destination.
         $key = $db_key . ':' . $tgt_key;
         if ($info['driver'] === 'mysql') {
-          $url = $info['driver'] . '://' . $info['username'] . ':' . $info['password'] . '@' . $info['host'] . (isset($info['port']) ? ':' . $info['port'] : '') . '/' . $info['database'];
+          // Compile the database connection string.
+          $url = 'mysql://';
+          $url .= urlencode($info['username']) . ':' . urlencode($info['password']);
+          $url .= '@';
+          $url .= urlencode($info['host']);
+          if (!empty($info['port'])) {
+            $url .= ':' . $info['port'];
+          }
+          $url .= '/' . urlencode($info['database']);
+
           if ($destination = backup_migrate_create_destination('mysql', array('url' => $url))) {
-            // Treat the default database differently because it is probably the only one available.
+            // Treat the default database differently because it is probably
+            // the only one available.
             if ($key == 'default:default') {
               $destination->set_id('db');
               $destination->set_name(t('Default Database'));
@@ -60,8 +73,8 @@ class backup_migrate_destination_db_mysql extends backup_migrate_destination_db
               $destination->remove_op('manual backup');
             }
             else {
-              $destination->set_id('db:'. $key);
-              $destination->set_name($key .": ". $destination->get_display_location());
+              $destination->set_id('db:' . $key);
+              $destination->set_name($key . ": " . $destination->get_display_location());
             }
             $out[$destination->get_id()] = $destination;
           }
@@ -74,14 +87,14 @@ class backup_migrate_destination_db_mysql extends backup_migrate_destination_db
   /**
    * Get the file type for to backup this destination to.
    */
-  function get_file_type_id() {
+  public function get_file_type_id() {
     return 'mysql';
   }
 
   /**
    * Get the form for the backup settings for this destination.
    */
-  function backup_settings_form($settings) {
+  public function backup_settings_form($settings) {
     $form = parent::backup_settings_form($settings);
 
     $form['use_mysqldump'] = array(
@@ -94,15 +107,14 @@ class backup_migrate_destination_db_mysql extends backup_migrate_destination_db
     return $form;
   }
 
-
   /**
    * Backup the databases to a file.
    *
    *  Returns a list of sql commands, one command per line.
    *  That makes it easier to import without loading the whole file into memory.
-   *  The files are a little harder to read, but human-readability is not a priority
+   *  The files are a little harder to read, but human-readability is not a priority.
    */
-  function _backup_db_to_file($file, $settings) {
+  public function _backup_db_to_file($file, $settings) {
     if (!empty($settings->filters['use_mysqldump']) && $this->_backup_db_to_file_mysqldump($file, $settings)) {
       return TRUE;
     }
@@ -145,16 +157,14 @@ class backup_migrate_destination_db_mysql extends backup_migrate_destination_db
     }
   }
 
-
   /**
    * Backup the databases to a file using the mysqldump command.
    */
-  function _backup_db_to_file_mysqldump($file, $settings) {
+  public function _backup_db_to_file_mysqldump($file, $settings) {
     $success = FALSE;
     $nodata_tables = array();
     $alltables = $this->_get_tables();
 
-
     $command = 'mysqldump --result-file=%file --opt -Q --host=%host --port=%port --user=%user --password=%pass %db';
     $args = array(
       '%file' => $file->filepath(),
@@ -168,17 +178,17 @@ class backup_migrate_destination_db_mysql extends backup_migrate_destination_db
     // Ignore the excluded and no-data tables.
     $db = $this->dest_url['path'];
     if (!empty($settings->filters['exclude_tables'])) {
-      foreach ((array)$settings->filters['exclude_tables'] as $table) {
+      foreach ((array) $settings->filters['exclude_tables'] as $table) {
         if (isset($alltables[$table])) {
-          $command .= ' --ignore-table='. $db .'.'. $table;
+          $command .= ' --ignore-table=' . $db . '.' . $table;
         }
       }
     }
     if (!empty($settings->filters['nodata_tables'])) {
-      foreach ((array)$settings->filters['nodata_tables'] as $table) {
+      foreach ((array) $settings->filters['nodata_tables'] as $table) {
         if (isset($alltables[$table])) {
           $nodata_tables[] = $table;
-          $command .= ' --ignore-table='. $db .'.'. $table;
+          $command .= ' --ignore-table=' . $db . '.' . $table;
         }
       }
     }
@@ -196,10 +206,19 @@ class backup_migrate_destination_db_mysql extends backup_migrate_destination_db
   /**
    * Backup the databases to a file.
    */
-  function _restore_db_from_file($file, $settings) {
+  public function _restore_db_from_file($file, $settings) {
     $num = 0;
 
     if ($file->open() && $conn = $this->_get_db_connection()) {
+      // Optionally drop all existing tables.
+      if (!empty($settings->filters['utils_drop_all_tables'])) {
+        $all_tables = $this->_get_tables();
+        $table_names = array_map('backup_migrate_array_name_value', $all_tables);
+        $table_list = join(', ', $table_names);
+        $stmt = $conn->prepare("DROP TABLE IF EXISTS $table_list;\n");
+        $stmt->execute();
+      }
+
       // Read one line at a time and run the query.
       while ($line = $this->_read_sql_command_from_file($file)) {
         if (_backup_migrate_check_timeout()) {
@@ -222,17 +241,16 @@ class backup_migrate_destination_db_mysql extends backup_migrate_destination_db
     return $num;
   }
 
-
   /**
    * Read a multiline sql command from a file.
    *
    * Supports the formatting created by mysqldump, but won't handle multiline comments.
    */
-  function _read_sql_command_from_file($file) {
+  public function _read_sql_command_from_file($file) {
     $out = '';
     while ($line = $file->read()) {
       $first2 = substr($line, 0, 2);
-      $first3 = substr($line, 0, 2);
+      $first3 = substr($line, 0, 3);
 
       // Ignore single line comments. This function doesn't support multiline comments or inline comments.
       if ($first2 != '--' && ($first2 != '/*' || $first3 == '/*!')) {
@@ -249,7 +267,7 @@ class backup_migrate_destination_db_mysql extends backup_migrate_destination_db
   /**
    * Get a list of tables in the database.
    */
-  function _get_table_names() {
+  public function _get_table_names() {
     $out = array();
     foreach ($this->_get_tables() as $table) {
       $out[$table['name']] = $table['name'];
@@ -260,7 +278,7 @@ class backup_migrate_destination_db_mysql extends backup_migrate_destination_db
   /**
    * Get a list of views in the database.
    */
-  function _get_view_names() {
+  public function _get_view_names() {
     $out = array();
     foreach ($this->_get_views() as $view) {
       $out[$view['name']] = $view['name'];
@@ -271,29 +289,29 @@ class backup_migrate_destination_db_mysql extends backup_migrate_destination_db
   /**
    * Lock the list of given tables in the database.
    */
-  function _lock_tables($tables) {
+  public function _lock_tables($tables) {
     if ($tables) {
       $tables_escaped = array();
       foreach ($tables as $table) {
-        $tables_escaped[] = '`'. db_escape_table($table) .'`  WRITE';
+        $tables_escaped[] = '`' . db_escape_table($table) . '`  WRITE';
       }
-      $this->query('LOCK TABLES '. implode(', ', $tables_escaped));
+      $this->query('LOCK TABLES ' . implode(', ', $tables_escaped));
     }
   }
 
   /**
    * Unlock all tables in the database.
    */
-  function _unlock_tables($settings) {
+  public function _unlock_tables($settings) {
     $this->query('UNLOCK TABLES');
   }
 
   /**
    * Get a list of tables in the db.
    */
-  function _get_tables() {
+  public function _get_tables() {
     $out = array();
-    // get auto_increment values and names of all tables
+    // get auto_increment values and names of all tables.
     $tables = $this->query("show table status", array(), array('fetch' => PDO::FETCH_ASSOC));
     foreach ($tables as $table) {
       // Lowercase the keys because between Drupal 7.12 and 7.13/14 the default query behavior was changed.
@@ -309,9 +327,9 @@ class backup_migrate_destination_db_mysql extends backup_migrate_destination_db
   /**
    * Get a list of views in the db.
    */
-  function _get_views() {
+  public function _get_views() {
     $out = array();
-    // get auto_increment values and names of all tables
+    // get auto_increment values and names of all tables.
     $tables = $this->query("show table status", array(), array('fetch' => PDO::FETCH_ASSOC));
     foreach ($tables as $table) {
       // Lowercase the keys because between Drupal 7.12 and 7.13/14 the default query behavior was changed.
@@ -327,36 +345,36 @@ class backup_migrate_destination_db_mysql extends backup_migrate_destination_db
   /**
    * Get the sql for the structure of the given table.
    */
-  function _get_table_structure_sql($table) {
+  public function _get_table_structure_sql($table) {
     $out = "";
-    $result = $this->query("SHOW CREATE TABLE `". $table['name'] ."`", array(), array('fetch' => PDO::FETCH_ASSOC));
+    $result = $this->query("SHOW CREATE TABLE `" . $table['name'] . "`", array(), array('fetch' => PDO::FETCH_ASSOC));
     foreach ($result as $create) {
       // Lowercase the keys because between Drupal 7.12 and 7.13/14 the default query behavior was changed.
       // See: http://drupal.org/node/1171866
       $create = array_change_key_case($create);
-      $out .= "DROP TABLE IF EXISTS `". $table['name'] ."`;\n";
+      $out .= "DROP TABLE IF EXISTS `" . $table['name'] . "`;\n";
       // Remove newlines and convert " to ` because PDO seems to convert those for some reason.
       $out .= strtr($create['create table'], array("\n" => ' ', '"' => '`'));
       if ($table['auto_increment']) {
-        $out .= " AUTO_INCREMENT=". $table['auto_increment'];
+        $out .= " AUTO_INCREMENT=" . $table['auto_increment'];
       }
       $out .= ";\n";
     }
     return $out;
   }
-  
+
   /**
    * Get the sql for the structure of the given table.
    */
-  function _get_view_create_sql($view) {
+  public function _get_view_create_sql($view) {
     $out = "";
-    // Switch SQL mode to get rid of "CREATE ALGORITHM..." what requires more permissions + troubles with the DEFINER user
+    // Switch SQL mode to get rid of "CREATE ALGORITHM..." what requires more permissions + troubles with the DEFINER user.
     $sql_mode = $this->query("SELECT @@SESSION.sql_mode")->fetchField();
     $this->query("SET sql_mode = 'ANSI'");
     $result = $this->query("SHOW CREATE VIEW `" . $view['name'] . "`", array(), array('fetch' => PDO::FETCH_ASSOC));
     $this->query("SET SQL_mode = :mode", array(':mode' => $sql_mode));
     foreach ($result as $create) {
-      $out .= "DROP VIEW IF EXISTS `". $view['name'] ."`;\n";
+      $out .= "DROP VIEW IF EXISTS `" . $view['name'] . "`;\n";
       $out .= "SET sql_mode = 'ANSI';\n";
       $out .= strtr($create['Create View'], "\n", " ") . ";\n";
       $out .= "SET sql_mode = '$sql_mode';\n";
@@ -365,67 +383,86 @@ class backup_migrate_destination_db_mysql extends backup_migrate_destination_db
   }
 
   /**
-   *  Get the sql to insert the data for a given table
+   * Get the sql to insert the data for a given table.
    */
-  function _dump_table_data_sql_to_file($file, $table) {
+  public function _dump_table_data_sql_to_file($file, $table) {
+    $rows_per_query = variable_get('backup_migrate_data_rows_per_query', 1000);
     $rows_per_line = variable_get('backup_migrate_data_rows_per_line', 30);
     $bytes_per_line = variable_get('backup_migrate_data_bytes_per_line', 2000);
-  
-    $lines = 0;
-    $data = $this->query("SELECT * FROM `". $table['name'] ."`", array(), array('fetch' => PDO::FETCH_ASSOC));
-    $rows = $bytes = 0;
 
-    // Escape backslashes, PHP code, special chars
+    if (variable_get('backup_migrate_verbose')) {
+      _backup_migrate_message('Table: %table', array('%table' => $table['name']), 'success');
+    }
+
+    // Escape backslashes, PHP code, special chars.
     $search = array('\\', "'", "\x00", "\x0a", "\x0d", "\x1a");
     $replace = array('\\\\', "''", '\0', '\n', '\r', '\Z');
-  
-    $line = array();
-    foreach ($data as $row) {
-      // DB Escape the values.
-      $items = array();
-      foreach ($row as $key => $value) {
-        $items[] = is_null($value) ? "null" : "'". str_replace($search, $replace, $value) ."'";
+
+    $lines = 0;
+    $from = 0;
+    $args = array('fetch' => PDO::FETCH_ASSOC);
+    while ($data = $this->query("SELECT * FROM `" . $table['name'] . "`", array(), $args, $from, $rows_per_query)) {
+      if ($data->rowCount() == 0) {
+        break;
       }
-  
-      // If there is a row to be added.
-      if ($items) {
-        // Start a new line if we need to.
-        if ($rows == 0) {
-          $file->write("INSERT INTO `". $table['name'] ."` VALUES ");
-          $bytes = $rows = 0;
-        }
-        // Otherwise add a comma to end the previous entry.
-        else {
-          $file->write(",");
+
+      $rows = $bytes = 0;
+
+      $line = array();
+      foreach ($data as $row) {
+        $from++;
+
+        // DB Escape the values.
+        $items = array();
+        foreach ($row as $key => $value) {
+          $items[] = is_null($value) ? "null" : "'" . str_replace($search, $replace, $value) . "'";
         }
-  
-        // Write the data itself.
-        $sql = implode(',', $items);
-        $file->write('('. $sql .')');
-        $bytes += strlen($sql);
-        $rows++;
-  
-        // Finish the last line if we've added enough items
-        if ($rows >= $rows_per_line || $bytes >= $bytes_per_line) {
-          $file->write(";\n");
-          $lines++;
-          $bytes = $rows = 0;
+
+        // If there is a row to be added.
+        if ($items) {
+          // Start a new line if we need to.
+          if ($rows == 0) {
+            $file->write("INSERT INTO `" . $table['name'] . "` VALUES ");
+            $bytes = $rows = 0;
+          }
+          // Otherwise add a comma to end the previous entry.
+          else {
+            $file->write(",");
+          }
+
+          // Write the data itself.
+          $sql = implode(',', $items);
+          $file->write('(' . $sql . ')');
+          $bytes += strlen($sql);
+          $rows++;
+
+          // Finish the last line if we've added enough items.
+          if ($rows >= $rows_per_line || $bytes >= $bytes_per_line) {
+            $file->write(";\n");
+            $lines++;
+            $bytes = $rows = 0;
+          }
         }
       }
+
+      // Finish any unfinished insert statements.
+      if ($rows > 0) {
+        $file->write(";\n");
+        $lines++;
+      }
     }
-    // Finish any unfinished insert statements.
-    if ($rows > 0) {
-      $file->write(";\n");
-      $lines++;
+
+    if (variable_get('backup_migrate_verbose')) {
+      _backup_migrate_message('Peak memory usage: %mem', array('%mem' => backup_migrate_get_peak_memory_usage() . 'MB'), 'success');
     }
-  
+
     return $lines;
   }
 
   /**
    * Get the db connection for the specified db.
    */
-  function _get_db_connection() {
+  public function _get_db_connection() {
     if (!$this->connection) {
       $this->connection = parent::_get_db_connection();
       // Set the sql mode because the default is ANSI,TRADITIONAL which is not aware of collation or storage engine.
@@ -435,11 +472,34 @@ class backup_migrate_destination_db_mysql extends backup_migrate_destination_db
   }
 
   /**
-   * Run a db query on this destination's db.
+   * Run a query on this destination's database using Drupal's MySQL engine.
+   *
+   * @param string $query
+   *   The query string.
+   * @param array $args
+   *   Arguments for the query.
+   * @param array $options
+   *   Options to pass to the query.
+   * @param int|null $from
+   *   The starting point for the query; when passed will perform a queryRange()
+   *   method instead of a regular query().
+   * @param int|null $count
+   *   The number of records to obtain from this query. Will be ignored if the
+   *   $from argument is empty.
+   *
+   * @see DatabaseConnection_mysql::query()
+   * @see DatabaseConnection_mysql::queryRange()
    */
-  function query($query, $args = array(), $options = array()) {
+  public function query($query, array $args = array(), array $options = array(), $from = NULL, $count = NULL) {
     if ($conn = $this->_get_db_connection()) {
-      return $conn->query($query, $args, $options);
+      // If no $from is passed in, just do a basic query.
+      if (is_null($from)) {
+        return $conn->query($query, $args, $options);
+      }
+      // The $from variable was passed in, so do a ranged query.
+      else {
+        return $conn->queryRange($query, $from, $count, $args, $options);
+      }
     }
   }
 
@@ -447,7 +507,7 @@ class backup_migrate_destination_db_mysql extends backup_migrate_destination_db
    * The header for the top of the sql dump file. These commands set the connection
    *  character encoding to help prevent encoding conversion issues.
    */
-  function _get_sql_file_header() {
+  public function _get_sql_file_header() {
     return "/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
 /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
 /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
@@ -459,11 +519,11 @@ SET NAMES utf8;
 
 ";
   }
-  
+
   /**
    * The footer of the sql dump file.
    */
-  function _get_sql_file_footer() {
+  public function _get_sql_file_footer() {
     return "
   
 /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
@@ -474,4 +534,5 @@ SET NAMES utf8;
 /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
 ";
   }
+
 }

+ 37 - 28
sites/all/modules/contrib/admin/backup_migrate/includes/destinations.email.inc

@@ -1,6 +1,5 @@
 <?php
 
-
 /**
  * @file
  * Functions to handle the email backup destination.
@@ -12,12 +11,12 @@
  * @ingroup backup_migrate_destinations
  */
 class backup_migrate_destination_email extends backup_migrate_destination {
-  var $supported_ops = array('scheduled backup', 'manual backup', 'remote backup', 'configure');
+  public $supported_ops = array('scheduled backup', 'manual backup', 'remote backup', 'configure');
 
   /**
    * Save to (ie. email the file) to the email destination.
    */
-  function save_file($file, $settings) {
+  public function save_file($file, $settings) {
     $size = filesize($file->filepath());
     $max = variable_get('backup_migrate_max_email_size', 20971520);
     if ($size > $max) {
@@ -34,14 +33,14 @@ class backup_migrate_destination_email extends backup_migrate_destination {
   /**
    * Get the form for the settings for this filter.
    */
-  function edit_form() {
+  public function edit_form() {
     $form = parent::edit_form();
     $form['location'] = array(
       "#type" => "textfield",
       "#title" => t("Email Address"),
       "#default_value" => $this->get_location(),
       "#required" => TRUE,
-      "#description" => t('Enter the email address to send the backup files to. Make sure the email sever can handle large file attachments'),
+      "#description" => t('Enter the email address to send the backup files to. Make sure the email server can handle large file attachments'),
     );
     return $form;
   }
@@ -49,11 +48,12 @@ class backup_migrate_destination_email extends backup_migrate_destination {
   /**
    * Validate the configuration form. Make sure the email address is valid.
    */
-  function settings_form_validate($values) {
+  public function settings_form_validate($values) {
     if (!valid_email_address($values['location'])) {
       form_set_error('[location]', t('The e-mail address %mail is not valid.', array('%mail' => $form_state['values']['location'])));
     }
   }
+
 }
 
 /**
@@ -72,28 +72,30 @@ class backup_migrate_destination_email extends backup_migrate_destination {
  *   filename and "filename" which is just the filename.
  */
 function _backup_migrate_destination_email_mail_backup($attachment, $to) {
-  // Send mail
+  // Send mail.
   $attach        = fread(fopen($attachment->path, "r"), filesize($attachment->path));
   $mail          = new mime_mail();
   $mail->from    = variable_get('site_mail', ini_get('sendmail_from'));
-  $mail->headers = 'Errors-To: [EMAIL='. $mail->from .']'. $mail->from .'[/EMAIL]';
+  $mail->headers = 'Errors-To: [EMAIL=' . $mail->from . ']' . $mail->from . '[/EMAIL]';
   $mail->to      = $to;
   $mail->subject = t('Database backup from !site: !file', array('!site' => variable_get('site_name', 'drupal'), '!file' => $attachment->filename));
-  $mail->body    = t('Database backup attached.') ."\n\n";
+  $mail->body    = t('Database backup attached.') . "\n\n";
 
   $mail->add_attachment("$attach", $attachment->filename, "Content-Transfer-Encoding: base64 /9j/4AAQSkZJRgABAgEASABIAAD/7QT+UGhvdG9zaG", NULL, TRUE);
   $mail->send();
 }
-
+/**
+ *
+ */
 class mime_mail {
-  var $parts;
-  var $to;
-  var $from;
-  var $headers;
-  var $subject;
-  var $body;
+  public $parts;
+  public $to;
+  public $from;
+  public $headers;
+  public $subject;
+  public $body;
 
-  function mime_mail() {
+  public function __construct() {
     $this->parts   = array();
     $this->to      = "";
     $this->from    = "";
@@ -102,7 +104,7 @@ class mime_mail {
     $this->body    = "";
   }
 
-  function add_attachment($message, $name = "", $ctype = "application/octet-stream", $encode = NULL, $attach = FALSE) {
+  public function add_attachment($message, $name = "", $ctype = "application/octet-stream", $encode = NULL, $attach = FALSE) {
     $this->parts[] = array(
       "ctype" => $ctype,
       "message" => $message,
@@ -112,29 +114,36 @@ class mime_mail {
     );
   }
 
-  function build_message($part) {
+  public function build_message($part) {
     $message  = $part["message"];
     $message  = chunk_split(base64_encode($message));
     $encoding = "base64";
     $disposition = $part['attach'] ? "Content-Disposition: attachment; filename=$part[name]\n" : '';
-    return "Content-Type: ". $part["ctype"] . ($part["name"] ? "; name = \"". $part["name"] ."\"" : "") ."\nContent-Transfer-Encoding: $encoding\n$disposition\n$message\n";
+    return "Content-Type: " . $part["ctype"] . ($part["name"] ? "; name = \"" . $part["name"] . "\"" : "") . "\nContent-Transfer-Encoding: $encoding\n$disposition\n$message\n";
   }
 
-  function build_multipart() {
-    $boundary = "b". md5(uniqid(time()));
+  public function build_multipart() {
+    $boundary = "b" . md5(uniqid(time()));
     $multipart = "Content-Type: multipart/mixed; boundary = $boundary\n\nThis is a MIME encoded message.\n\n--$boundary";
     for ($i = sizeof($this->parts) - 1; $i >= 0; $i--) {
-      $multipart .= "\n". $this->build_message($this->parts[$i]) ."--$boundary";
+      $multipart .= "\n" . $this->build_message($this->parts[$i]) . "--$boundary";
     }
     return $multipart .= "--\n";
   }
 
-  function send() {
+  public function send() {
     $mime = "";
-    if (!empty($this->from)) $mime .= "From: ". $this->from ."\n";
-    if (!empty($this->headers)) $mime .= $this->headers ."\n";
-    if (!empty($this->body)) $this->add_attachment($this->body, "", "text/plain");
-    $mime .= "MIME-Version: 1.0\n". $this->build_multipart();
+    if (!empty($this->from)) {
+      $mime .= "From: " . $this->from . "\n";
+    }
+    if (!empty($this->headers)) {
+      $mime .= $this->headers . "\n";
+    }
+    if (!empty($this->body)) {
+      $this->add_attachment($this->body, "", "text/plain");
+    }
+    $mime .= "MIME-Version: 1.0\n" . $this->build_multipart();
     mail(trim($this->to), $this->subject, "", $mime);
   }
+
 }

+ 50 - 43
sites/all/modules/contrib/admin/backup_migrate/includes/destinations.file.inc

@@ -1,6 +1,5 @@
 <?php
 
-
 /**
  * @file
  * A destination type for saving locally to the server.
@@ -11,34 +10,40 @@
  *
  * @ingroup backup_migrate_destinations
  */
-
 class backup_migrate_destination_files extends backup_migrate_destination {
-  var $supported_ops = array('scheduled backup', 'manual backup', 'local backup', 'restore', 'list files', 'configure', 'delete');
+  public $supported_ops = array('scheduled backup', 'manual backup', 'local backup', 'restore', 'list files', 'configure', 'delete');
 
-  function type_name() {
+  public function type_name() {
     return t("Server Directory");
   }
 
   /**
    * Get the file location.
    */
-  function get_realpath() {
-    return drupal_realpath($this->get_location());
+  public function get_realpath() {
+    if ($realpath = drupal_realpath($this->get_location())) {
+      return $realpath;
+    }
+    return $this->get_location();
   }
 
   /**
    * File save destination callback.
    */
-  function _save_file($file, $settings) {
+  public function _save_file($file, $settings) {
     if ($this->confirm_destination() && $dir = $this->get_location()) {
-      $filepath = rtrim($dir, "/") ."/". $file->filename();
+      $filepath = rtrim($dir, "/") . "/" . $file->filename();
+
+      // Allow files to be overwritten by the filesystem.
+      $replace_method = $settings->append_timestamp == 2 ? FILE_EXISTS_REPLACE : FILE_EXISTS_RENAME;
+
       // Copy the file if there are multiple destinations.
       if (count($settings->get_destinations()) > 1) {
-        file_unmanaged_copy($file->filepath(), $filepath);
+        file_unmanaged_copy($file->filepath(), $filepath, $replace_method);
       }
       // Otherwise we can move it and save a delete.
       else {
-        file_unmanaged_move($file->filepath(), $filepath);
+        file_unmanaged_move($file->filepath(), $filepath, $replace_method);
       }
 
       // chmod, chown and chgrp the file if needed.
@@ -59,14 +64,14 @@ class backup_migrate_destination_files extends backup_migrate_destination {
   /**
    * Determine if we can read the given file.
    */
-  function can_read_file($file_id) {
+  public function can_read_file($file_id) {
     return $this->op('restore') && is_readable($this->get_filepath($file_id));
   }
 
   /**
    * File load destination callback.
    */
-  function load_file($file_id) {
+  public function load_file($file_id) {
     $filepath = $this->get_filepath($file_id);
     if (file_exists($filepath)) {
       backup_migrate_include('files');
@@ -77,7 +82,7 @@ class backup_migrate_destination_files extends backup_migrate_destination {
   /**
    * Get the file object for the given file.
    */
-  function get_file($file_id) {
+  public function get_file($file_id) {
     $files = $this->list_files();
     if (isset($files[$file_id])) {
       isset($files[$file_id]);
@@ -88,15 +93,15 @@ class backup_migrate_destination_files extends backup_migrate_destination {
   /**
    * File list destination callback.
    */
-  function _list_files() {
+  public function _list_files() {
     $files = array();
     if ($dir = $this->get_realpath()) {
       if ($handle = @opendir($dir)) {
         backup_migrate_include('files');
         while (FALSE !== ($file = readdir($handle))) {
           if (substr($file, 0, 1) !== '.') {
-            $filepath = $dir ."/". $file;
-            $files[$file] = new backup_file(array('filepath' => $filepath));          
+            $filepath = $dir . "/" . $file;
+            $files[$file] = new backup_file(array('filepath' => $filepath));
           }
         }
       }
@@ -107,7 +112,7 @@ class backup_migrate_destination_files extends backup_migrate_destination {
   /**
    * File delete destination callback.
    */
-  function _delete_file($file_id) {
+  public function _delete_file($file_id) {
     $filepath = $this->get_filepath($file_id);
     file_unmanaged_delete($filepath);
   }
@@ -115,9 +120,9 @@ class backup_migrate_destination_files extends backup_migrate_destination {
   /**
    * Get the filepath from the given file id.
    */
-  function get_filepath($file_id) {
+  public function get_filepath($file_id) {
     if ($dir = $this->get_realpath()) {
-      $filepath = rtrim($dir, '/') .'/'. $file_id;
+      $filepath = rtrim($dir, '/') . '/' . $file_id;
       return $filepath;
     }
     return FALSE;
@@ -126,7 +131,7 @@ class backup_migrate_destination_files extends backup_migrate_destination {
   /**
    * Get the form for the settings for the files destination.
    */
-  function edit_form() {
+  public function edit_form() {
     $form = parent::edit_form();
     $form['location'] = array(
       "#type" => "textfield",
@@ -166,7 +171,7 @@ class backup_migrate_destination_files extends backup_migrate_destination {
   /**
    * Validate the form for the settings for the files destination.
    */
-  function edit_form_validate($form, &$form_state) {
+  public function edit_form_validate($form, &$form_state) {
     $values = $form_state['values'];
     if (isset($values['settings']['chmod']) && !empty($values['settings']['chmod']) && !preg_match('/0?[0-7]{3}/', $values['settings']['chmod'])) {
       form_set_error('chmod', t('You must enter a valid chmod octal value (e.g. 644 or 0644) in the change mode field, or leave it blank.'));
@@ -177,7 +182,7 @@ class backup_migrate_destination_files extends backup_migrate_destination {
   /**
    * Submit the form for the settings for the files destination.
    */
-  function edit_form_submit($form, &$form_state) {
+  public function edit_form_submit($form, &$form_state) {
     // Add a 0 to the start of a 3 digit file mode to make it proper PHP encoded octal.
     if (strlen($form_state['values']['settings']['chmod']) == 3) {
       $form_state['values']['settings']['chmod'] = '0' . $form_state['values']['settings']['chmod'];
@@ -188,7 +193,7 @@ class backup_migrate_destination_files extends backup_migrate_destination {
   /**
    * Check that a destination is valid.
    */
-  function confirm_destination() {
+  public function confirm_destination() {
     if ($dir = $this->get_location()) {
       return $this->check_dir($dir);
     }
@@ -198,10 +203,10 @@ class backup_migrate_destination_files extends backup_migrate_destination {
   /**
    * Prepare the destination directory for the backups.
    */
-  function check_dir($directory) {
+  public function check_dir($directory) {
     if (!file_prepare_directory($directory, FILE_CREATE_DIRECTORY)) {
       // Unable to create destination directory.
-      _backup_migrate_message("Unable to create or write to the save directory '%directory'. Please check the file permissions that directory and try again.", array('%directory' => $directory), "error");
+      _backup_migrate_message("Unable to create or write to the save directory '%directory'. Please check the file permissions of that directory and try again.", array('%directory' => $directory), "error");
       return FALSE;
     }
 
@@ -216,25 +221,25 @@ class backup_migrate_destination_files extends backup_migrate_destination {
   /**
    * Check that a web accessible directory has been properly secured, othewise attempt to secure it.
    */
-  function check_web_dir($directory) {
+  public function check_web_dir($directory) {
     // Check if the file has already been tested.
-    if (is_file($directory .'/tested.txt')) {
+    if (is_file($directory . '/tested.txt')) {
       return $directory;
     }
     else {
       file_create_htaccess($directory, TRUE);
-  
+
       // Check the user agent to make sure we're not responding to a request from drupal itself.
       // That should prevent infinite loops which could be caused by poormanscron in some circumstances.
       if (strpos($_SERVER['HTTP_USER_AGENT'], 'Drupal') !== FALSE) {
         return FALSE;
       }
-  
-      // Check to see if the destination is publicly accessible
+
+      // Check to see if the destination is publicly accessible.
       $test_contents = "this file should not be publicly accessible";
       // Create the the text.txt file if it's not already there.
-      if (!is_file($directory .'/test.txt') || file_get_contents($directory .'/test.txt') != $test_contents) {
-        if ($fp = fopen($directory .'/test.txt', 'w')) {
+      if (!is_file($directory . '/test.txt') || file_get_contents($directory . '/test.txt') != $test_contents) {
+        if ($fp = fopen($directory . '/test.txt', 'w')) {
           @fputs($fp, $test_contents);
           fclose($fp);
         }
@@ -244,16 +249,16 @@ class backup_migrate_destination_files extends backup_migrate_destination {
           return FALSE;
         }
       }
-  
+
       // Attempt to read the test file via http. This may fail for other reasons,
       // so it's not a bullet-proof check.
-      if ($this->test_file_readable_remotely($directory .'/test.txt', $test_contents)) {
+      if ($this->test_file_readable_remotely($directory . '/test.txt', $test_contents)) {
         $message = t("Security notice: Backup and Migrate will not save backup files to the server because the destination directory is publicly accessible. If you want to save files to the server, please secure the '%directory' directory", array('%directory' => $directory));
         drupal_set_message($message, "error");
         return FALSE;
       }
       // Directory tested OK, so we mark it as tested.
-      if ($fp = fopen($directory .'/tested.txt', 'w')) {
+      if ($fp = fopen($directory . '/tested.txt', 'w')) {
         $contents = t('The presence of this file indicates that this directory has been tested as safe to use as a destination for Backup and Migrate. If you change the permissions of this directory or change your web server settings, please delete this file so that the directory can be checked again.');
         @fputs($fp, $contents);
         fclose($fp);
@@ -265,7 +270,7 @@ class backup_migrate_destination_files extends backup_migrate_destination {
   /**
    * Check if the given directory is within the webroot and is therefore web accessible.
    */
-  function dir_in_webroot($directory) {
+  public function dir_in_webroot($directory) {
     $real_dir = drupal_realpath($directory);
     $real_root = drupal_realpath(DRUPAL_ROOT);
     if ($real_dir == $real_root || strpos($real_dir, $real_root . '/') === 0) {
@@ -274,10 +279,10 @@ class backup_migrate_destination_files extends backup_migrate_destination {
     return FALSE;
   }
 
-/**
+  /**
    * Check if a file can be read remotely via http.
    */
-  function test_file_readable_remotely($directory, $contents) {
+  public function test_file_readable_remotely($directory, $contents) {
     $real_dir = drupal_realpath($directory);
     $real_root = drupal_realpath(DRUPAL_ROOT);
     if ($real_dir && $real_root) {
@@ -292,27 +297,29 @@ class backup_migrate_destination_files extends backup_migrate_destination {
     }
     return FALSE;
   }
+
 }
 
 /**
  * The manual files directory.
  */
 class backup_migrate_destination_files_manual extends backup_migrate_destination_files {
-  var $supported_ops = array('manual backup', 'restore', 'list files', 'configure', 'delete');
-  function __construct($params = array()) {
+  public $supported_ops = array('manual backup', 'restore', 'list files', 'configure', 'delete');
+  public function __construct($params = array()) {
     $dir = 'private://backup_migrate/manual';
     parent::__construct($params + array('location' => $dir, 'name' => t('Manual Backups Directory')));
   }
+
 }
 
 /**
  * The scheduled files directory.
  */
 class backup_migrate_destination_files_scheduled extends backup_migrate_destination_files {
-  var $supported_ops = array('scheduled backup', 'restore', 'list files', 'configure', 'delete');
-  function __construct($params = array()) {
+  public $supported_ops = array('scheduled backup', 'restore', 'list files', 'configure', 'delete');
+  public function __construct($params = array()) {
     $dir = 'private://backup_migrate/scheduled';
     parent::__construct($params + array('location' => $dir, 'name' => t('Scheduled Backups Directory')));
   }
-}
 
+}

+ 55 - 61
sites/all/modules/contrib/admin/backup_migrate/includes/destinations.ftp.inc

@@ -11,13 +11,13 @@
  * @ingroup backup_migrate_destinations
  */
 class backup_migrate_destination_ftp extends backup_migrate_destination_remote {
-  var $supported_ops = array('scheduled backup', 'manual backup', 'remote backup', 'restore', 'list files', 'configure', 'delete');
-  var $ftp = NULL;
+  public $supported_ops = array('scheduled backup', 'manual backup', 'remote backup', 'restore', 'list files', 'configure', 'delete');
+  public $ftp = NULL;
 
   /**
    * Save to the ftp destination.
    */
-  function _save_file($file, $settings) {
+  public function _save_file($file, $settings) {
     $ftp = $this->ftp_object();
     if (drupal_ftp_file_to_ftp($file->filepath(), $file->filename(), '.', $ftp)) {
       return $file;
@@ -28,7 +28,7 @@ class backup_migrate_destination_ftp extends backup_migrate_destination_remote {
   /**
    * Load from the ftp destination.
    */
-  function load_file($file_id) {
+  public function load_file($file_id) {
     backup_migrate_include('files');
     $file = new backup_file(array('filename' => $file_id));
     $this->ftp_object();
@@ -41,16 +41,16 @@ class backup_migrate_destination_ftp extends backup_migrate_destination_remote {
   /**
    * Delete from the ftp destination.
    */
-  function _delete_file($file_id) {
+  public function _delete_file($file_id) {
     $this->ftp_object();
     drupal_ftp_delete_file($file_id, $this->ftp);
   }
 
-  function _list_files() {
+  public function _list_files() {
     backup_migrate_include('files');
     $files = array();
     $this->ftp_object();
-    $ftp_files = (array)drupal_ftp_file_list('.', $this->ftp);
+    $ftp_files = (array) drupal_ftp_file_list('.', $this->ftp);
     foreach ($ftp_files as $file) {
       $files[$file['filename']] = new backup_file($file);
     }
@@ -60,7 +60,7 @@ class backup_migrate_destination_ftp extends backup_migrate_destination_remote {
   /**
    * Get the form for the settings for this filter.
    */
-  function edit_form() {
+  public function edit_form() {
     $form = parent::edit_form();
     $form['scheme']['#type'] = 'value';
     $form['scheme']['#value'] = 'ftp';
@@ -79,15 +79,15 @@ class backup_migrate_destination_ftp extends backup_migrate_destination_remote {
     return $form;
   }
 
-  function set_pasv($value) {
-    $this->settings['pasv'] = (bool)$value;
+  public function set_pasv($value) {
+    $this->settings['pasv'] = (bool) $value;
   }
 
-  function get_pasv() {
+  public function get_pasv() {
     return isset($this->settings['pasv']) ? $this->settings['pasv'] : FALSE;
   }
 
-  function ftp_object() {
+  public function ftp_object() {
     if (!$this->ftp) {
       $this->dest_url['port'] = empty($this->dest_url['port']) ? '21' : $this->dest_url['port'];
       $this->dest_url['pasv'] = $this->get_pasv();
@@ -95,19 +95,18 @@ class backup_migrate_destination_ftp extends backup_migrate_destination_remote {
     }
     return $this->ftp;
   }
+
 }
 
 // The FTP code below was taken from the ftp module by Aaron Winborn.
-
 // Inspired by http://www.devarticles.com/c/a/PHP/My-FTP-Wrapper-Class-for-PHP/
 // It's been drupalized, however, and most of the bugs from that example have been fixed.
 // - winborn 2007-06-22 - 2007-06-28
-
 define('DRUPAL_FTP_FT_DIRECTORY', 0);
 define('DRUPAL_FTP_FT_FILE', 1);
 
 /**
- *  creates a new ftp object. if any elements of ftp_map are missing, they'll be filled with the server defaults.
+ * Creates a new ftp object. if any elements of ftp_map are missing, they'll be filled with the server defaults.
  */
 function drupal_ftp_ftp_object($server, $port, $user, $pass, $dir, $pasv) {
   $ftp = new stdClass();
@@ -123,17 +122,17 @@ function drupal_ftp_ftp_object($server, $port, $user, $pass, $dir, $pasv) {
 }
 
 /**
- *  The drupal_ftp_connect function
+ * The drupal_ftp_connect function
  *  This function connects to an FTP server and attempts to change into the directory specified by
  *  the fourth parameter, $directory.
  */
 function drupal_ftp_connect(&$ftp) {
-  if (is_NULL($ftp)) {
+  if (is_null($ftp)) {
     $ftp = drupal_ftp_ftp_object();
   }
 
   if (!$ftp->__conn && !drupal_ftp_connected($ftp)) {
-    // Attempt to connect to the remote server
+    // Attempt to connect to the remote server.
     $ftp->__conn = @ftp_connect($ftp->__server, $ftp->__port);
 
     if (!$ftp->__conn) {
@@ -141,7 +140,7 @@ function drupal_ftp_connect(&$ftp) {
       return FALSE;
     }
 
-    // Attempt to login to the remote server
+    // Attempt to login to the remote server.
     $ftp->__login = @ftp_login($ftp->__conn, $ftp->__user, $ftp->__password);
 
     if (!$ftp->__login) {
@@ -149,7 +148,7 @@ function drupal_ftp_connect(&$ftp) {
       return FALSE;
     }
 
-    // Attempt to change into the working directory
+    // Attempt to change into the working directory.
     $chdir = @ftp_chdir($ftp->__conn, $ftp->__directory);
 
     if (!$chdir) {
@@ -157,7 +156,7 @@ function drupal_ftp_connect(&$ftp) {
       return FALSE;
     }
 
-    // Set PASV - if needed
+    // Set PASV - if needed.
     if ($ftp->__pasv) {
       $pasv = @ftp_pasv($ftp->__conn, TRUE);
       if (!$pasv) {
@@ -167,7 +166,7 @@ function drupal_ftp_connect(&$ftp) {
     }
   }
 
-  // Everything worked OK, return TRUE
+  // Everything worked OK, return TRUE.
   return TRUE;
 }
 
@@ -178,38 +177,36 @@ function drupal_ftp_connect(&$ftp) {
  */
 function drupal_ftp_connected(&$ftp) {
   // Attempt to call the ftp_systype to see if the connect
-  // to the FTP server is still alive and kicking
-
-  if (is_NULL($ftp)) {
+  // to the FTP server is still alive and kicking.
+  if (is_null($ftp)) {
     $ftp = drupal_ftp_ftp_object();
     return FALSE;
   }
 
   if (!@ftp_systype($ftp->__conn)) {
-    // The connection is dead
+    // The connection is dead.
     return FALSE;
   }
   else {
-    // The connection is still alive
+    // The connection is still alive.
     return TRUE;
   }
 }
 
 /**
- *  This function tries to retrieve the contents of a file from the FTP server.
+ * This function tries to retrieve the contents of a file from the FTP server.
  *  Firstly it changes into the $directory directory, and then attempts to download the file $filename.
  *  The file is saved locally and its contents are returned to the caller of the function.
  */
 function drupal_ftp_ftp_to_file($file, $filename, $directory, &$ftp) {
   // Change into the remote directory and retrieve the content
-  // of a file. Once retrieve, return this value to the caller
-
+  // of a file. Once retrieve, return this value to the caller.
   if (!@drupal_ftp_connect($ftp)) {
     return FALSE;
   }
 
   // We are now connected, so let's retrieve the file contents.
-  // Firstly, we change into the directory
+  // Firstly, we change into the directory.
   $chdir = @ftp_chdir($ftp->__conn, $directory);
 
   if (!$chdir) {
@@ -217,7 +214,7 @@ function drupal_ftp_ftp_to_file($file, $filename, $directory, &$ftp) {
     return FALSE;
   }
 
-  // We have changed into the directory, let's attempt to get the file
+  // We have changed into the directory, let's attempt to get the file.
   $fp = @fopen($file, 'wb');
   $get_file = @ftp_fget($ftp->__conn, $fp, $filename, FTP_BINARY);
   fclose($fp);
@@ -228,7 +225,7 @@ function drupal_ftp_ftp_to_file($file, $filename, $directory, &$ftp) {
     _backup_migrate_message('FTP Error: Unable to download file: @filename from @directory', array('@filename' => $filename, '@directory' => $directory), 'error');
     return FALSE;
   }
-  
+
   return TRUE;
 }
 
@@ -241,15 +238,15 @@ function drupal_ftp_file_to_ftp($file, $ftp_filename, $ftp_directory, &$ftp) {
   }
 
   if ($source = drupal_realpath($file)) {
-    // Now we can try to write to the remote file
-    $complete_filename = $ftp_directory .'/'. $ftp_filename;
+    // Now we can try to write to the remote file.
+    $complete_filename = $ftp_directory . '/' . $ftp_filename;
     $put_file = @ftp_put($ftp->__conn, $complete_filename, $source, FTP_BINARY);
     if (!$put_file) {
       _backup_migrate_message('FTP Error: Couldn\'t write to @complete_filename when trying to save file on the ftp server.', array('@complete_filename' => $complete_filename), 'error');
       return FALSE;
     }
 
-    // Everything worked OK
+    // Everything worked OK.
     return TRUE;
   }
   else {
@@ -259,19 +256,18 @@ function drupal_ftp_file_to_ftp($file, $ftp_filename, $ftp_directory, &$ftp) {
 }
 
 /**
- *  The drupal_ftp_change_directory Function
+ * The drupal_ftp_change_directory Function
  *  This function simply changes into the $directory folder on the FTP server.
  *  If a connection or permission error occurs then _backup_migrate_message() will contain the error message.
  */
 function drupal_ftp_change_directory($directory, &$ftp) {
   // Switch to another directory on the web server. If we don't
-  // have permissions then an error will occur
-
+  // have permissions then an error will occur.
   if (!@drupal_ftp_connect($ftp)) {
     return FALSE;
   }
 
-  // Try and change into another directory
+  // Try and change into another directory.
   $chdir = ftp_chdir($ftp->__conn, $directory);
 
   if (!$chdir) {
@@ -279,21 +275,20 @@ function drupal_ftp_change_directory($directory, &$ftp) {
     return FALSE;
   }
   else {
-    // Changing directories worked OK
+    // Changing directories worked OK.
     return TRUE;
   }
 }
 
 /**
- *  The drupal_ftp_file_list Function
+ * The drupal_ftp_file_list Function
  *  This function will change into the $directory folder and get a list of files and directories contained in that folder.
  *  This function still needs a lot of work, but should work in most cases.
  */
 function drupal_ftp_file_list($directory, &$ftp) {
   // This function will attempt to change into the specified
   // directory and retrieve a list of files as an associative
-  // array. This list will include file name, size and date last modified
-
+  // array. This list will include file name, size and date last modified.
   $file_array = array();
 
   // Can we switch to the desired directory?
@@ -305,14 +300,14 @@ function drupal_ftp_file_list($directory, &$ftp) {
   // This is slower than parsing the raw return values, but it is faster.
   $file_list = ftp_nlist($ftp->__conn, $directory);
 
-  // Save the list of files
+  // Save the list of files.
   if (@is_array($file_list)) {
-    // Interate through the array
+    // Interate through the array.
     foreach ($file_list as $file) {
       $file_array[] = array(
         'filename' => $file,
-        'filesize' => ftp_size($ftp->__conn, $directory ."/". $file),
-        'filetime' => ftp_mdtm($ftp->__conn, $directory ."/". $file),
+        'filesize' => ftp_size($ftp->__conn, $directory . "/" . $file),
+        'filetime' => ftp_mdtm($ftp->__conn, $directory . "/" . $file),
       );
     }
   }
@@ -321,13 +316,12 @@ function drupal_ftp_file_list($directory, &$ftp) {
 }
 
 /**
- *  The drupal_ftp_create_directory Function
+ * The drupal_ftp_create_directory Function
  *  This function tries to make a new directory called $folder_name on the FTP server.
  *  If it can create the folder, then the folder is given appropriate rights with the CHMOD command.
  */
 function drupal_ftp_create_directory($folder_name, &$ftp) {
-  // Makes a new folder on the web server via FTP
-
+  // Makes a new folder on the web server via FTP.
   if (!@drupal_ftp_connect($ftp)) {
     return FALSE;
   }
@@ -336,7 +330,7 @@ function drupal_ftp_create_directory($folder_name, &$ftp) {
 
   if ($create_result == TRUE) {
     // Can we change the files permissions?
-    $exec_result = @ftp_site($ftp->__conn, 'chmod 0777 '. $folder_name .'/');
+    $exec_result = @ftp_site($ftp->__conn, 'chmod 0777 ' . $folder_name . '/');
 
     if ($exec_result == TRUE) {
       return TRUE;
@@ -353,11 +347,11 @@ function drupal_ftp_create_directory($folder_name, &$ftp) {
 }
 
 /**
- *  The drupal_ftp_delete_file Function
+ * The drupal_ftp_delete_file Function
  *  This function attempts to delete a file called $filename from the FTP server.
  */
 function drupal_ftp_delete_file($filename, &$ftp) {
-  // Remove the specified file from the FTP server
+  // Remove the specified file from the FTP server.
   if (!@drupal_ftp_connect($ftp)) {
     return FALSE;
   }
@@ -365,18 +359,18 @@ function drupal_ftp_delete_file($filename, &$ftp) {
   $delete_result = @ftp_delete($ftp->__conn, $filename);
 
   if ($delete_result == TRUE) {
-    // The file/folder was renamed successfully
+    // The file/folder was renamed successfully.
     return TRUE;
   }
   else {
-    // Couldn't delete the selected file
+    // Couldn't delete the selected file.
     _backup_migrate_message('FTP Error: Couldn\'t delete the selected file: @filename', array('@filename' => $filename), 'error');
     return FALSE;
   }
 }
 
 /**
- *  The drupal_ftp_delete_folder Function
+ * The drupal_ftp_delete_folder Function
  *  This function was one of the hardest to write. It recursively deletes all files and folders from a directory called $folder_name.
  */
 function drupal_ftp_delete_folder($folder_name, &$ftp) {
@@ -395,16 +389,16 @@ function drupal_ftp_delete_folder($folder_name, &$ftp) {
 
   for ($i = 0; $i < sizeof($content); $i++) {
     // If we can change into this then it's a directory.
-    // If not, it's a file
+    // If not, it's a file.
     if ($content[$i] != "." && $content[$i] != "..") {
       if (@ftp_chdir($ftp->__conn, $content[$i])) {
-        // We have a directory
+        // We have a directory.
         $directories[] = $content[$i];
         $dir_counter++;
         @ftp_cdup($ftp->__conn);
       }
       else {
-        // We have a file
+        // We have a file.
         $files[] = $content[$i];
         $file_counter++;
       }
@@ -419,7 +413,7 @@ function drupal_ftp_delete_folder($folder_name, &$ftp) {
     if ($directories[$j] != "." OR $directories[$j] != "..") {
       $location = ftp_pwd($ftp->__conn);
       drupal_ftp_delete_folder($directories[$j], $ftp);
-      @ftp_cdup ($ftp->__conn);
+      @ftp_cdup($ftp->__conn);
       @ftp_rmdir($ftp->__conn, $directories[$j]);
     }
   }

+ 115 - 121
sites/all/modules/contrib/admin/backup_migrate/includes/destinations.inc

@@ -1,6 +1,5 @@
 <?php
 
-
 /**
  * @file
  * All of the destination handling code needed for Backup and Migrate.
@@ -26,19 +25,19 @@ function backup_migrate_backup_migrate_destination_subtypes() {
     $out += array(
       'file' => array(
         'description' => t('Save the backup files to any directory on this server which the web-server can write to.'),
-        'file' => drupal_get_path('module', 'backup_migrate') .'/includes/destinations.file.inc',
+        'file' => drupal_get_path('module', 'backup_migrate') . '/includes/destinations.file.inc',
         'class' => 'backup_migrate_destination_files',
         'type_name' => t('Server Directory'),
         'local' => TRUE,
         'can_create' => TRUE,
       ),
       'file_manual' => array(
-        'file' => drupal_get_path('module', 'backup_migrate') .'/includes/destinations.file.inc',
+        'file' => drupal_get_path('module', 'backup_migrate') . '/includes/destinations.file.inc',
         'type_name' => t('Server Directory'),
         'class' => 'backup_migrate_destination_files_manual',
       ),
       'file_scheduled' => array(
-        'file' => drupal_get_path('module', 'backup_migrate') .'/includes/destinations.file.inc',
+        'file' => drupal_get_path('module', 'backup_migrate') . '/includes/destinations.file.inc',
         'type_name' => t('Server Directory'),
         'class' => 'backup_migrate_destination_files_scheduled',
       ),
@@ -46,16 +45,16 @@ function backup_migrate_backup_migrate_destination_subtypes() {
   }
   $out += array(
     'browser_download' => array(
-      'file' => drupal_get_path('module', 'backup_migrate') .'/includes/destinations.browser.inc',
+      'file' => drupal_get_path('module', 'backup_migrate') . '/includes/destinations.browser.inc',
       'class' => 'backup_migrate_destination_browser_download',
     ),
     'browser_upload' => array(
-      'file' => drupal_get_path('module', 'backup_migrate') .'/includes/destinations.browser.inc',
+      'file' => drupal_get_path('module', 'backup_migrate') . '/includes/destinations.browser.inc',
       'class' => 'backup_migrate_destination_browser_upload',
     ),
     'nodesquirrel' => array(
-      'description' => t('Save the backup files to the NodeSquirrel.com backup service.'),
-      'file' => drupal_get_path('module', 'backup_migrate') .'/includes/destinations.nodesquirrel.inc',
+      'description' => t('Save the backup files to the <a href="@link" target="_blank">NodeSquirrel</a> backup service.', array('@link' => url('http://nodesquirrel.com'))),
+      'file' => drupal_get_path('module', 'backup_migrate') . '/includes/destinations.nodesquirrel.inc',
       'class' => 'backup_migrate_destination_nodesquirrel',
       'type_name' => t('NodeSquirrel.com'),
       'can_create' => TRUE,
@@ -63,15 +62,15 @@ function backup_migrate_backup_migrate_destination_subtypes() {
     ),
     'ftp' => array(
       'description' => t('Save the backup files to any a directory on an FTP server.'),
-      'file' => drupal_get_path('module', 'backup_migrate') .'/includes/destinations.ftp.inc',
+      'file' => drupal_get_path('module', 'backup_migrate') . '/includes/destinations.ftp.inc',
       'class' => 'backup_migrate_destination_ftp',
       'type_name' => t('FTP Directory'),
       'can_create' => TRUE,
       'remote' => TRUE,
     ),
     's3' => array(
-      'description' => t('Save the backup files to a bucket on your !link.', array('!link' => l(t('Amazon S3 account'), 'http://aws.amazon.com/s3/'))),
-      'file' => drupal_get_path('module', 'backup_migrate') .'/includes/destinations.s3.inc',
+      'description' => t('Save the backup files to a bucket on your <a href="@link" target="_blank">Amazon S3 account</a>.', array('@link' => url('http://aws.amazon.com/s3/'))),
+      'file' => drupal_get_path('module', 'backup_migrate') . '/includes/destinations.s3.inc',
       'class' => 'backup_migrate_destination_s3',
       'type_name' => t('Amazon S3 Bucket'),
       'can_create' => TRUE,
@@ -80,7 +79,7 @@ function backup_migrate_backup_migrate_destination_subtypes() {
     'email' => array(
       'type_name' => t('Email'),
       'description' => t('Send the backup as an email attachment to the specified email address.'),
-      'file' => drupal_get_path('module', 'backup_migrate') .'/includes/destinations.email.inc',
+      'file' => drupal_get_path('module', 'backup_migrate') . '/includes/destinations.email.inc',
       'class' => 'backup_migrate_destination_email',
       'can_create' => TRUE,
       'remote' => TRUE,
@@ -134,7 +133,7 @@ function backup_migrate_backup_migrate_destinations() {
  *    'all' - all available destinations should be returned
  */
 function backup_migrate_get_destinations($op = 'all') {
-  static $destinations = NULL;
+  $destinations = &drupal_static('backup_migrate_get_destinations', NULL);
 
   // Get the list of destinations and cache them locally.
   if ($destinations === NULL) {
@@ -190,7 +189,7 @@ function backup_migrate_destination_get_latest_file($destination_id) {
   if ($destination = backup_migrate_get_destination($destination_id)) {
     $files = $destination->list_files();
     $max = 0;
-    foreach ((array)$files as $file) {
+    foreach ((array) $files as $file) {
       $info = $file->info();
 
       // If there's a datestamp, it should override the filetime as it's probably more reliable.
@@ -276,7 +275,7 @@ function _backup_migrate_destination_get_file_links($destination_id, $file_id) {
  * List the backup files in the given destination.
  */
 function backup_migrate_ui_destination_display_files($destination_id = NULL) {
-  drupal_add_css(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.css');
+  drupal_add_css(drupal_get_path('module', 'backup_migrate') . '/backup_migrate.css');
 
   $rows = $sort = array();
   if ($destination = backup_migrate_get_destination($destination_id)) {
@@ -302,7 +301,7 @@ function _backup_migrate_ui_destination_display_files($destination = NULL, $limi
 
     // Get the fetch link.
     if ($destination->cache_files && $destination->fetch_time) {
-      $fetch = '<div class="description">'. t('This listing was fetched !time ago. !refresh', array('!time' => format_interval(time() - $destination->fetch_time, 1), '!refresh' => l(t('fetch now'), $_GET['q'], array('query' => array('refresh' => 'true'))))) .'</div>';
+      $fetch = '<div class="description">' . t('This listing was fetched !time ago. !refresh', array('!time' => format_interval(time() - $destination->fetch_time, 1), '!refresh' => l(t('fetch now'), $_GET['q'], array('query' => array('refresh' => 'true'))))) . '</div>';
     }
 
     $out .= $fetch;
@@ -317,9 +316,9 @@ function _backup_migrate_ui_destination_display_files($destination = NULL, $limi
  * List the backup files in the given destination.
  */
 function _backup_migrate_ui_destination_display_file_list($files, $options = array()) {
-  drupal_add_css(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.css');
+  drupal_add_css(drupal_get_path('module', 'backup_migrate') . '/backup_migrate.css');
 
-  // Set soem default options
+  // Set some default options.
   $options += array(
     'pager' => TRUE,
     'more' => FALSE,
@@ -347,7 +346,7 @@ function _backup_migrate_ui_destination_display_file_list($files, $options = arr
     $sort_dir   = tablesort_get_sort($headers) == 'desc' ? SORT_DESC : SORT_ASC;
 
     $i          = 0;
-    foreach ((array)$files as $id => $file) {
+    foreach ((array) $files as $id => $file) {
       $info = $file->info();
 
       // If there's a datestamp, it should override the filetime as it's probably more reliable.
@@ -361,17 +360,17 @@ function _backup_migrate_ui_destination_display_file_list($files, $options = arr
       }
       // Add the backup source.
       if (!empty($info['bam_sourcename'])) {
-        $description .= ' <div title="'. check_plain($info['bam_sourcename']) .'" class="backup-migrate-tags"><span class="backup-migrate-label">'. t('Source:') .' </span>' . check_plain($info['bam_sourcename']) . '</div>';
+        $description .= ' <div title="' . check_plain($info['bam_sourcename']) . '" class="backup-migrate-tags"><span class="backup-migrate-label">' . t('Source:') . ' </span>' . check_plain($info['bam_sourcename']) . '</div>';
       }
       // Add the tags as a new row.
       if (!empty($info['tags'])) {
-        $tags = check_plain(implode(', ', (array)$info['tags']));
+        $tags = check_plain(implode(', ', (array) $info['tags']));
         $description .= ' <div title="' . $tags . '" class="backup-migrate-tags"><span class="backup-migrate-label">' . t('Tags:') . ' </span>' . $tags . '</div>';
       }
       // Add the other info.
       if (!empty($info['bam_other_safe'])) {
         foreach ($info['bam_other_safe'] as $label => $data) {
-          $description .= ' <div class="backup-migrate-tags"><span class="backup-migrate-label">' . $label . ' </span>' . $data . '</div>';        
+          $description .= ' <div class="backup-migrate-tags"><span class="backup-migrate-label">' . $label . ' </span>' . $data . '</div>';
         }
       }
 
@@ -404,7 +403,7 @@ function _backup_migrate_ui_destination_display_file_list($files, $options = arr
       $start = 0;
 
       if ($options['pager']) {
-        $page = isset($_GET['page']) ? $_GET['page'] : '';
+        $page = isset($_GET['page']) ? intval($_GET['page']) : 0;
         $start = $page * $limit;
 
         $element = 0;
@@ -416,15 +415,15 @@ function _backup_migrate_ui_destination_display_file_list($files, $options = arr
           '',
           t('older »'),
           t('oldest »'),
-          );
-        $pager = theme('pager', $tags, $limit, $element, array(), ceil($total/$limit));
+        );
+        $pager = theme('pager', $tags, $limit, $element, array(), ceil($total / $limit));
 
-        $end = min($total - 1, $start + $limit);
+        $end = min($total, $start + $limit);
       }
       if ($total > $limit && $options['more']) {
         $more = ' ' . l(t('view all'), $options['more']);
       }
-      $showing = t('Showing @start to @end of @total files.', array('@start' => $start + 1, '@end' => $end + 1, '@total' => $total));
+      $showing = t('Showing @start to @end of @total files.', array('@start' => $start + 1, '@end' => $end, '@total' => $total));
 
       // Limit the number of rows shown.
       $rows = array_slice($rows, $start, $limit, TRUE);
@@ -445,7 +444,7 @@ function _backup_migrate_ui_destination_display_file_list($files, $options = arr
  * List the backup files in the given destination.
  */
 function _backup_migrate_ui_destination_display_file_list_options($files, $limit = NULL) {
-  drupal_add_css(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.css');
+  drupal_add_css(drupal_get_path('module', 'backup_migrate') . '/backup_migrate.css');
 
   $rows = $sort = array();
   if ($files) {
@@ -461,7 +460,7 @@ function _backup_migrate_ui_destination_display_file_list_options($files, $limit
     $sort_dir   = tablesort_get_sort($headers) == 'desc' ? SORT_DESC : SORT_ASC;
 
     $i          = 0;
-    foreach ((array)$files as $file) {
+    foreach ((array) $files as $file) {
       $info = $file->info();
 
       // If there's a datestamp, it should override the filetime as it's probably more reliable.
@@ -475,17 +474,17 @@ function _backup_migrate_ui_destination_display_file_list_options($files, $limit
       }
       // Add the backup source.
       if (!empty($info['bam_sourcename'])) {
-        $description .= ' <div title="'. check_plain($info['bam_sourcename']) .'" class="backup-migrate-tags"><span class="backup-migrate-label">'. t('Source:') .' </span>' . check_plain($info['bam_sourcename']) . '</div>';
+        $description .= ' <div title="' . check_plain($info['bam_sourcename']) . '" class="backup-migrate-tags"><span class="backup-migrate-label">' . t('Source:') . ' </span>' . check_plain($info['bam_sourcename']) . '</div>';
       }
       // Add the tags as a new row.
       if (!empty($info['tags'])) {
-        $tags = check_plain(implode(', ', (array)$info['tags']));
+        $tags = check_plain(implode(', ', (array) $info['tags']));
         $description .= ' <div title="' . $tags . '" class="backup-migrate-tags"><span class="backup-migrate-label">' . t('Tags:') . ' </span>' . $tags . '</div>';
       }
       // Add the other info.
       if (!empty($info['bam_other_safe'])) {
         foreach ($info['bam_other_safe'] as $label => $data) {
-          $description .= ' <div class="backup-migrate-tags"><span class="backup-migrate-label">' . $label . ' </span>' . $data . '</div>';        
+          $description .= ' <div class="backup-migrate-tags"><span class="backup-migrate-label">' . $label . ' </span>' . $data . '</div>';
         }
       }
 
@@ -508,7 +507,6 @@ function _backup_migrate_ui_destination_display_file_list_options($files, $limit
       $end = $limit;
       $start = 0;
 
-
       $showing = t('Showing @start to @end of @total files.', array('@start' => $start + 1, '@end' => $end + 1, '@total' => $total));
 
       // Limit the number of rows shown.
@@ -573,7 +571,7 @@ function backup_migrate_ui_destination_restore_file_confirm($form, &$form_state,
 
   $form['destination_id'] = array('#type' => 'value', '#value' => $destination_id);
   $form['file_id'] = array('#type' => 'value', '#value' => $file_id);
-  $form = confirm_form($form, t('Are you sure you want to restore the database?'), BACKUP_MIGRATE_MENU_PATH . "/destination/list/files/". $destination_id, t('Are you sure you want to restore the database from the backup file %file_id? This will delete some or all of your data and cannot be undone. <strong>Always test your backups on a non-production server!</strong>', array('%file_id' => $file_id)), t('Restore'), t('Cancel'));
+  $form = confirm_form($form, t('Are you sure you want to restore the database?'), BACKUP_MIGRATE_MENU_PATH . "/destination/list/files/" . $destination_id, t('Are you sure you want to restore the database from the backup file %file_id? This will delete some or all of your data and cannot be undone. <strong>Always test your backups on a non-production server!</strong>', array('%file_id' => $file_id)), t('Restore'), t('Cancel'));
   drupal_set_message(t('Restoring will delete some or all of your data and cannot be undone. <strong>Always test your backups on a non-production server!</strong>'), 'warning', FALSE);
   $form = array_merge_recursive($form, backup_migrate_filters_settings_form(backup_migrate_filters_settings_default('restore'), 'restore'));
   $form['actions']['#weight'] = 100;
@@ -582,8 +580,8 @@ function backup_migrate_ui_destination_restore_file_confirm($form, &$form_state,
   if (@$form['advanced']) {
     $form['advanced']['#type'] = 'fieldset';
     $form['advanced']['#title'] = t('Advanced Options');
-    $form['advanced']['#collapsed'] = true;
-    $form['advanced']['#collapsible'] = true;
+    $form['advanced']['#collapsed'] = TRUE;
+    $form['advanced']['#collapsible'] = TRUE;
   }
 
   return $form;
@@ -598,7 +596,7 @@ function backup_migrate_ui_destination_restore_file_confirm_submit($form, &$form
   if ($destination_id && $file_id) {
     backup_migrate_perform_restore($destination_id, $file_id, $form_state['values']);
   }
-  $redir = user_access('access backup files') ? BACKUP_MIGRATE_MENU_PATH . "/destination/list/files/". $destination_id : BACKUP_MIGRATE_MENU_PATH;
+  $redir = user_access('access backup files') ? BACKUP_MIGRATE_MENU_PATH . "/destination/list/files/" . $destination_id : BACKUP_MIGRATE_MENU_PATH;
   $form_state['redirect'] = $redir;
 }
 
@@ -611,7 +609,7 @@ function backup_migrate_ui_destination_delete_file($destination_id = NULL, $file
   }
   _backup_migrate_message('Cannot delete the file: %file_id because it does not exist.', array('%file_id' => $file_id), 'error');
   if ($destination_id && user_access('access backup files')) {
-    drupal_goto(BACKUP_MIGRATE_MENU_PATH .'/destination/list/files/' . $destination_id);
+    drupal_goto(BACKUP_MIGRATE_MENU_PATH . '/destination/list/files/' . $destination_id);
   }
   drupal_goto(BACKUP_MIGRATE_MENU_PATH);
 }
@@ -622,7 +620,7 @@ function backup_migrate_ui_destination_delete_file($destination_id = NULL, $file
 function backup_migrate_ui_destination_delete_file_confirm($form, &$form_state, $destination_id, $file_id) {
   $form['destination_id'] = array('#type' => 'value', '#value' => $destination_id);
   $form['file_id'] = array('#type' => 'value', '#value' => $file_id);
-  return confirm_form($form, t('Are you sure you want to delete the backup file?'), BACKUP_MIGRATE_MENU_PATH . '/destination/list/files/'. $destination_id, t('Are you sure you want to delete the backup file %file_id? <strong>This action cannot be undone.</strong>', array('%file_id' => $file_id)), t('Delete'), t('Cancel'));
+  return confirm_form($form, t('Are you sure you want to delete the backup file?'), BACKUP_MIGRATE_MENU_PATH . '/destination/list/files/' . $destination_id, t('Are you sure you want to delete the backup file %file_id? <strong>This action cannot be undone.</strong>', array('%file_id' => $file_id)), t('Delete'), t('Cancel'));
 }
 
 /**
@@ -635,7 +633,7 @@ function backup_migrate_ui_destination_delete_file_confirm_submit($form, &$form_
     backup_migrate_destination_delete_file($destination_id, $file_id);
     _backup_migrate_message('Database backup file deleted: %file_id', array('%file_id' => $file_id));
   }
-  $form_state['redirect'] = user_access('access backup files') ? BACKUP_MIGRATE_MENU_PATH . "/destination/list/files/". $destination_id : BACKUP_MIGRATE_MENU_PATH;
+  $form_state['redirect'] = user_access('access backup files') ? BACKUP_MIGRATE_MENU_PATH . "/destination/list/files/" . $destination_id : BACKUP_MIGRATE_MENU_PATH;
 }
 
 /* Utilities */
@@ -644,7 +642,7 @@ function backup_migrate_ui_destination_delete_file_confirm_submit($form, &$form_
  * Get pulldown to select existing source options.
  */
 function _backup_migrate_get_destination_pulldown($op, $destination_id = NULL, $copy_destination_id = NULL) {
-  drupal_add_js(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.js');
+  drupal_add_js(drupal_get_path('module', 'backup_migrate') . '/backup_migrate.js');
 
   $destinations = _backup_migrate_get_destination_form_item_options($op);
   $form = array(
@@ -656,10 +654,10 @@ function _backup_migrate_get_destination_pulldown($op, $destination_id = NULL, $
     '#title' => t('Backup Destination'),
     '#options' => $destinations,
     '#default_value' => $destination_id,
-    //'#process' => array('_backup_migrate_process_destination_pulldown'),
+    // '#process' => array('_backup_migrate_process_destination_pulldown'),
   );
   if (user_access('administer backup and migrate')) {
-    $form['destination_id']['#description'] = l(t("Create new destination"), BACKUP_MIGRATE_MENU_PATH . "/destination/add");
+    $form['destination_id']['#description'] = l(t('Create new destination'), BACKUP_MIGRATE_MENU_PATH . '/settings/destination/add');
   }
   $form['copy'] = array(
     '#type' => 'checkbox',
@@ -679,8 +677,6 @@ function _backup_migrate_get_destination_pulldown($op, $destination_id = NULL, $
     '#default_value' => $copy_destination_id,
   );
 
-
-
   return $form;
 }
 
@@ -693,16 +689,16 @@ function _backup_migrate_process_destination_pulldown($element) {
     'backup_migrate' => array(
       'destination_selectors' => array(
         $id => array(
-          'destination_selector' => $id, 
+          'destination_selector' => $id,
           'copy_destination_selector' => $element['copy_destination']['copy_destination_id']['#id'],
           'copy' => $element['copy']['#id'],
           'labels' => array(
             t('Local') => t('Save an offsite copy to'),
             t('Offsite') => t('Save a local copy to'),
-          )
-        )
-      )
-    )
+          ),
+        ),
+      ),
+    ),
   );
   drupal_add_js($settings, 'setting');
 
@@ -750,7 +746,7 @@ function _backup_migrate_get_destination_form_item_options($op) {
       t('Local') => $local,
       t('Offsite') => $remote,
     );
-  } 
+  }
   else {
     $out = $remote + $local;
   }
@@ -761,25 +757,26 @@ function _backup_migrate_get_destination_form_item_options($op) {
  * A base class for creating destinations.
  */
 class backup_migrate_destination extends backup_migrate_location {
-  var $db_table = "backup_migrate_destinations";
-  var $type_name = "destination";
-  var $default_values = array('settings' => array());
-  var $singular = 'destination';
-  var $plural = 'destinations';
-  var $title_plural = 'Destinations';
-  var $title_singular = 'Destination';
-  var $cache_files = FALSE;
-  var $fetch_time = NULL;
-  var $cache_expire = 86400; // 24 hours
-  var $weight = 0;
-
-  var $destination_type = "";
-  var $supported_ops = array();
+  public $db_table = "backup_migrate_destinations";
+  public $type_name = "destination";
+  public $default_values = array('settings' => array());
+  public $singular = 'destination';
+  public $plural = 'destinations';
+  public $title_plural = 'Destinations';
+  public $title_singular = 'Destination';
+  public $cache_files = FALSE;
+  public $fetch_time = NULL;
+  public $cache_expire = 86400;
+  // 24 hours.
+  public $weight = 0;
+
+  public $destination_type = "";
+  public $supported_ops = array();
 
   /**
    * This function is not supposed to be called. It is just here to help the po extractor out.
    */
-  function strings() {
+  public function strings() {
     // Help the pot extractor find these strings.
     t('Destination');
     t('Destinations');
@@ -790,7 +787,7 @@ class backup_migrate_destination extends backup_migrate_location {
   /**
    * Save the given file to the destination.
    */
-  function save_file($file, $settings) {
+  public function save_file($file, $settings) {
     $this->file_cache_clear();
 
     // Save the file metadata if the destination supports it.
@@ -801,15 +798,15 @@ class backup_migrate_destination extends backup_migrate_location {
   /**
    * Save the given file to the destination.
    */
-  function _save_file($file, $settings) {
+  public function _save_file($file, $settings) {
     // This must be overriden.
     return $file;
   }
 
   /**
-   * Save the file metadata
+   * Save the file metadata.
    */
-  function save_file_info($file, $settings) {
+  public function save_file_info($file, $settings) {
     $info = $this->create_info_file($file);
     // Save the info file and the actual file.
     return $this->_save_file($info, $settings);
@@ -818,7 +815,7 @@ class backup_migrate_destination extends backup_migrate_location {
   /**
    * Load the file with the given destination specific id and return as a backup_file object.
    */
-  function load_file($file_id) {
+  public function load_file($file_id) {
     // This must be overriden.
     return NULL;
   }
@@ -826,7 +823,7 @@ class backup_migrate_destination extends backup_migrate_location {
   /**
    * Check if a file exists in the given destination.
    */
-  function file_exists($file_id) {
+  public function file_exists($file_id) {
     // Check if the file exists in the list of available files. Actual destination types may have more efficient ways of doing this.
     $files = $this->list_files();
     return isset($files[$file_id]);
@@ -835,7 +832,7 @@ class backup_migrate_destination extends backup_migrate_location {
   /**
    * List all the available files in the given destination with their destination specific id.
    */
-  function list_files() {
+  public function list_files() {
     $files = NULL;
     if ($this->cache_files) {
       $files = $this->file_cache_get();
@@ -846,6 +843,7 @@ class backup_migrate_destination extends backup_migrate_location {
       if ($this->cache_files) {
         $this->file_cache_set($files);
       }
+      _backup_migrate_temp_files_delete();
     }
 
     $out = array();
@@ -863,22 +861,21 @@ class backup_migrate_destination extends backup_migrate_location {
   /**
    * List all the available files in the given destination with their destination specific id.
    */
-  function count_files() {
+  public function count_files() {
     return count($this->list_files());
   }
 
-
   /**
    * List all the available files in the given destination with their destination specific id.
    */
-  function _list_files() {
+  public function _list_files() {
     return array();
   }
 
   /**
    * Load up the file's metadata from the accompanying .info file if applicable.
    */
-  function load_files_info($files) {
+  public function load_files_info($files) {
     foreach ($files as $key => $file) {
       // See if there is an info file with the same name as the backup.
       if (isset($files[$key . '.info'])) {
@@ -887,7 +884,7 @@ class backup_migrate_destination extends backup_migrate_location {
         // Allow the stored metadata to override the detected metadata.
         unset($info['filename']);
         $file->file_info = $info + $file->file_info;
-        // Remove the metadata file from the list
+        // Remove the metadata file from the list.
         unset($files[$key . '.info']);
       }
 
@@ -896,14 +893,13 @@ class backup_migrate_destination extends backup_migrate_location {
       $file->info_set('remote', $this->get('remote'));
     }
 
-
     return $files;
   }
 
   /**
    * Create an ini file and write the meta data.
    */
-  function create_info_file($file) {
+  public function create_info_file($file) {
     $info = $this->_file_info_file($file);
     $data = _backup_migrate_array_to_ini($file->file_info);
     $info->put_contents($data);
@@ -913,7 +909,7 @@ class backup_migrate_destination extends backup_migrate_location {
   /**
    * Create the info file object.
    */
-  function _file_info_file($file) {
+  public function _file_info_file($file) {
     $info = new backup_file(array('filename' => $this->_file_info_filename($file->file_id())));
     return $info;
   }
@@ -921,24 +917,23 @@ class backup_migrate_destination extends backup_migrate_location {
   /**
    * Determine the file name of the info file for a file.
    */
-  function _file_info_filename($file_id) {
+  public function _file_info_filename($file_id) {
     return $file_id . '.info';
   }
 
-
   /**
    * Cache the file list.
    */
-  function file_cache_set($files) {
-    cache_set('backup_migrate_file_list:'. $this->get_id(), $files, 'cache', time() + $this->cache_expire);
+  public function file_cache_set($files) {
+    cache_set('backup_migrate_file_list:' . $this->get_id(), $files, 'cache', time() + $this->cache_expire);
   }
 
   /**
    * Retrieve the file list.
    */
-  function file_cache_get() {
+  public function file_cache_get() {
     backup_migrate_include('files');
-    $cache = cache_get('backup_migrate_file_list:'. $this->get_id());
+    $cache = cache_get('backup_migrate_file_list:' . $this->get_id());
     if (!empty($cache->data) && $cache->created > (time() - $this->cache_expire)) {
       $this->fetch_time = $cache->created;
       return $cache->data;
@@ -950,7 +945,7 @@ class backup_migrate_destination extends backup_migrate_location {
   /**
    * Retrieve the file list.
    */
-  function file_cache_clear() {
+  public function file_cache_clear() {
     if ($this->cache_files) {
       $this->file_cache_set(NULL);
     }
@@ -959,7 +954,7 @@ class backup_migrate_destination extends backup_migrate_location {
   /**
    * Delete the file with the given destination specific id.
    */
-  function delete_file($file_id) {
+  public function delete_file($file_id) {
     $this->file_cache_clear();
     $this->_delete_file($file_id);
     $this->_delete_file($this->_file_info_filename($file_id));
@@ -968,14 +963,14 @@ class backup_migrate_destination extends backup_migrate_location {
   /**
    * Delete the file with the given destination specific id.
    */
-  function _delete_file($file_id) {
+  public function _delete_file($file_id) {
     // This must be overriden.
   }
 
   /**
    * Get the edit form for the item.
    */
-  function edit_form() {
+  public function edit_form() {
     if (get_class($this) !== 'backup_migrate_destination') {
       $form = parent::edit_form();
       $form['subtype'] = array(
@@ -990,17 +985,17 @@ class backup_migrate_destination extends backup_migrate_location {
           'title' => t('Offsite Destinations'),
           'description' => t('For the highest level of protection, set up an offsite backup destination in a location separate from your website.'),
           'items' => array(),
-         ),
+        ),
         'local' => array(
           'title' => t('Local Destinations'),
           'description' => t('Local backups are quick and convenient but do not provide the additional safety of offsite backups.'),
           'items' => array(),
-         ),
+        ),
         'other' => array(
           'title' => t('Other Destinations'),
           'description' => t('These destinations have not been classified because they were created for Backup and Migrate version 2. They may not work correctly with this version.'),
           'items' => array(),
-         ),
+        ),
 
       );
 
@@ -1009,8 +1004,8 @@ class backup_migrate_destination extends backup_migrate_location {
       foreach ($types as $key => $type) {
         if (@$type['can_create']) {
           $type_url_str = str_replace('_', '-', $key);
-          $out = '<dt>'. l($type['type_name'], $path . "/add/$type_url_str", array('attributes' => array('title' => t('Add a new @s destination.', array('@s' => $type['type_name']))))) .'</dt>';
-          $out .= '<dd>'. filter_xss_admin($type['description']) .'</dd>';
+          $out = '<dt>' . l($type['type_name'], $path . "/add/$type_url_str", array('attributes' => array('title' => t('Add a new @s destination.', array('@s' => $type['type_name']))))) . '</dt>';
+          $out .= '<dd>' . filter_xss_admin($type['description']) . '</dd>';
 
           if (!empty($type['local'])) {
             $items['local']['items'][] = $out;
@@ -1027,7 +1022,7 @@ class backup_migrate_destination extends backup_migrate_location {
         $output = '<p>' . t('Choose the type of destination you would like to create:') . '</p>';
         foreach ($items as $group) {
           if (count($group['items'])) {
-            $group['body'] = '<dl>'. implode('', $group['items']) .'</dl>';
+            $group['body'] = '<dl>' . implode('', $group['items']) . '</dl>';
             $output .= theme('backup_migrate_group', $group);
           }
         }
@@ -1046,27 +1041,27 @@ class backup_migrate_destination extends backup_migrate_location {
   /**
    * Get the message to send to the user when confirming the deletion of the item.
    */
-  function delete_confirm_message() {
+  public function delete_confirm_message() {
     return t('Are you sure you want to delete the destination %name? Backup files already saved to this destination will not be deleted.', array('%name' => $this->get_name()));
   }
 
   /**
    * Get a boolean representing if the destination is remote or local.
    */
-  function get_remote() {
+  public function get_remote() {
     return $this->op('remote backup');
   }
 
   /**
    * Get the action links for a destination.
    */
-  function get_action_links() {
+  public function get_action_links() {
     $out = parent::get_action_links();
     $item_id = $this->get_id();
 
     // Don't display the download/delete/restore ops if they are not available for this destination.
     if ($this->op('list files') && user_access("access backup files")) {
-      $out = array('list files' => l(t("list files"), $this->get_settings_path() . '/list/files/'. $item_id)) + $out;
+      $out = array('list files' => l(t("list files"), $this->get_settings_path() . '/list/files/' . $item_id)) + $out;
     }
     if (!$this->op('configure') || !user_access('administer backup and migrate')) {
       unset($out['edit']);
@@ -1077,7 +1072,7 @@ class backup_migrate_destination extends backup_migrate_location {
   /**
    * Get the action links for a file on a given destination.
    */
-  function get_file_links($file_id) {
+  public function get_file_links($file_id) {
     $out = array();
 
     // Don't display the download/delete/restore ops if they are not available for this destination.
@@ -1088,13 +1083,13 @@ class backup_migrate_destination extends backup_migrate_location {
 
     $destination_id = $this->get_id();
     if ($can_read && user_access("access backup files")) {
-      $out[] = l(t("download"), $path . '/downloadfile/'. $destination_id .'/'. $file_id);
+      $out[] = l(t("download"), $path . '/downloadfile/' . $destination_id . '/' . $file_id);
     }
     if ($can_read && user_access("restore from backup")) {
-      $out[] = l(t("restore"), $path . '/list/restorefile/' . $destination_id .'/'. $file_id);
+      $out[] = l(t("restore"), $path . '/list/restorefile/' . $destination_id . '/' . $file_id);
     }
     if ($can_delete && user_access("delete backup files")) {
-      $out[] = l(t("delete"), $path . '/list/deletefile/' . $destination_id .'/'. $file_id);
+      $out[] = l(t("delete"), $path . '/list/deletefile/' . $destination_id . '/' . $file_id);
     }
     return $out;
   }
@@ -1102,55 +1097,55 @@ class backup_migrate_destination extends backup_migrate_location {
   /**
    * Determine if we can read the given file.
    */
-  function can_read_file($file_id) {
+  public function can_read_file($file_id) {
     return $this->op('restore');
   }
 
   /**
    * Determine if we can read the given file.
    */
-  function can_delete_file($file_id) {
+  public function can_delete_file($file_id) {
     return $this->op('delete');
   }
 
   /**
    * Get the form for the settings for this destination type.
    */
-  function settings_default() {
+  public function settings_default() {
     return array();
   }
 
   /**
    * Get the form for the settings for this destination.
    */
-  function settings_form($form) {
+  public function settings_form($form) {
     return $form;
   }
 
   /**
    * Validate the form for the settings for this destination.
    */
-  function settings_form_validate($form_values) {
+  public function settings_form_validate($form_values) {
   }
 
   /**
    * Submit the settings form. Any values returned will be saved.
    */
-  function settings_form_submit($form_values) {
+  public function settings_form_submit($form_values) {
     return $form_values;
   }
 
   /**
    * Check that a destination is valid.
    */
-  function confirm_destination() {
-    return true;
+  public function confirm_destination() {
+    return TRUE;
   }
 
   /**
    * Add the menu items specific to the destination type.
    */
-  function get_menu_items() {
+  public function get_menu_items() {
     $items = parent::get_menu_items();
 
     $path = $this->get_settings_path();
@@ -1189,7 +1184,6 @@ class backup_migrate_destination extends backup_migrate_location {
     return $items;
   }
 
-
 }
 
 /**
@@ -1199,21 +1193,21 @@ class backup_migrate_destination_remote extends backup_migrate_destination {
   /**
    * The location is a URI so parse it and store the parts.
    */
-  function get_location() {
+  public function get_location() {
     return $this->url(FALSE);
   }
 
   /**
    * The location to display is the url without the password.
    */
-  function get_display_location() {
+  public function get_display_location() {
     return $this->url(TRUE);
   }
 
   /**
    * Return the location with the password.
    */
-  function set_location($location) {
+  public function set_location($location) {
     $this->location = $location;
     $this->set_url($location);
   }
@@ -1221,7 +1215,7 @@ class backup_migrate_destination_remote extends backup_migrate_destination {
   /**
    * Destination configuration callback.
    */
-  function edit_form() {
+  public function edit_form() {
     $form = parent::edit_form();
     $form['scheme'] = array(
       "#type" => "textfield",
@@ -1271,10 +1265,10 @@ class backup_migrate_destination_remote extends backup_migrate_destination {
   /**
    * Submit the configuration form. Glue the url together and add the old password back if a new one was not specified.
    */
-  function edit_form_submit($form, &$form_state) {
+  public function edit_form_submit($form, &$form_state) {
     $form_state['values']['pass'] = $form_state['values']['pass'] ? $form_state['values']['pass'] : $form_state['values']['old_password'];
     $form_state['values']['location'] = $this->glue_url($form_state['values'], FALSE);
     parent::edit_form_submit($form, $form_state);
   }
-}
 
+}

File diff suppressed because it is too large
+ 22 - 11
sites/all/modules/contrib/admin/backup_migrate/includes/destinations.nodesquirrel.inc


+ 28 - 26
sites/all/modules/contrib/admin/backup_migrate/includes/destinations.s3.inc

@@ -1,6 +1,5 @@
 <?php
 
-
 /**
  * @file
  * Functions to handle the s3 backup destination.
@@ -12,15 +11,14 @@
  * @ingroup backup_migrate_destinations
  */
 class backup_migrate_destination_s3 extends backup_migrate_destination_remote {
-  var $supported_ops = array('scheduled backup', 'manual backup', 'remote backup', 'restore', 'list files', 'configure', 'delete');
-  var $s3 = NULL;
-  var $cache_files = TRUE;
-
+  public $supported_ops = array('scheduled backup', 'manual backup', 'remote backup', 'restore', 'list files', 'configure', 'delete');
+  public $s3 = NULL;
+  public $cache_files = TRUE;
 
   /**
    * Save to to the s3 destination.
    */
-  function _save_file($file, $settings) {
+  public function _save_file($file, $settings) {
     if ($s3 = $this->s3_object()) {
       $path = $file->filename();
       if ($s3->putObject($s3->inputFile($file->filepath(), FALSE), $this->get_bucket(), $this->remote_path($file->filename()), S3::ACL_PRIVATE)) {
@@ -33,7 +31,7 @@ class backup_migrate_destination_s3 extends backup_migrate_destination_remote {
   /**
    * Load from the s3 destination.
    */
-  function load_file($file_id) {
+  public function load_file($file_id) {
     backup_migrate_include('files');
     $file = new backup_file(array('filename' => $file_id));
     if ($s3 = $this->s3_object()) {
@@ -48,7 +46,7 @@ class backup_migrate_destination_s3 extends backup_migrate_destination_remote {
   /**
    * Delete from the s3 destination.
    */
-  function _delete_file($file_id) {
+  public function _delete_file($file_id) {
     if ($s3 = $this->s3_object()) {
       $s3->deleteObject($this->get_bucket(), $this->remote_path($file_id));
     }
@@ -57,12 +55,12 @@ class backup_migrate_destination_s3 extends backup_migrate_destination_remote {
   /**
    * List all files from the s3 destination.
    */
-  function _list_files() {
+  public function _list_files() {
     backup_migrate_include('files');
     $files = array();
     if ($s3 = $this->s3_object()) {
       $s3_files = $s3->getBucket($this->get_bucket(), $this->get_subdir());
-      foreach ((array)$s3_files as $id => $file) {
+      foreach ((array) $s3_files as $id => $file) {
         $info = array(
           'filename' => $this->local_path($file['name']),
           'filesize' => $file['size'],
@@ -77,15 +75,14 @@ class backup_migrate_destination_s3 extends backup_migrate_destination_remote {
   /**
    * Get the form for the settings for this filter.
    */
-  function edit_form() {
+  public function edit_form() {
     // Check for the library.
     $this->s3_object();
 
     $form = parent::edit_form();
     $form['scheme']['#type'] = 'value';
     $form['scheme']['#value'] = 'https';
-    $form['host']['#type'] = 'value';
-    $form['host']['#value'] = 's3.amazonaws.com';
+    $form['host']['#default_value'] = @$this->dest_url['host'] ? $this->dest_url['host'] : 's3.amazonaws.com';
 
     $form['path']['#title'] = 'S3 Bucket';
     $form['path']['#default_value'] = $this->get_bucket();
@@ -98,7 +95,7 @@ class backup_migrate_destination_s3 extends backup_migrate_destination_remote {
       '#type' => 'textfield',
       '#title' => t('Subdirectory'),
       '#default_value' => $this->get_subdir(),
-      '#weight' => 25
+      '#weight' => 25,
     );
     $form['settings']['#weight'] = 50;
 
@@ -108,11 +105,10 @@ class backup_migrate_destination_s3 extends backup_migrate_destination_remote {
   /**
    * Submit the form for the settings for the s3 destination.
    */
-  function edit_form_submit($form, &$form_state) {
+  public function edit_form_submit($form, &$form_state) {
     // Append the subdir onto the path.
-
     if (!empty($form_state['values']['subdir'])) {
-      $form_state['values']['path'] .= '/'. trim($form_state['values']['subdir'], '/');
+      $form_state['values']['path'] .= '/' . trim($form_state['values']['subdir'], '/');
     }
     parent::edit_form_submit($form, $form_state);
   }
@@ -120,9 +116,9 @@ class backup_migrate_destination_s3 extends backup_migrate_destination_remote {
   /**
    * Generate a filepath with the correct prefix.
    */
-  function remote_path($path) {
+  public function remote_path($path) {
     if ($subdir = $this->get_subdir()) {
-      $path = $subdir .'/'. $path;
+      $path = $subdir . '/' . $path;
     }
     return $path;
   }
@@ -130,9 +126,9 @@ class backup_migrate_destination_s3 extends backup_migrate_destination_remote {
   /**
    * Generate a filepath with the correct prefix.
    */
-  function local_path($path) {
+  public function local_path($path) {
     if ($subdir = $this->get_subdir()) {
-      $path = str_replace($subdir .'/', '', $path);
+      $path = str_replace($subdir . '/', '', $path);
     }
     return $path;
   }
@@ -140,7 +136,7 @@ class backup_migrate_destination_s3 extends backup_migrate_destination_remote {
   /**
    * Get the bucket which is the first part of the path.
    */
-  function get_bucket() {
+  public function get_bucket() {
     $parts = explode('/', @$this->dest_url['path']);
     return $parts[0];
   }
@@ -148,7 +144,7 @@ class backup_migrate_destination_s3 extends backup_migrate_destination_remote {
   /**
    * Get the bucket which is the first part of the path.
    */
-  function get_subdir() {
+  public function get_subdir() {
     // Support the older style of subdir saving.
     if ($subdir = $this->settings('subdir')) {
       return $subdir;
@@ -158,7 +154,7 @@ class backup_migrate_destination_s3 extends backup_migrate_destination_remote {
     return implode('/', array_filter($parts));
   }
 
-  function s3_object() {
+  public function s3_object() {
     // Try to use libraries module if available to find the path.
     if (function_exists('libraries_get_path')) {
       $library_paths[] = libraries_get_path('s3-php5-curl');
@@ -169,11 +165,16 @@ class backup_migrate_destination_s3 extends backup_migrate_destination_remote {
     $library_paths[] = drupal_get_path('module', 'backup_migrate') . '/includes/s3-php5-curl';
     $library_paths[] = drupal_get_path('module', 'backup_migrate') . '/includes';
 
-    foreach($library_paths as $path) {
+    foreach ($library_paths as $path) {
       if (file_exists($path . '/S3.php')) {
         require_once $path . '/S3.php';
         if (!$this->s3 && !empty($this->dest_url['user'])) {
-          $this->s3 = new S3($this->dest_url['user'], $this->dest_url['pass']);
+          // The hostname can be overridden.
+          $host = 's3.amazonaws.com';
+          if (isset($this->dest_url['host'])) {
+            $host = $this->dest_url['host'];
+          }
+          $this->s3 = new S3($this->dest_url['user'], $this->dest_url['pass'], FALSE, $host);
         }
         return $this->s3;
       }
@@ -181,4 +182,5 @@ class backup_migrate_destination_s3 extends backup_migrate_destination_remote {
     drupal_set_message(t('Due to drupal.org code hosting policies, the S3 library needed to use an S3 destination is no longer distributed with this module. You must download the library from !link and place it in one of these locations: %locations.', array('%locations' => implode(', ', $library_paths), '!link' => l('http://undesigned.org.za/2007/10/22/amazon-s3-php-class', 'http://undesigned.org.za/2007/10/22/amazon-s3-php-class'))), 'error', FALSE);
     return NULL;
   }
+
 }

+ 56 - 52
sites/all/modules/contrib/admin/backup_migrate/includes/files.inc

@@ -1,6 +1,5 @@
 <?php
 
-
 /**
  * @file
  * General file handling code for Backup and Migrate.
@@ -12,7 +11,7 @@ define('BACKUP_MIGRATE_FILENAME_MAXLENGTH', 255);
  * Add a file to the temporary files list for deletion when we're done.
  */
 function backup_migrate_temp_files_add($filepath = NULL) {
-  static $files = array();
+  $files = &drupal_static('backup_migrate_temp_files_add', array());
   if (!$filepath) {
     return $files;
   }
@@ -51,7 +50,7 @@ function _backup_migrate_temp_files_delete() {
 }
 
 /**
- * Delete a temporary file or folder
+ * Delete a temporary file or folder.
  */
 function _backup_migrate_temp_files_delete_file($file) {
   if (file_exists($file) && (is_writable($file) || is_link($file))) {
@@ -62,6 +61,7 @@ function _backup_migrate_temp_files_delete_file($file) {
           _backup_migrate_temp_files_delete_file("$dir/$file");
         }
       }
+      closedir($handle);
       rmdir($dir);
     }
     else {
@@ -84,6 +84,7 @@ function _backup_migrate_move_files($from, $to) {
           _backup_migrate_move_files("$from/$file", "$to/$file");
         }
       }
+      closedir($handle);
     }
     else {
       rename($from, $to);
@@ -105,7 +106,7 @@ function backup_migrate_temp_directory() {
   }
 
   // Use a full path so that the files can be deleted during the shutdown function if needed.
-  $file = $tmp .'/'. uniqid('backup_migrate_');
+  $file = $tmp . '/' . uniqid('backup_migrate_');
   mkdir($file);
   backup_migrate_temp_files_add($file);
   return $file;
@@ -136,7 +137,7 @@ function _backup_migrate_filename_append_prepare($filename, $append_str) {
   }
   return $filename;
 }
-  
+
 /**
  * Construct a filename using token and some cleaning.
  */
@@ -144,17 +145,17 @@ function _backup_migrate_construct_filename($settings) {
   // Get the raw filename from the settings.
   $filename = $settings->filename;
 
-  // Replace any tokens
+  // Replace any tokens.
   if (module_exists('token') && function_exists('token_replace')) {
     $filename = token_replace($filename);
   }
 
-  // Remove illegal characters
+  // Remove illegal characters.
   $filename = _backup_migrate_clean_filename($filename);
 
   // Generate a timestamp if needed.
   $timestamp = '';
-  if ($settings->append_timestamp && $settings->timestamp_format) {
+  if ($settings->append_timestamp == 1 && $settings->timestamp_format) {
     $timestamp = format_date(time(), 'custom', $settings->timestamp_format);
   }
 
@@ -178,7 +179,8 @@ function _backup_migrate_default_filename() {
     return '[site:name]';
   }
   else {
-    // Cleaning the string isn't strictly necessary but it looks better in the settings field.
+    // Cleaning the string isn't strictly necessary but it looks better in the
+    // settings field.
     return _backup_migrate_clean_filename(variable_get('site_name', "backup_migrate"));
   }
 }
@@ -203,17 +205,17 @@ function _backup_migrate_file_dispose_buffer($buffer) {
  * A backup file which allows for saving to and reading from the server.
  */
 class backup_file {
-  var $file_info = array();
-  var $type = array();
-  var $ext = array();
-  var $path = "";
-  var $name = "";
-  var $handle = NULL;
+  public $file_info = array();
+  public $type = array();
+  public $ext = array();
+  public $path = "";
+  public $name = "";
+  public $handle = NULL;
 
   /**
    * Construct a file object given a file path, or create a temp file for writing.
    */
-  function backup_file($params = array()) {
+  public function __construct($params = array()) {
     if (isset($params['filepath']) && file_exists($params['filepath'])) {
       $this->set_filepath($params['filepath']);
     }
@@ -226,7 +228,7 @@ class backup_file {
   /**
    * Get the file_id if the file has been saved to a destination.
    */
-  function file_id() {
+  public function file_id() {
     // The default file_id is the filename. Destinations can override the file_id if needed.
     return isset($this->file_info['file_id']) ? $this->file_info['file_id'] : $this->filename();
   }
@@ -234,14 +236,17 @@ class backup_file {
   /**
    * Get the current filepath.
    */
-  function filepath() {
-    return drupal_realpath($this->path);
+  public function filepath() {
+    if ($filepath = drupal_realpath($this->path)) {
+      return $filepath;
+    }
+    return $this->path;
   }
 
   /**
    * Get the final filename.
    */
-  function filename($name = NULL) {
+  public function filename($name = NULL) {
     if ($name) {
       $this->name = $name;
     }
@@ -253,11 +258,11 @@ class backup_file {
   /**
    * Set the current filepath.
    */
-  function set_filepath($path) {
+  public function set_filepath($path) {
     $this->path = $path;
     $params = array(
       'filename' => basename($path),
-      'file_id' => basename($path)
+      'file_id' => basename($path),
     );
     if (file_exists($path)) {
       $params['filesize'] = filesize($path);
@@ -269,56 +274,55 @@ class backup_file {
   /**
    * Get one or all pieces of info for the file.
    */
-  function info($key = NULL) {
+  public function info($key = NULL) {
     if ($key) {
       return @$this->file_info[$key];
     }
     return $this->file_info;
   }
 
-
   /**
    * Get one or all pieces of info for the file.
    */
-  function info_set($key, $value) {
+  public function info_set($key, $value) {
     $this->file_info[$key] = $value;
   }
   /**
    * Get the file extension.
    */
-  function extension() {
+  public function extension() {
     return implode(".", $this->ext);
   }
 
   /**
    * Get the file type.
    */
-  function type() {
+  public function type() {
     return $this->type;
   }
 
   /**
    * Get the file mimetype.
    */
-  function mimetype() {
+  public function mimetype() {
     return @$this->type['filemime'] ? $this->type['filemime'] : 'application/octet-stream';
   }
 
   /**
    * Get the file mimetype.
    */
-  function type_id() {
+  public function type_id() {
     return @$this->type['id'];
   }
 
-  function filesize() {
+  public function filesize() {
     if (empty($this->file_info['filesize'])) {
       $this->calculate_filesize();
     }
     return $this->file_info['filesize'];
   }
 
-  function calculate_filesize() {
+  public function calculate_filesize() {
     $this->file_info['filesize'] = '';
     if (!empty($this->path) && file_exists($this->path)) {
       $this->file_info['filesize'] = filesize($this->path);
@@ -328,28 +332,28 @@ class backup_file {
   /**
    * Can this file be used to backup to.
    */
-  function can_backup() {
+  public function can_backup() {
     return @$this->type['backup'];
   }
 
   /**
    * Can this file be used to restore to.
    */
-  function can_restore() {
+  public function can_restore() {
     return @$this->type['restore'];
   }
 
   /**
    * Can this file be used to restore to.
    */
-  function is_recognized_type() {
+  public function is_recognized_type() {
     return @$this->type['restore'] || @$this->type['backup'];
   }
 
   /**
    * Open a file for reading or writing.
    */
-  function open($write = FALSE, $binary = FALSE) {
+  public function open($write = FALSE, $binary = FALSE) {
     if (!$this->handle) {
       $path = $this->filepath();
 
@@ -374,7 +378,7 @@ class backup_file {
   /**
    * Close a file when we're done reading/writing.
    */
-  function close() {
+  public function close() {
     fclose($this->handle);
     $this->handle = NULL;
   }
@@ -382,7 +386,7 @@ class backup_file {
   /**
    * Write a line to the file.
    */
-  function write($data) {
+  public function write($data) {
     if (!$this->handle) {
       $this->handle = $this->open(TRUE);
     }
@@ -394,7 +398,7 @@ class backup_file {
   /**
    * Read a line from the file.
    */
-  function read($size = NULL) {
+  public function read($size = NULL) {
     if (!$this->handle) {
       $this->handle = $this->open();
     }
@@ -407,14 +411,14 @@ class backup_file {
   /**
    * Write data to the file.
    */
-  function put_contents($data) {
+  public function put_contents($data) {
     file_put_contents($this->filepath(), $data);
   }
 
   /**
    * Read data from the file.
    */
-  function get_contents() {
+  public function get_contents() {
     return file_get_contents($this->filepath());
   }
 
@@ -422,10 +426,10 @@ class backup_file {
    * Transfer file using http to client. Similar to the built in file_transfer,
    *  but it calls module_invoke_all('exit') so that temp files can be deleted.
    */
-  function transfer() {
+  public function transfer() {
     $headers = array(
       array('key' => 'Content-Type', 'value' => $this->mimetype()),
-      array('key' => 'Content-Disposition', 'value' => 'attachment; filename="'. $this->filename() .'"'),
+      array('key' => 'Content-Disposition', 'value' => 'attachment; filename="' . $this->filename() . '"'),
     );
     // In some circumstances, web-servers will double compress gzipped files.
     // This may help aleviate that issue by disabling mod-deflate.
@@ -470,7 +474,7 @@ class backup_file {
   /**
    * Push a file extension onto the file and return the previous file path.
    */
-  function push_type($extension) {
+  public function push_type($extension) {
     $types = _backup_migrate_filetypes();
     if ($type = @$types[$extension]) {
       $this->push_filetype($type);
@@ -484,7 +488,7 @@ class backup_file {
   /**
    * Push a file extension onto the file and return the previous file path.
    */
-  function pop_type() {
+  public function pop_type() {
     $out = new backup_file(array('filepath' => $this->filepath()));
     $this->pop_filetype();
     $this->temporary_file();
@@ -494,7 +498,7 @@ class backup_file {
   /**
    * Set the current file type.
    */
-  function set_filetype($type) {
+  public function set_filetype($type) {
     $this->type = $type;
     $this->ext = array($type['extension']);
   }
@@ -502,7 +506,7 @@ class backup_file {
   /**
    * Set the current file type.
    */
-  function push_filetype($type) {
+  public function push_filetype($type) {
     $this->ext[] = $type['extension'];
     $this->type = $type;
   }
@@ -510,7 +514,7 @@ class backup_file {
   /**
    * Pop the current file type.
    */
-  function pop_filetype() {
+  public function pop_filetype() {
     array_pop($this->ext);
     $this->detect_filetype_from_extension();
   }
@@ -518,7 +522,7 @@ class backup_file {
   /**
    * Set the file info.
    */
-  function set_file_info($file_info) {
+  public function set_file_info($file_info) {
     $this->file_info = $file_info;
 
     $this->ext = explode('.', @$this->file_info['filename']);
@@ -533,7 +537,7 @@ class backup_file {
   /**
    * Get the filetype info of the given file, or false if the file is not a valid type.
    */
-  function detect_filetype_from_extension() {
+  public function detect_filetype_from_extension() {
     $ext = end($this->ext);
     $this->type = array();
     $types = _backup_migrate_filetypes();
@@ -548,17 +552,17 @@ class backup_file {
   /**
    * Get a temporary file name with path.
    */
-  function temporary_file() {
+  public function temporary_file() {
     $file = drupal_tempnam('temporary://', 'backup_migrate_');
     // Add the version without the extension. The tempnam function creates this for us.
     backup_migrate_temp_files_add($file);
 
     if ($this->extension()) {
-      $file .= '.'. $this->extension();
+      $file .= '.' . $this->extension();
       // Add the version with the extension. This is the one we will actually use.
       backup_migrate_temp_files_add($file);
     }
     $this->path = $file;
   }
-}
 
+}

+ 43 - 33
sites/all/modules/contrib/admin/backup_migrate/includes/filters.backup_restore.inc

@@ -1,9 +1,10 @@
 <?php
 
-
 /**
  * @file
- * This filter performs tha actual backup or restore operation. Not technically a filter per-se, but it does need to fit in the call chain.
+ * This filter performs tha actual backup or restore operation.
+ *
+ * Not technically a filter per-se, but it does need to fit in the call chain.
  */
 
 /**
@@ -12,12 +13,12 @@
  * @ingroup backup_migrate_filters
  */
 class backup_migrate_filter_backup_restore extends backup_migrate_filter {
-  var $op_weights = array('backup' => 0, 'restore' => 0);
+  public $op_weights = array('backup' => 0, 'restore' => 0);
 
   /**
    * Get the default destinations for this filter.
    */
-  function destinations() {
+  public function destinations() {
     $out = array();
     foreach ($this->_get_destination_types() as $destination) {
       if (method_exists($destination, 'destinations')) {
@@ -30,7 +31,7 @@ class backup_migrate_filter_backup_restore extends backup_migrate_filter {
   /**
    * Get the default sources for this filter.
    */
-  function sources() {
+  public function sources() {
     $out = array();
     foreach ($this->_get_source_types() as $type) {
       if (method_exists($type, 'sources')) {
@@ -43,7 +44,7 @@ class backup_migrate_filter_backup_restore extends backup_migrate_filter {
   /**
    * Get the default backup settings for this filter.
    */
-  function backup_settings_default() {
+  public function backup_settings_default() {
     backup_migrate_include('sources');
     $out = array();
     foreach (backup_migrate_get_sources() as $source) {
@@ -55,7 +56,7 @@ class backup_migrate_filter_backup_restore extends backup_migrate_filter {
   /**
    * Get the form for the settings for this filter.
    */
-  function backup_settings_form_validate($form, &$form_state) {
+  public function backup_settings_form_validate($form, &$form_state) {
     foreach ($this->_get_destination_types() as $destination) {
       $destination->backup_settings_form_validate($form, $form_state);
     }
@@ -64,7 +65,7 @@ class backup_migrate_filter_backup_restore extends backup_migrate_filter {
   /**
    * Submit the settings form. Any values returned will be saved.
    */
-  function backup_settings_form_submit($form, &$form_state) {
+  public function backup_settings_form_submit($form, &$form_state) {
     foreach ($this->_get_destination_types() as $destination) {
       $destination->backup_settings_form_submit($form, $form_state);
     }
@@ -73,7 +74,7 @@ class backup_migrate_filter_backup_restore extends backup_migrate_filter {
   /**
    * Get the default restore settings for this filter.
    */
-  function restore_settings_default() {
+  public function restore_settings_default() {
     $out = array();
     foreach ($this->_get_destination_types() as $destination) {
       $out += $destination->restore_settings_default();
@@ -84,13 +85,15 @@ class backup_migrate_filter_backup_restore extends backup_migrate_filter {
   /**
    * Get the form for the backup settings for this filter.
    */
-  function backup_settings_form($settings) {
+  public function backup_settings_form($settings) {
     backup_migrate_include('sources');
-    $out = array('sources' => array(
-      '#tree' => TRUE,
-    ));
-    foreach (backup_migrate_get_sources()  as $source) {
-      $source_settings = (array)(@$settings['sources'][$source->get_id()]) + $settings;
+    $out = array(
+      'sources' => array(
+        '#tree' => TRUE,
+      ),
+    );
+    foreach (backup_migrate_get_sources() as $source) {
+      $source_settings = (array) (@$settings['sources'][$source->get_id()]) + $settings;
       if ($form = $source->backup_settings_form($source_settings)) {
         $out['sources'][$source->get_id()] = array(
           '#type' => 'fieldset',
@@ -108,7 +111,7 @@ class backup_migrate_filter_backup_restore extends backup_migrate_filter {
   /**
    * Get the form for the restore settings for this filter.
    */
-  function restore_settings_form($settings) {
+  public function restore_settings_form($settings) {
     $form = array();
     foreach ($this->_get_destination_types() as $destination) {
       $destination->restore_settings_form($form, $settings);
@@ -119,7 +122,7 @@ class backup_migrate_filter_backup_restore extends backup_migrate_filter {
   /**
    * Get the before-backup form for the active sources and destinations.
    */
-  function before_action_form($op, $settings) {
+  public function before_action_form($op, $settings) {
     $form = array();
     $method = 'before_' . $op . '_form';
     if ($source = $settings->get_source()) {
@@ -138,7 +141,7 @@ class backup_migrate_filter_backup_restore extends backup_migrate_filter {
   /**
    * Get the before-backup form for the active sources and destinations.
    */
-  function before_action_form_validate($op, $settings, $form, $form_state) {
+  public function before_action_form_validate($op, $settings, $form, $form_state) {
     $method = 'before_' . $op . '_form_validate';
     foreach ($settings->get_all_locations() as $location) {
       if (method_exists($location, $method)) {
@@ -150,7 +153,7 @@ class backup_migrate_filter_backup_restore extends backup_migrate_filter {
   /**
    * Get the before-backup form for the active sources and destinations.
    */
-  function before_action_form_submit($op, $settings, $form, $form_state) {
+  public function before_action_form_submit($op, $settings, $form, $form_state) {
     $method = 'before_' . $op . '_form_submit';
     foreach ($settings->get_all_locations() as $location) {
       if (method_exists($location, $method)) {
@@ -162,7 +165,7 @@ class backup_migrate_filter_backup_restore extends backup_migrate_filter {
   /**
    * Get the file types supported by this destination.
    */
-  function file_types() {
+  public function file_types() {
     $types = array();
     foreach ($this->_get_destination_types() as $destination) {
       $types += $destination->file_types();
@@ -176,10 +179,10 @@ class backup_migrate_filter_backup_restore extends backup_migrate_filter {
   /**
    * Backup the data from the source specified in the settings.
    */
-  function backup($file, $settings) {
+  public function backup($file, $settings) {
     if ($source = $settings->get_source()) {
       if (!empty($settings->filters['sources'][$source->get_id()])) {
-        $settings->filters = (array)($settings->filters['sources'][$source->get_id()]) + $settings->filters;
+        $settings->filters = (array) ($settings->filters['sources'][$source->get_id()]) + $settings->filters;
       }
 
       $file = $source->backup_to_file($file, $settings);
@@ -192,10 +195,10 @@ class backup_migrate_filter_backup_restore extends backup_migrate_filter {
   /**
    * Restore the data from to source specified in the settings.
    */
-  function restore($file, $settings) {
+  public function restore($file, $settings) {
     if ($source = $settings->get_source()) {
       if (!empty($settings->filters['sources'][$source->get_id()])) {
-        $settings->filters = (array)($settings->filters['sources'][$source->get_id()]) + $settings->filters;
+        $settings->filters = (array) ($settings->filters['sources'][$source->get_id()]) + $settings->filters;
       }
       $num = $source->restore_from_file($file, $settings);
       return $num ? $file : FALSE;
@@ -205,11 +208,14 @@ class backup_migrate_filter_backup_restore extends backup_migrate_filter {
   }
 
   /**
-   * Get a list of dummy destinations representing each of the available destination types.
+   * Dummy destinations representing each of the available destination types.
+   *
+   * @return array
+   *   All of the available destination types.
    */
-  function _get_destination_types() {
+  public function _get_destination_types() {
     backup_migrate_include('destinations');
-    static $destinations = NULL;
+    $destinations = &drupal_static('backup_migrate_filter_backup_restore::_get_destination_types', NULL);
     if (!is_array($destinations)) {
       $destinations = array();
       $types = backup_migrate_get_destination_subtypes();
@@ -217,7 +223,7 @@ class backup_migrate_filter_backup_restore extends backup_migrate_filter {
       foreach ($types as $key => $type) {
         // Include the necessary file if specified by the type.
         if (!empty($type['file'])) {
-          require_once './'. $type['file'];
+          require_once './' . $type['file'];
         }
         $destinations[] = new $type['class'](array());
       }
@@ -226,19 +232,23 @@ class backup_migrate_filter_backup_restore extends backup_migrate_filter {
   }
 
   /**
-   * Get a list of dummy destinations representing each of the available source types.
+   * Dummy destinations representing each of the available source types.
+   *
+   * @return array
+   *   All of the available source types.
    */
-  function _get_source_types() {
+  public function _get_source_types() {
     backup_migrate_include('sources');
-    static $sources = NULL;
+    $sources = &drupal_static('backup_migrate_filter_backup_restore::_get_source_types', NULL);
     if (!is_array($sources)) {
       $sources = array();
       $types = backup_migrate_get_source_subtypes();
-      // If no (valid) node type has been provided, display a node type overview.
+      // If no (valid) node type has been provided, display a node type
+      // overview.
       foreach ($types as $key => $type) {
         // Include the necessary file if specified by the type.
         if (!empty($type['file'])) {
-          require_once './'. $type['file'];
+          require_once './' . $type['file'];
         }
         $sources[] = new $type['class'](array());
       }

+ 19 - 21
sites/all/modules/contrib/admin/backup_migrate/includes/filters.compression.inc

@@ -1,6 +1,5 @@
 <?php
 
-
 /**
  * @file
  * A filter for compressing bckups with zip, gz bzip etc.
@@ -12,26 +11,26 @@
  * @ingroup backup_migrate_filters
  */
 class backup_migrate_filter_compression extends backup_migrate_filter {
-  var $op_weights = array('backup' => 100, 'restore' => -100);
+  public $op_weights = array('backup' => 100, 'restore' => -100);
 
   /**
    * This function is called on a backup file after the backup has been completed.
    */
-  function backup($file, $settings) {
+  public function backup($file, $settings) {
     return $this->_backup_migrate_file_compress($file, $settings);
   }
 
   /**
    * This function is called on a backup file before importing it.
    */
-  function restore($file, $settings) {
+  public function restore($file, $settings) {
     return $this->_backup_migrate_file_decompress($file, $settings);
   }
 
   /**
    * Get the form for the settings for this filter.
    */
-  function backup_settings_default() {
+  public function backup_settings_default() {
     $options = $this->_backup_migrate_get_compression_form_item_options();
     return array('compression' => isset($options['gzip']) ? 'gzip' : 'none');
   }
@@ -39,7 +38,7 @@ class backup_migrate_filter_compression extends backup_migrate_filter {
   /**
    * Get the form for the settings for this filter.
    */
-  function backup_settings_form($settings) {
+  public function backup_settings_form($settings) {
     $form = array();
     $compression_options = $this->_backup_migrate_get_compression_form_item_options();
     $form['file']['compression'] = array(
@@ -54,7 +53,7 @@ class backup_migrate_filter_compression extends backup_migrate_filter {
   /**
    * Return a list of backup filetypes.
    */
-  function file_types() {
+  public function file_types() {
     return array(
       "gzip" => array(
         "extension" => "gz",
@@ -86,7 +85,7 @@ class backup_migrate_filter_compression extends backup_migrate_filter {
   /**
    * Get the compression options as an options array for a form item.
    */
-  function _backup_migrate_get_compression_form_item_options() {
+  public function _backup_migrate_get_compression_form_item_options() {
     $compression_options = array("none" => t("No Compression"));
     if (@function_exists("gzencode")) {
       $compression_options['gzip'] = t("GZip");
@@ -103,15 +102,14 @@ class backup_migrate_filter_compression extends backup_migrate_filter {
   /**
    * Gzip encode a file.
    */
-  function _backup_migrate_gzip_encode($source, $dest, $level = 9, $settings) {
+  public function _backup_migrate_gzip_encode($source, $dest, $level = 9, $settings) {
     $success = FALSE;
     // Try command line gzip first.
-    
     if (!empty($settings->filters['use_cli'])) {
       $success = backup_migrate_exec("gzip -c -$level %input > %dest", array('%input' => $source, '%dest' => $dest, '%level' => $level));
     }
     if (!$success && @function_exists("gzopen")) {
-      if (($fp_out = gzopen($dest, 'wb'. $level)) && ($fp_in = fopen($source, 'rb'))) {
+      if (($fp_out = gzopen($dest, 'wb' . $level)) && ($fp_in = fopen($source, 'rb'))) {
         while (!feof($fp_in)) {
           gzwrite($fp_out, fread($fp_in, 1024 * 512));
         }
@@ -126,7 +124,7 @@ class backup_migrate_filter_compression extends backup_migrate_filter {
   /**
    * Gzip decode a file.
    */
-  function _backup_migrate_gzip_decode($source, $dest, $settings) {
+  public function _backup_migrate_gzip_decode($source, $dest, $settings) {
     $success = FALSE;
 
     if (!empty($settings->filters['use_cli'])) {
@@ -149,7 +147,7 @@ class backup_migrate_filter_compression extends backup_migrate_filter {
   /**
    * Bzip encode a file.
    */
-  function _backup_migrate_bzip_encode($source, $dest) {
+  public function _backup_migrate_bzip_encode($source, $dest) {
     $success = FALSE;
     if (@function_exists("bzopen")) {
       if (($fp_out = bzopen($dest, 'w')) && ($fp_in = fopen($source, 'rb'))) {
@@ -170,7 +168,7 @@ class backup_migrate_filter_compression extends backup_migrate_filter {
   /**
    * Bzip decode a file.
    */
-  function _backup_migrate_bzip_decode($source, $dest) {
+  public function _backup_migrate_bzip_decode($source, $dest) {
     $success = FALSE;
     if (@function_exists("bzopen")) {
       if (($fp_out = fopen($dest, 'w')) && ($fp_in = bzopen($source, 'r'))) {
@@ -191,10 +189,10 @@ class backup_migrate_filter_compression extends backup_migrate_filter {
   /**
    * Zip encode a file.
    */
-  function _backup_migrate_zip_encode($source, $dest, $filename) {
+  public function _backup_migrate_zip_encode($source, $dest, $filename) {
     $success = FALSE;
     if (class_exists('ZipArchive')) {
-      $zip = new ZipArchive;
+      $zip = new ZipArchive();
       $res = $zip->open($dest, constant("ZipArchive::CREATE"));
       if ($res === TRUE) {
         $zip->addFile($source, $filename);
@@ -207,10 +205,10 @@ class backup_migrate_filter_compression extends backup_migrate_filter {
   /**
    * Zip decode a file.
    */
-  function _backup_migrate_zip_decode($source, $dest) {
+  public function _backup_migrate_zip_decode($source, $dest) {
     $success = FALSE;
     if (class_exists('ZipArchive')) {
-      $zip = new ZipArchive;
+      $zip = new ZipArchive();
       if (($fp_out = fopen($dest, "w")) && ($zip->open($source))) {
         $filename = ($zip->getNameIndex(0));
         if ($fp_in = $zip->getStream($filename)) {
@@ -230,7 +228,7 @@ class backup_migrate_filter_compression extends backup_migrate_filter {
    * Compress a file with the given settings.
    *  Also updates settings to reflect new file mime and file extension.
    */
-  function _backup_migrate_file_compress($file, $settings) {
+  public function _backup_migrate_file_compress($file, $settings) {
     switch ($settings->filters['compression']) {
       case "gzip":
         $from = $file->push_type('gzip');
@@ -264,7 +262,7 @@ class backup_migrate_filter_compression extends backup_migrate_filter {
    * Decompress a file with the given settings.
    *  Also updates settings to reflect new file mime and file extension.
    */
-  function _backup_migrate_file_decompress($file, $settings) {
+  public function _backup_migrate_file_decompress($file, $settings) {
     $success = FALSE;
 
     switch ($file->type_id()) {
@@ -294,5 +292,5 @@ class backup_migrate_filter_compression extends backup_migrate_filter {
     }
     return $success ? $file : NULL;
   }
-}
 
+}

+ 31 - 29
sites/all/modules/contrib/admin/backup_migrate/includes/filters.encryption.inc

@@ -1,6 +1,5 @@
 <?php
 
-
 /**
  * @file
  * A filter for encrypting bckups with AES.
@@ -12,33 +11,33 @@
  * @ingroup backup_migrate_filters
  */
 class backup_migrate_filter_encryption extends backup_migrate_filter {
-  var $op_weights = array('backup' => 170, 'restore' => -170);
+  public $op_weights = array('backup' => 170, 'restore' => -170);
 
   /**
-   * This function is called on a backup file after the backup has been completed.
+   * Called on a backup file after the backup has been completed.
    */
-  function backup($file, $settings) {
+  public function backup($file, $settings) {
     return $this->file_encrypt($file, $settings);
   }
 
   /**
-   * This function is called on a backup file before importing it.
+   * Called on a backup file before importing it.
    */
-  function restore($file, $settings) {
+  public function restore($file, $settings) {
     return $this->file_decrypt($file);
   }
 
   /**
-   * Get the form for the settings for this filter.
+   * Gets the form for the settings for this filter.
    */
-  function backup_settings_default() {
+  public function backup_settings_default() {
     return array('encryption' => 'none');
   }
 
   /**
-   * Get the form for the settings for this filter.
+   * Gets the form for the settings for this filter.
    */
-  function backup_settings_form($settings) {
+  public function backup_settings_form($settings) {
     $form = array();
     $options = $this->_backup_migrate_get_encryption_form_item_options();
     if (count($options) > 1) {
@@ -62,9 +61,9 @@ class backup_migrate_filter_encryption extends backup_migrate_filter {
   }
 
   /**
-   * Return a list of backup filetypes.
+   * Returns a list of backup filetypes.
    */
-  function file_types() {
+  public function file_types() {
     return array(
       "aes" => array(
         "extension" => "aes",
@@ -76,9 +75,9 @@ class backup_migrate_filter_encryption extends backup_migrate_filter {
   }
 
   /**
-   * Get the compression options as an options array for a form item.
+   * Gets the compression options as an options array for a form item.
    */
-  function _backup_migrate_get_encryption_form_item_options() {
+  public function _backup_migrate_get_encryption_form_item_options() {
     $options = array();
     $options = array('' => t('No Encryption'));
     if (@function_exists("aes_encrypt")) {
@@ -88,13 +87,14 @@ class backup_migrate_filter_encryption extends backup_migrate_filter {
   }
 
   /**
-   * AES encrypt a file.
+   * AES encrypts a file.
    */
-  function aes_encrypt($source, $dest) {
+  public function aes_encrypt($source, $dest) {
     $success = FALSE;
     if (function_exists('aes_encrypt')) {
       if ($data = $source->get_contents()) {
-        // Add a marker to the end of the data so we can trim the padding on decrpypt.
+        // Add a marker to the end of the data so we can trim the padding on
+        // decrpypt.
         $data = pack("a*H2", $data, "80");
         if ($data = aes_encrypt($data, FALSE)) {
           $dest->put_contents($data);
@@ -106,9 +106,9 @@ class backup_migrate_filter_encryption extends backup_migrate_filter {
   }
 
   /**
-   * Gzip decode a file.
+   * AES decodes a file.
    */
-  function aes_decrypt($source, $dest) {
+  public function aes_decrypt($source, $dest) {
     $success = FALSE;
     if (function_exists('aes_decrypt')) {
       if ($data = $source->get_contents()) {
@@ -124,10 +124,11 @@ class backup_migrate_filter_encryption extends backup_migrate_filter {
   }
 
   /**
-   * Compress a file with the given settings.
-   *  Also updates settings to reflect new file mime and file extension.
+   * Compresses a file with the given settings.
+   *
+   * Also updates settings to reflect new file mime and file extension.
    */
-  function file_encrypt($file, $settings) {
+  public function file_encrypt($file, $settings) {
     if (!empty($settings->filters['encryption'])) {
       switch ($settings->filters['encryption']) {
         case "aes":
@@ -146,10 +147,11 @@ class backup_migrate_filter_encryption extends backup_migrate_filter {
   }
 
   /**
-   * Decompress a file with the given settings.
-   *  Also updates settings to reflect new file mime and file extension.
+   * Decompresses a file with the given settings.
+   *
+   * Also updates settings to reflect new file mime and file extension.
    */
-  function file_decrypt($file) {
+  public function file_decrypt($file) {
     $success = FALSE;
     if ($file) {
       switch ($file->type_id()) {
@@ -157,11 +159,11 @@ class backup_migrate_filter_encryption extends backup_migrate_filter {
           $from = $file->pop_type();
           $success = $this->aes_decrypt($from, $file);
           break;
+
         default:
           return $file;
-        break;
-    }
-  
+      }
+
       if (!$success) {
         if (function_exists('aes_decrypt')) {
           _backup_migrate_message("Could not decrpyt backup file. Please check that the file is valid and that the encryption key of the server matches the server that created the backup.", array(), 'error');
@@ -173,5 +175,5 @@ class backup_migrate_filter_encryption extends backup_migrate_filter {
     }
     return $success ? $file : NULL;
   }
-}
 
+}

+ 47 - 49
sites/all/modules/contrib/admin/backup_migrate/includes/filters.inc

@@ -1,25 +1,24 @@
 <?php
 
-
 /**
  * @file
  * All of the filter handling code needed for Backup and Migrate.
  */
 
 /**
- * Get the available destination types.
+ * Gets the available destination types.
  */
 function backup_migrate_get_filters($op = NULL) {
-  static $filters = NULL;
+  $filters = &drupal_static('backup_migrate_get_filters', NULL);
   if ($filters === NULL) {
     $filters = array();
     $definitions = module_invoke_all('backup_migrate_filters');
     foreach ($definitions as $definition) {
       // Include the necessary file if specified by the filter.
       if (!empty($definition['file'])) {
-        require_once './'. $definition['file'];
+        require_once './' . $definition['file'];
       }
-      $filters[] = new $definition['class'];
+      $filters[] = new $definition['class']();
     }
   }
   $sort = array();
@@ -32,37 +31,37 @@ function backup_migrate_get_filters($op = NULL) {
 }
 
 /**
- * Implementation of hook_backup_migrate_filters().
+ * Implements hook_backup_migrate_filters().
  *
  * Get the built in Backup and Migrate filters.
  */
 function backup_migrate_backup_migrate_filters() {
   return array(
     'backup_restore' => array(
-      'file' => drupal_get_path('module', 'backup_migrate') .'/includes/filters.backup_restore.inc',
+      'file' => drupal_get_path('module', 'backup_migrate') . '/includes/filters.backup_restore.inc',
       'class' => 'backup_migrate_filter_backup_restore',
     ),
     'compression' => array(
-      'file' => drupal_get_path('module', 'backup_migrate') .'/includes/filters.compression.inc',
+      'file' => drupal_get_path('module', 'backup_migrate') . '/includes/filters.compression.inc',
       'class' => 'backup_migrate_filter_compression',
     ),
     'encryption' => array(
-      'file' => drupal_get_path('module', 'backup_migrate') .'/includes/filters.encryption.inc',
+      'file' => drupal_get_path('module', 'backup_migrate') . '/includes/filters.encryption.inc',
       'class' => 'backup_migrate_filter_encryption',
     ),
     'statusnotify' => array(
-      'file' => drupal_get_path('module', 'backup_migrate') .'/includes/filters.statusnotify.inc',
+      'file' => drupal_get_path('module', 'backup_migrate') . '/includes/filters.statusnotify.inc',
       'class' => 'backup_migrate_filter_statusnotify',
     ),
     'utils' => array(
-      'file' => drupal_get_path('module', 'backup_migrate') .'/includes/filters.utils.inc',
+      'file' => drupal_get_path('module', 'backup_migrate') . '/includes/filters.utils.inc',
       'class' => 'backup_migrate_filter_utils',
     ),
   );
 }
 
 /**
- * Invoke the given method on all of the available filters.
+ * Invokes the given method on all of the available filters.
  */
 function backup_migrate_filters_invoke_all() {
   $args    = func_get_args();
@@ -96,7 +95,7 @@ function backup_migrate_filters_invoke_all() {
 }
 
 /**
- * Filter a backup file before sending it to the destination.
+ * Filters a backup file before sending it to the destination.
  */
 function backup_migrate_filters_backup($file, &$settings) {
   backup_migrate_filters_invoke_all('pre_backup', $file, $settings);
@@ -112,7 +111,7 @@ function backup_migrate_filters_backup($file, &$settings) {
 }
 
 /**
- * Filter a backup file before sending it to the destination.
+ * Filters a backup file before sending it to the destination.
  */
 function backup_migrate_filters_restore($file, &$settings) {
   backup_migrate_filters_invoke_all('pre_restore', $file, $settings);
@@ -127,16 +126,16 @@ function backup_migrate_filters_restore($file, &$settings) {
 }
 
 /**
- * Get the backup settings for all of the filters.
+ * Gets the backup settings for all of the filters.
  */
 function backup_migrate_filters_settings_form($settings, $op) {
-  $out = backup_migrate_filters_invoke_all($op .'_settings_form', $settings);
+  $out = backup_migrate_filters_invoke_all($op . '_settings_form', $settings);
   $out = backup_migrate_filters_settings_form_set_parents($out);
   return $out;
 }
 
 /**
- * Add a form parent to the filter settings so that the filter values are saved in the right table.
+ * Adds form parent to filter settings so the values are saved in correct table.
  */
 function backup_migrate_filters_settings_form_set_parents($form) {
   foreach (element_children($form) as $key) {
@@ -149,24 +148,24 @@ function backup_migrate_filters_settings_form_set_parents($form) {
 }
 
 /**
- * Validate all the filters.
+ * Validates all the filters.
  */
 function backup_migrate_filters_settings_form_validate($op, $form, &$form_state) {
-  backup_migrate_filters_invoke_all($op .'_settings_form_validate', $form, $form_state);
+  backup_migrate_filters_invoke_all($op . '_settings_form_validate', $form, $form_state);
 }
 
 /**
- * Submit all of the filters.
+ * Submits all of the filters.
  */
 function backup_migrate_filters_settings_form_submit($op, $form, &$form_state) {
-  backup_migrate_filters_invoke_all($op .'_settings_form_submit', $form, $form_state);
+  backup_migrate_filters_invoke_all($op . '_settings_form_submit', $form, $form_state);
 }
 
 /**
- * Get the default settings for the filters.
+ * Gets the default settings for the filters.
  */
 function backup_migrate_filters_settings_default($op) {
-  return backup_migrate_filters_invoke_all($op .'_settings_default');
+  return backup_migrate_filters_invoke_all($op . '_settings_default');
 }
 
 /**
@@ -175,7 +174,7 @@ function backup_migrate_filters_settings_default($op) {
 function backup_migrate_filters_before_action_form($settings, $op) {
   $out = array();
   $out += backup_migrate_filters_invoke_all('before_action_form', $op, $settings);
-  $out += backup_migrate_filters_invoke_all('before_' . $op .'_form', $settings);
+  $out += backup_migrate_filters_invoke_all('before_' . $op . '_form', $settings);
   return $out;
 }
 
@@ -184,7 +183,7 @@ function backup_migrate_filters_before_action_form($settings, $op) {
  */
 function backup_migrate_filters_before_action_form_validate($settings, $op, $form, &$form_state) {
   backup_migrate_filters_invoke_all('before_action_form_validate', $op, $settings, $form, $form_state);
-  backup_migrate_filters_invoke_all('before_' . $op .'_form_validate', $settings, $form, $form_state);
+  backup_migrate_filters_invoke_all('before_' . $op . '_form_validate', $settings, $form, $form_state);
 }
 
 /**
@@ -192,7 +191,7 @@ function backup_migrate_filters_before_action_form_validate($settings, $op, $for
  */
 function backup_migrate_filters_before_action_form_submit($settings, $op, $form, &$form_state) {
   backup_migrate_filters_invoke_all('before_action_form_submit', $op, $settings, $form, $form_state);
-  backup_migrate_filters_invoke_all('before_' . $op .'_form_submit', $settings, $form, $form_state);
+  backup_migrate_filters_invoke_all('before_' . $op . '_form_submit', $settings, $form, $form_state);
 }
 
 /**
@@ -206,13 +205,13 @@ function backup_migrate_filters_file_types() {
  * A base class for basing filters on.
  */
 class backup_migrate_filter {
-  var $weight = 0;
-  var $op_weights = array();
+  public $weight = 0;
+  public $op_weights = array();
 
   /**
    * Get the weight of the filter for the given op.
    */
-  function weight($op = NULL) {
+  public function weight($op = NULL) {
     if ($op && isset($this->op_weights[$op])) {
       return $this->op_weights[$op];
     }
@@ -222,111 +221,110 @@ class backup_migrate_filter {
   /**
    * Get the form for the settings for this filter.
    */
-  function backup_settings_default() {
+  public function backup_settings_default() {
     return array();
   }
 
   /**
    * Get the form for the settings for this filter.
    */
-  function backup_settings_form($settings) {
+  public function backup_settings_form($settings) {
     return array();
   }
 
   /**
    * Get the form for the settings for this filter.
    */
-  function backup_settings_form_validate($form, &$form_state) {
+  public function backup_settings_form_validate($form, &$form_state) {
   }
 
   /**
    * Submit the settings form. Any values returned will be saved.
    */
-  function backup_settings_form_submit($form, &$form_state) {
+  public function backup_settings_form_submit($form, &$form_state) {
   }
 
   /**
    * Get the form for the settings for this filter.
    */
-  function restore_settings_default() {
+  public function restore_settings_default() {
     return array();
   }
 
   /**
    * Get the form for the settings for this filter.
    */
-  function restore_settings_form($settings) {
+  public function restore_settings_form($settings) {
     return array();
   }
 
   /**
    * Get the form for the settings for this filter.
    */
-  function restore_settings_form_validate($form, &$form_state) {
+  public function restore_settings_form_validate($form, &$form_state) {
   }
 
   /**
    * Submit the settings form. Any values returned will be saved.
    */
-  function restore_settings_form_submit($form, &$form_state) {
+  public function restore_settings_form_submit($form, &$form_state) {
     return $form_state['values'];
   }
 
   /**
    * Get a list of file types handled by this filter.
    */
-  function file_types() {
+  public function file_types() {
     return array();
   }
 
   /**
    * Declare any default destinations for this filter.
    */
-  function destinations() {
+  public function destinations() {
     return array();
   }
 
-
   /**
-   * This function is called on a backup file after the backup has been completed.
+   * Called on a backup file after the backup has been completed.
    */
-  function backup($file, $settings) {
+  public function backup($file, $settings) {
     return $file;
   }
 
   /**
    * This function is called immediately prior to backup.
    */
-  function pre_backup($file, $settings) {
+  public function pre_backup($file, $settings) {
 
   }
 
   /**
    * This function is called immediately post backup.
    */
-  function post_backup($file, $settings) {
+  public function post_backup($file, $settings) {
 
   }
- 
+
   /**
    * This function is called on a backup file before importing it.
    */
-  function restore($file, $settings) {
+  public function restore($file, $settings) {
     return $file;
   }
 
   /**
    * This function is called immediately prior to restore.
    */
-  function pre_restore($file, $settings) {
+  public function pre_restore($file, $settings) {
 
   }
 
   /**
    * This function is called immediately post restore.
    */
-  function post_restore($file, $settings) {
+  public function post_restore($file, $settings) {
 
   }
-}
 
+}

+ 9 - 12
sites/all/modules/contrib/admin/backup_migrate/includes/filters.statusnotify.inc

@@ -1,6 +1,5 @@
 <?php
 
-
 /**
  * @file
  * A filter for compressing bckups with zip, gz bzip etc.
@@ -12,11 +11,11 @@
  * @ingroup backup_migrate_filters
  */
 class backup_migrate_filter_statusnotify extends backup_migrate_filter {
-  
+
   /**
    * Get the default backup settings for this filter.
    */
-  function backup_settings_default() {
+  public function backup_settings_default() {
     return array(
       'notify_success_enable' => FALSE,
       'notify_failure_enable' => FALSE,
@@ -28,7 +27,7 @@ class backup_migrate_filter_statusnotify extends backup_migrate_filter {
   /**
    * Get the form for the settings for this filter.
    */
-  function backup_settings_form($settings) {
+  public function backup_settings_form($settings) {
     $form = array();
     $form['advanced']['notify_success_enable'] = array(
       "#type" => 'checkbox',
@@ -68,41 +67,39 @@ class backup_migrate_filter_statusnotify extends backup_migrate_filter {
   /**
    * Send the success email.
    */
-  function backup_succeed($settings) {
+  public function backup_succeed($settings) {
     if (@$settings->filters['notify_success_enable'] && $to = @$settings->filters['notify_success_email']) {
       $messages = $this->get_messages();
-      $subject = t('!site backup succeeded', array('!site' => variable_get('site_name', 'Drupal site')));
       if ($messages = $this->get_messages()) {
         $body = t("The site backup has completed successfully with the following messages:\n!messages", array('!messages' => $messages));
       }
       else {
         $body = t("The site backup has completed successfully.\n");
       }
-      mail($settings->filters['notify_success_email'], $subject, $body);
+      drupal_mail('backup_migrate', 'backup_succeed', $settings->filters['notify_success_email'], language_default(), array('body' => $body));
     }
   }
 
   /**
    * Send the failure email.
    */
-  function backup_fail($settings) {
+  public function backup_fail($settings) {
     if (@$settings->filters['notify_failure_enable'] && $to = @$settings->filters['notify_failure_email']) {
       $messages = $this->get_messages();
-      $subject = t('!site backup failed', array('!site' => variable_get('site_name', 'Drupal site')));
       if ($messages = $this->get_messages()) {
         $body = t("The site backup has failed with the following messages:\n!messages", array('!messages' => $messages));
       }
       else {
         $body = t("The site backup has failed for an unknown reason.");
       }
-      mail($settings->filters['notify_failure_email'], $subject, $body);
+      drupal_mail('backup_migrate', 'backup_fail', $settings->filters['notify_failure_email'], language_default(), array('body' => $body));
     }
   }
 
   /**
    * Render the messages and errors for the email.
    */
-  function get_messages() {
+  public function get_messages() {
     $out = "";
     $messages = _backup_migrate_messages();
     foreach ($messages as $message) {
@@ -110,5 +107,5 @@ class backup_migrate_filter_statusnotify extends backup_migrate_filter {
     }
     return $out;
   }
-}
 
+}

+ 43 - 32
sites/all/modules/contrib/admin/backup_migrate/includes/filters.utils.inc

@@ -1,6 +1,5 @@
 <?php
 
-
 /**
  * @file
  * A filter to run some basic utility functions. Basically any useful option not big enough to justify it's own class.
@@ -12,13 +11,13 @@
  * @ingroup backup_migrate_filters
  */
 class backup_migrate_filter_utils extends backup_migrate_filter {
-  var $op_weights = array('pre_backup' => -1000, 'post_backup' => 1000);
-  var $saved_devel_query = NULL;
+  public $op_weights = array('pre_backup' => -1000, 'post_backup' => 1000);
+  public $saved_devel_query = NULL;
 
   /**
    * Get the default backup settings for this filter.
    */
-  function backup_settings_default() {
+  public function backup_settings_default() {
     return array(
       'utils_disable_query_log' => TRUE,
       'utils_site_offline' => FALSE,
@@ -29,7 +28,7 @@ class backup_migrate_filter_utils extends backup_migrate_filter {
   /**
    * Get the default restore settings for this filter.
    */
-  function restore_settings_default() {
+  public function restore_settings_default() {
     return array(
       'utils_disable_query_log' => TRUE,
       'utils_site_offline' => FALSE,
@@ -39,7 +38,7 @@ class backup_migrate_filter_utils extends backup_migrate_filter {
   /**
    * Get the form for the backup settings for this filter.
    */
-  function backup_settings_form($settings) {
+  public function backup_settings_form($settings) {
     $form = array();
     if (module_exists('devel') && variable_get('dev_query', 0)) {
       $form['database']['utils_disable_query_log'] = array(
@@ -65,7 +64,7 @@ class backup_migrate_filter_utils extends backup_migrate_filter {
       '#type' => 'textarea',
       '#title' => t('Site off-line message'),
       '#default_value' => !empty($settings['utils_site_offline_message']) ? $settings['utils_site_offline_message'] : variable_get('site_offline_message', t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal')))),
-      '#description' => t('Message to show visitors when the site is in off-line mode.')
+      '#description' => t('Message to show visitors when the site is in off-line mode.'),
     );
     $form['advanced']['utils_description'] = array(
       '#type' => 'textarea',
@@ -92,7 +91,7 @@ class backup_migrate_filter_utils extends backup_migrate_filter {
   /**
    * Get the form for the restore settings for this filter.
    */
-  function restore_settings_form($settings) {
+  public function restore_settings_form($settings) {
     $form = array();
     if (module_exists('devel') && variable_get('dev_query', 0)) {
       $form['advanced']['utils_disable_query_log'] = array(
@@ -118,7 +117,13 @@ class backup_migrate_filter_utils extends backup_migrate_filter {
       '#type' => 'textarea',
       '#title' => t('Site off-line message'),
       '#default_value' => !empty($settings['utils_site_offline_message']) ? $settings['utils_site_offline_message'] : variable_get('site_offline_message', t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal')))),
-      '#description' => t('Message to show visitors when the site is in off-line mode.')
+      '#description' => t('Message to show visitors when the site is in off-line mode.'),
+    );
+    $form['advanced']['utils_drop_all_tables'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Drop all tables before import (MySQL only)'),
+      '#default_value' => !empty($settings['utils_drop_all_tables']) ? $settings['utils_drop_all_tables'] : NULL,
+      '#description' => t('Drop all existing database tables before restoring the backup. This option is currently available on MySQL servers only.'),
     );
     $form['advanced']['use_cli'] = array(
       "#type" => "checkbox",
@@ -135,25 +140,25 @@ class backup_migrate_filter_utils extends backup_migrate_filter {
     return $form;
   }
 
-  function pre_backup($file, $settings) {
+  public function pre_backup($file, $settings) {
     $this->take_site_offline($settings);
     $this->disable_devel_query($settings);
   }
 
-  function post_backup($file, $settings) {
+  public function post_backup($file, $settings) {
     $this->enable_devel_query($settings);
     $this->take_site_online($settings);
     if ($file) {
-      $this->add_file_info($file, $settings);    
+      $this->add_file_info($file, $settings);
     }
   }
 
-  function pre_restore($file, $settings) {
+  public function pre_restore($file, $settings) {
     $this->disable_devel_query($settings);
     $this->take_site_offline($settings);
   }
 
-  function post_restore($file, $settings) {
+  public function post_restore($file, $settings) {
     $this->enable_devel_query($settings);
     $this->take_site_online($settings);
   }
@@ -161,7 +166,7 @@ class backup_migrate_filter_utils extends backup_migrate_filter {
   /**
    * Disable devel query logging if it's active and the user has chosen to do so.
    */
-  function disable_devel_query($settings) {
+  public function disable_devel_query($settings) {
     $this->saved_devel_query = variable_get('dev_query', 0);
     if (module_exists('devel') && variable_get('dev_query', 0) && !empty($settings->filters['utils_disable_query_log'])) {
       variable_set('dev_query', 0);
@@ -171,7 +176,7 @@ class backup_migrate_filter_utils extends backup_migrate_filter {
   /**
    * Restore devel query to previous state.
    */
-  function enable_devel_query($settings) {
+  public function enable_devel_query($settings) {
     if (module_exists('devel')) {
       variable_set('dev_query', $this->saved_devel_query);
     }
@@ -180,7 +185,7 @@ class backup_migrate_filter_utils extends backup_migrate_filter {
   /**
    * Add the backup metadata to the file.
    */
-  function add_file_info($file, $settings) {
+  public function add_file_info($file, $settings) {
     $file->file_info['description']       = $settings->filters['utils_description'];
     $file->file_info['datestamp']         = time();
     $file->file_info['generator']         = 'Backup and Migrate (http://drupal.org/project/backup_migrate)';
@@ -204,31 +209,37 @@ class backup_migrate_filter_utils extends backup_migrate_filter {
   /**
    * Take the site offline if configured to do so.
    */
-  function take_site_offline($settings) {
-    // Save the current state of the site in case a restore overwrites it.
-    $this->saved_site_offline = variable_get('maintenance_mode', 0);
-    if (@$settings->filters['utils_site_offline']) {
-      $this->saved_site_offline_message = variable_get('maintenance_mode_message', NULL);
-      if (!empty($settings->filters['utils_site_offline_message'])) {
+  public function take_site_offline($settings) {
+    // If the site is already offline then don't do anything.
+    if (!variable_get('maintenance_mode', 0)) {
+      // Save the current state of the site in case a restore overwrites it.
+      if (!empty($settings->filters['utils_site_offline'])) {
+        $this->saved_site_offline = TRUE;
         $this->saved_site_offline_message = variable_get('maintenance_mode_message', NULL);
-        variable_set('maintenance_mode_message', $settings->filters['utils_site_offline_message']);
+        if (!empty($settings->filters['utils_site_offline_message'])) {
+          $this->saved_site_offline_message = variable_get('maintenance_mode_message', NULL);
+          variable_set('maintenance_mode_message', $settings->filters['utils_site_offline_message']);
+        }
+        variable_set('maintenance_mode', 1);
+        _backup_migrate_message('Site was taken offline.');
       }
-      variable_set('maintenance_mode', 1);
-      _backup_migrate_message('Site was taken offline.');
     }
   }
 
   /**
    * Take the site online again after backup or restore.
    */
-  function take_site_online($settings) {
+  public function take_site_online($settings) {
     // Take the site back off/online because the restored db may have changed that setting.
-    variable_set('maintenance_mode', $this->saved_site_offline);
-    if ($settings->filters['utils_site_offline']) {
-      if (!empty($this->saved_site_offline_message)) {
-        variable_set('maintenance_mode_message', $this->saved_site_offline_message);
+    if (variable_get('maintenance_mode', 0) && !empty($this->saved_site_offline)) {
+      variable_set('maintenance_mode', 0);
+      if ($settings->filters['utils_site_offline']) {
+        if (!empty($this->saved_site_offline_message)) {
+          variable_set('maintenance_mode_message', $this->saved_site_offline_message);
+        }
+        _backup_migrate_message('Site was taken online.');
       }
-      _backup_migrate_message('Site was taken online.');
     }
   }
+
 }

+ 67 - 106
sites/all/modules/contrib/admin/backup_migrate/includes/locations.inc

@@ -1,6 +1,5 @@
 <?php
 
-
 /**
  * @file
  * All of the location handling code needed for Backup and Migrate.
@@ -45,7 +44,7 @@ function backup_migrate_backup_migrate_locations() {
  *    'all' - all available locations should be returned
  */
 function backup_migrate_get_locations($op = 'all') {
-  static $locations = NULL;
+  $locations = &drupal_static('backup_migrate_get_locations', NULL);
 
   // Get the list of locations and cache them locally.
   if ($locations === NULL) {
@@ -82,21 +81,21 @@ function backup_migrate_get_location($id) {
  * A base class for creating locations.
  */
 class backup_migrate_location extends backup_migrate_item {
-  var $db_table = "backup_migrate_destinations";
-  var $type_name = "location";
-  var $default_values = array('settings' => array());
-  var $singular = 'location';
-  var $plural = 'locations';
-  var $title_plural = 'Locations';
-  var $title_singular = 'Location';
+  public $db_table = "backup_migrate_destinations";
+  public $type_name = "location";
+  public $default_values = array('settings' => array());
+  public $singular = 'location';
+  public $plural = 'locations';
+  public $title_plural = 'Locations';
+  public $title_singular = 'Location';
 
-  var $subtype = "";
-  var $supported_ops = array();
+  public $subtype = "";
+  public $supported_ops = array();
 
   /**
    * This function is not supposed to be called. It is just here to help the po extractor out.
    */
-  function strings() {
+  public function strings() {
     // Help the pot extractor find these strings.
     t('location');
     t('locations');
@@ -104,49 +103,49 @@ class backup_migrate_location extends backup_migrate_item {
     t('Locations');
   }
 
-  function ops() {
+  public function ops() {
     return $this->supported_ops;
   }
 
   /**
    * Does this location support the given operation.
    */
-  function op($op) {
-    $ops = (array)$this->ops();
+  public function op($op) {
+    $ops = (array) $this->ops();
     return in_array($op, $ops);
   }
 
   /**
    * Remove the given op from the support list.
    */
-  function remove_op($op) {
+  public function remove_op($op) {
     $key = array_search($op, $this->supported_ops);
     if ($key !== FALSE) {
       unset($this->supported_ops[$key]);
     }
   }
 
-  function get_name() {
+  public function get_name() {
     return @$this->name;
   }
 
-  function set_name($name) {
+  public function set_name($name) {
     return $this->name = $name;
   }
 
-  function set_location($location) {
+  public function set_location($location) {
     $this->location = $location;
   }
 
-  function get_location() {
+  public function get_location() {
     return @$this->location;
   }
 
-  function get_display_location() {
+  public function get_display_location() {
     return $this->get_location();
   }
 
-  function settings($key = NULL) {
+  public function settings($key = NULL) {
     $out = $this->settings;
     if ($key) {
       $out = isset($out[$key]) ? $out[$key] : NULL;
@@ -157,7 +156,7 @@ class backup_migrate_location extends backup_migrate_item {
   /**
    * Get the type name of this location for display to the user.
    */
-  function get_subtype_name() {
+  public function get_subtype_name() {
     if ($type = $this->get('subtype')) {
       $types = $this->location_types();
       return isset($types[$type]['type_name']) ? $types[$type]['type_name'] : $type;
@@ -167,7 +166,7 @@ class backup_migrate_location extends backup_migrate_item {
   /**
    * Get the edit form for the item.
    */
-  function edit_form() {
+  public function edit_form() {
     if (!empty($this->supported_ops)) {
       $form = parent::edit_form();
       $form['subtype'] = array(
@@ -182,13 +181,13 @@ class backup_migrate_location extends backup_migrate_item {
       foreach ($types as $key => $type) {
         if (@$type['can_create']) {
           $type_url_str = str_replace('_', '-', $key);
-          $out = '<dt>'. l($type['type_name'], BACKUP_MIGRATE_MENU_PATH . "/settings/$this->type_name/add/$type_url_str", array('attributes' => array('title' => t('Add a new @s location.', array('@s' => $type['type_name']))))) .'</dt>';
-          $out .= '<dd>'. filter_xss_admin($type['description']) .'</dd>';
+          $out = '<dt>' . l($type['type_name'], BACKUP_MIGRATE_MENU_PATH . "/settings/$this->type_name/add/$type_url_str", array('attributes' => array('title' => t('Add a new @s location.', array('@s' => $type['type_name']))))) . '</dt>';
+          $out .= '<dd>' . filter_xss_admin($type['description']) . '</dd>';
           $items[] = $out;
         }
       }
       if (count($items)) {
-        $output = t('Choose the type of location you would like to create:') .'<dl>'. implode('', $items) .'</dl>';
+        $output = t('Choose the type of location you would like to create:') . '<dl>' . implode('', $items) . '</dl>';
       }
       else {
         $output = t('No types available.');
@@ -204,21 +203,21 @@ class backup_migrate_location extends backup_migrate_item {
   /**
    * Get the available location types.
    */
-  function location_types() {
+  public function location_types() {
     return backup_migrate_get_location_subtypes();
   }
 
   /**
    * Get the message to send to the user when confirming the deletion of the item.
    */
-  function delete_confirm_message() {
+  public function delete_confirm_message() {
     return t('Are you sure you want to delete the %name?', array('%name' => $this->get_name()));
   }
 
   /**
    * Get the columns needed to list the type.
-   */  
-  function get_list_column_info() {
+   */
+  public function get_list_column_info() {
     $out = parent::get_list_column_info();
     $out = array(
       'name'                  => array('title' => t('Name')),
@@ -230,11 +229,11 @@ class backup_migrate_location extends backup_migrate_item {
 
   /**
    * Get a row of data to be used in a list of items of this type.
-   */  
-  function get_list_row() {
+   */
+  public function get_list_row() {
     $out = parent::get_list_row();
 
-    // Supress locations with no actions as there's no value in showing them (and they may confuse new users).
+    // Suppress locations with no actions as there's no value in showing them (and they may confuse new users).
     if (empty($out['actions'])) {
       return NULL;
     }
@@ -244,13 +243,13 @@ class backup_migrate_location extends backup_migrate_item {
   /**
    * Get the action links for a location.
    */
-  function get_action_links() {
+  public function get_action_links() {
     $out = parent::get_action_links();
     $item_id = $this->get_id();
 
     // Don't display the download/delete/restore ops if they are not available for this location.
     if ($this->op('list files') && user_access("access backup files")) {
-      $out = array('list files' => l(t("list files"), BACKUP_MIGRATE_MENU_PATH . "/$this->type_name/list/files/". $item_id)) + $out;
+      $out = array('list files' => l(t("list files"), BACKUP_MIGRATE_MENU_PATH . "/$this->type_name/list/files/" . $item_id)) + $out;
     }
     if (!$this->op('configure') || !user_access('administer backup and migrate')) {
       unset($out['edit']);
@@ -261,94 +260,94 @@ class backup_migrate_location extends backup_migrate_item {
   /**
    * Determine if we can read the given file.
    */
-  function can_read_file($file_id) {
+  public function can_read_file($file_id) {
     return $this->op('restore');
   }
 
   /**
    * Get the form for the settings for this location type.
    */
-  function settings_default() {
+  public function settings_default() {
     return array();
   }
 
   /**
    * Get the form for the settings for this location.
    */
-  function settings_form($form) {
+  public function settings_form($form) {
     return $form;
   }
 
   /**
    * Validate the form for the settings for this location.
    */
-  function settings_form_validate($form_values) {
+  public function settings_form_validate($form_values) {
   }
 
   /**
    * Submit the settings form. Any values returned will be saved.
    */
-  function settings_form_submit($form_values) {
+  public function settings_form_submit($form_values) {
     return $form_values;
   }
 
   /**
    * Get the form for the settings for this filter.
    */
-  function backup_settings_default() {
+  public function backup_settings_default() {
     return array();
   }
 
   /**
    * Get the form for the settings for this filter.
    */
-  function backup_settings_form($settings) {
+  public function backup_settings_form($settings) {
     return array();
   }
 
   /**
    * Get the form for the settings for this filter.
    */
-  function backup_settings_form_validate($form, &$form_state) {
+  public function backup_settings_form_validate($form, &$form_state) {
   }
 
   /**
    * Submit the settings form. Any values returned will be saved.
    */
-  function backup_settings_form_submit($form, &$form_state) {
+  public function backup_settings_form_submit($form, &$form_state) {
   }
 
   /**
    * Get the form for the settings for this filter.
    */
-  function restore_settings_default() {
+  public function restore_settings_default() {
     return array();
   }
 
   /**
    * Get the form for the settings for this filter.
    */
-  function restore_settings_form($settings) {
+  public function restore_settings_form($settings) {
     return array();
   }
 
   /**
    * Get the form for the settings for this filter.
    */
-  function restore_settings_form_validate($form_values) {
+  public function restore_settings_form_validate($form_values) {
   }
 
   /**
    * Submit the settings form. Any values returned will be saved.
    */
-  function restore_settings_form_submit($form_values) {
+  public function restore_settings_form_submit($form_values) {
     return $form_values;
   }
 
   /**
    * Create a new location of the correct type.
    */
-  function create($params = array()) {
+  public function create($params = array()) {
     $out = NULL;
     $types = backup_migrate_get_location_subtypes();
     // Get the type passed in in the params, or if none, check the url for a valid type name.
@@ -358,7 +357,7 @@ class backup_migrate_location extends backup_migrate_item {
     if ($location_type && ($type = @$types[$location_type])) {
       // Include the necessary file if specified by the type.
       if (!empty($type['file'])) {
-        require_once './'. $type['file'];
+        require_once './' . $type['file'];
       }
       $out = new $type['class']($params + array('subtype' => $location_type));
     }
@@ -369,37 +368,37 @@ class backup_migrate_location extends backup_migrate_item {
     return $out;
   }
 
-   /**
+  /**
    * Get a url from the parts.
    */
-  function url($hide_password = TRUE) {
+  public function url($hide_password = TRUE) {
     return $this->glue_url($this->dest_url, $hide_password);
   }
 
   /**
    * Glue a URLs component parts back into a URL.
    */
-  function glue_url($parts, $hide_password = TRUE) {
+  public function glue_url($parts, $hide_password = TRUE) {
     // Obscure the password if we need to.
     $parts['pass'] = $hide_password ? "" : $parts['pass'];
 
     // Assemble the URL.
     $out = "";
-    $out .= $parts['scheme'] .'://';
+    $out .= $parts['scheme'] . '://';
     $out .= $parts['user'] ? urlencode($parts['user']) : '';
-    $out .= ($parts['user'] && $parts['pass']) ? ":". urlencode($parts['pass']) : '';
+    $out .= ($parts['user'] && $parts['pass']) ? ":" . urlencode($parts['pass']) : '';
     $out .= ($parts['user'] || $parts['pass']) ? "@" : "";
     $out .= $parts['host'];
-    $out .= !empty($parts['port']) ? ':'. $parts['port'] : '';
-    $out .= "/". $parts['path'];
+    $out .= !empty($parts['port']) ? ':' . $parts['port'] : '';
+    $out .= "/" . $parts['path'];
     return $out;
   }
 
   /**
    * Break a URL into it's component parts.
    */
-  function set_url($url) {
-    $parts          = (array)parse_url($url);
+  public function set_url($url) {
+    $parts          = (array) parse_url($url);
     $parts['user'] = urldecode(@$parts['user']);
     $parts['pass'] = urldecode(@$parts['pass']);
     $parts['path'] = urldecode(@$parts['path']);
@@ -410,7 +409,7 @@ class backup_migrate_location extends backup_migrate_item {
   /**
    * Retrieve a list of filetypes supported by this source/destination.
    */
-  function file_types() {
+  public function file_types() {
     return array();
   }
 
@@ -423,67 +422,29 @@ class backup_migrate_location_remote extends backup_migrate_location {
   /**
    * The location is a URI so parse it and store the parts.
    */
-  function get_location() {
+  public function get_location() {
     return $this->url(FALSE);
   }
 
   /**
    * The location to display is the url without the password.
    */
-  function get_display_location() {
+  public function get_display_location() {
     return $this->url(TRUE);
   }
 
   /**
    * Return the location with the password.
    */
-  function set_location($location) {
+  public function set_location($location) {
     $this->location = $location;
     $this->set_url($location);
   }
 
   /**
-   * Get a url from the parts.
-   */
-  function url($hide_password = TRUE) {
-    return $this->glue_url($this->dest_url, $hide_password);
-  }
-
-  /**
-   * Glue a URLs component parts back into a URL.
-   */
-  function glue_url($parts, $hide_password = TRUE) {
-    // Obscure the password if we need to.
-    $parts['pass'] = $hide_password ? "" : $parts['pass'];
-
-    // Assemble the URL.
-    $out = "";
-    $out .= $parts['scheme'] .'://';
-    $out .= $parts['user'] ? urlencode($parts['user']) : '';
-    $out .= ($parts['user'] && $parts['pass']) ? ":". urlencode($parts['pass']) : '';
-    $out .= ($parts['user'] || $parts['pass']) ? "@" : "";
-    $out .= $parts['host'];
-    $out .= !empty($parts['port']) ? ':'. $parts['port'] : '';
-    $out .= "/". $parts['path'];
-    return $out;
-  }
-
-  /**
-   * Break a URL into it's component parts.
-   */
-  function set_url($url) {
-    $parts          = (array)parse_url($url);
-    $parts['user'] = urldecode(@$parts['user']);
-    $parts['pass'] = urldecode(@$parts['pass']);
-    $parts['path'] = urldecode(@$parts['path']);
-    $parts['path']  = ltrim(@$parts['path'], "/");
-    $this->dest_url = $parts;
-  }
-
-  /**
-   * location configuration callback.
+   * Location configuration callback.
    */
-  function edit_form() {
+  public function edit_form() {
     $form = parent::edit_form();
     $form['scheme'] = array(
       "#type" => "select",
@@ -534,10 +495,10 @@ class backup_migrate_location_remote extends backup_migrate_location {
   /**
    * Submit the configuration form. Glue the url together and add the old password back if a new one was not specified.
    */
-  function edit_form_submit($form, &$form_state) {
+  public function edit_form_submit($form, &$form_state) {
     $form_state['values']['pass'] = $form_state['values']['pass'] ? $form_state['values']['pass'] : $form_state['values']['old_password'];
     $form_state['values']['location'] = $this->glue_url($form_state['values'], FALSE);
     parent::edit_form_submit($form, $form_state);
   }
-}
 
+}

+ 62 - 44
sites/all/modules/contrib/admin/backup_migrate/includes/profiles.inc

@@ -1,6 +1,5 @@
 <?php
 
-
 /**
  * @file
  * All of the settings profiles handling code for Backup and Migrate.
@@ -32,12 +31,7 @@ function backup_migrate_backup_migrate_profile_subtypes() {
  */
 function backup_migrate_get_profiles() {
   backup_migrate_include('filters');
-  static $profiles = NULL;
-
-  // Get the list of profiles and cache them locally.
-  if ($profiles === NULL) {
-    $profiles = backup_migrate_crud_get_items('profile');
-  }
+  $profiles = backup_migrate_crud_get_items('profile');
   return $profiles;
 }
 
@@ -49,7 +43,7 @@ function backup_migrate_get_profiles() {
 function backup_migrate_backup_migrate_profiles_alter(&$profiles) {
   foreach ($profiles as $id => $profile) {
     // Set the default values for filter setting which don't exist in the profile.
-    $profiles[$id]->filters = (array)@$profile->filters + (array)backup_migrate_filters_settings_default('backup');
+    $profiles[$id]->filters = (array) @$profile->filters + (array) backup_migrate_filters_settings_default('backup');
   }
 }
 
@@ -80,7 +74,7 @@ function backup_migrate_backup_migrate_profiles() {
  */
 function _backup_migrate_get_profile_form_item_options() {
   $out = array();
-  foreach ((array)backup_migrate_get_profiles() as $key => $profile) {
+  foreach ((array) backup_migrate_get_profiles() as $key => $profile) {
     $out[$key] = $profile->get('name');
   }
   return $out;
@@ -91,8 +85,8 @@ function _backup_migrate_get_profile_form_item_options() {
  */
 function _backup_migrate_ui_backup_settings_form($profile) {
   drupal_add_js(array('backup_migrate' => array('checkboxLinkText' => t('View as checkboxes'))), array('type' => 'setting'));
-  drupal_add_js(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.js', array('type' => 'file', 'scope' => 'footer'));
-  drupal_add_css(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.css');
+  drupal_add_js(drupal_get_path('module', 'backup_migrate') . '/backup_migrate.js', array('type' => 'file', 'scope' => 'footer'));
+  drupal_add_css(drupal_get_path('module', 'backup_migrate') . '/backup_migrate.css');
 
   backup_migrate_include('files', 'destinations', 'filters');
 
@@ -126,14 +120,19 @@ function _backup_migrate_ui_backup_settings_form($profile) {
   }
 
   $form['file']['append_timestamp'] = array(
-    "#type" => "checkbox",
-    "#title" => t("Append a timestamp."),
+    "#type" => "radios",
+    '#options' => array(
+      0 => t('Create separate backups if `Backup file name` already exists'),
+      2 => t('Overwrite the existing backup file'),
+      1 => t('Append the timestamp'),
+    ),
+    "#title" => t("Save mode"),
     "#default_value" => $profile->append_timestamp,
   );
   $form['file']['timestamp_format_wrapper'] = array(
     '#type' => 'backup_migrate_dependent',
     '#dependencies' => array(
-      'append_timestamp' => TRUE,
+      'append_timestamp' => 1,
     ),
   );
   $form['file']['timestamp_format_wrapper']['timestamp_format'] = array(
@@ -150,8 +149,8 @@ function _backup_migrate_ui_backup_settings_form($profile) {
   if ($form['advanced']) {
     $form['advanced']['#type'] = 'fieldset';
     $form['advanced']['#title'] = t('Advanced Options');
-    $form['advanced']['#collapsed'] = true;
-    $form['advanced']['#collapsible'] = true;
+    $form['advanced']['#collapsed'] = TRUE;
+    $form['advanced']['#collapsible'] = TRUE;
   }
 
   $form['#validate'][]  = '_backup_migrate_ui_backup_settings_form_validate';
@@ -173,7 +172,7 @@ function _backup_migrate_ui_backup_settings_form_validate($form, &$form_state) {
 function _backup_migrate_ui_backup_settings_form_submit($form, &$form_state) {
   backup_migrate_filters_settings_form_submit('backup', $form, $form_state);
 }
-  
+
 /**
  * Get the default profile.
  */
@@ -207,17 +206,36 @@ function _backup_migrate_profile_saved_default_profile($profile_id = NULL) {
  * A profile class for crud operations.
  */
 class backup_migrate_profile extends backup_migrate_item {
-  var $db_table = "backup_migrate_profiles";
-  var $type_name = "profile";
-  var $singular = 'settings profile';
-  var $plural = 'settings profiles';
-  var $title_plural = 'Settings Profiles';
-  var $title_singular = 'Settings Profile';
+  public $db_table = "backup_migrate_profiles";
+  public $type_name = "profile";
+  public $singular = 'settings profile';
+  public $plural = 'settings profiles';
+  public $title_plural = 'Settings Profiles';
+  public $title_singular = 'Settings Profile';
+
+  /**
+   * Perform a shallow merge of the defaults and the parameters.
+   *
+   * This is needed because otherwise it will *combine* the nested arrays and
+   * make it impossible to deselect database tables from the 'nodata' setting.
+   *
+   * @param array $params
+   */
+  public function __construct(array $params = array()) {
+    $params = (array) $params;
+    $defaults = (array) $this->get_default_values();
+    foreach ($defaults as $key => $val) {
+      if (!isset($params[$key])) {
+        $params[$key] = $val;
+      }
+    }
+    $this->from_array($params);
+  }
 
   /**
    * This function is not supposed to be called. It is just here to help the po extractor out.
    */
-  function strings() {
+  public function strings() {
     // Help the pot extractor find these strings.
     t('Settings Profile');
     t('Settings Profiles');
@@ -228,22 +246,22 @@ class backup_migrate_profile extends backup_migrate_item {
   /**
    * Get the default values for standard parameters.
    */
-  function get_default_values() {
+  public function get_default_values() {
     return _backup_migrate_profile_default_profile() + array('name' => t("Untitled Profile"));
   }
 
   /**
    * Get a table of all items of this type.
-   */  
-  function get_list() {
-    drupal_add_css(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.css');
+   */
+  public function get_list() {
+    drupal_add_css(drupal_get_path('module', 'backup_migrate') . '/backup_migrate.css');
     return parent::get_list();
   }
 
   /**
    * Get the columns needed to list the type.
-   */  
-  function get_list_column_info() {
+   */
+  public function get_list_column_info() {
     $out = parent::get_list_column_info();
     $out = array(
       'name'                  => array('title' => t('Name')),
@@ -256,7 +274,7 @@ class backup_migrate_profile extends backup_migrate_item {
   /**
    * Set the source of this setings profile. Takes either a source object or source id.
    */
-  function set_source($source) {
+  public function set_source($source) {
     if (is_object($source)) {
       $this->source = $source;
       $this->source_id = $source->get_id();
@@ -270,7 +288,7 @@ class backup_migrate_profile extends backup_migrate_item {
   /**
    * Get the source of the profile.
    */
-  function get_source() {
+  public function get_source() {
     backup_migrate_include('locations');
     if (!empty($this->source_id) && (empty($this->source) || $this->source->get_id() !== $this->source_id)) {
       $this->source = backup_migrate_get_source($this->source_id);
@@ -281,7 +299,7 @@ class backup_migrate_profile extends backup_migrate_item {
   /**
    * Get the name of the source.
    */
-  function get_source_name() {
+  public function get_source_name() {
     if ($source = $this->get_source()) {
       return $source->get_name();
     }
@@ -291,22 +309,22 @@ class backup_migrate_profile extends backup_migrate_item {
   /**
    * Get the destination of the profile.
    */
-  function get_destination() {
-    $destinations = (array)$this->get_destinations();
+  public function get_destination() {
+    $destinations = (array) $this->get_destinations();
     return reset($destinations);
   }
 
   /**
    * Get the destination of the profile.
    */
-  function get_destinations() {
+  public function get_destinations() {
     backup_migrate_include('destinations');
     if (empty($this->destinations)) {
       $this->destinations = array();
       $ids = $weights = array();
       if (!empty($this->destination_id)) {
-        foreach ((array)$this->destination_id as $destination_id) {
-          if (!in_array($destination_id, $ids) && $destination = backup_migrate_get_destination($destination_id)) {            
+        foreach ((array) $this->destination_id as $destination_id) {
+          if (!in_array($destination_id, $ids) && $destination = backup_migrate_get_destination($destination_id)) {
             $this->destinations[] = $destination;
             $weights[] = $destination->get('weight');
             $ids[] = $destination_id;
@@ -323,7 +341,7 @@ class backup_migrate_profile extends backup_migrate_item {
   /**
    * Get the name of the destination.
    */
-  function get_destination_name() {
+  public function get_destination_name() {
     $out = array();
     foreach ($this->get_destinations() as $destination) {
       $out[] = $destination->get_name();
@@ -335,9 +353,9 @@ class backup_migrate_profile extends backup_migrate_item {
   }
 
   /**
-   * Get the source and destinations specified in the given settings profile
+   * Get the source and destinations specified in the given settings profile.
    */
-  function get_all_locations() {
+  public function get_all_locations() {
     $out = array();
     $out += $this->get('destinations');
     $out[] = $this->get('source');
@@ -347,7 +365,7 @@ class backup_migrate_profile extends backup_migrate_item {
   /**
    * Get the edit form.
    */
-  function edit_form() {
+  public function edit_form() {
     $form = parent::edit_form();
     $form['name'] = array(
       "#type" => "textfield",
@@ -362,8 +380,8 @@ class backup_migrate_profile extends backup_migrate_item {
   /**
    * Get the message to send to the user when confirming the deletion of the item.
    */
-  function delete_confirm_message() {
+  public function delete_confirm_message() {
     return t('Are you sure you want to delete the profile %name? Any schedules using this profile will be disabled.', array('%name' => $this->get('name')));
   }
-}
 
+}

+ 95 - 94
sites/all/modules/contrib/admin/backup_migrate/includes/schedules.inc

@@ -4,13 +4,13 @@
  * @file
  * All of the schedule handling code needed for Backup and Migrate.
  */
- 
+
 define('BACKUP_MIGRATE_KEEP_ALL', 0);
 define('BACKUP_MIGRATE_STANDARD_DELETE', -1);
 define('BACKUP_MIGRATE_SMART_DELETE', -2);
 
 define('BACKUP_MIGRATE_CRON_BUILTIN', 'builtin');
-define('BACKUP_MIGRATE_CRON_ELYSIA',  'elysia');
+define('BACKUP_MIGRATE_CRON_ELYSIA', 'elysia');
 define('BACKUP_MIGRATE_CRON_NONE', 'none');
 
 
@@ -67,7 +67,7 @@ function backup_migrate_schedules_run() {
  */
 function backup_migrate_schedule_run($schedule_id) {
   backup_migrate_include('profiles');
-  if ($schedule = backup_migrate_get_schedule($schedule_id)) {
+  if (($schedule = backup_migrate_get_schedule($schedule_id)) && $schedule->is_enabled()) {
     $schedule->run();
   }
   backup_migrate_cleanup();
@@ -77,7 +77,7 @@ function backup_migrate_schedule_run($schedule_id) {
  * Get all the available backup schedules.
  */
 function backup_migrate_get_schedules() {
-  static $schedules = NULL;
+  $schedules = &drupal_static('backup_migrate_get_schedules');
   // Get the list of schedules and cache them locally.
   if ($schedules === NULL) {
     $schedules = backup_migrate_crud_get_items('schedule');
@@ -97,18 +97,18 @@ function backup_migrate_get_schedule($schedule_id) {
  * A schedule class for crud operations.
  */
 class backup_migrate_schedule extends backup_migrate_item {
-  var $db_table = "backup_migrate_schedules";
-  var $type_name = 'schedule';
-  var $singular = 'schedule';
-  var $plural = 'schedules';
-  var $title_plural = 'Schedules';
-  var $title_singular = 'Schedule';
-  var $default_values = array();
+  public $db_table = "backup_migrate_schedules";
+  public $type_name = 'schedule';
+  public $singular = 'schedule';
+  public $plural = 'schedules';
+  public $title_plural = 'Schedules';
+  public $title_singular = 'Schedule';
+  public $default_values = array();
 
   /**
    * This function is not supposed to be called. It is just here to help the po extractor out.
    */
-  function strings() {
+  public function strings() {
     // Help the pot extractor find these strings.
     t('Schedule');
     t('Schedules');
@@ -119,23 +119,23 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Get the default values for this item.
    */
-  function get_default_values() {
+  public function get_default_values() {
     return array(
-        'name' => t("Untitled Schedule"),
-        'source_id' => 'db',
-        'enabled' => 1,
-        'keep' => BACKUP_MIGRATE_KEEP_ALL,
-        'period' => 60 * 60 * 24,
-        'storage' => BACKUP_MIGRATE_STORAGE_NONE,
-        'cron' => BACKUP_MIGRATE_CRON_BUILTIN,
-        'cron_schedule' => '0 4 * * *',
-      );
+      'name' => t("Untitled Schedule"),
+      'source_id' => 'db',
+      'enabled' => 1,
+      'keep' => BACKUP_MIGRATE_KEEP_ALL,
+      'period' => 60 * 60 * 24,
+      'storage' => BACKUP_MIGRATE_STORAGE_NONE,
+      'cron' => BACKUP_MIGRATE_CRON_BUILTIN,
+      'cron_schedule' => '0 4 * * *',
+    );
   }
 
   /**
    * Return as an array of values.
    */
-  function to_array() {
+  public function to_array() {
     $out = parent::to_array();
     unset($out['last_run']);
     return $out;
@@ -143,8 +143,8 @@ class backup_migrate_schedule extends backup_migrate_item {
 
   /**
    * Get the columns needed to list the type.
-   */  
-  function get_list_column_info() {
+   */
+  public function get_list_column_info() {
     $out = parent::get_list_column_info();
     $out = array(
       'name'                  => array('title' => t('Name')),
@@ -160,8 +160,8 @@ class backup_migrate_schedule extends backup_migrate_item {
 
   /**
    * Get the columns needed to list the type.
-   */  
-  function get_settings_path() {
+   */
+  public function get_settings_path() {
     // Pull the schedules tab up a level to the top.
     return BACKUP_MIGRATE_MENU_PATH . '/' . $this->type_name;
   }
@@ -169,7 +169,7 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Get the menu items for manipulating this type.
    */
-  function get_menu_items() {
+  public function get_menu_items() {
     $items = parent::get_menu_items();
     $path = $this->get_settings_path();
     return $items;
@@ -178,15 +178,15 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Get a row of data to be used in a list of items of this type.
    */
-  function get_list_row() {
-    drupal_add_css(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.css');
+  public function get_list_row() {
+    drupal_add_css(drupal_get_path('module', 'backup_migrate') . '/backup_migrate.css');
     $row = parent::get_list_row();
     if (!$this->is_enabled()) {
       foreach ($row as $key => $field) {
         if (!is_array($field)) {
           $row[$key] = array('data' => $field, 'class' => 'schedule-list-disabled');
         }
-        else if (isset($row[$key]['class'])) {
+        elseif (isset($row[$key]['class'])) {
           $row[$key]['class'] .= ' schedule-list-disabled';
         }
         else {
@@ -200,7 +200,7 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Is the schedule enabled and valid.
    */
-  function is_enabled() {
+  public function is_enabled() {
     $destination = $this->get_destination();
     $profile = $this->get_profile();
     return (!empty($this->enabled) && !empty($destination) && !empty($profile));
@@ -209,15 +209,15 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Get the destination object of the schedule.
    */
-  function get_destination() {
-    $destinations = (array)$this->get_destinations();
+  public function get_destination() {
+    $destinations = (array) $this->get_destinations();
     return reset($destinations);
   }
 
   /**
    * Get the destination object of the schedule.
    */
-  function get_destination_ids() {
+  public function get_destination_ids() {
     $out = array();
     foreach (array('destination_id', 'copy_destination_id') as $key) {
       if ($id = $this->get($key)) {
@@ -230,7 +230,7 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Get the destination object of the schedule.
    */
-  function get_destinations() {
+  public function get_destinations() {
     backup_migrate_include('destinations');
     $out = array();
     foreach ($this->get_destination_ids() as $id) {
@@ -244,14 +244,14 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Get the destination object of the schedule.
    */
-  function get_destination_remote() {
+  public function get_destination_remote() {
     backup_migrate_include('destinations');
     return backup_migrate_get_destination($this->get('destination_remote_id'));
   }
- /**
+  /**
    * Get the destination object of the schedule.
    */
-  function get_destination_local() {
+  public function get_destination_local() {
     backup_migrate_include('destinations');
     return backup_migrate_get_destination($this->get('destination_local_id'));
   }
@@ -259,28 +259,28 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Get the name of the destination.
    */
-  function get_destination_name() {
+  public function get_destination_name() {
     if ($destinations = $this->get_destinations()) {
       $out = array();
-      foreach ((array)$destinations as $destination) {
+      foreach ((array) $destinations as $destination) {
         $out[] = check_plain($destination->get_name());
       }
       return implode(', ', $out);
     }
-    return '<div class="row-error">'. t("Missing") .'</div>';
+    return '<div class="row-error">' . t("Missing") . '</div>';
   }
 
   /**
    * Get the destination of the schedule.
    */
-  function get_profile() {
+  public function get_profile() {
     backup_migrate_include('profiles');
     if ($settings = backup_migrate_get_profile($this->get('profile_id'))) {
       $settings->file_info = empty($settings->file_info) ? array() : $settings->file_info;
       $settings->file_info += array(
         'schedule_id'   => $this->get_id(),
         'schedule_name' => $this->get('name'),
-      );      
+      );
     }
     return $settings;
 
@@ -289,23 +289,23 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Get the name of the source.
    */
-  function get_profile_name() {
+  public function get_profile_name() {
     if ($profile = $this->get_profile()) {
       return check_plain($profile->get_name());
     }
-    return '<div class="row-error">'. t("Missing") .'</div>';
+    return '<div class="row-error">' . t("Missing") . '</div>';
   }
 
   /**
    * Format a frequency in human-readable form.
    */
-  function get_frequency_description() {
+  public function get_frequency_description() {
     $period = $this->get_frequency_period();
     $cron = $this->get('cron');
     if ($cron == BACKUP_MIGRATE_CRON_BUILTIN) {
       $out = format_plural(($this->period / $period['seconds']), $period['singular'], $period['plural']);
     }
-    else if ($cron == BACKUP_MIGRATE_CRON_ELYSIA) {
+    elseif ($cron == BACKUP_MIGRATE_CRON_ELYSIA) {
       $out = $this->get('cron_schedule');
     }
     else {
@@ -317,26 +317,26 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Format the number to keep in human-readable form.
    */
-  function get_keep_description() {
+  public function get_keep_description() {
     return $this->generate_keep_description($this->keep);
   }
 
   /**
-   * Format a number to keep in human readable from
+   * Format a number to keep in human readable from.
    */
-  function generate_keep_description($keep, $terse = TRUE) {
+  public function generate_keep_description($keep, $terse = TRUE) {
     if ($keep == BACKUP_MIGRATE_KEEP_ALL) {
       return t('all backups');
     }
-    else if ($keep == BACKUP_MIGRATE_SMART_DELETE) {
+    elseif ($keep == BACKUP_MIGRATE_SMART_DELETE) {
       $keep_hourly = variable_get('backup_migrate_smart_keep_hourly', BACKUP_MIGRATE_SMART_KEEP_HOURLY);
       $keep_daily  = variable_get('backup_migrate_smart_keep_daily', BACKUP_MIGRATE_SMART_KEEP_DAILY);
       $keep_weekly = variable_get('backup_migrate_smart_keep_weekly', BACKUP_MIGRATE_SMART_KEEP_WEEKLY);
       if ($terse) {
-      return t('!hours hourly, !days daily, !weeks weekly backups',
+        return t('!hours hourly, !days daily, !weeks weekly backups',
           array(
             '!hours' => $keep_hourly == PHP_INT_MAX ? t('all') : $keep_hourly,
-            '!days'  => $keep_daily  == PHP_INT_MAX ? t('all') : $keep_daily,
+            '!days'  => $keep_daily == PHP_INT_MAX ? t('all') : $keep_daily,
             '!weeks' => $keep_weekly == PHP_INT_MAX ? t('all') : $keep_weekly,
           ));
       }
@@ -344,7 +344,7 @@ class backup_migrate_schedule extends backup_migrate_item {
         return t('hourly backups !hours, daily backups !days and weekly backups !weeks',
           array(
             '!hours' => $keep_hourly == PHP_INT_MAX ? t('forever') : format_plural($keep_hourly, 'for 1 hour', 'for the past @count hours'),
-            '!days'  => $keep_daily  == PHP_INT_MAX ? t('forever') : format_plural($keep_daily,  'for 1 day',  'for the past @count days'),
+            '!days'  => $keep_daily == PHP_INT_MAX ? t('forever') : format_plural($keep_daily, 'for 1 day', 'for the past @count days'),
             '!weeks' => $keep_weekly == PHP_INT_MAX ? t('forever') : format_plural($keep_weekly, 'for 1 week', 'for the past @count weeks'),
           )
         );
@@ -357,14 +357,14 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Format the enabled status in human-readable form.
    */
-  function get_enabled_description() {
+  public function get_enabled_description() {
     return !empty($this->enabled) ? t('Enabled') : t('Disabled');
   }
 
   /**
    * Format the enabled status in human-readable form.
    */
-  function get_last_run_description() {
+  public function get_last_run_description() {
     $last_run = $this->get('last_run');
     return !empty($last_run) ? format_date($last_run, 'small') : t('Never');
   }
@@ -372,21 +372,21 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Get the number of excluded tables.
    */
-  function get_exclude_tables_count() {
+  public function get_exclude_tables_count() {
     return count($this->exclude_tables) ? count($this->exclude_tables) : t("No tables excluded");
   }
 
   /**
    * Get the number of excluded tables.
    */
-  function get_nodata_tables_count() {
+  public function get_nodata_tables_count() {
     return count($this->nodata_tables) ? count($this->nodata_tables) : t("No data omitted");
   }
 
   /**
    * Get the edit form.
    */
-  function edit_form() {
+  public function edit_form() {
     $form = parent::edit_form();
     backup_migrate_include('destinations', 'sources', 'profiles');
 
@@ -404,7 +404,7 @@ class backup_migrate_schedule extends backup_migrate_item {
       "#options" => _backup_migrate_get_profile_form_item_options(),
       "#default_value" => $this->get('profile_id'),
     );
-    $form['profile_id']['#description'] = ' '. l(t("Create new profile"), BACKUP_MIGRATE_MENU_PATH . "/profile/add");
+    $form['profile_id']['#description'] = ' ' . l(t('Create new profile'), BACKUP_MIGRATE_MENU_PATH . '/settings/profile/add');
     if (!$form['profile_id']['#options']) {
       $form['profile_id']['#options'] = array('0' => t('-- None Available --'));
     }
@@ -437,7 +437,7 @@ class backup_migrate_schedule extends backup_migrate_item {
       "#default_value" => $cron ? $cron : BACKUP_MIGRATE_CRON_BUILTIN,
       '#parents' => array('cron'),
     );
-    
+
     $form['cron_settings']['period_settings'] = array(
       '#type' => 'backup_migrate_dependent',
       '#dependencies' => array(
@@ -465,7 +465,6 @@ class backup_migrate_schedule extends backup_migrate_item {
       '#parents' => array('period', 'type'),
     );
 
-
     $form['cron_settings']['cron_elysia'] = array(
       "#type" => "radio",
       "#title" => t('Run using Elysia cron'),
@@ -474,7 +473,7 @@ class backup_migrate_schedule extends backup_migrate_item {
       "#default_value" => $cron ? $cron : BACKUP_MIGRATE_CRON_BUILTIN,
       '#parents' => array('cron'),
     );
-    if (!module_exists('elysia_cron')) {
+    if (!module_exists('elysia_cron') && !module_exists('ultimate_cron')) {
       $form['cron_settings']['cron_elysia']['#disabled'] = TRUE;
       $form['cron_settings']['cron_elysia']['#description'] .= ' ' . t('Install !elysia to enable this option.', array('!elysia' => l(t('Elysia Cron'), 'http://drupal.org/project/elysia_cron')));
     }
@@ -488,7 +487,7 @@ class backup_migrate_schedule extends backup_migrate_item {
       "#type" => "textfield",
       "#title" => t('Cron Schedule'),
       '#length' => 10,
-      "#description" => t('Specify the frequecy of the schedule using standard cron notation. For more information see the !elysiareadme.', array('!elysiareadme' => l(t('the Elysia Cron README'), 'http://drupalcode.org/project/elysia_cron.git/blob/refs/heads/7.x-1.x:/README.txt'))),
+      "#description" => t('Specify the frequency of the schedule using standard cron notation. For more information see the !elysiareadme.', array('!elysiareadme' => l(t('the Elysia Cron README'), 'http://drupalcode.org/project/elysia_cron.git/blob/refs/heads/7.x-1.x:/README.txt'))),
       "#default_value" => $this->get('cron_schedule'),
       '#parents' => array('cron_schedule'),
     );
@@ -502,8 +501,6 @@ class backup_migrate_schedule extends backup_migrate_item {
       '#parents' => array('cron'),
     );
 
-
-
     $keep = $this->get('keep');
     $form['delete'] = array(
       '#type' => 'checkbox',
@@ -558,7 +555,7 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Submit the edit form.
    */
-  function edit_form_validate($form, &$form_state) {
+  public function edit_form_validate($form, &$form_state) {
     if (!is_numeric($form_state['values']['period']['number']) || $form_state['values']['period']['number'] <= 0) {
       form_set_error('period][number', t('Backup period must be a number greater than 0.'));
     }
@@ -566,10 +563,10 @@ class backup_migrate_schedule extends backup_migrate_item {
     if (!$form_state['values']['delete']) {
       $form_state['values']['keep'] = 0;
     }
-    else if ($form_state['values']['deletetype'] == BACKUP_MIGRATE_SMART_DELETE) {
+    elseif ($form_state['values']['deletetype'] == BACKUP_MIGRATE_SMART_DELETE) {
       $form_state['values']['keep'] = BACKUP_MIGRATE_SMART_DELETE;
     }
-    else if (!is_numeric($form_state['values']['keep']) || $form_state['values']['keep'] <= 0) {
+    elseif (!is_numeric($form_state['values']['keep']) || $form_state['values']['keep'] <= 0) {
       form_set_error('keep', t('Number to keep must be a number greater than 0.'));
     }
     parent::edit_form_validate($form, $form_state);
@@ -578,7 +575,7 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Submit the edit form.
    */
-  function edit_form_submit($form, &$form_state) {
+  public function edit_form_submit($form, &$form_state) {
     $periods = $this->frequency_periods();
     $period = $periods[$form_state['values']['period']['type']];
     $form_state['values']['period'] = $form_state['values']['period']['number'] * $period['seconds'];
@@ -586,9 +583,9 @@ class backup_migrate_schedule extends backup_migrate_item {
   }
 
   /**
-   * Get the period of the frequency (ie: seconds, minutes etc.)
+   * Get the period of the frequency (ie: seconds, minutes etc.).
    */
-  function get_frequency_period() {
+  public function get_frequency_period() {
     foreach (array_reverse($this->frequency_periods()) as $period) {
       if ($period['seconds'] && ($this->period % $period['seconds']) === 0) {
         return $period;
@@ -600,7 +597,7 @@ class backup_migrate_schedule extends backup_migrate_item {
    * Get a list of available backup periods. Only returns time periods which have a
    *  (reasonably) consistent number of seconds (ie: no months).
    */
-  function frequency_periods() {
+  public function frequency_periods() {
     return array(
       'seconds' => array('type' => 'seconds', 'seconds' => 1, 'title' => t('Seconds'), 'singular' => t('Once a second'), 'plural' => t('Every @count seconds')),
       'minutes' => array('type' => 'minutes', 'seconds' => 60, 'title' => t('Minutes'), 'singular' => t('Once a minute'), 'plural' => t('Every @count minutes')),
@@ -613,14 +610,14 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Get the message to send to the user when confirming the deletion of the item.
    */
-  function delete_confirm_message() {
+  public function delete_confirm_message() {
     return t('Are you sure you want to delete the schedule %name? Backups made with this schedule will not be deleted.', array('%name' => $this->get('name')));
   }
 
   /**
    * Perform the cron action. Run the backup if enough time has elapsed.
    */
-  function cron() {
+  public function cron() {
     $now = time();
 
     // Add a small negative buffer (1% of the entire period) to the time to account for slight difference in cron run length.
@@ -635,13 +632,17 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Run the actual schedule.
    */
-  function run() {
+  public function run() {
+    // Clear cached profile data which could have been altered by previous
+    // schedule run; see #2672478
+    drupal_static_reset('backup_migrate_get_profiles');
+
     if ($settings = $this->get_profile()) {
       $settings->source_id = $this->get('source_id');
       $settings->destination_id = $this->get('destination_ids');
 
-      backup_migrate_perform_backup($settings);
       $this->update_last_run(time());
+      backup_migrate_perform_backup($settings);
       $this->remove_expired_backups();
     }
     else {
@@ -652,7 +653,7 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Set the last run time of a schedule to the given timestamp, or now if none specified.
    */
-  function update_last_run($timestamp = NULL) {
+  public function update_last_run($timestamp = NULL) {
     if ($timestamp === NULL) {
       $timestamp = time();
     }
@@ -662,31 +663,31 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Set the last run time of a schedule to the given timestamp, or now if none specified.
    */
-  function get_last_run() {
+  public function get_last_run() {
     return variable_get('backup_migrate_schedule_last_run_' . $this->get('id'), 0);
   }
 
   /**
    * Remove older backups keeping only the number specified by the aministrator.
    */
-  function remove_expired_backups() {
+  public function remove_expired_backups() {
     backup_migrate_include('destinations');
 
     $num_to_keep = $this->keep;
     // If num to keep is not 0 (0 is infinity).
-    foreach ((array)$this->get_destinations() as $destination) {
+    foreach ((array) $this->get_destinations() as $destination) {
       if ($destination && $destination->op('delete') && $destination_files = $destination->list_files()) {
         if ($num_to_keep == BACKUP_MIGRATE_SMART_DELETE) {
           $this->smart_delete_backups(
-            $destination, 
+            $destination,
             $destination_files,
             variable_get('backup_migrate_smart_keep_subhourly', BACKUP_MIGRATE_SMART_KEEP_SUBHOURLY),
-            variable_get('backup_migrate_smart_keep_hourly',    BACKUP_MIGRATE_SMART_KEEP_HOURLY),
-            variable_get('backup_migrate_smart_keep_daily',     BACKUP_MIGRATE_SMART_KEEP_DAILY),
-            variable_get('backup_migrate_smart_keep_weekly',    BACKUP_MIGRATE_SMART_KEEP_WEEKLY)
+            variable_get('backup_migrate_smart_keep_hourly', BACKUP_MIGRATE_SMART_KEEP_HOURLY),
+            variable_get('backup_migrate_smart_keep_daily', BACKUP_MIGRATE_SMART_KEEP_DAILY),
+            variable_get('backup_migrate_smart_keep_weekly', BACKUP_MIGRATE_SMART_KEEP_WEEKLY)
           );
         }
-        else if ($num_to_keep != BACKUP_MIGRATE_KEEP_ALL) {
+        elseif ($num_to_keep != BACKUP_MIGRATE_KEEP_ALL) {
           $this->delete_backups($destination, $destination_files, $num_to_keep);
         }
       }
@@ -696,7 +697,7 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Remove older backups keeping only the number specified by the aministrator.
    */
-  function delete_backups($destination, $files, $num_to_keep) {
+  public function delete_backups($destination, $files, $num_to_keep) {
     backup_migrate_include('destinations');
 
     $num_to_keep = $this->keep;
@@ -729,7 +730,7 @@ class backup_migrate_schedule extends backup_migrate_item {
   /**
    * Delete files keeping the specified number of hourly, daily, weekly and monthly backups.
    */
-  function smart_delete_backups($destination, $files, $keep_subhourly = 3600, $keep_hourly = 24, $keep_daily = 14, $keep_weekly = PHP_INT_MAX, $keep_monthly = PHP_INT_MAX) {
+  public function smart_delete_backups($destination, $files, $keep_subhourly = 3600, $keep_hourly = 24, $keep_daily = 14, $keep_weekly = PHP_INT_MAX, $keep_monthly = PHP_INT_MAX) {
     $now = time();
     $periods = array(
       'subhourly' => array(
@@ -739,19 +740,19 @@ class backup_migrate_schedule extends backup_migrate_item {
         'files' => array(),
       ),
       'hourly' => array(
-        'delta' => 60*60,
+        'delta' => 60 * 60,
         'keep' => $keep_hourly,
         'last_time' => 0,
         'files' => array(),
       ),
       'daily' => array(
-        'delta' => 60*60*24,
+        'delta' => 60 * 60 * 24,
         'keep' => $keep_daily,
         'last_time' => 0,
         'files' => array(),
       ),
       'weekly' => array(
-        'delta' => 60*60*24*7,
+        'delta' => 60 * 60 * 24 * 7,
         'keep' => $keep_weekly,
         'last_time' => 0,
         'files' => array(),
@@ -786,7 +787,7 @@ class backup_migrate_schedule extends backup_migrate_item {
           $keep_files[$id] = $id;
         }
       }
-      // Keep oldest backup or it will get deleted if it doesn't fall on an exact multiple of the period
+      // Keep oldest backup or it will get deleted if it doesn't fall on an exact multiple of the period.
       if ($id) {
         $keep_files[$id] = $id;
       }
@@ -799,5 +800,5 @@ class backup_migrate_schedule extends backup_migrate_item {
       }
     }
   }
-}
 
+}

+ 75 - 49
sites/all/modules/contrib/admin/backup_migrate/includes/sources.archivesource.inc

@@ -1,4 +1,9 @@
 <?php
+
+/**
+ * @file
+ */
+
 backup_migrate_include('sources.filesource');
 
 /**
@@ -11,27 +16,31 @@ backup_migrate_include('sources.filesource');
  *
  * @ingroup backup_migrate_destinations
  */
-
 class backup_migrate_files_destination_archivesource extends backup_migrate_destination_filesource {
-  var $supported_ops = array('source');
+  public $supported_ops = array('source');
 
-  function type_name() {
+  public function type_name() {
     return t("Site Archive Source");
   }
 
   /**
-   * Declare the current files directory as a backup source..
+   * Declares the current files directory as a backup source..
    */
-  function sources() {
-    $out  = array();
-    $out['archive'] = backup_migrate_create_destination('archive', array('machine_name' => 'archive', 'location' => '.', 'name' => t('Entire Site (code, files & DB)'), 'show_in_list' => FALSE));
+  public function sources() {
+    $out = array();
+    $out['archive'] = backup_migrate_create_destination('archive', array(
+      'machine_name' => 'archive',
+      'location' => '.',
+      'name' => t('Entire Site (code, files & DB)'),
+      'show_in_list' => FALSE,
+    ));
     return $out;
   }
 
   /**
-   * Return a list of backup filetypes.
+   * Returns a list of backup filetypes.
    */
-  function file_types() {
+  public function file_types() {
     return array(
       "sitearchive" => array(
         "extension" => "sitearchive.tar",
@@ -43,14 +52,15 @@ class backup_migrate_files_destination_archivesource extends backup_migrate_dest
   }
 
   /**
-   * Get the form for the settings for this destination.
+   * Gets the form for the settings for this destination.
    */
-  function backup_settings_default() {
+  public function backup_settings_default() {
     $out = parent::backup_settings_default();
     $excludes = explode("\n", $out['exclude_filepaths']);
     foreach ($excludes as $i => $exclude) {
       $excludes[$i] = 'public://' . $exclude;
     }
+    $excludes[] = 'private://backup_migrate';
     $excludes[] = conf_path() . '/settings.php';
     $excludes[] = file_directory_temp();
 
@@ -62,20 +72,22 @@ class backup_migrate_files_destination_archivesource extends backup_migrate_dest
   /**
    * Backup from this source.
    */
-  function _backup_to_file_php($file, $settings) {
+  public function _backup_to_file_php($file, $settings) {
     if ($this->check_libs()) {
+      $base_dir = $this->get_realpath();
+
       $excluded_paths = empty($settings->filters['exclude_filepaths']) ? '' : $settings->filters['exclude_filepaths'];
-      $exclude = $this->get_excluded_paths($excluded_paths);
-      $files = $this->get_files_to_backup($this->get_location(), $settings, $exclude, realpath('.') . '/');
+      $exclude = $this->get_excluded_paths($settings);
+      $files = $this->get_files_to_backup($this->get_realpath(), $settings, $exclude);
       if ($files) {
         $manifest = $this->generate_manifest();
         $db = $this->get_db();
 
         $file->push_type('sitearchive');
-        $gz = new Archive_Tar($file->filepath(), false);
+        $gz = new Archive_Tar($file->filepath(), FALSE);
 
-        $gz->addModify(array($manifest), $file->name .'/', dirname($manifest));
-        $gz->addModify($files, $file->name .'/docroot', $this->get_location());
+        $gz->addModify(array($manifest), $file->name . '/', dirname($manifest));
+        $gz->addModify($files, $file->name . '/docroot', $base_dir);
         $gz->addModify($db, $file->name . '/', dirname($db));
 
         unlink($manifest);
@@ -94,7 +106,7 @@ class backup_migrate_files_destination_archivesource extends backup_migrate_dest
   /**
    * Backup from this source.
    */
-  function _backup_to_file_cli($file, $settings) {
+  public function _backup_to_file_cli($file, $settings) {
     if (!empty($settings->filters['use_cli']) && function_exists('backup_migrate_exec') && function_exists('escapeshellarg')) {
       $excluded_paths = empty($settings->filters['exclude_filepaths']) ? '' : $settings->filters['exclude_filepaths'];
       foreach ($this->get_excluded_paths($excluded_paths) as $path) {
@@ -102,7 +114,8 @@ class backup_migrate_files_destination_archivesource extends backup_migrate_dest
       }
       $exclude = implode(' ', $exclude);
 
-      // Create a symlink in a temp directory so we can rename the file in the archive.
+      // Create a symlink in a temp directory so we can rename the file in the
+      // archive.
       $temp = backup_migrate_temp_directory();
 
       $manifest = $this->generate_manifest();
@@ -113,7 +126,12 @@ class backup_migrate_files_destination_archivesource extends backup_migrate_dest
       $file->push_type('sitearchive');
       $link = $temp . '/docroot';
       $input = realpath($this->get_location());
-      backup_migrate_exec("ln -s %input %link; tar --dereference -C %temp -rf %output $exclude .", array('%output' => $file->filepath(), '%input' => $input, '%temp' => $temp, '%link' => $link));
+      backup_migrate_exec("ln -s %input %link; tar --dereference -C %temp -rf %output $exclude .", array(
+        '%output' => $file->filepath(),
+        '%input' => $input,
+        '%temp' => $temp,
+        '%link' => $link,
+      ));
 
       return $file;
     }
@@ -121,15 +139,15 @@ class backup_migrate_files_destination_archivesource extends backup_migrate_dest
   }
 
   /**
-   * Generate a manifest file.
+   * Generates a manifest file.
    */
-  function generate_manifest() {
+  public function generate_manifest() {
     $info = array(
       'Global' => array(
         'datestamp' => time(),
         'formatversion' => '2011-07-02',
         'generator' => 'Backup and Migrate (http://drupal.org/project/backup_migrate)',
-        'generatorversion' => BACKUP_MIGRATE_VERSION, 
+        'generatorversion' => BACKUP_MIGRATE_VERSION,
       ),
       'Site 0' => array(
         'version' => VERSION,
@@ -153,18 +171,23 @@ class backup_migrate_files_destination_archivesource extends backup_migrate_dest
   }
 
   /**
-   * Get a database dump to add to the archive.
+   * Gets a database dump to add to the archive.
    */
-  function get_db() {
+  public function get_db() {
     backup_migrate_include('destinations', 'files', 'filters', 'profiles');
 
     $file = new backup_file();
-    $settings = _backup_migrate_profile_saved_default_profile();
+    // Clone the default settings so we can make changes without them leaking
+    // out of this function.
+    $settings = clone _backup_migrate_profile_saved_default_profile();
     $settings->source_id = 'db';
     $settings->filters['compression'] = 'none';
+
+    // Execute the backup on the db with the default settings.
     $file = backup_migrate_filters_backup($file, $settings);
 
-    // Generate a tmp file with the correct final title (because ArchiveTar doesn't seem to allow renaming).
+    // Generate a tmp file with the correct final title (because ArchiveTar
+    // doesn't seem to allow renaming).
     $tmpdir = backup_migrate_temp_directory();
     $filepath = $tmpdir . '/database.sql';
     rename($file->filepath(), $filepath);
@@ -173,10 +196,10 @@ class backup_migrate_files_destination_archivesource extends backup_migrate_dest
   }
 
   /**
-   * Restore to this source.
+   * Restores to this source.
    */
-  function _restore_from_file_php($file, &$settings) {
-    $success = false;
+  public function _restore_from_file_php($file, &$settings) {
+    $success = FALSE;
     if ($this->check_libs()) {
       $from = $file->pop_type();
       $temp = backup_migrate_temp_directory();
@@ -184,25 +207,25 @@ class backup_migrate_files_destination_archivesource extends backup_migrate_dest
       $tar = new Archive_Tar($from->filepath());
       $tar->extractModify($temp, $file->name);
 
-      // Parse the manifest
+      // Parse the manifest.
       $manifest = $this->read_manifest($temp);
 
       // Currently only the first site in the archive is supported.
       $site = $manifest['Site 0'];
 
-      $docroot  = $temp . '/' . $site['docroot'];
-      $sqlfile  = $temp . '/' . $site['database-file-default'];
+      $docroot = $temp . '/' . $site['docroot'];
+      $sqlfile = $temp . '/' . $site['database-file-default'];
       $filepath = NULL;
       if (isset($site['files-private'])) {
         $filepath = $temp . '/' . $site['files-private'];
       }
-      else if (isset($site['files-public'])) {
+      elseif (isset($site['files-public'])) {
         $filepath = $temp . '/' . $site['files-public'];
       }
 
       // Move the files from the temp directory.
       if ($filepath && file_exists($filepath)) {
-        _backup_migrate_move_files($filepath, file_directory_path());
+        _backup_migrate_move_files($filepath, variable_get('file_public_path', conf_path() . '/files'));
       }
       else {
         _backup_migrate_message('Files were not restored because the archive did not seem to contain a files directory or was in a format that Backup and Migrate couldn\'t read', array(), 'warning');
@@ -210,7 +233,7 @@ class backup_migrate_files_destination_archivesource extends backup_migrate_dest
 
       // Restore the sql db.
       if ($sqlfile && file_exists($sqlfile)) {
-        $db_settings = drupal_clone($settings);
+        $db_settings = clone $settings;
         $db_settings->source_id = 'db';
         $file = new backup_file(array('filepath' => $sqlfile));
         $success = backup_migrate_filters_restore($file, $db_settings);
@@ -229,17 +252,17 @@ class backup_migrate_files_destination_archivesource extends backup_migrate_dest
   }
 
   /**
-   * Restore to this source.
+   * Restores to this source.
    */
-  function _restore_from_file_cli($file, &$settings) {
+  public function _restore_from_file_cli($file, &$settings) {
     // @TODO: implement the cli version of the restore.
     return FALSE;
   }
 
   /**
-   * Generate a manifest file.
+   * Generates a manifest file.
    */
-  function read_manifest($directory) {
+  public function read_manifest($directory) {
     // Assume some defaults if values ore the manifest is missing.
     $defaults = array(
       'docroot' => 'docroot',
@@ -256,16 +279,17 @@ class backup_migrate_files_destination_archivesource extends backup_migrate_dest
     return $out;
   }
 
-
   /**
-   * Convert an associated array to an ini format string. Only allows 2 levels of depth to allow parse_ini_file to parse.
+   * Converts an associated array to an ini format string.
+   *
+   * Only allows 2 levels of depth to allow parse_ini_file to parse.
    */
-  function _array_to_ini($sections) {
-    $content = ""; 
+  public function _array_to_ini($sections) {
+    $content = "";
     foreach ($sections as $section => $data) {
-      $content .= '['. $section .']' . "\n";
+      $content .= '[' . $section . ']' . "\n";
       foreach ($data as $key => $val) {
-        $content .= $key . " = \"". $val ."\"\n";
+        $content .= $key . " = \"" . $val . "\"\n";
       }
       $content .= "\n";
     }
@@ -273,10 +297,12 @@ class backup_migrate_files_destination_archivesource extends backup_migrate_dest
   }
 
   /**
-   * Convert an associated array to an ini format string. Only allows 2 levels of depth to allow parse_ini_file to parse.
+   * Converts an associated array to an ini format string.
+   *
+   * Only allows 2 levels of depth to allow parse_ini_file to parse.
    */
-  function _ini_to_array($path) {
+  public function _ini_to_array($path) {
     return parse_ini_file($path, TRUE);
   }
-}
 
+}

+ 86 - 76
sites/all/modules/contrib/admin/backup_migrate/includes/sources.db.inc

@@ -1,6 +1,5 @@
 <?php
 
-
 /**
  * @file
  * Functions to handle the direct to database destination.
@@ -12,22 +11,22 @@
  * @ingroup backup_migrate_destinations
  */
 class backup_migrate_source_db extends backup_migrate_source_remote {
-  var $supported_ops = array('configure', 'source');
-  var $db_target = 'default';
-  var $connection = null;
+  public $supported_ops = array('configure', 'source');
+  public $db_target = 'default';
+  public $connection = NULL;
 
 
-  function type_name() {
+  public function type_name() {
     return t("Database");
   }
 
   /**
    * Save the info by importing it into the database.
    */
-  function save_file($file, $settings) {
+  public function save_file($file, $settings) {
     backup_migrate_include('files');
 
-    // Set the source_id to the destination_id in the settings since for a restore, the source_id is the 
+    // Set the source_id to the destination_id in the settings since for a restore, the source_id is the
     // database that gets restored to.
     $settings->set_source($this->get_id());
 
@@ -40,7 +39,7 @@ class backup_migrate_source_db extends backup_migrate_source_remote {
   /**
    * Destination configuration callback.
    */
-  function edit_form() {
+  public function edit_form() {
     $form = parent::edit_form();
     $form['scheme']['#default_value'] = $this->default_scheme();
     $form['scheme']['#access'] = FALSE;
@@ -53,7 +52,7 @@ class backup_migrate_source_db extends backup_migrate_source_remote {
   /**
    * Validate the configuration form. Make sure the db info is valid.
    */
-  function edit_form_validate($form, &$form_state) {
+  public function edit_form_validate($form, &$form_state) {
     if (!preg_match('/[a-zA-Z0-9_\$]+/', $form_state['values']['path'])) {
       form_set_error('path', t('The database name is not valid.'));
     }
@@ -61,56 +60,63 @@ class backup_migrate_source_db extends backup_migrate_source_remote {
   }
 
   /**
-   * Get the form for the settings for this destination.
+   * Get the default settings for this object.
    *
-   * Return the default tables whose data can be ignored. These tables mostly contain
-   *  info which can be easily reproducted (such as cache or search index)
-   *  but also tables which can become quite bloated but are not necessarily extremely
-   *  important to back up or migrate during development (such ass access log and watchdog)
+   * @return array
+   *   The default tables whose data can be ignored. These tables mostly
+   *   contain info which can be easily reproducted (such as cache or search
+   *   index) but also tables which can become quite bloated but are not
+   *   necessarily extremely important to back up or migrate during development
+   *   (such as access log and watchdog).
    */
-  function backup_settings_default() {
-    $core = array(
-        'cache',
-        'cache_admin_menu',
-        'cache_browscap',
-        'cache_content',
-        'cache_filter',
-        'cache_calendar_ical',
-        'cache_location',
-        'cache_menu',
-        'cache_page',
-        'cache_reptag',
-        'cache_views',
-        'cache_views_data',
-        'cache_block',
-        'cache_update',
-        'cache_form',
-        'cache_bootstrap',
-        'cache_field',
-        'cache_image',
-        'cache_path',
-        'sessions',
-        'search_dataset',
-        'search_index',
-        'search_keywords_log',
-        'search_total',
-        'watchdog',
-        'accesslog',
-        'devel_queries',
-        'devel_times',
-      );
-    $nodata_tables = array_merge($core, module_invoke_all('devel_caches'));
-     return array(
-      'nodata_tables' => $nodata_tables,
-      'exclude_tables' => array(),
+  public function backup_settings_default() {
+    $all_tables = $this->_get_table_names();
+
+    // Basic modules that should be excluded.
+    $basic = array(
+      // Default core tables.
+      'accesslog',
+      'sessions',
+      'watchdog',
+      // Search module.
+      'search_dataset',
+      'search_index',
+      'search_keywords_log',
+      'search_total',
+      // Devel module.
+      'devel_queries',
+      'devel_times',
+    );
+
+    // Identify all cache tables.
+    $cache = array('cache');
+    foreach ($all_tables as $table_name) {
+      if (strpos($table_name, 'cache_') === 0) {
+        $cache[] = $table_name;
+      }
+    }
+
+    // Simpletest can create a lot of tables that do not need to be backed up,
+    // but all of them start with the string 'simpletest' so they can be easily
+    // excluded.
+    $simpletest = array();
+    foreach ($all_tables as $table_name) {
+      if (strpos($table_name, 'simpletest') === 0) {
+        $simpletest[] = $table_name;
+      }
+    }
+
+    return array(
+      'nodata_tables' => drupal_map_assoc(array_merge($basic, $cache, module_invoke_all('devel_caches'))),
+      'exclude_tables' => $simpletest,
       'utils_lock_tables' => FALSE,
-   );
+    );
   }
 
   /**
    * Get the form for the backup settings for this destination.
    */
-  function backup_settings_form($settings) {
+  public function backup_settings_form($settings) {
     $objects  = $this->get_object_names();
     $form['#description'] = t("You may omit specific tables, or specific table data from the backup file. Only omit data that you know you will not need such as cache data, or tables from other applications. Excluding tables can break your Drupal install, so <strong>do not change these settings unless you know what you're doing</strong>.");
     $form['exclude_tables'] = array(
@@ -141,23 +147,27 @@ class backup_migrate_source_db extends backup_migrate_source_remote {
   /**
    * Backup from this source.
    */
-  function backup_to_file($file, $settings) {
+  public function backup_to_file($file, $settings) {
     $file->push_type($this->get_file_type_id());
 
-    //$this->lock_tables($settings);
-
+    // $this->lock_tables($settings);
     // Switch to a different db if specified.
+    if (variable_get('backup_migrate_verbose')) {
+      _backup_migrate_message('Start peak memory usage: %mem', array('%mem' => backup_migrate_get_peak_memory_usage() . 'MB'), 'success');
+    }
     $success = $this->_backup_db_to_file($file, $settings);
+    if (variable_get('backup_migrate_verbose')) {
+      _backup_migrate_message('Finish peak memory usage: %mem', array('%mem' => backup_migrate_get_peak_memory_usage() . 'MB'), 'success');
+    }
 
-    //$this->unlock_tables($settings);
-
+    // $this->unlock_tables($settings);
     return $success ? $file : FALSE;
   }
 
   /**
    * Restore to this source.
    */
-  function restore_from_file($file, &$settings) {
+  public function restore_from_file($file, &$settings) {
     $num = 0;
     $type = $this->get_file_type_id();
     // Open the file using the file wrapper. Check that the dump is of the right type (allow .sql for legacy reasons).
@@ -179,7 +189,7 @@ class backup_migrate_source_db extends backup_migrate_source_remote {
   /**
    * Get the db connection for the specified db.
    */
-  function _get_db_connection() {
+  public function _get_db_connection() {
     if (!$this->connection) {
       $target = $key = '';
       $parts = explode(':', $this->get_id());
@@ -193,12 +203,12 @@ class backup_migrate_source_db extends backup_migrate_source_remote {
         // If the url is specified build it into a connection info array.
         if (!empty($this->dest_url)) {
           $info = array(
-            'driver'    => empty($this->dest_url['scheme'])   ? NULL : $this->dest_url['scheme'],
-            'host'      => empty($this->dest_url['host'])     ? NULL : $this->dest_url['host'],
-            'port'      => empty($this->dest_url['port'])     ? NULL : $this->dest_url['port'],
-            'username'  => empty($this->dest_url['user'])     ? NULL : $this->dest_url['user'],
-            'password'  => empty($this->dest_url['pass'])     ? NULL : $this->dest_url['pass'],
-            'database'  => empty($this->dest_url['path'])     ? NULL : $this->dest_url['path'], 
+            'driver'    => empty($this->dest_url['scheme']) ? NULL : $this->dest_url['scheme'],
+            'host'      => empty($this->dest_url['host']) ? NULL : $this->dest_url['host'],
+            'port'      => empty($this->dest_url['port']) ? NULL : $this->dest_url['port'],
+            'username'  => empty($this->dest_url['user']) ? NULL : $this->dest_url['user'],
+            'password'  => empty($this->dest_url['pass']) ? NULL : $this->dest_url['pass'],
+            'database'  => empty($this->dest_url['path']) ? NULL : $this->dest_url['path'],
           );
           $key    = uniqid('backup_migrate_tmp_');
           $target = 'default';
@@ -219,21 +229,21 @@ class backup_migrate_source_db extends backup_migrate_source_remote {
   /**
    * Backup the databases to a file.
    */
-  function _backup_db_to_file($file, $settings) {
+  public function _backup_db_to_file($file, $settings) {
     // Must be overridden.
   }
 
   /**
    * Backup the databases to a file.
    */
-  function _restore_db_from_file($file, $settings) {
+  public function _restore_db_from_file($file, $settings) {
     // Must be overridden.
   }
 
   /**
    * Get a list of objects in the database.
    */
-  function get_object_names() {
+  public function get_object_names() {
     // Must be overridden.
     $out = $this->_get_table_names();
     if (method_exists($this, '_get_view_names')) {
@@ -245,7 +255,7 @@ class backup_migrate_source_db extends backup_migrate_source_remote {
   /**
    * Get a list of tables in the database.
    */
-  function get_table_names() {
+  public function get_table_names() {
     // Must be overridden.
     $out = $this->_get_table_names();
     return $out;
@@ -254,7 +264,7 @@ class backup_migrate_source_db extends backup_migrate_source_remote {
   /**
    * Get a list of tables in the database.
    */
-  function _get_table_names() {
+  public function _get_table_names() {
     // Must be overridden.
     return array();
   }
@@ -262,12 +272,12 @@ class backup_migrate_source_db extends backup_migrate_source_remote {
   /**
    * Lock the database in anticipation of a backup.
    */
-  function lock_tables($settings) {
+  public function lock_tables($settings) {
     if ($settings->filters['utils_lock_tables']) {
       $tables = array();
       foreach ($this->get_table_names() as $table) {
         // There's no need to lock excluded or structure only tables because it doesn't matter if they change.
-        if (empty($settings->filters['exclude_tables']) || !in_array($table, (array)$settings->filters['exclude_tables'])) {
+        if (empty($settings->filters['exclude_tables']) || !in_array($table, (array) $settings->filters['exclude_tables'])) {
           $tables[] = $table;
         }
       }
@@ -278,14 +288,14 @@ class backup_migrate_source_db extends backup_migrate_source_remote {
   /**
    * Lock the list of given tables in the database.
    */
-  function _lock_tables($tables) {
+  public function _lock_tables($tables) {
     // Must be overridden.
   }
 
   /**
    * Unlock any tables that have been locked.
    */
-  function unlock_tables($settings) {
+  public function unlock_tables($settings) {
     if ($settings->filters['utils_lock_tables']) {
       $this->_unlock_tables();
     }
@@ -294,21 +304,21 @@ class backup_migrate_source_db extends backup_migrate_source_remote {
   /**
    * Unlock the list of given tables in the database.
    */
-  function _unlock_tables($tables) {
+  public function _unlock_tables($tables) {
     // Must be overridden.
   }
 
   /**
    * Get the file type for to backup this destination to.
    */
-  function get_file_type_id() {
+  public function get_file_type_id() {
     return 'sql';
   }
 
   /**
    * Get the version info for the given DB.
    */
-  function _db_info() {
+  public function _db_info() {
     return array(
       'type' => FALSE,
       'version' => t('Unknown'),

+ 157 - 98
sites/all/modules/contrib/admin/backup_migrate/includes/sources.db.mysql.inc

@@ -1,5 +1,9 @@
 <?php
 
+/**
+ * @file
+ */
+
 backup_migrate_include('sources.db');
 
 /**
@@ -12,16 +16,15 @@ backup_migrate_include('sources.db');
  *
  * @ingroup backup_migrate_destinations
  */
-
 class backup_migrate_source_db_mysql extends backup_migrate_source_db {
-  function type_name() {
+  public function type_name() {
     return t("MySQL Database");
   }
 
   /**
    * Return a list of backup filetypes.
    */
-  function file_types() {
+  public function file_types() {
     return array(
       "sql" => array(
         "extension" => "sql",
@@ -41,25 +44,34 @@ class backup_migrate_source_db_mysql extends backup_migrate_source_db {
   /**
    * Return the scheme for this db type.
    */
-  function default_scheme() {
+  public function default_scheme() {
     return 'mysql';
   }
 
-
- /**
+  /**
    * Declare any mysql databases defined in the settings.php file as a possible source.
    */
-  function sources() {
+  public function sources() {
     $out = array();
     global $databases;
-    foreach ((array)$databases as $db_key => $target) {
-      foreach ((array)$target as $tgt_key => $info) {
+    foreach ((array) $databases as $db_key => $target) {
+      foreach ((array) $target as $tgt_key => $info) {
         // Only mysql/mysqli supported by this source.
         $key = $db_key . ':' . $tgt_key;
         if ($info['driver'] === 'mysql') {
-          $url = $info['driver'] . '://' . $info['username'] . ':' . $info['password'] . '@' . $info['host'] . (isset($info['port']) ? ':' . $info['port'] : '') . '/' . $info['database'];
+          // Compile the database connection string.
+          $url = 'mysql://';
+          $url .= urlencode($info['username']) . ':' . urlencode($info['password']);
+          $url .= '@';
+          $url .= urlencode($info['host']);
+          if (!empty($info['port'])) {
+            $url .= ':' . $info['port'];
+          }
+          $url .= '/' . urlencode($info['database']);
+
           if ($source = backup_migrate_create_destination('mysql', array('url' => $url))) {
-            // Treat the default database differently because it is probably the only one available.
+            // Treat the default database differently because it is probably
+            // the only one available.
             if ($key == 'default:default') {
               $source->set_id('db');
               $source->set_name(t('Default Database'));
@@ -68,8 +80,8 @@ class backup_migrate_source_db_mysql extends backup_migrate_source_db {
               $source->remove_op('manual backup');
             }
             else {
-              $source->set_id('db:'. $key);
-              $source->set_name($key .": ". $source->get_display_location());
+              $source->set_id('db:' . $key);
+              $source->set_name($key . ": " . $source->get_display_location());
             }
             $out[$source->get_id()] = $source;
           }
@@ -82,7 +94,7 @@ class backup_migrate_source_db_mysql extends backup_migrate_source_db {
   /**
    * Get the file type for to backup this source to.
    */
-  function get_file_type_id() {
+  public function get_file_type_id() {
     return 'mysql';
   }
 
@@ -91,9 +103,9 @@ class backup_migrate_source_db_mysql extends backup_migrate_source_db {
    *
    *  Returns a list of sql commands, one command per line.
    *  That makes it easier to import without loading the whole file into memory.
-   *  The files are a little harder to read, but human-readability is not a priority
+   *  The files are a little harder to read, but human-readability is not a priority.
    */
-  function _backup_db_to_file($file, $settings) {
+  public function _backup_db_to_file($file, $settings) {
     if (!empty($settings->filters['use_cli']) && $this->_backup_db_to_file_mysqldump($file, $settings)) {
       return TRUE;
     }
@@ -135,16 +147,14 @@ class backup_migrate_source_db_mysql extends backup_migrate_source_db {
     }
   }
 
-
   /**
    * Backup the databases to a file using the mysqldump command.
    */
-  function _backup_db_to_file_mysqldump($file, $settings) {
+  public function _backup_db_to_file_mysqldump($file, $settings) {
     $success = FALSE;
     $nodata_tables = array();
     $alltables = $this->_get_tables();
 
-
     $command = 'mysqldump --result-file=%file --opt -Q --host=%host --port=%port --user=%user --password=%pass %db';
     $args = array(
       '%file' => $file->filepath(),
@@ -158,15 +168,15 @@ class backup_migrate_source_db_mysql extends backup_migrate_source_db {
     // Ignore the excluded and no-data tables.
     if (!empty($settings->filters['exclude_tables'])) {
       $db = $this->dest_url['path'];
-      foreach ((array)$settings->filters['exclude_tables'] as $table) {
+      foreach ((array) $settings->filters['exclude_tables'] as $table) {
         if (isset($alltables[$table])) {
-          $command .= ' --ignore-table='. $db .'.'. $table;
+          $command .= ' --ignore-table=' . $db . '.' . $table;
         }
       }
-      foreach ((array)$settings->filters['nodata_tables'] as $table) {
+      foreach ((array) $settings->filters['nodata_tables'] as $table) {
         if (isset($alltables[$table])) {
           $nodata_tables[] = $table;
-          $command .= ' --ignore-table='. $db .'.'. $table;
+          $command .= ' --ignore-table=' . $db . '.' . $table;
         }
       }
     }
@@ -184,10 +194,19 @@ class backup_migrate_source_db_mysql extends backup_migrate_source_db {
   /**
    * Backup the databases to a file.
    */
-  function _restore_db_from_file($file, $settings) {
+  public function _restore_db_from_file($file, $settings) {
     $num = 0;
 
     if ($file->open() && $conn = $this->_get_db_connection()) {
+      // Optionally drop all existing tables.
+      if (!empty($settings->filters['utils_drop_all_tables'])) {
+        $all_tables = $this->_get_tables();
+        $table_names = array_map('backup_migrate_array_name_value', $all_tables);
+        $table_list = join(', ', $table_names);
+        $stmt = $conn->prepare("DROP TABLE IF EXISTS $table_list;\n");
+        $stmt->execute();
+      }
+
       // Read one line at a time and run the query.
       while ($line = $this->_read_sql_command_from_file($file)) {
         if (_backup_migrate_check_timeout()) {
@@ -210,17 +229,16 @@ class backup_migrate_source_db_mysql extends backup_migrate_source_db {
     return $num;
   }
 
-
   /**
    * Read a multiline sql command from a file.
    *
    * Supports the formatting created by mysqldump, but won't handle multiline comments.
    */
-  function _read_sql_command_from_file($file) {
+  public function _read_sql_command_from_file($file) {
     $out = '';
     while ($line = $file->read()) {
       $first2 = substr($line, 0, 2);
-      $first3 = substr($line, 0, 2);
+      $first3 = substr($line, 0, 3);
 
       // Ignore single line comments. This function doesn't support multiline comments or inline comments.
       if ($first2 != '--' && ($first2 != '/*' || $first3 == '/*!')) {
@@ -237,7 +255,7 @@ class backup_migrate_source_db_mysql extends backup_migrate_source_db {
   /**
    * Get a list of tables in the database.
    */
-  function _get_table_names() {
+  public function _get_table_names() {
     $out = array();
     foreach ($this->_get_tables() as $table) {
       $out[$table['name']] = $table['name'];
@@ -248,7 +266,7 @@ class backup_migrate_source_db_mysql extends backup_migrate_source_db {
   /**
    * Get a list of views in the database.
    */
-  function _get_view_names() {
+  public function _get_view_names() {
     $out = array();
     foreach ($this->_get_views() as $view) {
       $out[$view['name']] = $view['name'];
@@ -259,29 +277,29 @@ class backup_migrate_source_db_mysql extends backup_migrate_source_db {
   /**
    * Lock the list of given tables in the database.
    */
-  function _lock_tables($tables) {
+  public function _lock_tables($tables) {
     if ($tables) {
       $tables_escaped = array();
       foreach ($tables as $table) {
-        $tables_escaped[] = '`'. db_escape_table($table) .'`  WRITE';
+        $tables_escaped[] = '`' . db_escape_table($table) . '`  WRITE';
       }
-      $this->query('LOCK TABLES '. implode(', ', $tables_escaped));
+      $this->query('LOCK TABLES ' . implode(', ', $tables_escaped));
     }
   }
 
   /**
    * Unlock all tables in the database.
    */
-  function _unlock_tables($settings) {
+  public function _unlock_tables($settings) {
     $this->query('UNLOCK TABLES');
   }
 
   /**
    * Get a list of tables in the db.
    */
-  function _get_tables() {
+  public function _get_tables() {
     $out = array();
-    // get auto_increment values and names of all tables
+    // get auto_increment values and names of all tables.
     $tables = $this->query("show table status", array(), array('fetch' => PDO::FETCH_ASSOC));
     foreach ($tables as $table) {
       // Lowercase the keys because between Drupal 7.12 and 7.13/14 the default query behavior was changed.
@@ -297,9 +315,9 @@ class backup_migrate_source_db_mysql extends backup_migrate_source_db {
   /**
    * Get a list of views in the db.
    */
-  function _get_views() {
+  public function _get_views() {
     $out = array();
-    // get auto_increment values and names of all tables
+    // get auto_increment values and names of all tables.
     $tables = $this->query("show table status", array(), array('fetch' => PDO::FETCH_ASSOC));
     foreach ($tables as $table) {
       // Lowercase the keys because between Drupal 7.12 and 7.13/14 the default query behavior was changed.
@@ -312,12 +330,12 @@ class backup_migrate_source_db_mysql extends backup_migrate_source_db {
     return $out;
   }
 
-   /**
+  /**
    * Get the sql for the structure of the given view.
    */
-  function _get_view_create_sql($view) {
+  public function _get_view_create_sql($view) {
     $out = "";
-    // Switch SQL mode to get rid of "CREATE ALGORITHM..." what requires more permissions + troubles with the DEFINER user
+    // Switch SQL mode to get rid of "CREATE ALGORITHM..." what requires more permissions + troubles with the DEFINER user.
     $sql_mode = $this->query("SELECT @@SESSION.sql_mode")->fetchField();
     $this->query("SET sql_mode = 'ANSI'");
     $result = $this->query("SHOW CREATE VIEW `" . $view['name'] . "`", array(), array('fetch' => PDO::FETCH_ASSOC));
@@ -326,7 +344,7 @@ class backup_migrate_source_db_mysql extends backup_migrate_source_db {
       // Lowercase the keys because between Drupal 7.12 and 7.13/14 the default query behavior was changed.
       // See: http://drupal.org/node/1171866
       $create = array_change_key_case($create);
-      $out .= "DROP VIEW IF EXISTS `". $view['name'] ."`;\n";
+      $out .= "DROP VIEW IF EXISTS `" . $view['name'] . "`;\n";
       $out .= "SET sql_mode = 'ANSI';\n";
       $out .= strtr($create['create view'], "\n", " ") . ";\n";
       $out .= "SET sql_mode = '$sql_mode';\n";
@@ -339,86 +357,105 @@ class backup_migrate_source_db_mysql extends backup_migrate_source_db {
   /**
    * Get the sql for the structure of the given table.
    */
-  function _get_table_structure_sql($table) {
+  public function _get_table_structure_sql($table) {
     $out = "";
-    $result = $this->query("SHOW CREATE TABLE `". $table['name'] ."`", array(), array('fetch' => PDO::FETCH_ASSOC));
+    $result = $this->query("SHOW CREATE TABLE `" . $table['name'] . "`", array(), array('fetch' => PDO::FETCH_ASSOC));
     foreach ($result as $create) {
       // Lowercase the keys because between Drupal 7.12 and 7.13/14 the default query behavior was changed.
       // See: http://drupal.org/node/1171866
       $create = array_change_key_case($create);
-      $out .= "DROP TABLE IF EXISTS `". $table['name'] ."`;\n";
+      $out .= "DROP TABLE IF EXISTS `" . $table['name'] . "`;\n";
       // Remove newlines and convert " to ` because PDO seems to convert those for some reason.
       $out .= strtr($create['create table'], array("\n" => ' ', '"' => '`'));
       if ($table['auto_increment']) {
-        $out .= " AUTO_INCREMENT=". $table['auto_increment'];
+        $out .= " AUTO_INCREMENT=" . $table['auto_increment'];
       }
       $out .= ";\n";
     }
     return $out;
   }
-  
+
   /**
-   *  Get the sql to insert the data for a given table
+   * Get the sql to insert the data for a given table.
    */
-  function _dump_table_data_sql_to_file($file, $table) {
+  public function _dump_table_data_sql_to_file($file, $table) {
+    $rows_per_query = variable_get('backup_migrate_data_rows_per_query', 1000);
     $rows_per_line = variable_get('backup_migrate_data_rows_per_line', 30);
     $bytes_per_line = variable_get('backup_migrate_data_bytes_per_line', 2000);
-  
-    $lines = 0;
-    $data = $this->query("SELECT * FROM `". $table['name'] ."`", array(), array('fetch' => PDO::FETCH_ASSOC));
-    $rows = $bytes = 0;
 
-    // Escape backslashes, PHP code, special chars
+    if (variable_get('backup_migrate_verbose')) {
+      _backup_migrate_message('Table: %table', array('%table' => $table['name']), 'success');
+    }
+
+    // Escape backslashes, PHP code, special chars.
     $search = array('\\', "'", "\x00", "\x0a", "\x0d", "\x1a");
     $replace = array('\\\\', "''", '\0', '\n', '\r', '\Z');
-  
-    $line = array();
-    foreach ($data as $row) {
-      // DB Escape the values.
-      $items = array();
-      foreach ($row as $key => $value) {
-        $items[] = is_null($value) ? "null" : "'". str_replace($search, $replace, $value) ."'";
+
+    $lines = 0;
+    $from = 0;
+    $args = array('fetch' => PDO::FETCH_ASSOC);
+    while ($data = $this->query("SELECT * FROM `" . $table['name'] . "`", array(), $args, $from, $rows_per_query)) {
+      if ($data->rowCount() == 0) {
+        break;
       }
-  
-      // If there is a row to be added.
-      if ($items) {
-        // Start a new line if we need to.
-        if ($rows == 0) {
-          $file->write("INSERT INTO `". $table['name'] ."` VALUES ");
-          $bytes = $rows = 0;
-        }
-        // Otherwise add a comma to end the previous entry.
-        else {
-          $file->write(",");
+
+      $rows = $bytes = 0;
+
+      $line = array();
+      foreach ($data as $row) {
+        $from++;
+
+        // DB Escape the values.
+        $items = array();
+        foreach ($row as $key => $value) {
+          $items[] = is_null($value) ? "null" : "'" . str_replace($search, $replace, $value) . "'";
         }
-  
-        // Write the data itself.
-        $sql = implode(',', $items);
-        $file->write('('. $sql .')');
-        $bytes += strlen($sql);
-        $rows++;
-  
-        // Finish the last line if we've added enough items
-        if ($rows >= $rows_per_line || $bytes >= $bytes_per_line) {
-          $file->write(";\n");
-          $lines++;
-          $bytes = $rows = 0;
+
+        // If there is a row to be added.
+        if ($items) {
+          // Start a new line if we need to.
+          if ($rows == 0) {
+            $file->write("INSERT INTO `" . $table['name'] . "` VALUES ");
+            $bytes = $rows = 0;
+          }
+          // Otherwise add a comma to end the previous entry.
+          else {
+            $file->write(",");
+          }
+
+          // Write the data itself.
+          $sql = implode(',', $items);
+          $file->write('(' . $sql . ')');
+          $bytes += strlen($sql);
+          $rows++;
+
+          // Finish the last line if we've added enough items.
+          if ($rows >= $rows_per_line || $bytes >= $bytes_per_line) {
+            $file->write(";\n");
+            $lines++;
+            $bytes = $rows = 0;
+          }
         }
       }
+
+      // Finish any unfinished insert statements.
+      if ($rows > 0) {
+        $file->write(";\n");
+        $lines++;
+      }
     }
-    // Finish any unfinished insert statements.
-    if ($rows > 0) {
-      $file->write(";\n");
-      $lines++;
+
+    if (variable_get('backup_migrate_verbose')) {
+      _backup_migrate_message('Peak memory usage: %mem', array('%mem' => backup_migrate_get_peak_memory_usage() . 'MB'), 'success');
     }
-  
+
     return $lines;
   }
 
   /**
    * Get the db connection for the specified db.
    */
-  function _get_db_connection() {
+  public function _get_db_connection() {
     if (!$this->connection) {
       $this->connection = parent::_get_db_connection();
       // Set the sql mode because the default is ANSI,TRADITIONAL which is not aware of collation or storage engine.
@@ -428,11 +465,34 @@ class backup_migrate_source_db_mysql extends backup_migrate_source_db {
   }
 
   /**
-   * Run a db query on this destination's db.
+   * Run a query on this source's database using Drupal's MySQL engine.
+   *
+   * @param string $query
+   *   The query string.
+   * @param array $args
+   *   Arguments for the query.
+   * @param array $options
+   *   Options to pass to the query.
+   * @param int|null $from
+   *   The starting point for the query; when passed will perform a queryRange()
+   *   method instead of a regular query().
+   * @param int|null $count
+   *   The number of records to obtain from this query. Will be ignored if the
+   *   $from argument is empty.
+   *
+   * @see DatabaseConnection_mysql::query()
+   * @see DatabaseConnection_mysql::queryRange()
    */
-  function query($query, $args = array(), $options = array()) {
+  public function query($query, array $args = array(), array $options = array(), $from = NULL, $count = NULL) {
     if ($conn = $this->_get_db_connection()) {
-      return $conn->query($query, $args, $options);
+      // If no $from is passed in, just do a basic query.
+      if (is_null($from)) {
+        return $conn->query($query, $args, $options);
+      }
+      // The $from variable was passed in, so do a ranged query.
+      else {
+        return $conn->queryRange($query, $from, $count, $args, $options);
+      }
     }
   }
 
@@ -440,11 +500,11 @@ class backup_migrate_source_db_mysql extends backup_migrate_source_db {
    * The header for the top of the sql dump file. These commands set the connection
    *  character encoding to help prevent encoding conversion issues.
    */
-  function _get_sql_file_header() {
+  public function _get_sql_file_header() {
     $info = $this->_db_info();
 
     return "-- Backup and Migrate (Drupal) MySQL Dump
--- Backup and Migrate Version: ". BACKUP_MIGRATE_VERSION ."
+-- Backup and Migrate Version: " . BACKUP_MIGRATE_VERSION . "
 -- http://drupal.org/project/backup_migrate
 -- Drupal Version: " . VERSION . "
 -- http://drupal.org/
@@ -466,11 +526,11 @@ SET NAMES utf8;
 
 ";
   }
-  
+
   /**
    * The footer of the sql dump file.
    */
-  function _get_sql_file_footer() {
+  public function _get_sql_file_footer() {
     return "
   
 /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
@@ -485,7 +545,7 @@ SET NAMES utf8;
   /**
    * Get the version info for the given DB.
    */
-  function _db_info() {
+  public function _db_info() {
     $db = $this->_get_db_connection();
     return array(
       'type' => 'mysql',
@@ -494,4 +554,3 @@ SET NAMES utf8;
   }
 
 }
-

+ 91 - 60
sites/all/modules/contrib/admin/backup_migrate/includes/sources.filesource.inc

@@ -1,6 +1,5 @@
 <?php
 
-
 /**
  * @file
  * A destination type for saving locally to the server.
@@ -11,45 +10,54 @@
  *
  * @ingroup backup_migrate_destinations
  */
-
 class backup_migrate_destination_filesource extends backup_migrate_source {
-  var $supported_ops = array('restore', 'configure', 'delete', 'source');
+  public $supported_ops = array('restore', 'configure', 'delete', 'source');
 
-  function type_name() {
+  public function type_name() {
     return t("Files Directory");
   }
 
   /**
-   * Declare the current files directory as a backup source..
+   * Declares the current files directory as a backup source..
    */
-  function sources() {
-    $out  = array();
-    $out['files'] = backup_migrate_create_destination('filesource', array('machine_name' => 'files', 'location' => 'public://', 'name' => t('Public Files Directory'), 'show_in_list' => FALSE));
+  public function sources() {
+    $out = array();
+    $out['files'] = backup_migrate_create_destination('filesource', array(
+      'machine_name' => 'files',
+      'location' => 'public://',
+      'name' => t('Public Files Directory'),
+      'show_in_list' => FALSE,
+    ));
     if (variable_get('file_private_path', FALSE)) {
-      $out['files_private'] = backup_migrate_create_destination('filesource', array('machine_name' => 'files', 'location' => 'private://', 'name' => t('Private Files Directory'), 'show_in_list' => FALSE));
+      $out['files_private'] = backup_migrate_create_destination('filesource', array(
+        'machine_name' => 'files',
+        'location' => 'private://',
+        'name' => t('Private Files Directory'),
+        'show_in_list' => FALSE,
+      ));
     }
     return $out;
   }
 
   /**
-   * Get the form for the settings for the files destination.
+   * Gets the form for the settings for the files destination.
    */
-  function edit_form() {
+  public function edit_form() {
     $form = parent::edit_form();
     $form['location'] = array(
       "#type" => "textfield",
       "#title" => t("Directory path"),
       "#default_value" => $this->get_location(),
       "#required" => TRUE,
-      "#description" => t('Enter the path to the directory to save the backups to. Use a relative path to pick a path relative to your Drupal root directory. The web server must be able to write to this path.'),
+      "#description" => t('Enter the path to the directory to back up. Use a relative path to pick a path relative to your Drupal root directory. The web server must be able to read from this path.'),
     );
     return $form;
   }
 
   /**
-   * Return a list of backup filetypes.
+   * Returns a list of backup filetypes.
    */
-  function file_types() {
+  public function file_types() {
     return array(
       "tar" => array(
         "extension" => "tar",
@@ -61,27 +69,27 @@ class backup_migrate_destination_filesource extends backup_migrate_source {
   }
 
   /**
-   * Get the form for the settings for this destination.
+   * Gets the form for the settings for this destination.
    *
-   * Return the default directories whose data can be ignored. These directories contain
-   *  info which can be easily reproducted. Also exclude the backup and migrate folder 
-   *  to prevent exponential bloat.
+   * Return the default directories whose data can be ignored. These directories
+   * contain info which can be easily reproducted. Also exclude the backup and
+   * migrate folder to prevent exponential bloat.
    */
-  function backup_settings_default() {
-     return array(
-      'exclude_filepaths' => "backup_migrate\nstyles\ncss\njs\nctools\nless",
+  public function backup_settings_default() {
+    return array(
+      'exclude_filepaths' => "backup_migrate\nstyles\ncss\njs\nctools\nless\nlanguages",
     );
   }
 
   /**
    * Get the form for the backup settings for this destination.
    */
-  function backup_settings_form($settings) {
+  public function backup_settings_form($settings) {
     $form['exclude_filepaths'] = array(
       "#type" => "textarea",
       "#multiple" => TRUE,
       "#title" => t("Exclude the following files or directories"),
-      "#default_value" => $settings['exclude_filepaths'],
+      "#default_value" => isset($settings['exclude_filepaths']) ? $settings['exclude_filepaths'] : '',
       "#description" => t("A list of files or directories to be excluded from backups. Add one path per line relative to the directory being backed up."),
     );
     return $form;
@@ -90,7 +98,7 @@ class backup_migrate_destination_filesource extends backup_migrate_source {
   /**
    * Backup from this source.
    */
-  function backup_to_file($file, $settings) {
+  public function backup_to_file($file, $settings) {
     if ($out = $this->_backup_to_file_cli($file, $settings)) {
       return $out;
     }
@@ -102,13 +110,13 @@ class backup_migrate_destination_filesource extends backup_migrate_source {
   /**
    * Backup from this source.
    */
-  function _backup_to_file_php($file, $settings) {
+  public function _backup_to_file_php($file, $settings) {
     if ($this->check_libs()) {
-      $excluded_paths = empty($settings->filters['exclude_filepaths']) ? '' : $settings->filters['exclude_filepaths'];
-      $files = $this->get_files_to_backup($this->get_realpath(), $settings, $this->get_excluded_paths($excluded_paths), DRUPAL_ROOT . '/');
+      $excluded = $this->get_excluded_paths($settings);
+      $files = $this->get_files_to_backup($this->get_realpath(), $settings, $excluded);
       if ($files) {
         $file->push_type('tar');
-        $gz = new Archive_Tar($file->filepath(), false);
+        $gz = new Archive_Tar($file->filepath(), FALSE);
         $gz->addModify($files, '', $this->get_realpath());
         return $file;
       }
@@ -121,21 +129,25 @@ class backup_migrate_destination_filesource extends backup_migrate_source {
   /**
    * Backup from this source.
    */
-  function _backup_to_file_cli($file, $settings) {
+  public function _backup_to_file_cli($file, $settings) {
     if (!empty($settings->filters['use_cli']) && function_exists('backup_migrate_exec') && function_exists('escapeshellarg')) {
-      $excluded_paths = empty($settings->filters['exclude_filepaths']) ? '' : $settings->filters['exclude_filepaths'];
+      $excluded = $this->get_excluded_paths($settings);
       $exclude = array();
-      foreach ($this->get_excluded_paths($excluded_paths) as $path) {
+      foreach ($excluded as $path) {
         $exclude[] = '--exclude=' . escapeshellarg($path);
       }
       $exclude = implode(' ', $exclude);
 
-      // Create a symlink in a temp directory so we can rename the file in the archive.
+      // Create a symlink in a temp directory so we can rename the file in the
+      // archive.
       $temp = backup_migrate_temp_directory();
 
-      $file->push_type('tar'); 
-      backup_migrate_exec("tar --dereference -C %input -rf %output $exclude .", array('%output' => $file->filepath(), '%input' => $this->get_realpath(), '%temp' => $temp));
-
+      $file->push_type('tar');
+      backup_migrate_exec("tar --dereference -C %input -rf %output $exclude .", array(
+        '%output' => $file->filepath(),
+        '%input' => $this->get_realpath(),
+        '%temp' => $temp,
+      ));
       return $file;
     }
     return FALSE;
@@ -144,7 +156,7 @@ class backup_migrate_destination_filesource extends backup_migrate_source {
   /**
    * Restore to this source.
    */
-  function restore_from_file($file, &$settings) {
+  public function restore_from_file($file, &$settings) {
     if ($out = $this->_restore_from_file_cli($file, $settings)) {
       return $out;
     }
@@ -156,7 +168,7 @@ class backup_migrate_destination_filesource extends backup_migrate_source {
   /**
    * Restore to this source.
    */
-  function _restore_from_file_php($file, &$settings) {
+  public function _restore_from_file_php($file, &$settings) {
     if ($this->check_libs()) {
       $from = $file->pop_type();
       $temp = backup_migrate_temp_directory();
@@ -165,10 +177,10 @@ class backup_migrate_destination_filesource extends backup_migrate_source {
       $tar->extractModify($temp, $file->name);
 
       // Older B&M Files format included a base 'files' directory.
-      if (file_exists($temp .'/files')) {
+      if (file_exists($temp . '/files')) {
         $temp = $temp . '/files';
       }
-      if (file_exists($temp .'/'. $file->name .'/files')) {
+      if (file_exists($temp . '/' . $file->name . '/files')) {
         $temp = $temp . '/files';
       }
 
@@ -183,16 +195,16 @@ class backup_migrate_destination_filesource extends backup_migrate_source {
   /**
    * Restore to this source.
    */
-  function _restore_from_file_cli($file, &$settings) {
+  public function _restore_from_file_cli($file, &$settings) {
     if (!empty($settings->filters['use_cli']) && function_exists('backup_migrate_exec')) {
       $temp = backup_migrate_temp_directory();
       backup_migrate_exec("tar -C %temp -xf %input", array('%input' => $file->filepath(), '%temp' => $temp));
 
       // Older B&M Files format included a base 'files' directory.
-      if (file_exists($temp .'/files')) {
+      if (file_exists($temp . '/files')) {
         $temp = $temp . '/files';
       }
-      if (file_exists($temp .'/'. $file->name .'/files')) {
+      if (file_exists($temp . '/' . $file->name . '/files')) {
         $temp = $temp . '/files';
       }
 
@@ -204,10 +216,13 @@ class backup_migrate_destination_filesource extends backup_migrate_source {
   }
 
   /**
-   * Get a list of files to backup from the given set if dirs. Exclude any that match the array $exclude.
+   * Gets a list of files to backup from the given set if dirs.
+   *
+   * Exclude any that match the array $exclude.
    */
-  function get_files_to_backup($dir, $settings, $exclude = array(), $base_dir = '') {
+  public function get_files_to_backup($dir, $settings, $exclude = array()) {
     $out = $errors = array();
+
     if (!file_exists($dir)) {
       backup_migrate_backup_fail('Directory %dir does not exist.', array('%dir' => $dir), $settings);
       return FALSE;
@@ -216,25 +231,25 @@ class backup_migrate_destination_filesource extends backup_migrate_source {
       while (($file = readdir($handle)) !== FALSE) {
         if ($file != '.' && $file != '..' && !in_array($file, $exclude)) {
           $real = realpath($dir . '/' . $file);
-          $path = str_replace($base_dir, '', $real);
           // If the path is not excluded.
-          if (!in_array($path, $exclude)) {
+          if (!in_array($real, $exclude)) {
             if (is_dir($real)) {
-              $subdir = $this->get_files_to_backup($real, $settings, $exclude, $base_dir);
-              // If there was an error reading the subdirectory then abort the backup.
+              $subdir = $this->get_files_to_backup($real, $settings, $exclude);
+              // If there was an error reading the subdirectory then abort the
+              // backup.
               if ($subdir === FALSE) {
                 closedir($handle);
                 return FALSE;
               }
               // If the directory is empty, add an empty directory.
               if (count($subdir) == 0) {
-                $out[] = $path;
+                $out[] = $real;
               }
               $out = array_merge($out, $subdir);
             }
             else {
               if (is_readable($real)) {
-                $out[] = $path;
+                $out[] = $real;
               }
               else {
                 $errors[] = $dir . '/' . $file;
@@ -272,30 +287,46 @@ class backup_migrate_destination_filesource extends backup_migrate_source {
   }
 
   /**
-   * Break the excpluded paths string into a usable list of paths.
+   * Breaks the excluded paths string into a usable list of paths.
    */
-  function get_excluded_paths($paths) {
+  public function get_excluded_paths($settings) {
+    $base_dir = $this->get_realpath() . '/';
+    $paths = empty($settings->filters['exclude_filepaths']) ? '' : $settings->filters['exclude_filepaths'];
     $out = explode("\n", $paths);
     foreach ($out as $key => $val) {
-      $out[$key] = trim($val, "/ \t\r\n");
+      $path = trim($val, "/ \t\r\n");
+      // If the path specified is a stream url or absolute path add the
+      // normalized version.
+      if ($real = drupal_realpath($path)) {
+        $out[$key] = $real;
+      }
+      // If the path is a relative path add it.
+      elseif ($real = drupal_realpath($base_dir . $path)) {
+        $out[$key] = $real;
+      }
+      // Otherwise add it as is even though it probably won't match any files.
+      else {
+        $out[$key] = $path;
+      }
     }
     return $out;
   }
 
   /**
-   * Check that the required libraries are installed.
+   * Checks that the required libraries are installed.
    */
-  function check_libs() {
-    $result = true;
-    // Drupal 7 has Archive_Tar built in so there should be no need to include anything here.
+  public function check_libs() {
+    $result = TRUE;
+    // Drupal 7 has Archive_Tar built in so there should be no need to include
+    // anything here.
     return $result;
   }
 
- /**
+  /**
    * Get the file location.
    */
-  function get_realpath() {
+  public function get_realpath() {
     return drupal_realpath($this->get_location());
   }
-}
 
+}

+ 32 - 27
sites/all/modules/contrib/admin/backup_migrate/includes/sources.inc

@@ -1,6 +1,5 @@
 <?php
 
-
 /**
  * @file
  * All of the source handling code needed for Backup and Migrate.
@@ -39,7 +38,7 @@ function backup_migrate_create_source($subtype, $params = array()) {
 }
 
 /**
- * Implementation of hook_backup_migrate_source_subtypes().
+ * Implements hook_backup_migrate_source_subtypes().
  *
  * Get the built in Backup and Migrate source types.
  */
@@ -49,27 +48,27 @@ function backup_migrate_backup_migrate_source_subtypes() {
     'db' => array(
       'type_name' => t('Database'),
       'description' => t('Import the backup directly into another database. Database sources can also be used as a source to backup from.'),
-      'file' => drupal_get_path('module', 'backup_migrate') .'/includes/sources.db.inc',
+      'file' => drupal_get_path('module', 'backup_migrate') . '/includes/sources.db.inc',
       'class' => 'backup_migrate_source_db',
       'can_create' => FALSE,
     ),
     'mysql' => array(
       'type_name' => t('MySQL Database'),
       'description' => t('Import the backup directly into another MySQL database. Database sources can also be used as a source to backup from.'),
-      'file' => drupal_get_path('module', 'backup_migrate') .'/includes/sources.db.mysql.inc',
+      'file' => drupal_get_path('module', 'backup_migrate') . '/includes/sources.db.mysql.inc',
       'class' => 'backup_migrate_source_db_mysql',
       'can_create' => TRUE,
     ),
     'filesource' => array(
       'description' => t('A files directory which can be backed up from.'),
-      'file' => drupal_get_path('module', 'backup_migrate') .'/includes/sources.filesource.inc',
+      'file' => drupal_get_path('module', 'backup_migrate') . '/includes/sources.filesource.inc',
       'class' => 'backup_migrate_destination_filesource',
       'type_name' => t('File Directory'),
       'can_create' => TRUE,
     ),
     'archive' => array(
       'description' => t('Create an archive of your entire site.'),
-      'file' => drupal_get_path('module', 'backup_migrate') .'/includes/sources.archivesource.inc',
+      'file' => drupal_get_path('module', 'backup_migrate') . '/includes/sources.archivesource.inc',
       'class' => 'backup_migrate_files_destination_archivesource',
       'type_name' => t('Site Archive'),
       'can_create' => FALSE,
@@ -80,12 +79,12 @@ function backup_migrate_backup_migrate_source_subtypes() {
 }
 
 /**
- * Implementation of hook_backup_migrate_sources().
+ * Implements hook_backup_migrate_sources().
  *
  * Get the built in backup sources and those in the db.
  */
 function backup_migrate_backup_migrate_sources() {
-  $out              = array();
+  $out = array();
 
   // Expose the configured databases as sources.
   backup_migrate_include('filters');
@@ -153,17 +152,19 @@ function _backup_migrate_get_source_form_item_options() {
  * A base class for creating sources.
  */
 class backup_migrate_source extends backup_migrate_location {
-  var $db_table = "backup_migrate_sources";
-  var $type_name = 'source';
-  var $singular = 'source';
-  var $plural = 'sources';
-  var $title_plural = 'Sources';
-  var $title_singular = 'Source';
+  public $db_table = "backup_migrate_sources";
+  public $type_name = 'source';
+  public $singular = 'source';
+  public $plural = 'sources';
+  public $title_plural = 'Sources';
+  public $title_singular = 'Source';
 
   /**
-   * This function is not supposed to be called. It is just here to help the po extractor out.
+   * This function is not supposed to be called.
+   *
+   * It is just here to help out the po extractor.
    */
-  function strings() {
+  public function strings() {
     // Help the pot extractor find these strings.
     t('source');
     t('sources');
@@ -174,7 +175,7 @@ class backup_migrate_source extends backup_migrate_location {
   /**
    * Get the available location types.
    */
-  function location_types() {
+  public function location_types() {
     return backup_migrate_get_source_subtypes();
   }
 
@@ -184,32 +185,33 @@ class backup_migrate_source extends backup_migrate_location {
  * A base class for creating sources.
  */
 class backup_migrate_source_remote extends backup_migrate_source {
+
   /**
    * The location is a URI so parse it and store the parts.
    */
-  function get_location() {
+  public function get_location() {
     return $this->url(FALSE);
   }
 
   /**
    * The location to display is the url without the password.
    */
-  function get_display_location() {
+  public function get_display_location() {
     return $this->url(TRUE);
   }
 
   /**
-   * Return the location with the password.
+   * Returns the location with the password.
    */
-  function set_location($location) {
+  public function set_location($location) {
     $this->location = $location;
     $this->set_url($location);
   }
 
   /**
-   * source configuration callback.
+   * Source configuration callback.
    */
-  function edit_form() {
+  public function edit_form() {
     $form = parent::edit_form();
     $form['scheme'] = array(
       "#type" => "textfield",
@@ -251,18 +253,21 @@ class backup_migrate_source_remote extends backup_migrate_source {
         "#type" => "value",
         "#value" => @$this->dest_url['pass'],
       );
-      $form['pass']["#description"] .= t(' You do not need to enter a password unless you wish to change the currently saved password.');
+      $form['pass']["#description"] .= t('You do not need to enter a password unless you wish to change the currently saved password.');
     }
     return $form;
   }
 
   /**
-   * Submit the configuration form. Glue the url together and add the old password back if a new one was not specified.
+   * Submits the configuration form.
+   *
+   * Glue the url together and add the old password back if a new one was not
+   * specified.
    */
-  function edit_form_submit($form, &$form_state) {
+  public function edit_form_submit($form, &$form_state) {
     $form_state['values']['pass'] = $form_state['values']['pass'] ? $form_state['values']['pass'] : $form_state['values']['old_password'];
     $form_state['values']['location'] = $this->glue_url($form_state['values'], FALSE);
     parent::edit_form_submit($form, $form_state);
   }
-}
 
+}

+ 246 - 0
sites/all/modules/contrib/admin/backup_migrate/tests/BmTestBase.test

@@ -0,0 +1,246 @@
+<?php
+
+/**
+ * @file
+ * Shared functionality to make the rest of the tests simpler.
+ */
+
+/**
+ * Base class for testing a module's custom tags.
+ */
+abstract class BmTestBase extends DrupalWebTestCase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp(array $modules = array()) {
+    $modules[] = 'backup_migrate';
+    parent::setUp($modules);
+  }
+
+  /**
+   * Log in as user 1.
+   *
+   * The benefit of doing this is that it ignores permissions entirely, so the
+   * raw functionality can be tested.
+   */
+  protected function loginUser1() {
+    // Load user 1.
+    $account = user_load(1, TRUE);
+
+    // Reset the password.
+    $password = user_password();
+    $edit = array(
+      'pass' => $password,
+    );
+    user_save($account, $edit);
+    $account->pass_raw = $password;
+
+    // Login.
+    $this->drupalLogin($account);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function verbose($message, $title = NULL) {
+    // Handle arrays, objects, etc.
+    if (!is_string($message)) {
+      $message = "<pre>\n" . print_r($message, TRUE) . "\n</pre>\n";
+    }
+
+    // Optional title to go before the output.
+    if (!empty($title)) {
+      $title = '<h2>' . check_plain($title) . "</h2>\n";
+    }
+
+    parent::verbose($title . $message);
+  }
+
+  /**
+   * Confirm that a selector has the expected items.
+   */
+  protected function assertSelectOptions($select_id, array $options, $message = '') {
+    $elements = $this
+      ->xpath('//select[@id=:id]//option', array(
+        ':id' => $select_id,
+      ));
+    $results = $this->assertEqual(count($elements), count($options), t('The same number of items were found as were requested'));
+    $this->verbose($elements);
+
+    foreach ($options as $option) {
+      $elements = $this
+        ->xpath('//select[@id=:id]//option[@value=:option]', array(
+          ':id' => $select_id,
+          ':option' => $option,
+        ));
+      $this->verbose($elements);
+      $results *= $this->assertTrue(isset($elements[0]), $message ? $message : t('Option @option for field @id is present.', array(
+        '@option' => $option,
+        '@id' => $select_id,
+      )), t('Browser'));
+    }
+
+    return $results;
+  }
+
+  /**
+   * Confirm that a specific selector does not have items selected.
+   */
+  protected function assertNoOptionsSelected($id, $message = '') {
+    $elements = $this
+      ->xpath('//select[@id=:id]//option[@selected="selected"]', array(
+        ':id' => $id,
+      ));
+    return $this
+      ->assertTrue(!isset($elements[0]), $message ? $message : t('Field @id does not have any selected items.', array(
+        '@id' => $id,
+      )), t('Browser'));
+  }
+
+  /**
+   * Work out which compressor systems are supported by PHP.
+   *
+   * @return array
+   *   The list of supported compressors. Will always include the item 'none'.
+   */
+  protected function supportedCompressors() {
+    $items = array('none');
+
+    // Work out which systems are supported.
+    if (@function_exists("gzencode")) {
+      $items[] = 'gzip';
+    }
+    if (@function_exists("bzcompress")) {
+      $items[] = 'bzip';
+    }
+    if (class_exists('ZipArchive')) {
+      $items[] = 'zip';
+    }
+
+    return $items;
+  }
+
+  /**
+   * Get a list of the files in a specific destination.
+   *
+   * @param string $destination_id
+   *   The ID of the destination to check. Defaults to the manual file path.
+   *
+   * @return array
+   *   The backup files found in the requested backup destination.
+   */
+  protected function listBackupFiles($destination_id = 'manual') {
+    $items = array();
+
+    backup_migrate_include('destinations');
+
+    // Load the destination object.
+    $destination = backup_migrate_get_destination($destination_id);
+    if (!empty($destination)) {
+      $items = $destination->list_files();
+    }
+
+    return $items;
+  }
+
+  /**
+   * Run a specific backup.
+   *
+   * @param string $destination_id
+   *   The ID of the destination to check. Defaults to the manual file path.
+   */
+  protected function runBackup($destination_id = 'manual') {
+    $this->drupalGet(BACKUP_MIGRATE_MENU_PATH);
+    $this->assertResponse(200);
+    $edit = array(
+      'destination_id' => $destination_id,
+    );
+    $this->drupalPost(NULL, $edit, 'Backup now');
+    $this->assertResponse(200);
+    // Confirm the response is as expected. This is split up into separate
+    // pieces because it'd be more effort than is necessary right now to confirm
+    // what the exact filename is.
+    $this->assertText('Default Database backed up successfully');
+    $this->assertText('in destination');
+    $this->assertLink('download');
+    $this->assertLink('restore');
+    $this->assertLink('delete');
+  }
+
+  /**
+   * Delete all of the files in a specific backup destination.
+   *
+   * @param string $destination_id
+   *   The ID of the destination to check. Defaults to the manual file path.
+   */
+  protected function deleteBackups($destination_id = 'manual') {
+    $destination = backup_migrate_get_destination($destination_id);
+    $files = $this->listBackupFiles($destination_id);
+    if (!empty($files)) {
+      foreach ($files as $file_id => $file) {
+        $destination->delete_file($file_id);
+      }
+    }
+  }
+
+  /**
+   * Work out whether a backup filename includes a timestamp.
+   *
+   * @param object $file
+   *   The backup file to examine.
+   *
+   * @return mixed
+   *   Returns 1 if found, 0 if not found, FALSE if an error occurs.
+   */
+  protected function fileHasTimestamp($file) {
+    // Get the default filename, this is used later.
+    backup_migrate_include('files');
+    $default_filename = _backup_migrate_default_filename();
+    $ext = implode('.', $file->ext);
+    $pattern = "/{$default_filename}-(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d)-(\d\d)-(\d\d).{$ext}/";
+
+    return preg_match($pattern, $file->file_info['filename']);
+  }
+
+  /**
+   * Confirm that a backup filename includes a timestamp.
+   *
+   * @param object $file
+   *   The backup file to examine.
+   *
+   * @return bool
+   *   Indicates whether the file includes a timestamp.
+   */
+  protected function assertFileTimestamp($file) {
+    return $this->assertTrue($this->fileHasTimestamp($file));
+  }
+
+  /**
+   * Confirm that a backup filename does not include a timestamp.
+   *
+   * @param object $file
+   *   The backup file to examine.
+   *
+   * @return bool
+   *   Indicates whether the file does not include a timestamp.
+   */
+  protected function assertNoFileTimestamp($file) {
+    return !$this->assertFalse($this->fileHasTimestamp($file));
+  }
+
+  /**
+   * Get a profile.
+   *
+   * @param string $profile_id
+   *   The name of the profile to load. Defaults to 'default'.
+   *
+   * @return object
+   *   The profile object.
+   */
+  protected function getProfile($profile_id = 'default') {
+    backup_migrate_include('profiles');
+    return backup_migrate_get_profile($profile_id);
+  }
+
+}

+ 111 - 0
sites/all/modules/contrib/admin/backup_migrate/tests/BmTestBasics.test

@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * @file
+ * Tests for different parts of the Backup Migrate system.
+ */
+
+/**
+ * Test that the front page still loads.
+ */
+class BmTestBasics extends BmTestBase {
+
+  /**
+   * Define this test class.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Basic tests',
+      'description' => 'Run through basic scenarios and functionality.',
+      'group' => 'backup_migrate',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp(array $modules = array()) {
+    parent::setUp($modules);
+
+    // Log in as user 1, so that permissions are irrelevant.
+    $this->loginUser1();
+  }
+
+  /**
+   * Verify the main page has the expected functionality available.
+   */
+  public function testMainPage() {
+    // Load the main B&M page.
+    $this->drupalGet(BACKUP_MIGRATE_MENU_PATH);
+    $this->assertResponse(200);
+
+    // @todo Confirm each of the tabs are present.
+    // @todo Confirm each of the local tasks are present.
+    // Confirm the form has the expected fields.
+    $this->assertFieldByName('source_id');
+    $this->assertFieldByName('destination_id');
+    $this->assertFieldByName('profile_id');
+    $this->assertFieldByName('copy');
+    $this->assertFieldByName('copy_destination_id');
+    $this->assertFieldByName('description_enabled');
+    // This item should not have a value "selected", it just defaults to the
+    // first item being the active item.
+    $items = array('db', 'files', 'archive');
+    $this->assertSelectOptions('edit-source-id', $items);
+    $this->assertNoOptionsSelected('edit-source-id');
+    // This item should have a value "selected", not just the first item.
+    $items = array('manual', 'download', 'nodesquirrel');
+    $this->assertSelectOptions('edit-destination-id', $items);
+    $this->assertOptionSelected('edit-destination-id', 'download');
+    // This item should not have a value "selected", it just defaults to the
+    // first item being the active item.
+    $items = array('default');
+    $this->assertSelectOptions('edit-profile-id', $items);
+    $this->assertNoOptionsSelected('edit-profile-id');
+    // This item should not have a value "selected", it just defaults to the
+    // first item being the active item.
+    $items = array('manual', 'download', 'nodesquirrel');
+    $this->assertSelectOptions('edit-copy-destination-id', $items);
+    $this->assertNoOptionsSelected('edit-copy-destination-id');
+  }
+
+  /**
+   * Confirm the initial backup process works.
+   */
+  public function testFirstBackup() {
+    // Load the main B&M page.
+    $this->drupalGet(BACKUP_MIGRATE_MENU_PATH);
+    $this->assertResponse(200);
+
+    // Generate a backup and confirm it was created correctly.
+    $edit = array(
+      'destination_id' => 'manual',
+    );
+    $this->drupalPost(NULL, $edit, 'Backup now');
+    $this->assertResponse(200);
+    // Confirm the response is as expected. This is split up into separate
+    // pieces because it'd be more effort than is necessary right now to confirm
+    // what the exact filename is.
+    $this->assertText('Default Database backed up successfully');
+    $this->assertText('in destination Manual Backups Directory');
+    $this->assertLink('download');
+    $this->assertLink('restore');
+    $this->assertLink('delete');
+
+    // Try requesting the backup file.
+    $xpath = $this
+      ->xpath('//a[normalize-space(text())=:label]', array(
+        ':label' => 'download',
+      ));
+    $this->verbose($xpath);
+    $this->assertTrue(isset($xpath[0]['href']));
+    $this->assertNotNull($xpath[0]['href']);
+    // @todo This doesn't work on drupalci, so work out how to fix it.
+    // $this->drupalGet($xpath[0]['href']);
+    $this->assertResponse(200);
+  }
+
+}
+
+// @todo Test permissions.
+// @todo Test admin forms.

+ 168 - 0
sites/all/modules/contrib/admin/backup_migrate/tests/BmTestProfiles.test

@@ -0,0 +1,168 @@
+<?php
+
+/**
+ * @file
+ * Tests the profiles functionality.
+ */
+
+/**
+ * Test that the front page still loads.
+ */
+class BmTestProfiles extends BmTestBase {
+
+  /**
+   * Define this test class.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Destination tests',
+      'description' => 'Run through basic scenarios and functionality.',
+      'group' => 'backup_migrate',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp(array $modules = array()) {
+    parent::setUp($modules);
+
+    // Log in as user 1, so that permissions are irrelevant.
+    $this->loginUser1();
+  }
+
+  /**
+   * Verify the profile page has the expected functionality available.
+   */
+  public function testProfilePage() {
+    // Load the main B&M page.
+    $this->drupalGet(BACKUP_MIGRATE_MENU_PATH . '/settings');
+    $this->assertResponse(200);
+
+    // Confirm the page has the expected settings details.
+    $this->assertText('Settings Profiles');
+    $this->assertText('Default Settings');
+    $this->assertLink('Create a new settings profile');
+  }
+
+  /**
+   * Confirm adding a new backup process works.
+   */
+  public function testAddDefaultProfile() {
+    // Load the main B&M page.
+    $this->drupalGet(BACKUP_MIGRATE_MENU_PATH . '/settings/profile/add');
+    $this->assertResponse(200);
+
+    backup_migrate_include('files', 'profiles');
+    $filename = _backup_migrate_default_filename();
+    $defaults = _backup_migrate_profile_default_profile();
+
+    // Verify all of the expected fields exist.
+    $this->assertFieldByName('name');
+    $this->assertFieldByName('name', 'Untitled Profile');
+    $this->assertFieldByName('machine_name');
+    $this->assertFieldByName('filename');
+    $this->assertFieldByName('filename', $filename);
+    // @todo Confirm all of the expected options are present.
+    $this->assertFieldByName('append_timestamp');
+    $this->assertFieldByName('timestamp_format');
+    $this->assertFieldByName('timestamp_format', $defaults['timestamp_format']);
+    $this->assertFieldByName('filters[compression]');
+    $items = $this->supportedCompressors();
+    $this->assertSelectOptions('edit-filters-compression', $items);
+    $this->assertOptionSelected('edit-filters-compression', 'gzip');
+    $this->assertFieldByName('filters[sources][db][exclude_tables][]');
+    $this->assertFieldByName('filters[sources][db][nodata_tables][]');
+    $this->assertFieldByName('filters[sources][db][utils_lock_tables]');
+    $this->assertFieldByName('filters[sources][files][exclude_filepaths]');
+    $this->assertFieldByName('filters[sources][archive][exclude_filepaths]');
+    $this->assertFieldByName('filters[utils_site_offline]');
+    $this->assertFieldByName('filters[utils_site_offline_message]');
+    $this->assertFieldByName('filters[utils_description]');
+    $this->assertFieldByName('filters[use_cli]');
+    $this->assertFieldByName('filters[ignore_errors]');
+    $this->assertFieldByName('filters[notify_success_enable]');
+    $this->assertFieldByName('filters[notify_success_email]');
+    $this->assertFieldByName('filters[notify_failure_enable]');
+    $this->assertFieldByName('filters[notify_failure_email]');
+  }
+
+  /**
+   * Confirm the backup filename processes work as expected.
+   */
+  public function testFilenameOptions() {
+    // Load the profile. This will be interacted with directly because otherwise
+    // the number of form fields will likely make it impossible to execute
+    // properly due to the max_input_vars setting defaulting to 1000.
+    $profile = $this->getProfile();
+
+    // Run a backup.
+    $this->runBackup();
+
+    // Confirm that there is only one file and it has a timestamp of some sort.
+    $files1 = $this->listBackupFiles();
+    $this->verbose($files1);
+    $this->assertTrue(count($files1) === 1, 'One backup file was found.');
+    $this->assertFileTimestamp(array_shift($files1));
+
+    // Run another backup.
+    $this->runBackup();
+
+    // Confirm that there are two backup files.
+    $files1b = $this->listBackupFiles();
+    $this->verbose($files1b);
+    $this->assertTrue(count($files1b) === 2, 'Two backup files were found.');
+
+    // Cleanup before the next test - purge existing backups.
+    $this->deleteBackups();
+
+    // Change settings to "create separate backups".
+    $profile->append_timestamp = 0;
+    $profile->save();
+
+    // Run a backup.
+    $this->runBackup();
+
+    // Confirm that separate files are retained.
+    $files2 = $this->listBackupFiles();
+    $this->verbose($files2);
+    $this->assertTrue(count($files2) === 1, 'One backup file was found.');
+    $this->assertNoFileTimestamp(array_shift($files2));
+
+    // Run another backup.
+    $this->runBackup();
+
+    // Confirm that separate files are retained.
+    $files2b = $this->listBackupFiles();
+    $this->verbose($files2b);
+    $this->assertTrue(count($files2b) === 2, 'Two backup files were found.');
+
+    // Cleanup before the next test - purge existing backups.
+    $this->deleteBackups();
+
+    // Change settings to "overwrite".
+    $profile->append_timestamp = 2;
+    $profile->save();
+
+    // Run a backup.
+    $this->runBackup();
+
+    // Confirm that a new file was created.
+    $files3 = $this->listBackupFiles();
+    $this->verbose($files3);
+    $this->assertTrue(count($files3) === 1, 'One backup file was found.');
+    $this->assertNoFileTimestamp(array_shift($files3));
+
+    // Run the backup again.
+    $this->runBackup();
+
+    // Confirm that a new file was not created.
+    $files3b = $this->listBackupFiles();
+    $this->verbose($files3b);
+    $this->assertTrue(count($files3b) === 1, 'One backup file was found.');
+
+    // Cleanup - purge all backups.
+    $this->deleteBackups();
+  }
+
+}

+ 21 - 5
sites/all/modules/contrib/admin/context/context.core.inc

@@ -94,11 +94,27 @@ function context_theme_registry_alter(&$theme_registry) {
  */
 function context_ctools_render_alter($info, $page, $data) {
   extract($data);
-  if ($page && in_array($task['name'], array('node_view', 'node_edit'), TRUE)) {
-    foreach ($contexts as $ctools_context) {
-      if (in_array('node', $ctools_context->type) && !empty($ctools_context->data)) {
-        context_node_condition($ctools_context->data, $task['name'] === 'node_view' ? 'view' : 'form');
-        break;
+
+  // Check for page handlers.
+  if ($page) {
+    // Check for node page handler.
+    if (in_array($task['name'], array('node_view', 'node_edit'), TRUE)) {
+      foreach ($contexts as $ctools_context) {
+        if (in_array('node', $ctools_context->type) && !empty($ctools_context->data)) {
+          context_node_condition($ctools_context->data, $task['name'] === 'node_view' ? 'view' : 'form');
+          break;
+        }
+      }
+    }
+    // Check for taxonomy term page handler.
+    elseif (in_array($task['name'], array('term_view', 'term_edit'), TRUE)) {
+      foreach ($contexts as $ctools_context) {
+        if (in_array('taxonomy_term', $ctools_context->type) && !empty($ctools_context->data)) {
+          if ($plugin = context_get_plugin('condition', 'taxonomy_term')) {
+            $plugin->execute($ctools_context->data, $task['name'] === 'term_view' ? 'view' : 'form');
+          }
+          break;
+        }
       }
     }
   }

+ 3 - 4
sites/all/modules/contrib/admin/context/context.info

@@ -8,9 +8,8 @@ files[] = tests/context.test
 files[] = tests/context.conditions.test
 files[] = tests/context.reactions.test
 
-; Information added by Drupal.org packaging script on 2015-01-06
-version = "7.x-3.6"
+; Information added by Drupal.org packaging script on 2019-02-26
+version = "7.x-3.10"
 core = "7.x"
 project = "context"
-datestamp = "1420573188"
-
+datestamp = "1551220089"

+ 1 - 1
sites/all/modules/contrib/admin/context/context.install

@@ -103,7 +103,7 @@ function context_update_7000() {
     }
   }
   if (empty($updated)) {
-    $ret = t('No contexts requiring migration detected');
+    $ret = t('No contexts requiring migration detected.');
   }
   else {
     $ret = t('The following contexts had theme reaction data migrated: @names', array('@names' => join(', ', $updated)));

+ 60 - 5
sites/all/modules/contrib/admin/context/context.module

@@ -192,11 +192,66 @@ function context_init() {
  * theme('menu_link') for the menu rendering to html.
  */
 function context_preprocess_menu_link(&$variables) {
-  if($contexts = context_active_contexts()){
-    foreach($contexts as $context){
-      if((isset($context->reactions['menu']))){
-        if ($variables['element']['#href'] == $context->reactions['menu']) {
-          $variables['element']['#localized_options']['attributes']['class'][] = "active";
+  if ($contexts = context_active_contexts()) {
+    foreach ($contexts as $context) {
+      if (isset($context->reactions['menu'])) {
+        // In context module < v3.2 the url was a string. In version 3.3+ this is
+        // an array of urls. Implement interims BC layer.
+        //
+        // Examples:
+        // - OLD < v3.2 context reaction structure:
+        // array('menu' => 'taxonomy/term/1')
+        //
+        // - NEW 3.3+ context reaction structure:
+        // array(
+        //   'menu' => array(
+        //     0 => 'navigation:taxonomy/term/1'
+        //     1 => 'foo-menu:taxonomy/term/1'
+        //   )
+        // )
+        $reactions_menu = is_array($context->reactions['menu']) ? array_values($context->reactions['menu']) : array($context->reactions['menu']);
+
+        // Get everything after the first ':' character (if found) as the url to
+        // match against element '#href'.
+        $urls = array();
+        foreach ($reactions_menu as $url) {
+          if (strpos($url, ':') !== FALSE) {
+            // Get unique menu name 'navigation' from 'navigation:taxonomy/term/1'
+            $reaction_menu = explode(':', $url);
+            $path = $reaction_menu[1];
+            $urls[$path] = $reaction_menu[0];
+          }
+          else {
+            // BC layer for menu contexts that have not re-saved. This is for
+            // urls like 'taxonomy/term/1'. We need to add a fake menu key
+            // 'bc-context-menu-layer' or the BC link get's removed by
+            // array_intersect below.
+            //
+            // @TODO: Remove BC layer in 4.x
+            $urls[$url] = 'context-reaction-menu-bc-layer';
+          }
+        }
+
+        // Filter urls by the menu name of the current link. The link reaction
+        // can be configured per menu link in specific menus and the contect
+        // reaction should not applied to other menus with the same menu link.
+        $menu_name = $variables['element']['#original_link']['menu_name'];
+        $menu_paths = array_intersect($urls, array($menu_name, 'context-reaction-menu-bc-layer'));
+        $reaction_menu_paths = array_keys($menu_paths);
+
+        // - If menu href and context reaction menu url match, add the 'active'
+        //   css class to the link of this menu.
+        // - Do not add class twice on current page.
+        if (in_array($variables['element']['#href'], $reaction_menu_paths) && $variables['element']['#href'] != $_GET['q']) {
+          // Initialize classes array if not set.
+          if (!isset($variables['element']['#localized_options']['attributes']['class'])) {
+            $variables['element']['#localized_options']['attributes']['class'] = array();
+          }
+
+          // Do not add the 'active' class twice in views tabs.
+          if (!in_array('active', $variables['element']['#localized_options']['attributes']['class'])) {
+            $variables['element']['#localized_options']['attributes']['class'][] = 'active';
+          }
         }
       }
     }

+ 2 - 2
sites/all/modules/contrib/admin/context/context.plugins.inc

@@ -37,7 +37,7 @@ function _context_context_registry() {
       'plugin' => 'context_condition_path',
     ),
     'query_string' => array(
-      'title' => t('Query String'),
+      'title' => t('Query string'),
       'description' => t('Set this context when any of the query strings above match the page query string. Put each query string on a separate line. You can use the "*" character as a wildcard and <code>~</code> to exclude one or more query strings.'),
       'plugin' => 'context_condition_query_string',
     ),
@@ -119,7 +119,7 @@ function _context_context_registry() {
       'plugin' => 'context_reaction_template_suggestions',
     ),
     'theme' => array(
-      'title' => t('Theme Page'),
+      'title' => t('Theme page'),
       'description' => t('Control page theme variables using context.'),
       'plugin' => 'context_reaction_theme',
     ),

+ 3 - 4
sites/all/modules/contrib/admin/context/context_layouts/context_layouts.info

@@ -6,9 +6,8 @@ core = 7.x
 
 files[] = plugins/context_layouts_reaction_block.inc
 
-; Information added by Drupal.org packaging script on 2015-01-06
-version = "7.x-3.6"
+; Information added by Drupal.org packaging script on 2019-02-26
+version = "7.x-3.10"
 core = "7.x"
 project = "context"
-datestamp = "1420573188"
-
+datestamp = "1551220089"

+ 3 - 4
sites/all/modules/contrib/admin/context/context_ui/context_ui.info

@@ -8,9 +8,8 @@ configure = admin/structure/context
 files[] = context.module
 files[] = tests/context_ui.test
 
-; Information added by Drupal.org packaging script on 2015-01-06
-version = "7.x-3.6"
+; Information added by Drupal.org packaging script on 2019-02-26
+version = "7.x-3.10"
 core = "7.x"
 project = "context"
-datestamp = "1420573188"
-
+datestamp = "1551220089"

+ 2 - 2
sites/all/modules/contrib/admin/context/context_ui/context_ui.js

@@ -12,11 +12,11 @@ function DrupalContextPlugins(form) {
     $('.context-plugin-list > li', this.form).each(function() {
       var plugin = $(this).attr('class').split('context-plugin-')[1].split(' ')[0];
       if ($(this).is('.disabled')) {
-        $('.context-plugin-selector select option[value='+plugin+']', this.form).show();
+        $('.context-plugin-selector select option[value="'+plugin+'"]', this.form).show();
       }
       else {
         state.push(plugin);
-        $('.context-plugin-selector select option[value='+plugin+']', this.form).hide();
+        $('.context-plugin-selector select option[value="'+plugin+'"]', this.form).hide();
       }
     });
     // Set the hidden plugin list state.

+ 8 - 12
sites/all/modules/contrib/admin/context/context_ui/context_ui.module

@@ -81,7 +81,7 @@ function context_ui_permission() {
     'description' => 'Associate menus, views, blocks, etc. with different contexts to structure your site.'
   );
   $permissions['context ajax block access'] = array(
-    'title' => t('Access All Blocks'),
+    'title' => t('Access all blocks'),
     'description' => t('Allows users to access all rendered blocks via an AJAX callback. If you have some blocks that should not be rendered for some users but need those users to be able to use context UI, then implement hook_context_allow_ajax_block_access with the necessary logic.'),
   );
   return $permissions;
@@ -143,7 +143,7 @@ function context_ui_editor($form, &$form_state, $contexts) {
 
   $form['title'] = array(
     '#prefix' => '<h2 class="context-editor-title">',
-    '#markup' => t('Select the Context/Layer to Edit'),
+    '#markup' => t('Select the context/layer to edit'),
     '#suffix' => '</h2>',
     '#weight' => -2,
   );
@@ -151,10 +151,7 @@ function context_ui_editor($form, &$form_state, $contexts) {
   //add some help text to the top of the form
   $form['help'] = array (
     '#prefix' => '<p class="context-help help">',
-    '#markup' => t('Select which context, or layer of blocks, to edit.
-                    Each context is configured to appear on different sets of pages so read the description carefully.
-                    When you are done editing click Done and save your changes.
-                    You may use the Stop Editing Layout link to close the editor.'),
+    '#markup' => t('Select which context, or layer of blocks, to edit. Each context is configured to appear on different sets of pages so read the description carefully. When you are done editing click Done and save your changes. You may use the Stop Editing Layout link to close the editor.'),
     '#suffix' => '</p>',
     '#weight' => -1,
   );
@@ -166,10 +163,9 @@ function context_ui_editor($form, &$form_state, $contexts) {
     $edit = l(t('Edit'), $_GET['q'], array('fragment' => $context->name, 'attributes' => array('class' => array('edit'))));
     $done = l(t('Done'), $_GET['q'], array('fragment' => $context->name, 'attributes' => array('class' => array('done'))));
     $readable_name = ucwords(str_replace('_', ' ', $context->name));
-    $description = empty($context->description) ? '' :
-                        "<br/><div class='label bottom'>".check_plain($context->description)."</div>";
+    $description = empty($context->description) ? '' : '<br/><div class="label bottom">' . check_plain($context->description) . '</div>';
     $items[] = array(
-      'data' => "<div class='label top'>" . $readable_name. "</div><div class='links'>{$edit} {$done}</div>" . $description,
+      'data' => '<div class="label top">' . $readable_name. "</div><div class='links'>{$edit} {$done}</div>" . $description,
       'class' => array('context-editable clearfix'),
       'id' => "context-editable-trigger-{$context->name}",
     );
@@ -306,9 +302,9 @@ function context_ui_settings($form, &$form_state) {
   }
   $form['context_ui_dialog_enabled'] = array(
     '#type' => 'checkbox',
-    '#title' => t('Use Context Editor Dialog'),
+    '#title' => t('Use context editor dialog'),
     '#default_value' => context_ui_dialog_is_enabled(),
-    '#description' => t('When enabled all contextual links will have a Edit Layout link that will refresh the page with the context editor in a dialog box.'),
+    '#description' => t('When enabled all contextual links will have a <em>Edit layout</em> link that will refresh the page with the context editor in a dialog box.'),
   );
   $form = system_settings_form($form);
   $form['#submit'][] = 'context_ui_settings_submit';
@@ -374,7 +370,7 @@ function context_ui_menu_contextual_links_alter(&$links, $router_item, $root_pat
      !context_isset('context_ui', 'context_ui_editor_present') && user_access('administer contexts')) {
     $links['layout'] = array(
       'href' => 'context-ui/activate',
-      'title' => t('Configure Layout'),
+      'title' => t('Configure layout'),
       'localized_options' => array(
         'query' =>  array('destination'=> $_GET['q']),
         'options' => array('html' => FALSE, 'attributes' => array()),

+ 2 - 2
sites/all/modules/contrib/admin/context/context_ui/context_ui_dialog.js

@@ -9,11 +9,11 @@
         selector.detach();
         $('#page').prepend(selector);
 
-        var labelOpen = Drupal.t('Select Context');
+        var labelOpen = Drupal.t('Select context');
         var labelClose = Drupal.t('Hide');
 
         // Create a tab to show/hide our edit area
-        var tab = $('<a href="javascript:" class="context-ui-dialog-open" title="Show Context Selector">'+labelClose+'</a>');
+        var tab = $('<a href="javascript:" class="context-ui-dialog-open" title="' + Drupal.t('Show context selector') + '">'+labelClose+'</a>');
         selector.append(tab);
 
         selector.toggled = false;

+ 11 - 11
sites/all/modules/contrib/admin/context/context_ui/export_ui/context_export_ui.class.php

@@ -22,7 +22,7 @@ class context_export_ui extends ctools_export_ui {
   function list_render(&$form_state) {
     $table = array(
       'header' => $this->list_table_header(),
-      'rows' => $this->rows, 
+      'rows' => $this->rows,
       'attributes' => array(
         'class' => array('context-admin'),
         'id' => 'ctools-export-ui-list-items',
@@ -104,17 +104,17 @@ class context_export_ui extends ctools_export_ui {
  * @param $form_state
  *   Form state array
  */
-function context_ui_form(&$form, &$form_state) {  
+function context_ui_form(&$form, &$form_state) {
   $conditions = array_keys(context_conditions());
   sort($conditions);
   $reactions = array_keys(context_reactions());
   sort($reactions);
-    
+
   $context = $form_state['item'];
   if (!empty($form_state['input'])) {
     $context = _context_ui_rebuild_from_input($context, $form_state['input'], $conditions, $reactions);
   }
-  
+
   $form['#base'] = 'context_ui_form';
   $form['#theme'] = 'context_ui_form';
 
@@ -131,7 +131,7 @@ function context_ui_form(&$form, &$form_state) {
     '#required' => FALSE,
     '#maxlength' => 255,
     '#default_value' => isset($context->tag) ? $context->tag : '',
-    '#description' => t('Example: <code>theme</code>') .'<br/>'. t('A tag to group this context with others.'),
+    '#description' => t('Example: <code>theme</code>') . '<br/>' . t('A tag to group this context with others.'),
   );
 
   $form['info']['description'] = array(
@@ -155,11 +155,11 @@ function context_ui_form(&$form, &$form_state) {
   $form['conditions'] = array(
     '#theme' => 'context_ui_plugins',
     '#title' => t('Conditions'),
-    '#description' => t('Trigger the activation of this context'),
+    '#description' => t('Trigger the activation of this context.'),
     '#tree' => TRUE,
     'selector' => array(
       '#type' => 'select',
-      '#options' => array(0 => '<'. t('Add a condition') .'>'),
+      '#options' => array(0 => '<' . t('Add a condition') . '>'),
       '#default_value' => 0,
     ),
     'state' => array(
@@ -185,11 +185,11 @@ function context_ui_form(&$form, &$form_state) {
   $form['reactions'] = array(
     '#theme' => 'context_ui_plugins',
     '#title' => t('Reactions'),
-    '#description' => t('Actions to take when this context is active'),
+    '#description' => t('Actions to take when this context is active.'),
     '#tree' => TRUE,
     'selector' => array(
       '#type' => 'select',
-      '#options' => array(0 => '<'. t('Add a reaction') .'>'),
+      '#options' => array(0 => '<' . t('Add a reaction') . '>'),
       '#default_value' => 0,
     ),
     'state' => array(
@@ -225,7 +225,7 @@ function context_ui_form(&$form, &$form_state) {
  *   A context object
  */
 function _context_ui_rebuild_from_input($context, $input, $conditions, $reactions) {
-  $condition_defaults = array();  
+  $condition_defaults = array();
   foreach ($conditions as $condition) {
     if ($plugin = context_get_plugin('condition', $condition)) {
       $condition_defaults[$condition] = array(
@@ -235,7 +235,7 @@ function _context_ui_rebuild_from_input($context, $input, $conditions, $reaction
     }
   }
   $input['conditions']['plugins'] = array_merge($condition_defaults, $input['conditions']['plugins']);
-  
+
   $reaction_defaults = array();
   foreach ($reactions as $reaction) {
     if ($plugin = context_get_plugin('reaction', $reaction)) {

+ 1 - 1
sites/all/modules/contrib/admin/context/context_ui/tests/context_ui.test

@@ -48,7 +48,7 @@ class ContextUiTestCase extends DrupalWebTestCase {
       'description' => $context->description,
       'tag' => $context->tag,
       'conditions[plugins][node][values][blog]' => 'blog',
-      'reactions[plugins][menu]' => 'node/add/blog',
+      'reactions[plugins][menu][]' => 'navigation:node/add/blog',
     );
     $this->drupalPost('admin/structure/context/add', $edit, 'Save');
     $this->assertText($context->name . ' has been created.', 'Context saved.');

+ 1 - 1
sites/all/modules/contrib/admin/context/plugins/context_condition_bookroot.inc

@@ -8,7 +8,7 @@ class context_condition_bookroot extends context_condition_node {
     if ($this->condition_used() && !empty($node->book['bid'])) {
       $type = db_select('node')
         ->fields('node', array('type'))
-        ->condition('nid', $node->book['nid'])
+        ->condition('nid', $node->book['bid'])
         ->execute()
         ->fetchField();
       $book = new stdClass();

+ 1 - 1
sites/all/modules/contrib/admin/context/plugins/context_condition_menu.inc

@@ -14,7 +14,7 @@ class context_condition_menu extends context_condition {
       foreach ($menus as $key => $name) {
         $id = explode(':', $key);
         if ($id[1] == '0') {
-          $root_menus[$id[0]] = check_plain($name);
+          $root_menus[$id[0]] = $name;
         }
         else {
           $link = menu_link_load($id[1]);

+ 9 - 5
sites/all/modules/contrib/admin/context/plugins/context_condition_node_taxonomy.inc

@@ -45,24 +45,28 @@ class context_condition_node_taxonomy extends context_condition_node {
     $check_fields = array();
     foreach ($instance_fields as $key => $field_info) {
       if ($fields[$key]['type'] == 'taxonomy_term_reference') {
-        $check_fields[] = $key;
+        $check_fields[$key] = 'tid';
+      }
+      else if ($fields[$key]['type'] == 'entityreference' &&
+               $fields[$key]['settings']['target_type'] == 'taxonomy_term') {
+        $check_fields[$key] = 'target_id';
       }
     }
 
     if ($this->condition_used() && !empty($check_fields)) {
-      foreach ($check_fields as $field) {
+      foreach ($check_fields as $field => $term_id_key) {
         if ($terms = field_get_items('node', $node, $field)) {
           foreach ($terms as $term) {
-            foreach ($this->get_contexts($term['tid']) as $context) {
+            foreach ($this->get_contexts($term[$term_id_key]) as $context) {
               // Check the node form option.
               if ($op === 'form') {
                 $options = $this->fetch_from_context($context, 'options');
                 if (!empty($options['node_form'])) {
-                  $this->condition_met($context, $term['tid']);
+                  $this->condition_met($context, $term[$term_id_key]);
                 }
               }
               else {
-                $this->condition_met($context, $term['tid']);
+                $this->condition_met($context, $term[$term_id_key]);
               }
             }
           }

+ 1 - 1
sites/all/modules/contrib/admin/context/plugins/context_condition_query_string.inc

@@ -16,7 +16,7 @@ class context_condition_query_string extends context_condition_path {
    */
   function execute() {
     if ($this->condition_used()) {
-      $current_query_string = $_SERVER["QUERY_STRING"];
+      $current_query_string = empty($_SERVER["QUERY_STRING"]) ? '' : $_SERVER["QUERY_STRING"];
       foreach ($this->get_contexts() as $context) {
         $query_strings = $this->fetch_from_context($context, 'values');
         if ($this->match($current_query_string, $query_strings, TRUE)) {

+ 11 - 10
sites/all/modules/contrib/admin/context/plugins/context_reaction_block.inc

@@ -400,13 +400,17 @@ class context_reaction_block extends context_reaction {
       unset($_context_blocks);
 
       foreach ($context_blocks as $r => $blocks) {
-        //only render blocks in an active region
+        // Only render blocks in an active region.
         if (array_key_exists($r, $active_regions)) {
           $context_blocks[$r] = _block_render_blocks($blocks);
 
-          // Make blocks editable if allowed.
-          if ($this->is_editable_region($r)) {
-            foreach ($context_blocks[$r] as $key => $block) {
+          $editable = $this->is_editable_region($r);
+          foreach ($context_blocks[$r] as $key => $block) {
+            // Add the region property to each block.
+            $context_blocks[$r][$key]->region = $r;
+
+            // Make blocks editable if allowed.
+            if ($editable) {
               $context_blocks[$r][$key] = $this->editable_block($block);
             }
           }
@@ -450,12 +454,9 @@ class context_reaction_block extends context_reaction {
    */
   protected function max_block_weight() {
     $blocks = $this->get_blocks();
-    $block_count = 0;
-    foreach ($blocks as $region => $block_list) {
-      $block_count += count($block_list);
-    }
-    // Add 2 to make sure there's space at either end of the block list
-    return round(($block_count + 2) / 2);
+
+    // Add 2 to make sure there's space at either end of the block list.
+    return round((count($blocks) + 2) / 2);
   }
 
   /**

+ 5 - 6
sites/all/modules/contrib/admin/context/plugins/context_reaction_block.js

@@ -68,8 +68,8 @@ DrupalContextBlockForm = function(blockForm) {
 
     // Hide enabled blocks from selector that are used
     $('table.context-blockform-region tr').each(function() {
-      var bid = $(this).attr('id');
-      $('div.context-blockform-selector input[value='+bid+']').parents('div.form-item').eq(0).hide();
+      var bid = Drupal.checkPlain($(this).attr('id'));
+      $('div.context-blockform-selector input[value="'+bid+'"]').parents('div.form-item').eq(0).hide();
     });
     // Show blocks in selector that are unused
     $('div.context-blockform-selector input').each(function() {
@@ -159,7 +159,7 @@ DrupalContextBlockForm = function(blockForm) {
           $(this).removeAttr('checked');
         });
         if (weight_warn) {
-          alert(Drupal.t('Desired block weight exceeds available weight options, please check weights for blocks before saving'));
+          alert(Drupal.t('Desired block weight exceeds available weight options, please check weights for blocks before saving.'));
         }
       }
       return false;
@@ -426,9 +426,8 @@ DrupalContextBlockEditor.prototype = {
 
     $('.editing-context-label').remove();
     var label = $('#context-editable-trigger-'+context+' .label').text();
-    label = Drupal.t('Now Editing: ') + label;
-    editor.parent().parent()
-      .prepend('<div class="editing-context-label">'+ label + '</div>');
+    label = Drupal.t('Now editing: @label', {'@label': label});
+    editor.parent().parent().prepend('<div class="editing-context-label">' + label + '</div>');
 
     // First pass, enable sortables on all regions.
     $(this.regions).each(function() {

+ 1 - 0
sites/all/modules/contrib/admin/context/plugins/context_reaction_breadcrumb.inc

@@ -8,6 +8,7 @@ class context_reaction_breadcrumb extends context_reaction_menu {
    * Overrides set_active_trail_from_link to set the breadcrumb instead of the menu path.
    */
   function set_active_trail_from_link($item) {
+    $breadcrumb = array(l(t('Home'), '<front>'));
     $result = db_select('menu_links')
       ->fields('menu_links', array('p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8'))
       ->condition('hidden', 0)

+ 3 - 1
sites/all/modules/contrib/admin/context/plugins/context_reaction_menu.inc

@@ -22,19 +22,21 @@ class context_reaction_menu extends context_reaction {
           $link = menu_link_load($mlid);
           $identifier = $link['link_path'];
           $root_menu = $menu_names[$menu_name];
-          while (isset($options[$root_menu][$identifier])) {
+          while (isset($options[$root_menu][$menu_name . ':' . $identifier])) {
             $identifier .= "'";
           }
           $options[$root_menu][$menu_name . ':' . $identifier] = $title;
         }
       }
     }
+    $menu_count = count($options, COUNT_RECURSIVE);
     return array(
       '#title' => $this->title,
       '#description' => $this->description,
       '#options' => $options,
       '#type' => 'select',
       '#multiple' => TRUE,
+      '#size' => $menu_count > 20 ? 20 : $menu_count,
       '#default_value' => $this->fetch_from_context($context),
     );
   }

+ 5 - 1
sites/all/modules/contrib/admin/context/plugins/context_reaction_region.inc

@@ -17,7 +17,7 @@ class context_reaction_region extends context_reaction {
           '#type' => 'fieldset',
           '#title' => "Disable Regions in {$theme->name} Theme",
           '#collapsible' => TRUE,
-          '#collapsed' => !array_reduce($default, create_function('$a, $b', 'return $a || $b;')),
+          '#collapsed' => !array_reduce($default, 'context_reaction_region::collapseRegion'),
         );
         $form[$theme->name]['disable'] = array(
           '#type' => 'checkboxes',
@@ -29,6 +29,10 @@ class context_reaction_region extends context_reaction {
     }
     return $form;
   }
+
+  function collapseRegion($a, $b) {
+    return $a || $b;
+  }
   
   function execute(&$page) {  
     global $theme;

+ 1 - 1
sites/all/modules/contrib/admin/context/plugins/context_reaction_template_suggestions.inc

@@ -14,7 +14,7 @@ class context_reaction_template_suggestions extends context_reaction {
     return array(
       '#title' => t('Template suggestions'),
       '#type' => 'textarea',
-      '#description' => t('Enter template suggestions such as "page__front", one per line, in order of preference (using underscores instead of hyphens).  For more information, please visit ') . l(t('Drupal 7 Template (Theme Hook) Suggestions'), 'http://drupal.org/node/1089656', array(array('target' => '_blank'), 'html' => TRUE,)) . '.',
+      '#description' => t('Enter template suggestions such as "page__front", one per line, in order of preference (using underscores instead of hyphens). For more information, please visit <a href="@template-suggestions">Drupal 7 Template (Theme Hook) Suggestions</a>.', array('@template-suggestions' => 'http://drupal.org/node/1089656')),
       '#default_value' => is_string($default_value) ? $default_value : '',
     );
   }

+ 0 - 22
sites/all/modules/contrib/admin/context/tests/context.conditions.test

@@ -36,13 +36,6 @@ class ContextConditionUserTest extends DrupalWebTestCase {
     $this->assertTrue($saved, "Context 'testcontext' saved.");
   }
 
-  function tearDown() {
-    parent::tearDown();
-    context_delete($this->context);
-    user_delete($this->user1->uid);
-    user_delete($this->user2->uid);
-  }
-
   function test() {
     // User 1 triggers the context.
     $this->drupalLogin($this->user1);
@@ -82,14 +75,6 @@ class ContextConditionUserPageTest extends DrupalWebTestCase {
     $this->assertTrue($saved, "Context 'testcontext' saved.");
   }
 
-  function tearDown() {
-    parent::tearDown();
-    context_delete($this->context);
-    $edit = array();
-    user_delete($this->user1->uid);
-    user_delete($this->user2->uid);
-  }
-
   function test() {
     // Viewing any user profile triggers context.
     $this->drupalLogin($this->user1);
@@ -157,13 +142,6 @@ class ContextConditionNodeTaxonomyTest extends DrupalWebTestCase {
     $this->assertTrue($saved, "Context 'testcontext' saved.");
   }
 
-  function tearDown() {
-    parent::tearDown();
-    context_delete($this->context);
-    taxonomy_term_delete($this->terms['apples']->tid);
-    taxonomy_term_delete($this->terms['oranges']->tid);
-  }
-
   function test() {
     // Apples does trigger the context.
     $edit = array(

+ 3 - 4
sites/all/modules/contrib/admin/context/theme/context_reaction_block.theme.inc

@@ -52,7 +52,7 @@ function theme_context_block_regions_form($vars) {
     $output .= "<div class='label context-blockform-regionlabel-{$region}'>";
     $output .= l(t('+') . ' ' . t('Add'), $_GET['q'], array('fragment' => $region, 'attributes' => array('class' => array('add-block'))));
     $output .= $form[$region]['#title'];
-    $output .= "</div>";
+    $output .= '</div>';
     $output .= theme('table', array('rows' => $rows, 'attributes' => $attr));
   }
   return $output;
@@ -100,14 +100,13 @@ function template_preprocess_context_block_browser(&$vars) {
   //add help text to tell people how to use the block browser
   $help_text = array(
     '#prefix' => '<div class="context_ui-help-text">',
-    '#markup' => t('To add a block to the current region, simply click on the block.  You may use the category filter to filter by
-      block type or the search filter to find the block that you wish to add.'),
+    '#markup' => t('To add a block to the current region, simply click on the block. You may use the category filter to filter by block type or the search filter to find the block that you wish to add.'),
     '#suffix' => '</div>',
   );
 
   $filter_label = array(
     '#prefix' => '<div class="filter-label">',
-    '#markup' => t('Search Filter'),
+    '#markup' => t('Search filter'),
     '#suffix' => '</div>',
   );
 

+ 101 - 34
sites/all/modules/contrib/admin/features/features.admin.inc

@@ -88,6 +88,12 @@ function features_settings_form($form, $form_state) {
     '#default_value' => variable_get('features_rebuild_on_flush', TRUE),
     '#description' => t('If you have a large site with many features, you may experience lag on full cache clear. If disabled, features will rebuild only when viewing the features list or saving the modules list.'),
   );
+  $form['general']['features_rebuild_modules_page'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Rebuild features on accessing modules list page'),
+    '#default_value' => variable_get('features_rebuild_modules_page', FALSE),
+    '#description' => t('If you have a large site with many features, you may experience lag on accessing the modules administration page. If disabled, features will not rebuild when viewing the modules list.'),
+  );
 
   return system_settings_form($form);
 }
@@ -109,7 +115,7 @@ function features_export_form($form, $form_state, $feature = NULL) {
 
   $feature_name = !empty($feature->name) ? $feature->name : '';
   $form = array(
-    '#attributes' => array('class' => array('features-export-form')),
+    '#attributes' => array('class' => array('features-export-form', 'clearfix')),
     '#feature' => isset($feature) ? $feature : NULL,
   );
   $form['info'] = array(
@@ -203,7 +209,7 @@ function features_export_form($form, $form_state, $feature = NULL) {
     $form['advanced']['generate'] = array(
       '#type' => 'submit',
       '#value' => t('Generate feature'),
-      '#submit' => array('features_export_build_form_submit'),
+      '#submit' => array('features_export_build_form_submit', 'features_form_rebuild'),
     );
   }
   // build the Component Listing panel on the right
@@ -239,7 +245,7 @@ function features_export_form($form, $form_state, $feature = NULL) {
     '#type' => 'submit',
     '#value' => t('Download feature'),
     '#weight' => 10,
-    '#submit' => array('features_export_build_form_submit'),
+    '#submit' => array('features_export_build_form_submit', 'features_form_rebuild'),
   );
 
   $form['#attached']['library'][] = array('system', 'ui.dialog');
@@ -597,6 +603,7 @@ function _features_export_build($feature, &$form_state) {
       $component_export['selected'][$section] = array();
     }
     $options = features_invoke($component, 'features_export_options');
+    drupal_alter('features_export_options', $options, $component);
     if (!empty($options)) {
       $exported_components = !empty($exported_features_info[$component]) ? $exported_features_info[$component] : array();
       $new_components = !empty($new_features_info[$component]) ? $new_features_info[$component] : array();
@@ -755,7 +762,7 @@ function features_export_form_rebuild($form, &$form_state) {
 
 function features_export_components_json($feature_name) {
   module_load_include('inc', 'features', 'features.export');
-  $export = array();
+  $export = array('features' => array());
   if (!empty($_POST['items'])) {
     $excluded = (!empty($_POST['excluded'])) ? $_POST['excluded'] : array();
     $stub = array();
@@ -843,7 +850,7 @@ function _features_export_generate($export, $form_state, $feature = NULL) {
   }
   // If either update status-related keys are provided, add a project key
   // corresponding to the module name.
-  if (!empty($form_state['values']['version']) || !empty($form_state['values']['project_status_url'])) {
+  if (!empty($form_state['values']['version']) && !empty($form_state['values']['project_status_url'])) {
     $export['project'] = $form_state['values']['module_name'];
   }
   if (!empty($form_state['values']['version'])) {
@@ -864,7 +871,7 @@ function features_export_build_form_submit($form, &$form_state) {
   $feature = $form['#feature'];
   $export = _features_export_build($feature, $form_state);
   $export = _features_export_generate($export, $form_state, $feature);
-  $generate = ($form_state['values']['op'] == $form_state['values']['generate']);
+  $generate = isset($form_state['values']['generate']) && ($form_state['values']['op'] == $form_state['values']['generate']);
   $module_name = $form_state['values']['module_name'];
 
   if ($generate && !user_access('generate features')) {
@@ -900,6 +907,35 @@ function features_export_build_form_submit($form, &$form_state) {
 
     $tar = array();
     $filenames = array();
+    // Copy any files if _files key is there.
+    if (!empty($files['_files'])) {
+      foreach ($files['_files'] as $file_name => $file_info) {
+        if ($generate) {
+          // See if files are in a sub directory.
+          if (strpos($file_name, '/')) {
+            $file_directory = $directory . '/' . substr($file_name, 0, strrpos($file_name, '/'));
+            if (!is_dir($file_directory)) {
+              mkdir($file_directory);
+            }
+          }
+          if (!empty($file_info['file_path'])) {
+            file_unmanaged_copy($file_info['file_path'], "{$directory}/{$file_name}", FILE_EXISTS_REPLACE);
+          }
+          elseif ($file_info['file_content']) {
+            file_put_contents("{$directory}/{$file_name}", $file_info['file_content']);
+          }
+        }
+        else {
+          if (!empty($file_info['file_path'])) {
+            print features_tar_create("{$module_name}/{$file_name}", file_get_contents($file_info['file_path']));
+          }
+          elseif ($file_info['file_content']) {
+            features_tar_create("{$directory}/{$file_name}", $file_info['file_content']);
+          }
+        }
+      }
+      unset($files['_files']);
+    }
     foreach ($files as $extension => $file_contents) {
       if (!in_array($extension, array('module', 'info'))) {
         $extension .= '.inc';
@@ -973,28 +1009,14 @@ function features_filter_hidden($module) {
  * Form constructor for the features configuration form.
  */
 function features_admin_form($form, $form_state) {
-  // Load export functions to use in comparison.
-  module_load_include('inc', 'features', 'features.export');
-
-  // Clear & rebuild key caches
-  features_get_info(NULL, NULL, TRUE);
-  features_rebuild();
-
+  $features = _features_get_features_list();
   $modules = array_filter(features_get_modules(), 'features_filter_hidden');
-  $features = array_filter(features_get_features(), 'features_filter_hidden');
   $conflicts = features_get_conflicts();
 
-  foreach ($modules as $key => $module) {
-    if ($module->status && !empty($module->info['dependencies'])) {
-      foreach ($module->info['dependencies'] as $dependent) {
-        if (isset($features[$dependent])) {
-          $features[$dependent]->dependents[$key] = $module->info['name'];
-        }
-      }
-    }
-  }
+  // Load export functions to use in comparison.
+  module_load_include('inc', 'features', 'features.export');
 
-  if ( empty($features) ) {
+  if (empty($features) ) {
     $form['no_features'] = array(
       '#markup' => t('No Features were found. Please use the !create_link link to create
       a new Feature module, or upload an existing Feature to your modules directory.',
@@ -1073,6 +1095,7 @@ function features_admin_form($form, $form_state) {
     }
 
     $href = "admin/structure/features/{$name}";
+    $href_overridden = module_exists('diff') ? $href . '/diff' : $href;
     $module_name = (user_access('administer features')) ? l($module->info['name'], $href) : $module->info['name'];
     $form[$package]['status'][$name] = array(
       '#type' => 'checkbox',
@@ -1102,7 +1125,7 @@ function features_admin_form($form, $form_state) {
         $state .= l(t('Check'), "admin/structure/features/{$name}/status", array('attributes' => array('class' => array('admin-check'))));
         $state .= theme('features_storage_link', array('storage' => FEATURES_REBUILDING, 'path' => $href));
         $state .= theme('features_storage_link', array('storage' => FEATURES_NEEDS_REVIEW, 'path' =>  $href));
-        $state .= theme('features_storage_link', array('storage' => FEATURES_OVERRIDDEN, 'path' =>  $href));
+        $state .= theme('features_storage_link', array('storage' => FEATURES_OVERRIDDEN, 'path' =>  $href_overridden));
         $state .= theme('features_storage_link', array('storage' => FEATURES_DEFAULT, 'path' =>  $href));
       }
       elseif (!empty($conflicts[$name])) {
@@ -1126,7 +1149,7 @@ function features_admin_form($form, $form_state) {
   // As of 7.0 beta 2 it matters where the "vertical_tabs" element lives on the
   // the array. We add it late, but at the beginning of the array because that
   // keeps us away from trouble.
-  $form = array('packages' => array('#type' => 'vertical_tabs')) + $form;
+  $form = array_merge(array('packages' => array('#type' => 'vertical_tabs')), $form);
 
   $form['buttons'] = array(
     '#theme' => 'features_form_buttons',
@@ -1311,7 +1334,7 @@ function features_form_submit(&$form, &$form_state) {
   // page callback rather than as part of the submit handler as some modules
   // have includes/other directives of importance in hooks that have already
   // been called in this page load.
-  $form_state['redirect'] = 'admin/structure/features/cleanup/clear';
+  $form_state['redirect'] = array('admin/structure/features/cleanup', array('query' => array('token' => drupal_get_token())));
 
   $features = $form['#features'];
   if (!empty($features)) {
@@ -1328,21 +1351,26 @@ function features_form_submit(&$form, &$form_state) {
 }
 
 /**
- * Form for clearing cache after enabling a feature.
+ * Submit handler for the 'manage features' form rebuild button.
  */
-function features_cleanup_form($form, $form_state, $cache_clear = FALSE) {
-  // Clear caches if we're getting a post-submit redirect that requests it.
-  if ($cache_clear) {
-    drupal_flush_all_caches();
+function features_form_rebuild() {
+  cache_clear_all('features:features_list', 'cache');
+}
 
+/**
+ * Callback for clearing cache after enabling a feature.
+ */
+function features_cleanup() {
+  if (!empty($_GET['token']) && drupal_valid_token($_GET['token'])) {
+    drupal_flush_all_caches();
     // The following functions need to be run because drupal_flush_all_caches()
     // runs rebuilds in the wrong order. The node type cache is rebuilt *after*
     // the menu is rebuilt, meaning that the menu tree is stale in certain
     // circumstances after drupal_flush_all_caches(). We rebuild again.
     menu_rebuild();
-  }
-
     drupal_goto('admin/structure/features');
+  }
+  return MENU_NOT_FOUND;
 }
 
 /**
@@ -1587,3 +1615,42 @@ function _features_get_used($module_name = NULL) {
   $features_ignore_conflicts = $old_value;
   return $conflicts;
 }
+
+/**
+ * Retrieves the array of features as expected on the Manage Features form.
+ * Uses caching for performance reasons if caching is enabled.
+ *
+ * @internal - This function might return cached result with outdated data,
+ * use with caution.
+ */
+function _features_get_features_list() {
+  $features = array();
+
+  $cache = cache_get('features:features_list');
+  if ($cache) {
+    $features = $cache->data;
+  }
+
+  if (empty($features)) {
+    // Clear & rebuild key caches
+    features_get_info(NULL, NULL, TRUE);
+    features_rebuild();
+
+    $modules = array_filter(features_get_modules(), 'features_filter_hidden');
+    $features = array_filter(features_get_features(), 'features_filter_hidden');
+
+    foreach ($modules as $key => $module) {
+      if ($module->status && !empty($module->info['dependencies'])) {
+        foreach ($module->info['dependencies'] as $dependent) {
+          if (isset($features[$dependent])) {
+            $features[$dependent]->dependents[$key] = $module->info['name'];
+          }
+        }
+      }
+    }
+
+    cache_set('features:features_list', $features);
+  }
+
+  return $features;
+}

+ 24 - 3
sites/all/modules/contrib/admin/features/features.api.php

@@ -42,7 +42,7 @@
  *   are declared "dynamically" or are part of a family of components.
  *
  *   'alter_type': What type of alter hook this hook uses. 'normal' is called
- *   after the main hook is called. 'inline' is embeded within the default hook
+ *   after the main hook is called. 'inline' is embedded within the default hook
  *   and may not be implemented by some default hooks.
  *   'none' is no alter hook exists. Defaults to 'normal'
  *
@@ -157,7 +157,8 @@ function hook_features_export_options() {
  *   of the module, e.g. the key for `hook_example` should simply be `example`
  *   The values in the array can also be in the form of an associative array
  *   with the required key of 'code' and optional key of 'args', if 'args' need
- *   to be added to the hook.
+ *   to be added to the hook. Alternate it can be an associative array in the
+ *   same style as hook_features_export_files() to add additional files.
  */
 function hook_features_export_render($module_name, $data, $export = NULL) {
   $code = array();
@@ -309,11 +310,31 @@ function hook_features_pipe_COMPONENT_alter(&$pipe, $data, $export) {
  * The module being exported contained in $export['module_name'].
  */
 function hook_features_pipe_alter(&$pipe, $data, $export) {
-  if ($export['component'] == 'node' && in_array($data, 'my-node-type')) {
+  if ($export['component'] == 'node' && in_array('my-node-type', $data)) {
     $pipe['dependencies'][] = 'mymodule';
   }
 }
 
+
+/**
+ * Add extra files to the exported file.
+ *
+ * @return array
+ *   An array of files, keyed by file name that will appear in feature and
+ *   with either file_path key to indicate where to copy the file from or
+ *   file_content key to indicate the contents of the file.
+ */
+function hook_features_export_files($module_name, $export) {
+  return array('css/main.css' => array('file_content' => 'body {background-color:blue;}'));
+}
+
+/**
+ * Alter the extra files added to the export.
+ */
+function hook_features_export_files_alter(&$files, $module_name, $export) {
+  $files['css/main.css']['file_content'] = 'body {background-color:black;}';
+}
+
 /**
  * @defgroup features_component_alter_hooks Feature's component alter hooks
  * @{

+ 151 - 22
sites/all/modules/contrib/admin/features/features.drush.inc

@@ -32,6 +32,12 @@ function features_drush_command() {
     ),
     'drupal dependencies' => array('features'),
     'aliases' => array('fl', 'features'),
+    'outputformat' => array(
+      'default' => 'table',
+      'pipe-format' => 'list',
+      'field-labels' => array('name' => 'Name', 'feature' => 'Feature', 'status' => 'Status', 'version' => 'Version', 'state' => 'State'),
+      'output-data-type' => 'format-table',
+    ),
   );
   $items['features-export'] = array(
     'description' => "Export a feature from your site into a module.",
@@ -43,6 +49,7 @@ function features_drush_command() {
       'destination' => "Destination path (from Drupal root) of the exported feature. Defaults to '" . $path . "'.",
       'version-set' => "Specify a version number for the feature.",
       'version-increment' => "Increment the feature's version number.",
+      'ignore-conflicts' => "Ignore conflicts and export all components.",
     ),
     'drupal dependencies' => array('features'),
     'aliases' => array('fe'),
@@ -72,6 +79,9 @@ function features_drush_command() {
       'not-exported' => array(
         'description' => 'Show only components that have not been exported.',
       ),
+      'info-style' => array(
+        'description' => 'Export components in format suitable for using in an info file.',
+      ),
     ),
     'aliases' => array('fc'),
   );
@@ -134,6 +144,18 @@ function features_drush_command() {
     'aliases' => array('fd'),
   );
 
+  $items['features-diff-all'] = array(
+    'description' => "Show the code difference for all enabled features not in their default state.",
+    'arguments' => array(
+      'feature_exclude' => 'A space-delimited list of features to exclude from being reverted.',
+    ),
+    'options' => array(
+      'force' => "Bypass the confirmations. This is useful if you want to output all of the diffs to a log file.",
+    ),
+    'drupal dependencies' => array('features', 'diff'),
+    'aliases' => array('fda'),
+  );
+
   return $items;
 }
 
@@ -191,12 +213,12 @@ function drush_features_list() {
   }
 
   module_load_include('inc', 'features', 'features.export');
-  $rows = array(array(dt('Name'), dt('Feature'), dt('Status'), dt('Version'), dt('State')));
 
   // Sort the Features list before compiling the output.
   $features = features_get_features(NULL, TRUE);
   ksort($features);
 
+  $rows = array();
   foreach ($features as $k => $m) {
     switch (features_get_storage($m->name)) {
       case FEATURES_DEFAULT:
@@ -214,16 +236,19 @@ function drush_features_list() {
       ($m->status == 0 && ($status == 'all' || $status == 'disabled')) ||
       ($m->status == 1 && ($status == 'all' || $status == 'enabled'))
     ) {
-      $rows[] = array(
-        $m->info['name'],
-        $m->name,
-        $m->status ? dt('Enabled') : dt('Disabled'),
-        $m->info['version'],
-        $storage
+      $rows[$k] = array(
+        'name' => $m->info['name'],
+        'feature' => $m->name,
+        'status' => $m->status ? dt('Enabled') : dt('Disabled'),
+        'version' => $m->info['version'],
+        'state' => $storage
       );
     }
   }
-  drush_print_table($rows, TRUE);
+  if (version_compare(DRUSH_VERSION, '6.0', '<')) {
+    drush_print_table($rows, TRUE);
+  }
+  return $rows;
 }
 
 /**
@@ -253,10 +278,13 @@ function drush_features_components() {
   elseif (drush_get_option(array('not-exported', 'o'), NULL)) {
     $options['exported'] = FALSE;
   }
+  if (drush_get_option(array('info-style', 'is'), NULL)) {
+    $options['info style'] = TRUE;
+  }
 
   $filtered_components = _drush_features_component_filter($components, $args, $options);
   if ($filtered_components){
-    _drush_features_component_print($filtered_components);
+    _drush_features_component_print($filtered_components, $options);
   }
 }
 
@@ -290,7 +318,7 @@ function _drush_features_component_filter($all_components, $patterns = array(),
   // First filter on exported state.
   foreach ($all_components as $source => $components) {
     foreach ($components as $name => $title) {
-      $exported = sizeof($components_map[$source][$name]) > 0;
+      $exported = !empty($components_map[$source][$name]);
       if ($exported) {
         if ($options['exported']) {
           $pool[$source][$name] = $title;
@@ -318,7 +346,13 @@ function _drush_features_component_filter($all_components, $patterns = array(),
     // Rewrite * to %. Let users use both as wildcard.
     $pattern = strtr($pattern, array('*' => '%'));
     $sources = array();
-    list($source_pattern, $component_pattern) = explode(':', $pattern, 2);
+    if (strpos($pattern, ':') !== FALSE) {
+      list($source_pattern, $component_pattern) = explode(':', $pattern, 2);
+    }
+    else {
+      $source_pattern = $pattern;
+      $component_pattern = '';
+    }
     // If source is empty, use a pattern.
     if ($source_pattern == '') {
       $source_pattern = '%';
@@ -383,7 +417,7 @@ function _drush_features_component_filter($all_components, $patterns = array(),
             return drush_set_error('', dt('Ambiguous component "!component", matches !matches', array('!component' => $component_pattern, '!matches' => join(', ', $matches))));
           }
         }
-        if (!is_array($selected[$source])) {
+        if (empty($selected[$source])) {
           $selected[$source] = array();
         }
         $selected[$source] += array_intersect_key($pool[$source], array_flip($matches));
@@ -404,7 +438,7 @@ function _drush_features_component_filter($all_components, $patterns = array(),
   if ($options['provided by'] && $options['exported'] ) {
     foreach ($selected as $source => $components) {
       foreach ($components as $name => $title) {
-        $exported = sizeof($components_map[$source][$name]) > 0;
+        $exported = !empty($components_map[$source][$name]);
         if ($exported) {
           $provided_by[$source . ':' . $name] = join(', ', $components_map[$source][$name]);
         }
@@ -421,11 +455,17 @@ function _drush_features_component_filter($all_components, $patterns = array(),
 /**
  * Prints a list of filtered components.
  */
-function _drush_features_component_print($filtered_components) {
+function _drush_features_component_print($filtered_components, $options = array()) {
   $rows = array(array(dt('Available sources')));
   foreach ($filtered_components['components'] as $source => $components) {
     foreach ($components as $name => $value) {
-      $row = array($source .':'. $name);
+      if (!empty($options['info style'])) {
+        // Output as .info file style.
+        $row = array('features[' . $source . '][] = "' . $name . '"');
+      }
+      else {
+        $row = array($source .':'. $name);
+      }
       if (isset($filtered_components['sources'][$source .':'. $name])) {
         $row[] = dt('Provided by') . ': ' . $filtered_components['sources'][$source .':'. $name];
       }
@@ -447,9 +487,11 @@ function drush_features_export() {
       return drush_set_error('', 'No components supplied.');
     }
     $components = _drush_features_component_list();
-    $options = array(
-      'exported' => FALSE,
-    );
+    $options = array();
+
+    if (!drush_get_option('ignore-conflicts', FALSE)) {
+      $options['exported'] = FALSE;
+    }
 
     $filtered_components = _drush_features_component_filter($components, $args, $options);
     $items = $filtered_components['components'];
@@ -477,7 +519,7 @@ function drush_features_export() {
         drush_die('Aborting.');
       }
       $export = _drush_features_generate_export($items, $module);
-      _features_populate($items, $export[info], $export[name]);
+      _features_populate($items, $export['info'], $export['name']);
       _drush_features_export($export['info'], $module, $directory);
     }
   }
@@ -579,9 +621,40 @@ function _drush_features_export($info, $module_name = NULL, $directory = NULL) {
       drush_op('mkdir', $directory);
     }
     if (is_dir($directory)) {
+      // Ensure that the export will be created in the English language.
+      // The export language must be set before flushing caches as that can
+      // result into translatables being statically cached.
+      $language = _features_export_language();
+
       drupal_flush_all_caches();
       $export = _drush_features_generate_export($info, $module_name);
       $files = features_export_render($export, $module_name, TRUE);
+
+      // Restore the language
+      _features_export_language($language);
+
+      // Copy any files if _files key is there.
+      if (!empty($files['_files'])) {
+        foreach ($files['_files'] as $file_name => $file_info) {
+          // See if files are in a sub directory.
+          if (strpos($file_name, '/')) {
+            $file_directory = $directory . '/' . substr($file_name, 0, strrpos($file_name, '/'));
+            if (!is_dir($file_directory)) {
+              drush_op('mkdir', $file_directory);
+            }
+          }
+          if (!empty($file_info['file_path'])) {
+            drush_op('file_unmanaged_copy', $file_info['file_path'], "{$directory}/{$file_name}", FILE_EXISTS_REPLACE);
+          }
+          elseif (!empty($file_info['file_content'])) {
+            drush_op('file_put_contents', "{$directory}/{$file_name}", $file_info['file_content']);
+          }
+          else {
+            drush_log(dt("Entry for @file_name.in !module is invalid. ", array('!module' => $module_name, '@file_name' => $file_name)), 'ok');
+          }
+        }
+        unset($files['_files']);
+      }
       foreach ($files as $extension => $file_contents) {
         if (!in_array($extension, array('module', 'info'))) {
           $extension .= '.inc';
@@ -635,7 +708,7 @@ function _drush_features_generate_export(&$info, &$module_name) {
       }
       else {
         // Split version number parts.
-        $pattern = '/([0-9]-[a-z]+([0-9])+)/';
+        $pattern = '/([0-9]-[a-z]+([0-9]+))/';
         $matches = array();
         preg_match($pattern, $version_minor, $matches);
         $number = array_pop($matches);
@@ -747,7 +820,7 @@ function drush_features_revert() {
     }
   }
   else {
-    drush_features_list();
+    drush_print_table(drush_features_list());
     return;
   }
 }
@@ -801,7 +874,7 @@ function drush_features_revert_all() {
  */
 function drush_features_diff() {
   if (!$args = func_get_args()) {
-    drush_features_list();
+    drush_print_table(drush_features_list());
     return;
   }
   $module = $args[0];
@@ -886,6 +959,62 @@ function drush_features_diff() {
   }
 }
 
+/**
+ * Diff all enabled features that are not in their default state.
+ *
+ * @param ...
+ *   (Optional) A list of features to exclude from being reverted.
+ */
+function drush_features_diff_all() {
+  module_load_include('inc', 'features', 'features.export');
+  $features_to_exclude = func_get_args();
+
+  $features_to_revert = array();
+  foreach (features_get_features(NULL, TRUE) as $module) {
+    if ($module->status && !in_array($module->name, $features_to_exclude)) {
+      switch (features_get_storage($module->name)) {
+        case FEATURES_OVERRIDDEN:
+        case FEATURES_NEEDS_REVIEW:
+        case FEATURES_REBUILDABLE:
+          $features_to_diff[] = $module->name;
+          break;
+      }
+    }
+  }
+
+  if ($features_to_diff) {
+
+    // Check if the user wants to apply the force option.
+    $force = drush_get_option('force');
+
+    if($force) {
+      foreach ($features_to_diff as $module) {
+
+        drush_print(dt('Diff for !module:', array('!module' => $module)));
+
+        drush_invoke_process(drush_sitealias_get_record('@self'), 'features-diff', array($module));
+      }
+    }
+    else {
+
+      drush_print(dt('A diff will be performed for the following modules: !modules',
+        array('!modules' => implode(', ', $features_to_diff))
+      ));
+
+      if (drush_confirm(dt('Do you want to continue?'))) {
+        foreach ($features_to_diff as $module) {
+          if (drush_confirm(dt('Diff !module?', array('!module' => $module)))) {
+            drush_invoke_process(drush_sitealias_get_record('@self'), 'features-diff', array($module));
+          }
+        }
+      }
+      else {
+        return drush_user_abort('Aborting.');
+      }
+    }
+  }
+}
+
 /**
  * Helper function to call drush_set_error().
  *

+ 172 - 16
sites/all/modules/contrib/admin/features/features.export.inc

@@ -45,6 +45,9 @@ function features_populate($info, $module_name) {
  * @return fully populated $export array.
  */
 function _features_populate($pipe, &$export, $module_name = '', $reset = FALSE) {
+  // Ensure that the export will be created in the english language.
+  $language = _features_export_language();
+
   if ($reset) {
     drupal_static_reset(__FUNCTION__);
   }
@@ -89,6 +92,7 @@ function _features_populate($pipe, &$export, $module_name = '', $reset = FALSE)
       }
     }
   }
+  _features_export_language($language);
   return $export;
 }
 
@@ -295,6 +299,11 @@ function features_export_render($export, $module_name, $reset = FALSE) {
     }
 
     foreach ($hooks as $hook_name => $hook_info) {
+      // These are purely files that will be copied over.
+      if (is_array($hook_info) && (!empty($hook_info['file_path']) || !empty($hook_info['file_content']))) {
+        $code['_files'][$hook_name] = $hook_info;
+        continue;
+      }
       $hook_code = is_array($hook_info) ? $hook_info['code'] : $hook_info;
       $hook_args = is_array($hook_info) && !empty($hook_info['args']) ? $hook_info['args'] : '';
       $hook_file = is_array($hook_info) && !empty($hook_info['file']) ? $hook_info['file'] : $file['name'];
@@ -305,7 +314,17 @@ function features_export_render($export, $module_name, $reset = FALSE) {
   // Finalize strings to be written to files
   $code = array_filter($code);
   foreach ($code as $filename => $contents) {
-    $code[$filename] = "<?php\n/**\n * @file\n * {$module_name}.{$filename}.inc\n */\n\n". implode("\n\n", $contents) ."\n";
+    if ($filename != '_files') {
+      $code[$filename] = "<?php\n\n/**\n * @file\n * {$module_name}.{$filename}.inc\n */\n\n". implode("\n\n", $contents) ."\n";
+    }
+  }
+
+  // Allow extra files be added to feature.
+  if ($files =  module_invoke_all('features_export_files', $module_name, $export)) {
+    $code['_files'] = !empty($code['_files']) ? $code['_files'] + $files : $files;
+  }
+  if (!empty($code['_files'])) {
+    drupal_alter('features_export_files', $code['_files'], $module_name, $export);
   }
 
   // Generate info file output
@@ -313,8 +332,8 @@ function features_export_render($export, $module_name, $reset = FALSE) {
   $code['info'] = features_export_info($export);
 
   // Used to create or manipulate the generated .module for features.inc.
-  $modulefile_features_inc = "<?php\n/**\n * @file\n * Code for the {$export['name']} feature.\n */\n\ninclude_once '{$module_name}.features.inc';\n";
-  $modulefile_blank = "<?php\n/**\n * @file\n * Drupal needs this blank file.\n */\n";
+  $modulefile_features_inc = "<?php\n\n/**\n * @file\n * Code for the {$export['name']} feature.\n */\n\ninclude_once '{$module_name}.features.inc';\n";
+  $modulefile_blank = "<?php\n\n/**\n * @file\n * Drupal needs this blank file.\n */\n";
 
   // Prepare the module
   // If module exists, let it be and include it in the files
@@ -394,8 +413,8 @@ function features_detect_overrides($module) {
     $overridden = array();
 
     // Compare feature info
-    _features_sanitize($module->info);
-    _features_sanitize($export);
+    features_sanitize($module->info);
+    features_sanitize($export);
 
     $compare = array('normal' => features_export_info($export), 'default' => features_export_info($module->info));
     if ($compare['normal'] !== $compare['default']) {
@@ -408,8 +427,8 @@ function features_detect_overrides($module) {
       if ($state != FEATURES_DEFAULT) {
         $normal = features_get_normal($component, $module->name);
         $default = features_get_default($component, $module->name);
-        _features_sanitize($normal);
-        _features_sanitize($default);
+        features_sanitize($normal, $component);
+        features_sanitize($default, $component);
 
         $compare = array('normal' => features_var_export($normal), 'default' => features_var_export($default));
         if (_features_linetrim($compare['normal']) !== _features_linetrim($compare['default'])) {
@@ -663,8 +682,7 @@ function features_get_signature($state = 'default', $module_name, $component, $r
       break;
   }
   if (!empty($objects)) {
-    $objects = (array) $objects;
-    _features_sanitize($objects);
+    features_sanitize($objects, $component);
     return md5(_features_linetrim(features_var_export($objects)));
   }
   return FALSE;
@@ -721,7 +739,7 @@ function features_get_normal($component, $module_name, $reset = FALSE) {
 
     // Special handling for dependencies component.
     if ($component === 'dependencies') {
-      $cache[$module_name][$component] = isset($module->info['dependencies']) ? array_filter($module->info['dependencies'], 'module_exists') : array();
+      $cache[$module_name][$component] = isset($module->info['dependencies']) ? array_filter($module->info['dependencies'], '_features_module_exists') : array();
     }
     // All other components.
     else {
@@ -739,6 +757,17 @@ function features_get_normal($component, $module_name, $reset = FALSE) {
   return isset($cache[$module_name][$component]) ? $cache[$module_name][$component] : FALSE;
 }
 
+/**
+ * Helper function to determine if a module is enabled
+ * @param $module
+ *   This module name comes from the .info file and can have version info in it.
+ */
+function _features_module_exists($module) {
+  $parsed_dependency = drupal_parse_dependency($module);
+  $name = $parsed_dependency['name'];
+  return module_exists($name);
+}
+
 /**
  * Get defaults for a given module/component pair.
  */
@@ -815,6 +844,13 @@ function features_get_default($component, $module_name = NULL, $alter = TRUE, $r
 
 /**
  * Get a map of components to their providing modules.
+ *
+ * @param string $component
+ * @param string $attribute
+ * @param callable $callback
+ * @param bool $reset
+ *
+ * @return array|bool
  */
 function features_get_default_map($component, $attribute = NULL, $callback = NULL, $reset = FALSE) {
   $map = &drupal_static(__FUNCTION__, array());
@@ -863,6 +899,9 @@ function features_get_default_map($component, $attribute = NULL, $callback = NUL
  * Retrieve an array of features/components and their current states.
  */
 function features_get_component_states($features = array(), $rebuild_only = TRUE, $reset = FALSE) {
+  // Ensure that the export will be created in the English language.
+  $language = _features_export_language();
+
   if ($reset) {
     drupal_static_reset(__FUNCTION__);
   }
@@ -873,7 +912,7 @@ function features_get_component_states($features = array(), $rebuild_only = TRUE
 
   // Retrieve only rebuildable components if requested.
   features_include();
-  $components = array_keys(features_get_components());
+  $components = array_keys(features_get_components(NULL, NULL, $reset));
   if ($rebuild_only) {
     foreach ($components as $k => $component) {
       if (!features_hook($component, 'features_rebuild')) {
@@ -956,6 +995,9 @@ function features_get_component_states($features = array(), $rebuild_only = TRUE
   foreach ($return as $k => $v) {
     $return[$k] = array_intersect_key($return[$k], array_flip($components));
   }
+
+  _features_export_language($language);
+
   return $return;
 }
 
@@ -970,25 +1012,49 @@ function _features_linetrim($code) {
   return implode("\n", $code);
 }
 
+/**
+ * Helper function to "sanitize" an array or object.
+ * Converts everything to an array, sorts the keys, removes recursion.
+ * @param $array
+ * @param $component string name of component
+ * @param bool $remove_empty if set, remove null or empty values for assoc arrays.
+ */
+function features_sanitize(&$array, $component = NULL, $remove_empty = TRUE) {
+  $array = features_remove_recursion($array);
+  if (isset($component)) {
+    $ignore_keys = _features_get_ignore_keys($component);
+    // remove keys to be ignored
+    if (count($ignore_keys)) {
+      _features_remove_ignores($array, $ignore_keys);
+    }
+  }
+  _features_sanitize($array, $remove_empty);
+}
+
 /**
  * "Sanitizes" an array recursively, performing two key operations:
  * - Sort an array by its keys (assoc) or values (non-assoc)
- * - Remove any null or empty values for associative arrays (array_filter()).
+ * @param bool $remove_empty if set, remove null or empty values for assoc arrays.
  */
-function _features_sanitize(&$array) {
+function _features_sanitize(&$array, $remove_empty = TRUE) {
+  if (is_object($array)) {
+    $array = get_object_vars($array);
+  }
   if (is_array($array)) {
     $is_assoc = _features_is_assoc($array);
     if ($is_assoc) {
       ksort($array, SORT_STRING);
-      $array = array_filter($array);
+      if ($remove_empty) {
+        $array = array_filter($array);
+      }
     }
     else {
       sort($array);
     }
     foreach ($array as $k => $v) {
-      if (is_array($v)) {
+      if (is_array($v) or is_object($v)) {
         _features_sanitize($array[$k]);
-        if ($is_assoc && empty($array[$k])) {
+        if ($remove_empty && $is_assoc && empty($array[$k])) {
           unset($array[$k]);
         }
       }
@@ -1010,3 +1076,93 @@ function _features_sanitize(&$array) {
 function _features_is_assoc($array) {
   return (is_array($array) && (0 !== count(array_diff_key($array, array_keys(array_keys($array)))) || count($array)==0));
 }
+
+/**
+ * Removes recursion from an object or array.
+ *
+ * Taken from https://code.google.com/p/formaldehyde/source/browse/trunk/formaldehyde.php
+ * Also used in node_export module
+ *
+ * @param $o mixed
+ * @return mixed
+ *   returns a copy of the object or array with recursion removed
+ */
+function features_remove_recursion($o) {
+  if (is_array($o) || is_object($o)) {
+    $re = '#(r|R):([0-9]+);#';
+    $serialize = serialize($o);
+    if (preg_match($re, $serialize)) {
+      $last = $pos = 0;
+      while (false !== ($pos = strpos($serialize, 's:', $pos))) {
+        $chunk = substr($serialize, $last, $pos - $last);
+        if (preg_match($re, $chunk)) {
+          $length = strlen($chunk);
+          $chunk = preg_replace_callback($re, '_features_remove_recursion', $chunk);
+          $serialize = substr($serialize, 0, $last) . $chunk . substr($serialize, $last + ($pos - $last));
+          $pos += strlen($chunk) - $length;
+        }
+        $pos += 2;
+        $last = strpos($serialize, ':', $pos);
+        $length = substr($serialize, $pos, $last - $pos);
+        $last += 4 + $length;
+        $pos = $last;
+      }
+      $serialize = substr($serialize, 0, $last) . preg_replace_callback($re, '_features_remove_recursion', substr($serialize, $last));
+      $o = unserialize($serialize);
+    }
+  }
+  return $o;
+}
+
+/**
+ * Callback function for preg_replace_callback() to remove recursion.
+ */
+function _features_remove_recursion($m) {
+  $r = "\x00{$m[1]}ecursion_features";
+  return 's:' . strlen($r . $m[2]) . ':"' . $r . $m[2] . '";';
+}
+
+/**
+ * Helper to removes a set of keys an object/array.
+ *
+ * @param $item
+ *   An object or array passed by reference.
+ * @param $ignore_keys
+ *   Array of keys to be ignored. Values are the level of the key.
+ * @param $level
+ *   Level of key to remove.  Up to 2 levels deep because $item can still be
+ *   recursive
+ */
+function _features_remove_ignores(&$item, $ignore_keys, $level = -1) {
+  $is_object = is_object($item);
+  if (!is_array($item) && !is_object($item)) {
+    return;
+  }
+  foreach ($item as $key => &$value) {
+    if (isset($ignore_keys[$key]) && ($ignore_keys[$key] == $level)) {
+      if ($is_object) {
+        unset($item->$key);
+      }
+      else {
+        unset($item[$key]);
+      }
+    }
+    elseif (($level < 2) && (is_array($value) || is_object($value))) {
+      _features_remove_ignores($value, $ignore_keys, $level+1);
+    }
+  }
+  unset($value);
+}
+
+/**
+ * Returns an array of keys to be ignored for various exportables
+ * @param $component
+ *   The component to retrieve ignore_keys from.
+ */
+function _features_get_ignore_keys($component) {
+  static $cache;
+  if (!isset($cache[$component])) {
+    $cache[$component] = module_invoke_all('features_ignore', $component);
+  }
+  return $cache[$component];
+}

+ 7 - 4
sites/all/modules/contrib/admin/features/features.info

@@ -3,12 +3,15 @@ description = "Provides feature management for Drupal."
 core = 7.x
 package = "Features"
 files[] = tests/features.test
+test_dependencies[] = image
+test_dependencies[] = strongarm
+test_dependencies[] = taxonomy
+test_dependencies[] = views
 
 configure = admin/structure/features/settings
 
-; Information added by Drupal.org packaging script on 2015-01-05
-version = "7.x-2.3"
+; Information added by Drupal.org packaging script on 2018-11-01
+version = "7.x-2.11"
 core = "7.x"
 project = "features"
-datestamp = "1420492083"
-
+datestamp = "1541050686"

+ 27 - 4
sites/all/modules/contrib/admin/features/features.install

@@ -5,6 +5,15 @@
  * Install, update and uninstall functions for the features module.
  */
 
+/**
+ * Implements hook_schema().
+ */
+function features_schema() {
+  $schema['cache_features'] = drupal_get_schema_unprocessed('system', 'cache');
+  $schema['cache_features']['description'] = 'Cache table for features to store module info.';
+  return $schema;
+}
+
 /**
  * Implements hook_install().
  */
@@ -27,10 +36,14 @@ function features_uninstall() {
   variable_del('features_ignored_orphans');
   variable_del('features_feature_locked');
   variable_del('features_lock_mode');
-  foreach (array_keys(features_get_components()) as $component) {
-    variable_del('features_admin_show_component_' . $component);
-    variable_del('features_component_locked_' . $component);
-  }
+
+  db_delete('variable')
+    ->condition('name', 'features_admin_show_component_%', 'LIKE')
+    ->execute();
+  db_delete('variable')
+    ->condition('name', 'features_component_locked_%', 'LIKE')
+    ->execute();
+
   if (db_table_exists('menu_custom')) {
     db_delete('menu_custom')
       ->condition('menu_name', 'features')
@@ -126,3 +139,13 @@ function features_update_6101() {
   }
   return array();
 }
+
+/**
+ * Add {cache_features} table.
+ */
+function features_update_7200() {
+  if (!db_table_exists('cache_features')) {
+    $schema = drupal_get_schema_unprocessed('system', 'cache');
+    db_create_table('cache_features', $schema);
+  }
+}

+ 7 - 5
sites/all/modules/contrib/admin/features/features.js

@@ -128,10 +128,12 @@ jQuery.fn.sortElements = (function(){
             if (!$(this).hasClass('features-checkall')) {
               var key = $(this).attr('name');
               var matches = key.match(/^([^\[]+)(\[.+\])?\[(.+)\]\[(.+)\]$/);
-              var component = matches[1];
-              var item = matches[4];
-              if ((component in moduleConflicts) && (moduleConflicts[component].indexOf(item) != -1)) {
-                $(this).parent().addClass('features-conflict');
+              if (matches != null) {
+                var component = matches[1];
+                var item = matches[4];
+                if ((component in moduleConflicts) && (moduleConflicts[component].indexOf(item) != -1)) {
+                  $(this).parent().addClass('features-conflict');
+                }
               }
             }
           });
@@ -290,7 +292,7 @@ jQuery.fn.sortElements = (function(){
       }
 
       // Handle component selection UI
-      $('#features-export-wrapper input[type=checkbox]', context).click(function() {
+      $('#features-export-wrapper input[type=checkbox]:not(.processed)', context).addClass('processed').click(function() {
         _resetTimeout();
         if ($(this).hasClass('component-select')) {
           moveCheckbox(this, 'added', true);

+ 112 - 19
sites/all/modules/contrib/admin/features/features.module

@@ -84,8 +84,7 @@ function features_menu() {
   $items['admin/structure/features/cleanup'] = array(
     'title' => 'Cleanup',
     'description' => 'Clear cache after enabling/disabling a feature.',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('features_cleanup_form', 4),
+    'page callback' => 'features_cleanup',
     'type' => MENU_CALLBACK,
     'file' => 'features.admin.inc',
     'weight' => 1,
@@ -128,7 +127,7 @@ function features_menu() {
     'description' => 'Display components of a feature.',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('features_admin_components', 3),
-    'load arguments' => array(3, TRUE),
+    'load arguments' => array(TRUE),
     'access callback' => 'user_access',
     'access arguments' => array('administer features'),
     'type' => MENU_CALLBACK,
@@ -147,7 +146,7 @@ function features_menu() {
     'description' => 'Recreate an existing feature.',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('features_export_form', 3),
-    'load arguments' => array(3, TRUE),
+    'load arguments' => array(TRUE),
     'access callback' => 'user_access',
     'access arguments' => array('administer features'),
     'type' => MENU_LOCAL_TASK,
@@ -160,7 +159,7 @@ function features_menu() {
       'description' => 'Compare default and current feature.',
       'page callback' => 'features_feature_diff',
       'page arguments' => array(3, 5),
-      'load arguments' => array(3, TRUE),
+      'load arguments' => array(TRUE),
       'access callback' => 'features_access_override_actions',
       'access arguments' => array(3),
       'type' => MENU_LOCAL_TASK,
@@ -173,7 +172,7 @@ function features_menu() {
     'description' => 'Lock a feature or components.',
     'page callback' => 'features_admin_lock',
     'page arguments' => array(3, 5, 6),
-    'load arguments' => array(3, TRUE, TRUE),
+    'load arguments' => array(TRUE),
     'access arguments' => array('administer features'),
     'type' => MENU_CALLBACK,
     'file' => 'features.admin.inc',
@@ -183,7 +182,7 @@ function features_menu() {
     'description' => 'Javascript status call back.',
     'page callback' => 'features_feature_status',
     'page arguments' => array(3),
-    'load arguments' => array(3, TRUE),
+    'load arguments' => array(TRUE),
     'access callback' => 'user_access',
     'access arguments' => array('administer features'),
     'type' => MENU_CALLBACK,
@@ -268,13 +267,20 @@ function features_theme() {
  * Implements hook_flush_caches().
  */
 function features_flush_caches() {
-  if (variable_get('features_rebuild_on_flush', TRUE)) {
+  if (($modules_changed = variable_get('features_modules_changed', FALSE)) || variable_get('features_rebuild_on_flush', TRUE)) {
+    if ($modules_changed) {
+      variable_set('features_modules_changed', FALSE);
+    }
     features_rebuild();
     // Don't flush the modules cache during installation, for performance reasons.
     if (variable_get('install_task') == 'done') {
       features_get_modules(NULL, TRUE);
     }
   }
+
+  if (db_table_exists('cache_features')) {
+    return array('cache_features');
+  }
   return array();
 }
 
@@ -349,6 +355,15 @@ function features_modules_disabled($modules) {
  * Implements hook_modules_enabled().
  */
 function features_modules_enabled($modules) {
+  // Allow distributions to disable this behavior and rebuild the features
+  // manually inside a batch.
+  if (!variable_get('features_rebuild_on_module_install', TRUE)) {
+    return;
+  }
+
+  // mark modules as being changed for test in features_flush_caches
+  variable_set('features_modules_changed', TRUE);
+
   // Go through all modules and gather features that can be enabled.
   $items = array();
   foreach ($modules as $module) {
@@ -385,7 +400,7 @@ function features_include($reset = FALSE) {
     // Features provides integration on behalf of these modules.
     // The features include provides handling for the feature dependencies.
     // Note that ctools is placed last because it implements hooks "dynamically" for other modules.
-    $modules = array('features', 'block', 'context', 'field', 'filter', 'image', 'locale', 'menu', 'node', 'taxonomy', 'user', 'views', 'ctools');
+    $modules = array('features', 'block', 'contact', 'context', 'field', 'filter', 'image', 'locale', 'menu', 'node', 'taxonomy', 'user', 'views', 'ctools');
 
     foreach (array_filter($modules, 'module_exists') as $module) {
       module_load_include('inc', 'features', "includes/features.$module");
@@ -488,6 +503,7 @@ function features_load_feature($name, $reset = FALSE) {
         $features[$name]->name = $name;
         $features[$name]->filename = drupal_get_path('module', $name) . '/' . $name . '.module';
         $features[$name]->type = 'module';
+        $features[$name]->status = module_exists($name);
         $features[$name]->info = $info + $defaults;
       }
     }
@@ -503,7 +519,7 @@ function features_load_feature($name, $reset = FALSE) {
  * Return a module 'object' including .info information.
  *
  * @param $name
- *   The name of the module to retrieve information for. If ommitted,
+ *   The name of the module to retrieve information for. If omitted,
  *   an array of all available modules will be returned.
  * @param $reset
  *   Whether to reset the cache.
@@ -535,13 +551,13 @@ function features_get_components($component = NULL, $key = NULL, $reset = FALSE)
 
   if ($reset || !isset($components) || !isset($component_by_key)) {
     $components = $component_by_key = array();
-    if (!$reset && ($cache = cache_get('features_api'))) {
+    if (!$reset && ($cache = cache_get('features_api', 'cache_features'))) {
       $components = $cache->data;
     }
     else {
       $components = module_invoke_all('features_api');
       drupal_alter('features_api', $components);
-      cache_set('features_api', $components);
+      cache_set('features_api', $components, 'cache_features');
     }
 
     foreach ($components as $component_type => $component_information) {
@@ -607,6 +623,8 @@ function features_hook($component, $hook, $reset = FALSE) {
  *   Clear the module info cache.
  */
 function features_install_modules($modules) {
+  variable_set('features_modules_changed', TRUE);
+
   module_load_include('inc', 'features', 'features.export');
   $files = system_rebuild_module_data();
 
@@ -650,7 +668,7 @@ function features_get_features($name = NULL, $reset = FALSE) {
 function features_get_info($type = 'module', $name = NULL, $reset = FALSE) {
   static $cache;
   if (!isset($cache)) {
-    $cache = cache_get('features_module_info');
+    $cache = cache_get('features_module_info', 'cache_features');
   }
   if (empty($cache) || $reset) {
     $data = array(
@@ -738,14 +756,14 @@ function features_get_info($type = 'module', $name = NULL, $reset = FALSE) {
     $data['feature'] = $sorted;
 
     variable_set('features_ignored_orphans', $ignored);
-    cache_set("features_module_info", $data);
+    cache_set('features_module_info', $data, 'cache_features');
     $cache = new stdClass();
     $cache->data = $data;
   }
   if (!empty($name)) {
-    return !empty($cache->data[$type][$name]) ? clone $cache->data[$type][$name] : array();
+    return !empty($cache->data[$type][$name]) ? clone $cache->data[$type][$name] : FALSE;
   }
-  return !empty($cache->data[$type]) ? $cache->data[$type] : array();
+  return !empty($cache->data[$type]) ? $cache->data[$type] : FALSE;
 }
 
 /**
@@ -892,7 +910,7 @@ function features_access_override_actions($feature) {
 
       features_include();
       module_load_include('inc', 'features', 'features.export');
-      $access[$feature->name] = in_array(features_get_storage($feature->name), array(FEATURES_OVERRIDDEN, FEATURES_NEEDS_REVIEW)) && user_access('administer features');
+      $access[$feature->name] = in_array(features_get_storage($feature->name), array(FEATURES_DEFAULT, FEATURES_OVERRIDDEN, FEATURES_NEEDS_REVIEW));
     }
     return $access[$feature->name];
   }
@@ -903,7 +921,9 @@ function features_access_override_actions($feature) {
  * Implements hook_form_alter() for system_modules form().
  */
 function features_form_system_modules_alter(&$form) {
-  features_rebuild();
+  if (variable_get('features_rebuild_modules_page', FALSE)) {
+    features_rebuild();
+  }
 }
 
 /**
@@ -1067,6 +1087,7 @@ function features_hook_info() {
     'features_api',
     'features_pipe_alter',
     'features_export_alter',
+    'features_export_options_alter',
   );
   return array_fill_keys($hooks, array('group' => 'features'));
 }
@@ -1075,6 +1096,9 @@ function features_hook_info() {
  * Change vocabularies permission, from vocab id to machine name and vice versa.
  */
 function _user_features_change_term_permission(&$perm, $type = 'vid') {
+  if (!module_exists('taxonomy')) {
+    return;
+  }
   // Export vocabulary permissions using the machine name, instead of vocabulary
   // id.
   if (strpos($perm, 'edit terms in ') !== FALSE || strpos($perm, 'delete terms in ') !== FALSE) {
@@ -1186,7 +1210,6 @@ function features_feature_lock($feature, $component = NULL) {
   variable_set('features_feature_locked', $locked);
 }
 
-
 /**
  * Unlocks a feature or it's component.
  */
@@ -1200,3 +1223,73 @@ function features_feature_unlock($feature, $component = NULL) {
   }
   variable_set('features_feature_locked', $locked);
 }
+
+/**
+ * Sets/Returns the current language to english to ensure a proper export.
+ */
+function _features_export_language($language = NULL) {
+  $current = $GLOBALS['language'];
+  if (isset($language)) {
+    $GLOBALS['language'] = $language;
+  }
+  elseif ($GLOBALS['language']->language != 'en') {
+    // Create the language object as language_default() does.
+    $GLOBALS['language'] = (object) array(
+      'language' => 'en',
+      'name' => 'English',
+      'native' => 'English',
+      'direction' => 0,
+      'enabled' => 1,
+      'plurals' => 0,
+      'formula' => '',
+      'domain' => '',
+      'prefix' => '',
+      'weight' => 0,
+      'javascript' => '',
+    );
+  }
+  return $current;
+}
+
+/**
+ * Implements hook_features_ignore().
+ */
+function features_features_ignore($component) {
+  // Determine which keys need to be ignored for override diff for various components.
+  // Value is how many levels deep the key is.
+  $ignores = array();
+  switch ($component) {
+    case 'views_view':
+      $ignores['current_display'] = 0;
+      $ignores['display_handler'] = 0;
+      $ignores['handler'] = 2;
+      $ignores['query'] = 0;
+      $ignores['localization_plugin'] = 0;
+      // Views automatically adds these two on export to set values.
+      $ignores['api_version'] = 0;
+      $ignores['disabled'] = 0;
+      break;
+    case 'image':
+      $ignores['module'] = 0;
+      $ignores['name'] = 0;
+      $ignores['storage'] = 0;
+      // Various properties are loaded into the effect in image_styles.
+      $ignores['summary theme'] = 2;
+      $ignores['module'] = 2;
+      $ignores['label'] = 2;
+      $ignores['help'] = 2;
+      $ignores['form callback'] = 2;
+      $ignores['effect callback'] = 2;
+      $ignores['dimensions callback'] = 2;
+      break;
+    case 'field':
+      $ignores['locked'] = 1;
+      break;
+    case 'field_base':
+      $ignores['indexes'] = 0;
+      break;
+    case 'taxonomy':
+      $ignores['hierarchy'] = 0;
+  }
+  return $ignores;
+}

+ 115 - 0
sites/all/modules/contrib/admin/features/includes/features.contact.inc

@@ -0,0 +1,115 @@
+<?php
+
+/**
+ * @file
+ * Features integration for 'contact' module.
+ */
+
+/**
+ * Implements hook_features_api().
+ */
+function contact_features_api() {
+  return array(
+    'contact_categories' => array(
+      'name' => t('Contact categories'),
+      'feature_source' => TRUE,
+      'default_hook' => 'contact_categories_defaults',
+      'default_file' => FEATURES_DEFAULTS_INCLUDED,
+    ),
+  );
+}
+
+/**
+ * Implements hook_features_export_options().
+ */
+function contact_categories_features_export_options() {
+  $options = array();
+  $categories = db_select('contact', 'c')->fields('c')->execute()->fetchAll();
+  foreach ($categories as $category) {
+    $options["$category->category"] = "$category->category";
+  }
+  return $options;
+}
+
+/**
+ * Implements hook_features_export().
+ */
+function contact_categories_features_export($data, &$export, $module_name = '') {
+  $export['dependencies']['features'] = 'features';
+  $export['dependencies']['contact'] = 'contact';
+
+  foreach ($data as $name) {
+    $export['features']['contact_categories'][$name] = $name;
+  }
+
+  return array();
+}
+
+/**
+ * Implements hook_features_export_render().
+ */
+function contact_categories_features_export_render($module, $data, $export = NULL) {
+  $render = array();
+  foreach ($data as $name) {
+    $export_category = db_select('contact', 'c')
+      ->fields('c', array('cid', 'category'))
+      ->condition('category', $name, 'LIKE')
+      ->execute()
+      ->fetchAll();
+    if (isset($export_category[0]->cid) && ($category = contact_load($export_category[0]->cid))) {
+      unset($category['cid']);
+      $render[$name] = $category;
+    }
+  }
+  return array('contact_categories_defaults' => '  return ' . features_var_export($render, '  ') . ';');
+}
+
+/**
+ * Implements hook_features_revert().
+ */
+function contact_categories_features_revert($module) {
+  return contact_categories_features_rebuild($module);
+}
+
+/**
+ * Implements hook_features_rebuild().
+ */
+function contact_categories_features_rebuild($module) {
+  if ($defaults = features_get_default('contact_categories', $module)) {
+    foreach ($defaults as $default_category) {
+      $existing_categories = db_select('contact', 'c')
+        ->fields('c', array('cid', 'category'))
+        ->execute()
+        ->fetchAll();
+      if ($existing_categories) {
+        foreach ($existing_categories as $existing_category) {
+          if ($default_category['category'] == $existing_category->category) {
+            db_update('contact')
+              ->fields(
+                array(
+                  'recipients' => $default_category['recipients'],
+                  'reply' => $default_category['reply'],
+                  'weight' => $default_category['weight'],
+                  'selected' => $default_category['selected'],
+                )
+              )
+              ->condition('category', $existing_category->category, '=')
+              ->execute();
+          }
+          else {
+            db_merge('contact')
+              ->key(array('category' => $default_category['category']))
+              ->fields($default_category)
+              ->execute();
+          }
+        }
+      }
+      else {
+        db_merge('contact')
+          ->key(array('category' => $default_category['category']))
+          ->fields($default_category)
+          ->execute();
+      }
+    }
+  }
+}

+ 58 - 11
sites/all/modules/contrib/admin/features/includes/features.field.inc

@@ -151,14 +151,14 @@ function field_base_features_export_render($module, $data, $export = NULL) {
   foreach ($data as $identifier) {
     if ($field = features_field_base_load($identifier)) {
       unset($field['columns']);
-      // unset($field['locked']);
+      unset($field['foreign keys']);
       // Only remove the 'storage' declaration if the field is using the default
       // storage type.
       if ($field['storage']['type'] == variable_get('field_storage_default', 'field_sql_storage')) {
         unset($field['storage']);
       }
       // If we still have a storage declaration here it means that a non-default
-      // storage type was altered into to the field definition. And noone would
+      // storage type was altered into to the field definition. And no one would
       // never need to change the 'details' key, so don't render it.
       if (isset($field['storage']['details'])) {
         unset($field['storage']['details']);
@@ -166,8 +166,15 @@ function field_base_features_export_render($module, $data, $export = NULL) {
 
       _field_instance_features_export_sort($field);
       $field_export = features_var_export($field, '  ');
+      $field_prefix = '  // Exported field_base: ';
       $field_identifier = features_var_export($identifier);
-      $code[] = "  // Exported field_base: {$field_identifier}";
+      if (features_field_export_needs_wrap($field_prefix, $field_identifier)) {
+        $code[] = rtrim($field_prefix);
+        $code[] = "  // {$field_identifier}.";
+      }
+      else {
+        $code[] = $field_prefix . $field_identifier . '.';
+      }
       $code[] = "  \$field_bases[{$field_identifier}] = {$field_export};";
       $code[] = "";
     }
@@ -190,8 +197,15 @@ function field_instance_features_export_render($module, $data, $export = NULL) {
     if ($instance = features_field_instance_load($identifier)) {
       _field_instance_features_export_sort($instance);
       $field_export = features_var_export($instance, '  ');
+      $instance_prefix = '  // Exported field_instance: ';
       $instance_identifier = features_var_export($identifier);
-      $code[] = "  // Exported field_instance: {$instance_identifier}";
+      if (features_field_export_needs_wrap($instance_prefix, $instance_identifier)) {
+        $code[] = rtrim($instance_prefix);
+        $code[] = "  // {$instance_identifier}.";
+      }
+      else {
+        $code[] = $instance_prefix . $instance_identifier . '.';
+      }
       $code[] = "  \$field_instances[{$instance_identifier}] = {$field_export};";
       $code[] = "";
 
@@ -260,12 +274,23 @@ function field_base_features_rebuild($module) {
       // Create or update field.
       if (isset($existing_fields[$field['field_name']])) {
         $existing_field = $existing_fields[$field['field_name']];
-        if ($field + $existing_field !== $existing_field) {
-          field_update_field($field);
+        $array_diff_result = features_array_diff_assoc_recursive($field + $existing_field, $existing_field);
+        if (!empty($array_diff_result)) {
+          try {
+            field_update_field($field);
+          }
+          catch (FieldException $e) {
+            watchdog('features', 'Attempt to update field %label failed: %message', array('%label' => $field['field_name'], '%message' => $e->getMessage()), WATCHDOG_ERROR);
+          }
         }
       }
       else {
-        field_create_field($field);
+        try {
+          field_create_field($field);
+        }
+        catch (FieldException $e) {
+          watchdog('features', 'Attempt to create field %label failed: %message', array('%label' => $field['field_name'], '%message' => $e->getMessage()), WATCHDOG_ERROR);
+        }
         $existing_fields[$field['field_name']] = $field;
       }
       variable_set('menu_rebuild_needed', TRUE);
@@ -294,7 +319,7 @@ function field_instance_features_rebuild($module) {
       // Create or update field instance.
       if (isset($existing_instances[$field_instance['entity_type']][$field_instance['bundle']][$field_instance['field_name']])) {
         $existing_instance = $existing_instances[$field_instance['entity_type']][$field_instance['bundle']][$field_instance['field_name']];
-        if ($field_instance + $existing_instance !== $existing_instance) {
+        if ($field_instance + $existing_instance != $existing_instance) {
           try {
             field_update_instance($field_instance);
           }
@@ -395,7 +420,7 @@ function field_features_export_render($module, $data, $export = NULL) {
         unset($field['field_config']['storage']);
       }
       // If we still have a storage declaration here it means that a non-default
-      // storage type was altered into to the field definition. And noone would
+      // storage type was altered into to the field definition. And no one would
       // never need to change the 'details' key, so don't render it.
       if (isset($field['field_config']['storage']['details'])) {
         unset($field['field_config']['storage']['details']);
@@ -469,7 +494,8 @@ function field_features_rebuild($module) {
       $field_config = $field['field_config'];
       if (isset($existing_fields[$field_config['field_name']])) {
         $existing_field = $existing_fields[$field_config['field_name']];
-        if ($field_config + $existing_field !== $existing_field) {
+        $array_diff_result = features_array_diff_assoc_recursive($field_config + $existing_field, $existing_field);
+        if (!empty($array_diff_result)) {
           try {
             field_update_field($field_config);
           }
@@ -492,7 +518,7 @@ function field_features_rebuild($module) {
       $field_instance = $field['field_instance'];
       if (isset($existing_instances[$field_instance['entity_type']][$field_instance['bundle']][$field_instance['field_name']])) {
         $existing_instance = $existing_instances[$field_instance['entity_type']][$field_instance['bundle']][$field_instance['field_name']];
-        if ($field_instance + $existing_instance !== $existing_instance) {
+        if ($field_instance + $existing_instance != $existing_instance) {
           field_update_instance($field_instance);
         }
       }
@@ -528,3 +554,24 @@ function features_field_load($identifier) {
   }
   return FALSE;
 }
+
+/**
+ * Determine if a field export line needs to be wrapped.
+ *
+ * Drupal code standards specify that comments should wrap at 80 characters or
+ * less.
+ *
+ * @param string $prefix
+ *   The prefix to be exported before the field identifier.
+ * @param string $identifier
+ *   The field identifier.
+ *
+ * @return BOOL
+ *   TRUE if the line should be wrapped after the prefix, else FALSE.
+ *
+ * @see https://www.drupal.org/node/1354
+ */
+function features_field_export_needs_wrap($prefix, $identifier) {
+  // Check for 79 characters, since the comment ends with a full stop.
+  return (strlen($prefix) + strlen($identifier) > 79);
+}

+ 28 - 13
sites/all/modules/contrib/admin/features/includes/features.image.inc

@@ -73,11 +73,17 @@ function image_features_export_render($module_name, $data, $export = NULL) {
  */
 function image_features_revert($module) {
   if ($default_styles = features_get_default('image', $module)) {
-    foreach (array_keys($default_styles) as $default_style) {
-      if ($style = image_style_load($default_style)) {
+    foreach ($default_styles as $default_style_name => $default_style) {
+      if ($style = image_style_load($default_style_name)) {
         if ($style['storage'] != IMAGE_STORAGE_DEFAULT) {
           image_default_style_revert($style);
         }
+        else {
+          // Verify that the loaded style still matches what's in code.
+          if ($default_style['effects'] !== $style['effects']) {
+            image_default_style_revert($style);
+          }
+        }
       }
     }
   }
@@ -86,16 +92,25 @@ function image_features_revert($module) {
 /**
  * Remove unnecessary keys for export.
  */
-function _image_features_style_sanitize(&$style, $child = FALSE) {
-  $omit = $child ? array('isid', 'ieid', 'storage') : array('isid', 'ieid', 'storage', 'module');
-  if (is_array($style)) {
-    foreach ($style as $k => $v) {
-      if (in_array($k, $omit, TRUE)) {
-        unset($style[$k]);
-      }
-      else if (is_array($v)) {
-        _image_features_style_sanitize($style[$k], TRUE);
-      }
-    }
+function _image_features_style_sanitize(array &$style) {
+  // Sanitize style: Don't export numeric IDs and things which get overwritten
+  // in image_styles() or are code/storage specific. The name property will be
+  // the key of the exported $style array.
+  $style = array_diff_key($style, array_flip(array(
+    'isid',
+    'name',
+    'module',
+    'storage',
+  )));
+
+  // Sanitize effects: all that needs to be kept is name, weight and data,
+  // which holds all the style-specific configuration. Other keys are assumed
+  // to belong to the definition of the effect itself, so not configuration.
+  foreach ($style['effects'] as $id => $effect) {
+    $style['effects'][$id] = array_intersect_key($effect, array_flip(array(
+      'name',
+      'data',
+      'weight',
+    )));
   }
 }

+ 3 - 2
sites/all/modules/contrib/admin/features/includes/features.locale.inc

@@ -134,14 +134,15 @@ function _features_language_save($language) {
       ->fields(array(
         'plurals' => empty($language->plurals) ? 0 : $language->plurals,
         'formula' => empty($language->formula) ? '' : $language->formula,
+        'weight' => empty($language->weight) ? 0 : $language->weight,
       ))
       ->condition('language', $language->language)
       ->execute();
   }
   // Update Existing language.
   else {
-    // @TODO: get properties from schema.
-    $properties = array('language', 'name', 'native', 'direction', 'enabled', 'plurals', 'formula', 'domain', 'prefix', 'weight', 'javascript');
+    // Get field list from table schema.
+    $properties = drupal_schema_fields_sql('languages');
     // The javascript hash is not in the imported data but should be empty
     if (!isset($language->javascript)) {
       $language->javascript = '';

+ 11 - 7
sites/all/modules/contrib/admin/features/includes/features.menu.inc

@@ -103,7 +103,6 @@ function menu_custom_features_export_render($module, $data) {
     $code[] = features_translatables_export($translatables, '  ');
   }
 
-  $code[] = '';
   $code[] = '  return $menus;';
   $code = implode("\n", $code);
   return array('menu_default_menu_custom' => $code);
@@ -248,16 +247,16 @@ function menu_links_features_export_render($module, $data, $export = NULL) {
        unset($link['plid']);
        unset($link['mlid']);
 
-      $code[] = "  // Exported menu link: {$new_identifier}";
+      $code[] = "  // Exported menu link: {$new_identifier}.";
       $code[] = "  \$menu_links['{$new_identifier}'] = ". features_var_export($link, '  ') .";";
       $translatables[] = $link['link_title'];
     }
   }
+  $code[] = '';
   if (!empty($translatables)) {
     $code[] = features_translatables_export($translatables, '  ');
   }
 
-  $code[] = '';
   $code[] = '  return $menu_links;';
   $code = implode("\n", $code);
   return array('menu_default_menu_links' => $code);
@@ -316,6 +315,7 @@ function menu_links_features_rebuild_ordered($menu_links, $reset = FALSE) {
     foreach ($unordered as $link) {
       $identifier = menu_links_features_identifier($link);
       $ordered[$identifier] = 0;
+      $all_links[$identifier] = $link;
     }
     asort($ordered);
   }
@@ -420,8 +420,12 @@ function features_menu_link_load($identifier) {
  * Returns a lowercase clean string with only letters, numbers and dashes
  */
 function features_clean_title($str) {
-  return strtolower(preg_replace_callback('/(\s)|([^a-zA-Z\-0-9])/i', create_function(
-          '$matches',
-          'return $matches[1]?"-":"";'
-      ), $str));
+  return strtolower(preg_replace_callback('/(\s)|([^a-zA-Z\-0-9])/i', '_features_clean_title', $str));
+}
+
+/**
+ * Callback function for preg_replace_callback() to clean a string.
+ */
+function _features_clean_title($matches) {
+  return $matches[1] ? '-' : '';
 }

+ 15 - 4
sites/all/modules/contrib/admin/features/includes/features.node.inc

@@ -117,7 +117,7 @@ function node_features_revert($module = NULL) {
 }
 
 /**
- * Implements hook_features_disable().
+ * Implements hook_features_disable_feature().
  *
  * When a features module is disabled, modify any node types it provides so
  * they can be deleted manually through the content types UI.
@@ -125,7 +125,7 @@ function node_features_revert($module = NULL) {
  * @param $module
  *   Name of module that has been disabled.
  */
-function node_features_disable($module) {
+function node_features_disable_feature($module) {
   if ($default_types = features_get_default('node', $module)) {
     foreach ($default_types as $type_name => $type_info) {
       $type_info = node_type_load($type_name);
@@ -133,22 +133,26 @@ function node_features_disable($module) {
       $type_info->custom = 1;
       $type_info->modified = 1;
       $type_info->locked = 0;
+      $type_info->disabled = 0;
       node_type_save($type_info);
     }
   }
 }
 
 /**
- * Implements hook_features_enable().
+ * Implements hook_features_enable_feature().
  *
  * When a features module is enabled, modify any node types it provides so
  * they can no longer be deleted manually through the content types UI.
  *
+ * Update the database cache of node types if needed.
+ *
  * @param $module
  *   Name of module that has been enabled.
  */
-function node_features_enable($module) {
+function node_features_enable_feature($module) {
   if ($default_types = features_get_default('node', $module)) {
+    $rebuild = FALSE;
     foreach ($default_types as $type_name => $type_info) {
       // Ensure the type exists.
       if ($type_info = node_type_load($type_name)) {
@@ -156,8 +160,15 @@ function node_features_enable($module) {
         $type_info->custom = 0;
         $type_info->modified = 0;
         $type_info->locked = 1;
+        $type_info->disabled = 0;
         node_type_save($type_info);
       }
+      else {
+        $rebuild = TRUE;
+      }
+    }
+    if ($rebuild) {
+      node_types_rebuild();
     }
   }
 }

+ 44 - 1
sites/all/modules/contrib/admin/features/tests/features.test

@@ -14,6 +14,7 @@ class FeaturesUserTestCase extends DrupalWebTestCase {
       'name' => t('Component tests'),
       'description' => t('Run tests for components of Features.') ,
       'group' => t('Features'),
+      'dependencies' => array('views', 'strongarm'),
     );
   }
 
@@ -182,6 +183,7 @@ class FeaturesEnableTestCase extends DrupalWebTestCase {
       'name' => t('Features enable tests'),
       'description' => t('Run tests for enabling of features.') ,
       'group' => t('Features'),
+      'dependencies' => array('views', 'strongarm'),
     );
   }
 
@@ -218,7 +220,7 @@ class FeaturesEnableTestCase extends DrupalWebTestCase {
 
 
 /**
- * Tests intergration of ctools for features.
+ * Tests integration of ctools for features.
  */
 class FeaturesCtoolsIntegrationTest extends DrupalWebTestCase {
   protected $profile = 'testing';
@@ -231,6 +233,7 @@ class FeaturesCtoolsIntegrationTest extends DrupalWebTestCase {
       'name' => t('Features Chaos Tools integration'),
       'description' => t('Run tests for ctool integration of features.') ,
       'group' => t('Features'),
+      'dependencies' => array('views', 'strongarm'),
     );
   }
 
@@ -287,3 +290,43 @@ class FeaturesCtoolsIntegrationTest extends DrupalWebTestCase {
     }
   }
 }
+
+
+/**
+ * Test detecting modules as features. 
+ */
+class FeaturesDetectionTestCase extends DrupalWebTestCase {
+  protected $profile = 'testing';
+
+  /**
+   * Test info.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => t('Feature Detection tests'),
+      'description' => t('Run tests for detecting items as features.') ,
+      'group' => t('Features'),
+    );
+  }
+
+  /**
+   * Set up test.
+   */
+  public function setUp() {
+    parent::setUp(array(
+      'features',
+    ));
+  }
+
+  /**
+   * Run test.
+   */
+  public function test() {
+    module_load_include('inc', 'features', 'features.export');
+    // First test that features_populate inserts the features api key.
+    $export = features_populate(array(), array(), 'features_test_empty_fake');
+    $this->assertTrue(!empty($export['features']['features_api']) && key($export['features']['features_api']) == 'api:' . FEATURES_API, 'Features API key added to new export.');
+    $this->assertTrue((bool)features_get_features('features_test'), 'Features test recognized as a feature.');
+    $this->assertFalse((bool)features_get_features('features'), 'Features module not recognized as a feature.');
+  }
+}

+ 0 - 8
sites/all/modules/contrib/admin/features/tests/features_test/features_test.features.inc

@@ -29,16 +29,8 @@ function features_test_image_default_styles() {
 
   // Exported image style: features_test.
   $styles['features_test'] = array(
-    'name' => 'features_test',
     'effects' => array(
       2 => array(
-        'label' => 'Scale',
-        'help' => 'Scaling will maintain the aspect-ratio of the original image. If only a single dimension is specified, the other dimension will be calculated.',
-        'effect callback' => 'image_scale_effect',
-        'dimensions callback' => 'image_scale_dimensions',
-        'form callback' => 'image_scale_form',
-        'summary theme' => 'image_scale_summary',
-        'module' => 'image',
         'name' => 'image_scale',
         'data' => array(
           'width' => 100,

+ 3 - 4
sites/all/modules/contrib/admin/features/tests/features_test/features_test.info

@@ -21,9 +21,8 @@ features[user_permission][] = create features_test content
 features[views_view][] = features_test
 hidden = 1
 
-; Information added by Drupal.org packaging script on 2015-01-05
-version = "7.x-2.3"
+; Information added by Drupal.org packaging script on 2018-11-01
+version = "7.x-2.11"
 core = "7.x"
 project = "features"
-datestamp = "1420492083"
-
+datestamp = "1541050686"

+ 44 - 19
sites/all/modules/contrib/admin/google_analytics/README.txt

@@ -1,6 +1,6 @@
 
 Module: Google Analytics
-Author: Alexander Hass <http://drupal.org/user/85918>
+Author: Alexander Hass <https://drupal.org/user/85918>
 
 
 Description
@@ -12,12 +12,20 @@ Requirements
 
 * Google Analytics user account
 
-
 Installation
 ============
 Copy the 'googleanalytics' module directory in to your Drupal
 sites/all/modules directory as usual.
 
+Upgrading from 6.x-3.x and 7.x-1.x
+==================================
+If you upgrade from 6.x-3.x and 7.x-1.x (ga.js) to 7.x-2.x (analytics.js) you
+should verify if you used custom variables. Write down your settings or make a 
+screenshot. You need to re-configure the settings to use custom dimensions or
+metrics. There is no automatic upgrade path for custom variables feature. All
+other module settings are upgraded automatically.
+
+See https://support.google.com/analytics/answer/2795983?hl=en for more details.
 
 Usage
 =====
@@ -27,12 +35,8 @@ All pages will now have the required JavaScript added to the
 HTML footer can confirm this by viewing the page source from
 your browser.
 
-New approach to page tracking in 5.x-1.5 and 6.x-1.1
-====================================================
-With 5.x-1.5 and 6.x-1.1 there are new settings on the settings page at
-admin/config/system/googleanalytics. The "Page specific tracking" area now
-comes with an interface that copies Drupal's block visibility settings.
-
+Page specific tracking
+======================
 The default is set to "Add to every page except the listed pages". By
 default the following pages are listed for exclusion:
 
@@ -44,23 +48,24 @@ node/*/*
 user/*/*
 
 These defaults are changeable by the website administrator or any other
-user with 'administer google analytics' permission.
+user with 'Administer Google Analytics' permission.
 
-Like the blocks visibility settings in Drupal core, there is now a
-choice for "Add if the following PHP code returns TRUE." Sample PHP snippets
-that can be used in this textarea can be found on the handbook page
-"Overview-approach to block visibility" at http://drupal.org/node/64135.
+Like the blocks visibility settings in Drupal core, there is a choice for
+"Add if the following PHP code returns TRUE." Sample PHP snippets that can be
+used in this textarea can be found on the handbook page "Overview-approach to
+block visibility" at https://drupal.org/node/64135.
 
 Custom dimensions and metrics
 =============================
 One example for custom dimensions tracking is the "User roles" tracking.
 
-1. In the Google Analytics Management Interface you need to setup Dimension #1
-   with name e.g. "User roles". This step is required. Do not miss it, please.
+1. In the Google Analytics (https://marketingplatform.google.com/about/analytics/)
+   Management Interface you need to setup Dimension #1 with name 
+   e.g. "User roles". This step is required. Do not miss it, please.
 
-2. Enter the below configuration data into the custom dimensions settings form
-   under admin/config/system/googleanalytics. You can also choose another index,
-   but keep it always in sync with the index used in step #1.
+2. Enter the below configuration data into the Drupal custom dimensions settings
+   form under admin/config/system/googleanalytics. You can also choose another
+   index, but keep it always in sync with the index used in step #1.
 
    Index: 1
    Value: [current-user:role-names]
@@ -72,8 +77,28 @@ Advanced Settings
 =================
 You can include additional JavaScript snippets in the custom javascript
 code textarea. These can be found on the official Google Analytics pages
-and a few examples at http://drupal.org/node/248699. Support is not
+and a few examples at https://drupal.org/node/248699. Support is not
 provided for any customisations you include.
 
 To speed up page loading you may also cache the Google Analytics "analytics.js"
 file locally.
+
+Manual JS debugging
+===================
+For manual debugging of the JS code you are able to create a test node. This
+is the example HTML code for this test node. You need to enable debugging mode
+in your Drupal configuration of Google Analytics settings to see verbose
+messages in your browsers JS console.
+
+Title: Google Analytics test page
+
+Body:
+<ul>
+  <li><a href="mailto:foo@example.com">Mailto</a></li>
+  <li><a href="/files/test.txt">Download file</a></li>
+  <li><a class="colorbox" href="#">Open colorbox</a></li>
+  <li><a href="https://example.com/">External link</a></li>
+  <li><a href="/go/test">Go link</a></li>
+</ul>
+
+Text format: Full HTML

File diff suppressed because it is too large
+ 1 - 1
sites/all/modules/contrib/admin/google_analytics/googleanalytics.admin.inc


+ 9 - 0
sites/all/modules/contrib/admin/google_analytics/googleanalytics.admin.js

@@ -65,6 +65,15 @@ Drupal.behaviors.trackingSettingsSummary = {
       if ($('input#edit-googleanalytics-trackfiles', context).is(':checked')) {
         vals.push(Drupal.t('Downloads'));
       }
+      if ($('input#edit-googleanalytics-trackcolorbox', context).is(':checked')) {
+        vals.push(Drupal.t('Colorbox'));
+      }
+      if ($('input#edit-googleanalytics-tracklinkid', context).is(':checked')) {
+        vals.push(Drupal.t('Link attribution'));
+      }
+      if ($('input#edit-googleanalytics-trackurlfragments', context).is(':checked')) {
+        vals.push(Drupal.t('URL fragments'));
+      }
       if (!vals.length) {
         return Drupal.t('Not tracked');
       }

+ 50 - 20
sites/all/modules/contrib/admin/google_analytics/googleanalytics.debug.js

@@ -8,16 +8,16 @@ $(document).ready(function() {
   // clicks on all elements.
   $(document.body).bind("mousedown keyup touchstart", function(event) {
     console.group("Running Google Analytics for Drupal.");
-    console.info(event);
+    console.info("Event '%s' has been detected.", event.type);
 
     // Catch the closest surrounding link of a clicked element.
     $(event.target).closest("a,area").each(function() {
-      console.info("Element '%o' has been detected. Link '%s' found.", this, this.href);
+      console.info("Closest element '%o' has been found. URL '%s' extracted.", this, this.href);
 
       // Is the clicked URL internal?
       if (Drupal.googleanalytics.isInternal(this.href)) {
         // Skip 'click' tracking, if custom tracking events are bound.
-        if ($(this).is('.colorbox')) {
+        if ($(this).is('.colorbox') && (Drupal.settings.googleanalytics.trackColorbox)) {
           // Do nothing here. The custom event will handle all tracking.
           console.info("Click on .colorbox item has been detected.");
         }
@@ -25,12 +25,22 @@ $(document).ready(function() {
         else if (Drupal.settings.googleanalytics.trackDownload && Drupal.googleanalytics.isDownload(this.href)) {
           // Download link clicked.
           console.info("Download url '%s' has been found. Tracked download as extension '%s'.", Drupal.googleanalytics.getPageUrl(this.href), Drupal.googleanalytics.getDownloadExtension(this.href).toUpperCase());
-          ga("send", "event", "Downloads", Drupal.googleanalytics.getDownloadExtension(this.href).toUpperCase(), Drupal.googleanalytics.getPageUrl(this.href));
+          ga("send", {
+            "hitType": "event",
+            "eventCategory": "Downloads",
+            "eventAction": Drupal.googleanalytics.getDownloadExtension(this.href).toUpperCase(),
+            "eventLabel": Drupal.googleanalytics.getPageUrl(this.href),
+            "transport": "beacon"
+          });
         }
         else if (Drupal.googleanalytics.isInternalSpecial(this.href)) {
           // Keep the internal URL for Google Analytics website overlay intact.
           console.info("Click on internal special link '%s' has been tracked.", Drupal.googleanalytics.getPageUrl(this.href));
-          ga("send", "pageview", { "page": Drupal.googleanalytics.getPageUrl(this.href) });
+          ga("send", {
+            "hitType": "pageview",
+            "page": Drupal.googleanalytics.getPageUrl(this.href),
+            "transport": "beacon"
+          });
         }
         else {
           // e.g. anchor in same page or other internal page link
@@ -41,13 +51,25 @@ $(document).ready(function() {
         if (Drupal.settings.googleanalytics.trackMailto && $(this).is("a[href^='mailto:'],area[href^='mailto:']")) {
           // Mailto link clicked.
           console.info("Click on e-mail '%s' has been tracked.", this.href.substring(7));
-          ga("send", "event", "Mails", "Click", this.href.substring(7));
+          ga("send", {
+            "hitType": "event",
+            "eventCategory": "Mails",
+            "eventAction": "Click",
+            "eventLabel": this.href.substring(7),
+            "transport": "beacon"
+          });
         }
         else if (Drupal.settings.googleanalytics.trackOutbound && this.href.match(/^\w+:\/\//i)) {
-          if (Drupal.settings.googleanalytics.trackDomainMode != 2 || (Drupal.settings.googleanalytics.trackDomainMode == 2 && !Drupal.googleanalytics.isCrossDomain(this.hostname, Drupal.settings.googleanalytics.trackCrossDomains))) {
+          if (Drupal.settings.googleanalytics.trackDomainMode !== 2 || (Drupal.settings.googleanalytics.trackDomainMode === 2 && !Drupal.googleanalytics.isCrossDomain(this.hostname, Drupal.settings.googleanalytics.trackCrossDomains))) {
             // External link clicked / No top-level cross domain clicked.
             console.info("Outbound link '%s' has been tracked.", this.href);
-            ga("send", "event", "Outbound links", "Click", this.href);
+            ga("send", {
+              "hitType": "event",
+              "eventCategory": "Outbound links",
+              "eventAction": "Click",
+              "eventLabel": this.href,
+              "transport": "beacon"
+            });
           }
           else {
             console.info("Internal link '%s' clicked, not tracked.", this.href);
@@ -63,19 +85,27 @@ $(document).ready(function() {
   if (Drupal.settings.googleanalytics.trackUrlFragments) {
     window.onhashchange = function() {
       console.info("Track URL '%s' as pageview. Hash '%s' has changed.", location.pathname + location.search + location.hash, location.hash);
-      ga('send', 'pageview', location.pathname + location.search + location.hash);
-    }
+      ga("send", {
+        "hitType": "pageview",
+        "page": location.pathname + location.search + location.hash
+      });
+    };
   }
 
   // Colorbox: This event triggers when the transition has completed and the
   // newly loaded content has been revealed.
-  $(document).bind("cbox_complete", function () {
-    var href = $.colorbox.element().attr("href");
-    if (href) {
-      console.info("Colorbox transition to url '%s' has been tracked.", Drupal.googleanalytics.getPageUrl(href));
-      ga("send", "pageview", { "page": Drupal.googleanalytics.getPageUrl(href) });
-    }
-  });
+  if (Drupal.settings.googleanalytics.trackColorbox) {
+    $(document).bind("cbox_complete", function () {
+      var href = $.colorbox.element().attr("href");
+      if (href) {
+        console.info("Colorbox transition to url '%s' has been tracked.", Drupal.googleanalytics.getPageUrl(href));
+        ga("send", {
+          "hitType": "pageview",
+          "page": Drupal.googleanalytics.getPageUrl(href)
+        });
+      }
+    });
+  }
 
 });
 
@@ -92,7 +122,7 @@ $(document).ready(function() {
 Drupal.googleanalytics.isCrossDomain = function (hostname, crossDomains) {
   /**
    * jQuery < 1.6.3 bug: $.inArray crushes IE6 and Chrome if second argument is
-   * `null` or `undefined`, http://bugs.jquery.com/ticket/10076,
+   * `null` or `undefined`, https://bugs.jquery.com/ticket/10076,
    * https://github.com/jquery/jquery/commit/a839af034db2bd934e4d4fa6758a3fed8de74174
    *
    * @todo: Remove/Refactor in D8
@@ -151,8 +181,8 @@ Drupal.googleanalytics.isInternalSpecial = function (url) {
  * Extract the relative internal URL from an absolute internal URL.
  *
  * Examples:
- * - http://mydomain.com/node/1 -> /node/1
- * - http://example.com/foo/bar -> http://example.com/foo/bar
+ * - https://mydomain.com/node/1 -> /node/1
+ * - https://example.com/foo/bar -> https://example.com/foo/bar
  *
  * @param string url
  *   The web url to check.

+ 3 - 4
sites/all/modules/contrib/admin/google_analytics/googleanalytics.info

@@ -5,9 +5,8 @@ package = Statistics
 configure = admin/config/system/googleanalytics
 files[] = googleanalytics.test
 test_dependencies[] = token
-; Information added by Drupal.org packaging script on 2014-11-29
-version = "7.x-2.1"
+; Information added by Drupal.org packaging script on 2019-01-31
+version = "7.x-2.6"
 core = "7.x"
 project = "google_analytics"
-datestamp = "1417276982"
-
+datestamp = "1548968597"

File diff suppressed because it is too large
+ 22 - 15
sites/all/modules/contrib/admin/google_analytics/googleanalytics.install


+ 47 - 17
sites/all/modules/contrib/admin/google_analytics/googleanalytics.js

@@ -14,29 +14,51 @@ $(document).ready(function() {
       // Is the clicked URL internal?
       if (Drupal.googleanalytics.isInternal(this.href)) {
         // Skip 'click' tracking, if custom tracking events are bound.
-        if ($(this).is('.colorbox')) {
+        if ($(this).is('.colorbox') && (Drupal.settings.googleanalytics.trackColorbox)) {
           // Do nothing here. The custom event will handle all tracking.
           //console.info("Click on .colorbox item has been detected.");
         }
         // Is download tracking activated and the file extension configured for download tracking?
         else if (Drupal.settings.googleanalytics.trackDownload && Drupal.googleanalytics.isDownload(this.href)) {
           // Download link clicked.
-          ga("send", "event", "Downloads", Drupal.googleanalytics.getDownloadExtension(this.href).toUpperCase(), Drupal.googleanalytics.getPageUrl(this.href));
+          ga("send", {
+            "hitType": "event",
+            "eventCategory": "Downloads",
+            "eventAction": Drupal.googleanalytics.getDownloadExtension(this.href).toUpperCase(),
+            "eventLabel": Drupal.googleanalytics.getPageUrl(this.href),
+            "transport": "beacon"
+          });
         }
         else if (Drupal.googleanalytics.isInternalSpecial(this.href)) {
           // Keep the internal URL for Google Analytics website overlay intact.
-          ga("send", "pageview", { "page": Drupal.googleanalytics.getPageUrl(this.href) });
+          ga("send", {
+            "hitType": "pageview",
+            "page": Drupal.googleanalytics.getPageUrl(this.href),
+            "transport": "beacon"
+          });
         }
       }
       else {
         if (Drupal.settings.googleanalytics.trackMailto && $(this).is("a[href^='mailto:'],area[href^='mailto:']")) {
           // Mailto link clicked.
-          ga("send", "event", "Mails", "Click", this.href.substring(7));
+          ga("send", {
+            "hitType": "event",
+            "eventCategory": "Mails",
+            "eventAction": "Click",
+            "eventLabel": this.href.substring(7),
+            "transport": "beacon"
+          });
         }
         else if (Drupal.settings.googleanalytics.trackOutbound && this.href.match(/^\w+:\/\//i)) {
-          if (Drupal.settings.googleanalytics.trackDomainMode != 2 || (Drupal.settings.googleanalytics.trackDomainMode == 2 && !Drupal.googleanalytics.isCrossDomain(this.hostname, Drupal.settings.googleanalytics.trackCrossDomains))) {
+          if (Drupal.settings.googleanalytics.trackDomainMode !== 2 || (Drupal.settings.googleanalytics.trackDomainMode === 2 && !Drupal.googleanalytics.isCrossDomain(this.hostname, Drupal.settings.googleanalytics.trackCrossDomains))) {
             // External link clicked / No top-level cross domain clicked.
-            ga("send", "event", "Outbound links", "Click", this.href);
+            ga("send", {
+              "hitType": "event",
+              "eventCategory": "Outbound links",
+              "eventAction": "Click",
+              "eventLabel": this.href,
+              "transport": "beacon"
+            });
           }
         }
       }
@@ -46,18 +68,26 @@ $(document).ready(function() {
   // Track hash changes as unique pageviews, if this option has been enabled.
   if (Drupal.settings.googleanalytics.trackUrlFragments) {
     window.onhashchange = function() {
-      ga('send', 'pageview', location.pathname + location.search + location.hash);
-    }
+      ga("send", {
+        "hitType": "pageview",
+        "page": location.pathname + location.search + location.hash
+      });
+    };
   }
 
   // Colorbox: This event triggers when the transition has completed and the
   // newly loaded content has been revealed.
-  $(document).bind("cbox_complete", function () {
-    var href = $.colorbox.element().attr("href");
-    if (href) {
-      ga("send", "pageview", { "page": Drupal.googleanalytics.getPageUrl(href) });
-    }
-  });
+  if (Drupal.settings.googleanalytics.trackColorbox) {
+    $(document).bind("cbox_complete", function () {
+      var href = $.colorbox.element().attr("href");
+      if (href) {
+        ga("send", {
+          "hitType": "pageview",
+          "page": Drupal.googleanalytics.getPageUrl(href)
+        });
+      }
+    });
+  }
 
 });
 
@@ -74,7 +104,7 @@ $(document).ready(function() {
 Drupal.googleanalytics.isCrossDomain = function (hostname, crossDomains) {
   /**
    * jQuery < 1.6.3 bug: $.inArray crushes IE6 and Chrome if second argument is
-   * `null` or `undefined`, http://bugs.jquery.com/ticket/10076,
+   * `null` or `undefined`, https://bugs.jquery.com/ticket/10076,
    * https://github.com/jquery/jquery/commit/a839af034db2bd934e4d4fa6758a3fed8de74174
    *
    * @todo: Remove/Refactor in D8
@@ -133,8 +163,8 @@ Drupal.googleanalytics.isInternalSpecial = function (url) {
  * Extract the relative internal URL from an absolute internal URL.
  *
  * Examples:
- * - http://mydomain.com/node/1 -> /node/1
- * - http://example.com/foo/bar -> http://example.com/foo/bar
+ * - https://mydomain.com/node/1 -> /node/1
+ * - https://example.com/foo/bar -> https://example.com/foo/bar
  *
  * @param string url
  *   The web url to check.

+ 61 - 20
sites/all/modules/contrib/admin/google_analytics/googleanalytics.module

@@ -7,7 +7,7 @@
  * Adds the required Javascript to all your Drupal pages to allow tracking by
  * the Google Analytics statistics package.
  *
- * @author: Alexander Hass <http://drupal.org/user/85918>
+ * @author: Alexander Hass <https://drupal.org/user/85918>
  */
 
 /**
@@ -17,7 +17,7 @@ define('GOOGLEANALYTICS_TRACKFILES_EXTENSIONS', '7z|aac|arc|arj|asf|asx|avi|bin|
 
 /**
  * Define default path exclusion list to remove tracking from admin pages,
- * see http://drupal.org/node/34970 for more information.
+ * see https://drupal.org/node/34970 for more information.
  */
 define('GOOGLEANALYTICS_PAGES', "admin\nadmin/*\nbatch\nnode/add*\nnode/*/*\nuser/*/*");
 
@@ -36,7 +36,7 @@ function googleanalytics_api() {
 function googleanalytics_help($path, $arg) {
   switch ($path) {
     case 'admin/config/system/googleanalytics':
-      return t('<a href="@ga_url">Google Analytics</a> is a free (registration required) website traffic and marketing effectiveness service.', array('@ga_url' => 'http://www.google.com/analytics/'));
+      return t('<a href="@ga_url">Google Analytics</a> is a free (registration required) website traffic and marketing effectiveness service.', array('@ga_url' => 'https://marketingplatform.google.com/about/analytics/'));
   }
 }
 
@@ -69,6 +69,11 @@ function googleanalytics_permission() {
       'description' => t('Enter PHP code in the field for tracking visibility settings.'),
       'restrict access' => TRUE,
     ),
+    'add JS snippets for google analytics' => array(
+      'title' => t('Add JavaScript snippets'),
+      'description' => 'Enter JavaScript code snippets for advanced Google Analytics functionality.',
+      'restrict access' => TRUE,
+    ),
   );
 }
 
@@ -93,7 +98,7 @@ function googleanalytics_menu() {
  * Implements hook_page_alter() to insert JavaScript to the appropriate scope/region of the page.
  */
 function googleanalytics_page_alter(&$page) {
-  global $user;
+  global $base_path, $user;
 
   $id = variable_get('googleanalytics_account', '');
 
@@ -125,8 +130,11 @@ function googleanalytics_page_alter(&$page) {
       $link_settings['trackDownload'] = $track_download;
       $link_settings['trackDownloadExtensions'] = $trackfiles_extensions;
     }
+    if (module_exists('colorbox') && ($track_colorbox = variable_get('googleanalytics_trackcolorbox', 1))) {
+      $link_settings['trackColorbox'] = $track_colorbox;
+    }
     if ($track_domain_mode = variable_get('googleanalytics_domain_mode', 0)) {
-      $link_settings['trackDomainMode'] = $track_domain_mode;
+      $link_settings['trackDomainMode'] = (int) $track_domain_mode;
     }
     if ($track_cross_domains = variable_get('googleanalytics_cross_domains', '')) {
       $link_settings['trackCrossDomains'] = preg_split('/(\r\n?|\n)/', $track_cross_domains);
@@ -193,11 +201,23 @@ function googleanalytics_page_alter(&$page) {
 
     // Track access denied (403) and file not found (404) pages.
     if ($status == '403 Forbidden') {
-      // See http://www.google.com/support/analytics/bin/answer.py?answer=86927
-      $url_custom = '"/403.html?page=" + document.location.pathname + document.location.search + "&from=" + document.referrer';
+      // See https://www.google.com/support/analytics/bin/answer.py?answer=86927
+      $url_custom = '"' . $base_path . '403.html?page=" + document.location.pathname + document.location.search + "&from=" + document.referrer';
     }
     elseif ($status == '404 Not Found') {
-      $url_custom = '"/404.html?page=" + document.location.pathname + document.location.search + "&from=" + document.referrer';
+      $url_custom = '"' . $base_path . '404.html?page=" + document.location.pathname + document.location.search + "&from=" + document.referrer';
+    }
+
+    // #2693595: User has entered an invalid login and clicked on forgot
+    // password link. This link contains the username or email address and may
+    // get send to Google if we do not override it. Override only if 'name'
+    // query param exists. Last custom url condition, this need to win.
+    //
+    // URLs to protect are:
+    // - user/password?name=username
+    // - user/password?name=foo@example.com
+    if (arg(0) == 'user' && arg(1) == 'password' && array_key_exists('name', drupal_get_query_parameters())) {
+      $url_custom = '"' . $base_path . 'user/password"';
     }
 
     // Add custom dimensions and metrics.
@@ -248,11 +268,10 @@ function googleanalytics_page_alter(&$page) {
     $script .= '})(window,document,"script",';
 
     // Which version of the tracking library should be used?
-    $library_tracker_url = '//www.google-analytics.com/' . ($debug ? 'analytics_debug.js' : 'analytics.js');
-    $library_cache_url = 'http:' . $library_tracker_url;
+    $library_tracker_url = 'https://www.google-analytics.com/' . ($debug ? 'analytics_debug.js' : 'analytics.js');
 
     // Should a local cached copy of analytics.js be used?
-    if (variable_get('googleanalytics_cache', 0) && $url = _googleanalytics_cache($library_cache_url)) {
+    if (variable_get('googleanalytics_cache', 0) && $url = _googleanalytics_cache($library_tracker_url)) {
       // A dummy query-string is added to filenames, to gain control over
       // browser-caching. The string changes on every update or full cache
       // flush, forcing browsers to load a new copy of the files, as the
@@ -294,10 +313,7 @@ function googleanalytics_page_alter(&$page) {
 
     // Track logged in users across all devices.
     if (variable_get('googleanalytics_trackuserid', 0) && user_is_logged_in()) {
-      // The USER_ID value should be a unique, persistent, and non-personally
-      // identifiable string identifier that represents a user or signed-in
-      // account across devices.
-      $create_only_fields['userId'] = drupal_hmac_base64($user->uid, drupal_get_private_key() . drupal_get_hash_salt());
+      $create_only_fields['userId'] = google_analytics_user_id_hash($user->uid);
     }
 
     // Create a tracker.
@@ -352,13 +368,30 @@ function googleanalytics_page_alter(&$page) {
       // Custom tracking. Prepend before all other JavaScript.
       // @TODO: https://support.google.com/adsense/answer/98142
       // sounds like it could be appended to $script.
-      drupal_add_js($googleanalytics_adsense_script, array('type' => 'inline', 'group' => JS_LIBRARY-1));
+      drupal_add_js($googleanalytics_adsense_script, array('type' => 'inline', 'group' => JS_LIBRARY-1, 'requires_jquery' => FALSE));
     }
 
-    drupal_add_js($script, array('scope' => 'header', 'type' => 'inline'));
+    drupal_add_js($script, array('scope' => 'header', 'type' => 'inline', 'requires_jquery' => FALSE));
   }
 }
 
+/**
+ * Generate user id hash to implement USER_ID.
+ *
+ * The USER_ID value should be a unique, persistent, and non-personally
+ * identifiable string identifier that represents a user or signed-in
+ * account across devices.
+ *
+ * @param int $uid
+ *   User id.
+ *
+ * @return string
+ *   User id hash.
+ */
+function google_analytics_user_id_hash($uid) {
+  return drupal_hmac_base64($uid, drupal_get_private_key() . drupal_get_hash_salt());
+}
+
 /**
  * Implements hook_field_extra_fields().
  */
@@ -440,7 +473,7 @@ function googleanalytics_user_presave(&$edit, $account, $category) {
 function googleanalytics_cron() {
   // Regenerate the tracking code file every day.
   if (REQUEST_TIME - variable_get('googleanalytics_last_cache', 0) >= 86400 && variable_get('googleanalytics_cache', 0)) {
-    _googleanalytics_cache('http://www.google-analytics.com/analytics.js', TRUE);
+    _googleanalytics_cache('https://www.google-analytics.com/analytics.js', TRUE);
     variable_set('googleanalytics_last_cache', REQUEST_TIME);
   }
 }
@@ -456,14 +489,14 @@ function googleanalytics_preprocess_search_results(&$variables) {
     // found. But the pager item mumber can tell the number of search results.
     global $pager_total_items;
 
-    drupal_add_js('window.googleanalytics_search_results = ' . intval($pager_total_items[0]) . ';', array('type' => 'inline', 'group' => JS_LIBRARY-1));
+    drupal_add_js('window.googleanalytics_search_results = ' . intval($pager_total_items[0]) . ';', array('type' => 'inline', 'group' => JS_LIBRARY-1, 'requires_jquery' => FALSE));
   }
 }
 
 /**
  * Helper function for grabbing search keys. Function is missing in D7.
  *
- * http://api.drupal.org/api/function/search_get_keys/6
+ * https://api.drupal.org/api/function/search_get_keys/6
  */
 function googleanalytics_search_get_keys() {
   static $return;
@@ -504,6 +537,10 @@ function _googleanalytics_cache($location, $synchronize = FALSE) {
         if ($data_hash_local != $data_hash_remote && file_prepare_directory($path)) {
           // Save updated tracking code file to disk.
           file_unmanaged_save_data($result->data, $file_destination, FILE_EXISTS_REPLACE);
+          // Based on Drupal Core drupal_build_css_cache().
+          if (variable_get('css_gzip_compression', TRUE) && variable_get('clean_url', 0) && extension_loaded('zlib')) {
+            file_unmanaged_save_data(gzencode($result->data, 9, FORCE_GZIP), $file_destination . '.gz', FILE_EXISTS_REPLACE);
+          }
           watchdog('googleanalytics', 'Locally cached tracking code file has been updated.', array(), WATCHDOG_INFO);
 
           // Change query-strings on css/js files to enforce reload for all users.
@@ -516,6 +553,10 @@ function _googleanalytics_cache($location, $synchronize = FALSE) {
           // There is no need to flush JS here as core refreshes JS caches
           // automatically, if new files are added.
           file_unmanaged_save_data($result->data, $file_destination, FILE_EXISTS_REPLACE);
+          // Based on Drupal Core drupal_build_css_cache().
+          if (variable_get('css_gzip_compression', TRUE) && variable_get('clean_url', 0) && extension_loaded('zlib')) {
+            file_unmanaged_save_data(gzencode($result->data, 9, FORCE_GZIP), $file_destination . '.gz', FILE_EXISTS_REPLACE);
+          }
           watchdog('googleanalytics', 'Locally cached tracking code file has been saved.', array(), WATCHDOG_INFO);
 
           // Return the local JS file path.

+ 117 - 19
sites/all/modules/contrib/admin/google_analytics/googleanalytics.test

@@ -6,6 +6,13 @@
  */
 class GoogleAnalyticsBasicTest extends DrupalWebTestCase {
 
+  /**
+   * User without permissions to edit snippets.
+   *
+   * @var \StdClass
+   */
+  protected $noSnippetUser;
+
   public static function getInfo() {
     return array(
       'name' => 'Google Analytics basic tests',
@@ -25,6 +32,8 @@ class GoogleAnalyticsBasicTest extends DrupalWebTestCase {
     );
 
     // User to set up google_analytics.
+    $this->noSnippetUser = $this->drupalCreateUser($permissions);
+    $permissions[] = 'add JS snippets for google analytics';
     $this->admin_user = $this->drupalCreateUser($permissions);
     $this->drupalLogin($this->admin_user);
   }
@@ -48,13 +57,33 @@ class GoogleAnalyticsBasicTest extends DrupalWebTestCase {
     $edit['googleanalytics_account'] = $this->randomName(2);
     $this->drupalPost('admin/config/system/googleanalytics', $edit, t('Save configuration'));
     $this->assertRaw(t('A valid Google Analytics Web Property ID is case sensitive and formatted like UA-xxxxxxx-yy.'), '[testGoogleAnalyticsConfiguration]: Invalid Web Property ID number validated.');
+
+    // User should have access to code snippets.
+    $this->assertFieldByName('googleanalytics_codesnippet_create');
+    $this->assertFieldByName('googleanalytics_codesnippet_before');
+    $this->assertFieldByName('googleanalytics_codesnippet_after');
+    $this->assertNoFieldByXPath("//textarea[@name='googleanalytics_codesnippet_create' and @disabled='disabled']", NULL, '"Create only fields" is enabled.');
+    $this->assertNoFieldByXPath("//textarea[@name='googleanalytics_codesnippet_before' and @disabled='disabled']", NULL, '"Code snippet (before)" is enabled.');
+    $this->assertNoFieldByXPath("//textarea[@name='googleanalytics_codesnippet_after' and @disabled='disabled']", NULL, '"Code snippet (after)" is enabled.');
+
+    // Login as user without JS permissions.
+    $this->drupalLogin($this->noSnippetUser);
+    $this->drupalGet('admin/config/system/googleanalytics');
+
+    // User should *not* have access to snippets, but create fields.
+    $this->assertFieldByName('googleanalytics_codesnippet_create');
+    $this->assertFieldByName('googleanalytics_codesnippet_before');
+    $this->assertFieldByName('googleanalytics_codesnippet_after');
+    $this->assertNoFieldByXPath("//textarea[@name='googleanalytics_codesnippet_create' and @disabled='disabled']", NULL, '"Create only fields" is enabled.');
+    $this->assertFieldByXPath("//textarea[@name='googleanalytics_codesnippet_before' and @disabled='disabled']", NULL, '"Code snippet (before)" is disabled.');
+    $this->assertFieldByXPath("//textarea[@name='googleanalytics_codesnippet_after' and @disabled='disabled']", NULL, '"Code snippet (after)" is disabled.');
   }
 
   function testGoogleAnalyticsPageVisibility() {
     // Verify that no tracking code is embedded into the webpage; if there is
     // only the module installed, but UA code not configured. See #2246991.
     $this->drupalGet('');
-    $this->assertNoRaw('//www.google-analytics.com/analytics.js', '[testGoogleAnalyticsPageVisibility]: Tracking code is not displayed without UA code configured.');
+    $this->assertNoRaw('https://www.google-analytics.com/analytics.js', '[testGoogleAnalyticsPageVisibility]: Tracking code is not displayed without UA code configured.');
 
     $ua_code = 'UA-123456-1';
     variable_set('googleanalytics_account', $ua_code);
@@ -75,7 +104,7 @@ class GoogleAnalyticsBasicTest extends DrupalWebTestCase {
     $this->assertNoRaw($ua_code, '[testGoogleAnalyticsPageVisibility]: Tracking code is not displayed on admin page.');
     $this->drupalGet('admin/config/system/googleanalytics');
     // Checking for tracking code URI here, as $ua_code is displayed in the form.
-    $this->assertNoRaw('//www.google-analytics.com/analytics.js', '[testGoogleAnalyticsPageVisibility]: Tracking code is not displayed on admin subpage.');
+    $this->assertNoRaw('https://www.google-analytics.com/analytics.js', '[testGoogleAnalyticsPageVisibility]: Tracking code is not displayed on admin subpage.');
 
     // Test whether tracking code display is properly flipped.
     variable_set('googleanalytics_visibility_pages', 1);
@@ -83,7 +112,7 @@ class GoogleAnalyticsBasicTest extends DrupalWebTestCase {
     $this->assertRaw($ua_code, '[testGoogleAnalyticsPageVisibility]: Tracking code is displayed on admin page.');
     $this->drupalGet('admin/config/system/googleanalytics');
     // Checking for tracking code URI here, as $ua_code is displayed in the form.
-    $this->assertRaw('//www.google-analytics.com/analytics.js', '[testGoogleAnalyticsPageVisibility]: Tracking code is displayed on admin subpage.');
+    $this->assertRaw('https://www.google-analytics.com/analytics.js', '[testGoogleAnalyticsPageVisibility]: Tracking code is displayed on admin subpage.');
     $this->drupalGet('');
     $this->assertNoRaw($ua_code, '[testGoogleAnalyticsPageVisibility]: Tracking code is NOT displayed on front page.');
 
@@ -97,13 +126,15 @@ class GoogleAnalyticsBasicTest extends DrupalWebTestCase {
     // Enable tracking code for all user roles.
     variable_set('googleanalytics_roles', array());
 
+    $base_path = base_path();
+
     // Test whether 403 forbidden tracking code is shown if user has no access.
     $this->drupalGet('admin');
-    $this->assertRaw('/403.html', '[testGoogleAnalyticsPageVisibility]: 403 Forbidden tracking code shown if user has no access.');
+    $this->assertRaw($base_path . '403.html', '[testGoogleAnalyticsPageVisibility]: 403 Forbidden tracking code shown if user has no access.');
 
     // Test whether 404 not found tracking code is shown on non-existent pages.
     $this->drupalGet($this->randomName(64));
-    $this->assertRaw('/404.html', '[testGoogleAnalyticsPageVisibility]: 404 Not Found tracking code shown on non-existent page.');
+    $this->assertRaw($base_path . '404.html', '[testGoogleAnalyticsPageVisibility]: 404 Not Found tracking code shown on non-existent page.');
 
     // DNT Tests:
     // Enable system internal page cache for anonymous users.
@@ -139,7 +170,7 @@ class GoogleAnalyticsBasicTest extends DrupalWebTestCase {
     (function(q,u,i,c,k){window['GoogleAnalyticsObject']=q;
     window[q]=window[q]||function(){(window[q].q=window[q].q||[]).push(arguments)},
     window[q].l=1*new Date();c=i.createElement(u),k=i.getElementsByTagName(u)[0];
-    c.async=true;c.src='//www.google-analytics.com/analytics.js';
+    c.async=true;c.src='https://www.google-analytics.com/analytics.js';
     k.parentNode.insertBefore(c,k)})('ga','script',document);
     ga('create', 'UA-123456-7');
     ga('send', 'pageview');
@@ -150,7 +181,7 @@ class GoogleAnalyticsBasicTest extends DrupalWebTestCase {
     // Test whether tracking code uses latest JS.
     variable_set('googleanalytics_cache', 0);
     $this->drupalGet('');
-    $this->assertRaw('//www.google-analytics.com/analytics.js', '[testGoogleAnalyticsTrackingCode]: Latest tracking code used.');
+    $this->assertRaw('https://www.google-analytics.com/analytics.js', '[testGoogleAnalyticsTrackingCode]: Latest tracking code used.');
 
     // Test whether anonymize visitors IP address feature has been enabled.
     variable_set('googleanalytics_tracker_anonymizeip', 0);
@@ -227,13 +258,14 @@ class GoogleAnalyticsBasicTest extends DrupalWebTestCase {
     $this->assertRaw('ga("create", "' . $ua_code . '", {"cookieDomain":"auto","allowLinker":true', '[testGoogleAnalyticsTrackingCode]: "allowLinker" has been found. Cross domain tracking is active.');
     $this->assertRaw('ga("require", "linker");', '[testGoogleAnalyticsTrackingCode]: Require linker has been found. Cross domain tracking is active.');
     $this->assertRaw('ga("linker:autoLink", ["www.example.com","www.example.net"]);', '[testGoogleAnalyticsTrackingCode]: "linker:autoLink" has been found. Cross domain tracking is active.');
+    $this->assertRaw('"trackDomainMode":2,', '[testGoogleAnalyticsTrackingCode]: Domain mode value is of type integer.');
     $this->assertRaw('"trackCrossDomains":["www.example.com","www.example.net"]', '[testGoogleAnalyticsTrackingCode]: Cross domain tracking with www.example.com and www.example.net is active.');
     variable_set('googleanalytics_domain_mode', 0);
 
     // Test whether debugging script has been enabled.
     variable_set('googleanalytics_debug', 1);
     $this->drupalGet('');
-    $this->assertRaw('//www.google-analytics.com/analytics_debug.js', '[testGoogleAnalyticsTrackingCode]: Google debugging script has been enabled.');
+    $this->assertRaw('https://www.google-analytics.com/analytics_debug.js', '[testGoogleAnalyticsTrackingCode]: Google debugging script has been enabled.');
 
     // Check if text and link is shown on 'Status Reports' page.
     // Requires 'administer site configuration' permission.
@@ -243,7 +275,7 @@ class GoogleAnalyticsBasicTest extends DrupalWebTestCase {
     // Test whether debugging script has been disabled.
     variable_set('googleanalytics_debug', 0);
     $this->drupalGet('');
-    $this->assertRaw('//www.google-analytics.com/analytics.js', '[testGoogleAnalyticsTrackingCode]: Google debugging script has been disabled.');
+    $this->assertRaw('https://www.google-analytics.com/analytics.js', '[testGoogleAnalyticsTrackingCode]: Google debugging script has been disabled.');
 
     // Test whether the CREATE and BEFORE and AFTER code is added to the tracker.
     $codesnippet_create = array(
@@ -284,6 +316,7 @@ class GoogleAnalyticsCustomDimensionsAndMetricsTest extends DrupalWebTestCase {
 
     // User to set up google_analytics.
     $this->admin_user = $this->drupalCreateUser($permissions);
+    $this->drupalLogin($this->admin_user);
   }
 
   function testGoogleAnalyticsCustomDimensions() {
@@ -362,34 +395,30 @@ class GoogleAnalyticsCustomDimensionsAndMetricsTest extends DrupalWebTestCase {
       1 => array(
         'index' => 1,
         'value' => '6',
-        'value_expected' => 6,
       ),
       2 => array(
         'index' => 2,
         'value' => '8000',
-        'value_expected' => 8000,
       ),
       3 => array(
         'index' => 3,
         'value' => '7.8654',
-        'value_expected' => 7.8654,
       ),
       4 => array(
         'index' => 4,
         'value' => '1123.4',
-        'value_expected' => 1123.4,
       ),
       5 => array(
         'index' => 5,
         'value' => '5,67',
-        'value_expected' => 5,
       ),
     );
+
     variable_set('googleanalytics_custom_metric', $googleanalytics_custom_metric);
     $this->drupalGet('');
 
     foreach ($googleanalytics_custom_metric as $metric) {
-      $this->assertRaw('ga("set", ' . drupal_json_encode('metric' . $metric['index']) . ', ' . drupal_json_encode($metric['value_expected']) . ');', '[testGoogleAnalyticsCustomDimensionsAndMetrics]: Metric #' . $metric['index'] . ' is shown.');
+      $this->assertRaw('ga("set", ' . drupal_json_encode('metric' . $metric['index']) . ', ' . drupal_json_encode((float) $metric['value']) . ');', '[testGoogleAnalyticsCustomDimensionsAndMetrics]: Metric #' . $metric['index'] . ' is shown.');
     }
 
     // Test whether tokens are replaced in custom metric values.
@@ -421,6 +450,75 @@ class GoogleAnalyticsCustomDimensionsAndMetricsTest extends DrupalWebTestCase {
     $this->assertNoRaw('ga("set", ' . drupal_json_encode('metric3') . ', ' . drupal_json_encode('') . ');', '[testGoogleAnalyticsCustomDimensionsAndMetrics]: Empty value is not shown.');
     $this->assertRaw('ga("set", ' . drupal_json_encode('metric4') . ', ' . drupal_json_encode(0) . ');', '[testGoogleAnalyticsCustomDimensionsAndMetrics]: Value 0 is shown.');
   }
+
+  /**
+   * Tests if Custom Dimensions token form validation works.
+   */
+  public function testGoogleAnalyticsCustomDimensionsTokenFormValidation() {
+    $ua_code = 'UA-123456-1';
+
+    // Check form validation.
+    $edit['googleanalytics_account'] = $ua_code;
+    $edit['googleanalytics_custom_dimension[indexes][1][value]'] = '[current-user:name]';
+    $edit['googleanalytics_custom_dimension[indexes][2][value]'] = '[current-user:edit-url]';
+    $edit['googleanalytics_custom_dimension[indexes][3][value]'] = '[user:name]';
+    $edit['googleanalytics_custom_dimension[indexes][4][value]'] = '[term:name]';
+    $edit['googleanalytics_custom_dimension[indexes][5][value]'] = '[term:tid]';
+
+    $this->drupalPost('admin/config/system/googleanalytics', $edit, t('Save configuration'));
+
+    $this->assertRaw(t('The %element-title is using the following forbidden tokens with personal identifying information: @invalid-tokens.', array('%element-title' => t('Custom dimension value #@index', array('@index' => 1)), '@invalid-tokens' => implode(', ', array('[current-user:name]')))));
+    $this->assertRaw(t('The %element-title is using the following forbidden tokens with personal identifying information: @invalid-tokens.', array('%element-title' => t('Custom dimension value #@index', array('@index' => 2)), '@invalid-tokens' => implode(', ', array('[current-user:edit-url]')))));
+    $this->assertRaw(t('The %element-title is using the following forbidden tokens with personal identifying information: @invalid-tokens.', array('%element-title' => t('Custom dimension value #@index', array('@index' => 3)), '@invalid-tokens' => implode(', ', array('[user:name]')))));
+    // BUG #2037595
+    //$this->assertNoRaw(t('The %element-title is using the following forbidden tokens with personal identifying information: @invalid-tokens.', array('%element-title' => t('Custom dimension value #@index', array('@index' => 4)), '@invalid-tokens' => implode(', ', array('[term:name]')))));
+    //$this->assertNoRaw(t('The %element-title is using the following forbidden tokens with personal identifying information: @invalid-tokens.', array('%element-title' => t('Custom dimension value #@index', array('@index' => 5)), '@invalid-tokens' => implode(', ', array('[term:tid]')))));
+  }
+}
+
+/**
+ * Test custom url functionality of Google Analytics module.
+ */
+class GoogleAnalyticsCustomUrls extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Google Analytics custom url tests',
+      'description' => 'Test custom url functionality of Google Analytics module.',
+      'group' => 'Google Analytics',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('googleanalytics');
+
+    $permissions = array(
+      'access administration pages',
+      'administer google analytics',
+    );
+
+    // User to set up google_analytics.
+    $this->admin_user = $this->drupalCreateUser($permissions);
+  }
+
+  /**
+   * Tests if user password page urls are overridden.
+   */
+  public function testGoogleAnalyticsUserPasswordPage() {
+    $base_path = base_path();
+    $ua_code = 'UA-123456-4';
+    variable_set('googleanalytics_account', $ua_code);
+
+    $this->drupalGet('user/password', array('query' => array('name' => 'foo')));
+    $this->assertRaw('ga("set", "page", "' . $base_path . 'user/password"');
+
+    $this->drupalGet('user/password', array('query' => array('name' => 'foo@example.com')));
+    $this->assertRaw('ga("set", "page", "' . $base_path . 'user/password"');
+
+    $this->drupalGet('user/password');
+    $this->assertNoRaw('ga("set", "page",', '[testGoogleAnalyticsCustomUrls]: Custom url not set.');
+  }
+
 }
 
 class GoogleAnalyticsStatusMessagesTest extends DrupalWebTestCase {
@@ -460,7 +558,7 @@ class GoogleAnalyticsStatusMessagesTest extends DrupalWebTestCase {
     //drupal_set_message('Example status message.', 'status');
     //drupal_set_message('Example warning message.', 'warning');
     //drupal_set_message('Example error message.', 'error');
-    //drupal_set_message('Example error <em>message</em> with html tags and <a href="http://example.com/">link</a>.', 'error');
+    //drupal_set_message('Example error <em>message</em> with html tags and <a href="https://example.com/">link</a>.', 'error');
     //$this->drupalGet('');
     //$this->assertNoRaw('ga("send", "event", "Messages", "Status message", "Example status message.");', '[testGoogleAnalyticsStatusMessages]: Example status message is not enabled for tracking.');
     //$this->assertNoRaw('ga("send", "event", "Messages", "Warning message", "Example warning message.");', '[testGoogleAnalyticsStatusMessages]: Example warning message is not enabled for tracking.');
@@ -684,13 +782,13 @@ class GoogleAnalyticsPhpFilterTest extends DrupalWebTestCase {
     // Check tracking code visibility.
     variable_set('googleanalytics_pages', '<?php return TRUE; ?>');
     $this->drupalGet('');
-    $this->assertRaw('//www.google-analytics.com/analytics.js', '[testGoogleAnalyticsPhpFilter]: Tracking is displayed on frontpage page.');
+    $this->assertRaw('https://www.google-analytics.com/analytics.js', '[testGoogleAnalyticsPhpFilter]: Tracking is displayed on frontpage page.');
     $this->drupalGet('admin');
-    $this->assertRaw('//www.google-analytics.com/analytics.js', '[testGoogleAnalyticsPhpFilter]: Tracking is displayed on admin page.');
+    $this->assertRaw('https://www.google-analytics.com/analytics.js', '[testGoogleAnalyticsPhpFilter]: Tracking is displayed on admin page.');
 
     variable_set('googleanalytics_pages', '<?php return FALSE; ?>');
     $this->drupalGet('');
-    $this->assertNoRaw('//www.google-analytics.com/analytics.js', '[testGoogleAnalyticsPhpFilter]: Tracking is not displayed on frontpage page.');
+    $this->assertNoRaw('https://www.google-analytics.com/analytics.js', '[testGoogleAnalyticsPhpFilter]: Tracking is not displayed on frontpage page.');
 
     // Test administration form.
     variable_set('googleanalytics_pages', '<?php return TRUE; ?>');

+ 8 - 6
sites/all/modules/contrib/admin/google_analytics/googleanalytics.test.js

@@ -77,7 +77,7 @@ $(document).ready(function() {
   Drupal.googleanalytics.test.assertTrue(Drupal.googleanalytics.isInternal(base_url + Drupal.settings.basePath + 'node/1?foo=bar'), "Link '" + base_url + Drupal.settings.basePath + "node/1?foo=bar' has been detected as internal link.");
   Drupal.googleanalytics.test.assertTrue(Drupal.googleanalytics.isInternal(base_url + Drupal.settings.basePath + 'node/1?foo=bar#foo'), "Link '" + base_url + Drupal.settings.basePath + "node/1?foo=bar#foo' has been detected as internal link.");
   Drupal.googleanalytics.test.assertTrue(Drupal.googleanalytics.isInternal(base_url + Drupal.settings.basePath + 'go/foo'), "Link '" + base_url + Drupal.settings.basePath + "go/foo' has been detected as internal link.");
-  Drupal.googleanalytics.test.assertFalse(Drupal.googleanalytics.isInternal('http://example.com/node/3'), "Link 'http://example.com/node/3' has been detected as external link.");
+  Drupal.googleanalytics.test.assertFalse(Drupal.googleanalytics.isInternal('https://example.com/node/3'), "Link 'https://example.com/node/3' has been detected as external link.");
   console.groupEnd();
 
   console.group("Test 'isInternalSpecial':");
@@ -86,9 +86,11 @@ $(document).ready(function() {
   console.groupEnd();
 
   console.group("Test 'getPageUrl':");
-  Drupal.googleanalytics.test.assertSame(base_path, Drupal.googleanalytics.getPageUrl(base_url + Drupal.settings.basePath + 'node/1'), "Absolute internal URL '" +  Drupal.settings.basePath + "node/1' has been extracted from full qualified url '" + base_url + base_path + "'.");
-  Drupal.googleanalytics.test.assertSame(base_path, Drupal.googleanalytics.getPageUrl(Drupal.settings.basePath + 'node/1'), "Absolute internal URL '" +  Drupal.settings.basePath + "node/1' has been extracted from absolute url '" +  base_path + "'.");
-  Drupal.googleanalytics.test.assertSame('http://example.com/node/2', Drupal.googleanalytics.getPageUrl('http://example.com/node/2'), "Full qualified external url 'http://example.com/node/2' has been extracted.");
+  Drupal.google_analytics.test.assertSame(base_path, Drupal.google_analytics.getPageUrl(window.location.href), "Absolute internal URL '" + base_path + "' has been extracted from full qualified url '" + window.location.href + "'.");
+  Drupal.google_analytics.test.assertSame(base_path, Drupal.google_analytics.getPageUrl(base_path), "Absolute internal URL '" + base_path + "' has been extracted from absolute url '" + base_path + "'.");
+  //Drupal.googleanalytics.test.assertSame(base_path, Drupal.googleanalytics.getPageUrl(base_url + Drupal.settings.basePath + 'node/1'), "Absolute internal URL '" +  Drupal.settings.basePath + "node/1' has been extracted from full qualified url '" + base_url + base_path + "'.");
+  //Drupal.googleanalytics.test.assertSame(base_path, Drupal.googleanalytics.getPageUrl(Drupal.settings.basePath + 'node/1'), "Absolute internal URL '" +  Drupal.settings.basePath + "node/1' has been extracted from absolute url '" +  base_path + "'.");
+  Drupal.googleanalytics.test.assertSame('https://example.com/node/2', Drupal.googleanalytics.getPageUrl('https://example.com/node/2'), "Full qualified external url 'https://example.com/node/2' has been extracted.");
   Drupal.googleanalytics.test.assertSame('//example.com/node/2', Drupal.googleanalytics.getPageUrl('//example.com/node/2'), "Full qualified external url '//example.com/node/2' has been extracted.");
   console.groupEnd();
 
@@ -105,9 +107,9 @@ $(document).ready(function() {
   if (Drupal.settings.googleanalytics.trackCrossDomains) {
     console.dir(Drupal.settings.googleanalytics.trackCrossDomains);
     Drupal.googleanalytics.test.assertTrue(Drupal.googleanalytics.isCrossDomain('example.com', Drupal.settings.googleanalytics.trackCrossDomains), "URL 'example.com' has been found in cross domain list.");
-    Drupal.googleanalytics.test.assertTrue(Drupal.googleanalytics.isCrossDomain('example.net', Drupal.settings.googleanalytics.trackCrossDomains), "URL 'example.com' has been found in cross domain list.");
+    Drupal.googleanalytics.test.assertTrue(Drupal.googleanalytics.isCrossDomain('example.net', Drupal.settings.googleanalytics.trackCrossDomains), "URL 'example.net' has been found in cross domain list.");
     Drupal.googleanalytics.test.assertFalse(Drupal.googleanalytics.isCrossDomain('www.example.com', Drupal.settings.googleanalytics.trackCrossDomains), "URL 'www.example.com' not found in cross domain list.");
-    Drupal.googleanalytics.test.assertFalse(Drupal.googleanalytics.isCrossDomain('www.example.net', Drupal.settings.googleanalytics.trackCrossDomains), "URL 'www.example.com' not found in cross domain list.");
+    Drupal.googleanalytics.test.assertFalse(Drupal.googleanalytics.isCrossDomain('www.example.net', Drupal.settings.googleanalytics.trackCrossDomains), "URL 'www.example.net' not found in cross domain list.");
   }
   else {
     console.warn('Cross domain tracking is not enabled. Tests skipped.');

File diff suppressed because it is too large
+ 0 - 0
sites/all/modules/contrib/admin/google_analytics/googleanalytics.variable.inc


+ 184 - 41
sites/all/modules/contrib/admin/module_filter/CHANGELOG.txt

@@ -1,50 +1,193 @@
-Module Filter 7.x-1.8, 2013-08-08
+Module Filter 7.x-2.1, 2017-06-09
 ---------------------------------
-by greenSkin: Changed the title for the 'module_filter_dynamic_save_position'
-  checkbox to make it clearer what it does.
-#1933384 by kaidjohnson: Fixed jQuery UI button breaks functionality.
-#1166414 by greenSkin: Fixed module filter + tabs disabled = broken modules
-  page.
+Issue #2437439 by mikhail.krainiuk, greenSkin, jayhawkfan75: Module Filter does
+  not care about anchors in permission links.
+Issue #2866236 by Munavijayalakshmi, dhruveshdtripathi: Typo error in
+  README.TXT file.
+Issue #2452067 by Madis: Option to show description expanded as default
+  not working.
+Issue #2580791 by makbul_khan8: Coding standards and few function without
+  help comments.
+Issue #2153697 by annya: Disabling option "Number of enabled modules" breaks
+  tabs functionality.
+Issue #1710230 by willvincent: On | Off buttons does not change state with
+  jquery_update module active.
+Added option to show description as expanded by default.
+Improved description field so when it is open, interacting with it's contents
+  does not make it collapse.
+Placed collapsed/expanded images inside of module for easier, more reliable
+  access.
+Added option to place version in own column.
+Issue #2113191 by joelpittet: Category tabs not working.
 
 
-Module Filter 7.x-1.7, 2012-07-05
+Module Filter 7.x-2.0, 2015-02-22
 ---------------------------------
+Simplifying the table rows by hiding version and requirements until a
+  particular description is clicked.
+#2235553 by greenSkin: Fixed latest jquery_update breaks module filter.
+#2304687 by mpdonadio: Fixed Remove hardcoded operations.
+#2293029 by topsitemakers: Fixed Take header offset into account when selecting
+  a tab.
+#2290213 by topsitemakers: Minor typo in description - "has no affect" -> "has
+  no effect".
+#2141743, #2141743 by greenSkin: Fixed issues related to the new dynamically
+  positioned tabs and using the dynamically positioned save button.
+by greenSkin: Tabs now should always be visible while scrolling large lists of
+  modules.
+#1854348 by alexweber, greenSkin: Make filter textfield wider on modules page
+  when viewing as tabs.
+by greenSkin: Fixed what was suppose to be a call to variable_set().
+#1370492 by greenSkin: Remember selected tab after modules form submit.
+by greenSkin: Changed the title for the 'module_filter_dynamic_save_position'
+  checkbox to make it clearer what it does.
+#1166414 by greenSkin: Fixed broken submit when tabs are disabled. We no longer
+  reroute the theme of the system modules page unless tabs are enabled.
+by greenSkin: Fixed issue on Available updates page where the update state
+  would not be remembered.
+by greenSkin: Fixed 7200 update to rebuild the theme registry at the same time
+  as rebuilding the menu.
+by greenSkin: Fixed issue relating to row coloring when enabling/disabling
+  modules via switch due to recent fix for jQuery Update module.
+by greenSkin: Added functionality to show recently enabled/disabled modules.
+#1710230 by littlekoala, greenSkin: Fixed On | Off buttons does not change
+  state with jquery_update() module active.
+by greenSkin: Added description to filter textfield on permissions page.
+by greenSkin: Added filter to user permissions page.
+by greenSkin: Fixed styling when JavaScript is disabled.
+by greenSkin: Fixed compatiblility with page_actions.
+#1351184 klonos, greenSkin: Added support for update_advanced "Ignored from
+  settings".
+#1149978 by good_man, greenSkin: Added RTL Styling for tabs and toggle switch.
+by greenSkin: Hide toggle switch when JavaScript is disabled.
+by greenSkin: Added support for ctools dropbutton as well as views styling for
+  ctools dropbutton.
+by greenSkin: Improved hash validation.
+by greenSkin: Added ability for tabs to be disabled. Direct use case is when
+  the "New" tab contains zero new modules.
+by greenSkin: Added title to "New" tab link that helps to describe the criteria
+  of a "new" module.
+#1320796 by greenSkin: Added some validation checks before trying to select a
+  tab in case the tab does not actually exist.
+#1429248 by klonos, greenSkin: Fixed Modules page table header overlaps
+  admin_menu().
+#1494694 by greenSkin: Added Let me decide if I want the cursor to focus on the
+  search box or not.
+#1515256 by catmat, greenSkin: Fixed Tabbed theme may remove functional
+  content.
+by greenSkin: Integrated dynamic positioning of the save button back in, but
+  if the module page_actions is enabled we let it handle the save button. See
+  issue #1424994.
+by greenSkin: Clicking on module name now affects the toggle switch.
+by greenSkin: Do not render the switch for incompatible modules.
 #1033012 by greenSkin: Hide incompatible module rows when the 'Unavailable'
   checkbox is unchecked.
-#1170388 by greenSkin: Fixed conflict with Overlay module. Added class
-  "overlay-exclude" to tab links.
+by greenSkin: Performance tweak to counting the number of enabled modules.
+by greenSkin: Base new modules on the filectime of their .info file.
+#1424034 by greenSkin: Now adds the jquery.cookie.js file when needed.
+by greenSkin: Removed tweaks to the save configuration button in favor
+  http://drupal.org/project/page_actions.
+by greenSkin: Updated hook_uninstall to del all Module Filter variables.
+by greenSkin: No longer including machine name in the tab summary of modules to
+  enable/disable.
+by greenSkin: Made switches honor disabled checkboxes. A disabled switch can
+  not be turned on or off.
+by greenSkin: Added setting to toggle between using the switch or checkbox for
+  enabling/disabling modules.
+by greenSkin: Centered enable switch.
+by greenSkin: Moved couple styles to be applied via JavaScript instead of CSS
+  so they get applied once the initial loading has finished.
+by greenSkin: Implemented a "switch" look instead of checkboxes.
+by greenSkin: Remember last selected state on "Available updates" page.
+by greenSkin: Switched to using filectime() rather than filemtime() for
+  determining new modules.
+#1354134 by klonos, greenSkin: Module list now formats correctly in core
+  "Garland" theme.
+#1354134 by klonos, greenSkin: Module list now formats correctly in core
+  "Garland" theme.
 #1124218 by jyee, greenSkin: Suppress form submission when hitting the enter
   key while the filter input is focused.
-
-
-Module Filter 7.x-1.6, 2011-09-15
----------------------------------
-#1241662 by Niklas Fiekas: Sort modules by display name.
+by greenSkin: Simplified filter rules for updates page. Instead of checkboxes,
+  now using radios.
+by greenSkin: Fixed "All" tab to be selected by default when the page is first
+  loaded.
+#1350124 by greenSkin: Fixed filtering on package tab.
+by greenSkin: Added the "All" tab back. Added a "New" tab that lists modules
+  installed within the last week.
+by greenSkin: Fixed "No Results" not showing within selected tabs.
+#1259876 by greenSkin: Filter criteria remembered after save.
+by greenSkin: Remove deprecated function moduleGetID() from JavaScript code.
+by greenSkin: Filter now uses OR instead of AND when filtering multiply
+  queries.
+#1288590 by greenSkin: Fixed Drupal.settings.moduleFilter.enabledCounts[id] is
+  undefined.
+#1344214 by eMPee584: Fixed Notice: Undefined index: #default_value in
+  theme_module_filter_system_modules_tabs().
+#1170388 by greenSkin: Fixed confict with Overlay module. Added class
+  "overlay-exclude" to tab links. Added setting to toggle the use of a URL
+  fragment when selecting tabs.
+by greenSkin: Added README.txt.
+by greenSkin: Fixed table row striping.
+by greenSkin: Fixed regular expression to not require an operator be at the
+  beginning. Make filter queries filter with AND instead of OR. Each query will
+  further filter the list rather than potentially add to it.
+by greenSkin: Spruced up the regular expression for splitting query strings.
+by greenSkin: Added "description:" operator.
+by greenSkin: Added operator support to filter. Added "requires:" and
+  "requiredBy:" operators.
+by greenSkin: Filter now processes multiple queries separated by spaces. Use
+  quotes for a single query that includes space(s).
+by greenSkin: Fixed not updating the index when a module is enabled/disabled.
+by greenSkin: Fixed visual aid for enabling/disabling modules. Previously had
+  failed to remove +/- from tab summary.
+by Kiphaas7, greenSkin: Tabs can now be configured to hide when they contain
+  no results.
+by Kiphaas7, greenSkin: Added result info to tabs. When a filter is performed,
+  a count per tab is displayed of the number of visible results for that tab.
+by greenSkin: Now more descriptive of what modules are being enabled/disabled.
+by greenSkin: Moved module operation links to below "Requires" and
+  "Required by" section.
+by greenSkin: Added a suggest class to tabs when their module is hovered.
+by greenSkin: Distinguished difference between tab ID and hash.
+by greenSkin: Only alter the menu item 'admin/reports/updates' if it first
+  exists.
+by greenSkin: Added update to force a menu rebuild. This is needed to let
+  Module Filter alter the Update Status menu item in order to provide our
+  filter on its page.
 #1254140 by greenSkin: No longer return anything in hook_update_7100.
-by greenSkin: Fixed bug with visual aids sometimes not updating correctly.
-
-
-Module Filter 7.x-1.5, 2011-08-16
----------------------------------
-by greenSkin: Brought the 7.x branch current with the 6.x branch features.
-
-
-Module Filter 7.x-1.3, 2011-03-07
----------------------------------
-by realityloop: Updated CHANGELOG.txt
-
-
-Module Filter 7.x-1.2, 2011-03-07
----------------------------------
-by realityloop: Changed placement of Submit buttons for other languages
-
-
-Module Filter 7.x-1.1, 2011-03-07
----------------------------------
-by realityloop: first commit via git, broke the release somehow :/
-
-
-Module Filter 7.x-1.0, 2011-01-04
----------------------------------
-by realityloop: Fixed Undefined index error.
-by greenSkin: Removed unused .css and .js files.
+#1257860 by greenSkin: Added filter to update status report.
+by greenSkin: Moved hiding of the inputs wrapper to css rather than a style
+  attribute.
+by greenSkin: Added missing semi-colons in JavaScript.
+by greenSkin: Changed hook comment from using "Implementation of" to
+  "Implements".
+by greenSkin: Fixed showing all modules by default when no hash is present.
+by greenSkin: Turned off autocomplete for filter textfield.
+by greenSkin: Now using "all" for hash when no tab is selected.
+by greenSkin: Implemented visual aids (displays number of modules being
+  enabled/disabled as well as coloring the modules row accordingly).
+by greenSkin: Implemented the enabled count (displays the number of enabled
+  modules of total for a package.
+by greenSkin: Updated tabs setting description.
+by greenSkin: Added missing period.
+by greenSkin: Made dynamic save position default to on. Updated element title
+  on admin page and removed "DEVELOPMENTAL" from description.
+by greenSkin: Improved fixed-top positioning when toolbar is enabled.
+by greenSkin: Added fixed classes for submit button wrapper.
+by greenSkin: Changed module-filter-tabs from a class to an id.
+by greenSkin: Fixed filter on modules page when tabs are disabled.
+by greenSkin: Set min-height to #module-filter-modules.
+by greenSkin: Implemented module_filter element and using attached js and css
+  more.
+by greenSkin: Filter input and checkboxes can now have their default values set
+  based on query params.
+by greenSkin: Tabs now use URL fragments.
+by greenSkin: Fixed regular expression used to determine tab ID.
+by greenSkin: Tabs have been re-written and are functioning.
+by greenSkin: Added Module Filter to "Administration" package.
+by greenSkin: Improved filtering performance.
+by greenSkin: New filter code.
+by greenSkin: Initial tab layout modified. Modules are all in one table but
+  look like they are in packages. All JavaScript has to do on load now is
+  remove the package name and header rows from tbody then sort the rows.
+by greenSkin: Modified the menu item's description.

+ 0 - 0
sites/all/modules/contrib/admin/module_filter/LICENSE.txt


+ 107 - 0
sites/all/modules/contrib/admin/module_filter/README.txt

@@ -0,0 +1,107 @@
+Description
+-----------
+This module provides a method for filtering modules on the modules page as well
+as for filtering projects on the update status report.
+
+The supplied filter is simpler than using your browsers find feature which
+searches the entire page. The provided filter will filter modules/projects that
+do not meet your input.
+
+Along with the filter textfield there are additional
+checkboxes that help to narrow the search more. The modules page contains four
+checkboxes: Enabled, Disabled, Required, and Unavailable. While the first two
+are self-explanatory, the latter two can take an explanation. The Required
+checkbox affects visibility of modules that are enabled and have other
+module(s) that require it also enabled. The Unavailable checkbox affects
+visibility of modules that are disabled and depend on module(s) that are
+missing.
+
+The update status report filter also contains four checkboxes: Up-to-Date,
+Update available, Security update, and Unknown. These directly affect the
+visibility of each project; whether it is up-to-date, there is an update
+available, a security update is available, or the status is unknown.
+
+Installation
+------------
+To install this module, do the following:
+
+1. Extract the tar ball that you downloaded from Drupal.org.
+
+2. Upload the entire directory and all its contents to your modules directory.
+
+Configuration
+-------------
+To enable and configure this module do the following:
+
+1. Go to Admin -> Modules, and enable Module Filter.
+
+2. Go to Admin -> Configuration -> User interface -> Module filter, and make
+   any necessary configuration changes. 
+
+Tabs
+----
+By default Module Filter alters the modules page into tabs (Can be disabled on
+configuration page). In the tabs view, each package is converted to a vertical
+tab rather than a fieldset which greatly increases the ability to browse them.
+
+There are several benefits to using the tabs view over the standard view for
+the modules page. I've listed the key benefits below as well as additional
+information that pertains to each.
+
+1.  The increased ease of browsing between packages.
+
+2.  Allows all modules to be listed alphabetically outside of their package,
+    making it all the easier to find the module by name rather than package it
+    happens to be in.
+
+3.  The operations for a module are moved within the description column giving
+    the description more "elbow room".
+
+4.  Filtering is restricted to within the active tab or globally when no tab is
+    selected. By default no tab is selected which will list all modules. When a
+    tab is active and you want to get back to the 'all' state click on the
+    active tab to deselect it.
+
+5.  The number of enabled modules per tab is shown on the active tab. (Can be
+    disabled on configuration page)
+
+6.  Nice visual aids become available showing what modules are to be
+    enabled/disabled and the number of matching modules in each tab when
+    filtering. (Can be disabled on configuration page)
+
+7.  The save configuration button becomes more accessible, either staying at
+    the bottom of the window when the tabs exceed past the bottom and at the
+    top when scrolling past the tabs. (Can be disabled on configuration page)
+
+8.  When filtering, tabs that do not contain matches can be hidden. (Can be
+    enabled on configuration page)
+
+9.  Tab states are remembered like individual pages allowing you to move
+    forward and backward within your selections via your browsers
+    forward/backward buttons.
+
+10. When viewing all modules (no active tab) and mousing over modules it's tab
+    becomes highlighted to signify which tab it belongs to.
+
+Filter operators
+----------------
+The modules page's filter has three filter operators available. Filter
+operators allow alternative filtering techniques. A filter operator is applied
+by typing within the filter textfield 'operator:' (where operator is the
+operator type) followed immediately with the string to pass to the operator
+function (e.g. 'requires:block'). The available operators are:
+
+description:
+   Filter based on a module's description.
+
+requiredBy:
+   Filter based on what a module is required by.
+
+requires:
+   Filter based on what a module requires.
+
+Multiple filters (or queries) can be applied by space delimiting. For example,
+the filter string 'description:ctools views' would filter down to modules with
+"ctools" in the description and "views" within the module's name. To pass a
+space within a single query wrap it within double quotes (e.g. 'requires:"chaos
+tools"' or '"bulk export"').

+ 23 - 0
sites/all/modules/contrib/admin/module_filter/css/dynamic_position.css

@@ -0,0 +1,23 @@
+html.js #module-filter-submit {
+  background-color: #F6F6F6;
+  width: 239px;
+  border: 1px solid #ccc;
+  border-top: 0;
+}
+html.js #module-filter-submit .form-actions {
+  text-align: center;
+  margin: 0;
+}
+html.js #module-filter-submit input {
+  margin: 2em 0 1em;
+}
+html.js #module-filter-submit.fixed {
+  position: fixed;
+  border-top: 1px solid #ccc;
+}
+html.js #module-filter-submit.fixed-top {
+  top: 0;
+}
+html.js #module-filter-submit.fixed-bottom {
+  bottom: 0;
+}

+ 23 - 1
sites/all/modules/contrib/admin/module_filter/css/module_filter.css

@@ -1,4 +1,26 @@
-
+.module-filter-inputs-wrapper {
+  display: none;
+}
+.module-filter-clear {
+  display: inline;
+  position: relative;
+}
+.module-filter-clear a {
+  margin-left: 5px;
+  font-size: 11px;
+  position: absolute;
+}
 #module-filter-show-wrapper .form-item {
   display: inline;
 }
+.module-filter-no-results {
+  text-align: center;
+  text-transform: uppercase;
+  color: #888;
+}
+#module-filter-modules {
+  position: relative;
+}
+#module-filter-modules table {
+  position: absolute;
+}

+ 54 - 0
sites/all/modules/contrib/admin/module_filter/css/module_filter_tab-rtl.css

@@ -0,0 +1,54 @@
+#module-filter-tabs {
+  float: right;
+}
+#module-filter-tabs li.selected a,
+#module-filter-tabs li.selected a:hover,
+#module-filter-tabs li.selected a:focus,
+#module-filter-tabs li.selected a:active {
+  background-color: #fff;
+  margin-left: -1px;
+  margin-right: 0;
+}
+.admin-operations {
+  float: left;
+}
+#module-filter-modules {
+  margin-right: 240px;
+}
+html.js .toggle-enable {
+  background-image: -webkit-gradient(linear, 100% 0%, 0% 0%, color-stop(50%, red), color-stop(50%, orange), color-stop(100%, orange));
+  background-image: -moz-linear-gradient(right, red 50%, orange 50%, orange 100%);
+  background-image: linear-gradient(right, red 50%, orange 50%, orange 100%);
+}
+html.js .toggle-enable.enabled {
+  background-image: -webkit-gradient(linear, 100% 0%, 0% 0%, color-stop(50%, orange), color-stop(50%, green), color-stop(100%, green));
+  background-image: -moz-linear-gradient(right, orange 50%, green 50%, green 100%);
+  background-image: linear-gradient(right, orange 50%, green 50%, green 100%);
+}
+html.js .toggle-enable.disabled {
+  background: #ccc;
+  border-color: #ddd;
+  cursor: auto;
+}
+html.js .toggle-enable.disabled div {
+  background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #FEFEFE), color-stop(100%, #EEEEEE));
+  background-image: -moz-linear-gradient(top, #FEFEFE 0%, #EEEEEE 100%);
+  background-image: linear-gradient(top, #FEFEFE 0%, #EEEEEE 100%);
+}
+html.js .toggle-enable div {
+  -webkit-transition: right 0.2s;
+  -mox-transition: right 0.2s;
+  -o-transition: right 0.2s;
+  transition: right 0.2s;
+}
+html.js .toggle-enable div:before {
+  content: "ON";
+  left: -24px;
+}
+html.js .toggle-enable div:after {
+  content: "OFF";
+  left: -24px;
+}
+html.js .toggle-enable.off div {
+  right: 24px;
+}

+ 218 - 95
sites/all/modules/contrib/admin/module_filter/css/module_filter_tab.css

@@ -1,136 +1,259 @@
+.sticky-header {
+  z-index: 1;
+}
 
-#module-filter-wrapper .form-item {
-  border: 0px none;
+#module-filter-tabs {
+  float: left;
+}
+#module-filter-tabs ul {
+  width: 239px;
+  list-style: none;
+  list-style-image: none;
+  background-color: #ddd;
+  border: 1px solid #ccc;
+  border-top: none;
   margin: 0;
-  padding: 9px;
+  padding: 0;
+  line-height: 1;
 }
-#module-filter-wrapper .form-item:after {
-/*  display: block;*/
-  clear: none;
+#module-filter-tabs li {
+  background: #eee;
+  border-top: 1px solid #ccc;
+  padding: 0;
+  margin: 0;
+  min-width: 0;
 }
-#module-filter-left {
-  float: left;
-  background-color: #F6F6F6;
-  border: 1px solid #D6DBDE;
-  margin-right: -1px;
-  width: 185px;
+#module-filter-tabs li#new-tab {
+  margin-bottom: 10px;
+  border-bottom: 1px solid #ccc;
 }
-#module-filter-left ul {
-  margin: 0px;
-  padding: 0px;
-  list-style: none;
+#module-filter-tabs li.disabled,
+#module-filter-tabs li#new-tab.disabled {
+  pointer-events: none;
+  cursor: default;
+  background: #ccc;
+  border-top-color: #bbb;
+  border-bottom-color: #bbb;
 }
-#module-filter-left ul li {
-  background: #EFEFEF none repeat scroll 0 0;;
-  border-bottom: 1px solid #D6DBDE;
-  margin: 0px;
-  padding: 0px;
-  list-style-image: none;
+#module-filter-tabs li.disabled a,
+#module-filter-tabs li.disabled span {
+  color: #999;
 }
-#module-filter-left ul li.active {
-  margin-right: -1px;
-  width: 186px;
-  background-color: #FFFFFF;
-  position: relative;
+#module-filter-tabs li.suggest {
+  background: #F9F9F9;
 }
-#module-filter-left ul li a {
-  color: #777777;
+#module-filter-tabs li a {
   display: block;
-  padding: 0.5em;
-  line-height: 100%;
-  font-size: 90%;
-  outline: none;
-}
-#module-filter-left ul li.active a {
-  background-color: #FFFFFF;
-  color: #000000;
-  font-weight: bold;
-}
-#module-filter-left ul li a:hover {
-  background-color: #F6F6F6;
   text-decoration: none;
+  padding: 10px;
 }
-#module-filter-left ul li.active a:hover {
-  background-color: #FFFFFF;
+#module-filter-tabs li a span.result-info {
+  float: right;
+  font-size: 10px;
+  color: #999;
+  margin-top: 3px;
 }
-#module-filter-left ul li a span.visual-aid {
+#module-filter-tabs li a span.visual-aid {
   font-size: 8px;
-  float: right;
+/*  float: right;*/
 }
-#module-filter-left ul li a span.enabling {
+#module-filter-tabs li span.visual-aid {
+  font-weight: bold;
+}
+#module-filter-tabs li a span.enabling {
   color: green;
 }
-#module-filter-left ul li a span.disabling {
+#module-filter-tabs li a span.disabling {
   color: red;
-  margin-left: 5px;
 }
-#module-filter-left ul li a span.counts {
-  font-weight: normal;
+#module-filter-tabs li strong {
+  font-size: 0.923em;
+}
+#module-filter-tabs li a:hover,
+#module-filter-tabs li a:focus {
+  outline: 1px dotted;
+  background: #d5d5d5;
+  text-decoration: none;
+  outline: 0;
+}
+#module-filter-tabs li.selected a,
+#module-filter-tabs li.selected a:hover,
+#module-filter-tabs li.selected a:focus,
+#module-filter-tabs li.selected a:active {
+  background-color: #fff;
+  margin-right: -1px;
+}
+#module-filter-tabs li .summary {
+  display: block;
+  margin-bottom: 0;
+  color: #666;
+  font-size: 0.846em;
+  padding-top: 0.4em;
+}
+#module-filter-tabs li .summary .count {
   display: none;
-  font-size: 0.8em;
-  color: #333333;
-  padding: 2px 0 0;
 }
-#module-filter-left ul li.active a span.counts {
+#module-filter-tabs li.selected .summary .count {
   display: block;
 }
-#module-filter-submit {
-  margin: 0;
+html.js #module-filter-submit input {
+  margin: 2em 2em 1em;
 }
-#module-filter-submit .form-actions {
+html.js .module-filter-inputs-wrapper {
   text-align: center;
 }
-#module-filter-submit input.form-submit {
-  margin: 1em 0 0;
+html.js .module-filter-inputs-wrapper .form-item {
+  margin: 0;
+  padding: 9px;
 }
-#module-filter-submit.fixed {
-  position: fixed;
-  background-color: #F6F6F6;
-  border: 1px solid #D6DBDE;
-  margin-left: -1px;
-  width: 185px;
+html.js .module-filter-inputs-wrapper label {
+  display: inline;
 }
-#module-filter-submit.fixed-top {
-  top: 0;
+html.js .module-filter-inputs-wrapper input[name="module_filter[name]"] {
+  width: 80%;
 }
-#module-filter-submit.fixed-bottom {
-  bottom: 0;
+html.js #module-filter-show-wrapper {
+  margin-bottom: 1em;
 }
-#module-filter-right {
-  display: block;
+html.js #module-filter-modules {
+  margin-left: 240px;
+  border: 1px solid #ccc;
 }
-#module-filter-squeeze {
-  margin-left: 186px;
-  background-color: #FFFFFF;
-  border: 1px solid #D6DBDE;
-  height: auto !important;
+#module-filter-modules table {
+  border-top: none;
+  border-right: none;
+  border-left: none;
 }
-.form-item-module-filter-name {
-  text-align: center;
+html.js #module-filter-modules table {
+  margin: 0;
+  border-bottom: none;
 }
-.form-item-module-filter-name label {
-  display: inline;
+html.js #module-filter-modules table tr,
+html.js #module-filter-modules table td {
+  border-left: 0;
+  border-right: 0;
 }
-#module-filter-show-wrapper .form-checkboxes {
-  text-align: center;
+#module-filter-modules table thead {
+  display: none;
 }
-#module-filter-show-wrapper .form-item:after {
-  display: inline;
+#module-filter-modules table tr.admin-package-title,
+#module-filter-modules table tr.admin-package-title td {
+  border: none !important;
+  border-top: 1px solid #ccc !important;
+  background-color: transparent !important;
+  padding: 10px 0 0;
 }
-table.package {
-  margin: 1em 0;
+#module-filter-modules table tr.admin-package-title.first,
+#module-filter-modules table tr.admin-package-title.first td {
+  border-top: none !important;
 }
-table.package,
-table.package thead,
-table.package tbody,
-table.package tbody tr,
-table.package td:last-child {
-  border-right: 0 none;
-  border-left: 0 none;
+#module-filter-modules table tr.admin-package-header td {
+  border: 1px solid #ccc;
+  text-transform: uppercase;
+  background: #E1E2DC;
+  font-weight: normal;
+  padding: 3px 10px;
 }
-table.package tr.enabling {
+#module-filter-modules table tr.enabling {
   background-color: #dfd;
 }
-table.package tr.disabling {
+#module-filter-modules table tr.disabling {
   background-color: #fcc;
 }
+#module-filter-modules span.module-machine-name {
+  font-size: 0.9em;
+  font-weight: normal;
+}
+.admin-version {
+  white-space: nowrap;
+}
+.admin-operations a.module-link {
+  display: inline;
+}
+
+html.js .toggle-enable {
+  margin: auto;
+  position: relative;
+  width: 50px;
+  overflow: hidden;
+  height: 18px;
+  line-height: 18px;
+  font-size: 11px;
+  text-align: center;
+  cursor: pointer;
+  border: 1px solid #ccc;
+  -moz-border-radius: 3px;
+  -webkit-border-radius: 3px;
+  -khtml-border-radius: 3px;
+  border-radius: 3px;
+  -moz-box-shadow: 0 0 10px rgba(0,0,0,0.50) inset;
+  -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.50) inset;
+  box-shadow: 0 0 10px rgba(0,0,0,0.50) inset;
+  background-clip: padding-box;
+  background-image: -webkit-gradient(linear, 0% 0%, 100% 0%, color-stop(50%, red), color-stop(50%, orange), color-stop(100%, orange));
+  background-image: -moz-linear-gradient(left, red 50%, orange 50%, orange 100%);
+  background-image: linear-gradient(left, red 50%, orange 50%, orange 100%);
+}
+html.js .toggle-enable.enabled {
+  background-image: -webkit-gradient(linear, 0% 0%, 100% 0%, color-stop(50%, orange), color-stop(50%, green), color-stop(100%, green));
+  background-image: -moz-linear-gradient(left, orange 50%, green 50%, green 100%);
+  background-image: linear-gradient(left, orange 50%, green 50%, green 100%);
+}
+html.js .toggle-enable.disabled {
+  background: #ccc;
+  border-color: #ddd;
+  cursor: auto;
+}
+html.js .toggle-enable div {
+  position: relative;
+  color: #777;
+  width: 26px;
+  -moz-border-radius: 2px;
+  -webkit-border-radius: 2px;
+  -khtml-border-radius: 2px;
+  border-radius: 2px;
+  background: white;
+  text-shadow: 1px 1px 0 white;
+  background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #FEFEFE), color-stop(100%, #EAEAEA));
+  background-image: -moz-linear-gradient(top, #FEFEFE 0%, #EAEAEA 100%);
+  background-image: linear-gradient(top, #FEFEFE 0%, #EAEAEA 100%);
+  -webkit-transition: left 0.2s;
+  -mox-transition: left 0.2s;
+  -o-transition: left 0.2s;
+  transition: left 0.2s;
+}
+html.js .toggle-enable.disabled div {
+  background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #FEFEFE), color-stop(100%, #EEEEEE));
+  background-image: -moz-linear-gradient(top, #FEFEFE 0%, #EEEEEE 100%);
+  background-image: linear-gradient(top, #FEFEFE 0%, #EEEEEE 100%);
+}
+html.js .toggle-enable div:after,
+html.js .toggle-enable div:before {
+  color: white;
+  text-shadow: none;
+  width: 25px;
+  position: absolute;
+  top: 0;
+  font-size: 9px;
+  font-weight: bold;
+}
+html.js .toggle-enable div:before {
+  content: "OFF";
+  left: -24px;
+}
+html.js .toggle-enable div:after {
+  content: "ON";
+  right: -24px;
+}
+html.js .toggle-enable.off div {
+  left: 24px;
+}
+
+#module-filter-tabs.top-fixed {
+  position: fixed;
+  top: 0;
+}
+#module-filter-tabs.bottom-fixed {
+  position: fixed;
+  bottom: 0;
+}

+ 53 - 0
sites/all/modules/contrib/admin/module_filter/css/modules.css

@@ -0,0 +1,53 @@
+#system-modules table {
+  table-layout: fixed;
+}
+#system-modules th.checkbox {
+  width: 8%;
+}
+#system-modules th.name {
+  width: 25%;
+}
+#system-modules th.version {
+  width: 10%;
+}
+#system-modules th.links {
+  width: 15%;
+}
+#system-modules td {
+  vertical-align: top;
+}
+#system-modules .expand.inner {
+  background: transparent url(../images/collapsed.png) left 0.6em no-repeat;
+  margin-left: -12px;
+  padding-left: 12px;
+}
+#system-modules .expanded.expand.inner {
+  background: transparent url(../images/expanded.png) left 0.6em no-repeat;
+}
+#system-modules .description {
+  cursor: pointer;
+}
+#system-modules .description .inner {
+  overflow: hidden; /* truncates descriptions if too long */
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+#system-modules .description .inner.expanded > * {
+  cursor: auto;
+}
+#system-modules .description .requirements,
+#system-modules .description .links {
+  display: none;
+}
+#system-modules .description .expanded.inner {
+  overflow: visible;
+  white-space: normal;
+}
+#system-modules .description .expanded .requirements,
+#system-modules .description .expanded .links {
+  display: block;
+}
+#system-modules .requirements {
+  padding: 5px 0;
+  max-width: 490px;
+}

+ 18 - 0
sites/all/modules/contrib/admin/module_filter/css/update_status.css

@@ -0,0 +1,18 @@
+#module-filter-update-status-form {
+  float: right;
+}
+.module-filter-inputs-wrapper label {
+  display: inline;
+}
+.module-filter-inputs-wrapper .form-item-module-filter-name {
+  margin-bottom: 0;
+  padding-bottom: 0;
+  text-align: right;
+}
+#module-filter-show-wrapper .form-item {
+  padding: 5px;
+}
+p.module-filter-no-results {
+  clear: both;
+  padding-top: 30px;
+}

BIN
sites/all/modules/contrib/admin/module_filter/images/collapsed.png


BIN
sites/all/modules/contrib/admin/module_filter/images/expanded.png


Some files were not shown because too many files changed in this diff