diff --git a/sites/all/modules/contrib/admin/backup_migrate/CHANGELOG.txt b/sites/all/modules/contrib/admin/backup_migrate/CHANGELOG.txt new file mode 100644 index 0000000..f24ca73 --- /dev/null +++ b/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. diff --git a/sites/all/modules/contrib/admin/backup_migrate/LICENSE.txt b/sites/all/modules/contrib/admin/backup_migrate/LICENSE.txt old mode 100755 new mode 100644 diff --git a/sites/all/modules/contrib/admin/backup_migrate/README.txt b/sites/all/modules/contrib/admin/backup_migrate/README.txt index 7ecc15d..8275f46 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/README.txt +++ b/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. +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. + + +Installation ------------------------------------------------------------------------------- -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. - -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: -* 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: -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 ------------------------------------------------------------------------------- - -VERY IMPORTANT SECURITY NOTE: -Backup files may contain sensitive data and by default, are saved to your web +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 \ No newline at end of file diff --git a/sites/all/modules/contrib/admin/backup_migrate/backup_migrate.css b/sites/all/modules/contrib/admin/backup_migrate/backup_migrate.css index 59554e7..234b811 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/backup_migrate.css +++ b/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 { diff --git a/sites/all/modules/contrib/admin/backup_migrate/backup_migrate.info b/sites/all/modules/contrib/admin/backup_migrate/backup_migrate.info index 15ec545..e43c995 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/backup_migrate.info +++ b/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 - -files[] = includes/destinations.inc -files[] = includes/profiles.inc -files[] = includes/schedules.inc - configure = admin/config/system/backup_migrate -; Information added by Drupal.org packaging script on 2014-05-27 -version = "7.x-3.0" +; 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 + +; Test suite. +files[] = tests/BmTestBase.test +files[] = tests/BmTestBasics.test +files[] = tests/BmTestProfiles.test + +; 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" diff --git a/sites/all/modules/contrib/admin/backup_migrate/backup_migrate.install b/sites/all/modules/contrib/admin/backup_migrate/backup_migrate.install index 8084752..2c3d482 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/backup_migrate.install +++ b/sites/all/modules/contrib/admin/backup_migrate/backup_migrate.install @@ -1,22 +1,138 @@ $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 configuring the private directory path 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 configuring the private directory path 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'); + } +} diff --git a/sites/all/modules/contrib/admin/backup_migrate/backup_migrate.js b/sites/all/modules/contrib/admin/backup_migrate/backup_migrate.js index 3730a85..de756d9 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/backup_migrate.js +++ b/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( $('').append( - $(''+ Drupal.settings.backup_migrate.checkboxLinkText +'').click(function() { + $('' + Drupal.settings.backup_migrate.checkboxLinkText + '').click(function() { var $select = $(self); var $checkboxes = $('
').addClass('backup-migrate-tables-checkboxes'); $('option', $select).each(function(i) { $checkboxes.append( $('
').append( $('').prepend( - $('') + $('') .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() ) ) ); diff --git a/sites/all/modules/contrib/admin/backup_migrate/backup_migrate.module b/sites/all/modules/contrib/admin/backup_migrate/backup_migrate.module index 6fddda9..31ece4f 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/backup_migrate.module +++ b/sites/all/modules/contrib/admin/backup_migrate/backup_migrate.module @@ -1,32 +1,24 @@ - t('Backup and Migrate 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. The database backup files created with this module can be imported into this or any other Drupal installation with the !restorelink, or you can use a database tool such as phpMyAdmin or the mysql command line command.', - array( - '!restorelink' => user_access('restore from backup') ? l(t('restore feature'), BACKUP_MIGRATE_MENU_PATH . '/restore') : t('restore feature'), - '!phpmyadminurl' => 'http://www.phpmyadmin.net' - ) - ) + 'body' => t('Backup and Migrate 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. The database backup files created with this module can be imported into this or any other Drupal installation with the !restorelink, or you can use a database tool such as phpMyAdmin or the mysql command line command.', array( + '!restorelink' => user_access('restore from backup') ? l(t('restore feature'), BACKUP_MIGRATE_MENU_PATH . '/restore') : t('restore feature'), + '!phpmyadminurl' => 'http://www.phpmyadmin.net', + )), ), BACKUP_MIGRATE_MENU_PATH => array( 'title' => t('Quick Backup Tab'), @@ -35,8 +27,11 @@ function backup_migrate_help($section, $arg) { ), BACKUP_MIGRATE_MENU_PATH . '/export/advanced' => array( 'title' => t('Advanced Backup Tab'), - 'body' => t('Use this form to run manual backups of your database and files with more advanced options. If you have any !profilelink saved you can load those settings. You can save any of the changes you make to these settings as a new settings profile.', - array("!profilelink" => user_access('administer backup and migrate') ? l(t('settings profiles'), BACKUP_MIGRATE_MENU_PATH . '/profile') : t('settings profiles'), '!restorelink' => user_access('restore from backup') ? l(t('restore feature'), BACKUP_MIGRATE_MENU_PATH . '/restore') : t('restore feature'), '!phpmyadminurl' => 'http://www.phpmyadmin.net')), + 'body' => t('Use this form to run manual backups of your database and files with more advanced options. If you have any !profilelink saved you can load those settings. You can save any of the changes you make to these settings as a new settings profile.', array( + "!profilelink" => user_access('administer backup and migrate') ? l(t('settings profiles'), BACKUP_MIGRATE_MENU_PATH . '/profile') : t('settings profiles'), + '!restorelink' => user_access('restore from backup') ? l(t('restore feature'), BACKUP_MIGRATE_MENU_PATH . '/restore') : t('restore feature'), + '!phpmyadminurl' => 'http://www.phpmyadmin.net', + )), 'access arguments' => array('perform backup'), ), BACKUP_MIGRATE_MENU_PATH . '/restore' => array( @@ -57,22 +52,26 @@ function backup_migrate_help($section, $arg) { ), BACKUP_MIGRATE_MENU_PATH . '/settings/profile' => array( 'title' => t('Profiles'), - 'body' => t('Profiles are saved backup settings. Profiles store your table exclusion settings as well as your backup file name, compression and timestamp settings. You can use profiles in !schedulelink and for !manuallink.', - array('!schedulelink' => user_access('administer backup and migrate') ? l(t('schedules'), BACKUP_MIGRATE_MENU_PATH . '/schedule') : t('settings profiles'), '!manuallink' => user_access('perform backups') ? l(t('manual backups'), BACKUP_MIGRATE_MENU_PATH) : t('manual backups'))), + 'body' => t('Profiles are saved backup settings. Profiles store your table exclusion settings as well as your backup file name, compression and timestamp settings. You can use profiles in !schedulelink and for !manuallink.', array( + '!schedulelink' => user_access('administer backup and migrate') ? l(t('schedules'), BACKUP_MIGRATE_MENU_PATH . '/schedule') : t('settings profiles'), + '!manuallink' => user_access('perform backups') ? l(t('manual backups'), BACKUP_MIGRATE_MENU_PATH) : t('manual backups'), + )), 'more' => t('You can create new profiles using the add profiles tab or by checking the "Save these settings" button on the advanced backup page.'), 'access arguments' => array('administer backup and migrate'), ), BACKUP_MIGRATE_MENU_PATH . '/schedule' => array( 'title' => t('Scheduling'), - 'body' => t('Automatically backup up your database and files on a regular schedule using cron.', - array('!cronurl' => 'http://drupal.org/cron')), - 'more' => t('Each schedule will run a maximum of once per cron run, so they will not run more frequently than your cron is configured to run. If you specify a number of backups to keep for a schedule, old backups will be deleted as new ones created. If specifiy a number of files to keep other backup files in that schedule\'s destination will get deleted.'), + 'body' => t('Automatically backup up your database and files on a regular schedule using cron.', array( + '!cronurl' => 'http://drupal.org/cron', + )), + 'more' => t("Each schedule will run a maximum of once per cron run, so they will not run more frequently than your cron is configured to run. If you specify a number of backups to keep for a schedule, old backups will be deleted as new ones created. If specifiy a number of files to keep other backup files in that schedule's destination will get deleted."), 'access arguments' => array('administer backup and migrate'), ), BACKUP_MIGRATE_MENU_PATH . '/settings/import' => array( 'title' => t('Importing Settings'), - 'body' => t('Import a settings profile, backup schedule, source or destination by pasting the export code into the textarea.', - array('!cronurl' => 'http://drupal.org/cron')), + 'body' => t('Import a settings profile, backup schedule, source or destination by pasting the export code into the textarea.', array( + '!cronurl' => 'http://drupal.org/cron', + )), ), ); if (module_exists('help')) { @@ -84,7 +83,7 @@ function backup_migrate_help($section, $arg) { } if ($section == 'admin/help#backup_migrate') { - $out = ""; + $out = ''; foreach ($help as $key => $section) { if (isset($section['access arguments'])) { foreach ($section['access arguments'] as $access) { @@ -97,11 +96,11 @@ function backup_migrate_help($section, $arg) { if (!is_numeric($key)) { $section['title'] = l($section['title'], $key); } - $out .= "

". $section['title'] ."

"; + $out .= "

" . $section['title'] . "

"; } - $out .= "

". $section['body'] ."

"; + $out .= "

" . $section['body'] . "

"; if (!empty($section['more'])) { - $out .= "

". $section['more'] ."

"; + $out .= "

" . $section['more'] . "

"; } } return $out; @@ -136,7 +135,7 @@ function backup_migrate_menu() { 'description' => 'Backup the database.', 'page callback' => 'backup_migrate_menu_callback', 'page arguments' => array('', 'backup_migrate_ui_manual_backup_quick', TRUE), - 'access arguments' => array('access backup and migrate'), + 'access arguments' => array('perform backup'), 'weight' => 0, 'type' => MENU_DEFAULT_LOCAL_TASK, ); @@ -144,7 +143,11 @@ function backup_migrate_menu() { 'title' => 'Advanced Backup', 'description' => 'Backup the database.', 'page callback' => 'backup_migrate_menu_callback', - 'page arguments' => array('', 'backup_migrate_ui_manual_backup_advanced', TRUE), + 'page arguments' => array( + '', + 'backup_migrate_ui_manual_backup_advanced', + TRUE, + ), 'access arguments' => array('perform backup'), 'weight' => 1, 'type' => MENU_LOCAL_TASK, @@ -163,11 +166,13 @@ function backup_migrate_menu() { 'description' => 'Previously created backups', 'page callback' => 'backup_migrate_menu_callback', 'page arguments' => array('', 'backup_migrate_ui_saved_backups', TRUE), - 'access arguments' => array('access backup and migrate'), + 'access arguments' => array('access backup files'), 'weight' => 2, 'type' => MENU_LOCAL_TASK, ); - // @TODO: This was removed because it was getting far to complicated for an abstraction. Exposing the actual schedules is more direct. + // @TODO: This was removed because it was getting far too complicated for an + // abstraction. Exposing the actual schedules is more direct. + // @code // $items[BACKUP_MIGRATE_MENU_PATH . '/schedule'] = array( // 'title' => 'Schedule', // 'description' => 'Schedule automatic backups', @@ -177,6 +182,7 @@ function backup_migrate_menu() { // 'weight' => 3, // 'type' => MENU_LOCAL_TASK, // ); + // @endcode $items[BACKUP_MIGRATE_MENU_PATH . '/settings'] = array( 'title' => 'Settings', 'description' => 'Module settings.', @@ -249,44 +255,78 @@ function form_process_backup_migrate_file_list($element) { */ function backup_migrate_form_update_script_selection_form_alter(&$form, $form_state) { $msg = t('Before you run any database updates, make sure you have a backup of your database. Use !backup_migrate to create that backup now.', array('!backup_migrate' => l(t('Backup and Migrate'), BACKUP_MIGRATE_MENU_PATH))); - + drupal_set_message($msg, 'warning'); - + } /** * Implements hook_cron(). - * - * Takes care of scheduled backups and deletes abandoned temp files. */ function backup_migrate_cron() { + // Optionally disable hook_cron(). + if (variable_get('backup_migrate_disable_cron', FALSE)) { + if (variable_get('backup_migrate_verbose')) { + watchdog('backup_migrate', 'Cron disabled.'); + } + return; + } + + if (variable_get('backup_migrate_verbose')) { + watchdog('backup_migrate', 'Cron starting.'); + } + + // Takes care of scheduled backups and deletes abandoned temp files. // Set the message mode to logging. _backup_migrate_message_callback('_backup_migrate_message_log'); // Clean up any previous abandoned tmp files before we attempt to back up. backup_migrate_include('files'); + + // Clean up any temporary files that were already present. + if (variable_get('backup_migrate_verbose')) { + watchdog('backup_migrate', 'Deleting temp files.'); + } _backup_migrate_temp_files_delete(); + + if (variable_get('backup_migrate_verbose')) { + watchdog('backup_migrate', 'Processing schedule(s) backups.'); + } backup_migrate_include('schedules'); backup_migrate_schedules_cron(); - // Clean up any tmp files from this run. + // Clean up any temporary files from this run. + if (variable_get('backup_migrate_verbose')) { + watchdog('backup_migrate', 'Deleting temp files.'); + } _backup_migrate_temp_files_delete(); -} + if (variable_get('backup_migrate_verbose')) { + watchdog('backup_migrate', 'Cron finished.'); + } +} /** * Implements hook_cronapi(). */ function backup_migrate_cronapi($op, $job = NULL) { + // Optionally disable hook_cron(). + if (variable_get('backup_migrate_disable_cron', FALSE)) { + if (variable_get('backup_migrate_verbose')) { + watchdog('backup_migrate', 'Cron disabled.'); + } + return; + } + $items = array(); backup_migrate_include('schedules'); foreach (backup_migrate_get_schedules() as $schedule) { if ($schedule->get('cron') == BACKUP_MIGRATE_CRON_ELYSIA) { $id = $schedule->get('id'); $items['backup_migrate_' . $id] = array( - 'description' => t('Run the Backup and Migrate \'!name\' schedule', array('!name' => $schedule->get('name'))), + 'description' => t("Run the Backup and Migrate '!name' schedule", array('!name' => $schedule->get('name'))), 'rule' => $schedule->get('cron_schedule'), - 'callback' => 'backup_migrate_schedule_run', + 'callback' => 'backup_migrate_schedule_run', 'arguments' => array($id), 'file' => 'includes/schedules.inc', ); @@ -303,26 +343,32 @@ function backup_migrate_permission() { 'access backup and migrate' => array( 'title' => t('Access Backup and Migrate'), 'description' => t('Access the Backup and Migrate admin section.'), + 'restrict access' => TRUE, ), 'perform backup' => array( 'title' => t('Perform a backup'), 'description' => t('Back up any of the available databases.'), + 'restrict access' => TRUE, ), 'access backup files' => array( 'title' => t('Access backup files'), 'description' => t('Access and download the previously created backup files.'), + 'restrict access' => TRUE, ), 'delete backup files' => array( 'title' => t('Delete backup files'), 'description' => t('Delete the previously created backup files.'), + 'restrict access' => TRUE, ), 'restore from backup' => array( 'title' => t('Restore the site'), - 'description' => t('Restore the site\'s database from a backup file.'), + 'description' => t("Restore the site's database from a backup file."), + 'restrict access' => TRUE, ), 'administer backup and migrate' => array( 'title' => t('Administer Backup and Migrate'), 'description' => t('Edit Backup and Migrate profiles, schedules and destinations.'), + 'restrict access' => TRUE, ), ); } @@ -331,7 +377,7 @@ function backup_migrate_permission() { * Implements hook_simpletest(). */ function backup_migrate_simpletest() { - $dir = drupal_get_path('module', 'backup_migrate') .'/tests'; + $dir = drupal_get_path('module', 'backup_migrate') . '/tests'; $tests = file_scan_directory($dir, '\.test$'); return array_keys($tests); } @@ -364,8 +410,6 @@ function backup_migrate_theme() { return $themes; } -/* Blocks */ - /** * Implements hook_block_info(). */ @@ -373,7 +417,7 @@ function backup_migrate_block_info() { $blocks = array(); $blocks['quick_backup'] = array( 'info' => t('Backup and Migrate Quick Backup'), - 'admin' => TRUE + 'admin' => TRUE, ); return $blocks; } @@ -393,7 +437,7 @@ function backup_migrate_block_view($delta) { */ function _backup_migrate_block_view_quick_backup() { if (user_access('access backup and migrate') && user_access('perform backup') && $_GET['q'] != BACKUP_MIGRATE_MENU_PATH) { - drupal_add_css(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.css'); + drupal_add_css(drupal_get_path('module', 'backup_migrate') . '/backup_migrate.css'); // Messages should be sent to the browser. _backup_migrate_message_callback('_backup_migrate_message_browser'); @@ -414,7 +458,9 @@ function backup_migrate_backup_migrate_schedules() { $schedules = array(); // Declare a default backup for each source that can be enabled. - // @TODO: This makes setting up scheduled easier, but only when combined with the now disabled quick-schedule feature. + // @TODO: This makes setting up scheduled easier, but only when combined with + // the now disabled quick-schedule feature. + // @code // $sources = backup_migrate_get_sources(); // foreach ($sources as $id => $source) { // $schedule = array( @@ -429,39 +475,34 @@ function backup_migrate_backup_migrate_schedules() { // ); // $schedules[$id] = backup_migrate_crud_create_item('schedule', $schedule); // } - + // @endcode if (variable_get('nodesquirrel_secret_key', FALSE) != FALSE) { - $schedule = array( - 'machine_name' => 'nodesquirrel', - 'name' => 'NodeSquirrel', - 'source_id' => variable_get('nodesquirrel_schedule_source_id', 'db'), - 'destination_id' => 'nodesquirrel', - 'profile_id' => 'default', - 'period' => variable_get('nodesquirrel_schedule', 60*60*24), - 'enabled' => variable_get('nodesquirrel_schedule_enabled', TRUE) && variable_get('nodesquirrel_secret_key', FALSE) != FALSE, - ); - $schedules['nodesquirrel'] = backup_migrate_crud_create_item('schedule', $schedule); - } + $schedule = array( + 'machine_name' => 'nodesquirrel', + 'name' => 'NodeSquirrel', + 'source_id' => variable_get('nodesquirrel_schedule_source_id', 'db'), + 'destination_id' => 'nodesquirrel', + 'profile_id' => 'default', + 'period' => variable_get('nodesquirrel_schedule', 60 * 60 * 24), + 'enabled' => variable_get('nodesquirrel_schedule_enabled', TRUE) && variable_get('nodesquirrel_secret_key', FALSE) != FALSE, + 'keep' => variable_get('nodesquirrel_schedule_keep', BACKUP_MIGRATE_SMART_DELETE), + ); + $schedules['nodesquirrel'] = backup_migrate_crud_create_item('schedule', $schedule); + } return $schedules; } - /** - * Implementation of hook_ctools_plugin_api(). - * - * Tell CTools that we support the default_mymodule_presets API. + * Implements hook_ctools_plugin_api(). */ function backup_migrate_ctools_plugin_api($owner, $api) { + // Tell CTools that we support the default_mymodule_presets API. if ($owner == 'backup_migrate') { return array('version' => 1); } } - - -/* Menu Callbacks */ - /** * A menu callback helper. Handles file includes and interactivity setting. */ @@ -485,7 +526,7 @@ function backup_migrate_include() { if (!isset($used[$file])) { require_once backup_migrate_include_path($file); } - + $used[$file] = TRUE; } } @@ -497,7 +538,6 @@ function backup_migrate_include_path($file) { return DRUPAL_ROOT . '/' . drupal_get_path('module', 'backup_migrate') . "/includes/$file.inc"; } - /** * The menu callback for easy manual backups. */ @@ -608,13 +648,12 @@ function backup_migrate_ui_schedule_form($form, &$form_state, $sources, $schedul '#title' => t('Automatically backup my @source', array('@source' => $source->get('name'))), '#default_value' => $schedule->get('enabled'), ); - /* - $form['quickschedule'][$id]['source_id'] = array( - '#type' => 'item', - '#title' => t('Backup Source'), - '#value' => $source->get('name'), - ); - */ + + // $form['quickschedule'][$id]['source_id'] = array( + // '#type' => 'item', + // '#title' => t('Backup Source'), + // '#value' => $source->get('name'), + // ); $form['quickschedule'][$key]['settings'] = array( '#type' => 'backup_migrate_dependent', '#dependencies' => array( @@ -622,9 +661,9 @@ function backup_migrate_ui_schedule_form($form, &$form_state, $sources, $schedul ), ); $options = array( - (60*60) => t('Once an hour'), - (60*60*24) => t('Once a day'), - (60*60*24*7) => t('Once a week'), + (60 * 60) => t('Once an hour'), + (60 * 60 * 24) => t('Once a day'), + (60 * 60 * 24 * 7) => t('Once a week'), ); $period = $schedule->get('period'); if (!isset($options[$period])) { @@ -638,10 +677,21 @@ function backup_migrate_ui_schedule_form($form, &$form_state, $sources, $schedul ); $form['quickschedule'][$key]['settings']['destination'] = _backup_migrate_get_destination_pulldown('scheduled backup', $schedule->get('destination_id'), $schedule->get('copy_destination_id')); - $form['quickschedule'][$key]['settings']['destination']['destination_id']['#parents'] = array('quickschedule', $key, 'destination_id'); - $form['quickschedule'][$key]['settings']['destination']['copy']['#parents'] = array('quickschedule', $key, 'copy'); - $form['quickschedule'][$key]['settings']['destination']['copy_destination']['copy_destination_id']['#parents'] = array('quickschedule', $key, 'copy_destination_id'); - + $form['quickschedule'][$key]['settings']['destination']['destination_id']['#parents'] = array( + 'quickschedule', + $key, + 'destination_id', + ); + $form['quickschedule'][$key]['settings']['destination']['copy']['#parents'] = array( + 'quickschedule', + $key, + 'copy', + ); + $form['quickschedule'][$key]['settings']['destination']['copy_destination']['copy_destination_id']['#parents'] = array( + 'quickschedule', + $key, + 'copy_destination_id', + ); array( '#type' => 'select', @@ -657,7 +707,14 @@ function backup_migrate_ui_schedule_form($form, &$form_state, $sources, $schedul ); $keep = $schedule->get('keep'); - $option_keys = array_unique(array(BACKUP_MIGRATE_SMART_DELETE, 30, 100, 500, BACKUP_MIGRATE_KEEP_ALL, $keep)); + $option_keys = array_unique(array( + BACKUP_MIGRATE_SMART_DELETE, + 30, + 100, + 500, + BACKUP_MIGRATE_KEEP_ALL, + $keep, + )); $options = array(); foreach ($option_keys as $i) { $options[$i] = $schedule->generate_keep_description($i); @@ -674,7 +731,11 @@ function backup_migrate_ui_schedule_form($form, &$form_state, $sources, $schedul ); // Set the parent for the setttings up a level. foreach (element_children($form['quickschedule'][$key]['settings']) as $child) { - $form['quickschedule'][$key]['settings'][$child]['#parents'] = array('quickschedule', $key, $child); + $form['quickschedule'][$key]['settings'][$child]['#parents'] = array( + 'quickschedule', + $key, + $child, + ); } } } @@ -709,14 +770,12 @@ function backup_migrate_ui_schedule_form_submit($form, &$form_state) { } } - - /** * The quick backup form. */ function backup_migrate_ui_manual_quick_backup_form($form, &$form_state, $inline = TRUE) { backup_migrate_include('profiles', 'destinations', 'sources'); - drupal_add_js(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.js'); + drupal_add_js(drupal_get_path('module', 'backup_migrate') . '/backup_migrate.js'); $form = array(); @@ -764,7 +823,7 @@ function backup_migrate_ui_manual_quick_backup_form($form, &$form_state, $inline $form['advanced'] = array( '#type' => 'markup', - '#markup' => '', + '#markup' => '', ); $form['quickbackup']['submit'] = array( @@ -787,9 +846,11 @@ function backup_migrate_ui_manual_quick_backup_form_validate($form, &$form_state if ($form_state['values']['source_id'] == $form_state['values']['destination_id']) { form_set_error('destination_id', t('A source cannot be backed up to itself. Please pick a different destination for this backup.')); } + // Wrap profile_id for prevent Undefined index notice. + $profile_id = isset($form_state['values']['profile_id']) ? $form_state['values']['profile_id'] : NULL; // For a quick backup use the default settings. - $settings = _backup_migrate_profile_saved_default_profile($form_state['values']['profile_id']); + $settings = _backup_migrate_profile_saved_default_profile($profile_id); // Set the destination to the one chosen in the pulldown. $settings->destination_id = array($form_state['values']['destination_id']); @@ -800,7 +861,7 @@ function backup_migrate_ui_manual_quick_backup_form_validate($form, &$form_state $settings->destination_id[] = $form_state['values']['copy_destination_id']; } - // Add the note + // Add the note. if (!empty($form_state['values']['description_enabled'])) { $settings->filters['utils_description'] = $form_state['values']['description']; } @@ -829,7 +890,7 @@ function backup_migrate_ui_manual_quick_backup_form_submit($form, &$form_state) * Theme the quick backup form. */ function theme_backup_migrate_ui_manual_quick_backup_form_inline($form) { - drupal_add_css(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.css'); + drupal_add_css(drupal_get_path('module', 'backup_migrate') . '/backup_migrate.css'); $form = $form['form']; @@ -844,7 +905,7 @@ function theme_backup_migrate_ui_manual_quick_backup_form_inline($form) { '!from' => drupal_render($form['quickbackup']['source_id']), '!to' => drupal_render($form['quickbackup']['destination']['destination_id']), '!profile' => drupal_render($form['quickbackup']['profile_id']), - //'!submit' => drupal_render($form['quickbackup']['submit']), + // '!submit' => drupal_render($form['quickbackup']['submit']), ); $form['quickbackup']['markup'] = array( '#type' => 'markup', @@ -858,15 +919,15 @@ function theme_backup_migrate_ui_manual_quick_backup_form_inline($form) { ); $form['quickbackup']['destination']['copy']["#prefix"] = '
'; $form['quickbackup']['destination']['copy']["#suffix"] = $replacements['!to'] . '
'; - // This is not good translation practice as it relies on the structure of english - // If I add the pulldown to the label, howerver, the box toggles when the pulldown is clicked. - //$form['quickbackup']['destination']['copy']['#title'] = t('Save a copy to'); - + // This is not good translation practice as it relies on the structure of + // English. If I add the pulldown to the label, howerver, the box toggles + // when the pulldown is clicked. + // $form['quickbackup']['destination']['copy']['#title'] = t('Save a copy to'); unset($form['quickbackup']['source_id']); unset($form['quickbackup']['destination']['destination_id']); unset($form['quickbackup']['destination']['copy_destination']); unset($form['quickbackup']['profile_id']); - //unset($form['quickbackup']['submit']); + // unset($form['quickbackup']['submit']); return drupal_render_children($form); } @@ -874,8 +935,8 @@ function theme_backup_migrate_ui_manual_quick_backup_form_inline($form) { * Theme the quick schedule form. */ function theme_backup_migrate_ui_quick_schedule_form_inline($form) { - drupal_add_js(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.js'); - drupal_add_css(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.css'); + drupal_add_js(drupal_get_path('module', 'backup_migrate') . '/backup_migrate.js'); + drupal_add_css(drupal_get_path('module', 'backup_migrate') . '/backup_migrate.css'); $form = $form['form']; @@ -898,12 +959,14 @@ function theme_backup_migrate_ui_quick_schedule_form_inline($form) { '!profile' => drupal_render($form['settings']['profile_id']), '!keep' => drupal_render($form['settings']['keep']), ); - $form['settings'] = array('inline' => array( - '#type' => 'markup', - "#prefix" => '
', - "#suffix" => '
', - '#markup' => t('Backup !frequency to !to using !profile keeping !keep', $replacements), - )) + $form['settings']; + $form['settings'] = array( + 'inline' => array( + '#type' => 'markup', + "#prefix" => '
', + "#suffix" => '
', + '#markup' => t('Backup !frequency to !to using !profile keeping !keep', $replacements), + ), + ) + $form['settings']; $replacements = array( '!to' => drupal_render($form['settings']['destination']['copy_destination']['copy_destination_id']), @@ -911,7 +974,6 @@ function theme_backup_migrate_ui_quick_schedule_form_inline($form) { $form['settings']['destination']['copy']["#prefix"] = '
'; $form['settings']['destination']['copy']["#suffix"] = $replacements['!to'] . '
'; - unset($form['settings']['period']); unset($form['settings']['destination']['destination_id']); unset($form['settings']['destination']['copy_destination']); @@ -921,28 +983,26 @@ function theme_backup_migrate_ui_quick_schedule_form_inline($form) { return drupal_render_children($form); } - /** * Theme the dependent form section. */ function theme_backup_migrate_dependent($vars) { $element = $vars['element']; - drupal_add_js(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.js'); - drupal_add_css(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.css'); + drupal_add_js(drupal_get_path('module', 'backup_migrate') . '/backup_migrate.js'); + drupal_add_css(drupal_get_path('module', 'backup_migrate') . '/backup_migrate.css'); $id = drupal_html_id(implode('-', $element['#array_parents'])); $settings = array( 'backup_migrate' => array( - 'dependents' => array($id => array('dependent' => $id, 'dependencies' => $element['#dependencies'])) - ) + 'dependents' => array($id => array('dependent' => $id, 'dependencies' => $element['#dependencies'])), + ), ); drupal_add_js($settings, 'setting'); return '
' . drupal_render_children($element) . '
'; } - /** * Theme file list form widget. */ @@ -951,7 +1011,7 @@ function theme_backup_migrate_file_list($vars) { // Render the radios so they can be placed in the file list rendering. $vars['element']['#display_options']['form_elements'] = array(); - if ($vars['element']['files']) { + if (isset($vars['element']['files']) && count($vars['element']['files'])) { foreach (element_children($vars['element']['files']) as $id) { $vars['element']['files'][$id]['#title'] = ''; $vars['element']['#display_options']['form_elements'][$id] = render($vars['element']['files'][$id]); @@ -978,12 +1038,11 @@ function theme_backup_migrate_group($vars) { return $output; } - /** * The backup/export form. */ function backup_migrate_ui_manual_backup_form($form, &$form_state, $profile) { - drupal_add_js(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.js', array('type' => 'module', 'scope' => 'footer')); + drupal_add_js(drupal_get_path('module', 'backup_migrate') . '/backup_migrate.js', array('type' => 'module', 'scope' => 'footer')); $form = array(); @@ -1013,8 +1072,9 @@ function backup_migrate_ui_manual_backup_form($form, &$form_state, $profile) { $form += _backup_migrate_ui_backup_settings_form($profile); - // Make the source settings dependent on the source selected. - // Except don't because it looks super weird. + // Make the source settings dependent on the source selected. Except don't + // because it looks super weird. + // @code // foreach (element_children($form['sources']) as $source) { // $form['sources'][$source] = array( // '#type' => 'backup_migrate_dependent', @@ -1024,7 +1084,7 @@ function backup_migrate_ui_manual_backup_form($form, &$form_state, $profile) { // 'settings' => $form['sources'][$source] // ); // } - + // @endcode if (user_access('administer backup and migrate')) { $form['save_settings'] = array( "#type" => "checkbox", @@ -1081,9 +1141,10 @@ function backup_migrate_ui_manual_backup_form($form, &$form_state, $profile) { return _backup_migrate_ui_action_form($form, $form_state, 'backup'); } - /** - * Submit the form. Save the values as defaults if desired and output the backup file. + * Submits the form. + * + * Save the values as defaults if desired and output the backup file. */ function backup_migrate_ui_manual_backup_form_submit($form, &$form_state) { // Create a profile with the given settings. @@ -1110,12 +1171,13 @@ function backup_migrate_ui_manual_backup_form_submit($form, &$form_state) { } /** - * Alter a B&M action (backup/restore) form to allow for filters to add additional form steps. - **/ + * Alters a B&M action (backup/restore) form to allow for filters to add steps. + */ function _backup_migrate_ui_action_form($form, &$form_state, $op = 'backup') { $form['operation'] = array('#type' => 'value', '#value' => $op); - // If the form has been submitted at least once then see if there are additional form elements to add. + // If the form has been submitted at least once then see if there are + // additional form elements to add. if (!empty($form_state['storage']['values']) && !empty($form_state['values']['settings'])) { $page = _backup_migrate_filter_before_action_form($form_state['values']['settings'], $op); if (!empty($page)) { @@ -1140,9 +1202,11 @@ function _backup_migrate_ui_action_form($form, &$form_state, $op = 'backup') { } /** - * Allow filters, sources and destinations to present additional form elements - * to the end user before an action is submitted. - **/ + * Allows filters, sources and destinations to present additional form elements. + * + * The additional form elements will be shown to the user before an action is + * submitted. + */ function backup_migrate_ui_action_form_pre_validate($form, &$form_state) { if (!empty($form_state['storage']['values'])) { $form_state['values'] += $form_state['storage']['values']; @@ -1150,9 +1214,11 @@ function backup_migrate_ui_action_form_pre_validate($form, &$form_state) { } /** - * Allow filters, sources and destinations to present additional form elements - * to the end user before an action is submitted. - **/ + * Allows filters, sources and destinations to present additional form elements. + * + * The additional form elements will be shown to the user before an action is + * submitted. + */ function backup_migrate_ui_action_form_post_validate($form, &$form_state) { backup_migrate_include('filters'); @@ -1160,33 +1226,39 @@ function backup_migrate_ui_action_form_post_validate($form, &$form_state) { // See if there are extra form items to be processed. $form = _backup_migrate_filter_before_action_form($form_state['values']['settings'], $form_state['values']['operation']); if ($form) { - // First time through the form simply rebuild the form to show the additional fields. + // First time through the form simply rebuild the form to show the + // additional fields. if (empty($form_state['storage']['values'])) { $form_state['storage']['values'] = $form_state['values']; $form_state['rebuild'] = TRUE; } else { - backup_migrate_filters_before_action_form_validate($form_state['values']['settings'], $form_state['values']['operation'], $form, $form_state); + backup_migrate_filters_before_action_form_validate($form_state['values']['settings'], $form_state['values']['operation'], $form, $form_state); } } } } /** - * Allow filters, sources and destinations to present additional form elements - * to the end user before an action is submitted. - **/ + * Allows filters, sources and destinations to present additional form elements. + * + * The additional form elements will be shown to the user before an action is + * submitted. + */ function backup_migrate_ui_action_form_pre_submit($form, &$form_state) { if (!empty($form_state['storage']['values']) && !empty($form_state['values']['settings'])) { // Check if there are any form elements to submit. backup_migrate_filters_before_action_form_submit($form_state['values']['settings'], $form_state['values']['operation'], $form, $form_state); - // Clear the storage so the form can continue as normal + // Clear the storage so the form can continue as normal. unset($form_state['storage']); } } /** - * Retrieve an array of form items to be filled out before an action can continue. + * Retrieves form items to be filled out before an action can continue. + * + * @return array + * An array of form items. */ function _backup_migrate_filter_before_action_form($settings, $op) { $form = array(); @@ -1262,9 +1334,9 @@ function backup_migrate_ui_manual_restore_form() { $form['backup_migrate_restore_saved']['file'] = array( '#type' => 'backup_migrate_file_list', '#display_options' => array( - 'limit' => 5, - 'pager' => FALSE, - 'more' => BACKUP_MIGRATE_MENU_PATH . '/backups', + 'limit' => 5, + 'pager' => FALSE, + 'more' => BACKUP_MIGRATE_MENU_PATH . '/backups', 'operations' => FALSE, ), '#default_value' => $default, @@ -1290,7 +1362,7 @@ function backup_migrate_ui_manual_restore_form() { $form = array_merge_recursive($form, backup_migrate_filters_settings_form(backup_migrate_filters_settings_default('restore'), 'restore')); // Add the advanced fieldset if there are any fields in it. - if (@$form['advanced']) { + if (isset($form['advanced'])) { $form['advanced']['#type'] = 'fieldset'; $form['advanced']['#title'] = t('Advanced Options'); $form['advanced']['#collapsed'] = TRUE; @@ -1322,9 +1394,9 @@ function backup_migrate_ui_manual_restore_form_submit($form, &$form_state) { if ($values['from'] == 'upload' && $file = file_save_upload('backup_migrate_restore_upload', $validators)) { backup_migrate_perform_restore('upload', $file->uri, $values); } - else if ($values['from'] == 'saved' && $file = $values['file']) { + elseif ($values['from'] == 'saved' && $file = $values['file']) { list($destination, $file_id) = explode('/', $file, 2); - $file = + $file = backup_migrate_perform_restore($destination, $file_id, $form_state['values']); } @@ -1344,11 +1416,11 @@ function backup_migrate_ui_export_form($form, &$form_state, $item) { } /** - * List the previously created backups from accross multiple destinations. + * List the previously created backups from across multiple destinations. */ function backup_migrate_ui_saved_backups() { backup_migrate_include('profiles', 'destinations', 'sources'); - drupal_add_css(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.css'); + drupal_add_css(drupal_get_path('module', 'backup_migrate') . '/backup_migrate.css'); $remote = FALSE; $now = $fetch_time = time(); @@ -1365,7 +1437,12 @@ function backup_migrate_ui_saved_backups() { // Get the fetch link. if ($fetch_time < $now) { - $fetch = '
'. t('This listing was fetched !time ago. !refresh', array('!time' => format_interval($now - $fetch_time, 1), '!refresh' => l(t('fetch now'), $_GET['q'], array('query' => array('refresh' =>'true'))))) .'
'; + $fetch = '
' . t('This listing was fetched !time ago. !refresh', array( + '!time' => format_interval($now - $fetch_time, 1), + '!refresh' => l(t('fetch now'), $_GET['q'], array( + 'query' => array('refresh' => 'true'), + )), + )) . '
'; } // Render the list. @@ -1375,10 +1452,11 @@ function backup_migrate_ui_saved_backups() { // Add a remote backups warning if there are no remote backups. if (!$remote && user_access('administer backup and migrate')) { - drupal_set_message( - t('You do not have any offsite backups. For the highest level of protection, create an offsite destination or set up NodeSquirrel to save your backups to.', - array('!addurl' => url(BACKUP_MIGRATE_MENU_PATH . '/settings/destination/add'), - '!nsurl' => url(BACKUP_MIGRATE_MENU_PATH . '/nodesquirrel') + drupal_set_message( + t('You do not have any offsite backups. For the highest level of protection, create an offsite destination or set up NodeSquirrel to save your backups to.', + array( + '!addurl' => url(BACKUP_MIGRATE_MENU_PATH . '/settings/destination/add'), + '!nsurl' => url(BACKUP_MIGRATE_MENU_PATH . '/nodesquirrel'), )), 'warning', FALSE); } @@ -1386,11 +1464,11 @@ function backup_migrate_ui_saved_backups() { } /** - * List the previously created backups from accross multiple destinations. + * List the previously created backups from across multiple destinations. */ function backup_migrate_get_saved_backups($refresh = FALSE) { backup_migrate_include('profiles', 'destinations', 'sources'); - drupal_add_css(drupal_get_path('module', 'backup_migrate') .'/backup_migrate.css'); + drupal_add_css(drupal_get_path('module', 'backup_migrate') . '/backup_migrate.css'); $remote = FALSE; $now = $fetch_time = time(); @@ -1406,7 +1484,7 @@ function backup_migrate_get_saved_backups($refresh = FALSE) { $remote = TRUE; } - // Refresh the destination + // Refresh the destination. if ($refresh) { $destination->file_cache_clear(); } @@ -1429,6 +1507,27 @@ function backup_migrate_get_saved_backups($refresh = FALSE) { return array($files, $fetch_time, $remote); } +/** + * Helper function for byte conversion. + */ +function backup_migrate_to_bytes($from) { + $number = substr($from, 0, -1); + switch (strtoupper(substr($from, -1))) { + case "M": + return $number * pow(1024, 2); + + break; + + case "G": + return $number * pow(1024, 3); + + break; + + default: + return $number * pow(1024, 2); + } +} + /** * Perform a backup with the given settings. */ @@ -1436,12 +1535,41 @@ function backup_migrate_perform_backup(&$settings) { backup_migrate_include('destinations', 'files', 'filters'); timer_start('backup_migrate_backup'); - // If not in 'safe mode', increase the maximum execution time: - if (!ini_get('safe_mode') && strpos(ini_get('disable_functions'), 'set_time_limit') === FALSE && ini_get('max_execution_time') < 1200) { - set_time_limit(variable_get('backup_migrate_backup_max_time', 1200)); + _backup_migrate_set_timeout(); + + // Conditionally increase the memory limit + // Get the current php memory limit. + $php_memory_limit = ini_get('memory_limit'); + + // Convert the php memory limit to bytes. + $php_memory_limit_bytes = backup_migrate_to_bytes($php_memory_limit); + + // Get the current Backup Migrate memory limit. + // Hint: To further increase the memory limit used by Backup Migrate add one + // of the following line to the settings.php file: + // * $conf['backup_migrate_memory_limit'] = '512M'; + // * $conf['backup_migrate_memory_limit'] = '1G'; + $backup_migrate_memory_limit = variable_get('backup_migrate_memory_limit', '256M'); + + // Convert the Backup Migrate memory limit to bytes. + $backup_migrate_memory_limit_bytes = backup_migrate_to_bytes($backup_migrate_memory_limit); + + // If not in 'safe mode', increase the memory limit. + if (!ini_get('safe_mode') && $php_memory_limit_bytes < $backup_migrate_memory_limit_bytes) { + // Ensure ini_set() is not disabled. + if (!strpos(ini_get('disable_functions'), 'ini_set')) { + // Convert bytes to megabytes, add the 'M' unit. + $backup_migrate_memory_limit = round($backup_migrate_memory_limit_bytes / 1024 / 1024) . 'M'; + + // Update the php memory limit. + ini_set('memory_limit', $backup_migrate_memory_limit); + } + } + if (variable_get('backup_migrate_verbose')) { + _backup_migrate_message("Memory limit: %limit", array('%limit' => ini_get('memory_limit')), 'success'); } - // Confirm the destinations are valid + // Confirm the destinations are valid. $valid = backup_migrate_destination_confirm_destination($settings); if (!$valid) { backup_migrate_backup_fail("Could not run backup because a there was no valid destination to save to.", array(), $settings); @@ -1459,6 +1587,9 @@ function backup_migrate_perform_backup(&$settings) { // Register shutdown callback to deal with timeouts. register_shutdown_function('backup_migrate_shutdown', $settings); + if (variable_get('backup_migrate_verbose')) { + watchdog('backup_migrate', "Starting backup: %file", array('%file' => $file->filename())); + } $file = backup_migrate_filters_backup($file, $settings); if (!$file) { if (_backup_migrate_check_timeout()) { @@ -1470,13 +1601,19 @@ function backup_migrate_perform_backup(&$settings) { return FALSE; } + if (variable_get('backup_migrate_verbose')) { + watchdog('backup_migrate', "Preparing to save backup: %file", array('%file' => $file->filename())); + } $file = backup_migrate_destination_save_file($file, $settings); if (!$file) { backup_migrate_backup_fail("Could not run backup because the file could not be saved to the destination.", array(), $settings); return FALSE; } + if (variable_get('backup_migrate_verbose')) { + watchdog('backup_migrate', "Backup succeeded: %file", array('%file' => $file->filename())); + } - // Backup succeeded, + // Backup succeeded. $time = timer_stop('backup_migrate_backup'); $message = '%source backed up successfully to %file (%size) in destination %dest in !time. !action'; $params = array( @@ -1496,9 +1633,8 @@ function backup_migrate_perform_backup(&$settings) { } /** - * Format an elapsed time given in milleseconds. + * Formats an elapsed time given in milleseconds. */ - function backup_migrate_format_elapsed_time($time) { $ms = t('!time ms', array('!time' => $time)); if ($time < 1000) { @@ -1514,10 +1650,7 @@ function backup_migrate_perform_restore($destination_id, $file, $settings = arra backup_migrate_include('files', 'filters'); timer_start('backup_migrate_restore'); - // If not in 'safe mode', increase the maximum execution time: - if (!ini_get('safe_mode') && ini_get('max_execution_time') < variable_get('backup_migrate_backup_max_time', 1200)) { - set_time_limit(variable_get('backup_migrate_restore_max_time', 1200)); - } + _backup_migrate_set_timeout(); // Make the settings into a default profile. if (!is_object($settings)) { @@ -1564,7 +1697,7 @@ function backup_migrate_perform_restore($destination_id, $file, $settings = arra ); if ($destination && $destination->op('list files')) { $path = $destination->get_settings_path(); - $params['!links'] = t('Restore again', array('!restoreurl' => url($path . '/list/restorefile/' . $destination->get_id() .'/'. $file->file_id()))); + $params['!links'] = t('Restore again', array('!restoreurl' => url($path . '/list/restorefile/' . $destination->get_id() . '/' . $file->file_id()))); } backup_migrate_restore_succeed($message, $params, $settings); } @@ -1588,7 +1721,7 @@ function backup_migrate_backup_fail($message, $params, $settings) { } /** - * Clean up when a backup operation suceeds. + * Clean up when a backup operation succeeds. */ function backup_migrate_backup_succeed($message, $params, $settings) { backup_migrate_include('filters', 'files'); @@ -1610,7 +1743,7 @@ function backup_migrate_restore_fail($message, $params, $settings) { } /** - * Clean up when a restore operation suceeds. + * Clean up when a restore operation succeeds. */ function backup_migrate_restore_succeed($message, $params, $settings) { backup_migrate_include('filters', 'files'); @@ -1620,93 +1753,84 @@ function backup_migrate_restore_succeed($message, $params, $settings) { return FALSE; } - /** - * Cleanup after a success or failure. + * Cleans up after a success or failure. */ function backup_migrate_cleanup() { - // Check that the cleanup function exists. If it doesn't then we probably didn't create any files to be cleaned up. + // Check that the cleanup function exists. If it doesn't then we probably + // didn't create any files to be cleaned up. if (function_exists('_backup_migrate_temp_files_delete')) { _backup_migrate_temp_files_delete(); } } - /** - * Shutdown callback. Called when the script terminates even if the script timed out. + * Shutdown callback. + * + * Called when the script terminates even if the script timed out. */ function backup_migrate_shutdown($settings) { - // If we ran out of time, set an error so the user knows what happened + // If we ran out of time, set an error so the user knows what happened. if (_backup_migrate_check_timeout()) { backup_migrate_cleanup(); backup_migrate_backup_fail('The operation timed out. Try increasing your PHP max_execution_time setting.', array('!url' => 'http://www.php.net/manual/en/info.configuration.php#ini.max-execution-time'), $settings); - // The session will have already been written and closed, so we need to write any changes directly. + // The session will have already been written and closed, so we need to + // write any changes directly. _drupal_session_write(session_id(), session_encode()); // Add a redirect or we'll just get whitescreened. drupal_goto(BACKUP_MIGRATE_MENU_PATH); } } - - -/* Actions/Workflow integration */ +/** + * Action to backup the drupal site. + * + * Requires actions.module. + * + * function action_backup_migrate_backup($op, $edit = array()) { + * switch ($op) { + * case 'do': + * _backup_migrate_backup_with_defaults(); + * watchdog('action', 'Backed up database'); + * break;. + * * Case 'metadata': + * return array( + * 'description' => t('Backup the database with the default settings'), + * 'type' => t('Backup and Migrate'), + * 'batchable' => TRUE, + * 'configurable' => FALSE, + * ); + * * // Return an HTML config form for the action. + * * case 'form': + * return ''; + * * // Validate the HTML form. + * * case 'validate': + * return TRUE; + * * // Process the HTML form to store configuration. + * * case 'submit': + * return ''; + * } + * }. + */ /** - * Action to backup the drupal site. Requires actions.module. -function action_backup_migrate_backup($op, $edit = array()) { - switch ($op) { - case 'do': - _backup_migrate_backup_with_defaults(); - watchdog('action', 'Backed up database'); - break; - - case 'metadata': - return array( - 'description' => t('Backup the database with the default settings'), - 'type' => t('Backup and Migrate'), - 'batchable' => TRUE, - 'configurable' => FALSE, - ); - - // Return an HTML config form for the action. - - case 'form': - return ''; - - // Validate the HTML form. - - case 'validate': - return TRUE; - - // Process the HTML form to store configuration. - - case 'submit': - return ''; - } -} - */ - -/* * Implements hook_action_info(). -function backup_migrate_action_info() { - return array( - 'backup_migrate_action_backup' => array( - 'label' => t('Backup the database'), - 'description' => t('Backup the database with the default settings.'), - ), - ); -} */ - -/* +// function backup_migrate_action_info() { +// return array( +// 'backup_migrate_action_backup' => array( +// 'label' => t('Backup the database'), +// 'description' => t('Backup the database with the default settings.'), +// ), +// ); +// } +/** * Action callback. */ function backup_migrate_action_backup() { _backup_migrate_backup_with_defaults(); } -/* Utilities */ - /** * Backup the database with the default settings. */ @@ -1720,7 +1844,9 @@ function _backup_migrate_backup_with_defaults($destination_id = "manual") { } /** - * Helper function to set a drupal message and watchdog message depending on whether the module is being run interactively. + * Helper function to set a Drupal message and watchdog message. + * + * Depends on whether the module is being run interactively. */ function _backup_migrate_message($message, $replace = array(), $type = 'status') { // Only set a message if there is a callback handler to handle the message. @@ -1728,23 +1854,32 @@ function _backup_migrate_message($message, $replace = array(), $type = 'status') $callback($message, $replace, $type); } - // Store the message in case it's needed (for the status notification filter for example). + // Store the message in case it's needed (for the status notification filter + // for example). _backup_migrate_messages($message, $replace, $type); } /** - * Helper function to set a drupal message and watchdog message depending on whether the module is being run interactively. + * Helper function to set a Drupal message and watchdog message. + * + * Depends on whether the module is being run interactively. */ function _backup_migrate_messages($message = NULL, $replace = array(), $type = 'status') { static $messages = array(); if ($message) { - $messages[] = array('message' => $message, 'replace' => $replace, 'type' => $type); + $messages[] = array( + 'message' => $message, + 'replace' => $replace, + 'type' => $type, + ); } return $messages; } /** - * Send a message to the browser. The normal type of message handling for interactive use. + * Sends a message to the browser. + * + * The normal type of message handling for interactive use. */ function _backup_migrate_message_browser($message, $replace, $type) { // Log the message as well for admins. @@ -1759,7 +1894,7 @@ function _backup_migrate_message_browser($message, $replace, $type) { } /** - * Log message if we are in a non-interactive mode such as a cron run. + * Logs a message if we are in a non-interactive mode such as a cron run. */ function _backup_migrate_message_log($message, $replace, $type) { // We only want to log the errors or successful completions. @@ -1769,7 +1904,7 @@ function _backup_migrate_message_log($message, $replace, $type) { } /** - * Set or retrieve a message handler. + * Sets or retrieves a message handler. */ function _backup_migrate_message_callback($callback = NULL) { static $current_callback = '_backup_migrate_message_log'; @@ -1779,6 +1914,29 @@ function _backup_migrate_message_callback($callback = NULL) { return $current_callback; } +/** + * Increase the execution timeout, and also the database connection timeout. + */ +function _backup_migrate_set_timeout() { + // If not in 'safe mode', increase the maximum execution time: + $backup_max_time = variable_get('backup_migrate_backup_max_time', 1200); + if (!ini_get('safe_mode') && ini_get('max_execution_time') < $backup_max_time) { + drupal_set_time_limit($backup_max_time); + } + + // Also increase the time mysql keeps the connection open. + if (Database::getConnection() instanceof DatabaseConnection_mysql) { + $mysql_wait_timeout = db_query('SELECT @@session.wait_timeout')->fetchField(); + if ($mysql_wait_timeout < $backup_max_time) { + // This does not work with placeholders, so sanitize by casting to int. + db_query('SET SESSION wait_timeout = ' . (int) $backup_max_time); + } + } +} + +/** + * Determines if time for a page execution has run out. + */ function _backup_migrate_check_timeout() { static $timeout; @@ -1786,41 +1944,47 @@ function _backup_migrate_check_timeout() { if (ini_get('max_execution_time') == 0) { return FALSE; } + // Figure out when we should stop execution. if (!$timeout) { - $timeout = (!empty($_SERVER['REQUEST_TIME']) ? $_SERVER['REQUEST_TIME'] : time()) + ini_get('max_execution_time') - variable_get('backup_migrate_timeout_buffer', 5); + $backup_max_time = max(ini_get('max_execution_time'), variable_get('backup_migrate_backup_max_time', 1200)); + $timeout = (!empty($_SERVER['REQUEST_TIME']) ? $_SERVER['REQUEST_TIME'] : time()) + $backup_max_time - variable_get('backup_migrate_timeout_buffer', 5); } + return (time() > $timeout); } /** - * Convert an associated array to an ini format string. + * Converts an associated array to an ini format string. */ function _backup_migrate_array_to_ini($data, $prefix = '') { - $content = ""; + $content = ''; foreach ($data as $key => $val) { if ($prefix) { - $key = $prefix . '[' . $key .']'; + $key = $prefix . '[' . $key . ']'; } if (is_array($val)) { $content .= _backup_migrate_array_to_ini($val, $key); } else { - $content .= $key . " = \"". $val ."\"\n"; + $content .= $key . ' = "' . $val . "\"\n"; } } return $content; } /** - * Execute a command line command. Returns false if the function failed. + * Execute a command line command. + * + * @return bool + * Returns FALSE if the function failed. */ function backup_migrate_exec($command, $args = array()) { if (!function_exists('exec') || ini_get('safe_mode')) { return FALSE; } - // Escape the arguments + // Escape the arguments. foreach ($args as $key => $arg) { $args[$key] = escapeshellarg($arg); } @@ -1828,14 +1992,16 @@ function backup_migrate_exec($command, $args = array()) { $output = $result = NULL; // Run the command. - exec($command . ' 2>&1', $output, $result); + exec($command, $output, $result); return $result == 0; } /** - * A custom version of format size which treats 1GB as 1000 MB rather than 1024 MB - * This is a more standard and expected version for storage (as opposed to memory). + * Format a size, treating 1GB as 1000 MB rather than 1024 MB. + * + * This is a more standard and expected version for storage (as opposed to + * memory). */ function backup_migrate_format_size($size, $langcode = LANGUAGE_NONE) { $precision = 2; @@ -1862,7 +2028,45 @@ function backup_migrate_format_size($size, $langcode = LANGUAGE_NONE) { $size = ceil($size / 1000); $string = '@size TB'; } - return t($string, array('@size' => round($size/$multiply, $precision)), array('langcode' => $langcode)); + return t($string, array('@size' => round($size / $multiply, $precision)), array('langcode' => $langcode)); } - +} + +/** + * Implements hook_mail(). + */ +function backup_migrate_mail($key, &$message, $params) { + if ($key == 'backup_succeed') { + $message['id'] = 'backup_migrate_backup_succeed'; + $message['subject'] = t('!site backup succeeded', array('!site' => variable_get('site_name', 'Drupal site'))); + $message['body'][] = $params['body']; + } + elseif ($key == 'backup_fail') { + $message['id'] = 'backup_migrate_backup_fail'; + $message['subject'] = t('!site backup failed', array('!site' => variable_get('site_name', 'Drupal site'))); + $message['body'][] = $params['body']; + } +} + +/** + * Identify the current memory usage. + * + * Uses the same formula as the Devel module. + * + * @return int + * The current memory usage, in megabytes. + */ +function backup_migrate_get_peak_memory_usage() { + return round(memory_get_peak_usage(TRUE) / 1024 / 1024, 2); +} + +/** + * Returns the "name" element from an array. + * + * Callback for array_map() within + * backup_migrate_destination_db_mysql::_restore_db_from_file() and + * backup_migrate_source_db_mysql::_restore_db_from_file(). + */ +function backup_migrate_array_name_value($a) { + return $a["name"]; } diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/backup_migrate.drush.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/backup_migrate.drush.inc index 6a44eb6..a321f22 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/backup_migrate.drush.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/backup_migrate.drush.inc @@ -1,11 +1,10 @@ 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); } diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/crud.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/crud.inc index c6b0dcf..877427f 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/crud.inc +++ b/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'; + 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. + * 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("   ", $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]); } + } diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.browser.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.browser.inc index af75649..707f2f1 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.browser.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.browser.inc @@ -1,6 +1,5 @@ $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(); } -} +} diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.db.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.db.inc index 9a2e23a..e44308e 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.db.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.db.inc @@ -1,6 +1,5 @@ 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 do not change these settings unless you know what you're doing."); $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'; } + } diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.db.mysql.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.db.mysql.inc index 77ccfa4..35a6d1b 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.db.mysql.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.db.mysql.inc @@ -1,5 +1,9 @@ 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; + + $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) . "'"; } - // 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; + + // 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 */; "; } + } diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.email.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.email.inc index c7d426d..192d8cc 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.email.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.email.inc @@ -1,6 +1,5 @@ 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); } + } diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.file.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.file.inc index c6f22b5..b943d45 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.file.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.file.inc @@ -1,6 +1,5 @@ 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'))); } -} +} diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.ftp.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.ftp.inc index 34f24e0..2b94407 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.ftp.inc +++ b/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]); } } diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.inc index 792e312..94bdcaa 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.inc @@ -1,6 +1,5 @@ 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 NodeSquirrel 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 Amazon S3 account.', 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 = '
'. 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'))))) .'
'; + $fetch = '
' . 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'))))) . '
'; } $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 .= '
'. t('Source:') .' ' . check_plain($info['bam_sourcename']) . '
'; + $description .= '
' . t('Source:') . ' ' . check_plain($info['bam_sourcename']) . '
'; } // 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 .= '
' . t('Tags:') . ' ' . $tags . '
'; } // Add the other info. if (!empty($info['bam_other_safe'])) { foreach ($info['bam_other_safe'] as $label => $data) { - $description .= '
' . $label . ' ' . $data . '
'; + $description .= '
' . $label . ' ' . $data . '
'; } } @@ -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 .= '
'. t('Source:') .' ' . check_plain($info['bam_sourcename']) . '
'; + $description .= '
' . t('Source:') . ' ' . check_plain($info['bam_sourcename']) . '
'; } // 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 .= '
' . t('Tags:') . ' ' . $tags . '
'; } // Add the other info. if (!empty($info['bam_other_safe'])) { foreach ($info['bam_other_safe'] as $label => $data) { - $description .= '
' . $label . ' ' . $data . '
'; + $description .= '
' . $label . ' ' . $data . '
'; } } @@ -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. Always test your backups on a non-production server!', 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. Always test your backups on a non-production server!', 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. Always test your backups on a non-production server!'), '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? This action cannot be undone.', 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? This action cannot be undone.', 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; + 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; - var $destination_type = ""; - var $supported_ops = array(); + 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 = '
'. l($type['type_name'], $path . "/add/$type_url_str", array('attributes' => array('title' => t('Add a new @s destination.', array('@s' => $type['type_name']))))) .'
'; - $out .= '
'. filter_xss_admin($type['description']) .'
'; + $out = '
' . l($type['type_name'], $path . "/add/$type_url_str", array('attributes' => array('title' => t('Add a new @s destination.', array('@s' => $type['type_name']))))) . '
'; + $out .= '
' . filter_xss_admin($type['description']) . '
'; if (!empty($type['local'])) { $items['local']['items'][] = $out; @@ -1027,7 +1022,7 @@ class backup_migrate_destination extends backup_migrate_location { $output = '

' . t('Choose the type of destination you would like to create:') . '

'; foreach ($items as $group) { if (count($group['items'])) { - $group['body'] = '
'. implode('', $group['items']) .'
'; + $group['body'] = '
' . implode('', $group['items']) . '
'; $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); } -} +} diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.nodesquirrel.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.nodesquirrel.inc index c6281fb..4634bf1 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.nodesquirrel.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.nodesquirrel.inc @@ -8,7 +8,7 @@ define('NODESQUIRREL_SECRET_KEY_PATTERN', '/^[0-9a-f]{32}\:?[0-9a-f]{32}$/'); /** - * Check that a nodesquirel key is valid. + * Checks that a nodesquirel key is valid. */ function backup_migrate_nodesquirrel_check_secret_key($secret_key) { if ($destination = backup_migrate_nodesquirrel_get_destination($secret_key)) { @@ -20,7 +20,7 @@ function backup_migrate_nodesquirrel_check_secret_key($secret_key) { } /** - * Get the NS destination for the given key. + * Gets the NS destination for the given key. */ function backup_migrate_nodesquirrel_get_destination($secret_key) { if ($secret_key) { @@ -32,9 +32,8 @@ function backup_migrate_nodesquirrel_get_destination($secret_key) { return NULL; } - /** - * Get a helper link to activate a site and create a tree. + * Gets a helper link to activate a site and create a tree. */ function backup_migrate_nodesquirrel_get_activate_help_text() { $activate_link = backup_migrate_nodesquirrel_get_activate_link(); @@ -42,20 +41,26 @@ function backup_migrate_nodesquirrel_get_activate_help_text() { '#type' => 'item', '#title' => t('Need a Secret Key?'), '#markup' => t('Visit !nodesquirrel.', array('!nodesquirrel' => $activate_link)), - '#description' => t('Don\'t worry if you don\'t have an account yet. You can create one when you get there.'), + '#description' => t("Don't worry if you don't have an account yet. You can create one when you get there."), ); } /** - * Get a helper link to activate a site and create a tree. + * Gets a helper link to activate a site and create a tree. */ function backup_migrate_nodesquirrel_get_activate_link() { - $activate_link = l('nodesquirrel.com/activate', variable_get('nodesquirrel_activate_url', 'http://manage.nodesquirrel.com/activate'), array('query' => array('url' => url('', array('absolute' => TRUE)), 'email' => variable_get('site_mail', ''), 'configure' => url($_GET['q'], array('absolute' => TRUE))))); + $activate_link = l('nodesquirrel.com/activate', variable_get('nodesquirrel_activate_url', 'https://manage.nodesquirrel.com/activate'), array( + 'query' => array( + 'url' => url('', array('absolute' => TRUE)), + 'email' => variable_get('site_mail', ''), + 'configure' => url($_GET['q'], array('absolute' => TRUE)), + ), + )); return $activate_link; } /** - * Get a helper link to activate a site and create a tree. + * Gets a helper link to activate a site and create a tree. */ function backup_migrate_nodesquirrel_get_manage_link($destination) { $url = variable_get('nodesquirrel_manage_url', 'https://manage.nodesquirrel.com') . '/backups/' . $destination->_get_destination(); @@ -63,7 +68,7 @@ function backup_migrate_nodesquirrel_get_manage_link($destination) { } /** - * Get a helper link to activate a site and create a tree. + * Gets a helper link to activate a site and create a tree. */ function backup_migrate_nodesquirrel_get_plan_link() { $url = variable_get('nodesquirrel_manage_url', 'https://manage.nodesquirrel.com') . '/plan'; @@ -71,14 +76,13 @@ function backup_migrate_nodesquirrel_get_plan_link() { } /** - * Get the path to the nodesquirrel settings tab. + * Gets the path to the nodesquirrel settings tab. */ function backup_migrate_nodesquirrel_settings_path() { $path = BACKUP_MIGRATE_MENU_PATH . '/settings/destinations/nodesquirrel/edit'; return $path; } - /** * NodeSquirrel settings page callback. */ @@ -108,6 +112,13 @@ function backup_migrate_nodesquirrel_credentials_settings_form($key = '', $statu $form['nodesquirrel_credentials']['secret_key_help'] = backup_migrate_nodesquirrel_get_activate_help_text(); } + $form['nodesquirrel_credentials']['nodesquirrel_failover'] = array( + '#type' => 'checkbox', + '#title' => t('Connection failover by disabling SSL certification validation'), + '#description' => t("If normal connections to NodeSquirrel fail due incompatibility between this server's PHP installation and the destination server, use this option to disable validating the destination's SSL certificate.
Note: this has security implications and should be the last option taken; it probably would be safer to update the server's PHP."), + '#default_value' => variable_get('nodesquirrel_failover', FALSE), + ); + return $form; } @@ -124,8 +135,11 @@ function backup_migrate_nodesquirrel_info_form() { $form['nodesquirrel_info']['intro'] = array( '#type' => 'markup', - '#markup' => t('

NodeSquirrel is the cloud backup service for Drupal built by the maintainers of Backup and Migrate. It is secure, reliable and affordable.

NodeSquirrel is a paid service and profits help support Backup and Migrate.

Find out more at !nodesquirrel

', array('!nodesquirrel' => l('nodesquirrel.com', 'http://www.nodesquirrel.com'), '!add' => l(t('add other offsite destinations'), BACKUP_MIGRATE_MENU_PATH . '/destination/list/add'), '!bam' => l(t('Backup and Migrate project page'), 'http://drupal.org/project/backup_migrate'))), - ); + '#markup' => t('

NodeSquirrel is the cloud backup service built by the maintainers of Backup and Migrate.

NodeSquirrel is now free for all Drupal sites, courtesy of Pantheon.

Create your free account at !signup

', array( + '!nodesquirrel' => l('nodesquirrel.com', 'http://www.nodesquirrel.com'), + '!signup' => backup_migrate_nodesquirrel_get_activate_link(), + )), + ); return $form; } @@ -149,7 +163,7 @@ function backup_migrate_nodesquirrel_status_form($key, $destination, $status) { if ($key && empty($status)) { $form['nodesquirrel_status']['status']['#markup'] = t('Your secret key does not seem to be valid. Please check that you entered it correctly or visit !ns to generate a new key.', array('!ns' => backup_migrate_nodesquirrel_get_activate_link())); } - else if (!empty($destination) && is_array($status)) { + elseif (!empty($destination) && is_array($status)) { if (!empty($status['lifetime_backups_used']) && !empty($status['lifetime_backups']) && $status['lifetime_backups_used'] >= $status['lifetime_backups']) { $form['nodesquirrel_status']['status']['#markup'] = t('Your !num backup trial has expired. Visit !link to continue backing up.', array('!num' => $status['lifetime_backups'], '!link' => backup_migrate_nodesquirrel_get_plan_link())); } @@ -163,24 +177,22 @@ function backup_migrate_nodesquirrel_status_form($key, $destination, $status) { $form['nodesquirrel_status']['plan_name'] = array( '#type' => 'item', '#title' => t('Current Plan'), - '#markup' => check_plain($status['plan_name']) + '#markup' => check_plain($status['plan_name']), ); - if (isset($status['plan_id']) && strpos($status['plan_id'], 'trial') !== FALSE) { - if (isset($status['lifetime_backups']) && isset($status['lifetime_backups_used'])) { - $remains = $status['lifetime_backups'] - $status['lifetime_backups_used']; - $remains = $remains > 0 ? $remains : t('none'); - $form['nodesquirrel_status']['plan_name']['#markup'] .= ' ' . t('(@remains remaining of @backups backup trial)', array('@backups' => $status['lifetime_backups'], '@remains' => $remains)); - } + if (!empty($status['lifetime_backups']) && isset($status['lifetime_backups_used'])) { + $remains = $status['lifetime_backups'] - $status['lifetime_backups_used']; + $remains = $remains > 0 ? $remains : t('none'); + $form['nodesquirrel_status']['plan_name']['#markup'] .= ' ' . t('(@remains remaining of @backups backup trial)', array('@backups' => $status['lifetime_backups'], '@remains' => $remains)); + } - if (isset($status['lifespan']) && isset($status['age']) && $status['lifespan'] > 0) { - $remains = ceil(($status['lifespan'] - $status['age']) / 86400); - if ($remains <= 0) { - $form['nodesquirrel_status']['plan_name']['#markup'] .= ' ' . t('(Your !span day trial has expired.)', array('!span' => ceil($status['lifespan'] / 86400))); - } - else { - $form['nodesquirrel_status']['plan_name']['#markup'] .= ' ' . format_plural($remains, '(1 day remaining)', '(!span days remaining)', array('!span' => ceil($remains))); - } + if (!empty($status['lifespan']) && isset($status['age']) && $status['lifespan'] > 0) { + $remains = ceil(($status['lifespan'] - $status['age']) / 86400); + if ($remains <= 0) { + $form['nodesquirrel_status']['plan_name']['#markup'] .= ' ' . t('(Your !span day trial has expired.)', array('!span' => ceil($status['lifespan'] / 86400))); + } + else { + $form['nodesquirrel_status']['plan_name']['#markup'] .= ' ' . format_plural($remains, '(1 day remaining)', '(!span days remaining)', array('!span' => ceil($remains))); } } @@ -190,7 +202,7 @@ function backup_migrate_nodesquirrel_status_form($key, $destination, $status) { $form['nodesquirrel_status']['backups_used'] = array( '#type' => 'item', '#title' => t('Number of Stored Backups'), - '#markup' => $status['backups_used'] == 0 ? t('None') : number_format($status['backups_used']) + '#markup' => $status['backups_used'] == 0 ? t('None') : number_format($status['backups_used']), ); } @@ -198,7 +210,10 @@ function backup_migrate_nodesquirrel_status_form($key, $destination, $status) { $form['nodesquirrel_status']['last_backup'] = array( '#type' => 'item', '#title' => t('Last Backup'), - '#markup' => empty($status['last_backup']) ? t('Never') : t('!date (!ago ago)', array('!date' => format_date($status['last_backup'], 'small'), '!ago' => format_interval(time() - $status['last_backup'], 1))) + '#markup' => empty($status['last_backup']) ? t('Never') : t('!date (!ago ago)', array( + '!date' => format_date($status['last_backup'], 'small'), + '!ago' => format_interval(time() - $status['last_backup'], 1), + )), ); } if ($status['bytes_per_locker']) { @@ -206,14 +221,18 @@ function backup_migrate_nodesquirrel_status_form($key, $destination, $status) { $form['nodesquirrel_status']['space'] = array( '#type' => 'item', '#title' => t('Storage Space'), - '#markup' => t('!used used of !total (!remaining remaining)', array('!used' => backup_migrate_format_size($status['bytes_used']), '!total' => backup_migrate_format_size($status['bytes_per_locker']), '!remaining' => backup_migrate_format_size(max(0, $status['bytes_per_locker'] - $status['bytes_used'])))) + '#markup' => t('!used used of !total (!remaining remaining)', array( + '!used' => backup_migrate_format_size($status['bytes_used']), + '!total' => backup_migrate_format_size($status['bytes_per_locker']), + '!remaining' => backup_migrate_format_size(max(0, $status['bytes_per_locker'] - $status['bytes_used'])), + )), ); } else { $form['nodesquirrel_status']['space'] = array( '#type' => 'item', '#title' => t('Total Storage Space'), - '#markup' => t('!total', array('!total' => backup_migrate_format_size($status['bytes_per_locker']))) + '#markup' => t('!total', array('!total' => backup_migrate_format_size($status['bytes_per_locker']))), ); } } @@ -235,21 +254,25 @@ function backup_migrate_nodesquirrel_schedule_settings_form($destination, $statu // If the schedule has been overriden it must be edited in the schedule tab. $schedule = backup_migrate_crud_get_item('schedule', 'nodesquirrel'); - $default = 60*60*24; + $default = 60 * 60 * 24; $form = array(); $form['nodesquirrel_schedule'] = array( '#type' => 'fieldset', '#title' => t('Backup Schedule'), - '#description' => t('Set up a schedule to back up your site to NodeSquirrel. You can customize this schedule or add additional schedules in the !schedule.', array('!schedule' => l(t('Schedules tab'), BACKUP_MIGRATE_MENU_PATH . '/schedule'), '!cron' => l(t('cron'), 'http://drupal.org/cron'))), + '#description' => t('Set up a schedule to back up your site to NodeSquirrel. You can customize this schedule or add additional schedules in the !schedule.', array( + '!schedule' => l(t('Schedules tab'), BACKUP_MIGRATE_MENU_PATH . '/schedule'), + '!cron' => l(t('cron'), 'http://drupal.org/cron'), + )), ); $key = 'nodesquirrel_schedule'; $form['nodesquirrel_schedule'][$key] = array(); $defaults = array( - 'period' => empty($schedule) ? variable_get('nodesquirrel_schedule', 60*60*24) : $schedule->get('period'), + 'period' => empty($schedule) ? variable_get('nodesquirrel_schedule', 60 * 60 * 24) : $schedule->get('period'), 'enabled' => empty($schedule) ? variable_get('nodesquirrel_schedule_enabled', TRUE) : $schedule->get('enabled'), 'source_id' => empty($schedule) ? variable_get('nodesquirrel_schedule_source_id', 'db') : $schedule->get('source_id'), + 'keep' => empty($schedule) ? variable_get('nodesquirrel_schedule_keep', BACKUP_MIGRATE_SMART_DELETE) : $schedule->get('keep'), ); $form['nodesquirrel_schedule'][$key]['nodesquirrel_schedule_enabled'] = array( @@ -265,9 +288,9 @@ function backup_migrate_nodesquirrel_schedule_settings_form($destination, $statu ); $form['nodesquirrel_schedule'][$key]['settings']['nodesquirrel_schedule_source_id'] = _backup_migrate_get_source_pulldown($defaults['source_id']); $options = array( - (60*60) => t('Once an hour'), - (60*60*24) => t('Once a day'), - (60*60*24*7) => t('Once a week'), + (60 * 60) => t('Once an hour'), + (60 * 60 * 24) => t('Once a day'), + (60 * 60 * 24 * 7) => t('Once a week'), ); $period = $defaults['period']; if (!isset($options[$period])) { @@ -279,6 +302,12 @@ function backup_migrate_nodesquirrel_schedule_settings_form($destination, $statu '#options' => $options, '#default_value' => $period, ); + $form['nodesquirrel_schedule'][$key]['settings']['nodesquirrel_schedule_keep'] = array( + '#type' => 'checkbox', + '#title' => t('Automatically delete old backups'), + '#return_value' => $defaults['keep'] == BACKUP_MIGRATE_KEEP_ALL ? BACKUP_MIGRATE_SMART_DELETE : $defaults['keep'], + '#default_value' => $defaults['keep'], + ); return $form; } @@ -292,8 +321,8 @@ function backup_migrate_nodesquirrel_settings_form($form_state) { $key = variable_get('nodesquirrel_secret_key', ''); $status = array(); - if ($destination = backup_migrate_nodesquirrel_get_destination($key)) { - $status = $destination->check_limits(); + if ($destination = backup_migrate_nodesquirrel_get_destination($key)) { + $status = $destination->check_limits(); } $form += backup_migrate_nodesquirrel_info_form(); @@ -304,32 +333,34 @@ function backup_migrate_nodesquirrel_settings_form($form_state) { return system_settings_form($form); } - /** * A destination for sending database backups to the NodeSquirel backup service. * * @ingroup backup_migrate_destinations */ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination { - var $supported_ops = array('scheduled backup', 'manual backup', 'remote backup', 'restore', 'list files', 'configure', 'delete'); - var $cache_files = TRUE; - // Don't generate a metadata file as NodeSquirrel can save metadata natively. - var $save_metadata = FALSE; - + public $supported_ops = array( + 'scheduled backup', + 'manual backup', + 'remote backup', + 'restore', + 'list files', + 'configure', + 'delete', + ); + public $cache_files = TRUE; /** - * Get the destination name. Provide a default. + * Don't generate a metadata file as NodeSquirrel can save metadata natively. */ - function get_name() { - if (empty($this->name)) { - return t('NodeSquirrel'); - } - return $this->name; + public $save_metadata = FALSE; + public function __construct($params = array()) { + parent::__construct($params + array('name' => t('NodeSquirrel'))); } /** - * Get the menu items for manipulating this type. + * Gets the menu items for manipulating this type. */ - function get_menu_items() { + public function get_menu_items() { $items = array(); $path = BACKUP_MIGRATE_MENU_PATH . '/nodesquirrel'; $items[$path] = array( @@ -344,9 +375,9 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination } /** - * Declare any mysql databases defined in the settings.php file as a possible destination. + * Declares any MySQL databases defined in the settings.php file as a possible destination. */ - function destinations() { + public function destinations() { $out = array(); $out['nodesquirrel'] = backup_migrate_create_destination('nodesquirrel', array('machine_name' => 'nodesquirrel')); if ($secret_key = variable_get('nodesquirrel_secret_key', '')) { @@ -361,7 +392,7 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination /** * Returns a form to be completed before saving to the destination can be complete. */ - function before_backup_form($settings) { + public function before_backup_form($settings) { $form = array(); $key = $this->settings('secret_key'); if (empty($key)) { @@ -372,9 +403,9 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination } /** - * Validate the before_backup_form form. + * Validates the before_backup_form form. */ - function before_backup_form_validate($settings, $form, $form_state) { + public function before_backup_form_validate($settings, $form, $form_state) { if (isset($form_state['values']['nodesquirrel_secret_key'])) { $key = trim($form_state['values']['nodesquirrel_secret_key']); if ($error = $this->vaidate_key($key)) { @@ -384,32 +415,37 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination } /** - * Submit the before_backup_form form. + * Submits the before_backup_form form. */ - function before_backup_form_submit($settings, $form, $form_state) { + public function before_backup_form_submit($settings, $form, $form_state) { if (isset($form_state['values']['nodesquirrel_secret_key'])) { $this->settings['secret_key'] = $form_state['values']['nodesquirrel_secret_key']; - variable_set('nodesquirrel_secret_key', $this->settings['secret_key']); + variable_set('nodesquirrel_secret_key', $this->settings['secret_key']); } } - /** - * Save to the NodeSquirrel destination. + * Saves to the NodeSquirrel destination. */ - function save_file($file, $settings) { + public function save_file($file, $settings) { if ($destination = $this->_get_destination()) { - srand((double)microtime()*1000000); + srand((double) microtime() * 1000000); $filename = $file->filename(); $filesize = filesize($file->filepath()); - $ticket = $this->_xmlrpc('backups.getUploadTicket', array($destination, $filename, $filesize, $file->file_info)); + $ticket = $this->_xmlrpc('backups.getUploadTicket', array( + $destination, + $filename, + $filesize, + $file->file_info, + )); if ($ticket) { - + $url = $ticket['url']; - - // If the ticket requires authentication add our username/password to the url. + + // If the ticket requires authentication add our username/password to + // the url. if (!empty($ticket['auth']) && $ticket['auth'] = 'basic') { $parts = parse_url($ticket['url']); list($parts['user'], $parts['pass']) = $this->get_user_pass(); @@ -420,13 +456,15 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination if ($out->code == 200) { // Confirm the upload. - $confirm = $this->_xmlrpc('backups.confirmUpload', array($destination, $filename, $filesize)); + $confirm = $this->_xmlrpc('backups.confirmUpload', array( + $destination, + $filename, + $filesize, + )); if ($confirm['success']) { // Set a message with a link to the manage console. - $url = variable_get('nodesquirrel_manage_url', 'http://manage.nodesquirrel.com') . '/backups/' . $this->_get_destination(); - _backup_migrate_message('Your backup has been saved to your NodeSquirrel account. View it at !account', array('!account' => l($url, $url))); - + _backup_migrate_message('Your backup has been saved to your NodeSquirrel account. View it at !account', array('!account' => backup_migrate_nodesquirrel_get_manage_link($this))); return $file; } else { @@ -438,7 +476,7 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination _backup_migrate_message('The NodeSquirrel server returned the following error: %err', array('%err' => $error), 'error'); } } - else if ($err = xmlrpc_error()) { + elseif ($err = xmlrpc_error()) { // XMLRPC errors are already handled by the server function below. } else { @@ -449,9 +487,9 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination } /** - * Load from the NodeSquirrel destination. + * Loads from the NodeSquirrel destination. */ - function load_file($file_id) { + public function load_file($file_id) { if ($destination = $this->_get_destination()) { backup_migrate_include('files'); $file = new backup_file(array('filename' => $file_id)); @@ -459,14 +497,15 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination $ticket = $this->_xmlrpc('backups.getDownloadTicket', array($destination, $file_id)); if ($ticket && $url = $ticket['url']) { - // If the ticket requires authentication add our username/password to the url. + // If the ticket requires authentication add our username/password to + // the url. if (!empty($ticket['auth']) && $ticket['auth'] = 'basic') { $parts = parse_url($ticket['url']); $parts['user'] = @$this->dest_url['user']; $parts['pass'] = @$this->dest_url['pass']; $url = $this->glue_url($parts, FALSE); } - + $out = drupal_http_request($url); if ($out->code == 200) { @@ -483,24 +522,24 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination } /** - * Delete from the NodeSquirrel destination. + * Deletes from the NodeSquirrel destination. */ - function delete_file($file_id) { + public function delete_file($file_id) { if ($destination = $this->_get_destination()) { $result = $this->_xmlrpc('backups.deleteFile', array($destination, $file_id)); } } /** - * List the files in the remote destination. + * Lists the files in the remote destination. */ - function _list_files() { + public function _list_files() { $files = array(); backup_migrate_include('files'); if ($destination = $this->_get_destination()) { $file_list = $this->_xmlrpc('backups.listFiles', array($destination)); - foreach ((array)$file_list as $file) { + foreach ((array) $file_list as $file) { $files[$file['filename']] = new backup_file($file); } } @@ -509,9 +548,9 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination } /** - * List the files in the remote destination. + * Lists the files in the remote destination. */ - function check_limits() { + public function check_limits() { if (empty($this->limits)) { $this->limits = $this->_xmlrpc('backups.getLimits', array($this->_get_destination())); } @@ -519,20 +558,20 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination } /** - * Check that a destination is valid. + * Checks that a destination is valid. */ - function confirm_destination() { + public function confirm_destination() { return $this->check_limits(); } /** - * Get the form for the settings for this destination. + * Gets the form for the settings for this destination. */ - function edit_form() { + public function edit_form() { $form = parent::edit_form(); - // If this is a new destination but the default NS destination has not been created yet, - // redirect to the NS config screen. + // If this is a new destination but the default NS destination has not been + // created yet, redirect to the NS config screen. if (!$this->get_id() && !variable_get('nodesquirrel_secret_key', '')) { drupal_goto(BACKUP_MIGRATE_MENU_PATH . '/nodesquirrel'); } @@ -540,7 +579,8 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination $form['settings'] = array('#tree' => TRUE); $activate_link = backup_migrate_nodesquirrel_get_activate_link(); - // Retrieve the key from the settings or get it from the get string if this is an auto-config action. + // Retrieve the key from the settings or get it from the get string if this + // is an auto-config action. $key = $this->settings('secret_key'); if (!empty($_GET['key']) && preg_match(NODESQUIRREL_SECRET_KEY_PATTERN, $_GET['key'])) { $key = $_GET['key']; @@ -563,9 +603,12 @@ class backup_migrate_destination_nodesquirrel 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. + * Submits the configuration form. + * + * Glue the url together and add the old password back if a new one was not + * specified. */ - function edit_form_validate($form, &$form_state) { + public function edit_form_validate($form, &$form_state) { $key = trim($form_state['values']['settings']['secret_key']); if ($error = $this->vaidate_key($key)) { form_set_error('secret_key', $error); @@ -573,9 +616,11 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination } /** - * Validate a secret key. Returns error text if the key is invalid. + * Validates a secret key. + * + * Returns error text if the key is invalid. */ - function vaidate_key($key) { + public function vaidate_key($key) { $error = FALSE; if ($key) { if (!preg_match(NODESQUIRREL_SECRET_KEY_PATTERN, $key)) { @@ -602,33 +647,36 @@ class backup_migrate_destination_nodesquirrel 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. + * 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']['secret_key'] = check_plain($form_state['values']['settings']['secret_key']); parent::edit_form_submit($form, $form_state); } /** - * Get the destination id or warn the user that it has not been set. + * Gets the destination id or warn the user that it has not been set. */ - function _get_destination($warn = TRUE) { + public function _get_destination($warn = TRUE) { list($id, $key) = $this->get_user_pass(); return $id; } /** - * Get the destination id or warn the user that it has not been set. + * Gets the destination id or warn the user that it has not been set. */ - function _get_private_key($warn = TRUE) { + public function _get_private_key($warn = TRUE) { list($id, $key) = $this->get_user_pass(); return $key; } /** - * Break the secret key into the public/private key (user/pass). + * Breaks the secret key into the public/private key (user/pass). */ - function get_user_pass() { + public function get_user_pass() { $key = $this->settings('secret_key'); // The username is the first 32 chars. $user = substr($key, 0, 32); @@ -637,40 +685,57 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination return array($user, $pass); } - function get_display_location() { + public function get_display_location() { return t('NodeSquirrel.com'); } - function add_scheme($url) { - return 'http://' . $url; + public function add_scheme($url) { + return 'https://' . $url; } /** - * Get the form for the settings for this destination. + * Gets the form for the settings for this destination. */ - function _xmlrpc($method, $args = array()) { + public function _xmlrpc($method, $args = array()) { // Retrieve the severs or read them from a stored variable. $servers = $this->_get_endpoints(); // Do the actual call. - return $this->__xmlrpc($method, $args, $servers); + return $this->_xmlrpc_request($method, $args, $servers); } /** - * Get the form for the settings for this destination. + * Gets the form for the settings for this destination. */ - function __xmlrpc($method, $args, $servers, $retry = 3) { + public function _xmlrpc_request($method, $args, $servers, $retry = 3) { if ($servers && --$retry > 0) { // Add the key authentication arguments if we can. - if ($this->_sign_request($args)) { + if ($this->_sign_request($args)) { $url = reset($servers); // Try each available server in order. while ($url) { - + $url = $this->add_scheme($url); - $out = xmlrpc($url, array($method => $args)); - + $options = array(); + // Optionally disable SSL certification validation. This may be + // necessary if the server's SSL certificates are out of date because + // and might be incompatible with PHP 5.6's OpenSSL implementation. + // @see http://php.net/manual/en/migration56.openssl.php + // @see http://php.net/manual/en/context.ssl.php#context.ssl.verify-peer + if (variable_get('nodesquirrel_failover', FALSE)) { + $opts = array( + 'ssl' => array( + 'verify_peer' => FALSE, + ), + ); + $options = array( + 'context' => stream_context_create($opts), + ); + } + + $out = xmlrpc($url, array($method => $args), $options); + // Check for errors. $err = xmlrpc_error(); if ($err && $err->is_error) { @@ -681,28 +746,30 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination // Some sort of server error. Try the next one. $url = next($servers); - // If we're at the end of the line then try refetching the urls + // If we're at the end of the line then try refetching the urls. if (!$url) { $servers = $this->_get_endpoints(TRUE, $retry); - return $this->__xmlrpc($method, $args, $servers, $retry); + return $this->_xmlrpc_request($method, $args, $servers, $retry); } - break; + break; + case '300': - // 'Multiple Choices' means that the existing server list needs to be refreshed. + // 'Multiple Choices' means that the existing server list needs + // to be refreshed. $servers = $this->_get_endpoints(TRUE, $retry); - return $this->__xmlrpc($method, $args, $servers, $retry); - break; + return $this->_xmlrpc_request($method, $args, $servers, $retry); + case '401': case '403': // Authentication failed. _backup_migrate_message('Couldn\'t log in to NodeSquirrel. The server error was: %err', array('%err' => $err->message), 'error'); return FALSE; - break; + default: - // Some sort of client error. Don't try the next server because it'll probably say the same thing. + // Some sort of client error. Don't try the next server because + // it'll probably say the same thing. _backup_migrate_message('The NodeSquirrel server returned the following error: %err', array('%err' => $err->message), 'error'); return FALSE; - break; } } // No error, return the result. @@ -714,10 +781,10 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination } } - /** - * Genrate a hash with a given secret key, timestamp and random value. + /** + * Genrates a hash with a given secret key, timestamp and random value. */ - function _get_hash($time, $nonce) { + public function _get_hash($time, $nonce) { if ($private_key = $this->_get_private_key()) { $message = $time . ':' . $nonce . ':' . $private_key; // Use HMAC-SHA1 to authenticate the call. @@ -732,13 +799,13 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination return FALSE; } - /** - * Genrate a hash with a given secret key, timestamp and random value. + /** + * Genrates a hash with a given secret key, timestamp and random value. */ - function _sign_request(&$args) { - $nonce = md5(mt_rand()); - $time = time(); - $hash = $this->_get_hash($time, $nonce); + public function _sign_request(&$args) { + $nonce = md5(mt_rand()); + $time = time(); + $hash = $this->_get_hash($time, $nonce); if ($hash) { array_unshift($args, $nonce); array_unshift($args, $time); @@ -751,16 +818,17 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination } /** - * Retrieve the list of servers. + * Retrieves the list of servers. */ - function _get_endpoints($refresh = FALSE, $retry = 3) { - $servers = (array)variable_get('nodesquirrel_endpoint_urls', array()); + public function _get_endpoints($refresh = FALSE, $retry = 3) { + $servers = (array) variable_get('nodesquirrel_endpoint_urls', array()); // No servers saved or a force refreshr required. if ($refresh || empty($servers)) { $servers = array_unique(array_merge($servers, variable_get('nodesquirrel_default_endpoint_urls', array('api.nodesquirrel.com/services/xmlrpc')))); - // Call the get endpoints method but use the default or previous servers to avoid infinite loops. - $new_servers = $this->__xmlrpc('backups.getEndpoints', array($this->_get_destination(), 'xmlrpc'), $servers, $retry); + // Call the get endpoints method but use the default or previous servers + // to avoid infinite loops. + $new_servers = $this->_xmlrpc_request('backups.getEndpoints', array($this->_get_destination(), 'xmlrpc'), $servers, $retry); if ($new_servers) { variable_set('nodesquirrel_endpoint_urls', $new_servers); $servers = $new_servers; @@ -770,131 +838,163 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination } /** - * Post a file via http. + * Posts a file via HTTP. * - * This looks a lot like a clone of drupal_http_request but it can post a large - * file without reading the whole file into memory. + * This looks a lot like a clone of drupal_http_request() but it can post a + * large file without reading the whole file into memory. */ - function _post_file($url, $method = 'GET', $params = array(), $file = NULL, $retry = 3) { + public function _post_file($url, $method = 'GET', $params = array(), $file = NULL, $retry = 3) { global $db_prefix; - + $result = new stdClass(); - + // Parse the URL and make sure we can handle the schema. $uri = parse_url($url); - + if ($uri == FALSE) { $result->error = 'unable to parse URL'; $result->code = -1001; return $result; } - + if (!isset($uri['scheme'])) { $result->error = 'missing schema'; $result->code = -1002; return $result; } - + + // Avoid some "undefined variable" errors. + $errno = $errstr = NULL; + switch ($uri['scheme']) { case 'http': case 'feed': $port = isset($uri['port']) ? $uri['port'] : 80; - $host = $uri['host'] . ($port != 80 ? ':'. $port : ''); + $host = $uri['host'] . ($port != 80 ? ':' . $port : ''); $fp = @fsockopen($uri['host'], $port, $errno, $errstr, 15); break; + case 'https': - // Note: Only works for PHP 4.3 compiled with OpenSSL. + // Note: Only works for PHP 4.3+ compiled with OpenSSL. $port = isset($uri['port']) ? $uri['port'] : 443; - $host = $uri['host'] . ($port != 443 ? ':'. $port : ''); - $fp = @fsockopen('ssl://'. $uri['host'], $port, $errno, $errstr, 20); + $host = $uri['host'] . ($port != 443 ? ':' . $port : ''); + $fp = @fsockopen('ssl://' . $uri['host'], $port, $errno, $errstr, 20); + + // If fsockopen() doesn't work, try again after disabling SSL. + // Optionally disable SSL certification validation. This may be + // necessary if the server's SSL certificates are out of date and/or + // might be incompatible with PHP 5.6's OpenSSL implementation. + // @see http://php.net/manual/en/migration56.openssl.php + // @see http://php.net/manual/en/context.ssl.php#context.ssl.verify-peer + if (!$fp && variable_get('nodesquirrel_failover', FALSE)) { + $opts = array( + 'ssl' => array( + 'verify_peer' => FALSE, + ), + ); + $context = stream_context_create($opts); + $fp = stream_socket_client('ssl://' . $uri['host'] . ':' . $port, $errno, $errstr, 20, STREAM_CLIENT_CONNECT, $context); + } break; + default: - $result->error = 'invalid schema '. $uri['scheme']; + $result->error = 'invalid schema ' . $uri['scheme']; $result->code = -1003; return $result; } - + // Make sure the socket opened properly. if (!$fp) { // When a network error occurs, we use a negative number so it does not // clash with the HTTP status codes. $result->code = -$errno; $result->error = trim($errstr); - + // Mark that this request failed. This will trigger a check of the web // server's ability to make outgoing HTTP requests the next time that // requirements checking is performed. // @see system_requirements() variable_set('drupal_http_request_fails', TRUE); - + return $result; } - + // Construct the path to act on. $path = isset($uri['path']) ? $uri['path'] : '/'; if (isset($uri['query'])) { - $path .= '?'. $uri['query']; + $path .= '?' . $uri['query']; } // Prepare the data payload. - $boundary = '---------------------------'. substr(md5(rand(0,32000)),0,10); + $boundary = '---------------------------' . substr(md5(rand(0, 32000)), 0, 10); $data_footer = "\r\n--$boundary--\r\n"; $data_header = ''; foreach ($params as $key => $value) { - $data_header .="--$boundary\r\n"; - $data_header .= "Content-Disposition: form-data; name=\"".$key."\"\r\n"; - $data_header .= "\r\n".$value."\r\n"; - $data_header .="--$boundary\r\n"; + $data_header .= "--$boundary\r\n"; + $data_header .= "Content-Disposition: form-data; name=\"" . $key . "\"\r\n"; + $data_header .= "\r\n" . $value . "\r\n"; + $data_header .= "--$boundary\r\n"; } - + // Add the file header to the post payload. - $data_header .="--$boundary\r\n"; - $data_header .= "Content-Disposition: form-data; name=\"file\"; filename=\"". $file->filename() ."\"\r\n"; + $data_header .= "--$boundary\r\n"; + $data_header .= "Content-Disposition: form-data; name=\"file\"; filename=\"" . $file->filename() . "\"\r\n"; $data_header .= "Content-Type: application/octet-stream;\r\n"; $data_header .= "\r\n"; // Calculate the content length. $content_length = strlen($data_header . $data_footer) + filesize($file->filepath()); - //file_get_contents($file->filepath())); - + // file_get_contents($file->filepath())); // Create HTTP request. $defaults = array( // RFC 2616: "non-standard ports MUST, default ports MAY be included". - // We don't add the port to prevent from breaking rewrite rules checking the - // host that do not take into account the port number. + // We don't add the port to prevent from breaking rewrite rules checking + // the host that do not take into account the port number. 'Host' => "Host: $host", 'Content-type' => "Content-type: multipart/form-data, boundary=$boundary", - 'User-Agent' => 'User-Agent: NodeSquirrel Client/1.x (+http://www.nodesquirrel.com) (Drupal '. VERSION .'; Backup and Migrate 2.x)', - 'Content-Length' => 'Content-Length: '. $content_length + 'User-Agent' => 'User-Agent: NodeSquirrel Client/1.x (+http://www.nodesquirrel.com) (Drupal ' . VERSION . '; Backup and Migrate 2.x)', + 'Content-Length' => 'Content-Length: ' . $content_length, ); - // If the server url has a user then attempt to use basic authentication + // If the server url has a user then attempt to use basic authentication. if (isset($uri['user'])) { - $defaults['Authorization'] = 'Authorization: Basic '. base64_encode($uri['user'] . (!empty($uri['pass']) ? ":". $uri['pass'] : '')); + $defaults['Authorization'] = 'Authorization: Basic ' . base64_encode($uri['user'] . (!empty($uri['pass']) ? ":" . $uri['pass'] : '')); } - $request = $method .' '. $path ." HTTP/1.0\r\n"; + $request = $method . ' ' . $path . " HTTP/1.0\r\n"; $request .= implode("\r\n", $defaults); - $request .= "\r\n\r\n"; + $request .= "\r\n\r\n"; $result->request = $request; + $out = FALSE; // Write the headers and start of the headers - fwrite($fp, $request); - fwrite($fp, $data_header); + if (($fp_in = fopen($file->filepath(), 'rb')) && fwrite($fp, $request) && fwrite($fp, $data_header)) { + // Copy the file 1M at a time. + do { + $data = fread($fp_in, 1024 * 1024); + $size = strlen($data); + $sent = 0; + while ($sent < $size && ($out = fwrite($fp, substr($data, $sent)))) { + $sent += $out; + } + } while ($out && !feof($fp_in)); - // Copy the file 512k at a time to prevent memory issues. - if ($fp_in = fopen($file->filepath(), 'rb')) { - while (!feof($fp_in)) { - fwrite($fp, fread($fp_in, 1024 * 512)); + // Finish the write. + if ($out) { + $out = fwrite($fp, $data_footer); } - $success = TRUE; } @fclose($fp_in); - // Finish the write. - fwrite($fp, $data_footer); + // Check for errors. + if (!$out) { + watchdog('backup_migrate', 'NodeSquirrel: network write failed', NULL, WATCHDOG_ERROR); + $result->code = -$errno; + $result->error = trim($errstr); + return $result; + } // Fetch response. $response = ''; @@ -902,72 +1002,113 @@ class backup_migrate_destination_nodesquirrel extends backup_migrate_destination $response .= $chunk; } fclose($fp); - - if (variable_get('debug_http_request', FALSE)) { - drupal_debug(date('r')); - drupal_debug($request); - drupal_debug($response); - } - + + if (variable_get('debug_http_request', FALSE)) { + drupal_debug(date('r')); + drupal_debug($request); + drupal_debug($response); + } + // Parse response. list($split, $result->data) = explode("\r\n\r\n", $response, 2); $split = preg_split("/\r\n|\n|\r/", $split); - + list($protocol, $code, $status_message) = explode(' ', trim(array_shift($split)), 3); $result->protocol = $protocol; $result->status_message = $status_message; - + $result->headers = array(); - + // Parse headers. while ($line = trim(array_shift($split))) { list($header, $value) = explode(':', $line, 2); if (isset($result->headers[$header]) && $header == 'Set-Cookie') { // RFC 2109: the Set-Cookie response header comprises the token Set- // Cookie:, followed by a comma-separated list of one or more cookies. - $result->headers[$header] .= ','. trim($value); + $result->headers[$header] .= ',' . trim($value); } else { $result->headers[$header] = trim($value); } } - + $responses = array( - 100 => 'Continue', 101 => 'Switching Protocols', - 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', - 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect', - 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', - 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported' + 100 => 'Continue', + 101 => 'Switching Protocols', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Time-out', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Large', + 415 => 'Unsupported Media Type', + 416 => 'Requested range not satisfiable', + 417 => 'Expectation Failed', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Time-out', + 505 => 'HTTP Version not supported', ); - // RFC 2616 states that all unknown HTTP codes must be treated the same as the - // base code in their class. + // RFC 2616 states that all unknown HTTP codes must be treated the same as + // the base code in their class. if (!isset($responses[$code])) { $code = floor($code / 100) * 100; } - + switch ($code) { - case 200: // OK - case 304: // Not modified + case 200: + // OK. + case 304: + // Not modified. break; - case 301: // Moved permanently - case 302: // Moved temporarily - case 307: // Moved temporarily + + case 301: + // Moved permanently. + case 302: + // Moved temporarily. + case 307: + // Moved temporarily. $location = $result->headers['Location']; - + if ($retry) { $result = drupal_http_request($result->headers['Location'], $headers, $method, $data, --$retry); $result->redirect_code = $result->code; } $result->redirect_url = $location; - + break; + default: $result->error = $status_message; } - + $result->code = $code; return $result; } } - diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.s3.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.s3.inc index c049806..1bbffd3 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.s3.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/destinations.s3.inc @@ -1,6 +1,5 @@ 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; } + } diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/files.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/files.inc index 8931189..cb234b1 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/files.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/files.inc @@ -1,6 +1,5 @@ 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; } -} +} diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/filters.backup_restore.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/filters.backup_restore.inc index cf80b59..47e4955 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/filters.backup_restore.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/filters.backup_restore.inc @@ -1,9 +1,10 @@ 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()); } diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/filters.compression.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/filters.compression.inc index e399d29..e55ff92 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/filters.compression.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/filters.compression.inc @@ -1,6 +1,5 @@ 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; } -} +} diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/filters.encryption.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/filters.encryption.inc index 74b679c..98e2361 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/filters.encryption.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/filters.encryption.inc @@ -1,6 +1,5 @@ 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; } -} +} diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/filters.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/filters.inc index d0ed9ec..4b55f5d 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/filters.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/filters.inc @@ -1,25 +1,24 @@ 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) { } -} +} diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/filters.statusnotify.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/filters.statusnotify.inc index 28387a2..6101349 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/filters.statusnotify.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/filters.statusnotify.inc @@ -1,6 +1,5 @@ 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; } -} +} diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/filters.utils.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/filters.utils.inc index b3eb1a1..87ecb14 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/filters.utils.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/filters.utils.inc @@ -1,6 +1,5 @@ -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.'); } } + } diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/locations.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/locations.inc index 7cacd0f..d008341 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/locations.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/locations.inc @@ -1,6 +1,5 @@ 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 = '
'. 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']))))) .'
'; - $out .= '
'. filter_xss_admin($type['description']) .'
'; + $out = '
' . 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']))))) . '
'; + $out .= '
' . filter_xss_admin($type['description']) . '
'; $items[] = $out; } } if (count($items)) { - $output = t('Choose the type of location you would like to create:') .'
'. implode('', $items) .'
'; + $output = t('Choose the type of location you would like to create:') . '
' . implode('', $items) . '
'; } 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. + * Location configuration callback. */ - 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. - */ - 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); } -} +} diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/profiles.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/profiles.inc index 47265b7..5492b62 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/profiles.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/profiles.inc @@ -1,6 +1,5 @@ $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'))); } -} +} diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/schedules.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/schedules.inc index d0636d2..325b402 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/schedules.inc +++ b/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 '
'. t("Missing") .'
'; + return '
' . t("Missing") . '
'; } /** * 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 '
'. t("Missing") .'
'; + return '
' . t("Missing") . '
'; } /** * 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 { } } } -} +} diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/sources.archivesource.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/sources.archivesource.inc old mode 100755 new mode 100644 index b3625f7..73bf33a --- a/sites/all/modules/contrib/admin/backup_migrate/includes/sources.archivesource.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/sources.archivesource.inc @@ -1,4 +1,9 @@ '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); } -} +} diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/sources.db.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/sources.db.inc index 8bd75c3..a32dcd4 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/sources.db.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/sources.db.inc @@ -1,6 +1,5 @@ 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 do not change these settings unless you know what you're doing."); $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'), diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/sources.db.mysql.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/sources.db.mysql.inc index 49ded37..ee67c86 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/sources.db.mysql.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/sources.db.mysql.inc @@ -1,5 +1,9 @@ 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; + + $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) . "'"; } - // 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; + + // 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; } } - diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/sources.filesource.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/sources.filesource.inc index 9008852..fb6b1e3 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/sources.filesource.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/sources.filesource.inc @@ -1,6 +1,5 @@ '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()); } -} +} diff --git a/sites/all/modules/contrib/admin/backup_migrate/includes/sources.inc b/sites/all/modules/contrib/admin/backup_migrate/includes/sources.inc index 6c650fa..bc164cf 100644 --- a/sites/all/modules/contrib/admin/backup_migrate/includes/sources.inc +++ b/sites/all/modules/contrib/admin/backup_migrate/includes/sources.inc @@ -1,6 +1,5 @@ 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); } -} +} diff --git a/sites/all/modules/contrib/admin/backup_migrate/tests/BmTestBase.test b/sites/all/modules/contrib/admin/backup_migrate/tests/BmTestBase.test new file mode 100644 index 0000000..ad9663b --- /dev/null +++ b/sites/all/modules/contrib/admin/backup_migrate/tests/BmTestBase.test @@ -0,0 +1,246 @@ + $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 = "
\n" . print_r($message, TRUE) . "\n
\n"; + } + + // Optional title to go before the output. + if (!empty($title)) { + $title = '

' . check_plain($title) . "

\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); + } + +} diff --git a/sites/all/modules/contrib/admin/backup_migrate/tests/BmTestBasics.test b/sites/all/modules/contrib/admin/backup_migrate/tests/BmTestBasics.test new file mode 100644 index 0000000..d6ed7fb --- /dev/null +++ b/sites/all/modules/contrib/admin/backup_migrate/tests/BmTestBasics.test @@ -0,0 +1,111 @@ + '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. diff --git a/sites/all/modules/contrib/admin/backup_migrate/tests/BmTestProfiles.test b/sites/all/modules/contrib/admin/backup_migrate/tests/BmTestProfiles.test new file mode 100644 index 0000000..c8be956 --- /dev/null +++ b/sites/all/modules/contrib/admin/backup_migrate/tests/BmTestProfiles.test @@ -0,0 +1,168 @@ + '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(); + } + +} diff --git a/sites/all/modules/contrib/admin/context/context.core.inc b/sites/all/modules/contrib/admin/context/context.core.inc index 41ed93a..9e19831 100644 --- a/sites/all/modules/contrib/admin/context/context.core.inc +++ b/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; + } } } } diff --git a/sites/all/modules/contrib/admin/context/context.info b/sites/all/modules/contrib/admin/context/context.info index a980c87..eef3a9c 100644 --- a/sites/all/modules/contrib/admin/context/context.info +++ b/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" diff --git a/sites/all/modules/contrib/admin/context/context.install b/sites/all/modules/contrib/admin/context/context.install index 0a5a145..0d902ef 100644 --- a/sites/all/modules/contrib/admin/context/context.install +++ b/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))); diff --git a/sites/all/modules/contrib/admin/context/context.module b/sites/all/modules/contrib/admin/context/context.module index 96808dd..c83b15e 100644 --- a/sites/all/modules/contrib/admin/context/context.module +++ b/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'; + } } } } diff --git a/sites/all/modules/contrib/admin/context/context.plugins.inc b/sites/all/modules/contrib/admin/context/context.plugins.inc index a0849bb..95d003d 100644 --- a/sites/all/modules/contrib/admin/context/context.plugins.inc +++ b/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 ~ 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', ), diff --git a/sites/all/modules/contrib/admin/context/context_layouts/context_layouts.info b/sites/all/modules/contrib/admin/context/context_layouts/context_layouts.info index 5ddb2cf..65880e3 100644 --- a/sites/all/modules/contrib/admin/context/context_layouts/context_layouts.info +++ b/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" diff --git a/sites/all/modules/contrib/admin/context/context_ui/context_ui.info b/sites/all/modules/contrib/admin/context/context_ui/context_ui.info index 0006699..e5d17f8 100644 --- a/sites/all/modules/contrib/admin/context/context_ui/context_ui.info +++ b/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" diff --git a/sites/all/modules/contrib/admin/context/context_ui/context_ui.js b/sites/all/modules/contrib/admin/context/context_ui/context_ui.js index 308164f..56aa62a 100644 --- a/sites/all/modules/contrib/admin/context/context_ui/context_ui.js +++ b/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. diff --git a/sites/all/modules/contrib/admin/context/context_ui/context_ui.module b/sites/all/modules/contrib/admin/context/context_ui/context_ui.module index 2ed2aa6..d694870 100644 --- a/sites/all/modules/contrib/admin/context/context_ui/context_ui.module +++ b/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' => '

', - '#markup' => t('Select the Context/Layer to Edit'), + '#markup' => t('Select the context/layer to edit'), '#suffix' => '

', '#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' => '

', - '#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' => '

', '#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) ? '' : - "
".check_plain($context->description)."
"; + $description = empty($context->description) ? '' : '
' . check_plain($context->description) . '
'; $items[] = array( - 'data' => "
" . $readable_name. "
" . $description, + 'data' => '
' . $readable_name. "
" . $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 Edit layout 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()), diff --git a/sites/all/modules/contrib/admin/context/context_ui/context_ui_dialog.js b/sites/all/modules/contrib/admin/context/context_ui/context_ui_dialog.js index dc73f91..1dfc4f8 100644 --- a/sites/all/modules/contrib/admin/context/context_ui/context_ui_dialog.js +++ b/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 = $(''+labelClose+''); + var tab = $(''+labelClose+''); selector.append(tab); selector.toggled = false; diff --git a/sites/all/modules/contrib/admin/context/context_ui/export_ui/context_export_ui.class.php b/sites/all/modules/contrib/admin/context/context_ui/export_ui/context_export_ui.class.php index db5cf86..a6c9f79 100644 --- a/sites/all/modules/contrib/admin/context/context_ui/export_ui/context_export_ui.class.php +++ b/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: theme') .'
'. t('A tag to group this context with others.'), + '#description' => t('Example: theme') . '
' . 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)) { diff --git a/sites/all/modules/contrib/admin/context/context_ui/tests/context_ui.test b/sites/all/modules/contrib/admin/context/context_ui/tests/context_ui.test index 06c7a5e..76ae7d5 100644 --- a/sites/all/modules/contrib/admin/context/context_ui/tests/context_ui.test +++ b/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.'); diff --git a/sites/all/modules/contrib/admin/context/plugins/context_condition_bookroot.inc b/sites/all/modules/contrib/admin/context/plugins/context_condition_bookroot.inc index 200326c..778b826 100644 --- a/sites/all/modules/contrib/admin/context/plugins/context_condition_bookroot.inc +++ b/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(); diff --git a/sites/all/modules/contrib/admin/context/plugins/context_condition_menu.inc b/sites/all/modules/contrib/admin/context/plugins/context_condition_menu.inc index 0df8811..4b19e02 100644 --- a/sites/all/modules/contrib/admin/context/plugins/context_condition_menu.inc +++ b/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]); diff --git a/sites/all/modules/contrib/admin/context/plugins/context_condition_node_taxonomy.inc b/sites/all/modules/contrib/admin/context/plugins/context_condition_node_taxonomy.inc index cfb214f..00905aa 100644 --- a/sites/all/modules/contrib/admin/context/plugins/context_condition_node_taxonomy.inc +++ b/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]); } } } diff --git a/sites/all/modules/contrib/admin/context/plugins/context_condition_query_string.inc b/sites/all/modules/contrib/admin/context/plugins/context_condition_query_string.inc index 781d4cb..b7bf5d5 100644 --- a/sites/all/modules/contrib/admin/context/plugins/context_condition_query_string.inc +++ b/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)) { diff --git a/sites/all/modules/contrib/admin/context/plugins/context_reaction_block.inc b/sites/all/modules/contrib/admin/context/plugins/context_reaction_block.inc index abcafd8..90c005c 100644 --- a/sites/all/modules/contrib/admin/context/plugins/context_reaction_block.inc +++ b/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); } /** diff --git a/sites/all/modules/contrib/admin/context/plugins/context_reaction_block.js b/sites/all/modules/contrib/admin/context/plugins/context_reaction_block.js index 10e25a2..192b956 100644 --- a/sites/all/modules/contrib/admin/context/plugins/context_reaction_block.js +++ b/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('
'+ label + '
'); + label = Drupal.t('Now editing: @label', {'@label': label}); + editor.parent().parent().prepend('
' + label + '
'); // First pass, enable sortables on all regions. $(this.regions).each(function() { diff --git a/sites/all/modules/contrib/admin/context/plugins/context_reaction_breadcrumb.inc b/sites/all/modules/contrib/admin/context/plugins/context_reaction_breadcrumb.inc index 66d121c..e6ce6fd 100644 --- a/sites/all/modules/contrib/admin/context/plugins/context_reaction_breadcrumb.inc +++ b/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'), '')); $result = db_select('menu_links') ->fields('menu_links', array('p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8')) ->condition('hidden', 0) diff --git a/sites/all/modules/contrib/admin/context/plugins/context_reaction_menu.inc b/sites/all/modules/contrib/admin/context/plugins/context_reaction_menu.inc index 0d82adc..4f0e54c 100644 --- a/sites/all/modules/contrib/admin/context/plugins/context_reaction_menu.inc +++ b/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), ); } diff --git a/sites/all/modules/contrib/admin/context/plugins/context_reaction_region.inc b/sites/all/modules/contrib/admin/context/plugins/context_reaction_region.inc index c4f0fd7..d9c0a8e 100644 --- a/sites/all/modules/contrib/admin/context/plugins/context_reaction_region.inc +++ b/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; diff --git a/sites/all/modules/contrib/admin/context/plugins/context_reaction_template_suggestions.inc b/sites/all/modules/contrib/admin/context/plugins/context_reaction_template_suggestions.inc index 0376f0a..03a7ba6 100644 --- a/sites/all/modules/contrib/admin/context/plugins/context_reaction_template_suggestions.inc +++ b/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 Drupal 7 Template (Theme Hook) Suggestions.', array('@template-suggestions' => 'http://drupal.org/node/1089656')), '#default_value' => is_string($default_value) ? $default_value : '', ); } diff --git a/sites/all/modules/contrib/admin/context/tests/context.conditions.test b/sites/all/modules/contrib/admin/context/tests/context.conditions.test index e2cee73..a55276b 100644 --- a/sites/all/modules/contrib/admin/context/tests/context.conditions.test +++ b/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( diff --git a/sites/all/modules/contrib/admin/context/theme/context_reaction_block.theme.inc b/sites/all/modules/contrib/admin/context/theme/context_reaction_block.theme.inc index 4f538bc..e5fe9de 100644 --- a/sites/all/modules/contrib/admin/context/theme/context_reaction_block.theme.inc +++ b/sites/all/modules/contrib/admin/context/theme/context_reaction_block.theme.inc @@ -52,7 +52,7 @@ function theme_context_block_regions_form($vars) { $output .= "
"; $output .= l(t('+') . ' ' . t('Add'), $_GET['q'], array('fragment' => $region, 'attributes' => array('class' => array('add-block')))); $output .= $form[$region]['#title']; - $output .= "
"; + $output .= ''; $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' => '
', - '#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' => '
', ); $filter_label = array( '#prefix' => '
', - '#markup' => t('Search Filter'), + '#markup' => t('Search filter'), '#suffix' => '
', ); diff --git a/sites/all/modules/contrib/admin/features/features.admin.inc b/sites/all/modules/contrib/admin/features/features.admin.inc index c2cc508..2dea423 100644 --- a/sites/all/modules/contrib/admin/features/features.admin.inc +++ b/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) { + $features = _features_get_features_list(); + $modules = array_filter(features_get_modules(), 'features_filter_hidden'); + $conflicts = features_get_conflicts(); + // 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(); - - $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']; - } - } - } - } - - 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; +} diff --git a/sites/all/modules/contrib/admin/features/features.api.php b/sites/all/modules/contrib/admin/features/features.api.php index f5984e0..d7c7ede 100644 --- a/sites/all/modules/contrib/admin/features/features.api.php +++ b/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 * @{ diff --git a/sites/all/modules/contrib/admin/features/features.drush.inc b/sites/all/modules/contrib/admin/features/features.drush.inc index c1a15ac..4be5d98 100644 --- a/sites/all/modules/contrib/admin/features/features.drush.inc +++ b/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(). * diff --git a/sites/all/modules/contrib/admin/features/features.export.inc b/sites/all/modules/contrib/admin/features/features.export.inc index 154b26b..340818b 100644 --- a/sites/all/modules/contrib/admin/features/features.export.inc +++ b/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] = "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]; +} diff --git a/sites/all/modules/contrib/admin/features/features.info b/sites/all/modules/contrib/admin/features/features.info index dfadcf0..366a862 100644 --- a/sites/all/modules/contrib/admin/features/features.info +++ b/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" diff --git a/sites/all/modules/contrib/admin/features/features.install b/sites/all/modules/contrib/admin/features/features.install index aafdcc6..008f41b 100644 --- a/sites/all/modules/contrib/admin/features/features.install +++ b/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); + } +} diff --git a/sites/all/modules/contrib/admin/features/features.js b/sites/all/modules/contrib/admin/features/features.js index 023247a..0a4a130 100644 --- a/sites/all/modules/contrib/admin/features/features.js +++ b/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); diff --git a/sites/all/modules/contrib/admin/features/features.module b/sites/all/modules/contrib/admin/features/features.module index 5a42eb5..14fc3f9 100644 --- a/sites/all/modules/contrib/admin/features/features.module +++ b/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; +} diff --git a/sites/all/modules/contrib/admin/features/includes/features.contact.inc b/sites/all/modules/contrib/admin/features/includes/features.contact.inc new file mode 100644 index 0000000..af9b919 --- /dev/null +++ b/sites/all/modules/contrib/admin/features/includes/features.contact.inc @@ -0,0 +1,115 @@ + 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(); + } + } + } +} diff --git a/sites/all/modules/contrib/admin/features/includes/features.field.inc b/sites/all/modules/contrib/admin/features/includes/features.field.inc index 09104a3..ca305ae 100644 --- a/sites/all/modules/contrib/admin/features/includes/features.field.inc +++ b/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); +} diff --git a/sites/all/modules/contrib/admin/features/includes/features.image.inc b/sites/all/modules/contrib/admin/features/includes/features.image.inc index 2b5eb27..bf89191 100644 --- a/sites/all/modules/contrib/admin/features/includes/features.image.inc +++ b/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', + ))); } } diff --git a/sites/all/modules/contrib/admin/features/includes/features.locale.inc b/sites/all/modules/contrib/admin/features/includes/features.locale.inc index fc27174..258d541 100644 --- a/sites/all/modules/contrib/admin/features/includes/features.locale.inc +++ b/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 = ''; diff --git a/sites/all/modules/contrib/admin/features/includes/features.menu.inc b/sites/all/modules/contrib/admin/features/includes/features.menu.inc index c883e6a..84af623 100644 --- a/sites/all/modules/contrib/admin/features/includes/features.menu.inc +++ b/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] ? '-' : ''; } diff --git a/sites/all/modules/contrib/admin/features/includes/features.node.inc b/sites/all/modules/contrib/admin/features/includes/features.node.inc index 9461778..25a2c1c 100644 --- a/sites/all/modules/contrib/admin/features/includes/features.node.inc +++ b/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(); } } } diff --git a/sites/all/modules/contrib/admin/features/tests/features.test b/sites/all/modules/contrib/admin/features/tests/features.test index 1fa51ac..743cc1b 100644 --- a/sites/all/modules/contrib/admin/features/tests/features.test +++ b/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.'); + } +} diff --git a/sites/all/modules/contrib/admin/features/tests/features_test/features_test.features.inc b/sites/all/modules/contrib/admin/features/tests/features_test/features_test.features.inc index 38aaa4d..8d0646f 100644 --- a/sites/all/modules/contrib/admin/features/tests/features_test/features_test.features.inc +++ b/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, diff --git a/sites/all/modules/contrib/admin/features/tests/features_test/features_test.info b/sites/all/modules/contrib/admin/features/tests/features_test/features_test.info index 2148161..cd414ec 100644 --- a/sites/all/modules/contrib/admin/features/tests/features_test/features_test.info +++ b/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" diff --git a/sites/all/modules/contrib/admin/google_analytics/README.txt b/sites/all/modules/contrib/admin/google_analytics/README.txt index fab2f2e..8ef9818 100644 --- a/sites/all/modules/contrib/admin/google_analytics/README.txt +++ b/sites/all/modules/contrib/admin/google_analytics/README.txt @@ -1,6 +1,6 @@ Module: Google Analytics -Author: Alexander Hass +Author: Alexander Hass 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: + + +Text format: Full HTML diff --git a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.admin.inc b/sites/all/modules/contrib/admin/google_analytics/googleanalytics.admin.inc index 4449077..2d8afa3 100644 --- a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.admin.inc +++ b/sites/all/modules/contrib/admin/google_analytics/googleanalytics.admin.inc @@ -18,10 +18,17 @@ function googleanalytics_admin_settings_form($form_state) { '#title' => t('Web Property ID'), '#type' => 'textfield', '#default_value' => variable_get('googleanalytics_account', 'UA-'), - '#size' => 15, + '#size' => 20, '#maxlength' => 20, '#required' => TRUE, - '#description' => t('This ID is unique to each site you want to track separately, and is in the form of UA-xxxxxxx-yy. To get a Web Property ID, register your site with Google Analytics, or if you already have registered your site, go to your Google Analytics Settings page to see the ID next to every site profile. Find more information in the documentation.', array('@analytics' => 'http://www.google.com/analytics/', '@webpropertyid' => url('https://developers.google.com/analytics/resources/concepts/gaConceptsAccounts', array('fragment' => 'webProperty')))), + '#description' => t('This ID is unique to each site you want to track separately, and is in the form of UA-xxxxxxx-yy. To get a Web Property ID, register your site with Google Analytics, or if you already have registered your site, go to your Google Analytics Settings page to see the ID next to every site profile. Find more information in the documentation.', array('@analytics' => 'https://marketingplatform.google.com/about/analytics/', '@webpropertyid' => url('https://developers.google.com/analytics/resources/concepts/gaConceptsAccounts', array('fragment' => 'webProperty')))), + ); + + $form['account']['googleanalytics_premium'] = array( + '#type' => 'checkbox', + '#title' => t('Premium account'), + '#description' => t('If you are a Google Analytics Premium customer, you can use up to 200 instead of 20 custom dimensions and metrics.'), + '#default_value' => variable_get('googleanalytics_premium', FALSE), ); // Visibility settings. @@ -169,7 +176,7 @@ function googleanalytics_admin_settings_form($form_state) { '#type' => 'fieldset', '#title' => t('Users'), ); - $t_permission = array('%permission' => t('opt-in or out of tracking')); + $t_permission = array('%permission' => t('Opt-in or out of tracking')); $form['tracking']['user_vis_settings']['googleanalytics_custom'] = array( '#type' => 'radios', '#title' => t('Allow users to customize tracking on their account page'), @@ -224,6 +231,18 @@ function googleanalytics_admin_settings_form($form_state) { ), ), ); + + $colorbox_dependencies = '
'; + $colorbox_dependencies .= t('Requires: !module-list', array('!module-list' => (module_exists('colorbox') ? t('@module (enabled)', array('@module' => 'Colorbox')) : t('@module (disabled)', array('@module' => 'Colorbox'))))); + $colorbox_dependencies .= '
'; + + $form['tracking']['linktracking']['googleanalytics_trackcolorbox'] = array( + '#type' => 'checkbox', + '#title' => t('Track content in colorbox modal dialogs'), + '#default_value' => variable_get('googleanalytics_trackcolorbox', 1), + '#description' => t('Enable to track the content shown in colorbox modal windows.') . $colorbox_dependencies, + '#disabled' => (module_exists('colorbox') ? FALSE : TRUE), + ); $form['tracking']['linktracking']['googleanalytics_tracklinkid'] = array( '#type' => 'checkbox', '#title' => t('Track enhanced link attribution'), @@ -246,7 +265,7 @@ function googleanalytics_admin_settings_form($form_state) { '#type' => 'checkboxes', '#title' => t('Track messages of type'), '#default_value' => variable_get('googleanalytics_trackmessages', array()), - '#description' => t('This will track the selected message types shown to users. Tracking of form validation errors may help you identifying usability issues in your site. For each visit (user session), a maximum of approximately 500 combined GATC requests (both events and page views) can be tracked. Every message is tracked as one individual event. Note that - as the number of events in a session approaches the limit - additional events might not be tracked. Messages from excluded pages cannot tracked.'), + '#description' => t('This will track the selected message types shown to users. Tracking of form validation errors may help you identifying usability issues in your site. For each visit (user session), a maximum of approximately 500 combined GATC requests (both events and page views) can be tracked. Every message is tracked as one individual event. Note that - as the number of events in a session approaches the limit - additional events might not be tracked. Messages from excluded pages cannot be tracked.'), '#options' => array( 'status' => t('Status message'), 'warning' => t('Warning message'), @@ -297,7 +316,7 @@ function googleanalytics_admin_settings_form($form_state) { $form['tracking']['privacy']['googleanalytics_privacy_donottrack'] = array( '#type' => 'checkbox', '#title' => t('Universal web tracking opt-out'), - '#description' => t('If enabled and your server receives the Do-Not-Track header from the client browser, the Google Analytics module will not embed any tracking code into your site. Compliance with Do Not Track could be purely voluntary, enforced by industry self-regulation, or mandated by state or federal law. Please accept your visitors privacy. If they have opt-out from tracking and advertising, you should accept their personal decision. This feature is currently limited to logged in users and disabled page caching.', array('@donottrack' => 'http://donottrack.us/')), + '#description' => t('If enabled and your server receives the Do-Not-Track header from the client browser, the Google Analytics module will not embed any tracking code into your site. Compliance with Do Not Track could be purely voluntary, enforced by industry self-regulation, or mandated by state or federal law. Please accept your visitors privacy. If they have opt-out from tracking and advertising, you should accept their personal decision. This feature is currently limited to logged in users and disabled page caching.', array('@donottrack' => 'https://www.eff.org/issues/do-not-track')), '#default_value' => variable_get('googleanalytics_privacy_donottrack', 1), ); @@ -305,7 +324,7 @@ function googleanalytics_admin_settings_form($form_state) { $form['googleanalytics_custom_dimension'] = array( '#collapsed' => TRUE, '#collapsible' => TRUE, - '#description' => t('You can set values for Google Analytics Custom Dimensions here. You must have already configured your custom dimensions in the Google Analytics Management Interface. You may use tokens. Global and user tokens are always available; on node pages, node tokens are also available. A dimension value is allowed to have a maximum lenght of 150 bytes. Expect longer values to get trimmed.', array('@custom_var_documentation' => 'https://developers.google.com/analytics/devguides/collection/analyticsjs/custom-dims-mets', '@setup_documentation' => 'https://support.google.com/analytics/answer/2709829')), + '#description' => t('You can set values for Google Analytics Custom Dimensions here. You must have already configured your custom dimensions in the Google Analytics Management Interface. You may use tokens. Global and user tokens are always available; on node pages, node tokens are also available. A dimension value is allowed to have a maximum length of 150 bytes. Expect longer values to get trimmed.', array('@custom_var_documentation' => 'https://developers.google.com/analytics/devguides/collection/analyticsjs/custom-dims-mets', '@setup_documentation' => 'https://support.google.com/analytics/answer/2709829')), '#theme' => 'googleanalytics_admin_custom_var_table', '#title' => t('Custom dimensions'), '#tree' => TRUE, @@ -314,8 +333,10 @@ function googleanalytics_admin_settings_form($form_state) { $googleanalytics_custom_dimension = variable_get('googleanalytics_custom_dimension', array()); - // Google Analytics supports up to 20 custom dimensions. - for ($i = 1; $i <= 20; $i++) { + // Standard Google Analytics accounts support up to 20 custom dimensions, + // premium accounts support up to 200 custom dimensions. + $limit = (variable_get('googleanalytics_premium', FALSE)) ? 200 : 20; + for ($i = 1; $i <= $limit; $i++) { $form['googleanalytics_custom_dimension']['indexes'][$i]['index'] = array( '#default_value' => $i, '#description' => t('Index number'), @@ -342,7 +363,7 @@ function googleanalytics_admin_settings_form($form_state) { $form['googleanalytics_custom_dimension']['googleanalytics_description'] = array( '#type' => 'item', - '#description' => t('You can supplement Google Analytics\' basic IP address tracking of visitors by segmenting users based on custom dimensions. Section 7 of the Google Analytics terms of service requires that You will not (and will not allow any third party to) use the Service to track, collect or upload any data that personally identifies an individual (such as a name, userid, email address or billing information), or other data which can be reasonably linked to such information by Google. You will have and abide by an appropriate Privacy Policy and will comply with all applicable laws and regulations relating to the collection of information from Visitors. You must post a Privacy Policy and that Privacy Policy must provide notice of Your use of cookies that are used to collect traffic data, and You must not circumvent any privacy features (e.g., an opt-out) that are part of the Service.', array('@ga_tos' => 'http://www.google.com/analytics/terms/gb.html')), + '#description' => t('You can supplement Google Analytics\' basic IP address tracking of visitors by segmenting users based on custom dimensions. Section 7 of the Google Analytics terms of service requires that You will not (and will not allow any third party to) use the Service to track, collect or upload any data that personally identifies an individual (such as a name, userid, email address or billing information), or other data which can be reasonably linked to such information by Google. You will have and abide by an appropriate Privacy Policy and will comply with all applicable laws and regulations relating to the collection of information from Visitors. You must post a Privacy Policy and that Privacy Policy must provide notice of Your use of cookies that are used to collect traffic data, and You must not circumvent any privacy features (e.g., an opt-out) that are part of the Service.', array('@ga_tos' => 'https://www.google.com/analytics/terms/gb.html')), ); if (module_exists('token')) { $form['googleanalytics_custom_dimension']['googleanalytics_token_tree'] = array( @@ -365,8 +386,9 @@ function googleanalytics_admin_settings_form($form_state) { $googleanalytics_custom_metric = variable_get('googleanalytics_custom_metric', array()); - // Google Analytics supports up to 20 custom metrics. - for ($i = 1; $i <= 20; $i++) { + // Standard Google Analytics accounts support up to 20 custom metrics, + // premium accounts support up to 200 custom metrics. + for ($i = 1; $i <= $limit; $i++) { $form['googleanalytics_custom_metric']['indexes'][$i]['index'] = array( '#default_value' => $i, '#description' => t('Index number'), @@ -393,7 +415,7 @@ function googleanalytics_admin_settings_form($form_state) { $form['googleanalytics_custom_metric']['googleanalytics_description'] = array( '#type' => 'item', - '#description' => t('You can supplement Google Analytics\' basic IP address tracking of visitors by segmenting users based on custom metrics. Section 7 of the Google Analytics terms of service requires that You will not (and will not allow any third party to) use the Service to track, collect or upload any data that personally identifies an individual (such as a name, userid, email address or billing information), or other data which can be reasonably linked to such information by Google. You will have and abide by an appropriate Privacy Policy and will comply with all applicable laws and regulations relating to the collection of information from Visitors. You must post a Privacy Policy and that Privacy Policy must provide notice of Your use of cookies that are used to collect traffic data, and You must not circumvent any privacy features (e.g., an opt-out) that are part of the Service.', array('@ga_tos' => 'http://www.google.com/analytics/terms/gb.html')), + '#description' => t('You can supplement Google Analytics\' basic IP address tracking of visitors by segmenting users based on custom metrics. Section 7 of the Google Analytics terms of service requires that You will not (and will not allow any third party to) use the Service to track, collect or upload any data that personally identifies an individual (such as a name, userid, email address or billing information), or other data which can be reasonably linked to such information by Google. You will have and abide by an appropriate Privacy Policy and will comply with all applicable laws and regulations relating to the collection of information from Visitors. You must post a Privacy Policy and that Privacy Policy must provide notice of Your use of cookies that are used to collect traffic data, and You must not circumvent any privacy features (e.g., an opt-out) that are part of the Service.', array('@ga_tos' => 'https://www.google.com/analytics/terms/gb.html')), ); if (module_exists('token')) { $form['googleanalytics_custom_metric']['googleanalytics_token_tree'] = array( @@ -428,12 +450,14 @@ function googleanalytics_admin_settings_form($form_state) { ); } + $user_access_add_js_snippets = !user_access('add JS snippets for google analytics'); + $user_access_add_js_snippets_permission_warning = $user_access_add_js_snippets ? ' ' . t('This field has been disabled because you do not have sufficient permissions to edit it.') . '' : ''; $form['advanced']['codesnippet'] = array( '#type' => 'fieldset', '#title' => t('Custom JavaScript code'), '#collapsible' => TRUE, '#collapsed' => TRUE, - '#description' => t('You can add custom Google Analytics code snippets here. These will be added every time tracking is in effect. Before you add your custom code, you should read the Google Analytics Tracking Code - Functional Overview and the Google Analytics Tracking API documentation. Do not include the <script> tags, and always end your code with a semicolon (;).', array('@snippets' => 'http://drupal.org/node/248699', '@ga_concepts_overview' => 'https://developers.google.com/analytics/resources/concepts/gaConceptsTrackingOverview', '@ga_js_api' => 'https://developers.google.com/analytics/devguides/collection/analyticsjs/method-reference')), + '#description' => t('You can add custom Google Analytics code snippets here. These will be added every time tracking is in effect. Before you add your custom code, you should read the Google Analytics Tracking Code - Functional Overview and the Google Analytics Tracking API documentation. Do not include the <script> tags, and always end your code with a semicolon (;).', array('@snippets' => 'https://drupal.org/node/248699', '@ga_concepts_overview' => 'https://developers.google.com/analytics/resources/concepts/gaConceptsTrackingOverview', '@ga_js_api' => 'https://developers.google.com/analytics/devguides/collection/analyticsjs/method-reference')), ); $form['advanced']['codesnippet']['googleanalytics_codesnippet_create'] = array( '#type' => 'textarea', @@ -447,15 +471,17 @@ function googleanalytics_admin_settings_form($form_state) { '#type' => 'textarea', '#title' => t('Code snippet (before)'), '#default_value' => variable_get('googleanalytics_codesnippet_before', ''), + '#disabled' => $user_access_add_js_snippets, '#rows' => 5, - '#description' => t('Code in this textarea will be added before ga("send", "pageview");.'), + '#description' => t('Code in this textarea will be added before ga("send", "pageview");.') . $user_access_add_js_snippets_permission_warning, ); $form['advanced']['codesnippet']['googleanalytics_codesnippet_after'] = array( '#type' => 'textarea', '#title' => t('Code snippet (after)'), '#default_value' => variable_get('googleanalytics_codesnippet_after', ''), + '#disabled' => $user_access_add_js_snippets, '#rows' => 5, - '#description' => t('Code in this textarea will be added after ga("send", "pageview");. This is useful if you\'d like to track a site in two accounts.'), + '#description' => t('Code in this textarea will be added after ga("send", "pageview");. This is useful if you\'d like to track a site in two accounts.') . $user_access_add_js_snippets_permission_warning, ); $form['advanced']['googleanalytics_debug'] = array( @@ -541,6 +567,12 @@ function googleanalytics_admin_settings_form_validate($form, &$form_state) { /** * Layout for the custom variables table in the admin settings form. + * + * @param array $variables + * An array contains the form elements. + * + * @return string + * The rendered output. */ function theme_googleanalytics_admin_custom_var_table($variables) { $form = $variables['form']; @@ -551,7 +583,7 @@ function theme_googleanalytics_admin_custom_var_table($variables) { ); $rows = array(); - foreach (element_children($form['indexes']) as $key => $id) { + foreach (element_children($form['indexes']) as $id) { $rows[] = array( 'data' => array( drupal_render($form['indexes'][$id]['index']), @@ -600,11 +632,18 @@ function googleanalytics_token_element_validate(&$element, &$form_state) { return $element; } +/** + * @param array $value + * An array of token values. + * + * @return array + * A unique array of invalid tokens. + */ function _googleanalytics_get_forbidden_tokens($value) { $invalid_tokens = array(); $value_tokens = is_string($value) ? token_scan($value) : $value; - foreach ($value_tokens as $type => $tokens) { + foreach ($value_tokens as $tokens) { if (array_filter($tokens, '_googleanalytics_contains_forbidden_token')) { $invalid_tokens = array_merge($invalid_tokens, array_values($tokens)); } @@ -617,8 +656,9 @@ function _googleanalytics_get_forbidden_tokens($value) { /** * Validate if a string contains forbidden tokens not allowed by privacy rules. * - * @param $token_string + * @param string $token_string * A string with one or more tokens to be validated. + * * @return boolean * TRUE if blacklisted token has been found, otherwise FALSE. */ @@ -724,7 +764,7 @@ function _googleanalytics_extract_create_field_values($string) { $list = array_map('trim', $list); $list = array_filter($list, 'strlen'); - foreach ($list as $position => $text) { + foreach ($list as $text) { // Check for an explicit key. $matches = array(); if (preg_match('/(.*)\|(.*)/', $text, $matches)) { @@ -755,16 +795,18 @@ function _googleanalytics_validate_create_field_name($name) { // List of supported field names: // https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#create $create_only_fields = array( - 'clientId', - 'userId', - 'sampleRate', - 'siteSpeedSampleRate', - 'alwaysSendReferrer', 'allowAnchor', + 'alwaysSendReferrer', + 'clientId', 'cookieName', 'cookieDomain', 'cookieExpires', 'legacyCookieDomain', + 'legacyHistoryImport', + 'sampleRate', + 'siteSpeedSampleRate', + 'storage', + 'useAmpClientId', ); if ($name == 'name') { @@ -773,6 +815,9 @@ function _googleanalytics_validate_create_field_name($name) { if ($name == 'allowLinker') { return t('Create only field name %name is a disallowed field name. Please select Multiple top-level domains under What are you tracking to enable cross domain tracking.', array('%name' => $name)); } + if ($name == 'userId') { + return t('Create only field name %name is a disallowed field name. Please enable Track User ID under Tracking scope > Users.', array('%name' => $name)); + } if (!in_array($name, $create_only_fields)) { return t('Create only field name %name is an unknown field name. Field names are case sensitive. Please see create only fields documentation for supported field names.', array('%name' => $name, '@url' => 'https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#create')); } diff --git a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.admin.js b/sites/all/modules/contrib/admin/google_analytics/googleanalytics.admin.js index 083eeaa..c81470d 100644 --- a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.admin.js +++ b/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'); } diff --git a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.debug.js b/sites/all/modules/contrib/admin/google_analytics/googleanalytics.debug.js index 861f7fd..7186230 100644 --- a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.debug.js +++ b/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. diff --git a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.info b/sites/all/modules/contrib/admin/google_analytics/googleanalytics.info index adbf134..591816a 100644 --- a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.info +++ b/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" diff --git a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.install b/sites/all/modules/contrib/admin/google_analytics/googleanalytics.install index d7b2edf..08b4e05 100644 --- a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.install +++ b/sites/all/modules/contrib/admin/google_analytics/googleanalytics.install @@ -10,6 +10,7 @@ */ function googleanalytics_uninstall() { variable_del('googleanalytics_account'); + variable_del('googleanalytics_premium'); variable_del('googleanalytics_cache'); variable_del('googleanalytics_codesnippet_create'); variable_del('googleanalytics_codesnippet_before'); @@ -25,6 +26,7 @@ function googleanalytics_uninstall() { variable_del('googleanalytics_roles'); variable_del('googleanalytics_site_search'); variable_del('googleanalytics_trackadsense'); + variable_del('googleanalytics_trackcolorbox'); variable_del('googleanalytics_trackdoubleclick'); variable_del('googleanalytics_tracker_anonymizeip'); variable_del('googleanalytics_trackfiles'); @@ -40,7 +42,7 @@ function googleanalytics_uninstall() { variable_del('googleanalytics_visibility_roles'); variable_del('googleanalytics_privacy_donottrack'); - // Remove backup variables if exist. Remove this code in D8. + // Remove backup variables if they exist. Remove this code in D8. variable_del('googleanalytics_codesnippet_after_backup_7200'); variable_del('googleanalytics_codesnippet_before_backup_7200'); } @@ -87,7 +89,8 @@ function googleanalytics_requirements($phase) { } /** - * Upgrade old extension variable to new and use old name as enabled/disabled flag. + * Upgrade old extension variable to new and use old name as enabled/disabled + * flag. */ function googleanalytics_update_6000() { variable_set('googleanalytics_trackfiles_extensions', variable_get('googleanalytics_trackfiles', '7z|aac|avi|csv|doc|exe|flv|gif|gz|jpe?g|js|mp(3|4|e?g)|mov|pdf|phps|png|ppt|rar|sit|tar|torrent|txt|wma|wmv|xls|xml|zip')); @@ -100,7 +103,8 @@ function googleanalytics_update_6000() { function googleanalytics_update_6001() { variable_set('googleanalytics_visibility', 0); - // Remove tracking from all administrative pages, see http://drupal.org/node/34970. + // Remove tracking from all administrative pages, see: + // https://drupal.org/node/34970. $pages = array( 'admin*', 'user*', @@ -113,11 +117,12 @@ function googleanalytics_update_6001() { } /** - * Upgrade role settings and per user tracking settings - * of "User 1" and remove outdated tracking variables. + * Upgrade role settings and per user tracking settings of "User 1" and remove + * outdated tracking variables. */ function googleanalytics_update_6002() { - // Upgrade enabled/disabled roles to new logic (correct for upgrades from 5.x-1.4 and 6.x-1.0). + // Upgrade enabled/disabled roles to new logic (correct for upgrades from + // 5.x-1.4 and 6.x-1.0). $roles = array(); $messages = array(); foreach (user_roles() as $rid => $name) { @@ -136,7 +141,7 @@ function googleanalytics_update_6002() { if (!$track_user1 = variable_get('googleanalytics_track__user1', 1)) { variable_set('googleanalytics_custom', 1); - // Load user 1 object, set appropriate value and save new user settings back. + // Load user 1 object, set appropriate value and save new user settings. $account = user_load(1); $account = user_save($account, array('data' => array('googleanalytics' => array('custom' => 0))), 'account'); $messages[] = t('Disabled user specific page tracking for site administrator.'); @@ -151,11 +156,11 @@ function googleanalytics_update_6002() { } /** - * #262468: Clear menu cache to solve stale menu data in 5.x-1.5 and 6.x-1.1 + * #262468: Clear menu cache to solve stale menu data in 5.x-1.5 and 6.x-1.1. */ function googleanalytics_update_6003() { menu_rebuild(); - return t('Menu has been rebuild.'); + return t('Menu has been rebuilt.'); } /** @@ -230,8 +235,9 @@ function googleanalytics_update_6006() { /** * Remove "User identifier" and "User name" from segmentation fields. * - * This is a data protection and privacy law change. For more information see Google Analytics - * terms of use section 8.1 (http://www.google.com/analytics/en-GB/tos.html). + * This is a data protection and privacy law change. For more information see + * Google Analytics terms of use section 8.1: + * https://www.google.com/analytics/en-GB/tos.html */ function googleanalytics_update_6007() { $profile_fields = variable_get('googleanalytics_segmentation', array()); @@ -292,15 +298,15 @@ function googleanalytics_update_6300() { $messages[] = t('Upgraded custom "before" code snippet.'); } - // Upgrade of AFTER code snippet. - // We cannot update this code snippet automatically. Show message that the upgrade has been skipped. + // Upgrade of AFTER code snippet. We cannot update this code snippet + // automatically. Show message that the upgrade has been skipped. $code_after = variable_get('googleanalytics_codesnippet_after', ''); if (!empty($code_after)) { drupal_set_message(Database::getConnection()->prefixTables("Automatic upgrade of Google Analytics custom 'after' code snippet has been skipped. Backup of previous code snippet has been saved in database table '{variable}' as 'googleanalytics_codesnippet_after_backup_6300'. You need to manually upgrade the custom 'after' code snippet."), 'error'); $messages[] = t('Skipped custom "after" code snippet.'); } - return empty($messages) ? t('No custom code snipped found. Nothing to do.') : implode(' ', $messages); + return empty($messages) ? t('No custom code snippet found. Nothing to do.') : implode(' ', $messages); } /** @@ -355,18 +361,20 @@ function googleanalytics_update_7002() { $googleanalytics_custom_vars['slots'][1]['slot'] = 1; $googleanalytics_custom_vars['slots'][1]['name'] = 'User roles'; $googleanalytics_custom_vars['slots'][1]['value'] = '[current-user:role-names]'; - $googleanalytics_custom_vars['slots'][1]['scope'] = 1; // Sets the scope to visitor-level. + // Sets the scope to visitor-level. + $googleanalytics_custom_vars['slots'][1]['scope'] = 1; variable_set('googleanalytics_custom_var', $googleanalytics_custom_vars); - return t('The deprecated profile segmentation setting for "User roles" has been added to custom variables. You need to deselect all selected profile fields in Google Analytics settings and upgrade other profile fields manually or you may loose tracking data in future! See Google Analytics Custom Variables for more information.', array('@customvar' => 'https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingCustomVariables', '@admin' => url('admin/config/system/googleanalytics'))); + return t('The deprecated profile segmentation setting for "User roles" has been added to custom variables. You need to deselect all selected profile fields in Google Analytics settings and upgrade other profile fields manually or you may lose tracking data in future! See Google Analytics Custom Variables for more information.', array('@customvar' => 'https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingCustomVariables', '@admin' => url('admin/config/system/googleanalytics'))); } else { - return t('You need to deselect all selected profile fields in Google Analytics settings and upgrade other profile fields manually or you may loose tracking data in future! See Google Analytics Custom Variables for more information.', array('@customvar' => 'https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingCustomVariables', '@admin' => url('admin/config/system/googleanalytics'))); + return t('You need to deselect all selected profile fields in Google Analytics settings and upgrade other profile fields manually or you may lose tracking data in future! See Google Analytics Custom Variables for more information.', array('@customvar' => 'https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingCustomVariables', '@admin' => url('admin/config/system/googleanalytics'))); } } /** - * Rename googleanalytics_trackoutgoing variable to googleanalytics_trackoutbound. + * Rename googleanalytics_trackoutgoing variable to + * googleanalytics_trackoutbound. */ function googleanalytics_update_7003() { variable_set('googleanalytics_trackoutbound', variable_get('googleanalytics_trackoutgoing', 1)); @@ -376,7 +384,8 @@ function googleanalytics_update_7003() { } /** - * Rename googleanalytics_visibility variable to googleanalytics_visibility_pages for consistency. + * Rename googleanalytics_visibility variable to + * googleanalytics_visibility_pages for consistency. */ function googleanalytics_update_7004() { variable_set('googleanalytics_visibility_pages', variable_get('googleanalytics_visibility', 1)); @@ -400,7 +409,7 @@ function googleanalytics_update_7005() { $diff = array_diff($pages, preg_split('/(\r\n?|\n)/', variable_get('googleanalytics_pages', implode("\n", $pages)))); if (empty($diff)) { - // No diff to previous settings found. Update with new settings. + // No difference to previous settings found. Update with new settings. $pages = array( 'admin', 'admin/*', @@ -436,7 +445,8 @@ function googleanalytics_update_7007() { } /** - * Delete custom ga.js code snipptes to prevent malfunctions in new Universal Analytics tracker. A backup of your snippets will be created. + * Delete custom ga.js code snippets to prevent malfunctions in new Universal + * Analytics tracker. A backup of your snippets will be created. */ function googleanalytics_update_7200() { $messages = array(); @@ -446,23 +456,24 @@ function googleanalytics_update_7200() { if (!empty($googleanalytics_codesnippet_before) && stristr($googleanalytics_codesnippet_before, '_gaq.push(')) { variable_set('googleanalytics_codesnippet_before_backup_7200', $googleanalytics_codesnippet_before); variable_del('googleanalytics_codesnippet_before'); - drupal_set_message(Database::getConnection()->prefixTables("A backup of your previous Google Analytics code snippet has been saved in database table '{variable}' as 'googleanalytics_codesnippet_before_backup_7200'. You need to manually upgrade the custom 'before' code snippet."), 'warning'); - $messages[] = t('Manual upgrade of custom "before" code snippet is required.'); + drupal_set_message(Database::getConnection()->prefixTables("A backup of your previous Google Analytics code snippet (ga.js) has been saved in database table '{variable}' as 'googleanalytics_codesnippet_before_backup_7200'. You need to manually upgrade the custom 'before' code snippet to analytics.js API."), 'warning'); + $messages[] = t('Manual upgrade of custom "before" code snippet from ja.js to analytics.js API is required.'); } $googleanalytics_codesnippet_after = variable_get('googleanalytics_codesnippet_after', ''); if (!empty($googleanalytics_codesnippet_after) && stristr($googleanalytics_codesnippet_after, '_gaq.push(')) { variable_set('googleanalytics_codesnippet_after_backup_7200', $googleanalytics_codesnippet_after); variable_del('googleanalytics_codesnippet_after'); - drupal_set_message(Database::getConnection()->prefixTables("A backup of your previous Google Analytics code snippet has been saved in database table '{variable}' as 'googleanalytics_codesnippet_before_backup_7200'. You need to manually upgrade the custom 'before' code snippet."), 'warning'); - $messages[] = t('Manual upgrade of custom "after" code snippet is required.'); + drupal_set_message(Database::getConnection()->prefixTables("A backup of your previous Google Analytics code snippet (ga.js) has been saved in database table '{variable}' as 'googleanalytics_codesnippet_after_backup_7200'. You need to manually upgrade the custom 'after' code snippet to analytics.js API."), 'warning'); + $messages[] = t('Manual upgrade of custom "after" code snippet from ja.js to analytics.js API is required.'); } - return empty($messages) ? t('No custom code snipped found. Nothing to do.') : implode(' ', $messages); + return empty($messages) ? t('No custom code snippet found. Nothing to do.') : implode(' ', $messages); } /** - * Delete obsolete custom variables. Custom variables are now custom dimensions and metrics. + * Delete obsolete custom variables. Custom variables are now custom dimensions + * and metrics. */ function googleanalytics_update_7201() { variable_del('googleanalytics_custom_var'); @@ -474,7 +485,7 @@ function googleanalytics_update_7201() { * Delete obsolete JavaScript scope variable. */ function googleanalytics_update_7202() { - // Remove obsolete scope variable + // Remove obsolete scope variable. variable_del('googleanalytics_js_scope'); return t('Removed obsolete JavaScript scope variable.'); diff --git a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.js b/sites/all/modules/contrib/admin/google_analytics/googleanalytics.js index 1eed3c3..5ba42ca 100644 --- a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.js +++ b/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. diff --git a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.module b/sites/all/modules/contrib/admin/google_analytics/googleanalytics.module index 4051b1b..0026703 100644 --- a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.module +++ b/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 + * @author: Alexander Hass */ /** @@ -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('Google Analytics is a free (registration required) website traffic and marketing effectiveness service.', array('@ga_url' => 'http://www.google.com/analytics/')); + return t('Google Analytics 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. diff --git a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.test b/sites/all/modules/contrib/admin/google_analytics/googleanalytics.test index 0b64bb8..52c3148 100644 --- a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.test +++ b/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 message with html tags and link.', 'error'); + //drupal_set_message('Example error message with html tags and link.', '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', ''); $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', ''); $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', ''); diff --git a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.test.js b/sites/all/modules/contrib/admin/google_analytics/googleanalytics.test.js index 05c720f..f22d6f1 100644 --- a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.test.js +++ b/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.'); diff --git a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.variable.inc b/sites/all/modules/contrib/admin/google_analytics/googleanalytics.variable.inc index 6a65735..5b88b1f 100644 --- a/sites/all/modules/contrib/admin/google_analytics/googleanalytics.variable.inc +++ b/sites/all/modules/contrib/admin/google_analytics/googleanalytics.variable.inc @@ -13,7 +13,7 @@ function googleanalytics_variable_info($options) { 'type' => 'string', 'title' => t('Web Property ID', array(), $options), 'default' => 'UA-', - 'description' => t('This ID is unique to each site you want to track separately, and is in the form of UA-xxxxxxx-yy. To get a Web Property ID, register your site with Google Analytics, or if you already have registered your site, go to your Google Analytics Settings page to see the ID next to every site profile. Find more information in the documentation.', array('@analytics' => 'http://www.google.com/analytics/', '@webpropertyid' => url('https://developers.google.com/analytics/resources/concepts/gaConceptsAccounts', array('fragment' => 'webProperty'))), $options), + 'description' => t('This ID is unique to each site you want to track separately, and is in the form of UA-xxxxxxx-yy. To get a Web Property ID, register your site with Google Analytics, or if you already have registered your site, go to your Google Analytics Settings page to see the ID next to every site profile. Find more information in the documentation.', array('@analytics' => 'https://marketingplatform.google.com/about/analytics/', '@webpropertyid' => url('https://developers.google.com/analytics/resources/concepts/gaConceptsAccounts', array('fragment' => 'webProperty'))), $options), 'required' => TRUE, 'group' => 'googleanalytics', 'localize' => TRUE, @@ -40,6 +40,10 @@ function googleanalytics_variable_group_info() { /** * Validate Web Property ID variable. + * + * @param array $variable + * + * @return string */ function googleanalytics_validate_googleanalytics_account($variable) { // Replace all type of dashes (n-dash, m-dash, minus) with the normal dashes. diff --git a/sites/all/modules/contrib/admin/module_filter/CHANGELOG.txt b/sites/all/modules/contrib/admin/module_filter/CHANGELOG.txt old mode 100755 new mode 100644 index 218bc30..5513831 --- a/sites/all/modules/contrib/admin/module_filter/CHANGELOG.txt +++ b/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 --------------------------------- +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-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. -#1933384 by kaidjohnson: Fixed jQuery UI button breaks functionality. -#1166414 by greenSkin: Fixed module filter + tabs disabled = broken modules - page. - - -Module Filter 7.x-1.7, 2012-07-05 ---------------------------------- +#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. diff --git a/sites/all/modules/contrib/admin/module_filter/LICENSE.txt b/sites/all/modules/contrib/admin/module_filter/LICENSE.txt old mode 100755 new mode 100644 diff --git a/sites/all/modules/contrib/admin/module_filter/README.txt b/sites/all/modules/contrib/admin/module_filter/README.txt new file mode 100644 index 0000000..b03d970 --- /dev/null +++ b/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"'). diff --git a/sites/all/modules/contrib/admin/module_filter/css/dynamic_position.css b/sites/all/modules/contrib/admin/module_filter/css/dynamic_position.css new file mode 100644 index 0000000..bc3b3e7 --- /dev/null +++ b/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; +} diff --git a/sites/all/modules/contrib/admin/module_filter/css/module_filter.css b/sites/all/modules/contrib/admin/module_filter/css/module_filter.css old mode 100755 new mode 100644 index d2353ba..b03bbb7 --- a/sites/all/modules/contrib/admin/module_filter/css/module_filter.css +++ b/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; +} diff --git a/sites/all/modules/contrib/admin/module_filter/css/module_filter_tab-rtl.css b/sites/all/modules/contrib/admin/module_filter/css/module_filter_tab-rtl.css new file mode 100644 index 0000000..b70f6ef --- /dev/null +++ b/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; +} diff --git a/sites/all/modules/contrib/admin/module_filter/css/module_filter_tab.css b/sites/all/modules/contrib/admin/module_filter/css/module_filter_tab.css old mode 100755 new mode 100644 index 47e7337..fd7d0f0 --- a/sites/all/modules/contrib/admin/module_filter/css/module_filter_tab.css +++ b/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: 0; + line-height: 1; +} +#module-filter-tabs li { + background: #eee; + border-top: 1px solid #ccc; + padding: 0; + margin: 0; + min-width: 0; +} +#module-filter-tabs li#new-tab { + margin-bottom: 10px; + border-bottom: 1px solid #ccc; +} +#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-tabs li.disabled a, +#module-filter-tabs li.disabled span { + color: #999; +} +#module-filter-tabs li.suggest { + background: #F9F9F9; +} +#module-filter-tabs li a { + display: block; + text-decoration: none; + padding: 10px; +} +#module-filter-tabs li a span.result-info { + float: right; + font-size: 10px; + color: #999; + margin-top: 3px; +} +#module-filter-tabs li a span.visual-aid { + font-size: 8px; +/* float: right;*/ +} +#module-filter-tabs li span.visual-aid { + font-weight: bold; +} +#module-filter-tabs li a span.enabling { + color: green; +} +#module-filter-tabs li a span.disabling { + color: red; +} +#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; +} +#module-filter-tabs li.selected .summary .count { + display: block; +} +html.js #module-filter-submit input { + margin: 2em 2em 1em; +} +html.js .module-filter-inputs-wrapper { + text-align: center; +} +html.js .module-filter-inputs-wrapper .form-item { margin: 0; padding: 9px; } -#module-filter-wrapper .form-item:after { -/* display: block;*/ - clear: none; +html.js .module-filter-inputs-wrapper label { + display: inline; } -#module-filter-left { - float: left; - background-color: #F6F6F6; - border: 1px solid #D6DBDE; - margin-right: -1px; - width: 185px; +html.js .module-filter-inputs-wrapper input[name="module_filter[name]"] { + width: 80%; } -#module-filter-left ul { - margin: 0px; - padding: 0px; - list-style: none; +html.js #module-filter-show-wrapper { + margin-bottom: 1em; } -#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; +html.js #module-filter-modules { + margin-left: 240px; + border: 1px solid #ccc; } -#module-filter-left ul li.active { - margin-right: -1px; - width: 186px; - background-color: #FFFFFF; - position: relative; +#module-filter-modules table { + border-top: none; + border-right: none; + border-left: none; } -#module-filter-left ul li a { - color: #777777; - 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; -} -#module-filter-left ul li.active a:hover { - background-color: #FFFFFF; -} -#module-filter-left ul li a span.visual-aid { - font-size: 8px; - float: right; -} -#module-filter-left ul li a span.enabling { - color: green; -} -#module-filter-left ul li a span.disabling { - color: red; - margin-left: 5px; -} -#module-filter-left ul li a span.counts { - font-weight: normal; - display: none; - font-size: 0.8em; - color: #333333; - padding: 2px 0 0; -} -#module-filter-left ul li.active a span.counts { - display: block; -} -#module-filter-submit { +html.js #module-filter-modules table { margin: 0; + border-bottom: none; } -#module-filter-submit .form-actions { - text-align: center; +html.js #module-filter-modules table tr, +html.js #module-filter-modules table td { + border-left: 0; + border-right: 0; } -#module-filter-submit input.form-submit { - margin: 1em 0 0; +#module-filter-modules table thead { + display: none; } -#module-filter-submit.fixed { - position: fixed; - background-color: #F6F6F6; - border: 1px solid #D6DBDE; - margin-left: -1px; - width: 185px; +#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; } -#module-filter-submit.fixed-top { - top: 0; +#module-filter-modules table tr.admin-package-title.first, +#module-filter-modules table tr.admin-package-title.first td { + border-top: none !important; } -#module-filter-submit.fixed-bottom { - bottom: 0; +#module-filter-modules table tr.admin-package-header td { + border: 1px solid #ccc; + text-transform: uppercase; + background: #E1E2DC; + font-weight: normal; + padding: 3px 10px; } -#module-filter-right { - display: block; -} -#module-filter-squeeze { - margin-left: 186px; - background-color: #FFFFFF; - border: 1px solid #D6DBDE; - height: auto !important; -} -.form-item-module-filter-name { - text-align: center; -} -.form-item-module-filter-name label { - display: inline; -} -#module-filter-show-wrapper .form-checkboxes { - text-align: center; -} -#module-filter-show-wrapper .form-item:after { - display: inline; -} -table.package { - margin: 1em 0; -} -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; -} -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; +} diff --git a/sites/all/modules/contrib/admin/module_filter/css/modules.css b/sites/all/modules/contrib/admin/module_filter/css/modules.css new file mode 100644 index 0000000..0659825 --- /dev/null +++ b/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; +} diff --git a/sites/all/modules/contrib/admin/module_filter/css/update_status.css b/sites/all/modules/contrib/admin/module_filter/css/update_status.css new file mode 100644 index 0000000..c0bee44 --- /dev/null +++ b/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; +} diff --git a/sites/all/modules/contrib/admin/module_filter/images/collapsed.png b/sites/all/modules/contrib/admin/module_filter/images/collapsed.png new file mode 100644 index 0000000..91f3fd4 Binary files /dev/null and b/sites/all/modules/contrib/admin/module_filter/images/collapsed.png differ diff --git a/sites/all/modules/contrib/admin/module_filter/images/expanded.png b/sites/all/modules/contrib/admin/module_filter/images/expanded.png new file mode 100644 index 0000000..46f39ec Binary files /dev/null and b/sites/all/modules/contrib/admin/module_filter/images/expanded.png differ diff --git a/sites/all/modules/contrib/admin/module_filter/js/dynamic_position.js b/sites/all/modules/contrib/admin/module_filter/js/dynamic_position.js old mode 100755 new mode 100644 index 10c66b4..41c5731 --- a/sites/all/modules/contrib/admin/module_filter/js/dynamic_position.js +++ b/sites/all/modules/contrib/admin/module_filter/js/dynamic_position.js @@ -1,33 +1,48 @@ (function($) { - Drupal.behaviors.moduleFilterDynamicPosition = { - attach: function() { - $(window).scroll(function() { + +Drupal.behaviors.moduleFilterDynamicPosition = { + attach: function(context) { + var $window = $(window); + + $('#module-filter-wrapper', context).once('dynamic-position', function() { + // Move the submit button just below the tabs. + $('#module-filter-tabs').append($('#module-filter-submit')); + + var positionSubmit = function() { + var $tabs = $('#module-filter-tabs'); + var $submit = $('#module-filter-submit', $tabs); + // Vertical movement. - var top = $('#module-filter-tabs').offset().top; - var bottom = top + $('#module-filter-tabs').height(); - var windowHeight = $(window).height(); - if (((bottom - windowHeight) > ($(window).scrollTop() - $('#module-filter-submit').height())) && $(window).scrollTop() + windowHeight - $('#module-filter-submit').height() - $('#all-tab').height() > top) { - $('#module-filter-submit').removeClass('fixed-top').addClass('fixed fixed-bottom'); + var bottom = $tabs.offset().top + $tabs.outerHeight(); + if ($submit.hasClass('fixed-bottom')) { + bottom += $submit.height(); } - else if (bottom < $(window).scrollTop()) { - $('#module-filter-submit').removeClass('fixed-bottom').addClass('fixed fixed-top'); + if (bottom >= $window.height() + $window.scrollTop()) { + $submit.addClass('fixed fixed-bottom'); + $tabs.css('padding-bottom', $submit.height()); } else { - $('#module-filter-submit').removeClass('fixed fixed-bottom fixed-top'); + $submit.removeClass('fixed fixed-bottom'); + $tabs.css('padding-bottom', 0); } // Horizontal movement. - if ($('#module-filter-submit').hasClass('fixed-bottom') || $('#module-filter-submit').hasClass('fixed-top')) { - var left = $('#module-filter-tabs').offset().left - $(window).scrollLeft(); - if (left != $('#module-filter-submit').offset().left - $(window).scrollLeft()) { - $('#module-filter-submit').css('left', left); + if ($submit.hasClass('fixed-bottom') || $submit.hasClass('fixed-top')) { + var left = $tabs.offset().left - $window.scrollLeft(); + if (left != $submit.offset().left - $window.scrollLeft()) { + $submit.css('left', left); } } - }); - $(window).trigger('scroll'); - $(window).resize(function() { - $(window).trigger('scroll'); - }); - } + }; + + // Control the positioning. + $window.scroll(positionSubmit); + $window.resize(positionSubmit); + var moduleFilter = $('input[name="module_filter[name]"]').data('moduleFilter'); + moduleFilter.element.bind('moduleFilter:adjustHeight', positionSubmit); + moduleFilter.adjustHeight(); + }); } +}; + })(jQuery); diff --git a/sites/all/modules/contrib/admin/module_filter/js/module_filter.js b/sites/all/modules/contrib/admin/module_filter/js/module_filter.js old mode 100755 new mode 100644 index 49f6b7a..4fcf6e6 --- a/sites/all/modules/contrib/admin/module_filter/js/module_filter.js +++ b/sites/all/modules/contrib/admin/module_filter/js/module_filter.js @@ -1,120 +1,274 @@ - (function ($) { - var moduleFilterTimeOut; - var moduleFilterTextFilter = ''; - Drupal.behaviors.moduleFilter = { - attach: function() { - $("#module-filter-wrapper").show(); - $('input[name="module_filter[name]"]').focus(); - $('input[name="module_filter[name]"]').keyup(function(e) { - switch (e.which) { - case 13: - if (moduleFilterTimeOut) { - clearTimeout(moduleFilterTimeOut); - } +Drupal.ModuleFilter = {}; - moduleFilter(moduleFilterTextFilter); - break; - default: - if (moduleFilterTextFilter != $(this).val()) { - moduleFilterTextFilter = this.value; - if (moduleFilterTimeOut) { - clearTimeout(moduleFilterTimeOut); - } +Drupal.ModuleFilter.explode = function(string) { + var queryArray = string.match(/([a-zA-Z]+\:(\w+|"[^"]+")*)|\w+|"[^"]+"/g); + if (!queryArray) { + queryArray = new Array(); + } + var i = queryArray.length; + while (i--) { + queryArray[i] = queryArray[i].replace(/"/g, ""); + } + return queryArray; +}; - moduleFilterTimeOut = setTimeout('moduleFilter("' + moduleFilterTextFilter + '")', 500); - } - break; +Drupal.ModuleFilter.getState = function(key) { + if (!Drupal.ModuleFilter.state) { + Drupal.ModuleFilter.state = {}; + var cookie = $.cookie('DrupalModuleFilter'); + var query = cookie ? cookie.split('&') : []; + if (query) { + for (var i in query) { + // Extra check to avoid js errors in Chrome, IE and Safari when + // combined with JS like twitter's widget.js. + // See http://drupal.org/node/798764. + if (typeof(query[i]) == 'string' && query[i].indexOf('=') != -1) { + var values = query[i].split('='); + if (values.length === 2) { + Drupal.ModuleFilter.state[values[0]] = values[1]; + } } - }); - $('input[name="module_filter[name]"]').keypress(function(e) { - if (e.which == 13) e.preventDefault(); - }); - - $('#edit-module-filter-show-enabled').change(function() { - moduleFilter($('input[name="module_filter[name]"]').val()); - }); - $('#edit-module-filter-show-disabled').change(function() { - moduleFilter($('input[name="module_filter[name]"]').val()); - }); - $('#edit-module-filter-show-required').change(function() { - moduleFilter($('input[name="module_filter[name]"]').val()); - }); - $('#edit-module-filter-show-unavailable').change(function() { - moduleFilter($('input[name="module_filter[name]"]').val()); - }); + } } } + return Drupal.ModuleFilter.state[key] ? Drupal.ModuleFilter.state[key] : false; +}; - moduleFilter = function(string) { - stringLowerCase = string.toLowerCase(); +Drupal.ModuleFilter.setState = function(key, value) { + var existing = Drupal.ModuleFilter.getState(key); + if (existing != value) { + Drupal.ModuleFilter.state[key] = value; + var query = []; + for (var i in Drupal.ModuleFilter.state) { + query.push(i + '=' + Drupal.ModuleFilter.state[i]); + } + $.cookie('DrupalModuleFilter', query.join('&'), { expires: 7, path: '/' }); + } +}; - $("fieldset table tbody tr td label strong").each(function(i) { - var $row = $(this).parents('tr'); - var module = $(this).text(); - var moduleLowerCase = module.toLowerCase(); - var $fieldset = $row.parents('fieldset'); +Drupal.ModuleFilter.Filter = function(element, selector, options) { + var self = this; - if (string != '') { - if ($fieldset.hasClass('collapsed')) { - $fieldset.removeClass('collapsed'); + this.element = element; + this.text = $(this.element).val(); + + this.settings = Drupal.settings.moduleFilter; + + this.selector = selector; + + this.options = $.extend({ + delay: 500, + striping: false, + childSelector: null, + empty: Drupal.t('No results'), + rules: new Array() + }, options); + if (this.options.wrapper == undefined) { + this.options.wrapper = $(self.selector).parent(); + } + + // Add clear button. + this.element.after(''); + if (this.text) { + $('.module-filter-clear a', this.element.parent()).removeClass('js-hide'); + } + $('.module-filter-clear a', this.element.parent()).click(function() { + self.element.val(''); + self.text = ''; + delete self.queries; + self.applyFilter(); + self.element.focus(); + $(this).addClass('js-hide'); + return false; + }); + + this.updateQueries = function() { + var queryStrings = Drupal.ModuleFilter.explode(self.text); + + self.queries = new Array(); + for (var i in queryStrings) { + var query = { operator: 'text', string: queryStrings[i] }; + + if (self.operators != undefined) { + // Check if an operator is possibly used. + if (queryStrings[i].indexOf(':') > 0) { + // Determine operator used. + var args = queryStrings[i].split(':', 2); + var operator = args.shift(); + if (self.operators[operator] != undefined) { + query.operator = operator; + query.string = args.shift(); + } } } - if (moduleLowerCase.match(stringLowerCase)) { - if (moduleFilterVisible($('td.checkbox input', $row))) { - if (!$row.is(':visible')) { - $row.show(); - if ($fieldset.hasClass('collapsed')) { - $fieldset.removeClass('collapsed'); - } - if (!$fieldset.is(':visible')) { - $fieldset.show(); - } + query.string = query.string.toLowerCase(); + + self.queries.push(query); + } + + if (self.queries.length <= 0) { + // Add a blank string query. + self.queries.push({ operator: 'text', string: '' }); + } + }; + + this.applyFilter = function() { + self.results = new Array(); + + self.updateQueries(); + + if (self.index == undefined) { + self.buildIndex(); + } + + self.element.trigger('moduleFilter:start'); + + $.each(self.index, function(key, item) { + var $item = item.element; + + for (var i in self.queries) { + var query = self.queries[i]; + if (query.operator == 'text') { + if (item.text.indexOf(query.string) < 0) { + continue; } } else { - $row.hide(); - if ($row.siblings(':visible').html() == null) { - $fieldset.hide(); + var func = self.operators[query.operator]; + if (!(func(query.string, self, item))) { + continue; } } - } - else { - if ($row.is(':visible')) { - $row.hide(); - if ($row.siblings(':visible').html() == null) { - $fieldset.hide(); - } - } - } - }); - } - moduleFilterVisible = function(checkbox) { - if (checkbox.length > 0) { - if ($('#edit-module-filter-show-enabled').is(':checked')) { - if ($(checkbox).is(':checked') && !$(checkbox).is(':disabled')) { + var rulesResult = self.processRules(item); + if (rulesResult !== false) { return true; } } - if ($('#edit-module-filter-show-disabled').is(':checked')) { - if (!$(checkbox).is(':checked') && !$(checkbox).is(':disabled')) { - return true; + + $item.addClass('js-hide'); + }); + + self.element.trigger('moduleFilter:finish', { results: self.results }); + + if (self.options.striping) { + self.stripe(); + } + + if (self.results.length > 0) { + self.options.wrapper.find('.module-filter-no-results').remove(); + } + else { + if (!self.options.wrapper.find('.module-filter-no-results').length) { + self.options.wrapper.append($('

').text(self.options.empty)); + }; + } + }; + + self.element.keyup(function(e) { + switch (e.which) { + case 13: + if (self.timeOut) { + clearTimeout(self.timeOut); } - } - if ($('#edit-module-filter-show-required').is(':checked')) { - if ($(checkbox).is(':checked') && $(checkbox).is(':disabled')) { - return true; + self.applyFilter(); + break; + default: + if (self.text != $(this).val()) { + if (self.timeOut) { + clearTimeout(self.timeOut); + } + + self.text = $(this).val(); + + if (self.text) { + self.element.parent().find('.module-filter-clear a').removeClass('js-hide'); + } + else { + self.element.parent().find('.module-filter-clear a').addClass('js-hide'); + } + + self.element.trigger('moduleFilter:keyup'); + + self.timeOut = setTimeout(self.applyFilter, self.options.delay); } + break; + } + }); + + self.element.keypress(function(e) { + if (e.which == 13) e.preventDefault(); + }); +}; + +Drupal.ModuleFilter.Filter.prototype.buildIndex = function() { + var self = this; + var index = new Array(); + $(this.selector).each(function(i) { + var text = (self.options.childSelector) ? $(self.options.childSelector, this).text() : $(this).text(); + var item = { + key: i, + element: $(this), + text: text.toLowerCase() + }; + for (var j in self.options.buildIndex) { + var func = self.options.buildIndex[j]; + item = $.extend(func(self, item), item); + } + $(this).data('indexKey', i); + index.push(item); + delete item; + }); + this.index = index; +}; + +Drupal.ModuleFilter.Filter.prototype.processRules = function(item) { + var self = this; + var $item = item.element; + var rulesResult = true; + if (self.options.rules.length > 0) { + for (var i in self.options.rules) { + var func = self.options.rules[i]; + rulesResult = func(self, item); + if (rulesResult === false) { + break; } } - if ($('#edit-module-filter-show-unavailable').is(':checked')) { - if (checkbox.length == 0 || ($(checkbox).size() > 0 && !$(checkbox).is(':checked') && $(checkbox).is(':disabled'))) { - return true; - } - } - return false; } + if (rulesResult !== false) { + $item.removeClass('js-hide'); + self.results.push(item); + } + return rulesResult; +}; + +Drupal.ModuleFilter.Filter.prototype.stripe = function() { + var self = this; + var flip = { even: 'odd', odd: 'even' }; + var stripe = 'odd'; + + $.each(self.index, function(key, item) { + if (!item.element.hasClass('js-hide')) { + item.element.removeClass('odd even') + .addClass(stripe); + stripe = flip[stripe]; + } + }); +}; + +$.fn.moduleFilter = function(selector, options) { + var filterInput = this; + filterInput.parents('.module-filter-inputs-wrapper').show(); + if (Drupal.settings.moduleFilter.setFocus) { + filterInput.focus(); + } + if (Drupal.settings.moduleFilter.expandedDescription) { + $('#system-modules td.description .inner.expand').addClass('expanded'); + } + filterInput.data('moduleFilter', new Drupal.ModuleFilter.Filter(this, selector, options)); +}; + })(jQuery); diff --git a/sites/all/modules/contrib/admin/module_filter/js/module_filter_tab.js b/sites/all/modules/contrib/admin/module_filter/js/module_filter_tab.js old mode 100755 new mode 100644 index a602445..c62b9ef --- a/sites/all/modules/contrib/admin/module_filter/js/module_filter_tab.js +++ b/sites/all/modules/contrib/admin/module_filter/js/module_filter_tab.js @@ -1,281 +1,559 @@ (function ($) { - Drupal.ModuleFilter = Drupal.ModuleFilter || {}; - Drupal.ModuleFilter.textFilter = ''; - Drupal.ModuleFilter.timeout; - Drupal.ModuleFilter.tabs = {}; - Drupal.ModuleFilter.enabling = {}; - Drupal.ModuleFilter.disabling = {}; - Drupal.behaviors.moduleFilter = { - attach: function() { - // Set the focus on the module filter textfield. - $('input[name="module_filter[name]"]').focus(); +Drupal.ModuleFilter.tabs = {}; +Drupal.ModuleFilter.enabling = {}; +Drupal.ModuleFilter.disabling = {}; - $('#module-filter-squeeze').css('min-height', $('#module-filter-tabs').height()); +Drupal.ModuleFilter.jQueryIsNewer = function() { + if (Drupal.ModuleFilter.jQueryNewer == undefined) { + var v1parts = $.fn.jquery.split('.'); + var v2parts = new Array('1', '4', '4'); - $('#module-filter-left a.project-tab').each(function(i) { - Drupal.ModuleFilter.tabs[$(this).attr('id')] = new Drupal.ModuleFilter.Tab(this); - }); + for (var i = 0; i < v1parts.length; ++i) { + if (v2parts.length == i) { + Drupal.ModuleFilter.jQueryNewer = true; + return Drupal.ModuleFilter.jQueryNewer; + } - // Move anchors to top of tabs. - $('a.anchor', $('#module-filter-left')).remove().prependTo('#module-filter-tabs'); + if (v1parts[i] == v2parts[i]) { + continue; + } + else if (v1parts[i] > v2parts[i]) { + Drupal.ModuleFilter.jQueryNewer = true; + return Drupal.ModuleFilter.jQueryNewer; + } + else { + Drupal.ModuleFilter.jQueryNewer = false; + return Drupal.ModuleFilter.jQueryNewer; + } + } - $('input[name="module_filter[name]"]').keyup(function(e) { - switch (e.which) { - case 13: - if (Drupal.ModuleFilter.timeout) { - clearTimeout(Drupal.ModuleFilter.timeout); - } + if (v1parts.length != v2parts.length) { + Drupal.ModuleFilter.jQueryNewer = false; + return Drupal.ModuleFilter.jQueryNewer; + } - Drupal.ModuleFilter.filter(Drupal.ModuleFilter.textFilter); - break; - default: - if (Drupal.ModuleFilter.textFilter != $(this).val()) { - Drupal.ModuleFilter.textFilter = this.value; - if (Drupal.ModuleFilter.timeout) { - clearTimeout(Drupal.ModuleFilter.timeout); + Drupal.ModuleFilter.jQueryNewer = false; + } + return Drupal.ModuleFilter.jQueryNewer; +}; + +Drupal.behaviors.moduleFilterTabs = { + attach: function(context) { + if (Drupal.settings.moduleFilter.tabs) { + $('#module-filter-wrapper table:not(.sticky-header)', context).once('module-filter-tabs', function() { + var $modules = $('#module-filter-modules'); + var moduleFilter = $('input[name="module_filter[name]"]').data('moduleFilter'); + var table = $(this); + + $('thead', table).show(); + + // Remove package header rows. + $('tr.admin-package-header', table).remove(); + + var $tabsWrapper = $('

'); + + // Build tabs from package title rows. + var tabs = '
    '; + for (var i in Drupal.settings.moduleFilter.packageIDs) { + var id = Drupal.checkPlain(Drupal.settings.moduleFilter.packageIDs[i]); + + var name = id; + var tabClass = 'project-tab'; + var title = null; + var summary = (Drupal.settings.moduleFilter.countEnabled) ? '' + Drupal.ModuleFilter.countSummary(id) + '' : ''; + + switch (id) { + case 'all': + name = Drupal.t('All'); + break; + case 'new': + name = Drupal.t('New'); + title = Drupal.t('Modules installed within the last week.'); + if (Drupal.settings.moduleFilter.enabledCounts['new'].total == 0) { + tabClass += ' disabled'; + summary += '' + Drupal.t('No modules added within the last week.') + ''; } - Drupal.ModuleFilter.timeout = setTimeout('Drupal.ModuleFilter.filter("' + Drupal.ModuleFilter.textFilter + '")', 500); - } - break; - } - }); - $('input[name="module_filter[name]"]').keypress(function(e) { - if (e.which == 13) e.preventDefault(); - }); - - Drupal.ModuleFilter.showEnabled = $('#edit-module-filter-show-enabled').is(':checked'); - $('#edit-module-filter-show-enabled').change(function() { - Drupal.ModuleFilter.showEnabled = $(this).is(':checked'); - Drupal.ModuleFilter.filter($('input[name="module_filter[name]"]').val()); - }); - Drupal.ModuleFilter.showDisabled = $('#edit-module-filter-show-disabled').is(':checked'); - $('#edit-module-filter-show-disabled').change(function() { - Drupal.ModuleFilter.showDisabled = $(this).is(':checked'); - Drupal.ModuleFilter.filter($('input[name="module_filter[name]"]').val()); - }); - Drupal.ModuleFilter.showRequired = $('#edit-module-filter-show-required').is(':checked'); - $('#edit-module-filter-show-required').change(function() { - Drupal.ModuleFilter.showRequired = $(this).is(':checked'); - Drupal.ModuleFilter.filter($('input[name="module_filter[name]"]').val()); - }); - Drupal.ModuleFilter.showUnavailable = $('#edit-module-filter-show-unavailable').is(':checked'); - $('#edit-module-filter-show-unavailable').change(function() { - Drupal.ModuleFilter.showUnavailable = $(this).is(':checked'); - Drupal.ModuleFilter.filter($('input[name="module_filter[name]"]').val()); - }); - - if (Drupal.settings.moduleFilter.visualAid == 1) { - $('table.package tbody td.checkbox input').change(function() { - if ($(this).is(':checked')) { - Drupal.ModuleFilter.updateVisualAid('enable', $(this).parents('tr')); + break; + case 'recent': + name = Drupal.t('Recent'); + title = Drupal.t('Modules enabled/disabled within the last week.'); + if (Drupal.settings.moduleFilter.enabledCounts['recent'].total == 0) { + tabClass += ' disabled'; + summary += '' + Drupal.t('No modules were enabled or disabled within the last week.') + ''; + } + break; + default: + var $row = $('#' + id + '-package', this); + name = Drupal.checkPlain($.trim($row.text())); + $row.remove(); + break; } - else { - Drupal.ModuleFilter.updateVisualAid('disable', $(this).parents('tr')); + + tabs += '
  • ' + name + '' + summary + '
  • '; + } + tabs += '
'; + $tabsWrapper.append(tabs); + $modules.before($tabsWrapper); + + // Index tabs. + $('#module-filter-tabs li').each(function() { + var $tab = $(this); + var id = $tab.attr('id'); + Drupal.ModuleFilter.tabs[id] = new Drupal.ModuleFilter.Tab($tab, id); + }); + + $('tbody td.checkbox input', $modules).change(function() { + var $checkbox = $(this); + var key = $checkbox.parents('tr').data('indexKey'); + + moduleFilter.index[key].status = $checkbox.is(':checked'); + + if (Drupal.settings.moduleFilter.visualAid) { + var type = ($checkbox.is(':checked')) ? 'enable' : 'disable'; + Drupal.ModuleFilter.updateVisualAid(type, $checkbox.parents('tr')); } }); - } - // Check for anchor. - var url = document.location.toString(); - if (url.match('#')) { - // Make tab active based on anchor. - var anchor = '#' + url.split('#')[1]; - $('a[href="' + anchor + '"]').click(); - } - // Else if no active tab is defined, set it to the all tab. - else if (Drupal.ModuleFilter.activeTab == undefined) { - Drupal.ModuleFilter.activeTab = Drupal.ModuleFilter.tabs['all-tab']; + // Sort rows. + var rows = $('tbody tr.module', table).get(); + rows.sort(function(a, b) { + var compA = $('td:nth(1)', a).text().toLowerCase(); + var compB = $('td:nth(1)', b).text().toLowerCase(); + return (compA < compB) ? -1 : (compA > compB) ? 1 : 0; + }); + $.each(rows, function(idx, itm) { table.append(itm); }); + + // Re-stripe rows. + $('tr.module', table) + .removeClass('odd even') + .filter(':odd').addClass('even').end() + .filter(':even').addClass('odd'); + + moduleFilter.adjustHeight(); + + moduleFilter.element.bind('moduleFilter:start', function() { + moduleFilter.tabResults = { + 'all-tab': { items: {}, count: 0 }, + 'recent-tab': { items: {}, count: 0 }, + 'new-tab': { items: {}, count: 0 } + }; + + // Empty result info from tabs. + for (var i in Drupal.ModuleFilter.tabs) { + if (Drupal.ModuleFilter.tabs[i].resultInfo != undefined) { + Drupal.ModuleFilter.tabs[i].resultInfo.empty(); + } + } + }); + + moduleFilter.element.bind('moduleFilter:finish', function(e, data) { + $.each(moduleFilter.index, function(key, item) { + if (!item.element.hasClass('js-hide')) { + var id = Drupal.ModuleFilter.getTabID(item.element); + + if (moduleFilter.tabResults[id] == undefined) { + moduleFilter.tabResults[id] = { items: {}, count: 0 }; + } + if (moduleFilter.tabResults[id].items[item.key] == undefined) { + // All tab + moduleFilter.tabResults['all-tab'].count++; + + // Recent tab + if (item.element.hasClass('recent-module')) { + moduleFilter.tabResults['recent-tab'].count++; + } + + // New tab + if (item.element.hasClass('new-module')) { + moduleFilter.tabResults['new-tab'].count++; + } + + moduleFilter.tabResults[id].items[item.key] = item; + moduleFilter.tabResults[id].count++; + } + + if (Drupal.ModuleFilter.activeTab != undefined && Drupal.ModuleFilter.activeTab.id != 'all-tab') { + if ((Drupal.ModuleFilter.activeTab.id == 'recent-tab' && !item.element.hasClass('recent-module')) || (Drupal.ModuleFilter.activeTab.id == 'new-tab' && !item.element.hasClass('new-module')) || (Drupal.ModuleFilter.activeTab.id != 'recent-tab' && Drupal.ModuleFilter.activeTab.id != 'new-tab' && id != Drupal.ModuleFilter.activeTab.id)) { + // The item is not in the active tab, so hide it. + item.element.addClass('js-hide'); + } + } + } + }); + + if (Drupal.settings.moduleFilter.visualAid) { + if (moduleFilter.text) { + // Add result info to tabs. + for (var id in moduleFilter.tabResults) { + var tab = Drupal.ModuleFilter.tabs[id]; + + if (tab.resultInfo == undefined) { + var resultInfo = '' + $('a', tab.element).prepend(resultInfo); + tab.resultInfo = $('span.result-info', tab.element); + } + + tab.resultInfo.append(moduleFilter.tabResults[id].count); + } + + if (Drupal.settings.moduleFilter.hideEmptyTabs) { + for (var id in Drupal.ModuleFilter.tabs) { + if (moduleFilter.tabResults[id] != undefined) { + Drupal.ModuleFilter.tabs[id].element.show(); + } + else if (Drupal.ModuleFilter.activeTab == undefined || Drupal.ModuleFilter.activeTab.id != id) { + Drupal.ModuleFilter.tabs[id].element.hide(); + } + } + } + } + else { + // Make sure all tabs are visible. + if (Drupal.settings.moduleFilter.hideEmptyTabs) { + $('#module-filter-tabs li').show(); + } + } + } + + if ((Drupal.ModuleFilter.activeTab != undefined && (moduleFilter.tabResults[Drupal.ModuleFilter.activeTab.id] == undefined || moduleFilter.tabResults[Drupal.ModuleFilter.activeTab.id].count <= 0))) { + // The current tab contains no results. + moduleFilter.results = 0; + } + + moduleFilter.adjustHeight(); + }); + + if (Drupal.settings.moduleFilter.useURLFragment) { + $(window).bind('hashchange.module-filter', $.proxy(Drupal.ModuleFilter, 'eventHandlerOperateByURLFragment')).triggerHandler('hashchange.module-filter'); + } + else { + Drupal.ModuleFilter.selectTab(); + } + + if (Drupal.settings.moduleFilter.useSwitch) { + $('td.checkbox div.form-item', table).hide(); + $('td.checkbox', table).each(function(i) { + var $cell = $(this); + var $checkbox = $(':checkbox', $cell); + var $switch = $('.toggle-enable', $cell); + $switch.removeClass('js-hide').click(function() { + if (!$(this).hasClass('disabled')) { + if (Drupal.ModuleFilter.jQueryIsNewer()) { + $checkbox.click(); + $switch.toggleClass('off'); + } + else { + $checkbox.click().change(); + $switch.toggleClass('off'); + } + } + }); + }); + } + + var $tabs = $('#module-filter-tabs'); + + function getParentTopOffset($obj, offset) { + var $parent = $obj.offsetParent(); + if ($obj[0] != $parent[0]) { + offset += $parent.position().top; + return getParentTopOffset($parent, offset); + } + return offset; + } + + var tabsTopOffset = null; + function getParentsTopOffset() { + if (tabsTopOffset === null) { + tabsTopOffset = getParentTopOffset($tabs.parent(), 0); + } + return tabsTopOffset; + } + + function viewportTop() { + var top = $(window).scrollTop(); + return top; + } + + function viewportBottom() { + var top = $(window).scrollTop(); + var bottom = top + $(window).height(); + + bottom -= $('#page-actions').height(); + + return bottom; + } + + function fixToTop(top) { + if ($tabs.hasClass('bottom-fixed')) { + $tabs.css({ + 'position': 'absolute', + 'top': $tabs.position().top - getParentsTopOffset(), + 'bottom': 'auto' + }); + $tabs.removeClass('bottom-fixed'); + } + + if (($tabs.css('position') == 'absolute' && $tabs.offset().top - top >= 0) || ($tabs.css('position') != 'absolute' && $tabs.offset().top - top <= 0)) { + $tabs.addClass('top-fixed'); + $tabs.attr('style', ''); + } + } + + function fixToBottom(bottom) { + if ($tabs.hasClass('top-fixed')) { + $tabs.css({ + 'position': 'absolute', + 'top': $tabs.position().top - getParentsTopOffset(), + 'bottom': 'auto' + }); + $tabs.removeClass('top-fixed'); + } + + if ($tabs.offset().top + $tabs.height() - bottom <= 0) { + $tabs.addClass('bottom-fixed'); + var style = ''; + var pageActionsHeight = $('#page-actions').height(); + if (pageActionsHeight > 0) { + style = 'bottom: ' + pageActionsHeight + 'px'; + } + else if (Drupal.settings.moduleFilter.dynamicPosition) { + // style = 'bottom: ' + $('#module-filter-submit', $tabs).height() + 'px'; + } + $tabs.attr('style', style); + } + } + + var lastTop = 0; + $(window).scroll(function() { + var top = viewportTop(); + var bottom = viewportBottom(); + + if ($modules.offset().top >= top) { + $tabs.removeClass('top-fixed').attr('style', ''); + } + else { + if (top > lastTop) { // Downward scroll. + if ($tabs.height() > bottom - top) { + fixToBottom(bottom); + } + else { + fixToTop(top); + } + } + else { // Upward scroll. + fixToTop(top); + } + } + lastTop = top; + }); + + moduleFilter.adjustHeight(); + }); + } + } +}; + +Drupal.ModuleFilter.Tab = function(element, id) { + var self = this; + + this.id = id; + this.hash = id.substring(0, id.length - 4); + this.element = element; + + $('a', this.element).click(function() { + if (!Drupal.settings.moduleFilter.useURLFragment) { + var hash = (!self.element.hasClass('selected')) ? self.hash : 'all'; + Drupal.ModuleFilter.selectTab(hash); + return false; + } + + if (self.element.hasClass('selected')) { + // Clear the active tab. + window.location.hash = 'all'; + return false; + } + }); + + $('tr.' + this.id, $('#system-modules')).hover( + function() { + self.element.addClass('suggest'); + }, + function() { + self.element.removeClass('suggest'); + } + ); +}; + +Drupal.ModuleFilter.selectTab = function(hash) { + if (!hash || Drupal.ModuleFilter.tabs[hash + '-tab'] == undefined || Drupal.settings.moduleFilter.enabledCounts[hash].total == 0) { + if (Drupal.settings.moduleFilter.rememberActiveTab) { + var activeTab = Drupal.ModuleFilter.getState('activeTab'); + if (activeTab && Drupal.ModuleFilter.tabs[activeTab + '-tab'] != undefined) { + hash = activeTab; } } + + if (!hash) { + hash = 'all'; + } } - Drupal.ModuleFilter.visible = function(checkbox) { - if (checkbox.length > 0) { - if (Drupal.ModuleFilter.showEnabled) { - if ($(checkbox).is(':checked') && !$(checkbox).is(':disabled')) { - return true; - } + if (Drupal.ModuleFilter.activeTab != undefined) { + Drupal.ModuleFilter.activeTab.element.removeClass('selected'); + } + + Drupal.ModuleFilter.activeTab = Drupal.ModuleFilter.tabs[hash + '-tab']; + Drupal.ModuleFilter.activeTab.element.addClass('selected'); + + var moduleFilter = $('input[name="module_filter[name]"]').data('moduleFilter'); + var filter = moduleFilter.applyFilter(); + + if (!Drupal.ModuleFilter.modulesTop) { + Drupal.ModuleFilter.modulesTop = $('#module-filter-modules').offset().top; + } + else { + // Calculate header offset; this is important in case the site is using + // admin_menu module which has fixed positioning and is on top of everything + // else. + var headerOffset = Drupal.settings.tableHeaderOffset ? eval(Drupal.settings.tableHeaderOffset + '()') : 0; + // Scroll back to top of #module-filter-modules. + $('html, body').animate({ + scrollTop: Drupal.ModuleFilter.modulesTop - headerOffset + }, 500); + // $('html, body').scrollTop(Drupal.ModuleFilter.modulesTop); + } + + Drupal.ModuleFilter.setState('activeTab', hash); +}; + +Drupal.ModuleFilter.eventHandlerOperateByURLFragment = function(event) { + var hash = $.param.fragment(); + Drupal.ModuleFilter.selectTab(hash); +}; + +Drupal.ModuleFilter.countSummary = function(id) { + return Drupal.t('@enabled of @total', { '@enabled': Drupal.settings.moduleFilter.enabledCounts[id].enabled, '@total': Drupal.settings.moduleFilter.enabledCounts[id].total }); +}; + +Drupal.ModuleFilter.Tab.prototype.updateEnabling = function(name, remove) { + this.enabling = this.enabling || {}; + if (!remove) { + this.enabling[name] = name; + } + else { + delete this.enabling[name]; + } +}; + +Drupal.ModuleFilter.Tab.prototype.updateDisabling = function(name, remove) { + this.disabling = this.disabling || {}; + if (!remove) { + this.disabling[name] = name; + } + else { + delete this.disabling[name]; + } +}; + +Drupal.ModuleFilter.Tab.prototype.updateVisualAid = function() { + var visualAid = ''; + var enabling = new Array(); + var disabling = new Array(); + + if (this.enabling != undefined) { + for (var i in this.enabling) { + enabling.push(this.enabling[i]); + } + if (enabling.length > 0) { + enabling.sort(); + visualAid += '+' + enabling.join(', ') + ''; + } + } + if (this.disabling != undefined) { + for (var i in this.disabling) { + disabling.push(this.disabling[i]); + } + if (disabling.length > 0) { + disabling.sort(); + if (enabling.length > 0) { + visualAid += '
'; } - if (Drupal.ModuleFilter.showDisabled) { - if (!$(checkbox).is(':checked') && !$(checkbox).is(':disabled')) { - return true; - } - } - if (Drupal.ModuleFilter.showRequired) { - if ($(checkbox).is(':checked') && $(checkbox).is(':disabled')) { - return true; - } - } - } - if (Drupal.ModuleFilter.showUnavailable) { - if (checkbox.length == 0 || (!$(checkbox).is(':checked') && $(checkbox).is(':disabled'))) { - return true; + visualAid += '-' + disabling.join(', ') + ''; + } + } + + if (this.visualAid == undefined) { + $('a span.summary', this.element).append(''); + this.visualAid = $('span.visual-aid', this.element); + } + + this.visualAid.empty().append(visualAid); +}; + +Drupal.ModuleFilter.getTabID = function($row) { + var id = $row.data('moduleFilterTabID'); + if (!id) { + // Find the tab ID. + var classes = $row.attr('class').split(' '); + for (var i in classes) { + if (Drupal.ModuleFilter.tabs[classes[i]] != undefined) { + id = classes[i]; + break; } } + $row.data('moduleFilterTabID', id); + } + return id; +}; + +Drupal.ModuleFilter.updateVisualAid = function(type, $row) { + var id = Drupal.ModuleFilter.getTabID($row); + + if (!id) { return false; } - Drupal.ModuleFilter.filter = function(string) { - var stringLowerCase = string.toLowerCase(); - var flip = 'odd'; - - if (Drupal.ModuleFilter.activeTab.id == 'all-tab') { - var selector = 'table.package tbody tr td label > strong'; - } - else { - var selector = 'table.package tbody tr.' + Drupal.ModuleFilter.activeTab.id + '-content td label strong'; - } - - $(selector).each(function(i) { - var $row = $(this).parents('tr'); - var module = $(this).text(); - var moduleLowerCase = module.toLowerCase(); - - if (moduleLowerCase.match(stringLowerCase)) { - if (Drupal.ModuleFilter.visible($('td.checkbox :checkbox', $row))) { - $row.removeClass('odd even'); - $row.addClass(flip); - $row.show(); - flip = (flip == 'odd') ? 'even' : 'odd'; - } - else { - $row.hide(); - } + var tab = Drupal.ModuleFilter.tabs[id]; + var name = Drupal.checkPlain($('td:nth(1) strong', $row).text()); + switch (type) { + case 'enable': + if (Drupal.ModuleFilter.disabling[id + name] != undefined) { + delete Drupal.ModuleFilter.disabling[id + name]; + tab.updateDisabling(name, true); + $row.removeClass('disabling'); } else { - $row.hide(); + Drupal.ModuleFilter.enabling[id + name] = name; + tab.updateEnabling(name); + $row.addClass('enabling'); } - }); - } - - Drupal.ModuleFilter.Tab = function(element) { - this.id = $(element).attr('id'); - this.element = element; - - $(this.element).click(function() { - Drupal.ModuleFilter.tabs[$(this).attr('id')].setActive(); - }); - } - - Drupal.ModuleFilter.Tab.prototype.setActive = function() { - if (Drupal.ModuleFilter.activeTab) { - $(Drupal.ModuleFilter.activeTab.element).parent().removeClass('active'); - } - // Assume the default active tab is #all-tab. Remove its active class. - else { - $('#all-tab').parent().removeClass('active'); - } - - Drupal.ModuleFilter.activeTab = this; - $(Drupal.ModuleFilter.activeTab.element).parent().addClass('active'); - Drupal.ModuleFilter.activeTab.displayRows(); - - // Clear filter textfield and refocus on it. - $('input[name="module_filter[name]"]').val(''); - $('input[name="module_filter[name]"]').focus(); - } - - Drupal.ModuleFilter.Tab.prototype.displayRows = function() { - var flip = 'odd'; - var selector = (Drupal.ModuleFilter.activeTab.id == 'all-tab') ? 'table.package tbody tr' : 'table.package tbody tr.' + this.id + '-content'; - $('table.package tbody tr').hide(); - $('table.package tbody tr').removeClass('odd even'); - $(selector).each(function(i) { - if (Drupal.ModuleFilter.visible($('td.checkbox input', $(this)))) { - $(this).addClass(flip); - flip = (flip == 'odd') ? 'even' : 'odd'; - $(this).show(); + break; + case 'disable': + if (Drupal.ModuleFilter.enabling[id + name] != undefined) { + delete Drupal.ModuleFilter.enabling[id + name]; + tab.updateEnabling(name, true); + $row.removeClass('enabling'); } - }); - } - - Drupal.ModuleFilter.Tab.prototype.updateEnabling = function(amount) { - this.enabling = this.enabling || 0; - this.enabling += amount; - if (this.enabling == 0) { - delete(this.enabling); - } - } - - Drupal.ModuleFilter.Tab.prototype.updateDisabling = function(amount) { - this.disabling = this.disabling || 0; - this.disabling += amount; - if (this.disabling == 0) { - delete(this.disabling); - } - } - - Drupal.ModuleFilter.Tab.prototype.updateVisualAid = function() { - var visualAid = ''; - if (this.enabling != undefined) { - visualAid += '' + Drupal.t('+@count', { '@count': this.enabling }) + ''; - } - if (this.disabling != undefined) { - visualAid += '' + Drupal.t('-@count', { '@count': this.disabling }) + ''; - } - - if (!$('span.visual-aid', $(this.element)).size() && visualAid != '') { - $(this.element).prepend(''); - } - - $('span.visual-aid', $(this.element)).empty().append(visualAid); - } - - Drupal.ModuleFilter.updateVisualAid = function(type, row) { - // Find row class. - var classes = row.attr('class').split(' '); - for (var i in classes) { - // Remove '-content' so we can use as id. - var id = classes[i].substring(0, classes[i].length - 8); - if (Drupal.ModuleFilter.tabs[id] != undefined) { - break; + else { + Drupal.ModuleFilter.disabling[id + name] = name; + tab.updateDisabling(name); + $row.addClass('disabling'); } - } - - if (Drupal.ModuleFilter.activeTab.id == 'all-tab') { - var allTab = Drupal.ModuleFilter.activeTab; - var projectTab = Drupal.ModuleFilter.tabs[id]; - } - else { - var allTab = Drupal.ModuleFilter.tabs['all-tab']; - var projectTab = Drupal.ModuleFilter.activeTab; - } - - var name = $('td label strong', row).text(); - switch (type) { - case 'enable': - if (Drupal.ModuleFilter.disabling[id + name] != undefined) { - delete(Drupal.ModuleFilter.disabling[id + name]); - allTab.updateDisabling(-1); - projectTab.updateDisabling(-1); - row.removeClass('disabling'); - } - else { - Drupal.ModuleFilter.enabling[id + name] = name; - allTab.updateEnabling(1); - projectTab.updateEnabling(1); - row.addClass('enabling'); - } - break; - case 'disable': - if (Drupal.ModuleFilter.enabling[id + name] != undefined) { - delete(Drupal.ModuleFilter.enabling[id + name]); - allTab.updateEnabling(-1); - projectTab.updateEnabling(-1); - row.removeClass('enabling'); - } - else { - Drupal.ModuleFilter.disabling[id + name] = name; - allTab.updateDisabling(1); - projectTab.updateDisabling(1); - row.addClass('disabling'); - } - break; - } - - allTab.updateVisualAid(); - projectTab.updateVisualAid(); + break; } + + tab.updateVisualAid(); +}; + +Drupal.ModuleFilter.Filter.prototype.adjustHeight = function() { + // Hack for adjusting the height of the modules section. + var minHeight = $('#module-filter-tabs ul').height() + 10; + minHeight += $('#module-filter-tabs #module-filter-submit').height(); + $('#module-filter-modules').css('min-height', minHeight); + this.element.trigger('moduleFilter:adjustHeight'); +} + })(jQuery); diff --git a/sites/all/modules/contrib/admin/module_filter/js/modules.js b/sites/all/modules/contrib/admin/module_filter/js/modules.js new file mode 100644 index 0000000..b0b4939 --- /dev/null +++ b/sites/all/modules/contrib/admin/module_filter/js/modules.js @@ -0,0 +1,181 @@ +(function($) { + +Drupal.behaviors.moduleFilter = { + attach: function(context) { + $('#system-modules td.description').once('description', function() { + $(this).click(function() { + $('.inner.expand', $(this)).toggleClass('expanded'); + }); + $('.inner.expand', $(this)).children().click(function(e) { + if ($(this).parent().hasClass('expanded')) { + e.stopPropagation(); + } + }); + }); + + $('.module-filter-inputs-wrapper', context).once('module-filter', function() { + var filterInput = $('input[name="module_filter[name]"]', context); + var selector = '#system-modules table tbody tr'; + if (Drupal.settings.moduleFilter.tabs) { + selector += '.module'; + } + + filterInput.moduleFilter(selector, { + wrapper: $('#module-filter-modules'), + delay: 500, + striping: true, + childSelector: 'td:nth(1)', + rules: [ + function(moduleFilter, item) { + if (!item.unavailable) { + if (moduleFilter.options.showEnabled) { + if (item.status && !item.disabled) { + return true; + } + } + if (moduleFilter.options.showDisabled) { + if (!item.status && !item.disabled) { + return true; + } + } + if (moduleFilter.options.showRequired) { + if (item.status && item.disabled) { + return true; + } + } + } + if (moduleFilter.options.showUnavailable) { + if (item.unavailable || (!item.status && item.disabled)) { + return true; + } + } + return false; + } + ], + buildIndex: [ + function(moduleFilter, item) { + var $checkbox = $('td.checkbox :checkbox', item.element); + if ($checkbox.size() > 0) { + item.status = $checkbox.is(':checked'); + item.disabled = $checkbox.is(':disabled'); + } + else { + item.status = false; + item.disabled = true; + item.unavailable = true; + } + return item; + } + ], + showEnabled: $('#edit-module-filter-show-enabled').is(':checked'), + showDisabled: $('#edit-module-filter-show-disabled').is(':checked'), + showRequired: $('#edit-module-filter-show-required').is(':checked'), + showUnavailable: $('#edit-module-filter-show-unavailable').is(':checked') + }); + + var moduleFilter = filterInput.data('moduleFilter'); + + moduleFilter.operators = { + description: function(string, moduleFilter, item) { + if (item.description == undefined) { + var description = $('.description', item.element).clone(); + $('.admin-requirements', description).remove(); + $('.admin-operations', description).remove(); + item.description = description.text().toLowerCase(); + } + + if (item.description.indexOf(string) >= 0) { + return true; + } + }, + requiredBy: function(string, moduleFilter, item) { + if (item.requiredBy == undefined) { + var requirements = Drupal.ModuleFilter.getRequirements(item.element); + item.requires = requirements.requires; + item.requiredBy = requirements.requiredBy; + } + + for (var i in item.requiredBy) { + if (item.requiredBy[i].indexOf(string) >= 0) { + return true; + } + } + }, + requires: function(string, moduleFilter, item) { + if (item.requires == undefined) { + var requirements = Drupal.ModuleFilter.getRequirements(item.element); + item.requires = requirements.requires; + item.requiredBy = requirements.requiredBy; + } + + for (var i in item.requires) { + if (item.requires[i].indexOf(string) >= 0) { + return true; + } + } + } + }; + + $('#edit-module-filter-show-enabled', context).change(function() { + moduleFilter.options.showEnabled = $(this).is(':checked'); + moduleFilter.applyFilter(); + }); + $('#edit-module-filter-show-disabled', context).change(function() { + moduleFilter.options.showDisabled = $(this).is(':checked'); + moduleFilter.applyFilter(); + }); + $('#edit-module-filter-show-required', context).change(function() { + moduleFilter.options.showRequired = $(this).is(':checked'); + moduleFilter.applyFilter(); + }); + $('#edit-module-filter-show-unavailable', context).change(function() { + moduleFilter.options.showUnavailable = $(this).is(':checked'); + moduleFilter.applyFilter(); + }); + + if (!Drupal.settings.moduleFilter.tabs) { + moduleFilter.element.bind('moduleFilter:start', function() { + $('#system-modules fieldset').show(); + }); + + moduleFilter.element.bind('moduleFilter:finish', function(e, data) { + $('#system-modules fieldset').each(function(i) { + $fieldset = $(this); + if ($('tbody tr', $fieldset).filter(':visible').length == 0) { + $fieldset.hide(); + } + }); + }); + + moduleFilter.applyFilter(); + } + }); + } +}; + +Drupal.ModuleFilter.getRequirements = function(element) { + var requires = new Array(); + var requiredBy = new Array(); + $('.admin-requirements', element).each(function() { + var text = $(this).text(); + if (text.substr(0, 9) == 'Requires:') { + // Requires element. + requiresString = text.substr(9); + requires = requiresString.replace(/\([a-z]*\)/g, '').split(','); + } + else if (text.substr(0, 12) == 'Required by:') { + // Required by element. + requiredByString = text.substr(12); + requiredBy = requiredByString.replace(/\([a-z]*\)/g, '').split(','); + } + }); + for (var i in requires) { + requires[i] = $.trim(requires[i].toLowerCase()); + } + for (var i in requiredBy) { + requiredBy[i] = $.trim(requiredBy[i].toLowerCase()); + } + return { requires: requires, requiredBy: requiredBy }; +}; + +})(jQuery); diff --git a/sites/all/modules/contrib/admin/module_filter/js/permissions.js b/sites/all/modules/contrib/admin/module_filter/js/permissions.js new file mode 100644 index 0000000..8340389 --- /dev/null +++ b/sites/all/modules/contrib/admin/module_filter/js/permissions.js @@ -0,0 +1,67 @@ +(function($) { + +var lastModuleItem; + +Drupal.behaviors.moduleFilterPermissions = { + attach: function(context) { + $('.module-filter-inputs-wrapper', context).once('module-filter', function() { + var filterInput = $('input[name="module_filter[name]"]', context); + var selector = '#permissions tbody tr'; + + // Move location of filter input. + $('#permissions').parent().prepend(filterInput.parent().parent()); + + filterInput.moduleFilter(selector, { + wrapper: $('#permissions').parent(), + childSelector: 'td.module', + buildIndex: [ + function(moduleFilter, item) { + item.isModule = (item.text != '') ? true : false; + if (item.isModule) { + item.children = new Array(); + lastModuleItem = item; + } + else { + item.parent = lastModuleItem; + lastModuleItem.children.push(item); + } + return item; + } + ] + }); + + var moduleFilter = filterInput.data('moduleFilter'); + + moduleFilter.operators = { + perm: function(string, moduleFilter, item) { + if (!item.isModule) { + if (item.name == undefined) { + var $name = $('td.permission', item.element).clone(); + $('.description', $name).remove(); + item.name = $name.text().trim().toLowerCase(); + } + + if (item.name.indexOf(string) >= 0) { + return true; + } + } + } + }; + + moduleFilter.element.bind('moduleFilter:finish', function(e, data) { + for (var i in moduleFilter.results) { + if (moduleFilter.results[i].isModule) { + for (var k in moduleFilter.results[i].children) { + moduleFilter.results[i].children[k].element.removeClass('js-hide'); + } + } + else { + moduleFilter.results[i].parent.element.removeClass('js-hide'); + } + } + }); + }); + } +}; + +})(jQuery); \ No newline at end of file diff --git a/sites/all/modules/contrib/admin/module_filter/js/update_status.js b/sites/all/modules/contrib/admin/module_filter/js/update_status.js new file mode 100644 index 0000000..fcd3610 --- /dev/null +++ b/sites/all/modules/contrib/admin/module_filter/js/update_status.js @@ -0,0 +1,117 @@ +(function($) { + +Drupal.behaviors.moduleFilterUpdateStatus = { + attach: function(context) { + $('#module-filter-update-status-form').once('update-status', function() { + var filterInput = $('input[name="module_filter[name]"]', context); + filterInput.moduleFilter('table.update > tbody > tr', { + wrapper: $('table.update:first').parent(), + delay: 300, + childSelector: 'div.project a', + rules: [ + function(moduleFilter, item) { + switch (moduleFilter.options.show) { + case 'all': + return true; + case 'updates': + if (item.state == 'warning' || item.state == 'error') { + return true; + } + break; + case 'security': + if (item.state == 'error') { + return true; + } + break; + case 'ignore': + if (item.state == 'ignored') { + return true; + } + break; + case 'unknown': + if (item.state == 'unknown') { + return true; + } + break; + } + return false; + } + ], + buildIndex: [ + function(moduleFilter, item) { + if ($('.version-status', item.element).text() == Drupal.t('Ignored from settings')) { + item.state = 'ignored'; + return item; + } + if (item.element.is('.ok')) { + item.state = 'ok'; + } + else if (item.element.is('.warning')) { + item.state = 'warning'; + } + else if (item.element.is('.error')) { + item.state = 'error'; + } + else if (item.element.is('.unknown')) { + item.state = 'unknown'; + } + return item; + } + ], + show: $('#edit-module-filter-show input[name="module_filter[show]"]', context).val() + }); + + var moduleFilter = filterInput.data('moduleFilter'); + + if (Drupal.settings.moduleFilter.rememberUpdateState) { + var updateShow = Drupal.ModuleFilter.getState('updateShow'); + if (updateShow) { + moduleFilter.options.show = updateShow; + $('#edit-module-filter-show input[name="module_filter[show]"][value="' + updateShow + '"]', context).click(); + } + } + + $('#edit-module-filter-show input[name="module_filter[show]"]', context).change(function() { + moduleFilter.options.show = $(this).val(); + Drupal.ModuleFilter.setState('updateShow', moduleFilter.options.show); + moduleFilter.applyFilter(); + }); + + moduleFilter.element.bind('moduleFilter:start', function() { + $('table.update').each(function() { + $(this).show().prev('h3').show(); + }); + }); + + moduleFilter.element.bind('moduleFilter:finish', function(e, data) { + $('table.update').each(function() { + var $table = $(this); + if ($('tbody tr', $(this)).filter(':visible').length == 0) { + $table.hide().prev('h3').hide(); + } + }); + }); + + moduleFilter.element.bind('moduleFilter:keyup', function() { + if (moduleFilter.clearOffset == undefined) { + moduleFilter.inputWidth = filterInput.width(); + moduleFilter.clearOffset = moduleFilter.element.parent().find('.module-filter-clear a').width(); + } + if (moduleFilter.text) { + filterInput.width(moduleFilter.inputWidth - moduleFilter.clearOffset - 5).parent().css('margin-right', moduleFilter.clearOffset + 5); + } + else { + filterInput.width(moduleFilter.inputWidth).parent().css('margin-right', 0); + } + }); + + moduleFilter.element.parent().find('.module-filter-clear a').click(function() { + filterInput.width(moduleFilter.inputWidth).parent().css('margin-right', 0); + }); + + moduleFilter.applyFilter(); + }); + } +}; + +})(jQuery); diff --git a/sites/all/modules/contrib/admin/module_filter/module_filter.admin.inc b/sites/all/modules/contrib/admin/module_filter/module_filter.admin.inc old mode 100755 new mode 100644 index 0763c9c..cdb3f49 --- a/sites/all/modules/contrib/admin/module_filter/module_filter.admin.inc +++ b/sites/all/modules/contrib/admin/module_filter/module_filter.admin.inc @@ -14,36 +14,103 @@ * Settings form for module filter. */ function module_filter_settings() { + $form['module_filter_set_focus'] = array( + '#type' => 'checkbox', + '#title' => t('Set focus to filter field on page load'), + '#description' => t('Currently has no effect when using Overlay module.'), + '#default_value' => variable_get('module_filter_set_focus', 1), + ); + $form['module_filter_tabs'] = array( '#type' => 'checkbox', - '#title' => t('Tabs'), - '#description' => t('Divide module groups into tabbed list.'), - '#default_value' => variable_get('module_filter_tabs', 1) + '#title' => t('Enhance the modules page with tabs'), + '#description' => t('Alternate tabbed theme that restructures packages into tabs.'), + '#default_value' => variable_get('module_filter_tabs', 1), ); $form['tabs'] = array( '#type' => 'fieldset', '#title' => t('Tabs'), '#description' => t('Settings used with the tabs view of the modules page.'), '#collapsible' => TRUE, - '#collapsed' => FALSE + '#collapsed' => FALSE, ); $form['tabs']['module_filter_count_enabled'] = array( '#type' => 'checkbox', '#title' => t('Number of enabled modules'), '#description' => t('Display the number of enabled modules in the active tab along with the total number of modules.'), - '#default_value' => variable_get('module_filter_count_enabled', 1) + '#default_value' => variable_get('module_filter_count_enabled', 1), ); $form['tabs']['module_filter_visual_aid'] = array( '#type' => 'checkbox', - '#title' => t('Visuals for newly enabled and disabled modules'), - '#description' => t("Adds a basic count to tabs of modules being enabled/disabled and colors the module row pending it's being enabled or disabled"), - '#default_value' => variable_get('module_filter_visual_aid', 1) + '#title' => t('Visual aids'), + '#description' => t('When enabling/disabling modules, the module name will display in the tab summary.
When filtering, a count of results for each tab will be presented.'), + '#default_value' => variable_get('module_filter_visual_aid', 1), + ); + $form['tabs']['module_filter_hide_empty_tabs'] = array( + '#type' => 'checkbox', + '#title' => t('Hide tabs with no results'), + '#description' => t('When a filter returns no results for a tab, the tab is hidden. This is dependent on visual aids being enabled.'), + '#default_value' => variable_get('module_filter_hide_empty_tabs', 0), ); $form['tabs']['module_filter_dynamic_save_position'] = array( '#type' => 'checkbox', - '#title' => t('Dynamically position save button'), - '#description' => t("DEVELOPMENTAL: For sites with lots of tabs, enable to help keep the 'Save configuration' button more accessible."), - '#default_value' => variable_get('module_filter_dynamic_save_position', 0) + '#title' => t('Dynamically position Save button'), + '#description' => t("For sites with lots of tabs, enable to help keep the 'Save configuration' button more accessible."), + '#default_value' => variable_get('module_filter_dynamic_save_position', 1), ); + $form['tabs']['module_filter_use_url_fragment'] = array( + '#type' => 'checkbox', + '#title' => t('Use URL fragment'), + '#description' => t('Use URL fragment when navigating between tabs. This lets you use the browsers back/forward buttons to navigate through the tabs you selected.') . '
' . t('When the Overlay module is enabled this functionality will not be used since overlay relies on the URL fragment.'), + '#default_value' => variable_get('module_filter_use_url_fragment', 1), + ); + $form['tabs']['module_filter_use_switch'] = array( + '#type' => 'checkbox', + '#title' => t('Use switch instead of checkbox'), + '#description' => t('This is purely cosmetic (at least for now). Displays a ON/OFF switch rather than a checkbox to enable/disable modules.
Modules will not actually be enabled/disabled until the form is saved.'), + '#default_value' => variable_get('module_filter_use_switch', 1), + ); + $form['tabs']['module_filter_track_recent_modules'] = array( + '#type' => 'checkbox', + '#title' => t('Track recently enabled/disabled modules'), + '#description' => t('Adds a "Recent" tab that displays modules that have been enabled or disabled with the last week.'), + '#default_value' => variable_get('module_filter_track_recent_modules', 1), + ); + $form['tabs']['module_filter_remember_active_tab'] = array( + '#type' => 'checkbox', + '#title' => t('Remember active tab.'), + '#description' => t('When enabled, the active tab will be remembered.'), + '#default_value' => variable_get('module_filter_remember_active_tab', 1), + ); + $form['tabs']['module_filter_version_column'] = array( + '#type' => 'checkbox', + '#title' => t('Place version in own column'), + '#description' => t("Moves the version out of the description and into it's own column"), + '#default_value' => variable_get('module_filter_version_column', 0), + ); + $form['tabs']['module_filter_expanded_description'] = array( + '#type' => 'checkbox', + '#title' => t('Expand description by default'), + '#description' => t('When enabled, the description will be expanded by default.'), + '#default_value' => variable_get('module_filter_expanded_description', 0), + ); + + $form['update'] = array( + '#type' => 'fieldset', + '#title' => t('Update status'), + '#collapsible' => TRUE, + '#collapsed' => (module_exists('update')) ? FALSE : TRUE, + ); + $form['update']['module_filter_remember_update_state'] = array( + '#type' => 'checkbox', + '#title' => t('Remember the last selected filter.'), + '#description' => t('When enabled, the last state (All, Update available, Security update, Unknown) will be remembered.'), + '#default_value' => variable_get('module_filter_remember_update_state', 0), + ); + + if (module_exists('page_actions')) { + $form['tabs']['module_filter_dynamic_save_position']['#description'] .= '
' . t('The module %name is enabled and thus this setting will have no affect.', array('%name' => t('Page actions'))); + } + return system_settings_form($form); } diff --git a/sites/all/modules/contrib/admin/module_filter/module_filter.info b/sites/all/modules/contrib/admin/module_filter/module_filter.info old mode 100755 new mode 100644 index 89fe45d..e21267d --- a/sites/all/modules/contrib/admin/module_filter/module_filter.info +++ b/sites/all/modules/contrib/admin/module_filter/module_filter.info @@ -1,9 +1,9 @@ name = Module filter description = "Filter the modules list." core = 7.x +package = Administration files[] = module_filter.install -files[] = module_filter.js files[] = module_filter.module files[] = module_filter.admin.inc files[] = module_filter.theme.inc @@ -16,9 +16,8 @@ files[] = js/module_filter_tab.js configure = admin/config/user-interface/modulefilter -; Information added by drupal.org packaging script on 2013-08-08 -version = "7.x-1.8" +; Information added by Drupal.org packaging script on 2019-03-27 +version = "7.x-2.2" core = "7.x" project = "module_filter" -datestamp = "1375995220" - +datestamp = "1553698385" diff --git a/sites/all/modules/contrib/admin/module_filter/module_filter.install b/sites/all/modules/contrib/admin/module_filter/module_filter.install old mode 100755 new mode 100644 index c06cfd8..529d836 --- a/sites/all/modules/contrib/admin/module_filter/module_filter.install +++ b/sites/all/modules/contrib/admin/module_filter/module_filter.install @@ -5,13 +5,22 @@ */ /** - * Implementation of hook_uninstall(). + * Implements hook_uninstall(). */ function module_filter_uninstall() { + variable_del('module_filter_set_focus'); variable_del('module_filter_tabs'); variable_del('module_filter_count_enabled'); variable_del('module_filter_visual_aid'); + variable_del('module_filter_hide_empty_tabs'); variable_del('module_filter_dynamic_save_position'); + variable_del('module_filter_use_url_fragment'); + variable_del('module_filter_use_switch'); + variable_del('module_filter_track_recent_modules'); + variable_del('module_filter_remember_active_tab'); + variable_del('module_filter_remember_update_state'); + variable_del('module_filter_version_column'); + variable_del('module_filter_expanded_description'); } /** @@ -20,3 +29,21 @@ function module_filter_uninstall() { function module_filter_update_7100() { variable_del('module_filter_autocomplete'); } + +/** + * Rebuild the menu and theme registry. + */ +function module_filter_update_7200() { + menu_rebuild(); + system_rebuild_theme_data(); + drupal_theme_rebuild(); +} + +/** + * Old update that use to remove the module_filter_dynamic_save_position variable. + */ +function module_filter_update_7201() { + // We don't want to remove this update hook but at the same time we no + // longer want to lose the variable setting, so we just comment it out. + // variable_del('module_filter_dynamic_save_position'); +} diff --git a/sites/all/modules/contrib/admin/module_filter/module_filter.module b/sites/all/modules/contrib/admin/module_filter/module_filter.module old mode 100755 new mode 100644 index 09b7542..17059ec --- a/sites/all/modules/contrib/admin/module_filter/module_filter.module +++ b/sites/all/modules/contrib/admin/module_filter/module_filter.module @@ -12,34 +12,46 @@ */ /** - * Implementation of hook_perm(). + * Implements hook_perm(). */ function module_filter_permission() { return array( 'administer module filter' => array( 'title' => t('Administer Module Filter'), - 'description' => t('Configure how Module Filter performs.') - ) + 'description' => t('Configure how Module Filter performs.'), + ), ); } /** - * Implementation of hook_menu(). + * Implements hook_menu(). */ function module_filter_menu() { $items['admin/config/user-interface/modulefilter'] = array( 'title' => 'Module filter', - 'description' => 'Configure settings for Module Filter.', + 'description' => 'Configure how the modules page looks and acts.', 'access arguments' => array('administer module filter'), 'page callback' => 'drupal_get_form', 'page arguments' => array('module_filter_settings'), - 'file' => 'module_filter.admin.inc' + 'file' => 'module_filter.admin.inc', ); return $items; } /** - * Implementation of hook_form_FORM_ID_alter(). + * Implements hook_menu_alter(). + */ +function module_filter_menu_alter(&$items) { + if (isset($items['admin/reports/updates'])) { + // We route the updates report page through us. + $items['admin/reports/updates']['page callback'] = 'module_filter_update_status'; + $items['admin/reports/updates']['file'] = 'module_filter.pages.inc'; + $items['admin/reports/updates']['module'] = 'module_filter'; + } +} + +/** + * Implements hook_form_FORM_ID_alter(). */ function module_filter_form_system_modules_alter(&$form, &$form_state, $form_id) { // Don't alter the form when confirming. @@ -48,121 +60,237 @@ function module_filter_form_system_modules_alter(&$form, &$form_state, $form_id) } $form['module_filter'] = array( - '#tree' => TRUE, - '#weight' => -1, + '#type' => 'module_filter', '#attached' => array( - 'css' => array( - drupal_get_path('module', 'module_filter') .'/css/module_filter.css', + 'js' => array( + drupal_get_path('module', 'module_filter') . '/js/modules.js' => array('weight' => 1), ), ), ); - $form['module_filter']['name'] = array( - '#type' => 'textfield', - '#title' => t('Filter list') + $checkbox_defaults = array( + ((isset($_GET['enabled'])) ? $_GET['enabled'] : 1) ? 'enabled' : '', + ((isset($_GET['disabled'])) ? $_GET['disabled'] : 1) ? 'disabled' : '', + ((isset($_GET['required'])) ? $_GET['required'] : 1) ? 'required' : '', + ((isset($_GET['unavailable'])) ? $_GET['unavailable'] : 1) ? 'unavailable' : '', ); - $form['module_filter']['show'] = array( '#type' => 'checkboxes', - '#default_value' => array('enabled', 'disabled', 'required', 'unavailable'), + '#default_value' => array_filter($checkbox_defaults), '#options' => array('enabled' => t('Enabled'), 'disabled' => t('Disabled'), 'required' => t('Required'), 'unavailable' => t('Unavailable')), '#prefix' => '
', - '#suffix' => '
' + '#suffix' => '', ); if (variable_get('module_filter_tabs', 1)) { - $form['module_filter']['#attached']['css'][] = drupal_get_path('module', 'module_filter') .'/css/module_filter_tab.css'; - $form['module_filter']['#attached']['js'][] = drupal_get_path('module', 'module_filter') .'/js/module_filter_tab.js'; - $form['module_filter']['#attached']['js'][] = array( - 'data' => array('moduleFilter' => array('visualAid' => variable_get('module_filter_visual_aid', 1))), - 'type' => 'setting', - ); + $form['module_filter']['#attached']['css'][] = drupal_get_path('module', 'module_filter') . '/css/module_filter_tab.css'; + $form['module_filter']['#attached']['library'][] = array('system', 'jquery.bbq'); + $form['module_filter']['#attached']['js'][drupal_get_path('module', 'module_filter') . '/js/module_filter_tab.js'] = array('weight' => 2); - if (variable_get('module_filter_dynamic_save_position', 0)) { - $form['module_filter']['#attached']['js'][] = drupal_get_path('module', 'module_filter') .'/js/dynamic_position.js'; + if (!module_exists('page_actions') && variable_get('module_filter_dynamic_save_position', 1)) { + $form['module_filter']['#attached']['css'][] = drupal_get_path('module', 'module_filter') . '/css/dynamic_position.css'; + $form['module_filter']['#attached']['js'][drupal_get_path('module', 'module_filter') . '/js/dynamic_position.js'] = array('weight' => 3); } - $form['module_filter']['#size'] = 45; - - // Remove the fieldsets for each package since we will be using tabs - // instead. Put all modules into one array. - $modules = array( - '#theme' => 'module_filter_modules_table', - '#header' => array( - array('data' => t('Enabled'), 'class' => 'checkbox'), - t('Name'), - t('Version'), - t('Description'), - array('data' => t('Operations'), 'colspan' => 3) - ) - ); - - $all = t('All'); - $tab_counts = array($all => array('id' => 'all', 'enabled' => 0, 'total' => 0)); - $form['#packages'] = array(); - foreach (element_children($form['modules']) as $package) { - // Add the package to $form['#packages']. Tabs are built from this. - $form['#packages'][$package] = $package; - - if (!isset($tab_counts[$package])) { - $tab_counts[$package] = array('enabled' => 0, 'total' => 0); - } - - foreach (element_children($form['modules'][$package]) as $module) { - $tab_counts[$all]['total']++; - $tab_counts[$package]['total']++; - if (!empty($form['modules'][$package][$module]['enable']['#default_value'])) { - $tab_counts[$all]['enabled']++; - $tab_counts[$package]['enabled']++; - } - - $modules[$module] = $form['modules'][$package][$module]; - $modules[$module]['#package'] = $package; - $modules[$module]['#parents'] = array('modules', $package, $module); - } - } - - // Sort the array of modules alphabetically. - uasort($modules, 'module_filter_sort_modules_by_display_name'); - - // Replace the $form['modules'] with our $modules array. - $form['modules'] = $modules; - - // Add our $tab_counts array to the form. - $form['#tab_counts'] = $tab_counts; + $form['#attached']['css'][] = drupal_get_path('module', 'module_filter') . '/css/modules.css'; $form['#theme'] = 'module_filter_system_modules_tabs'; } - else { - $form['module_filter']['#attached']['js'][] = drupal_get_path('module', 'module_filter') .'/js/module_filter.js'; - $form['module_filter']['#prefix'] = ''; + + $form['#submit'][] = 'module_filter_system_modules_submit_redirect'; + + if (variable_get('module_filter_track_recent_modules', 1)) { + $form['#submit'][] = 'module_filter_system_modules_submit_recent'; } } /** - * Implementation of hook_theme(). + * Implements hook_form_FORM_ID_alter(). + */ +function module_filter_form_user_admin_permissions_alter(&$form, &$form_state) { + $form['module_filter'] = array( + '#type' => 'module_filter', + '#description' => t('Filter list by module. Use the query operator "perm" to filter by permission, e.g., perm:access.'), + '#attached' => array( + 'js' => array( + drupal_get_path('module', 'module_filter') . '/js/permissions.js', + ), + ), + '#weight' => -100, + ); +} + +/** + * Implements hook_element_info(). + */ +function module_filter_element_info() { + $types['module_filter'] = array( + '#input' => TRUE, + '#process' => array('form_process_module_filter', 'ajax_process_form'), + '#weight' => -1, + '#tree' => TRUE, + '#theme' => 'module_filter', + ); + return $types; +} + +/** + * Implements hook_theme(). */ function module_filter_theme() { return array( - 'module_filter_modules_table' => array( - 'render element' => 'form', + 'module_filter' => array( + 'render element' => 'element', 'file' => 'module_filter.theme.inc', ), 'module_filter_system_modules_tabs' => array( 'render element' => 'form', - 'file' => 'module_filter.theme.inc' - ) + 'file' => 'module_filter.theme.inc', + ), + 'module_filter_operations' => array( + 'variables' => array('links' => array(), 'dropbutton' => FALSE), + 'file' => 'module_filter.theme.inc', + ), ); } -function module_filter_sort_modules_by_display_name($a, $b) { - if (is_array($a) && is_array($b) && isset($a['#package'], $b['#package'])) { - return strcasecmp($a['name']['#markup'], $b['name']['#markup']); +/** + * Create and add new textfield element. + * + * @param $element + * An associative array containing the properties and children of the + * form actions container. + * @param $form_state + * The $form_state array for the form this element belongs to. + * + * @return + * The processed element. + */ +function form_process_module_filter($element, &$form_state) { + $element['name'] = array( + '#type' => 'textfield', + '#title' => (isset($element['#title'])) ? $element['#title'] : t('Filter list'), + '#default_value' => (isset($element['#default_value'])) ? $element['#default_value'] : ((isset($_GET['filter'])) ? $_GET['filter'] : ''), + '#size' => (isset($element['#size'])) ? $element['#size'] : 45, + '#weight' => (isset($element['#weight'])) ? $element['#weight'] : -10, + '#attributes' => ((isset($element['#attributes'])) ? $element['#attributes'] : array()) + array('autocomplete' => 'off'), + '#attached' => array( + 'css' => array( + drupal_get_path('module', 'module_filter') . '/css/module_filter.css', + ), + 'js' => array( + 'misc/jquery.cookie.js', + drupal_get_path('module', 'module_filter') . '/js/module_filter.js', + array( + 'data' => array( + 'moduleFilter' => array( + 'setFocus' => variable_get('module_filter_set_focus', 1), + 'tabs' => variable_get('module_filter_tabs', 1), + 'countEnabled' => variable_get('module_filter_count_enabled', 1), + 'visualAid' => variable_get('module_filter_visual_aid', 1), + 'hideEmptyTabs' => variable_get('module_filter_hide_empty_tabs', 0), + 'dynamicPosition' => (!module_exists('page_actions')) ? variable_get('module_filter_dynamic_save_position', 1) : FALSE, + 'useURLFragment' => variable_get('module_filter_use_url_fragment', 1), + 'useSwitch' => variable_get('module_filter_use_switch', 1), + 'trackRecent' => variable_get('module_filter_track_recent_modules', 1), + 'rememberActiveTab' => variable_get('module_filter_remember_active_tab', 1), + 'rememberUpdateState' => variable_get('module_filter_remember_update_state', 0), + 'expandedDescription' => variable_get('module_filter_expanded_description', 0), + ), + ), + 'type' => 'setting', + ), + ), + ), + ); + if (isset($element['#description'])) { + $element['name']['#description'] = $element['#description']; } - return 0; + return $element; } +/** + * Form submission handler to filters module list. + */ +function module_filter_system_modules_submit_redirect($form, &$form_state) { + $query = array(); + if (!empty($form_state['values']['module_filter']['name'])) { + $query['filter'] = $form_state['values']['module_filter']['name']; + } + $query['enabled'] = (int) (!empty($form_state['values']['module_filter']['show']['enabled'])); + $query['disabled'] = (int) (!empty($form_state['values']['module_filter']['show']['disabled'])); + $query['required'] = (int) (!empty($form_state['values']['module_filter']['show']['required'])); + $query['unavailable'] = (int) (!empty($form_state['values']['module_filter']['show']['unavailable'])); + + $form_state['redirect'] = array( + 'admin/modules', + array('query' => $query), + ); +} + +/** + * Form submission handler to track recently enabled/disabled modules + */ +function module_filter_system_modules_submit_recent($form, &$form_state) { + $recent_modules = variable_get('module_filter_recent_modules', array()); + + foreach ($form_state['values']['modules'] as $package => $modules) { + foreach ($modules as $key => $module) { + if ($form['modules'][$package][$key]['enable']['#default_value'] != $module['enable']) { + $recent_modules[$key] = REQUEST_TIME; + } + } + } + + variable_set('module_filter_recent_modules', $recent_modules); +} + +/** + * Create list of newly added modules (within a week) + * @return + * An array of newly added modules. + */ +function module_filter_new_modules() { + // Get current list of modules. + $files = system_rebuild_module_data(); + + // Remove hidden modules from display list. + $visible_files = $files; + foreach ($visible_files as $filename => $file) { + if (!empty($file->info['hidden'])) { + unset($visible_files[$filename]); + } + } + + uasort($visible_files, 'system_sort_modules_by_info_name'); + + $new_modules = array(); + foreach ($visible_files as $filename => $module) { + $ctime = filectime(dirname($module->uri) . '/' . $module->name . '.info'); + if (($ctime - strtotime('-1 week')) > 0) { + $new_modules[$filename] = module_filter_get_id($filename); + } + } + return $new_modules; +} + +/** + * Function to replace special characters with hyphen. + * @param string $text + * @return + * String + */ function module_filter_get_id($text) { $id = strtolower($text); - return preg_replace('/([^a-z])([\/( )])*/', '-', $id); + $id = preg_replace('/([^a-z0-9]+)/', '-', $id); + return trim($id, '-'); +} + +/** + * Function to return true/false depending on module changed time and a week timestamp + * @param integer $var + * @return + * Boolean indicating + */ +function module_filter_recent_filter($var) { + return (!($var < REQUEST_TIME - 60 * 60 * 24 * 7)); } diff --git a/sites/all/modules/contrib/admin/module_filter/module_filter.pages.inc b/sites/all/modules/contrib/admin/module_filter/module_filter.pages.inc new file mode 100644 index 0000000..a18fbc5 --- /dev/null +++ b/sites/all/modules/contrib/admin/module_filter/module_filter.pages.inc @@ -0,0 +1,53 @@ + drupal_get_form('module_filter_update_status_form'), + 'update_report' => array( + '#markup' => $update_report, + ), + ); +} + +/** + * Form builder for the module filter form. + */ +function module_filter_update_status_form($form, &$form_state) { + $form['module_filter'] = array( + '#type' => 'module_filter', + '#attached' => array( + 'css' => array( + drupal_get_path('module', 'module_filter') . '/css/update_status.css', + ), + 'js' => array( + drupal_get_path('module', 'module_filter') . '/js/update_status.js', + ), + ), + ); + $form['module_filter']['show'] = array( + '#type' => 'radios', + '#default_value' => (isset($_GET['show']) && in_array($_GET['show'], array('all', 'updates', 'security', 'unknown'))) ? $_GET['show'] : 'all', + '#options' => array('all' => t('All'), 'updates' => t('Update available'), 'security' => t('Security update'), 'unknown' => t('Unknown')), + '#prefix' => '
', + '#suffix' => '
', + ); + if (module_exists('update_advanced')) { + $options = $form['module_filter']['show']['#options']; + $form['module_filter']['show']['#options'] = array_slice($options, 0, 2); + $form['module_filter']['show']['#options']['ignore'] = t('Ignored from settings'); + $form['module_filter']['show']['#options'] = array_merge($form['module_filter']['show']['#options'], array_slice($options, 2)); + } + return $form; +} diff --git a/sites/all/modules/contrib/admin/module_filter/module_filter.theme.inc b/sites/all/modules/contrib/admin/module_filter/module_filter.theme.inc old mode 100755 new mode 100644 index 59cce42..2d00ab7 --- a/sites/all/modules/contrib/admin/module_filter/module_filter.theme.inc +++ b/sites/all/modules/contrib/admin/module_filter/module_filter.theme.inc @@ -6,76 +6,209 @@ * @author greenSkin */ -function theme_module_filter_modules_table($variables) { - $form = $variables['form']; - - // Individual table headers. - $rows = array(); - // Iterate through all the modules, which are - // children of this fieldset. - foreach (element_children($form) as $key) { - // Stick it into $module for easier accessing. - $module = $form[$key]; - $row = array(); - unset($module['enable']['#title']); - $row[] = array('class' => array('checkbox'), 'data' => drupal_render($module['enable'])); - $label = '' . drupal_render($module['name']) . ''; - $row[] = drupal_render($module['version']); - // Add the description, along with any modules it requires. - $description = drupal_render($module['description']); - if ($module['#requires']) { - $description .= '
' . t('Requires: !module-list', array('!module-list' => implode(', ', $module['#requires']))) . '
'; - } - if ($module['#required_by']) { - $description .= '
' . t('Required by: !module-list', array('!module-list' => implode(', ', $module['#required_by']))) . '
'; - } - $row[] = array('data' => $description, 'class' => array('description')); - // Display links (such as help or permissions) in their own columns. - foreach (array('help', 'permissions', 'configure') as $key) { - $row[] = array('data' => drupal_render($module['links'][$key]), 'class' => array($key)); - } - - $id = module_filter_get_id($module['#package']); - $rows[] = array( - 'data' => $row, - 'class' => array($id .'-tab-content') - ); - } - - return theme('table', array('header' => $form['#header'], 'rows' => $rows, 'attributes' => array('class' => array('package')))); +function theme_module_filter($variables) { + $element = $variables['element']; + return '
' . drupal_render_children($element) . '
'; } /** * Theme callback for the modules tabbed form. */ function theme_module_filter_system_modules_tabs($variables) { - $form = $variables['form']; - - $count_enabled = variable_get('module_filter_count_enabled', 1); - - // Display packages. - $all = t('All'); - $all_count = ($count_enabled) ? '' . t('!enabled of !total', array('!enabled' => $form['#tab_counts'][$all]['enabled'], '!total' => $form['#tab_counts'][$all]['total'])) . '' : ''; - $tabs = array('all' => '
  • ' . $all . $all_count . '
  • '); - foreach ($form['#packages'] as $package) { - $id = module_filter_get_id($package); - - $count = ($count_enabled) ? '' . t('!enabled of !total', array('!enabled' => $form['#tab_counts'][$package]['enabled'], '!total' => $form['#tab_counts'][$package]['total'])) . '' : ''; - $tabs[$id] = '
  • ' . $package . $count . '
  • '; + if (module_exists('views_ui')) { + // Hack to get consistent style with views ctools dropbutton. + if (module_load_include('inc', 'views_ui', 'includes/admin')) { + foreach (views_ui_get_admin_css() as $file => $options) { + drupal_add_css($file, $options); + } + } } + $form = $variables['form']; + + if (!module_exists('page_actions')) { + $form['actions']['#prefix'] = '
    '; + $form['actions']['#suffix'] = '
    '; + } + + $header = array( + array('data' => '', 'class' => array('checkbox')), + array('data' => t('Name'), 'class' => array('name')), + array('data' => t('Description'), 'class' => array('description')), + array('data' => t('Links'), 'class' => array('links')), + ); + if (variable_get('module_filter_version_column', 0)) { + array_splice($header, 2, 0, array(array('data' => t('Version'), 'class' => array('version')))); + } + $package_ids = array('all'); + $enabled['all'] = array(); + + if (variable_get('module_filter_track_recent_modules', 1)) { + $recent_modules = array_filter(variable_get('module_filter_recent_modules', array()), 'module_filter_recent_filter'); + // Save the filtered results. + variable_set('module_filter_recent_modules', $recent_modules); + + $package_ids[] = 'recent'; + $enabled['recent'] = array(); + } + + // Determine what modules are new (within a week). + $new_modules = module_filter_new_modules(); + $package_ids[] = 'new'; + $enabled['new'] = array(); + + $rows = array(); + $flip = array('even' => 'odd', 'odd' => 'even'); + foreach (element_children($form['modules']) as $package) { + $package_id = module_filter_get_id($package); + $package_ids[] = $package_id; + + // Package title and header. + $rows[] = array('data' => array(array('data' => '

    ' . $form['modules'][$package]['#title'] . '

    ', 'colspan' => 4)), 'id' => $package_id . '-package', 'class' => array('admin-package-title')); + $rows[] = array('data' => $header, 'class' => array('admin-package-header')); + + $stripe = 'odd'; + $enabled[$package_id] = array(); + foreach (element_children($form['modules'][$package]) as $key) { + $module = &$form['modules'][$package][$key]; + + $is_enabled = isset($module['enable']['#default_value']) ? $module['enable']['#default_value'] : ''; + $enabled['all'][] = $enabled[$package_id][] = $is_enabled; + if (isset($recent_modules[$key])) { + $enabled['recent'][] = $is_enabled; + } + if (isset($new_modules[$key])) { + $enabled['new'][] = $is_enabled; + } + + $row = array(); + + $version = !empty($module['version']['#markup']); + $requires = !empty($module['#requires']); + $required_by = !empty($module['#required_by']); + + $toggle_enable = ''; + if (isset($module['enable']['#type']) && $module['enable']['#type'] == 'checkbox') { + unset($module['enable']['#title']); + $class = ($is_enabled ? 'enabled' : 'off'); + if (!empty($module['enable']['#disabled'])) { + $class .= ' disabled'; + } + $toggle_enable = '
     
    '; + } + $row[] = array('class' => array('checkbox'), 'data' => $toggle_enable . drupal_render($module['enable'])); + + $label = ' array('name'), 'data' => $label . '>' . drupal_render($module['name']) . ' (' . $key . ')'); + + if (variable_get('module_filter_version_column', 0) && $version) { + $row[] = array('class' => array('version'), 'data' => drupal_render($module['version'])); + } + + // Add the description, along with any modules it requires. + if (empty($module['description']['#markup'])) { + $module['description']['#markup'] = '' . t('No description available.') . ''; + } + $description = '' . drupal_render($module['description']) . ''; + if ($requires || $required_by || (!variable_get('module_filter_version_column', 0) && $version)) { + $description .= '
    '; + if (!variable_get('module_filter_version_column', 0) && $version) { + $description .= '
    ' . t('Version: !module-version', array('!module-version' => drupal_render($module['version']))) . '
    '; + } + if ($requires) { + $description .= '
    ' . t('Requires: !module-list', array('!module-list' => implode(', ', $module['#requires']))) . '
    '; + } + if ($required_by) { + $description .= '
    ' . t('Required by: !module-list', array('!module-list' => implode(', ', $module['#required_by']))) . '
    '; + } + $description .= '
    '; + } + $row[] = array('data' => '
    ' . $description . '
    ', 'class' => array('description')); + + $operations = (module_exists('ctools')) ? theme('module_filter_operations', array('links' => $module['links'], 'dropbutton' => TRUE)) : theme('module_filter_operations', array('links' => $module['links'])); + $row[] = array('data' => '', 'class' => array('links')); + + $class = array(module_filter_get_id($package) . '-tab', 'module', $stripe); + if (isset($recent_modules[$key])) { + $class[] = 'recent-module'; + } + if (isset($new_modules[$key])) { + $class[] = 'new-module'; + } + $rows[] = array('data' => $row, 'no_striping' => TRUE, 'class' => $class); + $stripe = $flip[$stripe]; + } + + // Set the package as printed. + $form['modules'][$package]['#printed'] = TRUE; + } + + //Get packages and count number of modules + $enabled_counts = array(); + foreach ($enabled as $package_id => $value) { + $enabled_counts[$package_id] = array( + 'enabled' => count(array_filter($value)), + 'total' => count($value), + ); + } + drupal_add_js(array( + 'moduleFilter' => array( + 'packageIDs' => $package_ids, + 'enabledCounts' => $enabled_counts, + ) + ), 'setting'); + + // Add first and last class to rows. + $rows[0]['class'][] = 'first'; + $rows[count($rows) - 1]['class'][] = 'last'; + $output = '
    '; - $output .= '
    '; - $output .= '
      '. implode($tabs) . '
    '; - $output .= '
    ' . drupal_render($form['actions']) . '
    '; - $output .= '
    ' . drupal_render($form['module_filter']); - $output .= drupal_render($form['modules']) . '
    '; - $output .= '
    '; - $output .= '
    '; + $output .= '
    ' . drupal_render($form['module_filter']); + $output .= theme('table', array('header' => $header, 'rows' => $rows)); $output .= drupal_render_children($form); + $output .= '
    '; + $output .= ''; return $output; } + +/** + * Theme function for module filter operations. + * @param $variables + * @return + * HTML for admin status operations. + */ +function theme_module_filter_operations(&$vars) { + $links = &$vars['links']; + $dropbutton = $vars['dropbutton']; + + $operations = array(); + foreach (element_children($links) as $key) { + if ($dropbutton) { + hide($links[$key]); + if (!empty($links[$key]['#href'])) { + $operations[$key] = array( + 'title' => $links[$key]['#title'], + 'href' => $links[$key]['#href'], + ); + if (isset($links[$key]['#options'])) { + $operations[$key] += $links[$key]['#options']; + } + } + } + else { + $data = drupal_render($links[$key]); + if (!empty($data)) { + $operations[$key] = array('data' => $data); + } + } + } + if (!empty($operations)) { + if ($dropbutton) { + return '
    ' . theme('links__ctools_dropbutton', array('title' => t('Operations'), 'links' => $operations, 'attributes' => array('class' => array('links')))) . '
    '; + } + return '
    ' . theme('item_list', array('items' => $operations, 'attributes' => array('class' => array('links', 'inline')))) . '
    '; + } +} diff --git a/sites/all/modules/contrib/dev/ctools/CHANGELOG.txt b/sites/all/modules/contrib/dev/ctools/CHANGELOG.txt deleted file mode 100644 index c5bd5e6..0000000 --- a/sites/all/modules/contrib/dev/ctools/CHANGELOG.txt +++ /dev/null @@ -1,82 +0,0 @@ -Current API VERSION: 2.0. See API.txt for more information. - -ctools 7.x-1.x-dev -================== -#1008120: "New custom content" shows empty form if custom content panes module is not enabled. -#999302 by troky: Fix jump menu. Apparently this wasn't actually committed the last time it was committed. -#1065976 by tekante and David_Rothstein: Reset plugin static cache during module enable to prevent stale data from harming export ui. -#1016510 by EclipseGC: Make the taxonomy system page functional. - -ctools 7.x-1.x-alpha2 (05-Jan-2011) -=================================== - -#911396 by alex_b: Prevent notices in export UI. -#919768 by mikey_p: Allow url options to be sent to ctools_ajax_command_url(). -#358953 by cedarm: Allow term context to return lowercase, spaces to dashes versions of terms. -#931434 by EclipseGc: Argument plugin for node revision ID. -#910656: CTools AJAX sample wizard demo "domesticated" checkbox value not stored. -#922442 by EugenMayer, neclimdul and voxpelli: Make sure ctools_include can handle '' or NULL directory. -#919956 by traviss359: Correct example in wizard advanced help. -#942968: Fix taxonomy term access rule with tag term vocabs. -#840344: node add argument had crufty code causing notices. -#944462 by longhairedgit: Invalid character in regex causes rare notice. -#938778 by dereine: Fix profile content type for D7 updates. -Add detach event to modal close so that wysiwyg can detach the editor. -Variant titles showing up as blank if more than one variant on a page. -#940016: token support was not yet updated for D7. -#940446: Skip validation on back and cancel buttons in all wizards. -#954492: Redirect not always working in wizard.inc -#955348: Lack of redirect on "Update" button in Page Manager causing data loss sometimes. -#941778: Update and save button should not appear in the "Add variant" path. -#955070 by EclipseGc: Update ctools internal page tokens to work properly on content all content. -#956890 by EclipseGc: Update views_content to not use views dependency since that is gone. -#954728 by EclipseGc: Update node template page function name to not collide with new hook_node_view(). -#946534 by EclipseGc: Add support for field content on all entitities. -#952586 by EclipseGc: Fix node_author content type. -#959206: If a context is not set when rendering content, attempt to guess the context (fixes Views panes where "From context" was added but pane was never edited.) -#961654 by benshell: drupal_alter() only supports 4 arguments. -#911362 by alex_b: Facilitate plugin cache resets for tests. -#945360 by naxoc: node_tag_new() not updated to D7. -#953804 by EclipseGc: Fix node comment rendering. -#953542 by EclipseGc: Fix node rendering. -#953776 by EclipseGc: Fix node link rendering. -#954772 by EclipseGc: Fix node build mode selection in node content type. -#954762 by EclipseGc: Fix comment forbidden theme call. -#954894 by EclipseGc: Fix breadcrumb content type. -#955180 by EclipseGc: Fix page primary navigation type. -#957190 by EclipseGc: Fix page secondary navigation type. -#957194 by EclipseGc: Remove mission content type, since D7 no longer has a site mission. -#957348 by EclipseGc: Fix search form URL path. -#952586 by andypost: Use format_username for displaying unlinked usernames. -#963800 by benshell: Fix query to fetch custom block title. -#983496 by Amitaibu: Fix term argument to use proper load function. -#989484 by Amitaibu: Fix notice in views plugin. -#982496: Fix token context. -#995026: Fix export UI during enable/disable which would throw notices and not properly set/unset menu items. -#998870 by Amitaibu: Fix notice when content has no icon by using function already designed for that. -#983576 by Amitaibu: Node view fallback task showed white screen. -#1004644 by pillarsdotnet: Update a missed theme() call to D7. -#1006162 by aspilicious: .info file cleanup. -#998312 by dereine: Support the expanded/hidden options that Views did for dependent.js -#955030: Remove no longer supported footer message content type. -Fix broken query in term context config. -#992022 by pcambra: Fix node autocomplete. -#946302 by BerdArt and arywyr: Fix PHP 5.3 reference error. -#980528 by das-peter: Notice fix with entity settings. -#999302 by troky: ctools_jump_menu() needed updating to new form parameters. -#964174: stylizer plugin theme delegation was in the wrong place, causing errors. -#991658 by burlap: Fully load the "user" context for the logged in user because not all fields are in $user. -#1014866 by das-peter: Smarter title panes, notice fix on access plugin descriptions. -#1015662 by troky: plugin .info files were not using correct filepaths. -#941780 by EclipseGc: Restore the "No blocks" functionality. -#951048 by EclipseGc: Tighter entity integration so that new entities are automatic contexts and relationships. -#941800 by me and aspilicious: Use Drupal 7 #machine_name automation on page manager pages and all export_ui defaults. -Disabled exportables and pages not properly greyed out. -#969208 by me and benshell: Get user_view and user profile working. -#941796: Recategorize blocks - -ctools 7.x-1.x-alpha1 -===================== - -Changelog reset for 7.x -Basic conversion done during sprint. diff --git a/sites/all/modules/contrib/dev/ctools/UPGRADE.txt b/sites/all/modules/contrib/dev/ctools/UPGRADE.txt index 844ecce..2fbfa4f 100644 --- a/sites/all/modules/contrib/dev/ctools/UPGRADE.txt +++ b/sites/all/modules/contrib/dev/ctools/UPGRADE.txt @@ -60,4 +60,4 @@ Upgrading from ctools-6.x-1.x to ctools-7.x-2.x: For all of these forms, the separate settings #trees in the form are now gone, so form ids may be adjusted. Also, these are now all real forms - using CTools form wizard instead of fake subforms as previously. \ No newline at end of file + using CTools form wizard instead of fake subforms as previously. diff --git a/sites/all/modules/contrib/dev/ctools/bulk_export/bulk_export.info b/sites/all/modules/contrib/dev/ctools/bulk_export/bulk_export.info index 1e9031d..9bb3394 100644 --- a/sites/all/modules/contrib/dev/ctools/bulk_export/bulk_export.info +++ b/sites/all/modules/contrib/dev/ctools/bulk_export/bulk_export.info @@ -3,12 +3,9 @@ description = Performs bulk exporting of data objects known about by Chaos tools core = 7.x dependencies[] = ctools package = Chaos tool suite -version = CTOOLS_MODULE_VERSION - -; Information added by Drupal.org packaging script on 2015-01-28 -version = "7.x-1.6" +; Information added by Drupal.org packaging script on 2019-02-08 +version = "7.x-1.15" core = "7.x" project = "ctools" -datestamp = "1422471484" - +datestamp = "1549603691" diff --git a/sites/all/modules/contrib/dev/ctools/bulk_export/bulk_export.module b/sites/all/modules/contrib/dev/ctools/bulk_export/bulk_export.module index afb15b9..1050caa 100644 --- a/sites/all/modules/contrib/dev/ctools/bulk_export/bulk_export.module +++ b/sites/all/modules/contrib/dev/ctools/bulk_export/bulk_export.module @@ -137,7 +137,7 @@ function bulk_export_export($cli = FALSE, $options = array()) { // Add hook_ctools_plugin_api at the top of the module code, if there is any. if ($api_code) { foreach ($api_code as $api_hook => $text) { - $api = "\n/**\n"; + $api = "\n/**\n"; $api .= " * Implements hook_$api_hook().\n"; $api .= " */\n"; $api .= "function {$module_name}_$api_hook(\$module, \$api) {\n"; @@ -148,7 +148,7 @@ function bulk_export_export($cli = FALSE, $options = array()) { } if ($module_code) { - $module = "conf['css_id'] = 'my-id'; +} + /** * @} End of "addtogroup hooks". */ diff --git a/sites/all/modules/contrib/dev/ctools/ctools.info b/sites/all/modules/contrib/dev/ctools/ctools.info index bd2e3c1..188b55d 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools.info +++ b/sites/all/modules/contrib/dev/ctools/ctools.info @@ -2,16 +2,25 @@ name = Chaos tools description = A library of helpful tools by Merlin of Chaos. core = 7.x package = Chaos tool suite -version = CTOOLS_MODULE_VERSION files[] = includes/context.inc files[] = includes/css-cache.inc files[] = includes/math-expr.inc files[] = includes/stylizer.inc -files[] = tests/css_cache.test -; Information added by Drupal.org packaging script on 2015-01-28 -version = "7.x-1.6" +; Tests. +files[] = tests/context.test +files[] = tests/css.test +files[] = tests/css_cache.test +files[] = tests/ctools.plugins.test +files[] = tests/ctools.test +files[] = tests/math_expression.test +files[] = tests/math_expression_stack.test +files[] = tests/object_cache.test +files[] = tests/object_cache_unit.test +files[] = tests/page_tokens.test + +; Information added by Drupal.org packaging script on 2019-02-08 +version = "7.x-1.15" core = "7.x" project = "ctools" -datestamp = "1422471484" - +datestamp = "1549603691" diff --git a/sites/all/modules/contrib/dev/ctools/ctools.install b/sites/all/modules/contrib/dev/ctools/ctools.install index e96c743..d50bc9f 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools.install +++ b/sites/all/modules/contrib/dev/ctools/ctools.install @@ -12,24 +12,25 @@ function ctools_requirements($phase) { $requirements = array(); if ($phase == 'runtime') { + $t = get_t(); $requirements['ctools_css_cache'] = array( - 'title' => t('CTools CSS Cache'), + 'title' => $t('CTools CSS Cache'), 'severity' => REQUIREMENT_OK, - 'value' => t('Exists'), + 'value' => $t('Exists'), ); $path = 'public://ctools/css'; if (!file_prepare_directory($path, FILE_CREATE_DIRECTORY)) { - $requirements['ctools_css_cache']['description'] = t('The CTools CSS cache directory, %path could not be created due to a misconfigured files directory. Please ensure that the files directory is correctly configured and that the webserver has permission to create directories.', array('%path' => file_uri_target($path))); + $requirements['ctools_css_cache']['description'] = $t('The CTools CSS cache directory, %path could not be created due to a misconfigured files directory. Please ensure that the files directory is correctly configured and that the webserver has permission to create directories.', array('%path' => file_uri_target($path))); $requirements['ctools_css_cache']['severity'] = REQUIREMENT_ERROR; - $requirements['ctools_css_cache']['value'] = t('Unable to create'); + $requirements['ctools_css_cache']['value'] = $t('Unable to create'); } if (!function_exists('error_get_last')) { - $requirements['ctools_php_52']['title'] = t('CTools PHP requirements'); - $requirements['ctools_php_52']['description'] = t('CTools requires certain features only available in PHP 5.2.0 or higher.'); + $requirements['ctools_php_52']['title'] = $t('CTools PHP requirements'); + $requirements['ctools_php_52']['description'] = $t('CTools requires certain features only available in PHP 5.2.0 or higher.'); $requirements['ctools_php_52']['severity'] = REQUIREMENT_WARNING; - $requirements['ctools_php_52']['value'] = t('PHP !version', array('!version' => phpversion())); + $requirements['ctools_php_52']['value'] = $t('PHP !version', array('!version' => phpversion())); } } @@ -40,7 +41,19 @@ function ctools_requirements($phase) { * Implements hook_schema(). */ function ctools_schema() { - return ctools_schema_3(); + return ctools_schema_4(); +} + +/** + * Version 4 of the CTools schema. + */ +function ctools_schema_4() { + $schema = ctools_schema_3(); + + // Removed due to alternative database configuration issues. + // @see https://www.drupal.org/project/ctools/issues/2941920 + + return $schema; } /** @@ -49,7 +62,7 @@ function ctools_schema() { function ctools_schema_3() { $schema = ctools_schema_2(); - // update the 'obj' field to be 128 bytes long: + // Update the 'obj' field to be 128 bytes long: $schema['ctools_object_cache']['fields']['obj']['length'] = 128; return $schema; @@ -61,7 +74,7 @@ function ctools_schema_3() { function ctools_schema_2() { $schema = ctools_schema_1(); - // update the 'name' field to be 128 bytes long: + // Update the 'name' field to be 128 bytes long: $schema['ctools_object_cache']['fields']['name']['length'] = 128; // Update the 'data' field to be type 'blob'. @@ -95,10 +108,10 @@ function ctools_schema_2() { 'serialize' => TRUE, ), 'filter' => array( - 'type' => 'int', - 'size' => 'tiny', - 'description' => 'Whether or not this CSS needs to be filtered.', - ), + 'type' => 'int', + 'size' => 'tiny', + 'description' => 'Whether or not this CSS needs to be filtered.', + ), ), 'primary key' => array('cid'), ); @@ -197,7 +210,7 @@ function ctools_update_6003() { if ($result) { db_delete('system')->condition('name', 'panels_views')->execute(); module_enable(array('views_content'), TRUE); - } + } } /** @@ -216,7 +229,7 @@ function ctools_update_6005() { } /** - * ctools_custom_content table was originally here, but is now moved to + * The ctools_custom_content table was originally here, but is now moved to * its own module. */ function ctools_update_6007() { @@ -230,18 +243,18 @@ function ctools_update_6007() { } /** - * ctools_object_cache needs to be defined as a blob. + * The ctools_object_cache needs to be defined as a blob. */ function ctools_update_6008() { db_delete('ctools_object_cache') ->execute(); db_change_field('ctools_object_cache', 'data', 'data', array( - 'type' => 'blob', - 'size' => 'big', - 'description' => 'Serialized data being stored.', - 'serialize' => TRUE, - ) + 'type' => 'blob', + 'size' => 'big', + 'description' => 'Serialized data being stored.', + 'serialize' => TRUE, + ) ); } @@ -263,3 +276,24 @@ function ctools_update_7001() { 'description' => 'The type of the object this cache is attached to; this essentially represents the owner so that several sub-systems can use this cache.', )); } + +/** + * Increase the length of the ctools_object_cache.name column to 255. + */ +function ctools_update_7002() { + // Removed due to alternative database configuration issues. + // @see https://www.drupal.org/project/ctools/issues/2941920 +} + +/** + * Revert the length of the ctools_object_cache.name column back to 128. + */ +function ctools_update_7003() { + db_delete('ctools_object_cache')->execute(); + db_change_field('ctools_object_cache', 'name', 'name', array( + 'type' => 'varchar', + 'length' => '128', + 'not null' => TRUE, + 'description' => 'The name of the object this cache is attached to.', + )); +} diff --git a/sites/all/modules/contrib/dev/ctools/ctools.module b/sites/all/modules/contrib/dev/ctools/ctools.module index c37b96f..62f2919 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools.module +++ b/sites/all/modules/contrib/dev/ctools/ctools.module @@ -9,7 +9,7 @@ * must be implemented in the module file. */ -define('CTOOLS_API_VERSION', '2.0.8'); +define('CTOOLS_API_VERSION', '2.0.9'); /** * The current working ctools version. @@ -22,8 +22,11 @@ define('CTOOLS_API_VERSION', '2.0.8'); * simply include a dependency line in that module's info file, e.g.: * ; Requires CTools v7.x-1.4 or newer. * dependencies[] = ctools (>=1.4) + * + * @deprecated in CTools 1.15 and will be removed before CTools 2.0.0. + * Use the version provided by the drupal.org packaging system. */ -define('CTOOLS_MODULE_VERSION', '7.x-1.6'); +define('CTOOLS_MODULE_VERSION', '7.x-1.13'); /** * Test the CTools API version. @@ -75,6 +78,9 @@ define('CTOOLS_MODULE_VERSION', '7.x-1.6'); * The minimum version of CTools necessary for your software to run with it. * @param $maximum * The maximum version of CTools allowed for your software to run with it. + * + * @return bool + * TRUE if the running ctools is usable, FALSE otherwise. */ function ctools_api_version($minimum, $maximum = NULL) { if (version_compare(CTOOLS_API_VERSION, $minimum, '<')) { @@ -89,8 +95,7 @@ function ctools_api_version($minimum, $maximum = NULL) { } // ----------------------------------------------------------------------- -// General utility functions - +// General utility functions. /** * Include .inc files as necessary. * @@ -118,6 +123,7 @@ function ctools_api_version($minimum, $maximum = NULL) { * @param $dir * Optional subdirectory containing the include file. */ + function ctools_include($file, $module = 'ctools', $dir = 'includes') { static $used = array(); @@ -147,8 +153,8 @@ function ctools_form_include(&$form_state, $file, $module = 'ctools', $dir = 'in /** * Add an arbitrary path to the $form_state so it can work with form cache. * - * module_load_include uses an unfortunately annoying syntax to work, making it - * difficult to translate the more simple $path + $file syntax. + * The module_load_include() function uses an unfortunately annoying syntax to + * work, making it difficult to translate the more simple $path + $file syntax. */ function ctools_form_include_file(&$form_state, $filename) { if (!isset($form_state['build_info']['args'])) { @@ -172,6 +178,9 @@ function ctools_form_include_file(&$form_state, $filename) { * Optional module containing the include. * @param $dir * Optional subdirectory containing the include file. + * + * @return string + * A string containing the appropriate path from drupal root. */ function ctools_image_path($image, $module = 'ctools', $dir = 'images') { return drupal_get_path('module', $module) . "/$dir/" . $image; @@ -211,6 +220,9 @@ function ctools_add_css($file, $module = 'ctools', $dir = 'css') { * Optional module containing the include. * @param $dir * Optional subdirectory containing the include file. + * + * @return string + * A string containing the appropriate path from drupal root. */ function ctools_attach_css($file, $module = 'ctools', $dir = 'css') { return drupal_get_path('module', $module) . "/$dir/$file.css"; @@ -249,6 +261,9 @@ function ctools_add_js($file, $module = 'ctools', $dir = 'js') { * Optional module containing the include. * @param $dir * Optional subdirectory containing the include file. + * + * @return string + * A string containing the appropriate path from drupal root. */ function ctools_attach_js($file, $module = 'ctools', $dir = 'js') { return drupal_get_path('module', $module) . "/$dir/$file.js"; @@ -267,16 +282,29 @@ function ctools_get_roles() { return user_roles(); } -/* - * Break x,y,z and x+y+z into an array. Numeric only. +/** + * Parse integer sequences of the form "x,y,z" or "x+y+z" into separate values. + * + * A string with integers separated by comma (,) is reported as an 'and' set; + * separation by a plus sign (+) or a space ( ) is an 'or' set. The meaning + * of this is up to the caller. Negative or fractional numbers are not + * recognised. + * + * Additional space characters within or around the sequence are not allowed. * * @param $str * The string to parse. * - * @return $object - * An object containing - * - operator: Either 'and' or 'or' - * - value: An array of numeric values. + * @return object + * An object containing the properties: + * + * - operator: Either 'and' or 'or' when there are multiple matched values. + * Absent when invalid_input is TRUE or there is only one value. + * - value: An array of integers (never strings) from $str. An empty array is + * returned if the input is empty. A single integer input is returned + * as a single value, but no 'operator' is defined. + * - invalid_input: TRUE if input could not be parsed and the values array + * will contain just -1. This property is otherwise absent. */ function ctools_break_phrase($str) { $object = new stdClass(); @@ -286,7 +314,7 @@ function ctools_break_phrase($str) { $object->operator = 'or'; $object->value = preg_split('/[+ ]/', $str); } - else if (preg_match('/^([0-9]+,)*[0-9]+$/', $str)) { + elseif (preg_match('/^([0-9]+,)*[0-9]+$/', $str)) { $object->operator = 'and'; $object->value = explode(',', $str); } @@ -304,7 +332,7 @@ function ctools_break_phrase($str) { // Doubly ensure that all values are numeric only. foreach ($object->value as $id => $value) { - $object->value[$id] = intval($value); + $object->value[$id] = (int) $value; } return $object; @@ -314,23 +342,29 @@ function ctools_break_phrase($str) { * Set a token/value pair to be replaced later in the request, specifically in * ctools_page_token_processing(). * - * @param $token + * @param string $token * The token to be replaced later, during page rendering. This should - * ideally be a string inside of an HTML comment, so that if there is - * no replacement, the token will not render on the page. - * @param $type + * ideally be a string inside of an HTML comment, so that if there is + * no replacement, the token will not render on the page. + * If $token is NULL, the token set is not changed, but is still + * returned. + * @param string $type * The type of the token. Can be either 'variable', which will pull data - * directly from the page variables - * @param $argument - * If $type == 'variable' then argument should be the key to fetch from - * the $variables. If $type == 'callback' then it should either be the - * callback, or an array that will be sent to call_user_func_array(). + * directly from the page variables, or 'callback', which causes a function + * to be called to calculate the value. No other values are supported. + * @param string|array $argument + * For $type of: + * - 'variable': argument should be the key to fetch from the $variables. + * - 'callback': then it should either be the callback function name as a + * string, or an array that will be sent to call_user_func_array(). Argument + * arrays must not use array keys (i.e. $a[0] is the first and $a[1] the + * second element, etc.) * - * @return + * @return array * A array of token/variable names to be replaced. */ function ctools_set_page_token($token = NULL, $type = NULL, $argument = NULL) { - static $tokens = array(); + $tokens = &drupal_static('ctools_set_page_token', array()); if (isset($token)) { $tokens[$token] = array($type, $argument); @@ -339,13 +373,32 @@ function ctools_set_page_token($token = NULL, $type = NULL, $argument = NULL) { } /** - * Easily set a token from the page variables. + * Reset the defined page tokens within this request. + * + * Introduced for simpletest purposes. Normally not needed. + */ +function ctools_reset_page_tokens() { + drupal_static_reset('ctools_set_page_token'); +} + +/** + * Set a replacement token from the containing element's children during #post_render. * * This function can be used like this: - * $token = ctools_set_variable_token('tabs'); + * $token = ctools_set_variable_token('tabs'); * - * $token will then be a simple replacement for the 'tabs' about of the - * variables available in the page template. + * The token "" would then be replaced by the value of + * this element's (sibling) render array key 'tabs' during post-render (or be + * deleted if there was no such key by that point). + * + * @param string $token + * The token string for the page callback, e.g. 'title'. + * + * @return string + * The constructed token. + * + * @see ctools_set_callback_token() + * @see ctools_page_token_processing() */ function ctools_set_variable_token($token) { $string = ''; @@ -354,10 +407,45 @@ function ctools_set_variable_token($token) { } /** - * Easily set a token from the page variables. + * Set a replacement token from the value of a function during #post_render. * * This function can be used like this: - * $token = ctools_set_variable_token('id', 'mymodule_myfunction'); + * $token = ctools_set_callback_token('id', 'mymodule_myfunction'); + * + * Or this (from its use in ctools_page_title_content_type_render): + * $token = ctools_set_callback_token('title', array( + * 'ctools_page_title_content_type_token', $conf['markup'], $conf['id'], $conf['class'] + * ) + * ); + * + * The token (e.g: "") + * would then be replaced during post-render by the return value of: + * + * ctools_page_title_content_type_token($value_markup, $value_id, $value_class); + * + * @param string $token + * The token string for the page callback, e.g. 'title'. + * + * @param string|array $callback + * For callback functions that require no args, the name of the function as a + * string; otherwise an array of two or more elements: the function name + * followed by one or more function arguments. + * + * NB: the value of $callback must be a procedural (non-class) function that + * passes the php function_exists() check. + * + * The callback function itself will be called with args dependent + * on $callback. If: + * - $callback is a string, the function is called with a reference to the + * render array; + * - $callback is an array, the function is called with $callback merged + * with an array containing a reference to the render array. + * + * @return string + * The constructed token. + * + * @see ctools_set_variable_token() + * @see ctools_page_token_processing() */ function ctools_set_callback_token($token, $callback) { // If the callback uses arguments they are considered in the token. @@ -384,9 +472,9 @@ function ctools_set_no_blocks($blocks = FALSE) { /** * Wrapper function to create UUIDs via ctools, falls back on UUID module * if it is enabled. This code is a copy of uuid.inc from the uuid module. + * * @see http://php.net/uniqid#65879 */ - function ctools_uuid_generate() { if (!module_exists('uuid')) { ctools_include('uuid'); @@ -413,6 +501,7 @@ function ctools_uuid_generate() { /** * Check that a string appears to be in the format of a UUID. + * * @see http://drupal.org/project/uuid * * @param $uuid @@ -468,6 +557,8 @@ function ctools_class_add($classes, $hook = 'html') { */ function ctools_class_remove($classes, $hook = 'html') { if (!is_array($classes)) { + // @todo Consider using explode(' ', $classes); + // @todo Consider checking that $classes is a string before adding. $classes = array($classes); } @@ -480,12 +571,35 @@ function ctools_class_remove($classes, $hook = 'html') { } } -// ----------------------------------------------------------------------- -// Drupal core hooks +/** + * Reset the storage used for ctools_class_add and ctools_class_remove. + * + * @see ctools_class_add() + * @see ctools_class_remove() + */ +function ctools_class_reset() { + drupal_static_reset('ctools_process_classes'); +} +/** + * Return the classes for the body (added by ctools_class_add). + * + * @return array + * A copy of the array of classes to add to the body tag. If none have been + * added, this will be an empty array. + * + * @see ctools_class_add() + */ +function ctools_get_classes() { + return drupal_static('ctools_process_classes', array()); +} + +// ----------------------------------------------------------------------- +// Drupal core hooks. /** * Implement hook_init to keep our global CSS at the ready. */ + function ctools_init() { ctools_add_css('ctools'); // If we are sure that CTools' AJAX is in use, change the error handling. @@ -504,7 +618,7 @@ function ctools_init() { * Shutdown handler used during ajax operations to help catch fatal errors. */ function ctools_shutdown_handler() { - if (function_exists('error_get_last') AND ($error = error_get_last())) { + if (function_exists('error_get_last') && ($error = error_get_last())) { switch ($error['type']) { case E_ERROR: case E_CORE_ERROR: @@ -583,7 +697,6 @@ function ctools_flush_caches() { /** * Implements hook_element_info_alter(). - * */ function ctools_element_info_alter(&$type) { ctools_include('dependent'); @@ -618,12 +731,33 @@ function ctools_registry_files_alter(&$files, $indexed_modules) { } // ----------------------------------------------------------------------- -// CTools hook implementations. +// FAPI hooks that must be in the .module file. +/** + * Alter the comment form to get a little more control over it. + */ +function ctools_form_comment_form_alter(&$form, &$form_state) { + if (!empty($form_state['ctools comment alter'])) { + // Force the form to post back to wherever we are. + $form['#action'] = url($_GET['q'], array('fragment' => 'comment-form')); + if (empty($form['#submit'])) { + $form['#submit'] = array('comment_form_submit'); + } + $form['#submit'][] = 'ctools_node_comment_form_submit'; + } +} + +function ctools_node_comment_form_submit(&$form, &$form_state) { + $form_state['redirect'][0] = $_GET['q']; +} + +// ----------------------------------------------------------------------- +// CTools hook implementations. /** * Implementation of hook_ctools_plugin_directory() to let the system know * where all our own plugins are. */ + function ctools_ctools_plugin_directory($owner, $plugin_type) { if ($owner == 'ctools') { return 'plugins/' . $plugin_type; @@ -644,11 +778,11 @@ function ctools_ctools_plugin_type() { // ----------------------------------------------------------------------- // Drupal theme preprocess hooks that must be in the .module file. - /** * A theme preprocess function to automatically allow panels-based node * templates based upon input when the panel was configured. */ + function ctools_preprocess_node(&$vars) { // The 'ctools_template_identifier' attribute of the node is added when the pane is // rendered. @@ -658,14 +792,13 @@ function ctools_preprocess_node(&$vars) { } } - /** * Implements hook_page_alter(). * * Last ditch attempt to remove sidebar regions if the "no blocks" * functionality has been activated. * - * @see ctools_block_list_alter(). + * @see ctools_block_list_alter() */ function ctools_page_alter(&$page) { $check = drupal_static('ctools_set_no_blocks', TRUE); @@ -695,6 +828,7 @@ function ctools_page_token_processing($children, $elements) { case 'variable': $tokens[$token] = isset($elements[$argument]) ? $elements[$argument] : ''; break; + case 'callback': if (is_string($argument) && function_exists($argument)) { $tokens[$token] = $argument($elements); @@ -723,7 +857,7 @@ function ctools_process(&$variables, $hook) { return; } - $classes = drupal_static('ctools_process_classes', array()); + $classes = ctools_get_classes(); // Process the classses to add. if (!empty($classes[$hook]['add'])) { @@ -744,7 +878,6 @@ function ctools_process(&$variables, $hook) { // ----------------------------------------------------------------------- // Menu callbacks that must be in the .module file. - /** * Determine if the current user has access via a plugin. * @@ -768,6 +901,7 @@ function ctools_process(&$variables, $hook) { * @return * TRUE if access is granted, false if otherwise. */ + function ctools_access_menu($access) { // Short circuit everything if there are no access tests. if (empty($access['plugins'])) { @@ -796,7 +930,7 @@ function ctools_access_menu($access) { * An indexed array of zero or more permission strings to be checked by * user_access(). * - * @return + * @return bool * Iff all checks pass will this function return TRUE. If an invalid argument * is passed (e.g., not a string), this function errs on the safe said and * returns FALSE. @@ -858,7 +992,6 @@ function ctools_export_ui_load($item_name, $plugin_name) { // ----------------------------------------------------------------------- // Caching callbacks on behalf of export-ui. - /** * Menu access callback for various tasks of export-ui. */ @@ -902,7 +1035,7 @@ function ctools_export_ui_ctools_access_get($argument) { } /** - * Callback for access control ajax form on behalf of export ui + * Callback for access control ajax form on behalf of export ui. * * Returns the cached access config and contexts used. * Note that this is assuming that access will be in $item->access -- if it @@ -935,8 +1068,9 @@ function ctools_menu_local_tasks_alter(&$data, $router_item, $root_path) { } /** - * Implement hook_block_list_alter() to potentially remove blocks. + * Implements hook_block_list_alter(). * + * Used to potentially remove blocks. * This exists in order to replicate Drupal 6's "no blocks" functionality. */ function ctools_block_list_alter(&$blocks) { @@ -999,6 +1133,7 @@ function ctools_ctools_entity_context_alter(&$plugin, &$entity, $plugin_id) { case 'entity_id:taxonomy_term': $plugin['no ui'] = TRUE; break; + case 'entity:user': $plugin = ctools_get_context('user'); unset($plugin['no ui']); @@ -1018,3 +1153,53 @@ function ctools_ctools_entity_context_alter(&$plugin, &$entity, $plugin_id) { } } } + +/** + * Implements hook_field_create_field(). + */ +function ctools_field_create_field($field) { + ctools_flush_field_caches(); +} + +/** + * Implements hook_field_create_instance(). + */ +function ctools_field_create_instance($instance) { + ctools_flush_field_caches(); +} + +/** + * Implements hook_field_delete_field(). + */ +function ctools_field_delete_field($field) { + ctools_flush_field_caches(); +} + +/** + * Implements hook_field_delete_instance(). + */ +function ctools_field_delete_instance($instance) { + ctools_flush_field_caches(); +} + +/** + * Implements hook_field_update_field(). + */ +function ctools_field_update_field($field, $prior_field, $has_data) { + ctools_flush_field_caches(); +} + +/** + * Implements hook_field_update_instance(). + */ +function ctools_field_update_instance($instance, $prior_instance) { + ctools_flush_field_caches(); +} + +/** + * Clear field related caches. + */ +function ctools_flush_field_caches() { + // Clear caches of 'Entity field' content type plugin. + cache_clear_all('ctools_entity_field_content_type_content_types', 'cache'); +} diff --git a/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/ctools_access_ruleset.info b/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/ctools_access_ruleset.info index 2180cec..3fc3556 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/ctools_access_ruleset.info +++ b/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/ctools_access_ruleset.info @@ -2,12 +2,10 @@ name = Custom rulesets description = Create custom, exportable, reusable access rulesets for applications like Panels. core = 7.x package = Chaos tool suite -version = CTOOLS_MODULE_VERSION dependencies[] = ctools -; Information added by Drupal.org packaging script on 2015-01-28 -version = "7.x-1.6" +; Information added by Drupal.org packaging script on 2019-02-08 +version = "7.x-1.15" core = "7.x" project = "ctools" -datestamp = "1422471484" - +datestamp = "1549603691" diff --git a/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/ctools_access_ruleset.install b/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/ctools_access_ruleset.install index 3f00877..70afb3c 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/ctools_access_ruleset.install +++ b/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/ctools_access_ruleset.install @@ -1,5 +1,9 @@ t('Custom rulesets are combinations of access plugins you can use for access control, selection criteria and pane visibility.'), ); - // Load all mini panels and their displays. + // Load all mini panels and their displays. ctools_include('export'); $items = ctools_export_crud_load_all('ctools_access_ruleset'); $count = 0; diff --git a/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/plugins/access/ruleset.inc b/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/plugins/access/ruleset.inc index f8abea6..95f32c3 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/plugins/access/ruleset.inc +++ b/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/plugins/access/ruleset.inc @@ -35,7 +35,7 @@ function ctools_ruleset_ctools_access_merge_plugin($plugin, $parent, $item) { $plugin['required context'] = array(); foreach ($item->requiredcontexts as $context) { $info = ctools_get_context($context['name']); - // TODO: allow an optional setting + // TODO: allow an optional setting. $plugin['required context'][] = new ctools_context_required($context['identifier'], $info['context name']); } } @@ -72,7 +72,7 @@ function ctools_ruleset_ctools_access_get_children($plugin, $parent) { } /** - * Settings form for the 'by ruleset' access plugin + * Settings form for the 'by ruleset' access plugin. */ function ctools_ruleset_ctools_access_settings(&$form, &$form_state, $conf) { if (!empty($form_state['plugin']['ruleset']->admin_description)) { @@ -106,4 +106,3 @@ function ctools_ruleset_ctools_access_summary($conf, $context, $plugin) { return check_plain($plugin['ruleset']->admin_title); } } - diff --git a/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/plugins/export_ui/ctools_access_ruleset.inc b/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/plugins/export_ui/ctools_access_ruleset.inc index d2a1c60..2589ac3 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/plugins/export_ui/ctools_access_ruleset.inc +++ b/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/plugins/export_ui/ctools_access_ruleset.inc @@ -1,5 +1,9 @@ 'ctools_access_ruleset', 'access' => 'administer ctools access ruleset', @@ -26,4 +30,3 @@ $plugin = array( ), ), ); - diff --git a/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/plugins/export_ui/ctools_access_ruleset_ui.class.php b/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/plugins/export_ui/ctools_access_ruleset_ui.class.php index b181464..c9f8c20 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/plugins/export_ui/ctools_access_ruleset_ui.class.php +++ b/sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/plugins/export_ui/ctools_access_ruleset_ui.class.php @@ -2,7 +2,7 @@ class ctools_access_ruleset_ui extends ctools_export_ui { - function edit_form_context(&$form, &$form_state) { + public function edit_form_context(&$form, &$form_state) { ctools_include('context-admin'); ctools_context_admin_includes(); ctools_add_css('ruleset'); @@ -27,7 +27,7 @@ class ctools_access_ruleset_ui extends ctools_export_ui { ctools_context_add_relationship_form($module, $form, $form_state, $form['right']['relationships_table'], $form_state['item'], $name); } - function edit_form_rules(&$form, &$form_state) { + public function edit_form_rules(&$form, &$form_state) { // The 'access' UI passes everything via $form_state, unlike the 'context' UI. // The main difference is that one is about 3 years newer than the other. ctools_include('context'); @@ -43,11 +43,12 @@ class ctools_access_ruleset_ui extends ctools_export_ui { $form = ctools_access_admin_form($form, $form_state); } - function edit_form_rules_submit(&$form, &$form_state) { + public function edit_form_rules_submit(&$form, &$form_state) { $form_state['item']->access['logic'] = $form_state['values']['logic']; } - function edit_form_submit(&$form, &$form_state) { + public function edit_form_submit(&$form, &$form_state) { parent::edit_form_submit($form, $form_state); } + } diff --git a/sites/all/modules/contrib/dev/ctools/ctools_ajax_sample/css/ctools-ajax-sample.css b/sites/all/modules/contrib/dev/ctools/ctools_ajax_sample/css/ctools-ajax-sample.css index 8df17de..c312e99 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_ajax_sample/css/ctools-ajax-sample.css +++ b/sites/all/modules/contrib/dev/ctools/ctools_ajax_sample/css/ctools-ajax-sample.css @@ -1,52 +1,52 @@ div.ctools-sample-modal-content { - background:none; - border:0; - color:#000000; - margin:0; - padding:0; - text-align:left; + background: none; + border: 0; + color: #000; + margin: 0; + padding: 0; + text-align: left; } -div.ctools-sample-modal-content .modal-scroll{ - overflow:hidden; - overflow-y:auto; +div.ctools-sample-modal-content .modal-scroll { + overflow: hidden; + overflow-y: auto; } div.ctools-sample-modal-content #popups-overlay { - background-color:transparent; + background-color: transparent; } div.ctools-sample-modal-content #popups-loading { - width:248px; - position:absolute; - display:none; - opacity:1; + width: 248px; + position: absolute; + display: none; + opacity: 1; -moz-border-radius: 8px; -webkit-border-radius: 8px; - z-index:99; + z-index: 99; } div.ctools-sample-modal-content #popups-loading span.popups-loading-message { - background:#FFF url(../images/loading-large.gif) no-repeat 8px center; - display:block; - color:#444444; - font-family:Arial; - font-size:22px; - font-weight:bold; - height:36px; - line-height:36px; - padding:0 40px; + background: #fff url(../images/loading-large.gif) no-repeat 8px center; + display: block; + color: #444; + font-family: Arial, serif; + font-size: 22px; + font-weight: bold; + height: 36px; + line-height: 36px; + padding: 0 40px; } div.ctools-sample-modal-content #popups-loading table, div.ctools-sample-modal-content .popups-box table { - margin:0px; + margin: 0; } div.ctools-sample-modal-content #popups-loading tbody, div.ctools-sample-modal-content .popups-box tbody { - border:none; + border: none; } div.ctools-sample-modal-content .popups-box tr { - background-color:transparent; + background-color: transparent; } div.ctools-sample-modal-content td.popups-border { background: url(../images/popups-border.png); - background-color:transparent; + background-color: transparent; border: none; } div.ctools-sample-modal-content td.popups-tl, @@ -54,79 +54,94 @@ div.ctools-sample-modal-content td.popups-tr, div.ctools-sample-modal-content td.popups-bl, div.ctools-sample-modal-content td.popups-br { background-repeat: no-repeat; - height:10px; - padding:0px; + height: 10px; + padding: 0; +} +div.ctools-sample-modal-content td.popups-tl { + background-position: 0 0; } -div.ctools-sample-modal-content td.popups-tl { background-position: 0px 0px; } div.ctools-sample-modal-content td.popups-t, div.ctools-sample-modal-content td.popups-b { - background-position: 0px -40px; + background-position: 0 -40px; background-repeat: repeat-x; } -div.ctools-sample-modal-content td.popups-tr { background-position: 0px -10px; width: 10px; } +div.ctools-sample-modal-content td.popups-tr { + background-position: 0 -10px; + width: 10px; +} div.ctools-sample-modal-content td.popups-cl, div.ctools-sample-modal-content td.popups-cr { background-position: -10px 0; background-repeat: repeat-y; - width:10px; + width: 10px; } div.ctools-sample-modal-content td.popups-cl, div.ctools-sample-modal-content td.popups-cr, -div.ctools-sample-modal-content td.popups-c { padding:0; border: none; } -div.ctools-sample-modal-content td.popups-c { background:#fff; } -div.ctools-sample-modal-content td.popups-bl { background-position: 0px -20px; } -div.ctools-sample-modal-content td.popups-br { background-position: 0px -30px; width: 10px; } +div.ctools-sample-modal-content td.popups-c { + padding: 0; + border: none; +} +div.ctools-sample-modal-content td.popups-c { + background: #fff; +} +div.ctools-sample-modal-content td.popups-bl { + background-position: 0 -20px; +} +div.ctools-sample-modal-content td.popups-br { + background-position: 0 -30px; + width: 10px; +} div.ctools-sample-modal-content .popups-box, div.ctools-sample-modal-content #popups-loading { - border: 0px solid #454545; - opacity:1; - overflow:hidden; - padding:0; - background-color:transparent; + border: 0 solid #454545; + opacity: 1; + overflow: hidden; + padding: 0; + background-color: transparent; } div.ctools-sample-modal-content .popups-container { - overflow:hidden; - height:100%; - background-color:#fff; + overflow: hidden; + height: 100%; + background-color: #fff; } div.ctools-sample-modal-content div.popups-title { - -moz-border-radius-topleft: 0px; - -webkit-border-radius-topleft: 0px; - margin-bottom:0px; - background-color:#ff7200; - border:1px solid #ce5c00; - padding:4px 10px 5px; - color:white; - font-size:1em; - font-weight:bold; + -moz-border-radius-topleft: 0; + -webkit-border-radius-topleft: 0; + margin-bottom: 0; + background-color: #ff7200; + border: 1px solid #ce5c00; + padding: 4px 10px 5px; + color: white; + font-size: 1em; + font-weight: bold; } div.ctools-sample-modal-content .popups-body { - background-color:#fff; - padding:8px; + background-color: #fff; + padding: 8px; } div.ctools-sample-modal-content .popups-box .popups-buttons, div.ctools-sample-modal-content .popups-box .popups-footer { - background-color:#fff; + background-color: #fff; } div.ctools-sample-modal-content .popups-title a.close { color: #fff; - text-decoration:none; + text-decoration: none; } div.ctools-sample-modal-content .popups-close { - font-size:120%; - float:right; - text-align:right; + font-size: 120%; + float: right; + text-align: right; } div.ctools-sample-modal-content .modal-loading-wrapper { - width:220px; - height:19px; - margin:0 auto; - margin-top:2%; + width: 220px; + height: 19px; + margin: 0 auto; + margin-top: 2%; } -div.ctools-sample-modal-content tbody{ - border:none; +div.ctools-sample-modal-content tbody { + border: none; } div.ctools-sample-modal-content .modal-content .modal-throbber-wrapper img { diff --git a/sites/all/modules/contrib/dev/ctools/ctools_ajax_sample/ctools_ajax_sample.info b/sites/all/modules/contrib/dev/ctools/ctools_ajax_sample/ctools_ajax_sample.info index 009662f..c2a52df 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_ajax_sample/ctools_ajax_sample.info +++ b/sites/all/modules/contrib/dev/ctools/ctools_ajax_sample/ctools_ajax_sample.info @@ -1,13 +1,11 @@ name = Chaos Tools (CTools) AJAX Example description = Shows how to use the power of Chaos AJAX. package = Chaos tool suite -version = CTOOLS_MODULE_VERSION dependencies[] = ctools core = 7.x -; Information added by Drupal.org packaging script on 2015-01-28 -version = "7.x-1.6" +; Information added by Drupal.org packaging script on 2019-02-08 +version = "7.x-1.15" core = "7.x" project = "ctools" -datestamp = "1422471484" - +datestamp = "1549603691" diff --git a/sites/all/modules/contrib/dev/ctools/ctools_ajax_sample/ctools_ajax_sample.install b/sites/all/modules/contrib/dev/ctools/ctools_ajax_sample/ctools_ajax_sample.install index 04325db..e0fdfc6 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_ajax_sample/ctools_ajax_sample.install +++ b/sites/all/modules/contrib/dev/ctools/ctools_ajax_sample/ctools_ajax_sample.install @@ -1,18 +1,18 @@ 'Chaos Tools AJAX Demo', - 'page callback' => 'ctools_ajax_sample_page', - 'access callback' => TRUE, - 'type' => MENU_NORMAL_ITEM, + 'title' => 'Chaos Tools AJAX Demo', + 'page callback' => 'ctools_ajax_sample_page', + 'access callback' => TRUE, + 'type' => MENU_NORMAL_ITEM, ); $items['ctools_ajax_sample/simple_form'] = array( 'title' => 'Simple Form', @@ -26,39 +26,39 @@ function ctools_ajax_sample_menu() { 'type' => MENU_CALLBACK, ); $items['ctools_ajax_sample/%ctools_js/hello'] = array( - 'title' => 'Hello World', - 'page callback' => 'ctools_ajax_sample_hello', - 'page arguments' => array(1), - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, + 'title' => 'Hello World', + 'page callback' => 'ctools_ajax_sample_hello', + 'page arguments' => array(1), + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, ); $items['ctools_ajax_sample/%ctools_js/tablenix/%'] = array( - 'title' => 'Hello World', - 'page callback' => 'ctools_ajax_sample_tablenix', - 'page arguments' => array(1, 3), - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, + 'title' => 'Hello World', + 'page callback' => 'ctools_ajax_sample_tablenix', + 'page arguments' => array(1, 3), + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, ); $items['ctools_ajax_sample/%ctools_js/login'] = array( - 'title' => 'Login', - 'page callback' => 'ctools_ajax_sample_login', - 'page arguments' => array(1), - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, + 'title' => 'Login', + 'page callback' => 'ctools_ajax_sample_login', + 'page arguments' => array(1), + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, ); $items['ctools_ajax_sample/%ctools_js/animal'] = array( - 'title' => 'Animal', - 'page callback' => 'ctools_ajax_sample_animal', - 'page arguments' => array(1), - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, + 'title' => 'Animal', + 'page callback' => 'ctools_ajax_sample_animal', + 'page arguments' => array(1), + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, ); $items['ctools_ajax_sample/%ctools_js/login/%'] = array( - 'title' => 'Post-Login Action', - 'page callback' => 'ctools_ajax_sample_login_success', - 'page arguments' => array(1, 3), - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, + 'title' => 'Post-Login Action', + 'page callback' => 'ctools_ajax_sample_login_success', + 'page arguments' => array(1, 3), + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, ); $items['ctools_ajax_sample/jumped'] = array( 'title' => 'Successful Jumping', @@ -104,11 +104,11 @@ function ctools_ajax_sample_theme() { } // --------------------------------------------------------------------------- -// Page callbacks - +// Page callbacks. /** * Page callback to display links and render a container for AJAX stuff. */ + function ctools_ajax_sample_page() { global $user; @@ -156,14 +156,14 @@ function ctools_ajax_sample_page() { // The extra class points to the info in ctools-sample-style which we added // to the settings, prefixed with 'ctools-modal'. - $links[] = ctools_modal_text_button(t('Modal Login (custom style)'), 'ctools_ajax_sample/nojs/login', t('Login via modal'), 'ctools-modal-ctools-sample-style'); + $links[] = ctools_modal_text_button(t('Modal Login (custom style)'), 'ctools_ajax_sample/nojs/login', t('Login via modal'), 'ctools-modal-ctools-sample-style'); } // Four ways to do our animal picking wizard. $button_form = ctools_ajax_sample_ajax_button_form(); $links[] = l(t('Wizard (no modal)'), 'ctools_ajax_sample/nojs/animal'); $links[] = ctools_modal_text_button(t('Wizard (default modal)'), 'ctools_ajax_sample/nojs/animal', t('Pick an animal')); - $links[] = ctools_modal_text_button(t('Wizard (custom modal)'), 'ctools_ajax_sample/nojs/animal', t('Pick an animal'), 'ctools-modal-ctools-sample-style'); + $links[] = ctools_modal_text_button(t('Wizard (custom modal)'), 'ctools_ajax_sample/nojs/animal', t('Pick an animal'), 'ctools-modal-ctools-sample-style'); $links[] = drupal_render($button_form); $links[] = ctools_ajax_text_button(t('Hello world!'), "ctools_ajax_sample/nojs/hello", t('Replace text with "hello world"')); @@ -176,9 +176,9 @@ function ctools_ajax_sample_page() { // Create a table that we can have data removed from via AJAX. $header = array(t('Row'), t('Content'), t('Actions')); $rows = array(); - for($i = 1; $i < 11; $i++) { + for ($i = 1; $i < 11; $i++) { $rows[] = array( - 'class' => array('ajax-sample-row-'. $i), + 'class' => array('ajax-sample-row-' . $i), 'data' => array( $i, md5($i), @@ -189,10 +189,10 @@ function ctools_ajax_sample_page() { $output .= theme('table', array('header' => $header, 'rows' => $rows, array('class' => array('ajax-sample-table')))); - // Show examples of ctools javascript widgets - $output .= '

    '. t('CTools Javascript Widgets') .'

    '; + // Show examples of ctools javascript widgets. + $output .= '

    ' . t('CTools Javascript Widgets') . '

    '; - // Create a drop down menu + // Create a drop down menu. $links = array(); $links[] = array('title' => t('Link 1'), 'href' => $_GET['q']); $links[] = array('title' => t('Link 2'), 'href' => $_GET['q']); @@ -201,16 +201,16 @@ function ctools_ajax_sample_page() { $output .= '

    ' . t('Drop Down Menu') . '

    '; $output .= theme('ctools_dropdown', array('title' => t('Click to Drop Down'), 'links' => $links)); - // Create a collapsible div + // Create a collapsible div. $handle = t('Click to Collapse'); $content = 'Nulla ligula ante, aliquam at adipiscing egestas, varius vel arcu. Etiam laoreet elementum mi vel consequat. Etiam scelerisque lorem vel neque consequat quis bibendum libero congue. Nulla facilisi. Mauris a elit a leo feugiat porta. Phasellus placerat cursus est vitae elementum.'; - $output .= '

    '. t('Collapsible Div') .'

    '; + $output .= '

    ' . t('Collapsible Div') . '

    '; $output .= theme('ctools_collapsible', array('handle' => $handle, 'content' => $content, 'collapsed' => FALSE)); - // Create a jump menu + // Create a jump menu. ctools_include('jump-menu'); $form = drupal_get_form('ctools_ajax_sample_jump_menu_form'); - $output .= '

    '. t('Jump Menu') .'

    '; + $output .= '

    ' . t('Jump Menu') . '

    '; $output .= drupal_render($form); return array('markup' => array('#markup' => $output)); @@ -225,7 +225,8 @@ function ctools_ajax_sample_hello($js = NULL) { ctools_include('ajax'); $commands = array(); $commands[] = ajax_command_html('#ctools-sample', $output); - print ajax_render($commands); // this function exits. + // This function exits. + print ajax_render($commands); exit; } else { @@ -234,7 +235,7 @@ function ctools_ajax_sample_hello($js = NULL) { } /** - * Nix a row from a table and restripe. + * Nix a row from a table and restripe. */ function ctools_ajax_sample_tablenix($js, $row) { if (!$js) { @@ -272,7 +273,7 @@ function ctools_ajax_sample_login($js = NULL) { $output = array(); $inplace = ctools_ajax_text_button(t('remain here'), 'ctools_ajax_sample/nojs/login/inplace', t('Go to your account')); $account = ctools_ajax_text_button(t('your account'), 'ctools_ajax_sample/nojs/login/user', t('Go to your account')); - $output[] = ctools_modal_command_display(t('Login Success'), ''); + $output[] = ctools_modal_command_display(t('Login Success'), ''); } print ajax_render($output); exit; @@ -283,7 +284,7 @@ function ctools_ajax_sample_login($js = NULL) { */ function ctools_ajax_sample_login_success($js, $action) { if (!$js) { - // we should never be here out of ajax context + // We should never be here out of ajax context. return MENU_NOT_FOUND; } @@ -291,11 +292,11 @@ function ctools_ajax_sample_login_success($js, $action) { ctools_add_js('ajax-responder'); $commands = array(); if ($action == 'inplace') { - // stay here + // Stay here. $commands[] = ctools_ajax_command_reload(); } else { - // bounce bounce + // Bounce bounce. $commands[] = ctools_ajax_command_redirect('user'); } print ajax_render($commands); @@ -318,18 +319,18 @@ function ctools_ajax_sample_animal($js = NULL, $step = NULL) { 'show back' => TRUE, 'show cancel' => TRUE, 'show return' => FALSE, - 'next callback' => 'ctools_ajax_sample_wizard_next', + 'next callback' => 'ctools_ajax_sample_wizard_next', 'finish callback' => 'ctools_ajax_sample_wizard_finish', 'cancel callback' => 'ctools_ajax_sample_wizard_cancel', - // this controls order, as well as form labels + // This controls order, as well as form labels. 'order' => array( 'start' => t('Choose animal'), ), - // here we map a step to a form id. + // Here we map a step to a form id. 'forms' => array( - // e.g. this for the step at wombat/create + // e.g. this for the step at wombat/create. 'start' => array( - 'form id' => 'ctools_ajax_sample_start' + 'form id' => 'ctools_ajax_sample_start', ), ), ); @@ -341,7 +342,6 @@ function ctools_ajax_sample_animal($js = NULL, $step = NULL) { // in creation. // // We skip all this here by just using an id of 1. - $object_id = 1; if (empty($step)) { @@ -406,7 +406,7 @@ function ctools_ajax_sample_animal($js = NULL, $step = NULL) { $commands[] = ajax_command_html('#ctools-sample', $animal); $commands[] = ctools_modal_command_dismiss(); } - else if (!empty($form_state['cancel'])) { + elseif (!empty($form_state['cancel'])) { // If cancelling, return to the activity. $commands[] = ctools_modal_command_dismiss(); } @@ -420,7 +420,7 @@ function ctools_ajax_sample_animal($js = NULL, $step = NULL) { if ($output === FALSE || !empty($form_state['complete'])) { return $animal; } - else if (!empty($form_state['cancel'])) { + elseif (!empty($form_state['cancel'])) { drupal_goto('ctools_ajax_sample'); } else { @@ -430,11 +430,11 @@ function ctools_ajax_sample_animal($js = NULL, $step = NULL) { } // --------------------------------------------------------------------------- -// Themes - +// Themes. /** * Theme function for main rendered output. */ + function theme_ctools_ajax_sample_container($vars) { $output = '
    '; $output .= $vars['content']; @@ -445,7 +445,6 @@ function theme_ctools_ajax_sample_container($vars) { // --------------------------------------------------------------------------- // Stuff needed for our little wizard. - /** * Get a list of our animals and associated forms. * @@ -453,6 +452,7 @@ function theme_ctools_ajax_sample_container($vars) { * which is often how it will work in the real world. If using CTools, what * you would probably really have, here, is a set of plugins for each animal. */ + function ctools_ajax_sample_animals() { return array( 'sheep' => array( @@ -478,10 +478,10 @@ function ctools_ajax_sample_animals() { // --------------------------------------------------------------------------- // Wizard caching helpers. - /** * Store our little cache so that we can retain data from form to form. */ + function ctools_ajax_sample_cache_set($id, $object) { ctools_include('object-cache'); ctools_object_cache_set('ctools_ajax_sample', $id, $object); @@ -495,7 +495,7 @@ function ctools_ajax_sample_cache_get($id) { $object = ctools_object_cache_get('ctools_ajax_sample', $id); if (!$object) { // Create a default object. - $object = new stdClass; + $object = new stdClass(); $object->type = 'unknown'; $object->name = ''; } @@ -513,18 +513,18 @@ function ctools_ajax_sample_cache_clear($id) { // --------------------------------------------------------------------------- // Wizard in-between helpers; what to do between or after forms. - /** * Handle the 'next' click on the add/edit pane form wizard. * * All we need to do is store the updated pane in the cache. */ + function ctools_ajax_sample_wizard_next(&$form_state) { ctools_ajax_sample_cache_set($form_state['object_id'], $form_state['object']); } /** - * Handle the 'finish' click on teh add/edit pane form wizard. + * Handle the 'finish' click on the add/edit pane form wizard. * * All we need to do is set a flag so the return can handle adding * the pane. @@ -542,10 +542,10 @@ function ctools_ajax_sample_wizard_cancel(&$form_state) { // --------------------------------------------------------------------------- // Wizard forms for our simple info collection wizard. - /** * Wizard start form. Choose an animal. */ + function ctools_ajax_sample_start($form, &$form_state) { $form_state['title'] = t('Choose animal'); @@ -713,7 +713,7 @@ function ctools_ajax_sample_show_raptor($object) { } /** - * Helper function to provide a sample jump menu form + * Helper function to provide a sample jump menu form. */ function ctools_ajax_sample_jump_menu_form() { $url = url('ctools_ajax_sample/jumped'); @@ -723,7 +723,7 @@ function ctools_ajax_sample_jump_menu_form() { } /** - * Provide a message to the user that the jump menu worked + * Provide a message to the user that the jump menu worked. */ function ctools_ajax_sample_jump_menu_page() { $return_link = l(t('Return to the examples page.'), 'ctools_ajax_sample'); @@ -732,7 +732,7 @@ function ctools_ajax_sample_jump_menu_page() { } /** - * Provide a form for an example ajax modal button + * Provide a form for an example ajax modal button. */ function ctools_ajax_sample_ajax_button_form() { $form = array(); diff --git a/sites/all/modules/contrib/dev/ctools/ctools_custom_content/ctools_custom_content.info b/sites/all/modules/contrib/dev/ctools/ctools_custom_content/ctools_custom_content.info index b991d61..2bc7348 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_custom_content/ctools_custom_content.info +++ b/sites/all/modules/contrib/dev/ctools/ctools_custom_content/ctools_custom_content.info @@ -2,12 +2,10 @@ name = Custom content panes description = Create custom, exportable, reusable content panes for applications like Panels. core = 7.x package = Chaos tool suite -version = CTOOLS_MODULE_VERSION dependencies[] = ctools -; Information added by Drupal.org packaging script on 2015-01-28 -version = "7.x-1.6" +; Information added by Drupal.org packaging script on 2019-02-08 +version = "7.x-1.15" core = "7.x" project = "ctools" -datestamp = "1422471484" - +datestamp = "1549603691" diff --git a/sites/all/modules/contrib/dev/ctools/ctools_custom_content/ctools_custom_content.install b/sites/all/modules/contrib/dev/ctools/ctools_custom_content/ctools_custom_content.install index b4512f2..dcf87e7 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_custom_content/ctools_custom_content.install +++ b/sites/all/modules/contrib/dev/ctools/ctools_custom_content/ctools_custom_content.install @@ -1,5 +1,9 @@ type == 'custom') { - if(!isset($pane->configuration['name'])) { + if (!isset($pane->configuration['name'])) { $name_of_pane = $pane->subtype; } else { @@ -80,7 +80,7 @@ function ctools_custom_content_panels_dashboard_blocks(&$vars) { 'description' => t('Custom content panes are basic HTML you enter that can be reused in all of your panels.'), ); - // Load all mini panels and their displays. + // Load all mini panels and their displays. ctools_include('export'); $items = ctools_export_crud_load_all('ctools_custom_content'); $count = 0; diff --git a/sites/all/modules/contrib/dev/ctools/ctools_custom_content/plugins/export_ui/ctools_custom_content.inc b/sites/all/modules/contrib/dev/ctools/ctools_custom_content/plugins/export_ui/ctools_custom_content.inc index 467dc58..c7933bc 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_custom_content/plugins/export_ui/ctools_custom_content.inc +++ b/sites/all/modules/contrib/dev/ctools/ctools_custom_content/plugins/export_ui/ctools_custom_content.inc @@ -1,5 +1,9 @@ 'ctools_custom_content', 'access' => 'administer custom content', @@ -17,4 +21,3 @@ $plugin = array( 'handler' => 'ctools_custom_content_ui', ); - diff --git a/sites/all/modules/contrib/dev/ctools/ctools_custom_content/plugins/export_ui/ctools_custom_content_ui.class.php b/sites/all/modules/contrib/dev/ctools/ctools_custom_content/plugins/export_ui/ctools_custom_content_ui.class.php index 56fe4b2..e56f7a7 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_custom_content/plugins/export_ui/ctools_custom_content_ui.class.php +++ b/sites/all/modules/contrib/dev/ctools/ctools_custom_content/plugins/export_ui/ctools_custom_content_ui.class.php @@ -2,7 +2,7 @@ class ctools_custom_content_ui extends ctools_export_ui { - function edit_form(&$form, &$form_state) { + public function edit_form(&$form, &$form_state) { // Correct for an error that came in because filter format changed. if (is_array($form_state['item']->settings['body'])) { $form_state['item']->settings['format'] = $form_state['item']->settings['body']['format']; @@ -23,6 +23,22 @@ class ctools_custom_content_ui extends ctools_export_ui { '#title' => t('Title'), ); + $form['title_heading'] = array( + '#title' => t('Title heading'), + '#type' => 'select', + '#default_value' => isset($form_state['item']->settings['title_heading']) ? $form_state['item']->settings['title_heading'] : 'h2', + '#options' => array( + 'h1' => t('h1'), + 'h2' => t('h2'), + 'h3' => t('h3'), + 'h4' => t('h4'), + 'h5' => t('h5'), + 'h6' => t('h6'), + 'div' => t('div'), + 'span' => t('span'), + ), + ); + $form['body'] = array( '#type' => 'text_format', '#title' => t('Body'), @@ -38,17 +54,18 @@ class ctools_custom_content_ui extends ctools_export_ui { ); } - function edit_form_submit(&$form, &$form_state) { + public function edit_form_submit(&$form, &$form_state) { parent::edit_form_submit($form, $form_state); // Since items in our settings are not in the schema, we have to do these manually: $form_state['item']->settings['title'] = $form_state['values']['title']; + $form_state['item']->settings['title_heading'] = $form_state['values']['title_heading']; $form_state['item']->settings['body'] = $form_state['values']['body']['value']; $form_state['item']->settings['format'] = $form_state['values']['body']['format']; $form_state['item']->settings['substitute'] = $form_state['values']['substitute']; } - function list_form(&$form, &$form_state) { + public function list_form(&$form, &$form_state) { parent::list_form($form, $form_state); $options = array('all' => t('- All -')); @@ -65,7 +82,7 @@ class ctools_custom_content_ui extends ctools_export_ui { ); } - function list_filter($form_state, $item) { + public function list_filter($form_state, $item) { if ($form_state['values']['category'] != 'all' && $form_state['values']['category'] != $item->category) { return TRUE; } @@ -73,7 +90,7 @@ class ctools_custom_content_ui extends ctools_export_ui { return parent::list_filter($form_state, $item); } - function list_sort_options() { + public function list_sort_options() { return array( 'disabled' => t('Enabled, title'), 'title' => t('Title'), @@ -83,21 +100,25 @@ class ctools_custom_content_ui extends ctools_export_ui { ); } - function list_build_row($item, &$form_state, $operations) { - // Set up sorting + public function list_build_row($item, &$form_state, $operations) { + // Set up sorting. switch ($form_state['values']['order']) { case 'disabled': $this->sorts[$item->name] = empty($item->disabled) . $item->admin_title; break; + case 'title': $this->sorts[$item->name] = $item->admin_title; break; + case 'name': $this->sorts[$item->name] = $item->name; break; + case 'category': $this->sorts[$item->name] = $item->category; break; + case 'storage': $this->sorts[$item->name] = $item->type . $item->admin_title; break; @@ -117,7 +138,7 @@ class ctools_custom_content_ui extends ctools_export_ui { ); } - function list_table_header() { + public function list_table_header() { return array( array('data' => t('Name'), 'class' => array('ctools-export-ui-name')), array('data' => t('Title'), 'class' => array('ctools-export-ui-title')), diff --git a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/README.txt b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/README.txt index 42edcdc..2f9b0ff 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/README.txt +++ b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/README.txt @@ -11,4 +11,4 @@ There are a number of ways to profit from this: 2. There is a sample panel. You can access it at /ctools_plugin_example/xxxx to see how it works. -3. There is Advanced Help at admin/advanced_help/ctools_plugin_example. \ No newline at end of file +3. There is Advanced Help at admin/advanced_help/ctools_plugin_example. diff --git a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/ctools_plugin_example.info b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/ctools_plugin_example.info index f526be6..3f268db 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/ctools_plugin_example.info +++ b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/ctools_plugin_example.info @@ -1,16 +1,14 @@ name = Chaos Tools (CTools) Plugin Example description = Shows how an external module can provide ctools plugins (for Panels, etc.). package = Chaos tool suite -version = CTOOLS_MODULE_VERSION dependencies[] = ctools dependencies[] = panels dependencies[] = page_manager dependencies[] = advanced_help core = 7.x -; Information added by Drupal.org packaging script on 2015-01-28 -version = "7.x-1.6" +; Information added by Drupal.org packaging script on 2019-02-08 +version = "7.x-1.15" core = "7.x" project = "ctools" -datestamp = "1422471484" - +datestamp = "1549603691" diff --git a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/ctools_plugin_example.module b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/ctools_plugin_example.module index 01d5338..a9a6080 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/ctools_plugin_example.module +++ b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/ctools_plugin_example.module @@ -1,9 +1,8 @@ .pages_default.inc + * .pages_default.inc. */ function ctools_plugin_example_ctools_plugin_api($module, $api) { // @todo -- this example should explain how to put it in a different file. @@ -71,7 +70,8 @@ function ctools_plugin_example_ctools_plugin_api($module, $api) { } /** - * Just provide an explanation page for the admin section + * Just provide an explanation page for the admin section. + * * @return unknown_type */ function ctools_plugin_example_explanation_page() { diff --git a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/ctools_plugin_example.pages_default.inc b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/ctools_plugin_example.pages_default.inc index 10a7619..ed3ffd7 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/ctools_plugin_example.pages_default.inc +++ b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/ctools_plugin_example.pages_default.inc @@ -6,7 +6,7 @@ */ /** - * Default panels pages for CTools Plugin Example + * Default panels pages for CTools Plugin Example. * * To pick up this file, your module needs to implement * hook_ctools_plugin_api() - See ctools_plugin_example_ctools_plugin_api() in @@ -23,12 +23,10 @@ * @return * Array of pages, normally exported from Panels. */ - function ctools_plugin_example_default_page_manager_pages() { - // begin exported panel. - - $page = new stdClass; + // Begin exported panel. + $page = new stdClass(); $page->disabled = FALSE; /* Edit this to true to make a default page disabled initially */ $page->api_version = 1; $page->name = 'ctools_plugin_example'; @@ -37,31 +35,31 @@ function ctools_plugin_example_default_page_manager_pages() { $page->admin_description = 'This panel provides no functionality to a working Drupal system. It\'s intended to display the various sample plugins provided by the CTools Plugin Example module. '; $page->path = 'ctools_plugin_example/%sc'; $page->access = array( - 'logic' => 'and', + 'logic' => 'and', ); $page->menu = array( - 'type' => 'normal', - 'title' => 'CTools plugin example', - 'name' => 'navigation', - 'weight' => '0', - 'parent' => array( - 'type' => 'none', - 'title' => '', + 'type' => 'normal', + 'title' => 'CTools plugin example', 'name' => 'navigation', 'weight' => '0', - ), + 'parent' => array( + 'type' => 'none', + 'title' => '', + 'name' => 'navigation', + 'weight' => '0', + ), ); $page->arguments = array( - 'sc' => array( - 'id' => 2, - 'identifier' => 'simplecontext-arg', - 'name' => 'simplecontext_arg', - 'settings' => array(), - ), + 'sc' => array( + 'id' => 2, + 'identifier' => 'simplecontext-arg', + 'name' => 'simplecontext_arg', + 'settings' => array(), + ), ); $page->conf = array(); $page->default_handlers = array(); - $handler = new stdClass; + $handler = new stdClass(); $handler->disabled = FALSE; /* Edit this to true to make a default handler disabled initially */ $handler->api_version = 1; $handler->name = 'page_ctools_panel_context'; @@ -70,44 +68,45 @@ function ctools_plugin_example_default_page_manager_pages() { $handler->handler = 'panel_context'; $handler->weight = 0; $handler->conf = array( - 'title' => 'Panel', - 'no_blocks' => FALSE, - 'css_id' => '', - 'css' => '', - 'contexts' => array( - '0' => array( - 'name' => 'simplecontext', - 'id' => 1, - 'identifier' => 'Configured simplecontext (not from argument)', - 'keyword' => 'configured_simplecontext', - 'context_settings' => array( - 'sample_simplecontext_setting' => 'default simplecontext setting', - ), - ), - ), - 'relationships' => array( - '0' => array( - 'context' => 'argument_simplecontext_arg_2', - 'name' => 'relcontext_from_simplecontext', - 'id' => 1, - 'identifier' => 'Relcontext from simplecontext (from relationship)', - 'keyword' => 'relcontext', - ), - ), - 'access' => array( - 'logic' => 'and', - ), + 'title' => 'Panel', + 'no_blocks' => FALSE, + 'css_id' => '', + 'css' => '', + 'contexts' => array( + '0' => array( + 'name' => 'simplecontext', + 'id' => 1, + 'identifier' => 'Configured simplecontext (not from argument)', + 'keyword' => 'configured_simplecontext', + 'context_settings' => array( + 'sample_simplecontext_setting' => 'default simplecontext setting', + ), + ), + ), + 'relationships' => array( + '0' => array( + 'context' => 'argument_simplecontext_arg_2', + 'name' => 'relcontext_from_simplecontext', + 'id' => 1, + 'identifier' => 'Relcontext from simplecontext (from relationship)', + 'keyword' => 'relcontext', + ), + ), + 'access' => array( + 'logic' => 'and', + ), + 'pipeline' => 'standard', ); - $display = new panels_display; + $display = new panels_display(); $display->layout = 'threecol_33_34_33_stacked'; $display->layout_settings = array(); $display->panel_settings = array( - 'style' => 'rounded_corners', - 'style_settings' => array( - 'default' => array( - 'corner_location' => 'pane', - ), - ), + 'style' => 'rounded_corners', + 'style_settings' => array( + 'default' => array( + 'corner_location' => 'pane', + ), + ), ); $display->cache = array(); $display->title = 'CTools plugin example panel'; @@ -115,7 +114,7 @@ function ctools_plugin_example_default_page_manager_pages() { $display->title_pane = 1; $display->content = array(); $display->panels = array(); - $pane = new stdClass; + $pane = new stdClass(); $pane->pid = 'new-1'; $pane->panel = 'left'; $pane->type = 'no_context_content_type'; @@ -135,7 +134,7 @@ function ctools_plugin_example_default_page_manager_pages() { $pane->position = 0; $display->content['new-1'] = $pane; $display->panels['left'][0] = 'new-1'; - $pane = new stdClass; + $pane = new stdClass(); $pane->pid = 'new-2'; $pane->panel = 'left'; $pane->type = 'custom'; @@ -148,15 +147,15 @@ function ctools_plugin_example_default_page_manager_pages() { 'settings' => array( 'greater_than' => '1', 'arg_length' => '4', - ), + ), 'context' => 'argument_simplecontext_arg_2', - ), - ), + ), + ), ); $pane->configuration = array( 'title' => 'Long Arg Visibility Block', 'body' => 'This block will be here when the argument is longer than configured arg length. It uses the \'arg_length\' access plugin to test against the length of the argument used for Simplecontext.', - 'format' => '1', + 'format' => 'filtered_html', 'substitute' => 1, ); $pane->cache = array(); @@ -166,7 +165,7 @@ function ctools_plugin_example_default_page_manager_pages() { $pane->position = 1; $display->content['new-2'] = $pane; $display->panels['left'][1] = 'new-2'; - $pane = new stdClass; + $pane = new stdClass(); $pane->pid = 'new-3'; $pane->panel = 'left'; $pane->type = 'custom'; @@ -179,15 +178,15 @@ function ctools_plugin_example_default_page_manager_pages() { 'settings' => array( 'greater_than' => '0', 'arg_length' => '4', - ), + ), 'context' => 'argument_simplecontext_arg_2', - ), - ), + ), + ), ); $pane->configuration = array( 'title' => 'Short Arg Visibility', 'body' => 'This block appears when the simplecontext argument is less than the configured length.', - 'format' => '1', + 'format' => 'filtered_html', 'substitute' => 1, ); $pane->cache = array(); @@ -197,7 +196,7 @@ function ctools_plugin_example_default_page_manager_pages() { $pane->position = 2; $display->content['new-3'] = $pane; $display->panels['left'][2] = 'new-3'; - $pane = new stdClass; + $pane = new stdClass(); $pane->pid = 'new-4'; $pane->panel = 'middle'; $pane->type = 'simplecontext_content_type'; @@ -241,7 +240,7 @@ function ctools_plugin_example_default_page_manager_pages() { $pane->position = 0; $display->content['new-4'] = $pane; $display->panels['middle'][0] = 'new-4'; - $pane = new stdClass; + $pane = new stdClass(); $pane->pid = 'new-5'; $pane->panel = 'middle'; $pane->type = 'simplecontext_content_type'; @@ -285,7 +284,7 @@ function ctools_plugin_example_default_page_manager_pages() { $pane->position = 1; $display->content['new-5'] = $pane; $display->panels['middle'][1] = 'new-5'; - $pane = new stdClass; + $pane = new stdClass(); $pane->pid = 'new-6'; $pane->panel = 'middle'; $pane->type = 'custom'; @@ -299,7 +298,7 @@ function ctools_plugin_example_default_page_manager_pages() { item1 is %sc:item1 item2 is %sc:item2 description is %sc:description', - 'format' => '1', + 'format' => 'filtered_html', 'substitute' => 1, ); $pane->cache = array(); @@ -309,7 +308,7 @@ function ctools_plugin_example_default_page_manager_pages() { $pane->position = 2; $display->content['new-6'] = $pane; $display->panels['middle'][2] = 'new-6'; - $pane = new stdClass; + $pane = new stdClass(); $pane->pid = 'new-7'; $pane->panel = 'right'; $pane->type = 'relcontext_content_type'; @@ -353,7 +352,7 @@ function ctools_plugin_example_default_page_manager_pages() { $pane->position = 0; $display->content['new-7'] = $pane; $display->panels['right'][0] = 'new-7'; - $pane = new stdClass; + $pane = new stdClass(); $pane->pid = 'new-8'; $pane->panel = 'top'; $pane->type = 'custom'; @@ -365,7 +364,7 @@ function ctools_plugin_example_default_page_manager_pages() { 'body' => 'The CTools Plugin Example module (and this panel page) are just here to demonstrate how to build CTools plugins. ', - 'format' => '2', + 'format' => 'full_html', 'substitute' => 1, ); $pane->cache = array(); @@ -378,12 +377,11 @@ function ctools_plugin_example_default_page_manager_pages() { $handler->conf['display'] = $display; $page->default_handlers[$handler->name] = $handler; - // end of exported panel. + // End of exported panel. $pages['ctools_plugin_example_demo_page'] = $page; - // begin exported panel - - $page = new stdClass; + // Begin exported panel. + $page = new stdClass(); $page->disabled = FALSE; /* Edit this to true to make a default page disabled initially */ $page->api_version = 1; $page->name = 'ctools_plugin_example_base'; @@ -396,7 +394,7 @@ function ctools_plugin_example_default_page_manager_pages() { $page->arguments = array(); $page->conf = array(); $page->default_handlers = array(); - $handler = new stdClass; + $handler = new stdClass(); $handler->disabled = FALSE; /* Edit this to true to make a default handler disabled initially */ $handler->api_version = 1; $handler->name = 'page_ctools_plugin_example_base_panel_context'; @@ -405,14 +403,15 @@ function ctools_plugin_example_default_page_manager_pages() { $handler->handler = 'panel_context'; $handler->weight = 0; $handler->conf = array( - 'title' => 'Panel', - 'no_blocks' => FALSE, - 'css_id' => '', - 'css' => '', - 'contexts' => array(), - 'relationships' => array(), + 'title' => 'Panel', + 'no_blocks' => FALSE, + 'css_id' => '', + 'css' => '', + 'contexts' => array(), + 'relationships' => array(), + 'pipeline' => 'standard', ); - $display = new panels_display; + $display = new panels_display(); $display->layout = 'onecol'; $display->layout_settings = array(); $display->panel_settings = array(); @@ -421,7 +420,7 @@ function ctools_plugin_example_default_page_manager_pages() { $display->hide_title = FALSE; $display->content = array(); $display->panels = array(); - $pane = new stdClass; + $pane = new stdClass(); $pane->pid = 'new-1'; $pane->panel = 'middle'; $pane->type = 'custom'; @@ -431,7 +430,7 @@ function ctools_plugin_example_default_page_manager_pages() { $pane->configuration = array( 'title' => 'Use this page with an argument', 'body' => 'This demo page works if you use an argument, like ctools_plugin_example/xxxxx.', - 'format' => '1', + 'format' => 'filtered_html', 'substitute' => NULL, ); $pane->cache = array(); @@ -443,9 +442,8 @@ function ctools_plugin_example_default_page_manager_pages() { $display->panels['middle'][0] = 'new-1'; $handler->conf['display'] = $display; $page->default_handlers[$handler->name] = $handler; - // end exported panel. - + // End exported panel. $pages['base_page'] = $page; return $pages; -} \ No newline at end of file +} diff --git a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/access/arg_length.inc b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/access/arg_length.inc index 2a09eea..3b5d863 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/access/arg_length.inc +++ b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/access/arg_length.inc @@ -59,7 +59,8 @@ function ctools_plugin_example_arg_length_ctools_access_check($conf, $context) { */ function ctools_plugin_example_arg_length_ctools_access_summary($conf, $context) { return t('Simpletext argument must be !comp @length characters', - array('!comp' => $conf['greater_than'] ? 'greater than' : 'less than or equal to', - '@length' => $conf['arg_length'])); + array( + '!comp' => $conf['greater_than'] ? 'greater than' : 'less than or equal to', + '@length' => $conf['arg_length'], + )); } - diff --git a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/access/example_role.inc b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/access/example_role.inc index bbe364c..75721e8 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/access/example_role.inc +++ b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/access/example_role.inc @@ -4,7 +4,7 @@ * @file * Plugin to provide access control based upon role membership. * This is directly from the ctools module, but serves as a good - * example of an access plugin + * example of an access plugin. */ /** @@ -73,4 +73,3 @@ function ctools_plugin_example_example_role_ctools_access_summary($conf, $contex } return format_plural(count($names), '@identifier must have role "@roles"', '@identifier can be one of "@roles"', array('@roles' => implode(', ', $names), '@identifier' => $context->identifier)); } - diff --git a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/arguments/simplecontext_arg.inc b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/arguments/simplecontext_arg.inc index 51c7c60..7fb732c 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/arguments/simplecontext_arg.inc +++ b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/arguments/simplecontext_arg.inc @@ -2,7 +2,6 @@ /** * @file - * * Sample plugin to provide an argument handler for a simplecontext. * * Given any argument to the page, simplecontext will get it @@ -18,14 +17,12 @@ */ $plugin = array( 'title' => t("Simplecontext arg"), - // keyword to use for %substitution + // Keyword to use for %substitution. 'keyword' => 'simplecontext', 'description' => t('Creates a "simplecontext" from the arg.'), 'context' => 'simplecontext_arg_context', - // 'settings form' => 'simplecontext_arg_settings_form', - // placeholder_form is used in panels preview, for example, so we can - // preview without getting the arg from a URL + // preview without getting the arg from a URL. 'placeholder form' => array( '#type' => 'textfield', '#description' => t('Enter the simplecontext arg'), diff --git a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/content_types/no_context_content_type.inc b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/content_types/no_context_content_type.inc index 3c02ab8..48ce0f5 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/content_types/no_context_content_type.inc +++ b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/content_types/no_context_content_type.inc @@ -5,7 +5,6 @@ * "No context" sample content type. It operates with no context at all. It would * be basically the same as a 'custom content' block, but it's not even that * sophisticated. - * */ /** @@ -33,7 +32,7 @@ $plugin = array( 'icon' => 'icon_example.png', 'category' => array(t('CTools Examples'), -9), - // this example does not provide 'admin info', which would populate the + // This example does not provide 'admin info', which would populate the // panels builder page preview. ); @@ -56,7 +55,7 @@ function no_context_content_type_render($subtype, $conf, $args, $context) { $ctools_help = theme('advanced_help_topic', array('module' => 'ctools', 'topic' => 'plugins', 'type' => 'title')); $ctools_plugin_example_help = theme('advanced_help_topic', array('module' => 'ctools_plugin_example', 'topic' => 'Chaos-Tools--CTools--Plugin-Examples', 'type' => 'title')); - // The title actually used in rendering + // The title actually used in rendering. $block->title = check_plain("No-context content type"); $block->content = t("
    Welcome to the CTools Plugin Example demonstration content type. @@ -84,7 +83,6 @@ function no_context_content_type_render($subtype, $conf, $args, $context) { * Note that if we had not provided an entry for this in hook_content_types, * this could have had the default name * ctools_plugin_example_no_context_content_type_edit_form. - * */ function no_context_content_type_edit_form($form, &$form_state) { $conf = $form_state['conf']; diff --git a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/content_types/relcontext_content_type.inc b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/content_types/relcontext_content_type.inc index bf54dce..ced6411 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/content_types/relcontext_content_type.inc +++ b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/content_types/relcontext_content_type.inc @@ -1,6 +1,5 @@ array(t('CTools Examples'), -9), 'edit form' => 'relcontext_edit_form', - // this example does not provide 'admin info', which would populate the + // This example does not provide 'admin info', which would populate the // panels builder page preview. - ); /** @@ -60,7 +58,7 @@ function relcontext_content_type_render($subtype, $conf, $args, $context) { In our case, the configuration form (\$conf) has just one field, 'config_item_1; and it's configured with: "); - if (!empty($conf)) { + if (!empty($conf)) { $block->content .= '
    ' . var_export($conf['config_item_1'], TRUE) . '
    '; } if (!empty($context)) { @@ -77,7 +75,6 @@ function relcontext_content_type_render($subtype, $conf, $args, $context) { /** * 'Edit' callback for the content type. * This example just returns a form. - * */ function relcontext_edit_form($form, &$form_state) { $conf = $form_state['conf']; diff --git a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/content_types/simplecontext_content_type.inc b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/content_types/simplecontext_content_type.inc index a308683..e34fa18 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/content_types/simplecontext_content_type.inc +++ b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/content_types/simplecontext_content_type.inc @@ -1,6 +1,5 @@ 'simplecontext_content_type_edit_form', 'admin title' => 'ctools_plugin_example_simplecontext_content_type_admin_title', - // presents a block which is used in the preview of the data. + // Presents a block which is used in the preview of the data. // Pn Panels this is the preview pane shown on the panels building page. 'admin info' => 'ctools_plugin_example_simplecontext_content_type_admin_info', 'category' => array(t('CTools Examples'), -9), @@ -103,7 +101,6 @@ function simplecontext_content_type_render($subtype, $conf, $args, $context) { /** * 'Edit' callback for the content type. * This example just returns a form. - * */ function simplecontext_content_type_edit_form($form, &$form_state) { $conf = $form_state['conf']; diff --git a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/contexts/relcontext.inc b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/contexts/relcontext.inc index 0c7ef11..61e25db 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/contexts/relcontext.inc +++ b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/contexts/relcontext.inc @@ -80,4 +80,3 @@ function relcontext_settings_form($conf, $external = FALSE) { ); return $form; } - diff --git a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/contexts/simplecontext.inc b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/contexts/simplecontext.inc index e19a842..0ee4658 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/contexts/simplecontext.inc +++ b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/contexts/simplecontext.inc @@ -1,10 +1,8 @@ t("Simplecontext"), 'description' => t('A single "simplecontext" context, or data element.'), - 'context' => 'ctools_plugin_example_context_create_simplecontext', // func to create context +// Func to create context. + 'context' => 'ctools_plugin_example_context_create_simplecontext', 'context name' => 'simplecontext', 'settings form' => 'simplecontext_settings_form', 'keyword' => 'simplecontext', @@ -69,7 +68,7 @@ function ctools_plugin_example_context_create_simplecontext($empty, $data = NULL // This is used for keyword. $context->title = $data; $context->argument = $data; - // Make up a bogus context + // Make up a bogus context. $context->data = new stdClass(); $context->data->item1 = t("Item1"); $context->data->item2 = t("Item2"); @@ -102,8 +101,6 @@ function simplecontext_settings_form($conf, $external = FALSE) { return $form; } - - /** * Provide a list of sub-keywords. * @@ -125,10 +122,11 @@ function simplecontext_convert($context, $type) { switch ($type) { case 'item1': return $context->data->item1; + case 'item2': return $context->data->item2; + case 'description': return $context->data->description; } } - diff --git a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/panels.pages.inc b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/panels.pages.inc index d3022af..25422cf 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/panels.pages.inc +++ b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/panels.pages.inc @@ -19,24 +19,24 @@ function ctools_plugin_example_default_panel_pages() { $page->load_flags = 1; $page->css_id = ''; $page->arguments = array( - 0 => + 0 => array( - 'name' => 'simplecontext_arg', - 'id' => 1, - 'default' => '404', - 'title' => '', - 'identifier' => 'Simplecontext arg', - 'keyword' => 'simplecontext', + 'name' => 'simplecontext_arg', + 'id' => 1, + 'default' => '404', + 'title' => '', + 'identifier' => 'Simplecontext arg', + 'keyword' => 'simplecontext', ), ); $page->relationships = array( - 0 => + 0 => array( - 'context' => 'argument_simplecontext_arg_1', - 'name' => 'relcontext_from_simplecontext', - 'id' => 1, - 'identifier' => 'Relcontext from Simplecontext', - 'keyword' => 'relcontext', + 'context' => 'argument_simplecontext_arg_1', + 'name' => 'relcontext_from_simplecontext', + 'id' => 1, + 'identifier' => 'Relcontext from Simplecontext', + 'keyword' => 'relcontext', ), ); $page->no_blocks = '0'; @@ -58,14 +58,14 @@ function ctools_plugin_example_default_panel_pages() { $pane->subtype = 'custom'; $pane->access = array(); $pane->configuration = array( - 'style' => 'default', - 'override_title' => 0, - 'override_title_text' => '', - 'css_id' => '', - 'css_class' => '', - 'title' => '"No Context Item"', - 'body' => 'The "no context item" content type is here to demonstrate that you can create a content_type that does not require a context. This is probably the same as just creating a custom php block on the fly, and might serve the same purpose.', - 'format' => '1', + 'style' => 'default', + 'override_title' => 0, + 'override_title_text' => '', + 'css_id' => '', + 'css_class' => '', + 'title' => '"No Context Item"', + 'body' => 'The "no context item" content type is here to demonstrate that you can create a content_type that does not require a context. This is probably the same as just creating a custom php block on the fly, and might serve the same purpose.', + 'format' => '1', ); $pane->cache = array(); $display->content['new-1'] = $pane; @@ -78,14 +78,14 @@ function ctools_plugin_example_default_panel_pages() { $pane->subtype = 'description'; $pane->access = array(); $pane->configuration = array( - 'style' => 'default', - 'override_title' => 0, - 'override_title_text' => '', - 'css_id' => '', - 'css_class' => '', - 'item1' => 'one', - 'item2' => 'two', - 'item3' => 'three', + 'style' => 'default', + 'override_title' => 0, + 'override_title_text' => '', + 'css_id' => '', + 'css_class' => '', + 'item1' => 'one', + 'item2' => 'two', + 'item3' => 'three', ); $pane->cache = array(); $display->content['new-2'] = $pane; @@ -98,16 +98,16 @@ function ctools_plugin_example_default_panel_pages() { $pane->subtype = 'custom'; $pane->access = array(); $pane->configuration = array( - 'style' => 'default', - 'override_title' => 0, - 'override_title_text' => '', - 'css_id' => '', - 'css_class' => '', - 'title' => 'Simplecontext', - 'body' => 'The "Simplecontext" content and content type demonstrate a very basic context and how to display it. + 'style' => 'default', + 'override_title' => 0, + 'override_title_text' => '', + 'css_id' => '', + 'css_class' => '', + 'title' => 'Simplecontext', + 'body' => 'The "Simplecontext" content and content type demonstrate a very basic context and how to display it. Simplecontext includes configuration, so it can get info from the config. It can also get its information to run from a simplecontext context, generated either from an arg to the panels page or via explicitly adding a context to the page.', - 'format' => '1', + 'format' => '1', ); $pane->cache = array(); $display->content['new-3'] = $pane; @@ -119,17 +119,17 @@ function ctools_plugin_example_default_panel_pages() { $pane->shown = '1'; $pane->subtype = 'description'; $pane->access = array( - 0 => '2', - 1 => '4', + 0 => '2', + 1 => '4', ); $pane->configuration = array( - 'context' => 'argument_simplecontext_arg_1', - 'style' => 'default', - 'override_title' => 0, - 'override_title_text' => '', - 'css_id' => '', - 'css_class' => '', - 'config_item_1' => 'simplecontext called from arg', + 'context' => 'argument_simplecontext_arg_1', + 'style' => 'default', + 'override_title' => 0, + 'override_title_text' => '', + 'css_id' => '', + 'css_class' => '', + 'config_item_1' => 'simplecontext called from arg', ); $pane->cache = array(); $display->content['new-4'] = $pane; @@ -142,14 +142,14 @@ function ctools_plugin_example_default_panel_pages() { $pane->subtype = 'custom'; $pane->access = array(); $pane->configuration = array( - 'style' => 'default', - 'override_title' => 0, - 'override_title_text' => '', - 'css_id' => '', - 'css_class' => '', - 'title' => 'Relcontext', - 'body' => 'The relcontext content_type gets its data from a relcontext, which is an example of a relationship. This panel should be run with an argument like "/xxx", which allows the simplecontext to get its context, and then the relcontext is configured in this panel to get (create) its data from the simplecontext.', - 'format' => '1', + 'style' => 'default', + 'override_title' => 0, + 'override_title_text' => '', + 'css_id' => '', + 'css_class' => '', + 'title' => 'Relcontext', + 'body' => 'The relcontext content_type gets its data from a relcontext, which is an example of a relationship. This panel should be run with an argument like "/xxx", which allows the simplecontext to get its context, and then the relcontext is configured in this panel to get (create) its data from the simplecontext.', + 'format' => '1', ); $pane->cache = array(); $display->content['new-5'] = $pane; @@ -162,13 +162,13 @@ function ctools_plugin_example_default_panel_pages() { $pane->subtype = 'description'; $pane->access = array(); $pane->configuration = array( - 'context' => 'relationship_relcontext_from_simplecontext_1', - 'style' => 'default', - 'override_title' => 0, - 'override_title_text' => '', - 'css_id' => '', - 'css_class' => '', - 'config_item_1' => 'default1', + 'context' => 'relationship_relcontext_from_simplecontext_1', + 'style' => 'default', + 'override_title' => 0, + 'override_title_text' => '', + 'css_id' => '', + 'css_class' => '', + 'config_item_1' => 'default1', ); $pane->cache = array(); $display->content['new-6'] = $pane; @@ -181,13 +181,13 @@ function ctools_plugin_example_default_panel_pages() { $pane->subtype = 'custom_php'; $pane->access = array(); $pane->configuration = array( - 'style' => 'default', - 'override_title' => 0, - 'override_title_text' => '', - 'css_id' => '', - 'css_class' => '', - 'title' => '', - 'body' => '$arg = arg(1); + 'style' => 'default', + 'override_title' => 0, + 'override_title_text' => '', + 'css_id' => '', + 'css_class' => '', + 'title' => '', + 'body' => '$arg = arg(1); $arg0 = arg(0); if (!$arg) { $block->content = <<displays = array(); $pages['ctools_plugin_example'] = $page; - return $pages; } diff --git a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/relationships/relcontext_from_simplecontext.inc b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/relationships/relcontext_from_simplecontext.inc index 6224621..087d5ec 100644 --- a/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/relationships/relcontext_from_simplecontext.inc +++ b/sites/all/modules/contrib/dev/ctools/ctools_plugin_example/plugins/relationships/relcontext_from_simplecontext.inc @@ -1,9 +1,7 @@ 'Space separated list of exportables you want to view.', ), 'options' => array( - 'indent' => 'The string to use for indentation when dispalying the exportable export code. Defaults to \'\'.', + 'indent' => 'The string to use for indentation when displaying the exportable export code. Defaults to \'\'.', 'no-colour' => 'Remove any colour formatting from export string output. Ideal if you are sending the output of this command to a file.', 'module' => $module_text, 'all' => $all_text, @@ -140,13 +140,14 @@ function ctools_drush_help($section) { switch ($section) { case 'meta:ctools:title': return dt('CTools commands'); + case 'meta:entity:summary': return dt('CTools drush commands.'); } } /** - * Drush callback: export + * Drush callback: export. */ function ctools_drush_export($module = 'foo') { $error = FALSE; @@ -338,7 +339,7 @@ function _ctools_drush_selection_screen(array $tables = array()) { $build[$table_choice]['count'] = count($multi_select); $selections[$table_choice] = array(); foreach ($multi_select as $key) { - $selections[$table_choice][$key] = $key; + $selections[$table_choice][$key] = $key; } } } @@ -445,11 +446,13 @@ function ctools_drush_export_info() { } } } + /** * Drush callback: Acts as the hub for all op commands to keep * all arg handling etc in one place. */ function ctools_drush_export_op_command() { + $args = func_get_args(); // Get all info for the current drush command. $command = drush_get_command(); $op = ''; @@ -457,17 +460,20 @@ function ctools_drush_export_op_command() { switch ($command['command']) { case 'ctools-export-view': $op = 'view'; - break; + break; + case 'ctools-export-revert': // Revert is same as deleting. As any objects in the db are deleted. $op = 'delete'; - break; + break; + case 'ctools-export-enable': $op = 'enable'; - break; + break; + case 'ctools-export-disable': $op = 'disable'; - break; + break; } if (!$op) { @@ -475,7 +481,7 @@ function ctools_drush_export_op_command() { } if (drush_get_option('all', FALSE)) { - $info = _ctools_drush_export_info('', TRUE); + $info = _ctools_drush_export_info(array(), TRUE); $exportable_info = $info['exportables']; $all = drush_confirm(dt('Are you sure you would like to !op all exportables on the system?', @@ -490,7 +496,6 @@ function ctools_drush_export_op_command() { } } else { - $args = func_get_args(); // Table name should always be first arg... $table_name = array_shift($args); // Any additional args are assumed to be exportable names. @@ -512,7 +517,6 @@ function ctools_drush_export_op_command() { * @param $op * @param $table_name * @param $exportables - * */ function ctools_drush_export_op($op = '', $table_name = '', $exportables = NULL) { $objects = ctools_export_crud_load_multiple($table_name, array_keys($exportables)); @@ -538,7 +542,7 @@ function ctools_drush_export_op($op = '', $table_name = '', $exportables = NULL) * @param $object_names * * @return - * Array of exportable objects (filtered if necessary, by name etc..) or FALSE if not. + * Array of exportable objects (filtered if necessary, by name etc..) or FALSE if not. */ function _ctools_drush_export_op_command_logic($op = '', $table_name = NULL, array $object_names = array()) { if (!$table_name) { @@ -644,7 +648,7 @@ function _ctools_drush_export_info(array $table_names = array(), $load = FALSE) return array('exportables' => $exportables, 'schemas' => $schemas); } -/* +/** * View a single object. * * @param $table_name @@ -662,7 +666,7 @@ function _ctools_drush_export_view($table_name, $object) { } } -/* +/** * Revert a single object. * * @param $table_name @@ -681,7 +685,7 @@ function _ctools_drush_export_delete($table_name, $object) { } } -/* +/** * Enable a single object. * * @param $table_name @@ -701,7 +705,7 @@ function _ctools_drush_export_enable($table_name, $object) { } } -/* +/** * Disable a single object. * * @param $table_name @@ -723,9 +727,9 @@ function _ctools_drush_export_disable($table_name, $object) { /** * Filter a nested array of exportables by export module. * - * @param $exportables array + * @param array $exportables * Passed by reference. A nested array of exportables, keyed by table name. - * @param $export_module string + * @param string $export_module * The name of the export module providing the exportable. */ function _ctools_drush_export_module_filter($exportables, $export_module) { @@ -778,7 +782,7 @@ function _ctools_drush_object_is_disabled($object) { /** * Determine if an object is enabled. * - * @see _ctools_drush_object_is_disabled. + * @see _ctools_drush_object_is_disabled() */ function _ctools_drush_object_is_enabled($object) { return (empty($object->disabled)) ? TRUE : FALSE; @@ -835,7 +839,7 @@ function _ctools_drush_object_is_not_code_only($object) { * Array of exportables to count. * * @return - * Array of count data containing the following: + * Array of count data containing the following: * 'total' - A total count of all exportables. * 'exportables' - An array of exportable counts per table. */ @@ -867,26 +871,32 @@ function _ctools_drush_filter_exportables($exportables, $filter) { // Show enabled exportables only. case 'enabled': $eval = '_ctools_drush_object_is_disabled'; - break; + break; + // Show disabled exportables only. case 'disabled': $eval = '_ctools_drush_object_is_enabled'; - break; + break; + // Show overridden exportables only. case 'overridden': $eval = '_ctools_drush_object_is_not_overridden'; - break; + break; + // Show database only exportables. case 'database': $eval = '_ctools_drush_object_is_not_db_only'; - break; + break; + // Show code only exportables. case 'code': $eval = '_ctools_drush_object_is_not_code_only'; - break; + break; + // Do nothing. case 'all': break; + default: drush_log(dt('Invalid filter option. Available options are: enabled, disabled, overridden, database, and code.'), 'error'); return; @@ -983,33 +993,42 @@ class shellColours { 'light_gray' => '47', ); + /** + * shellColours constructor. + */ private function __construct() {} - // Returns coloured string + /** + * Returns coloured string. + */ public static function getColouredOutput($string, $foreground_colour = NULL, $background_colour = NULL) { $coloured_string = ""; - // Check if given foreground colour found + // Check if given foreground colour found. if ($foreground_colour) { $coloured_string .= "\033[" . self::$foreground_colours[$foreground_colour] . "m"; } - // Check if given background colour found + // Check if given background colour found. if ($background_colour) { $coloured_string .= "\033[" . self::$background_colours[$background_colour] . "m"; } - // Add string and end colouring - $coloured_string .= $string . "\033[0m"; + // Add string and end colouring. + $coloured_string .= $string . "\033[0m"; return $coloured_string; } - // Returns all foreground colour names + /** + * Returns all foreground colour names. + */ public static function getForegroundColours() { return array_keys(self::$foreground_colours); } - // Returns all background colour names + /** + * Returns all background colour names. + */ public static function getBackgroundColours() { return array_keys(self::$background_colours); } diff --git a/sites/all/modules/contrib/dev/ctools/help/export.html b/sites/all/modules/contrib/dev/ctools/help/export.html index ce24cad..573c98a 100644 --- a/sites/all/modules/contrib/dev/ctools/help/export.html +++ b/sites/all/modules/contrib/dev/ctools/help/export.html @@ -154,7 +154,7 @@ function mymodule_schema() {
    Bulk export callback to provide a list of exportable objects to be chosen for bulk exporting. Defaults to $module . '_' . $table . '_list' if the function exists. If it is not, a default listing function will be provided that will make a best effort to list the titles. See ctools_export_default_list().
    to hook code callback
    -
    Function used to generate an export for the bulk export process. This is only necessary if the export is more complicated than simply listing the fields. Defaults to $module . '_' . $table . '_to_hook_code'. +
    Function used to generate an export for the bulk export process. This is only necessary if the export is more complicated than simply listing the fields. Defaults to $module . '_' . $table . '_to_hook_code'.
    boolean
    Explicitly indicate if a table field contains a boolean or not. The Schema API does not model the diff --git a/sites/all/modules/contrib/dev/ctools/help/modal.html b/sites/all/modules/contrib/dev/ctools/help/modal.html index ea823a0..761fd2e 100644 --- a/sites/all/modules/contrib/dev/ctools/help/modal.html +++ b/sites/all/modules/contrib/dev/ctools/help/modal.html @@ -82,7 +82,7 @@ function ctools_modal_text_button($text, $dest, $alt, $class = '') {
  • modalSize: an array of data to control the sizing of the modal. It can contain:
    • type: Either fixed or scale. If fixed, the modal will always be a fixed size. If scale the modal will scale to a percentage of the browser window. Default: scale. -
    • width: If fixed the width in pixels. If scale the percentage of the screen expressed as a number less than zero. (For 80 percent, use .8, for example). Default: .8
    • +
    • width: If fixed the width in pixels. If scale the percentage of the screen expressed as a number less than zero. (For 80 percent, use .8, for example). Default: .8
    • height: If fixed the height in pixels. If scale the percentage of the screen expressed as a number less than zero. (For 80 percent, use .8, for example). Default: .8
    • addWidth: Any additional width to add to the modal in pixels. Only useful if the type is scale. Default: 0
    • addHeight: Any additional height to add to the modal in pixels. Only useful if the type is scale. Default: 0
    • @@ -168,7 +168,7 @@ function ctools_ajax_sample_login($js) {
       function ctools_ajax_hello_world($js) {
         $title = t('Greetings');
      -  $output = '<p>' . t('Hello world') . ''</p>';
      +  $output = '<p>' . t('Hello world') . '</p>';
         if ($js) {
           ctools_modal_render($title, $output);
         }
      diff --git a/sites/all/modules/contrib/dev/ctools/help/plugins-creating.html b/sites/all/modules/contrib/dev/ctools/help/plugins-creating.html
      index 2323705..6d5b35f 100644
      --- a/sites/all/modules/contrib/dev/ctools/help/plugins-creating.html
      +++ b/sites/all/modules/contrib/dev/ctools/help/plugins-creating.html
      @@ -26,7 +26,7 @@ The following information can be specified for each plugin type:
       
      If 'cache' is TRUE, then this value specifies the cache table where the cached plugin information will be stored.
      classes
      Defaults to: array()
      -
      An array of class identifiers(i.e. plugin array keys) which a plugin of this type uses to provide classes to the CTools autoloader. For example, if classes is set to array('class'), then CTools will search each $plugin['class'] for a class to autoload. Depending of the plugin structure, a class identifier may be either:
      +
      An array of class identifiers(i.e. plugin array keys) which a plugin of this type uses to provide classes to the CTools autoloader. For example, if classes is set to array('class'), then CTools will search each $plugin['class'] for a class to autoload. Depending of the plugin structure, a class identifier may be either:
      - a file name:
      the file which holds the class with the name structure as: [filename].[class].php
      @@ -35,6 +35,7 @@ The following information can be specified for each plugin type:
      if the class is in the same file as the $plugin
      the plugin .inc file can have a different name than the class identifier
      +
      defaults
      Defaults to: array()
      An array of defaults that should be added to each plugin; this can be used to ensure that every plugin has the basic data necessary. These defaults will not ovewrite data supplied by the plugin. This could also be a function name, in which case the callback will be used to provide defaults. NOTE, however, that the callback-based approach is deprecated as it is redundant with the 'process' callback, and as such will be removed in later versions. Consequently, you should only use the array form for maximum cross-version compatibility.
      diff --git a/sites/all/modules/contrib/dev/ctools/includes/action-links.theme.inc b/sites/all/modules/contrib/dev/ctools/includes/action-links.theme.inc index 3a2398a..f53f59c 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/action-links.theme.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/action-links.theme.inc @@ -1,4 +1,5 @@ ' . $links . '
    '; -} \ No newline at end of file +} diff --git a/sites/all/modules/contrib/dev/ctools/includes/ajax.inc b/sites/all/modules/contrib/dev/ctools/includes/ajax.inc index 96f5068..4d72d0c 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/ajax.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/ajax.inc @@ -1,6 +1,10 @@ 'attr', - 'selector' => $selector, - 'name' => $name, - 'value' => $value, - ); - } + 'command' => 'attr', + 'selector' => $selector, + 'name' => $name, + 'value' => $value, + ); +} /** * Force a client-side redirect. @@ -154,4 +158,3 @@ function ctools_ajax_render_error($error = '') { print ajax_render($commands); exit; } - diff --git a/sites/all/modules/contrib/dev/ctools/includes/cache.inc b/sites/all/modules/contrib/dev/ctools/includes/cache.inc index 3918683..cace800 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/cache.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/cache.inc @@ -2,7 +2,6 @@ /** * @file - * * Plugins to handle cache-indirection. * * Simple plugin management to allow clients to more tightly control where @@ -52,7 +51,6 @@ * @param string $mechanism * A string containing the plugin name, and an optional data element to * send to the plugin separated by two colons. - * * @param string $key * The key used to identify the cache. * @@ -70,7 +68,6 @@ function ctools_cache_get($mechanism, $key) { * @param string $mechanism * A string containing the plugin name, and an optional data element to * send to the plugin separated by two colons. - * * @param string $key * The key used to identify the cache. * @@ -88,7 +85,6 @@ function ctools_cache_set($mechanism, $key, $object) { * @param string $mechanism * A string containing the plugin name, and an optional data element to * send to the plugin separated by two colons. - * * @param string $key * The key used to identify the cache. */ @@ -108,13 +104,10 @@ function ctools_cache_clear($mechanism, $key) { * @param string $mechanism * A string containing the plugin name, and an optional data element to * send to the plugin separated by two colons. - * * @param string $key * The key used to identify the cache. - * * @param string $op * The operation to call, such as 'break' or 'finalize'. - * * @param mixed $object * The cache data being operated on, in case it is necessary. This is * optional so no references should be used. @@ -150,7 +143,7 @@ function ctools_cache_operation($mechanism, $key, $op, $object = NULL) { */ function ctools_cache_find_plugin($mechanism) { if (strpos($mechanism, '::') !== FALSE) { - // use explode(2) to ensure that the data can contain double + // Use explode(2) to ensure that the data can contain double // colons, just in case. list($name, $data) = explode('::', $mechanism, 2); } diff --git a/sites/all/modules/contrib/dev/ctools/includes/cleanstring.inc b/sites/all/modules/contrib/dev/ctools/includes/cleanstring.inc index 56b3e36..11ffcef 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/cleanstring.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/cleanstring.inc @@ -1,5 +1,4 @@ $length) { - $string = drupal_substr($string, 0, $length + 1); // leave one more character - if ($last_break = strrpos($string, $separator)) { // space exists AND is not on position 0 + // Leave one more character. + $string = drupal_substr($string, 0, $length + 1); + // Space exists AND is not on position 0. + if ($last_break = strrpos($string, $separator)) { $string = substr($string, 0, $last_break); } else { diff --git a/sites/all/modules/contrib/dev/ctools/includes/collapsible.theme.inc b/sites/all/modules/contrib/dev/ctools/includes/collapsible.theme.inc index f7bbbb3..81df4bc 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/collapsible.theme.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/collapsible.theme.inc @@ -30,8 +30,8 @@ function ctools_collapsible_theme(&$items) { * Text to put in the handle/title area of the div. * @param $content * Text to put in the content area of the div, this is what will get - * collapsed - * @param $collapsed = FALSE + * collapsed. + * @param $collapsed * If true, this div will start out collapsed. */ function theme_ctools_collapsible($vars) { @@ -56,8 +56,8 @@ function theme_ctools_collapsible($vars) { * Text to put in the handle/title area of the div. * @param $content * Text to put in the content area of the div, this is what will get - * collapsed - * @param $collapsed = FALSE + * collapsed. + * @param $collapsed * If true, this div will start out collapsed. */ function theme_ctools_collapsible_remembered($vars) { @@ -76,4 +76,3 @@ function theme_ctools_collapsible_remembered($vars) { return $output; } - diff --git a/sites/all/modules/contrib/dev/ctools/includes/content.inc b/sites/all/modules/contrib/dev/ctools/includes/content.inc index b4660b4..49c3565 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/content.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/content.inc @@ -86,7 +86,7 @@ function ctools_content_process(&$plugin, $info) { /** * Fetch metadata on a specific content_type plugin. * - * @param $content type + * @param mixed $content * Name of a panel content type. * * @return @@ -146,7 +146,7 @@ function ctools_content_get_subtypes($type) { if (is_array($function)) { $subtypes = $function; } - else if (function_exists($function)) { + elseif (function_exists($function)) { // Cast to array to prevent errors from non-array returns. $subtypes = (array) $function($plugin); } @@ -155,6 +155,11 @@ function ctools_content_get_subtypes($type) { // Walk through the subtypes and ensure minimal settings are // retained. foreach ($subtypes as $id => $subtype) { + // Ensure that the 'subtype_id' value exists. + if (!isset($subtype['subtype_id'])) { + $subtypes[$id]['subtype_id'] = $id; + } + // Use exact name since this is a modify by reference. ctools_content_prepare_subtype($subtypes[$id], $plugin); } @@ -202,6 +207,13 @@ function ctools_content_get_subtype($type, $subtype_id) { } if ($subtype) { + // Ensure that the 'subtype_id' value exists. This is also done in + // ctools_content_get_subtypes(), but it wouldn't be called if the plugin + // provides the subtype through its own function. + if (!isset($subtype['subtype_id'])) { + $subtype['subtype_id'] = $subtype_id; + } + ctools_content_prepare_subtype($subtype, $plugin); } return $subtype; @@ -217,6 +229,7 @@ function ctools_content_prepare_subtype(&$subtype, $plugin) { } } + // Trigger hook_ctools_content_subtype_alter(). drupal_alter('ctools_content_subtype', $subtype, $plugin); } @@ -241,8 +254,8 @@ function ctools_content_prepare_subtype(&$subtype, $plugin) { * Any incoming content, if this display is a wrapper. * * @return - * The content as rendered by the plugin. This content should be an array - * with the following possible keys: + * The content as rendered by the plugin, or NULL. + * This content should be an object with the following possible properties: * - title: The safe to render title of the content. * - title_heading: The title heading. * - content: The safe to render HTML content. @@ -294,7 +307,7 @@ function ctools_content_render($type, $subtype, $conf, $keywords = array(), $arg $content->subtype = $subtype; } - // Override the title if configured to + // Override the title if configured to. if (!empty($conf['override_title'])) { // Give previous title as an available substitution here. $keywords['%title'] = empty($content->title) ? '' : $content->title; @@ -304,12 +317,12 @@ function ctools_content_render($type, $subtype, $conf, $keywords = array(), $arg } if (!empty($content->title)) { - // Perform substitutions + // Perform substitutions. if (!empty($keywords) || !empty($context)) { $content->title = ctools_context_keyword_substitute($content->title, $keywords, $context); } - // Sterilize the title + // Sterilize the title. $content->title = filter_xss_admin($content->title); // If a link is specified, populate. @@ -320,7 +333,7 @@ function ctools_content_render($type, $subtype, $conf, $keywords = array(), $arg else { $url = $content->title_link; } - // set defaults so we don't bring up notices + // Set defaults so we don't bring up notices. $url += array('href' => '', 'attributes' => array(), 'query' => array(), 'fragment' => '', 'absolute' => NULL, 'html' => TRUE); $content->title = l($content->title, $url['href'], $url); } @@ -341,7 +354,16 @@ function ctools_content_editable($type, $subtype, $conf) { return FALSE; } - if ($function = ctools_plugin_get_function($subtype, 'check editable')) { + $function = FALSE; + + if (!empty($subtype['check editable'])) { + $function = ctools_plugin_get_function($subtype, 'check editable'); + } + elseif (!empty($type['check editable'])) { + $function = ctools_plugin_get_function($type, 'check editable'); + } + + if ($function) { return $function($type, $subtype, $conf); } @@ -364,7 +386,7 @@ function ctools_content_admin_title($type, $subtype, $conf, $context = NULL) { if (is_array($type)) { $plugin = $type; } - else if (is_string($type)) { + elseif (is_string($type)) { $plugin = ctools_get_content_type($type); } else { @@ -382,10 +404,10 @@ function ctools_content_admin_title($type, $subtype, $conf, $context = NULL) { return $function($subtype, $conf, $pane_context); } - else if (isset($plugin['admin title'])) { + elseif (isset($plugin['admin title'])) { return $plugin['admin title']; } - else if (isset($plugin['title'])) { + elseif (isset($plugin['title'])) { return $plugin['title']; } } @@ -420,7 +442,7 @@ function ctools_content_get_defaults($plugin, $subtype) { if (isset($plugin['defaults'])) { $defaults = $plugin['defaults']; } - else if (isset($subtype['defaults'])) { + elseif (isset($subtype['defaults'])) { $defaults = $subtype['defaults']; } if (isset($defaults)) { @@ -429,7 +451,7 @@ function ctools_content_get_defaults($plugin, $subtype) { return $return; } } - else if (is_array($defaults)) { + elseif (is_array($defaults)) { return $defaults; } } @@ -463,7 +485,7 @@ function ctools_content_admin_info($type, $subtype, $conf, $context = NULL) { if (empty($output) || !is_object($output)) { $output = new stdClass(); - // replace the _ with " " for a better output + // Replace the _ with " " for a better output. $subtype = check_plain(str_replace("_", " ", $subtype)); $output->title = $subtype; $output->content = t('No info available.'); @@ -472,7 +494,7 @@ function ctools_content_admin_info($type, $subtype, $conf, $context = NULL) { } /** - * Add the default FAPI elements to the content type configuration form + * Add the default FAPI elements to the content type configuration form. */ function ctools_content_configure_form_defaults($form, &$form_state) { $plugin = $form_state['plugin']; @@ -589,7 +611,7 @@ function ctools_content_form($op, $form_info, &$form_state, $plugin, $subtype_na if (!empty($subtype['add form'])) { _ctools_content_create_form_info($form_info, $subtype['add form'], $subtype, $subtype, $op); } - else if (!empty($plugin['add form'])) { + elseif (!empty($plugin['add form'])) { _ctools_content_create_form_info($form_info, $plugin['add form'], $plugin, $subtype, $op); } } @@ -599,7 +621,7 @@ function ctools_content_form($op, $form_info, &$form_state, $plugin, $subtype_na if (!empty($subtype['edit form'])) { _ctools_content_create_form_info($form_info, $subtype['edit form'], $subtype, $subtype, $op); } - else if (!empty($plugin['edit form'])) { + elseif (!empty($plugin['edit form'])) { _ctools_content_create_form_info($form_info, $plugin['edit form'], $plugin, $subtype, $op); } } @@ -618,7 +640,7 @@ function _ctools_content_create_form_info(&$form_info, $info, $plugin, $subtype, if (empty($subtype['title'])) { $title = t('Configure'); } - else if ($op == 'add') { + elseif ($op == 'add') { $title = t('Configure new !subtype_title', array('!subtype_title' => $subtype['title'])); } else { @@ -633,7 +655,7 @@ function _ctools_content_create_form_info(&$form_info, $info, $plugin, $subtype, ), ); } - else if (is_array($info)) { + elseif (is_array($info)) { $form_info['order'] = array(); $form_info['forms'] = array(); $count = 0; @@ -684,7 +706,7 @@ function ctools_content_get_available_types($contexts = NULL, $has_content = FAL foreach ($plugins as $id => $plugin) { foreach (ctools_content_get_subtypes($plugin) as $subtype_id => $subtype) { - // exclude items that require content if we're saying we don't + // Exclude items that require content if we're saying we don't // provide it. if (!empty($subtype['requires content']) && !$has_content) { continue; @@ -724,7 +746,6 @@ function ctools_content_get_available_types($contexts = NULL, $has_content = FAL * Get an array of all content types that can be fed into the * display editor for the add content list, regardless of * availability. - * */ function ctools_content_get_all_types() { $plugins = ctools_get_content_types(); diff --git a/sites/all/modules/contrib/dev/ctools/includes/content.menu.inc b/sites/all/modules/contrib/dev/ctools/includes/content.menu.inc index ba32cb5..64d44b0 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/content.menu.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/content.menu.inc @@ -23,12 +23,13 @@ function ctools_content_menu(&$items) { /** * Helper function for autocompletion of entity titles. */ -function ctools_content_autocomplete_entity($type, $string = '') { +function ctools_content_autocomplete_entity($entity_type, $string = '') { if ($string != '') { - global $user; - $entity_info = entity_get_info($type); - if ($type == 'node') { - $entity_info['entity keys']['bundle field'] = 'type'; + $entity_info = entity_get_info($entity_type); + + if (!module_exists('entity')) { + module_load_include('inc', 'ctools', 'includes/entity-access'); + _ctools_entity_access($entity_info, $entity_type); } // We must query all ids, because if every one of the 10 don't have access @@ -42,84 +43,77 @@ function ctools_content_autocomplete_entity($type, $string = '') { // If an ID match was found, use that ID rather than the whole string. if ($match) { $entity_id = $preg_matches[1]; - $entity = entity_load($type, array($entity_id)); - - // Format results in an array so later we could add attributes to the - // autocomplete text that is returned. - $results = array($entity_id => array( - 'label' => $entity[$entity_id]->$entity_info['entity keys']['label'], - )); + $results = _ctools_getReferencableEntities($entity_type, $entity_info, $entity_id, '=', 1); } else { - $results = _ctools_getReferencableEntities($type, $entity_info, $string, 'LIKE', 10); + // We cannot find results if the entity doesn't have a label to search. + if (!isset($entity_info['entity keys']['label'])) { + drupal_json_output(array("[id: NULL]" => '' . t('Entity Type !entity_type does not support autocomplete search.', array('!entity_type' => $entity_type)) . '')); + return; + } + $results = _ctools_getReferencableEntities($entity_type, $entity_info, $string, 'LIKE', 10); } - foreach($results as $entity_id => $result) { - if (!$entity_info['entity keys']['label']) { - $matches["[id: $entity_id]"] = '' . $entity_id . ''; - } - else { - $matches[$result['label'] . " [id: $entity_id]"] = '' . check_plain($result['label']) . ''; - $matches[$result['label'] . " [id: $entity_id]"] .= isset($result['bundle field']) ? ' (' . check_plain($result['bundle field']) . ')' : ''; - } + foreach ($results as $entity_id => $result) { + $matches[$result['label'] . " [id: $entity_id]"] = '' . check_plain($result['label']) . ''; + $matches[$result['label'] . " [id: $entity_id]"] .= isset($result['bundle']) ? ' (' . check_plain($result['bundle']) . ')' : ''; } drupal_json_output($matches); } } -/* - * Use well known/tested entity reference code to build our search query - * From EntityReference_SelectionHandler_Generic class +/** + * Use EntityReference_SelectionHandler_Generic class to build our search query. */ function _ctools_buildQuery($entity_type, $entity_info, $match = NULL, $match_operator = 'CONTAINS') { $base_table = $entity_info['base table']; - $query = db_select($base_table) - ->fields($base_table, array($entity_info['entity keys']['id'])); + $label_key = $entity_info['entity keys']['label']; + $query = db_select($base_table) + ->fields($base_table, array($entity_info['entity keys']['id'])); - if (isset($match)) { - if (isset($entity_info['entity keys']['label'])) { - $query->condition($base_table .'.'. $entity_info['entity keys']['label'], '%' . $match . '%' , $match_operator); - } - } - - // Add a label to the query, if the label exists - if (isset($entity_info['entity keys']['label'])) { - $query->fields($base_table, array($entity_info['entity keys']['label'])); - } - - // Add bundle field to the query, if it exists. - if (isset($entity_info['entity keys']['bundle field'])) { - $query->fields($base_table, array($entity_info['entity keys']['bundle field'])); - } - - // Add a generic entity access tag to the query. - $query->addTag('ctools'); - - if($entity_type == 'comment') { - // Adding the 'comment_access' tag is sadly insufficient for comments: core - // requires us to also know about the concept of 'published' and - // 'unpublished'. - if (!user_access('administer comments')) { - $query->condition('comment.status', COMMENT_PUBLISHED); - } - // Join to a node if the user does not have node access bypass permissions - // to obey node published permissions - if (!user_access('bypass node access') && !count(module_implements('node_grants'))) { - $node_alias = $query->innerJoin('node', 'n', '%alias.nid = comment.nid'); - $query->condition($node_alias . '.status', NODE_PUBLISHED); - } - $query->addTag('node_access'); + if (isset($match)) { + if (isset($label_key)) { + $query->condition($base_table . '.' . $label_key, '%' . $match . '%', $match_operator); } + // This should never happen, but double check just in case. else { - $query->addTag($entity_type . '_access'); + return array(); + } + } + // Add a generic entity access tag to the query. + $query->addTag('ctools'); + + // We have to perform two checks. First check is a query alter (with tags) + // in an attempt to only return results that have access. However, this is + // not full-proof since entities many not implement hook_access query tag. + // This is why we have a second check after entity load, before we display + // the label of an entity. + if ($entity_type == 'comment') { + // Adding the 'comment_access' tag is sadly insufficient for comments: core
 + // requires us to also know about the concept of 'published' and
 + // 'unpublished'. + if (!user_access('administer comments')) { + $query->condition('comment.status', COMMENT_PUBLISHED); } - // Add the sort option. - if(isset($entity_info['entity keys']['label'])) { - $query->orderBy($base_table .'.'. $entity_info['entity keys']['label'], 'ASC'); + // Join to a node if the user does not have node access bypass permissions
 + // to obey node published permissions. + if (!user_access('bypass node access')) { + $node_alias = $query->innerJoin('node', 'n', '%alias.nid = comment.nid'); + $query->condition($node_alias . '.status', NODE_PUBLISHED); } + $query->addTag('node_access'); + } + else { + $query->addTag($entity_type . '_access'); + } - return $query; + // Add the sort option. + if (isset($label_key)) { + $query->orderBy($base_table . '.' . $label_key, 'ASC'); + } + + return $query; } /** @@ -127,23 +121,58 @@ function _ctools_buildQuery($entity_type, $entity_info, $match = NULL, $match_op * Entity Reference module. */ function _ctools_getReferencableEntities($entity_type, $entity_info, $match = NULL, $match_operator = 'LIKE', $limit = 0) { + global $user; + $account = $user; $options = array(); - - $query = _ctools_buildQuery($entity_type, $entity_info, $match, $match_operator); - if ($limit > 0) { - $query->range(0, $limit); - } - - $results = $query->execute(); - - if (!empty($results)) { - foreach ($results as $record) { - $options[$record->{$entity_info['entity keys']['id']}] = array( - 'label' => isset($entity_info['entity keys']['label']) ? check_plain($record->{$entity_info['entity keys']['label']}) : $record->{$entity_info['entity keys']['id']}, - 'bundle field' => isset($entity_info['entity keys']['bundle field']) ? check_plain($record->{$entity_info['entity keys']['bundle field']}) : '', - ); + // We're an entity ID, return the id. + if (is_numeric($match) && $match_operator == '=') { + if ($entity = array_shift(entity_load($entity_type, array($match)))) { + if (isset($entity_info['access callback']) && function_exists($entity_info['access callback'])) { + if ($entity_info['access callback']('view', $entity, $account, $entity_type)) { + $label = entity_label($entity_type, $entity); + return array( + $match => array( + 'label' => !empty($label) ? $label : $entity->{$entity_info['entity keys']['id']}, + 'bundle' => !empty($entity_info['entity keys']['bundle']) ? check_plain($entity->{$entity_info['entity keys']['bundle']}) : NULL, + ), + ); + } + } } + // If you don't have access, or an access callback or a valid entity, just + // Return back the Entity ID. + return array( + $match => array( + 'label' => $match, + 'bundle' => NULL, + ), + ); } - return $options; -} \ No newline at end of file + // We have matches, build a query to fetch the result. + if ($query = _ctools_buildQuery($entity_type, $entity_info, $match, $match_operator)) { + if ($limit > 0) { + $query->range(0, $limit); + } + + $results = $query->execute(); + + if (!empty($results)) { + foreach ($results as $record) { + $entities = entity_load($entity_type, array($record->{$entity_info['entity keys']['id']})); + $entity = array_shift($entities); + if (isset($entity_info['access callback']) && function_exists($entity_info['access callback'])) { + if ($entity_info['access callback']('view', $entity, $account, $entity_type)) { + $label = entity_label($entity_type, $entity); + $options[$record->{$entity_info['entity keys']['id']}] = array( + 'label' => !empty($label) ? $label : $entity->{$entity_info['entity keys']['id']}, + 'bundle' => !empty($entity_info['entity keys']['bundle']) ? check_plain($entity->{$entity_info['entity keys']['bundle']}) : NULL, + ); + } + } + } + } + return $options; + } + return array(); +} diff --git a/sites/all/modules/contrib/dev/ctools/includes/content.plugin-type.inc b/sites/all/modules/contrib/dev/ctools/includes/content.plugin-type.inc index a0debc3..0364a92 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/content.plugin-type.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/content.plugin-type.inc @@ -14,4 +14,4 @@ function ctools_content_plugin_type(&$items) { 'path' => drupal_get_path('module', 'ctools') . '/includes', ), ); -} \ No newline at end of file +} diff --git a/sites/all/modules/contrib/dev/ctools/includes/context-access-admin.inc b/sites/all/modules/contrib/dev/ctools/includes/context-access-admin.inc index 76643cf..d8c4f3b 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/context-access-admin.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/context-access-admin.inc @@ -245,10 +245,10 @@ function ctools_access_admin_form_submit($form, &$form_state) { // -------------------------------------------------------------------------- // AJAX menu entry points. - /** * AJAX callback to add a new access test to the list. */ + function ctools_access_ajax_add($fragment = NULL, $name = NULL) { ctools_include('ajax'); ctools_include('modal'); @@ -263,7 +263,7 @@ function ctools_access_ajax_add($fragment = NULL, $name = NULL) { ctools_ajax_render_error(); } - // Separate the fragment into 'module' and 'argument' + // Separate the fragment into 'module' and 'argument'. if (strpos($fragment, '-') === FALSE) { $module = $fragment; $argument = NULL; @@ -279,7 +279,7 @@ function ctools_access_ajax_add($fragment = NULL, $name = NULL) { list($access, $contexts) = $function($argument); - // Make sure we have the logged in user context + // Make sure we have the logged in user context. if (!isset($contexts['logged-in-user'])) { $contexts['logged-in-user'] = ctools_access_get_loggedin_context(); } @@ -306,6 +306,9 @@ function ctools_access_ajax_add($fragment = NULL, $name = NULL) { ); $output = ctools_modal_form_wrapper('ctools_access_ajax_edit_item', $form_state); + $access = $form_state['access']; + $access['plugins'][$id] = $form_state['test']; + if (!isset($output[0])) { $function = $module . '_ctools_access_set'; if (function_exists($function)) { @@ -333,7 +336,7 @@ function ctools_access_ajax_edit($fragment = NULL, $id = NULL) { ctools_ajax_render_error(); } - // Separate the fragment into 'module' and 'argument' + // Separate the fragment into 'module' and 'argument'. if (strpos($fragment, '-') === FALSE) { $module = $fragment; $argument = NULL; @@ -353,7 +356,7 @@ function ctools_access_ajax_edit($fragment = NULL, $id = NULL) { ctools_ajax_render_error(); } - // Make sure we have the logged in user context + // Make sure we have the logged in user context. if (!isset($contexts['logged-in-user'])) { $contexts['logged-in-user'] = ctools_access_get_loggedin_context(); } @@ -367,12 +370,14 @@ function ctools_access_ajax_edit($fragment = NULL, $id = NULL) { 'contexts' => $contexts, 'title' => t('Edit criteria'), 'ajax' => TRUE, - 'ajax' => TRUE, 'modal' => TRUE, 'modal return' => TRUE, ); $output = ctools_modal_form_wrapper('ctools_access_ajax_edit_item', $form_state); + $access = $form_state['access']; + $access['plugins'][$id] = $form_state['test']; + if (!isset($output[0])) { $function = $module . '_ctools_access_set'; if (function_exists($function)) { @@ -452,7 +457,7 @@ function ctools_access_ajax_delete($fragment = NULL, $id = NULL) { ajax_render_error(); } - // Separate the fragment into 'module' and 'argument' + // Separate the fragment into 'module' and 'argument'. if (strpos($fragment, '-') === FALSE) { $module = $fragment; $argument = NULL; @@ -472,7 +477,7 @@ function ctools_access_ajax_delete($fragment = NULL, $id = NULL) { unset($access['plugins'][$id]); } - // re-cache + // re-cache. $function = $module . '_ctools_access_set'; if (function_exists($function)) { $function($argument, $access); diff --git a/sites/all/modules/contrib/dev/ctools/includes/context-admin.inc b/sites/all/modules/contrib/dev/ctools/includes/context-admin.inc index 2d8c8f9..533c89f 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/context-admin.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/context-admin.inc @@ -376,11 +376,11 @@ function ctools_context_ajax_item_add($mechanism = NULL, $type = NULL, $cache_ke 'path' => "ctools/context/ajax/add/$mechanism/$type/$cache_key/$name/%step", 'show cancel' => TRUE, 'default form' => 'ctools_edit_context_form_defaults', - 'auto caching' => TRUE, + 'auto cache' => TRUE, 'cache mechanism' => $mechanism, 'cache key' => $cache_key, // This is stating what the cache will be referred to in $form_state - 'cache storage' => 'object', + 'cache location' => 'object', ); if ($type == 'requiredcontext') { @@ -395,7 +395,7 @@ function ctools_context_ajax_item_add($mechanism = NULL, $type = NULL, $cache_ke if (!empty($form_state['cancel'])) { $output = array(ctools_modal_command_dismiss()); } - else if (!empty($form_state['complete'])) { + elseif (!empty($form_state['complete'])) { // Successful submit -- move temporary data to location. // Create a reference to the place our context lives. Since this is fairly @@ -561,11 +561,11 @@ function ctools_context_ajax_item_edit($mechanism = NULL, $type = NULL, $cache_k 'path' => "ctools/context/ajax/configure/$mechanism/$type/$cache_key/$position/%step", 'show cancel' => TRUE, 'default form' => 'ctools_edit_context_form_defaults', - 'auto caching' => TRUE, + 'auto cache' => TRUE, 'cache mechanism' => $mechanism, 'cache key' => $cache_key, // This is stating what the cache will be referred to in $form_state - 'cache storage' => 'object', + 'cache location' => 'object', ); if ($type == 'requiredcontext') { @@ -580,7 +580,7 @@ function ctools_context_ajax_item_edit($mechanism = NULL, $type = NULL, $cache_k if (!empty($form_state['cancel'])) { $output = array(ctools_modal_command_dismiss()); } - else if (!empty($form_state['complete'])) { + elseif (!empty($form_state['complete'])) { // successful submit $ref[$position] = $conf; if (isset($object->temporary)) { @@ -657,7 +657,7 @@ function ctools_context_get_defaults($plugin_definition, $object, $type) { if (isset($plugin_definition['defaults'])) { $defaults = $plugin_definition['defaults']; } - else if (isset($subtype['defaults'])) { + elseif (isset($subtype['defaults'])) { $defaults = $subtype['defaults']; } @@ -667,7 +667,7 @@ function ctools_context_get_defaults($plugin_definition, $object, $type) { $conf += $settings; } } - else if (is_array($defaults)) { + elseif (is_array($defaults)) { $conf += $defaults; } } @@ -736,6 +736,15 @@ function ctools_edit_context_form_defaults($form, &$form_state) { '#default_value' => $conf['keyword'], ); + if ($type_info['key'] == 'requiredcontexts') { + $form['optional'] = array( + '#type' => 'checkbox', + '#title' => t('Context is optional'), + '#default_value' => !empty($form_state['conf']['optional']), + '#description' => t('This context need not be present for the component to function.'), + ); + } + $form['#submit'][] = 'ctools_edit_context_form_defaults_submit'; return $form; @@ -752,6 +761,9 @@ function ctools_edit_context_form_defaults_submit(&$form, &$form_state) { $form_state['conf']['default'] = $form_state['values']['default']; $form_state['conf']['title'] = $form_state['values']['title']; } + if ($form_state['type info']['key'] == 'requiredcontexts') { + $form_state['conf']['optional'] = $form_state['values']['optional']; + } $form_state['conf']['identifier'] = $form_state['values']['identifier']; $form_state['conf']['keyword'] = $form_state['values']['keyword']; diff --git a/sites/all/modules/contrib/dev/ctools/includes/context-task-handler.inc b/sites/all/modules/contrib/dev/ctools/includes/context-task-handler.inc index 21ceea5..ed4acad 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/context-task-handler.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/context-task-handler.inc @@ -4,9 +4,9 @@ * @file * Support for creating 'context' type task handlers. * - * Context task handlers expect the task to provide 0 or more contexts. The - * task handler should use those contexts as selection rules, as well as - * rendering with them. + * Context task handlers expect the task to provide 0 or more contexts. The task + * handler should use those contexts as selection rules, as well as rendering + * with them. * * The functions and forms in this file should be common to every context type * task handler made. @@ -31,6 +31,7 @@ * If TRUE then this renderer owns the page and can use theme('page') * for no blocks; if false, output is returned regardless of any no * blocks settings. + * * @return * Either the output or NULL if there was output, FALSE if no handler * accepted the task. If $page is FALSE then the $info block is returned instead. @@ -84,7 +85,7 @@ function ctools_context_handler_get_render_handler($task, $subtask, $handlers, $ */ function ctools_context_handler_default_test($handler, $base_contexts, $args) { ctools_include('context'); - // Add my contexts + // Add my contexts. $contexts = ctools_context_handler_get_handler_contexts($base_contexts, $handler); // Test. @@ -128,7 +129,7 @@ function ctools_context_handler_render_handler($task, $subtask, $handler, $conte 'contexts' => $contexts, 'task' => $task, 'subtask' => $subtask, - 'handler' => $handler + 'handler' => $handler, ); drupal_alter('ctools_render', $info, $page, $context); @@ -141,12 +142,15 @@ function ctools_context_handler_render_handler($task, $subtask, $handler, $conte switch ($info['response code']) { case 403: return MENU_ACCESS_DENIED; + case 404: return MENU_NOT_FOUND; + case 410: drupal_add_http_header('Status', '410 Gone'); drupal_exit(); break; + case 301: case 302: case 303: @@ -162,7 +166,7 @@ function ctools_context_handler_render_handler($task, $subtask, $handler, $conte 'fragment' => $info['fragment'], ); drupal_goto($info['destination'], $options, $info['response code']); - // @todo -- should other response codes be supported here? + // @todo -- should other response codes be supported here? } } @@ -222,7 +226,7 @@ function ctools_context_handler_render_handler($task, $subtask, $handler, $conte } /** - * Default function to provide contextual link for a task as defined by the handler. + * Provides contextual link for a task as defined by the handler. * * This provides a simple link to th main content operation and is suitable * for most normal handlers. Setting 'contextual link' to a function overrides @@ -241,7 +245,7 @@ function ctools_task_handler_default_contextual_link($handler, $plugin, $context if (is_array($plugin['tab operation'])) { $trail = $plugin['tab operation']; } - else if (function_exists($plugin['tab operation'])) { + elseif (function_exists($plugin['tab operation'])) { $trail = $plugin['tab operation']($handler, $contexts, $args); } } @@ -251,7 +255,8 @@ function ctools_task_handler_default_contextual_link($handler, $plugin, $context 'href' => $path, 'title' => $title, 'query' => drupal_get_destination(), - )); + ), + ); return $links; } @@ -259,17 +264,22 @@ function ctools_task_handler_default_contextual_link($handler, $plugin, $context /** * Called to execute actions that should happen before a handler is rendered. */ -function ctools_context_handler_pre_render($handler, $contexts, $args) { } +function ctools_context_handler_pre_render($handler, $contexts, $args) { + foreach (module_implements('ctools_context_handler_pre_render') as $module) { + $function = $module . '_ctools_context_handler_pre_render'; + $function($handler, $contexts, $args); + } +} /** * Compare arguments to contexts for selection purposes. * - * @param $handler + * @param object $handler * The handler in question. - * @param $contexts + * @param object $contexts * The context objects provided by the task. * - * @return + * @return bool * TRUE if these contexts match the selection rules. NULL or FALSE * otherwise. */ @@ -288,12 +298,15 @@ function ctools_context_handler_select($handler, $contexts) { * These summary strings are used to communicate to the user what * arguments the task handlers are selecting. * - * @param $task + * @param object $task * The loaded task plugin. - * @param $subtask + * @param object $subtask * The subtask id. - * @param $handler + * @param object $handler * The handler to be checked. + * + * @return array + * Returns array of summary strings for arguments selected by task handlers. */ function ctools_context_handler_summary($task, $subtask, $handler) { if (empty($handler->conf['access']['plugins'])) { @@ -320,7 +333,6 @@ function ctools_context_handler_summary($task, $subtask, $handler) { // the task handler, for example) but sometimes we need them separately // (when a task has contexts loaded and is trying out the task handlers, // for example). Therefore there are two paths we can take to getting contexts. - /** * Load the contexts for a task, using arguments. * @@ -368,7 +380,7 @@ function ctools_context_handler_get_all_contexts($task, $subtask, $handler) { * expects things in a certain, kind of clunky format. */ function ctools_context_handler_get_handler_object($handler) { - $object = new stdClass; + $object = new stdClass(); $object->name = $handler->name; $object->contexts = isset($handler->conf['contexts']) ? $handler->conf['contexts'] : array(); $object->relationships = isset($handler->conf['relationships']) ? $handler->conf['relationships'] : array(); @@ -382,7 +394,7 @@ function ctools_context_handler_get_handler_object($handler) { * arguments from the task. */ function ctools_context_handler_get_task_object($task, $subtask, $handler) { - $object = new stdClass; + $object = new stdClass(); $object->name = !empty($handler->name) ? $handler->name : 'temp'; $object->base_contexts = ctools_context_handler_get_base_contexts($task, $subtask, TRUE); $object->arguments = ctools_context_handler_get_task_arguments($task, $subtask); @@ -456,7 +468,7 @@ function ctools_context_handler_edit_criteria($form, &$form_state) { ctools_modal_add_plugin_js(ctools_get_access_plugins()); ctools_include('context-access-admin'); $form_state['module'] = (isset($form_state['module'])) ? $form_state['module'] : 'page_manager_task_handler'; - // Encode a bunch of info into the argument so we can get our cache later + // Encode a bunch of info into the argument so we can get our cache later. $form_state['callback argument'] = $form_state['task_name'] . '*' . $form_state['handler']->name; $form_state['access'] = $form_state['handler']->conf['access']; $form_state['no buttons'] = TRUE; @@ -472,7 +484,7 @@ function ctools_context_handler_edit_criteria($form, &$form_state) { } /** - * Submit handler for rules selection + * Submit handler for rules selection. */ function ctools_context_handler_edit_criteria_submit(&$form, &$form_state) { $form_state['handler']->conf['access']['logic'] = $form_state['values']['logic']; @@ -537,4 +549,3 @@ function ctools_context_handler_edit_context_submit(&$form, &$form_state) { unset($form_state['page']->context_cache[$cache_name]); } } - diff --git a/sites/all/modules/contrib/dev/ctools/includes/context.inc b/sites/all/modules/contrib/dev/ctools/includes/context.inc index 6628b5f..0fc6208 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/context.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/context.inc @@ -2,7 +2,6 @@ /** * @file - * * Contains code related to the ctools system of 'context'. * * Context, originally from Panels, is a method of packaging objects into @@ -28,28 +27,104 @@ * of the context is important. */ class ctools_context { - var $type = NULL; - var $data = NULL; - // The title of this object. - var $title = ''; - // The title of the page if this object exists - var $page_title = ''; - // The identifier (in the UI) of this object - var $identifier = ''; - var $argument = NULL; - var $keyword = ''; - var $original_argument = NULL; - var $restrictions = array(); - var $empty = FALSE; + /** + * @var string|array + * A string naming this specific context type. The values 'any' and 'none' + * are special: + * - 'any': used in is_type() to match any other type. + * - 'none': used to signal the type is not defined. + */ + public $type; - function ctools_context($type = 'none', $data = NULL) { - $this->type = $type; - $this->data = $data; + /** + * @var mixed + * The data payload for this context object. + */ + public $data; + + /** + * @var string + * The title of this object. + */ + public $title; + + /** + * @var string + * The title of the page if this object exists + */ + public $page_title; + + /** + * @var string + * The identifier (in the UI) of this object. + */ + public $identifier; + + /** + * @var + */ + public $argument; + + /** + * @var string + */ + public $keyword; + + /** + * @var + */ + public $original_argument; + + /** + * @var array + */ + public $restrictions; + + /** + * @var bool + */ + public $empty; + + /** + * The ctools_context constructor. + * + * @param string $type + * The type name of this context. Should be unique. Use the machine_name + * conventions: lowercase, short, underscores and no spaces. + * @param mixed $data + * The data payload, if required for this context. + */ + public function __construct($type = 'none', $data = NULL) { + $this->type = $type; + $this->data = $data; $this->title = t('Unknown context'); + $this->page_title = ''; + $this->identifier = ''; + $this->keyword = ''; + $this->restrictions = array(); + $this->empty = FALSE; + // Other vars are NULL. } - function is_type($type) { - if ($type == 'any' || $this->type == 'any') { + /** + * Determine whether this object is of type @var $type . + * + * Both the internal value ($this->type) and the supplied value ($type) can + * be a string or an array of strings, and if one or both are arrays the match + * succeeds if at least one common element is found. + * + * Type names + * + * @param string|array $type + * 'type' can be: + * - 'any' to match all types (this is true of the internal value too). + * - an array of type name strings, when more than one type is acceptable. + * + * @return bool + * True if the type matches, False otherwise. + */ + public function is_type($type) { + if ($type === 'any' || $this->type === 'any') { return TRUE; } @@ -58,32 +133,71 @@ class ctools_context { return (bool) array_intersect($a, $b); } - function get_argument() { + /** + * Return the argument. + * + * @return mixed + * The value of $argument. + */ + public function get_argument() { return $this->argument; } - function get_original_argument() { + /** + * Return the value of argument (or arg) variable as it was passed in. + * + * For example see ctools_plugin_load_function() and ctools_terms_context(). + * + * @return mixed + * The original arg value. + */ + public function get_original_argument() { if (!is_null($this->original_argument)) { return $this->original_argument; } return $this->argument; } - function get_keyword() { + /** + * Return the keyword. + * + * @return mixed + * The value of $keyword. + */ + public function get_keyword() { return $this->keyword; } - function get_identifier() { + /** + * Return the identifier. + * + * @return mixed + * The value of $identifier. + */ + public function get_identifier() { return $this->identifier; } - function get_title() { + /** + * Return the title. + * + * @return mixed + * The value of $title. + */ + public function get_title() { return $this->title; } - function get_page_title() { + /** + * Return the page title. + * + * @return mixed + * The value of $page_title. + */ + public function get_page_title() { return $this->page_title; } + } /** @@ -91,39 +205,57 @@ class ctools_context { * match a required context type. */ class ctools_context_required { - var $keywords = ''; + /** + * @var array + * Keyword strings associated with the context. + */ + public $keywords; /** * If set, the title will be used in the selector to identify * the context. This is very useful when multiple contexts * are required to inform the user will be used for what. */ - var $title = NULL; + public $title; /** * Test to see if this context is required. */ - var $required = TRUE; + public $required = TRUE; /** * If TRUE, skip the check in ctools_context_required::select() * for contexts whose names may have changed. */ - var $skip_name_check = FALSE; + public $skip_name_check = FALSE; /** + * The ctools_context_required constructor. * - * @param $title - * The first parameter should be the 'title' of the context for use - * in UYI selectors when multiple contexts qualify. - * @param ... + * Note: Constructor accepts a variable number of arguments, with optional + * type-dependent args at the end of the list and one required argument, + * the title. Note in particular that skip_name_check MUST be passed in as + * a boolean (and not, for example, as an integer). + * + * @param string $title + * The title of the context for use in UI selectors when multiple contexts + * qualify. + * @param string $keywords * One or more keywords to use for matching which contexts are allowed. + * @param array $restrictions + * Array of context restrictions. + * @param bool $skip_name_check + * If True, skip the check in select() for contexts whose names may have + * changed. */ - function ctools_context_required($title) { + public function __construct($title) { + // If it was possible, using variadic syntax this should be: + // __construct($title, string ...$keywords, array $restrictions = NULL, bool $skip = NULL) + // but that form isn't allowed. $args = func_get_args(); $this->title = array_shift($args); - // If we have a boolean value at the end for $skip_name_check, store it + // If we have a boolean value at the end for $skip_name_check, store it. if (is_bool(end($args))) { $this->skip_name_check = array_pop($args); } @@ -133,20 +265,44 @@ class ctools_context_required { $this->restrictions = array_pop($args); } - if (count($args) == 1) { + if (count($args) === 1) { $args = array_shift($args); } $this->keywords = $args; } - function filter($contexts) { + /** + * Filter the contexts to determine which apply in the current environment. + * + * A context passes the filter if: + * - the context matches 'type' of the required keywords (uses + * ctools_context::is_type(), so includes 'any' matches, etc). + * - AND if restrictions are present, there are some common elements between + * the requirement and the context. + * + * @param array $contexts + * An array of ctools_context objects (or something which will cast to an + * array of them). The contexts to apply the filter on. + * + * @return array + * An array of context objects, keyed with the same keys used for $contexts, + * which pass the filter. + * + * @see ctools_context::is_type() + */ + public function filter($contexts) { $result = array(); - // See which of these contexts are valid + /** + * See which of these contexts are valid. + * @var ctools_context $context + */ foreach ((array) $contexts as $cid => $context) { if ($context->is_type($this->keywords)) { + // Compare to see if our contexts were met. if (!empty($this->restrictions) && !empty($context->restrictions)) { + foreach ($this->restrictions as $key => $values) { // If we have a restriction, the context must either not have that // restriction listed, which means we simply don't know what it is, @@ -155,11 +311,16 @@ class ctools_context_required { if (!is_array($values)) { $values = array($values); } - if (!empty($context->restrictions[$key]) && !array_intersect($values, $context->restrictions[$key])) { + + if (!empty($context->restrictions[$key]) + && !array_intersect($values, $context->restrictions[$key]) + ) { + // Break out to check next context; this one fails the filter. continue 2; } } } + // This context passes the filter. $result[$cid] = $context; } } @@ -167,7 +328,28 @@ class ctools_context_required { return $result; } - function select($contexts, $context) { + /** + * Select one context from the list of contexts, accounting for changed IDs. + * + * Fundamentally, this returns $contexts[$context] or FALSE if that does not + * exist. Additional logic accounts for changes in context names and dealing + * with a $contexts parameter that is not an array. + * + * If we had requested a $context but that $context doesn't exist in our + * context list, there is a good chance that what happened is the context + * IDs changed. Look for another context that satisfies our requirements, + * unless $skip_name_check is set. + * + * @param ctools_context|array $contexts + * A context, or an array of ctools_context. + * @param string $context + * A context ID. + * + * @return bool|ctools_context + * The matching ctools_context, or False if no such context was found. + */ + public function select($contexts, $context) { + // Easier to deal with a standalone object as a 1-element array of objects. if (!is_array($contexts)) { if (is_object($contexts) && $contexts instanceof ctools_context) { $contexts = array($contexts->id => $contexts); @@ -177,11 +359,12 @@ class ctools_context_required { } } - // If we had requested a $context but that $context doesn't exist - // in our context list, there is a good chance that what happened - // is our context IDs changed. See if there's another context - // that satisfies our requirements. - if (!$this->skip_name_check && !empty($context) && !isset($contexts[$context])) { + // If we had requested a $context but that $context doesn't exist in our + // context list, there is a good chance that what happened is the context + // IDs changed. Check for another context that satisfies our requirements. + if (!$this->skip_name_check + && !empty($context) && !isset($contexts[$context]) + ) { $choices = $this->filter($contexts); // If we got a hit, take the first one that matches. @@ -196,6 +379,7 @@ class ctools_context_required { } return $contexts[$context]; } + } /** @@ -203,28 +387,73 @@ class ctools_context_required { * can produce empty contexts to use as placeholders. */ class ctools_context_optional extends ctools_context_required { - var $required = FALSE; - function ctools_context_optional() { - $args = func_get_args(); - call_user_func_array(array($this, 'ctools_context_required'), $args); + + /** + * {@inheritdoc} + */ + public $required = FALSE; + + /** + * Add the 'empty' context to the existing set. + * + * @param array &$contexts + * An array of ctools_context objects. + */ + public function add_empty(&$contexts) { + $context = new ctools_context('any'); + $context->title = t('No context'); + $context->identifier = t('No context'); + $contexts['empty'] = $context; } /** - * Add the 'empty' context which is possible for optional + * Filter the contexts to determine which apply in the current environment. + * + * As for ctools_context_required, but we add the empty context to those + * passed in so the check is optional (i.e. if nothing else matches, the + * empty context will, and so there will always be at least one matched). + * + * @param array $contexts + * An array of ctools_context objects (or something which will cast to an + * array of them). The contexts to apply the filter on. + * + * @return array + * An array of context objects, keyed with the same keys used for $contexts, + * which pass the filter. + * + * @see ctools_context::is_type() */ - function add_empty(&$contexts) { - $context = new ctools_context('any'); - $context->title = t('No context'); - $context->identifier = t('No context'); - $contexts = array_merge(array('empty' => $context), $contexts); - } - - function filter($contexts) { + public function filter($contexts) { + /** + * @todo We are assuming here that $contexts is actually an array, whereas + * ctools_context_required::filter only requires $contexts is convertible + * to an array. + */ $this->add_empty($contexts); return parent::filter($contexts); } - function select($contexts, $context) { + /** + * Select and return one context from the list of applicable contexts. + * + * Fundamentally, this returns $contexts[$context] or the empty context if + * that does not exist. + * + * @param array $contexts + * The applicable contexts to check. + * @param string $context + * The context id to check for. + * + * @return bool|ctools_context + * The matching ctools_context, or False if no such context was found. + * + * @see ctools_context_required::select() + */ + public function select($contexts, $context) { + /** + * @todo We are assuming here that $contexts is actually an array, whereas + * ctools_context_required::select permits ctools_context objects as well. + */ $this->add_empty($contexts); if (empty($context)) { return $contexts['empty']; @@ -234,11 +463,12 @@ class ctools_context_optional extends ctools_context_required { // Don't flip out if it can't find the context; this is optional, put // in an empty. - if ($result == FALSE) { + if ($result === FALSE) { $result = $contexts['empty']; } return $result; } + } /** @@ -253,20 +483,20 @@ class ctools_context_optional extends ctools_context_required { * Since multiple contexts can be required, this function will accept either * an array of all required contexts, or just a single required context object. * - * @param $contexts + * @param array $contexts * A keyed array of all available contexts. - * @param $required - * A ctools_context_required or ctools_context_optional object, or an array - * of such objects. + * @param array|ctools_context_required|ctools_context_optional $required + * A *_required or *_optional object, or an array of such objects, which + * define the selection condition. * - * @return + * @return array * A keyed array of contexts that match the filter. */ function ctools_context_filter($contexts, $required) { if (is_array($required)) { $result = array(); - foreach ($required as $r) { - $result = array_merge($result, _ctools_context_filter($contexts, $r)); + foreach ($required as $item) { + $result = array_merge($result, _ctools_context_filter($contexts, $item)); } return $result; } @@ -274,6 +504,21 @@ function ctools_context_filter($contexts, $required) { return _ctools_context_filter($contexts, $required); } +/** + * Helper function for ctools_context_filter(). + * + * Used to transform the required context during the merge into the final array. + * + * @internal This function DOES NOT form part of the CTools API. + * + * @param array $contexts + * A keyed array of all available contexts. + * @param ctools_context_required|ctools_context_optional $required + * A ctools_context_required or ctools_context_optional object, although if + * given something else will return an empty array. + * + * @return array + */ function _ctools_context_filter($contexts, $required) { $result = array(); @@ -293,12 +538,17 @@ function _ctools_context_filter($contexts, $required) { * If an array of required contexts is provided, one selector will be * provided for each context. * - * @param $contexts + * @param array $contexts * A keyed array of all available contexts. - * @param $required + * @param array|ctools_context_required|ctools_context_optional $required * The required context object or array of objects. + * @param array|string $default + * The default value for the select object, suitable for a #default_value + * render key. Where $required is an array, this is an array keyed by the + * same key values as $required for all keys where an empty string is not a + * suitable default. Otherwise it is just the default value. * - * @return + * @return array * A form element, or NULL if there are no contexts that satisfy the * requirements. */ @@ -306,8 +556,10 @@ function ctools_context_selector($contexts, $required, $default) { if (is_array($required)) { $result = array('#tree' => TRUE); $count = 1; - foreach ($required as $id => $r) { - $result[] = _ctools_context_selector($contexts, $r, isset($default[$id]) ? $default[$id] : '', $count++); + foreach ($required as $id => $item) { + $result[] = _ctools_context_selector( + $contexts, $item, isset($default[$id]) ? $default[$id] : '', $count++ + ); } return $result; } @@ -315,6 +567,27 @@ function ctools_context_selector($contexts, $required, $default) { return _ctools_context_selector($contexts, $required, $default); } +/** + * Helper function for ctools_context_selector(). + * + * @internal This function DOES NOT form part of the CTools API. Use the API + * function ctools_context_selector() instead. + * + * @param array $contexts + * A keyed array of all available contexts. + * @param ctools_context_required|ctools_context_optional $required + * The required context object. + * @param $default + * The default value for the select object, suitable for a #default_value + * render key. + * @param int $num + * If supplied and non-zero, the title of the select form element will be + * "Context $num", otherwise it will be "Context". + * + * @return array + * A form element, or NULL if there are no contexts that satisfy the + * requirements. + */ function _ctools_context_selector($contexts, $required, $default, $num = 0) { $filtered = ctools_context_filter($contexts, $required); $count = count($filtered); @@ -358,8 +631,8 @@ function _ctools_context_selector($contexts, $required, $default, $num = 0) { * @param $required * The required context object or array of objects. * - * @return - * TRUE if there are enough contexts, FALSE if there are not. + * @return bool + * True if there are enough contexts, otherwise False. */ function ctools_context_match_requirements($contexts, $required) { if (!is_array($required)) { @@ -392,8 +665,13 @@ function ctools_context_match_requirements($contexts, $required) { * A keyed array of all available contexts. * @param $required * The required context object or array of objects. + * @param array|string $default + * The default value for the select object, suitable for a #default_value + * render key. Where $required is an array, this is an array keyed by the + * same key values as $required for all keys where an empty string is not a + * suitable default. Otherwise it is just the default value. * - * @return + * @return array * A form element, or NULL if there are no contexts that satisfy the * requirements. */ @@ -401,8 +679,11 @@ function ctools_context_converter_selector($contexts, $required, $default) { if (is_array($required)) { $result = array('#tree' => TRUE); $count = 1; - foreach ($required as $id => $r) { - $result[] = _ctools_context_converter_selector($contexts, $r, isset($default[$id]) ? $default[$id] : '', $count++); + foreach ($required as $id => $dependency) { + $default_id = isset($default[$id]) ? $default[$id] : ''; + $result[] = _ctools_context_converter_selector( + $contexts, $dependency, $default_id, $count++ + ); } return $result; } @@ -410,17 +691,36 @@ function ctools_context_converter_selector($contexts, $required, $default) { return _ctools_context_converter_selector($contexts, $required, $default); } +/** + * Helper function for ctools_context_converter_selector(). + * + * @internal This function DOES NOT form part of the CTools API. Use the API + * function ctools_context_converter_selector() instead. + * + * @param array $contexts + * A keyed array of all available contexts. + * @param ctools_context $required + * The required context object. + * @param $default + * The default value for the select object, suitable for a #default_value + * render key. + * @param int $num + * If supplied and non-zero, the title of the select form element will be + * "Context $num", otherwise it will be "Context". + * + * @return array|null + * A form element, or NULL if there are no contexts that satisfy the + * requirements. + */ function _ctools_context_converter_selector($contexts, $required, $default, $num = 0) { $filtered = ctools_context_filter($contexts, $required); $count = count($filtered); - $form = array(); - if ($count > 1) { // If there's more than one to choose from, create a select widget. $options = array(); foreach ($filtered as $cid => $context) { - if ($context->type == 'any') { + if ($context->type === 'any') { $options[''] = t('No context'); continue; } @@ -450,10 +750,22 @@ function _ctools_context_converter_selector($contexts, $required, $default, $num '#default_value' => $default, ); } + else { + // Not enough choices to need a selector, so don't make one. + return NULL; + } } /** * Get a list of converters available for a given context. + * + * @param string $cid + * A context ID. + * @param ctools_context $context + * The context for which converters are needed. + * + * @return array + * A list of context converters. */ function ctools_context_get_converters($cid, $context) { if (empty($context->plugin)) { @@ -465,6 +777,17 @@ function ctools_context_get_converters($cid, $context) { /** * Get a list of converters available for a given context. + * + * @internal This function DOES NOT form part of the CTools API. Use the API + * function ctools_context_get_converters() instead. + * + * @param string $id + * A context ID. + * @param string $plugin_name + * The name of the context plugin. + * + * @return array + * A list of context converters. */ function _ctools_context_get_converters($id, $plugin_name) { $plugin = ctools_get_context($plugin_name); @@ -476,7 +799,7 @@ function _ctools_context_get_converters($id, $plugin_name) { if (is_array($plugin['convert list'])) { $converters = $plugin['convert list']; } - else if ($function = ctools_plugin_get_function($plugin, 'convert list')) { + elseif ($function = ctools_plugin_get_function($plugin, 'convert list')) { $converters = (array) $function($plugin); } @@ -496,7 +819,13 @@ function _ctools_context_get_converters($id, $plugin_name) { } /** - * Get a list of all contexts + converters available. + * Get a list of all contexts converters available. + * + * For all contexts returned by ctools_get_contexts(), return the converter + * for all contexts that have one. + * + * @return array + * A list of context converters, keyed by the title of the converter. */ function ctools_context_get_all_converters() { $contexts = ctools_get_contexts(); @@ -516,21 +845,24 @@ function ctools_context_get_all_converters() { /** * Let the context convert an argument based upon the converter that was given. * - * @param $context - * The context object - * @param $converter - * The converter to use, which should be a string provided by the converter list. - * @param $converter_options - * A n array of options to pass on to the generation function. For contexts + * @param ctools_context $context + * The context object. + * @param string $converter + * The type of converter to use, which should be a string provided by the + * converter list function. + * @param array $converter_options + * An array of options to pass on to the generation function. For contexts * that use token module, of particular use is 'sanitize' => FALSE which can * get raw tokens. This should ONLY be used in values that will later be * treated as unsafe user input since these values are by themselves unsafe. * It is particularly useful to get raw values from Field API. + * + * @return string|null */ function ctools_context_convert_context($context, $converter, $converter_options = array()) { // Contexts without plugins might be optional placeholders. if (empty($context->plugin)) { - return; + return NULL; } $value = $context->argument; @@ -551,22 +883,35 @@ function ctools_context_convert_context($context, $converter, $converter_options * Choose a context or contexts based upon the selection made via * ctools_context_filter. * - * @param $contexts - * A keyed array of all available contexts - * @param $required - * The required context object provided by the plugin + * @param array $contexts + * A keyed array of all available contexts. + * @param array|ctools_context_required $required + * The required context object(s) provided by the plugin. * @param $context - * The selection made using ctools_context_selector + * The selection made using ctools_context_selector(). + * + * @return ctools_context|array|false + * Returns FALSE if $required is not an object, or array of objects, or + * the value of $required->select() for the context, or an array of those (if + * passed an array in $required). */ function ctools_context_select($contexts, $required, $context) { if (is_array($required)) { + + /** + * @var array $required + * Array of required context objects. + * @var ctools_context_required $item + * A required context object. + */ $result = array(); - foreach ($required as $id => $r) { + foreach ($required as $id => $item) { + // @todo What's the difference between the following and "empty($item)" ? if (empty($required[$id])) { continue; } - if (($result[] = _ctools_context_select($contexts, $r, $context[$id])) === FALSE) { + if (($result[] = _ctools_context_select($contexts, $item, $context[$id])) === FALSE) { return FALSE; } } @@ -576,6 +921,22 @@ function ctools_context_select($contexts, $required, $context) { return _ctools_context_select($contexts, $required, $context); } +/** + * Helper function for calling the required context object's selection function. + * + * This function DOES NOT form part of the CTools API. + * + * @param array $contexts + * A keyed array of all available contexts. + * @param ctools_context_required $required + * The required context object provided by the plugin. + * @param $context + * The selection made using ctools_context_selector(). + * + * @return ctools_context|bool + * FALSE if the $required is not an object. A ctools_context object if one + * matched. + */ function _ctools_context_select($contexts, $required, $context) { if (!is_object($required)) { return FALSE; @@ -587,16 +948,14 @@ function _ctools_context_select($contexts, $required, $context) { /** * Create a new context object. * - * @param $type + * @param string $type * The type of context to create; this loads a plugin. - * @param $data + * @param mixed $data * The data to put into the context. - * @param $empty - * Whether or not this context is specifically empty. * @param $conf * A configuration structure if this context was created via UI. * - * @return + * @return ctools_context * A $context or NULL if one could not be created. */ function ctools_context_create($type, $data = NULL, $conf = FALSE) { @@ -619,7 +978,7 @@ function ctools_context_create($type, $data = NULL, $conf = FALSE) { * @param $type * The type of context to create; this loads a plugin. * - * @return + * @return ctools_context * A $context or NULL if one could not be created. */ function ctools_context_create_empty($type) { @@ -636,8 +995,21 @@ function ctools_context_create_empty($type) { /** * Perform keyword and context substitutions. + * + * @param string $string + * The string in which to replace keywords. + * @param array $keywords + * Array of keyword-replacement pairs. + * @param array $contexts + * + * @param array $converter_options + * Options to pass on to ctools_context_convert_context(), defaults to an + * empty array. + * + * @return string + * The returned string, with substitutions performed. */ -function ctools_context_keyword_substitute($string, $keywords, $contexts, $converter_options = array()) { +function ctools_context_keyword_substitute($string, $keywords, $contexts, array $converter_options = array()) { // Ensure a default keyword exists: $keywords['%%'] = '%'; @@ -660,7 +1032,7 @@ function ctools_context_keyword_substitute($string, $keywords, $contexts, $conve // If the keyword is already set by something passed in, don't try to // overwrite it. - if (!empty($keywords['%' . $keyword])) { + if (array_key_exists('%' . $keyword, $keywords)) { continue; } @@ -680,14 +1052,21 @@ function ctools_context_keyword_substitute($string, $keywords, $contexts, $conve } } - if (empty($context_keywords[$context]) || !empty($context_keywords[$context]->empty)) { - $keywords['%' . $keyword] = ''; - } - else if (!empty($converter)) { - $keywords['%' . $keyword] = ctools_context_convert_context($context_keywords[$context], $converter, $converter_options); + if (!isset($context_keywords[$context])) { + $keywords['%' . $keyword] = '%' . $keyword; } else { - $keywords['%' . $keyword] = $context_keywords[$keyword]->title; + if (empty($context_keywords[$context]) || !empty($context_keywords[$context]->empty)) { + $keywords['%' . $keyword] = ''; + } + else { + if (!empty($converter)) { + $keywords['%' . $keyword] = ctools_context_convert_context($context_keywords[$context], $converter, $converter_options); + } + else { + $keywords['%' . $keyword] = $context_keywords[$keyword]->title; + } + } } } } @@ -695,18 +1074,22 @@ function ctools_context_keyword_substitute($string, $keywords, $contexts, $conve } /** - * Determine a unique context ID for a context + * Determine a unique context ID for a context. * * Often contexts of many different types will be placed into a list. This * ensures that even though contexts of multiple types may share IDs, they * are unique in the final list. */ function ctools_context_id($context, $type = 'context') { - if (!$context['id']) { + // If not set, FALSE or empty. + if (!isset($context['id']) || !$context['id']) { $context['id'] = 1; } - return $type . '_' . $context['name'] . '_' . $context['id']; + // @todo is '' the appropriate default value? + $name = isset($context['name']) ? $context['name'] : ''; + + return $type . '_' . $name . '_' . $context['id']; } /** @@ -714,42 +1097,46 @@ function ctools_context_id($context, $type = 'context') { * * This finds the next id available for the named object. * - * @param $objects - * A list of context descriptor objects, i.e, arguments, relationships, contexts, etc. - * @param $name + * @param array $objects + * A list of context descriptor objects, i.e, arguments, relationships, + * contexts, etc. + * @param string $name * The name being used. + * + * @return int + * The next integer id available. */ function ctools_context_next_id($objects, $name) { $id = 0; - // Figure out which instance of this argument we're creating + // Figure out which instance of this argument we're creating. if (!$objects) { return $id + 1; } foreach ($objects as $object) { - if (isset($object['name']) && $object['name'] == $name) { - if ($object['id'] > $id) { + if (isset($object['name']) && $object['name'] === $name) { + if (isset($object['id']) && $object['id'] > $id) { $id = $object['id']; } + // @todo If obj has no 'id', should we increment local id? $id = $id + 1; } } return $id + 1; } - // --------------------------------------------------------------------------- // Functions related to contexts from arguments. - /** - * Fetch metadata on a specific argument plugin. + * Fetch metadata for a specific argument plugin. * * @param $argument * Name of an argument plugin. * - * @return + * @return array * An array with information about the requested argument plugin. */ + function ctools_get_argument($argument) { ctools_include('plugins'); return ctools_get_plugins('ctools', 'arguments', $argument); @@ -758,7 +1145,7 @@ function ctools_get_argument($argument) { /** * Fetch metadata for all argument plugins. * - * @return + * @return array * An array of arrays with information about all available argument plugins. */ function ctools_get_arguments() { @@ -778,22 +1165,23 @@ function ctools_get_arguments() { * - keyword: The keyword used for this argument for substitutions. * * @param $arg - * The actual argument received. This is expected to be a string from a URL but - * this does not have to be the only source of arguments. + * The actual argument received. This is expected to be a string from a URL + * but this does not have to be the only source of arguments. * @param $empty * If true, the $arg will not be used to load the context. Instead, an empty * placeholder context will be loaded. * - * @return + * @return ctools_context * A context object if one can be loaded. */ function ctools_context_get_context_from_argument($argument, $arg, $empty = FALSE) { ctools_include('plugins'); if (empty($argument['name'])) { - return; + return NULL; } - if ($function = ctools_plugin_load_function('ctools', 'arguments', $argument['name'], 'context')) { + $function = ctools_plugin_load_function('ctools', 'arguments', $argument['name'], 'context'); + if ($function) { // Backward compatibility: Merge old style settings into new style: if (!empty($argument['settings'])) { $argument += $argument['settings']; @@ -805,8 +1193,8 @@ function ctools_context_get_context_from_argument($argument, $arg, $empty = FALS if (is_object($context)) { $context->identifier = $argument['identifier']; $context->page_title = isset($argument['title']) ? $argument['title'] : ''; - $context->keyword = $argument['keyword']; - $context->id = ctools_context_id($argument, 'argument'); + $context->keyword = $argument['keyword']; + $context->id = ctools_context_id($argument, 'argument'); $context->original_argument = $arg; if (!empty($context->empty)) { @@ -822,6 +1210,12 @@ function ctools_context_get_context_from_argument($argument, $arg, $empty = FALS /** * Retrieve a list of empty contexts for all arguments. + * + * @param array $arguments + * + * @return array + * + * @see ctools_context_get_context_from_arguments() */ function ctools_context_get_placeholders_from_argument($arguments) { $contexts = array(); @@ -837,19 +1231,21 @@ function ctools_context_get_placeholders_from_argument($arguments) { /** * Load the contexts for a given list of arguments. * - * @param $arguments + * @param array $arguments * The array of argument definitions. - * @param &$contexts + * @param array &$contexts * The array of existing contexts. New contexts will be added to this array. - * @param $args + * @param array $args * The arguments to load. * - * @return - * FALSE if an argument wants to 404. + * @return bool + * TRUE if all is well, FALSE if an argument wants to 404. + * + * @see ctools_context_get_context_from_argument() */ function ctools_context_get_context_from_arguments($arguments, &$contexts, $args) { foreach ($arguments as $argument) { - // pull the argument off the list. + // Pull the argument off the list. $arg = array_shift($args); $id = ctools_context_id($argument, 'argument'); @@ -864,7 +1260,10 @@ function ctools_context_get_context_from_arguments($arguments, &$contexts, $args $context = $contexts[$id]; } - if ((empty($context) || empty($context->data)) && !empty($argument['default']) && $argument['default'] == '404') { + if ((empty($context) || empty($context->data)) + && !empty($argument['default']) + && $argument['default'] === '404' + ) { return FALSE; } } @@ -873,16 +1272,18 @@ function ctools_context_get_context_from_arguments($arguments, &$contexts, $args // --------------------------------------------------------------------------- // Functions related to contexts from relationships. - /** - * Fetch metadata on a specific relationship plugin. + * Fetch plugin metadata for a specific relationship plugin. * - * @param $content type + * @param $relationship * Name of a panel content type. * - * @return + * @return array * An array with information about the requested relationship. + * + * @see ctools_get_relationships() */ + function ctools_get_relationship($relationship) { ctools_include('plugins'); return ctools_get_plugins('ctools', 'relationships', $relationship); @@ -891,8 +1292,10 @@ function ctools_get_relationship($relationship) { /** * Fetch metadata for all relationship plugins. * - * @return + * @return array * An array of arrays with information about all available relationships. + * + * @see ctools_get_relationship() */ function ctools_get_relationships() { ctools_include('plugins'); @@ -900,8 +1303,9 @@ function ctools_get_relationships() { } /** + * Return a context from a relationship. * - * @param $relationship + * @param array $relationship * The configuration of a relationship. It must contain the following data: * - name: The name of the relationship plugin being used. * - relationship_settings: The configuration based upon the plugin forms. @@ -909,18 +1313,21 @@ function ctools_get_relationships() { * defined by the UI. * - keyword: The keyword used for this relationship for substitutions. * - * @param $source_context + * @param ctools_context $source_context * The context this relationship is based upon. - * - * @param $placeholders + * @param bool $placeholders * If TRUE, placeholders are acceptable. * - * @return - * A context object if one can be loaded. + * @return ctools_context|null + * A context object if one can be loaded, otherwise NULL. + * + * @see ctools_context_get_relevant_relationships() + * @see ctools_context_get_context_from_relationships() */ function ctools_context_get_context_from_relationship($relationship, $source_context, $placeholders = FALSE) { ctools_include('plugins'); - if ($function = ctools_plugin_load_function('ctools', 'relationships', $relationship['name'], 'context')) { + $function = ctools_plugin_load_function('ctools', 'relationships', $relationship['name'], 'context'); + if ($function) { // Backward compatibility: Merge old style settings into new style: if (!empty($relationship['relationship_settings'])) { $relationship += $relationship['relationship_settings']; @@ -931,7 +1338,7 @@ function ctools_context_get_context_from_relationship($relationship, $source_con if ($context) { $context->identifier = $relationship['identifier']; $context->page_title = isset($relationship['title']) ? $relationship['title'] : ''; - $context->keyword = $relationship['keyword']; + $context->keyword = $relationship['keyword']; if (!empty($context->empty)) { $context->placeholder = array( 'type' => 'relationship', @@ -941,6 +1348,7 @@ function ctools_context_get_context_from_relationship($relationship, $source_con return $context; } } + return NULL; } /** @@ -954,18 +1362,24 @@ function ctools_context_get_context_from_relationship($relationship, $source_con * @param $contexts * An array of contexts used to figure out which relationships are relevant. * - * @return + * @return array * An array of relationship keys that are relevant for the given set of * contexts. + * + * @see ctools_context_filter() + * @see ctools_context_get_context_from_relationship() + * @see ctools_context_get_context_from_relationships() */ function ctools_context_get_relevant_relationships($contexts) { $relevant = array(); $relationships = ctools_get_relationships(); - // Go through each relationship + // Go through each relationship. foreach ($relationships as $rid => $relationship) { // For each relationship, see if there is a context that satisfies it. - if (empty($relationship['no ui']) && ctools_context_filter($contexts, $relationship['required context'])) { + if (empty($relationship['no ui']) + && ctools_context_filter($contexts, $relationship['required context']) + ) { $relevant[$rid] = $relationship['title']; } } @@ -974,7 +1388,7 @@ function ctools_context_get_relevant_relationships($contexts) { } /** - * Fetch all active relationships + * Fetch all active relationships. * * @param $relationships * An keyed array of relationship data including: @@ -989,10 +1403,11 @@ function ctools_context_get_relevant_relationships($contexts) { * * @param $placeholders * If TRUE, placeholders are acceptable. + * + * @see ctools_context_get_context_from_relationship() + * @see ctools_context_get_relevant_relationships() */ function ctools_context_get_context_from_relationships($relationships, &$contexts, $placeholders = FALSE) { - $return = array(); - foreach ($relationships as $rdata) { if (!isset($rdata['context'])) { continue; @@ -1023,16 +1438,16 @@ function ctools_context_get_context_from_relationships($relationships, &$context // --------------------------------------------------------------------------- // Functions related to loading contexts from simple context definitions. - /** * Fetch metadata on a specific context plugin. * - * @param $context + * @param string $context * Name of a context. * - * @return + * @return array * An array with information about the requested panel context. */ + function ctools_get_context($context) { static $gate = array(); ctools_include('plugins'); @@ -1056,7 +1471,7 @@ function ctools_get_context($context) { /** * Fetch metadata for all context plugins. * - * @return + * @return array * An array of arrays with information about all available panel contexts. */ function ctools_get_contexts() { @@ -1065,27 +1480,38 @@ function ctools_get_contexts() { } /** + * Return a context object from a context definition array. * - * @param $context + * The input $context contains the information needed to identify and invoke + * the context plugin and create the plugin context from that. + * + * @param array $context * The configuration of a context. It must contain the following data: * - name: The name of the context plugin being used. * - context_settings: The configuration based upon the plugin forms. * - identifier: The human readable identifier for this context, usually * defined by the UI. * - keyword: The keyword used for this context for substitutions. - * @param $type + * @param string $type * This is either 'context' which indicates the context will be loaded - * from data in the settings, or 'required_context' which means the + * from data in the settings, or 'requiredcontext' which means the * context must be acquired from an external source. This is the method * used to pass pure contexts from one system to another. + * @param mixed $argument + * Optional information passed to the plugin context via the arg defined in + * the plugin's "placeholder name" field. * - * @return + * @return ctools_context|null * A context object if one can be loaded. + * + * @see ctools_get_context() + * @see ctools_plugin_get_function() */ function ctools_context_get_context_from_context($context, $type = 'context', $argument = NULL) { ctools_include('plugins'); $plugin = ctools_get_context($context['name']); - if ($function = ctools_plugin_get_function($plugin, 'context')) { + $function = ctools_plugin_get_function($plugin, 'context'); + if ($function) { // Backward compatibility: Merge old style settings into new style: if (!empty($context['context_settings'])) { $context += $context['context_settings']; @@ -1100,7 +1526,7 @@ function ctools_context_get_context_from_context($context, $type = 'context', $a if ($return) { $return->identifier = $context['identifier']; $return->page_title = isset($context['title']) ? $context['title'] : ''; - $return->keyword = $context['keyword']; + $return->keyword = $context['keyword']; if (!empty($context->empty)) { $context->placeholder = array( @@ -1112,6 +1538,8 @@ function ctools_context_get_context_from_context($context, $type = 'context', $a return $return; } } + + return NULL; } /** @@ -1125,7 +1553,10 @@ function ctools_context_get_context_from_context($context, $type = 'context', $a * Either 'context' or 'requiredcontext', which indicates whether the contexts * are loaded from internal data or copied from an external source. * @param $placeholders - * If true, placeholders are acceptable. + * If True, placeholders are acceptable. + * + * @return array + * Array of contexts, keyed by context ID. */ function ctools_context_get_context_from_contexts($contexts, $type = 'context', $placeholders = FALSE) { $return = array(); @@ -1144,17 +1575,20 @@ function ctools_context_get_context_from_contexts($contexts, $type = 'context', /** * Match up external contexts to our required contexts. * - * This function is used to create a list of contexts with proper - * IDs based upon a list of required contexts. + * This function is used to create a list of contexts with proper IDs based + * upon a list of required contexts. * - * These contexts passed in should match the numeric positions of the - * required contexts. The caller must ensure this has already happened - * correctly as this function will not detect errors here. + * These contexts passed in should match the numeric positions of the required + * contexts. The caller must ensure this has already happened correctly as this + * function will not detect errors here. * * @param $required * A list of required contexts as defined by the UI. * @param $contexts * A list of matching contexts as passed in from the calling system. + * + * @return array + * Array of contexts, keyed by context ID. */ function ctools_context_match_required_contexts($required, $contexts) { $return = array(); @@ -1163,10 +1597,10 @@ function ctools_context_match_required_contexts($required, $contexts) { } foreach ($required as $r) { - $context = clone(array_shift($contexts)); + $context = clone array_shift($contexts); $context->identifier = $r['identifier']; $context->page_title = isset($r['title']) ? $r['title'] : ''; - $context->keyword = $r['keyword']; + $context->keyword = $r['keyword']; $return[ctools_context_id($r, 'requiredcontext')] = $context; } @@ -1178,31 +1612,35 @@ function ctools_context_match_required_contexts($required, $contexts) { * * Not all of the types need to be supported by this object. * - * This function is not used to load contexts from external data, but may - * be used to load internal contexts and relationships. Otherwise it can also - * be used to generate a full set of placeholders for UI purposes. + * This function is not used to load contexts from external data, but may be + * used to load internal contexts and relationships. Otherwise it can also be + * used to generate a full set of placeholders for UI purposes. * - * @param $object + * @param object $object * An object that contains some or all of the following variables: * - * - requiredcontexts: A list of UI configured contexts that are required - * from an external source. Since these require external data, they will - * only be added if $placeholders is set to TRUE, and empty contexts will - * be created. - * - arguments: A list of UI configured arguments that will create contexts. - * Since these require external data, they will only be added if $placeholders - * is set to TRUE. - * - contexts: A list of UI configured contexts that have no external source, - * and are essentially hardcoded. For example, these might configure a - * particular node or a particular taxonomy term. - * - relationships: A list of UI configured contexts to be derived from other - * contexts that already exist from other sources. For example, these might - * be used to get a user object from a node via the node author relationship. - * @param $placeholders - * If TRUE, this will generate placeholder objects for types this function + * - requiredcontexts: A list of UI configured contexts that are required + * from an external source. Since these require external data, they will + * only be added if $placeholders is set to TRUE, and empty contexts will + * be created. + * - arguments: A list of UI configured arguments that will create contexts. + * As these require external data, they will only be added if $placeholders + * is set to TRUE. + * - contexts: A list of UI configured contexts that have no external source, + * and are essentially hardcoded. For example, these might configure a + * particular node or a particular taxonomy term. + * - relationships: A list of UI configured contexts to be derived from other + * contexts that already exist from other sources. For example, these might + * be used to get a user object from a node via the node author + * relationship. + * @param bool $placeholders + * If True, this will generate placeholder objects for any types this function * cannot load. - * @param $contexts + * @param array $contexts * An array of pre-existing contexts that will be part of the return value. + * + * @return array + * Merged output of all results of ctools_context_get_context_from_contexts(). */ function ctools_context_load_contexts($object, $placeholders = TRUE, $contexts = array()) { if (!empty($object->base_contexts)) { @@ -1226,7 +1664,7 @@ function ctools_context_load_contexts($object, $placeholders = TRUE, $contexts = $contexts += ctools_context_get_context_from_contexts($object->contexts, 'context', $placeholders); } - // add contexts from relationships + // Add contexts from relationships. if (!empty($object->relationships) && is_array($object->relationships)) { ctools_context_get_context_from_relationships($object->relationships, $contexts, $placeholders); } @@ -1245,7 +1683,7 @@ function ctools_context_load_contexts($object, $placeholders = TRUE, $contexts = function ctools_context_get_form($contexts) { if (!empty($contexts)) { foreach ($contexts as $id => $context) { - // if a form shows its id as being a 'required context' that means the + // If a form shows its id as being a 'required context' that means the // the context is external to this display and does not count. if (!empty($context->form_id) && substr($id, 0, 15) != 'requiredcontext') { return $context; @@ -1264,7 +1702,7 @@ function ctools_context_get_form($contexts) { * The arguments. These will be acquired from $form_state['values'] and the * keys must match the context IDs. * - * @return + * @return array * A new $contexts array containing the replaced contexts. Not all contexts * may be replaced if, for example, an argument was unable to be converted * into a context. @@ -1283,12 +1721,14 @@ function ctools_context_replace_placeholders($contexts, $arguments) { $new_context = ctools_context_get_context_from_relationship($relationship, $contexts[$relationship['context']]); } break; + case 'argument': if (isset($arguments[$cid]) && $arguments[$cid] !== '') { $argument = $context->placeholder['conf']; $new_context = ctools_context_get_context_from_argument($argument, $arguments[$cid]); } break; + case 'context': if (!empty($arguments[$cid])) { $context_info = $context->placeholder['conf']; @@ -1337,32 +1777,37 @@ function ctools_context_replace_form(&$form, $contexts) { if (is_array($plugin['placeholder form'])) { $form[$cid] = $plugin['placeholder form']; } - else if (function_exists($plugin['placeholder form'])) { - $widget = $plugin['placeholder form']($info); - if ($widget) { - $form[$cid] = $widget; + else { + if (function_exists($plugin['placeholder form'])) { + $widget = $plugin['placeholder form']($info); + if ($widget) { + $form[$cid] = $widget; + } } } if (!empty($form[$cid])) { - $form[$cid]['#title'] = t('@identifier (@keyword)', array('@keyword' => '%' . $context->keyword, '@identifier' => $context->identifier)); + $form[$cid]['#title'] = t('@identifier (@keyword)', array( + '@keyword' => '%' . $context->keyword, + '@identifier' => $context->identifier, + )); } } } } // --------------------------------------------------------------------------- -// Functions related to loading access control plugins - +// Functions related to loading access control plugins. /** * Fetch metadata on a specific access control plugin. * * @param $name * Name of a plugin. * - * @return + * @return array * An array with information about the requested access control plugin. */ + function ctools_get_access_plugin($name) { ctools_include('plugins'); return ctools_get_plugins('ctools', 'access', $name); @@ -1371,7 +1816,7 @@ function ctools_get_access_plugin($name) { /** * Fetch metadata for all access control plugins. * - * @return + * @return array * An array of arrays with information about all available access control plugins. */ function ctools_get_access_plugins() { @@ -1383,8 +1828,14 @@ function ctools_get_access_plugins() { * Fetch a list of access plugins that are available for a given list of * contexts. * - * if 'logged-in-user' is not in the list of contexts, it will be added as + * If 'logged-in-user' is not in the list of contexts, it will be added as * this is required. + * + * @param array $contexts + * Array of ctools_context objects with which to select access plugins. + * + * @return array + * Array of applicable access plugins. Can be empty. */ function ctools_get_relevant_access_plugins($contexts) { if (!isset($contexts['logged-in-user'])) { @@ -1394,7 +1845,9 @@ function ctools_get_relevant_access_plugins($contexts) { $all_plugins = ctools_get_access_plugins(); $plugins = array(); foreach ($all_plugins as $id => $plugin) { - if (!empty($plugin['required context']) && !ctools_context_match_requirements($contexts, $plugin['required context'])) { + if (!empty($plugin['required context']) + && !ctools_context_match_requirements($contexts, $plugin['required context']) + ) { continue; } $plugins[$id] = $plugin; @@ -1409,14 +1862,17 @@ function ctools_get_relevant_access_plugins($contexts) { function ctools_access_get_loggedin_context() { $context = ctools_context_create('entity:user', array('type' => 'current'), TRUE); $context->identifier = t('Logged in user'); - $context->keyword = 'viewer'; - $context->id = 0; + $context->keyword = 'viewer'; + $context->id = 0; return $context; } /** * Get a summary of an access plugin's settings. + * + * @return string + * The summary text. */ function ctools_access_summary($plugin, $contexts, $test) { if (!isset($contexts['logged-in-user'])) { @@ -1426,8 +1882,9 @@ function ctools_access_summary($plugin, $contexts, $test) { $description = ''; if ($function = ctools_plugin_get_function($plugin, 'summary')) { $required_context = isset($plugin['required context']) ? $plugin['required context'] : array(); - $context = isset($test['context']) ? $test['context'] : array(); - $description = $function($test['settings'], ctools_context_select($contexts, $required_context, $context), $plugin); + $context = isset($test['context']) ? $test['context'] : array(); + $selected_context = ctools_context_select($contexts, $required_context, $context); + $description = $function($test['settings'], $selected_context, $plugin); } if (!empty($test['not'])) { @@ -1439,10 +1896,23 @@ function ctools_access_summary($plugin, $contexts, $test) { /** * Get a summary of a group of access plugin's settings. + * + * @param $access + * An array of settings theoretically set by the user, including the array + * of plugins to check: + * - 'plugins': the array of plugin metadata info to check + * - 'logic': (optional) either 'and' or 'or', indicating how to combine + * restrictions. Defaults to 'or'. + * @param array $contexts + * An array of zero or more contexts that may be used to determine if + * the user has access. + * + * @return string + * The summary text. Can be NULL if there are no plugins defined. */ function ctools_access_group_summary($access, $contexts) { - if (empty($access['plugins'])) { - return; + if (empty($access['plugins']) || !is_array($access['plugins'])) { + return NULL; } $descriptions = array(); @@ -1451,21 +1921,29 @@ function ctools_access_group_summary($access, $contexts) { $descriptions[] = ctools_access_summary($plugin, $contexts, $test); } - $separator = (isset($access['logic']) && $access['logic'] == 'and') ? t(', and ') : t(', or '); + $separator = + (isset($access['logic']) && $access['logic'] === 'and') + ? t(', and ') : t(', or '); return implode($separator, $descriptions); } /** - * Determine if the current user has access via plugin. + * Determine if the current user has access via a plugin. * - * @param $settings - * An array of settings theoretically set by the user. - * @param $contexts + * @param array $settings + * An array of settings theoretically set by the user, including the array + * of plugins to check: + * - 'plugins': the array of plugin metadata info to check + * - 'logic': (optional) either 'and' or 'or', indicating how to combine + * restrictions. The 'or' case is not fully implemented and returns the + * input contexts unchanged. + * + * @param array $contexts * An array of zero or more contexts that may be used to determine if * the user has access. * - * @return - * TRUE if access is granted, false if otherwise. + * @return bool + * TRUE if access is granted, FALSE if otherwise. */ function ctools_access($settings, $contexts = array()) { if (empty($settings['plugins'])) { @@ -1504,15 +1982,15 @@ function ctools_access($settings, $contexts = array()) { // Pass if 'or' and this rule passed. return TRUE; } - else if (!$pass && $settings['logic'] == 'and') { - // Fail if 'and' and htis rule failed. + elseif (!$pass && $settings['logic'] == 'and') { + // Fail if 'and' and this rule failed. return FALSE; } } // Return TRUE if logic was and, meaning all rules passed. // Return FALSE if logic was or, meaning no rule passed. - return $settings['logic'] == 'and'; + return ($settings['logic'] === 'and'); } /** @@ -1521,7 +1999,7 @@ function ctools_access($settings, $contexts = array()) { * @param $plugin * The access plugin being used. * - * @return + * @return array * A default configured test that should be placed in $access['plugins']; */ function ctools_access_new_test($plugin) { @@ -1543,7 +2021,6 @@ function ctools_access_new_test($plugin) { } } - $default = NULL; if (isset($plugin['default'])) { $default = $plugin['default']; @@ -1557,7 +2034,7 @@ function ctools_access_new_test($plugin) { if (is_array($default)) { $test['settings'] = $default; } - else if (function_exists($default)) { + elseif (function_exists($default)) { $test['settings'] = $default(); } else { @@ -1571,11 +2048,23 @@ function ctools_access_new_test($plugin) { /** * Apply restrictions to contexts based upon the access control configured. * - * These restrictions allow the UI to not show content that may not - * be relevant to all types of a particular context. + * These restrictions allow the UI to not show content that may not be relevant + * to all types of a particular context. + * + * @param array $settings + * Array of keys specifying the settings: + * - 'plugins': the array of plugin metadata info to check. If not set, or + * not an array, the function returns with no action. + * - 'logic': (optional) either 'and' or 'or', indicating how to combine + * restrictions. Defaults to 'and'. + * The 'or' case is not fully implemented and returns with no action if + * there is more than one plugin. + * + * @param array $contexts + * Array of available contexts. */ function ctools_access_add_restrictions($settings, $contexts) { - if (empty($settings['plugins'])) { + if (empty($settings['plugins']) || !is_array($settings['plugins'])) { return; } @@ -1584,16 +2073,20 @@ function ctools_access_add_restrictions($settings, $contexts) { } // We're not going to try to figure out restrictions on the or. - if ($settings['logic'] == 'or' && count($settings['plugins']) > 1) { + if ($settings['logic'] === 'or' && count($settings['plugins']) > 1) { return; } foreach ($settings['plugins'] as $test) { $plugin = ctools_get_access_plugin($test['name']); - if ($plugin && $function = ctools_plugin_get_function($plugin, 'restrictions')) { + // $plugin is 'array()' on error. + if ($plugin + && $function = ctools_plugin_get_function($plugin, 'restrictions') + ) { $required_context = isset($plugin['required context']) ? $plugin['required context'] : array(); $context = isset($test['context']) ? $test['context'] : array(); $contexts = ctools_context_select($contexts, $required_context, $context); + if ($contexts !== FALSE) { $function($test['settings'], $contexts); } diff --git a/sites/all/modules/contrib/dev/ctools/includes/context.menu.inc b/sites/all/modules/contrib/dev/ctools/includes/context.menu.inc index ee227cb..798d167 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/context.menu.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/context.menu.inc @@ -25,7 +25,7 @@ function ctools_context_menu(&$items) { 'page callback' => 'ctools_context_ajax_item_delete', ) + $base; - // For the access system + // For the access system. $base['file'] = 'includes/context-access-admin.inc'; $items['ctools/context/ajax/access/add'] = array( 'page callback' => 'ctools_access_ajax_add', diff --git a/sites/all/modules/contrib/dev/ctools/includes/context.theme.inc b/sites/all/modules/contrib/dev/ctools/includes/context.theme.inc index 8f660b8..d0d866f 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/context.theme.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/context.theme.inc @@ -19,7 +19,6 @@ function ctools_context_theme(&$theme) { ); $theme['ctools_context_item_form'] = array( 'render element' => 'form', -// 'variables' => array('form' => NULL), 'file' => 'includes/context.theme.inc', ); $theme['ctools_context_item_row'] = array( @@ -27,7 +26,7 @@ function ctools_context_theme(&$theme) { 'file' => 'includes/context.theme.inc', ); - // For the access plugin + // For the access plugin. $theme['ctools_access_admin_add'] = array( 'render element' => 'form', 'file' => 'includes/context-access-admin.inc', @@ -62,10 +61,10 @@ function theme_ctools_context_item_row($vars) { function theme_ctools_context_item_form($vars) { $form = $vars['form']; - $output = ''; - $type = $form['#ctools_context_type']; - $module = $form['#ctools_context_module']; - $cache_key = $form['#cache_key']; + $output = ''; + $type = $form['#ctools_context_type']; + $module = $form['#ctools_context_module']; + $cache_key = $form['#cache_key']; $type_info = ctools_context_info($type); @@ -104,17 +103,17 @@ function theme_ctools_context_item_form($vars) { if (!empty($form['buttons'])) { // Display the add context item. - $row = array(); - $row[] = array('data' => render($form['buttons'][$type]['item']), 'class' => array('title')); - $row[] = array('data' => render($form['buttons'][$type]['add']), 'class' => array('add'), 'width' => "60%"); - $output .= '
    '; - $output .= render($form['buttons'][$type]); - $theme_vars = array(); - $theme_vars['header'] = array(); - $theme_vars['rows'] = array($row); + $row = array(); + $row[] = array('data' => render($form['buttons'][$type]['item']), 'class' => array('title')); + $row[] = array('data' => render($form['buttons'][$type]['add']), 'class' => array('add'), 'width' => "60%"); + $output .= '
    '; + $output .= render($form['buttons'][$type]); + $theme_vars = array(); + $theme_vars['header'] = array(); + $theme_vars['rows'] = array($row); $theme_vars['attributes'] = array('id' => $type . '-add-table'); - $output .= theme('table', $theme_vars); - $output .= '
    '; + $output .= theme('table', $theme_vars); + $output .= '
    '; } if (!empty($form['description'])) { $output .= render($form['description']); @@ -139,7 +138,7 @@ function theme_ctools_context_list($vars) { $description = (!empty($vars['description'])) ? $vars['description'] : NULL; $titles = array(); $output = ''; - $count = 1; + $count = 1; $contexts = ctools_context_load_contexts($object); @@ -209,7 +208,7 @@ function theme_ctools_context_list($vars) { } } - // And relationships + // And relationships. if (!empty($object->relationships)) { foreach ($object->relationships as $relationship) { $output .= ''; @@ -253,15 +252,15 @@ function theme_ctools_context_list($vars) { } /** - * ctools_context_list() but not in a table format because tabledrag - * won't let us have tables within tables and still drag. + * The ctools_context_list() function but not in a table format because + * tabledrag won't let us have tables within tables and still drag. */ function theme_ctools_context_list_no_table($vars) { $object = $vars['object']; ctools_add_css('context'); $titles = array(); $output = ''; - $count = 1; + $count = 1; // Describe 'built in' contexts. if (!empty($object->base_contexts)) { foreach ($object->base_contexts as $id => $context) { @@ -312,7 +311,7 @@ function theme_ctools_context_list_no_table($vars) { $count++; } } - // And relationships + // And relationships. if (!empty($object->relationships)) { foreach ($object->relationships as $relationship) { $output .= '
    '; @@ -341,4 +340,3 @@ function theme_ctools_context_list_no_table($vars) { return $output; } - diff --git a/sites/all/modules/contrib/dev/ctools/includes/css.inc b/sites/all/modules/contrib/dev/ctools/includes/css.inc index 9813a87..83fe1c3 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/css.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/css.inc @@ -1,6 +1,6 @@ $declaration) { foreach ($declaration as $property => $value) { if (!in_array($property, $allowed_properties)) { - // $filtered['properties'][$selector_str][$property] = $value; + // $filtered['properties'][$selector_str][$property] = $value;. unset($css[$selector_str][$property]); continue; } $value = str_replace('!important', '', $value); if (preg_match($disallowed_values_regex, $value) || !(in_array($value, $allowed_values) || preg_match($allowed_values_regex, $value))) { - // $filtered['values'][$selector_str][$property] = $value; + // $filtered['values'][$selector_str][$property] = $value;. unset($css[$selector_str][$property]); continue; } diff --git a/sites/all/modules/contrib/dev/ctools/includes/dependent.inc b/sites/all/modules/contrib/dev/ctools/includes/dependent.inc index 74de919..20c25de 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/dependent.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/dependent.inc @@ -44,7 +44,7 @@ * * A fuller example, that hides the menu title when no menu is selected: * @code - *function ctools_dependent_example() { + * function ctools_dependent_example() { * $form = array(); * $form['menu'] = array( * '#type' => 'fieldset', @@ -72,12 +72,12 @@ * ); * * return system_settings_form($form); - *} + * } * @endcode * * An example for hiding checkboxes using #prefix and #suffix: * @code - *function ctools_dependent_example_checkbox() { + * function ctools_dependent_example_checkbox() { * $form = array(); * $form['object'] = array( * '#type' => 'fieldset', @@ -111,7 +111,7 @@ * ); * * return system_settings_form($form); - *} + * } * @endcode * * Deprecated: @@ -125,7 +125,6 @@ /** * Process callback to add dependency to form items. - * */ function ctools_dependent_process($element, &$form_state, &$form) { return $element; diff --git a/sites/all/modules/contrib/dev/ctools/includes/dropbutton.theme.inc b/sites/all/modules/contrib/dev/ctools/includes/dropbutton.theme.inc index fcdd5a3..4b95ef0 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/dropbutton.theme.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/dropbutton.theme.inc @@ -75,7 +75,7 @@ function theme_links__ctools_dropbutton($vars) { if (!empty($vars['links'])) { $is_drop_button = (count($vars['links']) > 1); - // Add needed files + // Add needed files. if ($is_drop_button) { ctools_add_js('dropbutton'); ctools_add_css('dropbutton'); @@ -86,7 +86,7 @@ function theme_links__ctools_dropbutton($vars) { static $id = 0; $id++; - // Wrapping div + // Wrapping div. $class = 'ctools-no-js'; $class .= ($is_drop_button) ? ' ctools-dropbutton' : ''; $class .= ' ctools-button'; @@ -98,21 +98,22 @@ function theme_links__ctools_dropbutton($vars) { $output .= '
    '; - // Add a twisty if this is a dropbutton + // Add a twisty if this is a dropbutton. if ($is_drop_button) { $vars['title'] = ($vars['title'] ? check_plain($vars['title']) : t('open')); $output .= ''; // ctools-link + // ctools-link. + $output .= '
    '; } - // The button content + // The button content. $output .= '
    '; // Check for attributes. theme_links expects an array(). @@ -132,12 +133,11 @@ function theme_links__ctools_dropbutton($vars) { // Call theme_links to render the list of links. $output .= theme_links(array('links' => $vars['links'], 'attributes' => $vars['attributes'], 'heading' => '')); - $output .= '
    '; // ctools-content - $output .= '
    '; // ctools-dropbutton + $output .= '
  • '; // ctools-content. + $output .= '
    '; // ctools-dropbutton. return $output; } else { return ''; } } - diff --git a/sites/all/modules/contrib/dev/ctools/includes/dropdown.theme.inc b/sites/all/modules/contrib/dev/ctools/includes/dropdown.theme.inc index 7e748f5..b763687 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/dropdown.theme.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/dropdown.theme.inc @@ -44,20 +44,21 @@ function ctools_dropdown_theme(&$items) { /** * Create a dropdown menu. * - * @param $title - * The text to place in the clickable area to activate the dropdown. - * @param $links - * A list of links to provide within the dropdown, suitable for use - * in via Drupal's theme('links'). - * @param $image - * If true, the dropdown link is an image and will not get extra decorations - * that a text dropdown link will. - * @param $class - * An optional class to add to the dropdown's container div to allow you - * to style a single dropdown however you like without interfering with - * other dropdowns. + * @param array $variables + * An associative array containing: + * - title: The text to place in the clickable area to activate the dropdown. + * - links: A list of links to provide within the dropdown, suitable for use + * in via Drupal's theme('links'). + * - image: If true, the dropdown link is an image and will not get extra + * decorations that a text dropdown link will. + * - class: An optional class to add to the dropdown's container div to allow + * you to style a single dropdown however you like without interfering with + * other dropdowns. + * + * @return string + * Returns HTML for a language configuration form. */ -function theme_ctools_dropdown($vars) { +function theme_ctools_dropdown($vars) { // Provide a unique identifier for every dropdown on the page. static $id = 0; $id++; @@ -67,9 +68,7 @@ function theme_ctools_dropdown($vars) { ctools_add_js('dropdown'); ctools_add_css('dropdown'); - $output = ''; - - $output .= '
    '; + $output = '
    '; $output .= ''; // wrapper + $output .= '
    '; $output .= '
    '; $output .= '
    '; $output .= theme_links(array('links' => $vars['links'], 'attributes' => array(), 'heading' => '')); - $output .= '
    '; // container - $output .= '
    '; // container wrapper - $output .= '
    '; // dropdown + $output .= ''; + $output .= ''; + $output .= ''; + return $output; } - diff --git a/sites/all/modules/contrib/dev/ctools/includes/entity-access.inc b/sites/all/modules/contrib/dev/ctools/includes/entity-access.inc new file mode 100644 index 0000000..69f2c33 --- /dev/null +++ b/sites/all/modules/contrib/dev/ctools/includes/entity-access.inc @@ -0,0 +1,154 @@ +nid); + if ($node->vid !== $default_revision->vid) { + return _node_revision_access($node, $op, $account); + } + else { + return node_access($op, $node, $account); + } + } + // No node is provided. Check for access to all nodes. + if (user_access('bypass node access', $account)) { + return TRUE; + } + if (!user_access('access content', $account)) { + return FALSE; + } + if ($op == 'view' && node_access_view_all_nodes($account)) { + return TRUE; + } + return FALSE; +} + +/** + * Access callback for the user entity. + */ +function ctools_metadata_user_access($op, $entity = NULL, $account = NULL, $entity_type) { + $account = isset($account) ? $account : $GLOBALS['user']; + // Grant access to the users own user account and to the anonymous one. + if (isset($entity) && $op != 'delete' && (($entity->uid == $account->uid && $entity->uid) || (!$entity->uid && $op == 'view'))) { + return TRUE; + } + if (user_access('administer users', $account) || user_access('access user profiles', $account) && $op == 'view' && $entity->status) { + return TRUE; + } + return FALSE; +} + +/** + * Access callback for the comment entity. + */ +function ctools_metadata_comment_access($op, $entity = NULL, $account = NULL) { + // When determining access to a comment, if comment has an associated node, + // the user must be able to view the node in order to access the comment. + if (isset($entity->nid)) { + if (!node_access('view', node_load($entity->nid), $account)) { + return FALSE; + } + } + + // Comment administrators are allowed to perform all operations on all + // comments. + if (user_access('administer comments', $account)) { + return TRUE; + } + + // Unpublished comments can never be accessed by non-admins. + if (isset($entity->status) && $entity->status == COMMENT_NOT_PUBLISHED) { + return FALSE; + } + + if (user_access('access comments', $account) && $op == 'view') { + return TRUE; + } + return FALSE; +} + +/** + * Access callback for the taxonomy entities. + */ +function ctools_metadata_taxonomy_access($op, $entity = NULL, $account = NULL, $entity_type) { + if ($entity_type == 'taxonomy_vocabulary') { + return user_access('administer taxonomy', $account); + } + if (user_access('administer taxonomy', $account) || user_access('access content', $account) && $op == 'view') { + return TRUE; + } + return FALSE; +} diff --git a/sites/all/modules/contrib/dev/ctools/includes/export-ui.inc b/sites/all/modules/contrib/dev/ctools/includes/export-ui.inc index 16e57d6..72e5a2f 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/export-ui.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/export-ui.inc @@ -81,7 +81,6 @@ function ctools_export_ui_process(&$plugin, $info) { // Add some default fields that appear often in exports // If these use different keys they can easily be specified in the // $plugin. - if (empty($plugin['export']['admin_title']) && !empty($schema['fields']['admin_title'])) { $plugin['export']['admin_title'] = 'admin_title'; } @@ -297,7 +296,6 @@ function ctools_export_ui_process(&$plugin, $info) { ); // Define strings. - // For all strings, %title may be filled in at a later time via str_replace // since we do not know the title now. $plugin['strings'] += array( @@ -335,7 +333,7 @@ function ctools_export_ui_process(&$plugin, $info) { $plugin['strings']['confirmation']['delete'] += array( 'question' => t('Are you sure you want to delete %title?'), - 'information' => t('This action will permanently remove this item from your database..'), + 'information' => t('This action will permanently remove this item from your database.'), 'success' => t('The item has been deleted.'), ); @@ -414,6 +412,7 @@ function ctools_export_ui_plugin_base_path($plugin) { * The id in the menu items from the plugin. * @param $export_key * The export key of the item being edited, if it exists. + * * @return * The menu path to the plugin's list. */ @@ -429,12 +428,11 @@ function ctools_export_ui_plugin_menu_path($plugin, $item_id, $export_key = NULL * Helper function to include CTools plugins and get an export-ui exportable. * * @param $plugin_name - * The plugin that should be laoded. + * The plugin that should be loaded. */ function ctools_get_export_ui($plugin_name) { ctools_include('plugins'); return ctools_get_plugins('ctools', 'export_ui', $plugin_name); - } /** @@ -456,14 +454,14 @@ function ctools_export_ui_switcher_page($plugin_name, $op) { $args = func_get_args(); $js = !empty($_REQUEST['js']); - // Load the $plugin information + // Load the $plugin information. $plugin = ctools_get_export_ui($plugin_name); $handler = ctools_export_ui_get_handler($plugin); if ($handler) { $method = $op . '_page'; if (method_exists($handler, $method)) { - // replace the first two arguments: + // Replace the first two arguments: $args[0] = $js; $args[1] = $_POST; return call_user_func_array(array($handler, $method), $args); diff --git a/sites/all/modules/contrib/dev/ctools/includes/export-ui.menu.inc b/sites/all/modules/contrib/dev/ctools/includes/export-ui.menu.inc index d27bf15..0fda8c2 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/export-ui.menu.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/export-ui.menu.inc @@ -1,5 +1,9 @@ array('handler'), ); -} \ No newline at end of file +} diff --git a/sites/all/modules/contrib/dev/ctools/includes/export.inc b/sites/all/modules/contrib/dev/ctools/includes/export.inc index 0b85c2e..d2fff2f 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/export.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/export.inc @@ -4,7 +4,7 @@ * @file * Contains code to make it easier to have exportable objects. * - * Documentation for exportable objects is contained in help/export.html + * Documentation for exportable objects is contained in help/export.html. */ /** @@ -126,6 +126,7 @@ function ctools_export_crud_load_multiple($table, array $names) { * If TRUE, the static cache of all objects will be flushed prior to * loading all. This can be important on listing pages where items * might have changed on the page load. + * * @return * An array of all loaded objects, keyed by the unique IDs of the export key. */ @@ -328,7 +329,6 @@ function ctools_export_crud_set_status($table, $object, $status) { } - /** * Enable a certain object. * @@ -426,7 +426,7 @@ function ctools_export_load_object($table, $type = 'all', $args = array()) { } } - // Build the query + // Build the query. $query = db_select($table, 't__0')->fields('t__0'); $alias_count = 1; if (!empty($schema['join'])) { @@ -451,7 +451,7 @@ function ctools_export_load_object($table, $type = 'all', $args = array()) { if ($type == 'names') { $query->condition($export['key'], $args, 'IN'); } - else if ($type == 'conditions') { + elseif ($type == 'conditions') { foreach ($args as $key => $value) { if (isset($schema['fields'][$key])) { $query->condition($key, $value); @@ -499,7 +499,7 @@ function ctools_export_load_object($table, $type = 'all', $args = array()) { if ($defaults) { foreach ($defaults as $object) { if ($type == 'conditions') { - // if this does not match all of our conditions, skip it. + // If this does not match all of our conditions, skip it. foreach ($args as $key => $value) { if (!isset($object->$key)) { continue 2; @@ -509,12 +509,12 @@ function ctools_export_load_object($table, $type = 'all', $args = array()) { continue 2; } } - else if ($object->$key != $value) { + elseif ($object->$key != $value) { continue 2; } } } - else if ($type == 'names') { + elseif ($type == 'names') { if (!in_array($object->{$export['key']}, $args)) { continue; } @@ -561,7 +561,6 @@ function ctools_export_load_object($table, $type = 'all', $args = array()) { } } - // For conditions, return $return; } @@ -807,10 +806,10 @@ function _ctools_export_get_some_defaults($table, $export, $names) { function _ctools_export_unpack_object($schema, $data, $object = 'stdClass') { if (is_string($object)) { if (class_exists($object)) { - $object = new $object; + $object = new $object(); } else { - $object = new stdClass; + $object = new stdClass(); } } @@ -873,14 +872,14 @@ function ctools_var_export($var, $prefix = '') { $output .= $prefix . ')'; } } - else if (is_object($var) && get_class($var) === 'stdClass') { + elseif (is_object($var) && get_class($var) === 'stdClass') { // var_export() will export stdClass objects using an undefined // magic method __set_state() leaving the export broken. This // workaround avoids this by casting the object as an array for // export and casting it back to an object when evaluated. $output = '(object) ' . ctools_var_export((array) $var, $prefix); } - else if (is_bool($var)) { + elseif (is_bool($var)) { $output = $var ? 'TRUE' : 'FALSE'; } else { @@ -902,7 +901,8 @@ function ctools_export_object($table, $object, $indent = '', $identifier = NULL, $output = $indent . '$' . $identifier . ' = new ' . get_class($object) . "();\n"; if ($schema['export']['can disable']) { - $output .= $indent . '$' . $identifier . '->disabled = FALSE; /* Edit this to true to make a default ' . $identifier . ' disabled initially */' . "\n"; + $disabled = !isset($object->disabled) || $object->disabled != TRUE ? 'FALSE' : 'TRUE'; + $output .= $indent . '$' . $identifier . '->disabled = ' . $disabled . '; /* Edit this to true to make a default ' . $identifier . ' disabled initially */' . "\n"; } if (!empty($schema['export']['api']['current_version'])) { $output .= $indent . '$' . $identifier . '->api_version = ' . $schema['export']['api']['current_version'] . ";\n"; @@ -958,7 +958,7 @@ function ctools_export_object($table, $object, $indent = '', $identifier = NULL, } } - // And bottom additions here + // And bottom additions here. foreach ($additions2 as $field => $value) { $output .= $indent . '$' . $identifier . '->' . $field . ' = ' . ctools_var_export($value, $indent) . ";\n"; } @@ -986,7 +986,7 @@ function ctools_export_get_schema($table) { // simply hasn't been cached. If we've been asked, let's force the // issue. if (!$schema || empty($schema['export'])) { - // force a schema reset: + // Force a schema reset: $schema = drupal_get_schema($table, TRUE); } @@ -998,7 +998,7 @@ function ctools_export_get_schema($table) { return array(); } - // Add some defaults + // Add some defaults. $schema['export'] += array( 'key' => 'name', 'key name' => 'Name', @@ -1106,7 +1106,7 @@ function ctools_export_set_object_status($object, $new_status = TRUE) { $export = $schema['export']; $status = variable_get($export['status'], array()); - // Compare + // Compare. if (!$new_status && $object->export_type & EXPORT_IN_DATABASE) { unset($status[$object->{$export['key']}]); } @@ -1149,12 +1149,12 @@ function ctools_export_new_object($table, $set_defaults = TRUE) { $schema = ctools_export_get_schema($table); $export = $schema['export']; - $object = new $export['object']; + $object = new $export['object'](); foreach ($schema['fields'] as $field => $info) { if (isset($info['object default'])) { $object->$field = $info['object default']; } - else if (isset($info['default'])) { + elseif (isset($info['default'])) { $object->$field = $info['default']; } else { @@ -1179,11 +1179,11 @@ function ctools_export_new_object($table, $set_defaults = TRUE) { function ctools_export_to_hook_code(&$code, $table, $names = array(), $name = 'foo') { $schema = ctools_export_get_schema($table); $export = $schema['export']; - // Use the schema-specified function for generating hook code, if one exists + // Use the schema-specified function for generating hook code, if one exists. if (function_exists($export['to hook code callback'])) { $output = $export['to hook code callback']($names, $name); } - // Otherwise, the following code generates basic hook code + // Otherwise, the following code generates basic hook code. else { $output = ctools_export_default_to_hook_code($schema, $table, $names, $name); } @@ -1227,7 +1227,7 @@ function ctools_export_default_to_hook_code($schema, $table, $names, $name) { $output .= " \${$export['identifier']}s = array();\n\n"; foreach ($objects as $object) { $output .= ctools_export_crud_export($table, $object, ' '); - $output .= " \${$export['identifier']}s['" . check_plain($object->$export['key']) . "'] = \${$export['identifier']};\n\n"; + $output .= " \${$export['identifier']}s['" . check_plain($object->{$export['key']}) . "'] = \${$export['identifier']};\n\n"; } $output .= " return \${$export['identifier']}s;\n"; $output .= "}\n"; @@ -1235,6 +1235,7 @@ function ctools_export_default_to_hook_code($schema, $table, $names, $name) { return $output; } + /** * Default function for listing bulk exportable objects. */ diff --git a/sites/all/modules/contrib/dev/ctools/includes/fields.inc b/sites/all/modules/contrib/dev/ctools/includes/fields.inc index f379f5e..0d3256f 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/fields.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/fields.inc @@ -5,7 +5,6 @@ * Extend core fields with some helper functions to reduce code complexity within views and ctools plugins. */ - /** * Fake an instance of a field. * @@ -75,27 +74,30 @@ function ctools_fields_get_field_formatter_settings_form($field, $formatter_type $conf['formatter_settings'] += $formatter['settings']; } $function = $formatter['module'] . '_field_formatter_settings_form'; - if (function_exists($function)) { - $instance = ctools_fields_fake_field_instance($field['field_name'], $view_mode, $formatter_type, $conf['formatter_settings']); - $settings_form = $function($field, $instance, $view_mode, $form, $form_state); - if ($settings_form) { - // Allow other modules to alter the formatter settings form. - $context = array( - 'module' => $formatter['module'], - 'formatter' => $formatter, - 'field' => $field, - 'instance' => $instance, - 'view_mode' => $view_mode, - 'form' => $form, - 'form_state' => $form_state, - ); - drupal_alter('field_formatter_settings_form', $settings_form, $context); - $settings_form['#tree'] = TRUE; - $form['ctools_field_list']['#value'][] = $field; - $form += $settings_form; - } + $instance = ctools_fields_fake_field_instance($field['field_name'], $view_mode, $formatter_type, $conf['formatter_settings']); + if (function_exists($function)) { + $settings_form = $function($field, $instance, $view_mode, $form, $form_state); } + if (empty($settings_form)) { + $settings_form = array(); + } + + // Allow other modules to alter the formatter settings form. + $context = array( + 'module' => $formatter['module'], + 'formatter' => $formatter, + 'field' => $field, + 'instance' => $instance, + 'view_mode' => $view_mode, + 'form' => $form, + 'form_state' => $form_state, + ); + drupal_alter('field_formatter_settings_form', $settings_form, $context); + + $settings_form['#tree'] = TRUE; + $form['ctools_field_list']['#value'][] = $field; + $form += $settings_form; if (isset($field['cardinality']) && $field['cardinality'] != 1) { list($prefix, $suffix) = explode('@count', t('Skip the first @count item(s)')); @@ -137,7 +139,7 @@ function ctools_fields_get_field_formatter_settings_form($field, $formatter_type */ function ctools_fields_get_field_formatter_info($fields) { $info = array(); - $field_info = module_invoke_all('field_formatter_info'); + $field_info = field_info_formatter_types(); foreach ($fields as $field) { foreach ($field_info as $format_name => $formatter_info) { if (in_array($field['type'], $formatter_info['field types'])) { @@ -145,7 +147,6 @@ function ctools_fields_get_field_formatter_info($fields) { } } } - drupal_alter('field_formatter_info', $info); return $info; } @@ -209,15 +210,15 @@ function ctools_field_label($field_name) { * - Otherwise NULL. * @param $options * An associative array of additional options, with the following keys: - * - 'field_name': The name of the field whose operation should be + * - 'field_name': The name of the field whose operation should be * invoked. By default, the operation is invoked on all the fields * in the entity's bundle. NOTE: This option is not compatible with * the 'deleted' option; the 'field_id' option should be used * instead. - * - 'field_id': The id of the field whose operation should be + * - 'field_id': The id of the field whose operation should be * invoked. By default, the operation is invoked on all the fields * in the entity's' bundles. - * - 'default': A boolean value, specifying which implementation of + * - 'default': A boolean value, specifying which implementation of * the operation should be invoked. * - if FALSE (default), the field types implementation of the operation * will be invoked (hook_field_[op]) @@ -225,10 +226,10 @@ function ctools_field_label($field_name) { * will be invoked (field_default_[op]) * Internal use only. Do not explicitely set to TRUE, but use * _field_invoke_default() instead. - * - 'deleted': If TRUE, the function will operate on deleted fields + * - 'deleted': If TRUE, the function will operate on deleted fields * as well as non-deleted fields. If unset or FALSE, only * non-deleted fields are operated on. - * - 'language': A language code or an array of language codes keyed by field + * - 'language': A language code or an array of language codes keyed by field * name. It will be used to narrow down to a single value the available * languages to act on. * @@ -341,7 +342,7 @@ function ctools_field_foreign_keys($field_name) { $foreign_keys[$field_name] = $field['foreign keys']; } else { - // try to fetch foreign keys from schema, as not everything + // Try to fetch foreign keys from schema, as not everything // stores foreign keys properly in the field info. $module = $field['module']; diff --git a/sites/all/modules/contrib/dev/ctools/includes/jump-menu.inc b/sites/all/modules/contrib/dev/ctools/includes/jump-menu.inc index a5f99aa..e2ae1cf 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/jump-menu.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/jump-menu.inc @@ -8,7 +8,6 @@ * if javascript is in use. Each item is keyed to the href that the button * should go to. With javascript, the page is immediately redirected. Without * javascript, the form is submitted and a drupal_goto() is given. - * */ /** @@ -51,7 +50,7 @@ function ctools_jump_menu($form, &$form_state, $select, $options = array()) { 'hide' => TRUE, ); - ctools_add_js('jump-menu'); + $form['#attached']['js'][] = ctools_attach_js('jump-menu'); if (!empty($options['choose'])) { $select = array('' => $options['choose']) + $select; @@ -127,7 +126,7 @@ function ctools_jump_menu_submit($form, &$form_state) { // This allows duplicate paths to be used in jump menus for multiple options. $redirect_array = explode("::", $form_state['values']['jump']); - if(isset($redirect_array[1]) && !empty($redirect_array[1])){ + if (isset($redirect_array[1]) && !empty($redirect_array[1])) { $redirect = $redirect_array[1]; } else { diff --git a/sites/all/modules/contrib/dev/ctools/includes/language.inc b/sites/all/modules/contrib/dev/ctools/includes/language.inc index 9a7850b..a850670 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/language.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/language.inc @@ -1,5 +1,9 @@ t("Current user's language"), @@ -41,4 +44,4 @@ function ctools_language_list_all() { ); $languages = array_merge($languages, ctools_language_list()); return $languages; -} \ No newline at end of file +} diff --git a/sites/all/modules/contrib/dev/ctools/includes/math-expr.inc b/sites/all/modules/contrib/dev/ctools/includes/math-expr.inc index beffb93..d82281e 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/math-expr.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/math-expr.inc @@ -99,7 +99,10 @@ class ctools_math_expr { 'sqrt','abs','ln','log', 'time', 'ceil', 'floor', 'min', 'max', 'round'); - function ctools_math_expr() { + /** + * ctools_math_expr constructor. + */ + function __construct() { // make the variables a little more accurate $this->v['pi'] = pi(); $this->v['e'] = exp(1); @@ -271,7 +274,7 @@ class ctools_math_expr { } elseif (in_array($op, $ops) and !$expecting_op) { return $this->trigger("unexpected operator '$op'"); } else { // I don't even want to know what you did to get here - return $this->trigger("an unexpected error occured"); + return $this->trigger("an unexpected error occurred"); } if ($index == strlen($expr)) { if (in_array($op, $ops)) { // did we end with an operator? bad. diff --git a/sites/all/modules/contrib/dev/ctools/includes/modal.inc b/sites/all/modules/contrib/dev/ctools/includes/modal.inc index ee969ab..18b216f 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/modal.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/modal.inc @@ -55,9 +55,9 @@ function ctools_modal_add_js() { 'alt' => t('Close window'), )), 'throbber' => theme('image', array( - 'path' => ctools_image_path('throbber.gif'), - 'title' => t('Loading...'), - 'alt' => t('Loading'), + 'path' => ctools_image_path('throbber.gif'), + 'title' => t('Loading...'), + 'alt' => t('Loading'), )), ), ); @@ -66,6 +66,7 @@ function ctools_modal_add_js() { drupal_add_library('system', 'jquery.form'); drupal_add_library('system', 'drupal.progress'); drupal_add_library('system', 'drupal.ajax'); + drupal_add_library('system', 'ui'); ctools_add_js('modal'); ctools_add_css('modal'); @@ -84,7 +85,7 @@ function ctools_modal_add_plugin_js($plugins) { if (file_exists($file)) { $js[$file] = TRUE; } - else if (file(exists($subtype['path'] . '/' . $file))) { + elseif (file(exists($subtype['path'] . '/' . $file))) { $js[$subtype['path'] . '/' . $file] = TRUE; } } @@ -94,7 +95,7 @@ function ctools_modal_add_plugin_js($plugins) { if (file_exists($file)) { $css[$file] = TRUE; } - else if (file(exists($subtype['path'] . '/' . $file))) { + elseif (file(exists($subtype['path'] . '/' . $file))) { $css[$subtype['path'] . '/' . $file] = TRUE; } } @@ -139,7 +140,7 @@ function ctools_modal_command_dismiss() { } /** - * Display loading screen in the modal + * Display loading screen in the modal. */ function ctools_modal_command_loading() { return array( @@ -188,7 +189,7 @@ function ctools_modal_text_button($text, $dest, $alt, $class = '') { * Wrap a form so that we can use it properly with AJAX. Essentially if the * form wishes to render, it automatically does that, otherwise it returns * the render array so we can see submission results. - + * * @param array $form * An associative array containing the structure of the form. * @param array $form_state @@ -221,7 +222,7 @@ function ctools_modal_form_wrapper($form_id, &$form_state) { $output = drupal_build_form($form_id, $form_state); if (!empty($form_state['ajax']) && (!$form_state['executed'] || $form_state['rebuild'])) { - return ctools_modal_form_render($form_state, $output); + return ctools_modal_form_render($form_state, $output); } return $output; diff --git a/sites/all/modules/contrib/dev/ctools/includes/object-cache.inc b/sites/all/modules/contrib/dev/ctools/includes/object-cache.inc index 29225b0..ef52f9a 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/object-cache.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/object-cache.inc @@ -27,6 +27,7 @@ * defaults to session_id(). * * @deprecated $skip_cache is deprecated in favor of drupal_static* + * * @return * The data that was cached. */ @@ -42,8 +43,11 @@ function ctools_object_cache_get($obj, $name, $skip_cache = FALSE, $sid = NULL) } if (!array_key_exists($key, $cache)) { - $data = db_query('SELECT * FROM {ctools_object_cache} WHERE sid = :session_id AND obj = :object AND name = :name', array(':session_id' => $sid, ':object' => $obj, ':name' => $name)) - ->fetchObject(); + $data = db_query('SELECT * FROM {ctools_object_cache} WHERE sid = :session_id AND obj = :object AND name = :name', array( + ':session_id' => $sid, + ':object' => $obj, + ':name' => md5($name), + ))->fetchObject(); if ($data) { $cache[$key] = unserialize($data->data); } @@ -83,7 +87,7 @@ function ctools_object_cache_set($obj, $name, $cache, $sid = NULL) { ->fields(array( 'sid' => $sid, 'obj' => $obj, - 'name' => $name, + 'name' => md5($name), 'data' => serialize($cache), 'updated' => REQUEST_TIME, )) @@ -91,7 +95,7 @@ function ctools_object_cache_set($obj, $name, $cache, $sid = NULL) { } /** - * Remove an object from the non-volatile ctools cache + * Remove an object from the non-volatile ctools cache. * * @param $obj * A 128 character or less string to define what kind of object is being @@ -103,7 +107,6 @@ function ctools_object_cache_set($obj, $name, $cache, $sid = NULL) { * defaults to session_id(). */ function ctools_object_cache_clear($obj, $name, $sid = NULL) { - if (!$sid) { $sid = session_id(); } @@ -111,13 +114,12 @@ function ctools_object_cache_clear($obj, $name, $sid = NULL) { db_delete('ctools_object_cache') ->condition('sid', $sid) ->condition('obj', $obj) - ->condition('name', $name) + ->condition('name', md5($name)) ->execute(); // Ensure the static cache is emptied of this obj:name set. drupal_static_reset('ctools_object_cache_get'); } - /** * Determine if another user has a given object cached. * @@ -142,8 +144,11 @@ function ctools_object_cache_test($obj, $name, $sid = NULL) { $sid = session_id(); } - return db_query('SELECT s.uid, c.updated FROM {ctools_object_cache} c INNER JOIN {sessions} s ON c.sid = s.sid WHERE s.sid <> :session_id AND c.obj = :obj AND c.name = :name ORDER BY c.updated ASC', array(':session_id' => $sid, ':obj' => $obj, ':name' => $name)) - ->fetchObject(); + return db_query('SELECT s.uid, c.updated FROM {ctools_object_cache} c INNER JOIN {sessions} s ON c.sid = s.sid WHERE s.sid <> :session_id AND c.obj = :obj AND c.name = :name ORDER BY c.updated ASC', array( + ':session_id' => $sid, + ':obj' => $obj, + ':name' => md5($name), + ))->fetchObject(); } /** @@ -162,6 +167,7 @@ function ctools_object_cache_test($obj, $name, $sid = NULL) { * An array of objects containing the UID and updated date for each name found. */ function ctools_object_cache_test_objects($obj, $names) { + array_walk($names, 'md5'); return db_query("SELECT c.name, s.uid, c.updated FROM {ctools_object_cache} c INNER JOIN {sessions} s ON c.sid = s.sid WHERE c.obj = :obj AND c.name IN (:names) ORDER BY c.updated ASC", array(':obj' => $obj, ':names' => $names)) ->fetchAllAssoc('name'); } @@ -180,7 +186,7 @@ function ctools_object_cache_test_objects($obj, $names) { function ctools_object_cache_clear_all($obj, $name) { db_delete('ctools_object_cache') ->condition('obj', $obj) - ->condition('name', $name) + ->condition('name', md5($name)) ->execute(); // Ensure the static cache is emptied of this obj:name set. $cache = &drupal_static('ctools_object_cache_get', array()); @@ -197,7 +203,8 @@ function ctools_object_cache_clear_all($obj, $name) { */ function ctools_object_cache_clean($age = NULL) { if (empty($age)) { - $age = 86400 * 7; // 7 days + // 7 days. + $age = 86400 * 7; } db_delete('ctools_object_cache') ->condition('updated', REQUEST_TIME - $age, '<') diff --git a/sites/all/modules/contrib/dev/ctools/includes/page-wizard.inc b/sites/all/modules/contrib/dev/ctools/includes/page-wizard.inc index a211361..9690aae 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/page-wizard.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/page-wizard.inc @@ -1,5 +1,9 @@ plugin = $plugin; if ($function = ctools_plugin_get_function($plugin, 'default cache')) { $function($cache); diff --git a/sites/all/modules/contrib/dev/ctools/includes/plugins-admin.inc b/sites/all/modules/contrib/dev/ctools/includes/plugins-admin.inc index d4ead0a..c11fcfd 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/plugins-admin.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/plugins-admin.inc @@ -10,6 +10,7 @@ * * Implementing this */ + /** * Get a plugin configuration form. * @@ -100,7 +101,7 @@ function _ctools_plugin_configure_create_form_info(&$form_info, $plugin_definiti if (empty($plugin_definition['title'])) { $title = t('Configure'); } - else if ($op == 'add') { + elseif ($op == 'add') { $title = t('Configure new !plugin_title', array('!plugin_title' => $plugin_definition['title'])); } else { @@ -134,10 +135,10 @@ function _ctools_plugin_configure_create_form_info(&$form_info, $plugin_definiti $form_info['forms']['form']['wrapper'] = 'ctools_plugins_default_form_wrapper'; } } - else if (is_array($info)) { - if (empty($form_info['order'])) { - $form_info['order'] = array(); - } + elseif (is_array($info)) { + if (empty($form_info['order'])) { + $form_info['order'] = array(); + } if (empty($form_info['forms'])) { $form_info['forms'] = array(); } diff --git a/sites/all/modules/contrib/dev/ctools/includes/plugins.inc b/sites/all/modules/contrib/dev/ctools/includes/plugins.inc index eadb68f..7b53ae1 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/plugins.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/plugins.inc @@ -2,7 +2,6 @@ /** * @file - * * Contains routines to organize and load plugins. It allows a special * variation of the hook system so that plugins can be kept in separate * .inc files, and can be either loaded all at once or loaded only when @@ -70,7 +69,7 @@ function ctools_plugin_api_info($owner, $api, $minimum_version, $current_version if (isset($info['version'])) { $version = $info['version']; } - else if (isset($info['api'])) { + elseif (isset($info['api'])) { $version = $info['api']; } @@ -79,7 +78,9 @@ function ctools_plugin_api_info($owner, $api, $minimum_version, $current_version } // Only process if version is between minimum and current, inclusive. - if (version_compare($version, $minimum_version, '>=') && version_compare($version, $current_version, '<=')) { + if (($version == $minimum_version) || ($version == $current_version) + || (version_compare($version, $minimum_version, '>=') + && version_compare($version, $current_version, '<='))) { if (!isset($info['path'])) { $info['path'] = drupal_get_path('module', $module); } @@ -97,7 +98,7 @@ function ctools_plugin_api_info($owner, $api, $minimum_version, $current_version } // Only process if version is between minimum and current, inclusive. - if (version_compare($info['version'], $minimum_version, '>=') && version_compare($info['version'], $current_version, '<=')) { + if (version_compare($info['version'], $minimum_version, '>=') && version_compare($info['version'], $current_version, '<=')) { if (!isset($info['path'])) { $info['path'] = ''; } @@ -110,7 +111,7 @@ function ctools_plugin_api_info($owner, $api, $minimum_version, $current_version } // Allow other modules to hook in. - drupal_alter($hook, $cache[$owner][$api]); + drupal_alter($hook, $cache[$owner][$api], $owner, $api); } return $cache[$owner][$api]; @@ -149,7 +150,7 @@ function ctools_plugin_api_include($owner, $api, $minimum_version, $current_vers if (isset($plugin_info["$api file"])) { $file = $plugin_info["$api file"]; } - else if (isset($plugin_info['file'])) { + elseif (isset($plugin_info['file'])) { $file = $plugin_info['file']; } else { @@ -159,7 +160,7 @@ function ctools_plugin_api_include($owner, $api, $minimum_version, $current_vers if (file_exists(DRUPAL_ROOT . "/$plugin_info[path]/$file")) { require_once DRUPAL_ROOT . "/$plugin_info[path]/$file"; } - else if (file_exists(DRUPAL_ROOT . "/$file")) { + elseif (file_exists(DRUPAL_ROOT . "/$file")) { require_once DRUPAL_ROOT . "/$file"; } $already_done[$owner][$api][$module] = TRUE; @@ -182,7 +183,7 @@ function ctools_plugin_api_get_hook($owner, $api) { if (function_exists($function = $owner . '_' . $api . '_hook_name')) { $hook = $function(); } - else if (function_exists($function = $owner . '_ctools_plugin_api_hook_name')) { + elseif (function_exists($function = $owner . '_ctools_plugin_api_hook_name')) { $hook = $function(); } @@ -198,22 +199,22 @@ function ctools_plugin_api_get_hook($owner, $api) { /** * Fetch a group of plugins by name. * - * @param $module - * The name of the module that utilizes this plugin system. It will be - * used to call hook_ctools_plugin_$plugin() to get more data about the plugin. - * @param $type + * @param string $module + * The name of the module that utilizes this plugin system. It will be used to + * get more data about the plugin as defined on hook_ctools_plugin_type(). + * @param string $type * The type identifier of the plugin. - * @param $id + * @param string $id * If specified, return only information about plugin with this identifier. * The system will do its utmost to load only plugins with this id. * - * @return - * An array of information arrays about the plugins received. The contents - * of the array are specific to the plugin. + * @return array + * An array of information arrays about the plugins received. The contents of + * the array are specific to the plugin. */ function ctools_get_plugins($module, $type, $id = NULL) { // Store local caches of plugins and plugin info so we don't have to do full - // lookups everytime. + // lookups every time. static $drupal_static_fast; if (!isset($drupal_static_fast)) { $drupal_static_fast['plugins'] = &drupal_static('ctools_plugins', array()); @@ -222,10 +223,14 @@ function ctools_get_plugins($module, $type, $id = NULL) { $info = ctools_plugin_get_plugin_type_info(); - // Bail out noisily if an invalid module/type combination is requested. if (!isset($info[$module][$type])) { - watchdog('ctools', 'Invalid plugin module/type combination requested: module @module and type @type', array('@module' => $module, '@type' => $type), WATCHDOG_ERROR); - return array(); + // If we don't find the plugin we attempt a cache rebuild before bailing out. + $info = ctools_plugin_get_plugin_type_info(TRUE); + // Bail out noisily if an invalid module/type combination is requested. + if (!isset($info[$module][$type])) { + watchdog('ctools', 'Invalid plugin module/type combination requested: module @module and type @type', array('@module' => $module, '@type' => $type), WATCHDOG_ERROR); + return array(); + } } // Make sure our plugins array is populated. @@ -233,8 +238,8 @@ function ctools_get_plugins($module, $type, $id = NULL) { $plugins[$module][$type] = array(); } - // Attempt to shortcut this whole piece of code if we already have - // the requested plugin: + // Attempt to shortcut this whole piece of code if we already have the + // requested plugin: if ($id && array_key_exists($id, $plugins[$module][$type])) { return $plugins[$module][$type][$id]; } @@ -252,9 +257,9 @@ function ctools_get_plugins($module, $type, $id = NULL) { if (!empty($cache->data)) { // Cache load succeeded so use the cached plugin list. - $plugins[$module][$type] = $cache->data; + $plugins[$module][$type] = $cache->data; // Set $setup to true so we know things where loaded. - $setup[$module][$type] = TRUE; + $setup[$module][$type] = TRUE; } else { // Cache load failed so store that we need to build and write the cache. @@ -269,8 +274,8 @@ function ctools_get_plugins($module, $type, $id = NULL) { $plugins[$module][$type] = ctools_plugin_load_hooks($info[$module][$type]); } - // Then see if we should load all files. We only do this if we - // want a list of all plugins or there was a cache miss. + // Then see if we should load all files. We only do this if we want a list of + // all plugins or there was a cache miss. if (empty($setup[$module][$type]) && ($build_cache || !$id)) { $setup[$module][$type] = TRUE; $plugins[$module][$type] = array_merge($plugins[$module][$type], ctools_plugin_load_includes($info[$module][$type])); @@ -293,9 +298,8 @@ function ctools_get_plugins($module, $type, $id = NULL) { } } - - // If we were told earlier that this is cacheable and the cache was - // empty, give something back. + // If we were told earlier that this is cacheable and the cache was empty, + // give something back. if ($build_cache) { cache_set("plugins:$module:$type", $plugins[$module][$type], $info[$module][$type]['cache table']); } @@ -307,7 +311,7 @@ function ctools_get_plugins($module, $type, $id = NULL) { return array_filter($plugins[$module][$type]); } - // Check to see if we need to look for the file + // Check to see if we need to look for the file. if (!array_key_exists($id, $plugins[$module][$type])) { // If we can have child plugins, check to see if the plugin name is in the // format of parent:child and break it up if it is. @@ -413,19 +417,20 @@ function ctools_get_plugins_reset() { /** * Load plugins from a directory. * - * @param $info + * @param array $info * The plugin info as returned by ctools_plugin_get_info() - * @param $file + * @param string $filename * The file to load if we're looking for just one particular plugin. * - * @return - * An array of information created for this plugin. + * @return array + * A (possibly empty) array of information created for this plugin. */ function ctools_plugin_load_includes($info, $filename = NULL) { // Keep a static array so we don't hit file_scan_directory more than necessary. $all_files = &drupal_static(__FUNCTION__, array()); - // store static of plugin arrays for reference because they can't be reincluded. + // Store static of plugin arrays for reference because they can't be + // reincluded, so there is no point in using drupal_static(). static $plugin_arrays = array(); if (!isset($all_files[$info['module']][$info['type']])) { @@ -460,24 +465,23 @@ function ctools_plugin_load_includes($info, $filename = NULL) { } foreach ($files as $file) { if (!empty($info['info file'])) { - // Parse a .info file + // Parse a .info file. $result = ctools_plugin_process_info($info, $module, $file); } else { // Parse a hook. - $plugin = NULL; // ensure that we don't have something leftover from earlier. + // Ensure that we don't have something leftover from earlier. + $plugin = NULL; if (isset($plugin_arrays[$file->uri])) { $identifier = $plugin_arrays[$file->uri]; } else { - - require_once DRUPAL_ROOT . '/' . $file->uri; + include_once DRUPAL_ROOT . '/' . $file->uri; // .inc files have a special format for the hook identifier. // For example, 'foo.inc' in the module 'mogul' using the plugin - // whose hook is named 'borg_type' should have a function named (deep breath) - // mogul_foo_borg_type() - + // whose hook is named 'borg_type' should have a function named + // (deep breath) mogul_foo_borg_type(). // If, however, the .inc file set the quasi-global $plugin array, we // can use that and not even call a function. Set the $identifier // appropriately and ctools_plugin_process() will handle it. @@ -490,7 +494,8 @@ function ctools_plugin_load_includes($info, $filename = NULL) { } } - $result = ctools_plugin_process($info, $module, $identifier, dirname($file->uri), basename($file->uri), $file->name); + $result = ctools_plugin_process($info, $module, $identifier, + dirname($file->uri), basename($file->uri), $file->name); } if (is_array($result)) { $plugins = array_merge($plugins, $result); @@ -510,7 +515,7 @@ function ctools_plugin_load_includes($info, $filename = NULL) { * @param $info * The $info array for the plugin as returned by ctools_plugin_get_info(). * - * @return array $directories + * @return array * An array of directories to search. */ function ctools_plugin_get_directories($info) { @@ -536,10 +541,9 @@ function ctools_plugin_get_directories($info) { } /** - * Helper function to build a ctools-friendly list of themes capable of - * providing plugins. + * Helper to build a ctools-friendly list of themes capable of providing plugins. * - * @return array $themes + * @return array * A list of themes that can act as plugin providers, sorted parent-first with * the active theme placed last. */ @@ -550,7 +554,7 @@ function _ctools_list_themes() { $themes = $active = array(); $all_themes = list_themes(); foreach ($all_themes as $name => $theme) { - // Only search from active themes + // Only search from active themes. if (empty($theme->status) && $theme->name != $current) { continue; } @@ -561,19 +565,19 @@ function _ctools_list_themes() { } } - // Construct a parent-first list of all themes + // Construct a parent-first list of all themes. foreach ($active as $name => $theme) { $base_themes = isset($theme->base_themes) ? $theme->base_themes : array(); $themes = array_merge($themes, $base_themes, array($name => $theme->info['name'])); } - // Put the actual theme info objects into the array + // Put the actual theme info objects into the array. foreach (array_keys($themes) as $name) { if (isset($all_themes[$name])) { $themes[$name] = $all_themes[$name]; } } - // Make sure the current default theme always gets the last word + // Make sure the current default theme always gets the last word. if ($current_key = array_search($current, array_keys($themes))) { $themes += array_splice($themes, $current_key, 1); } @@ -581,7 +585,6 @@ function _ctools_list_themes() { return $themes; } - /** * Find all the base themes for the specified theme. * @@ -597,9 +600,10 @@ function _ctools_list_themes() { * The name of the theme whose base we are looking for. * @param $used_keys * A recursion parameter preventing endless loops. - * @return + * + * @return array * Returns an array of all of the theme's ancestors; the first element's value - * will be NULL if an error occurred. + * will be NULL if an error occurred. (Note: this is NOT $arr[0]). */ function ctools_find_base_themes($themes, $key, $used_keys = array()) { $base_key = $themes[$key]->info['base theme']; @@ -627,7 +631,6 @@ function ctools_find_base_themes($themes, $key, $used_keys = array()) { return $current_base_theme; } - /** * Load plugin info for the provided hook; this is handled separately from * plugins from files. @@ -652,22 +655,28 @@ function ctools_plugin_load_hooks($info) { /** * Process a single hook implementation of a ctools plugin. * - * @param $info + * @param array $info * The $info array about the plugin as returned by ctools_plugin_get_info() - * @param $module + * @param string $module * The module that implements the plugin being processed. - * @param $identifier - * The plugin identifier, which is used to create the name of the hook - * function being called. - * @param $path + * @param string|array $identifier + * Used to create the base setting of return value. If: + * - $identifier is a string, a hook name is created from this and the 'hook' + * key of the $info array, and the return value of that hook function is + * used. The hook is called like this: $identifier_$hook($info); + * - $identifier is an array, this array is used directly. + * @param string $path * The path where files utilized by this plugin will be found. - * @param $file + * @param string $file * The file that was loaded for this plugin, if it exists. - * @param $base + * @param string $base * The base plugin name to use. If a file was loaded for the plugin, this * is the plugin to assume must be present. This is used to automatically * translate the array to make the syntax more friendly to plugin * implementors. + * + * @return null|array + * NULL on failure, otherwise an array containing the results keyed by name. */ function ctools_plugin_process($info, $module, $identifier, $path, $file = NULL, $base = NULL) { if (is_array($identifier)) { @@ -735,9 +744,19 @@ function _ctools_process_data($result, $plugin_type_info, $module, $path, $file) return $result; } - /** * Process an info file for plugin information, rather than a hook. + * + * @param array $info + * The $info array about the plugin as returned by ctools_plugin_get_info() + * @param string $module + * The module that implements the plugin being processed. + * @param object $file + * An object containing 'uri' and 'name' properties. 'uri' is the name of the + * 'info' file to process. 'name' is the plugin key-name. + * + * @return null|array + * NULL on failure, otherwise an array containing the results keyed by name. */ function ctools_plugin_process_info($info, $module, $file) { $result = drupal_parse_info_file($file->uri); @@ -764,7 +783,7 @@ function ctools_plugin_get_info($module, $type) { * @param $function_name * The identifier of the function. For example, 'settings form'. * - * @return + * @return string * The actual name of the function to call, or NULL if the function * does not exist. */ @@ -781,7 +800,7 @@ function ctools_plugin_get_function($plugin_definition, $function_name) { } if (!isset($plugin_definition[$function_name])) { - return; + return NULL; } if (is_array($plugin_definition[$function_name]) && isset($plugin_definition[$function_name]['function'])) { @@ -816,7 +835,7 @@ function ctools_plugin_get_function($plugin_definition, $function_name) { * @param $function_name * The identifier of the function. For example, 'settings form'. * - * @return + * @return string * The actual name of the function to call, or NULL if the function * does not exist. */ @@ -834,7 +853,7 @@ function ctools_plugin_load_function($module, $type, $id, $function_name) { * @param $class_name * The identifier of the class. For example, 'handler'. * - * @return + * @return string * The actual name of the class to call, or NULL if the class does not exist. */ function ctools_plugin_get_class($plugin_definition, $class_name) { @@ -853,11 +872,11 @@ function ctools_plugin_get_class($plugin_definition, $class_name) { if (!isset($plugin_definition[$class_name])) { return; } - else if (is_string($plugin_definition[$class_name])) { + elseif (is_string($plugin_definition[$class_name])) { // Plugin uses the string form shorthand. $return = $plugin_definition[$class_name]; } - else if (isset($plugin_definition[$class_name]['class'])) { + elseif (isset($plugin_definition[$class_name]['class'])) { // Plugin uses the verbose array form. $return = $plugin_definition[$class_name]['class']; } @@ -879,7 +898,7 @@ function ctools_plugin_get_class($plugin_definition, $class_name) { * @param $class_name * The identifier of the class. For example, 'handler'. * - * @return + * @return string * The actual name of the class to call, or NULL if the class does not exist. */ function ctools_plugin_load_class($module, $type, $id, $class_name) { diff --git a/sites/all/modules/contrib/dev/ctools/includes/registry.inc b/sites/all/modules/contrib/dev/ctools/includes/registry.inc index 9d4328e..7e517bb 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/registry.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/registry.inc @@ -2,7 +2,6 @@ /** * @file - * * Registry magic. In a separate file to minimize unnecessary code loading. */ diff --git a/sites/all/modules/contrib/dev/ctools/includes/stylizer.inc b/sites/all/modules/contrib/dev/ctools/includes/stylizer.inc index 9fdc81d..488a7f3 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/stylizer.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/stylizer.inc @@ -202,7 +202,7 @@ function ctools_stylizer_get_settings_name($settings) { } /** - * Get the path where images will be stored for a given style plugin and settings. + * Get the path where images will be stored for a style plugin and settings. * * This function will make sure the path exists. */ @@ -249,7 +249,7 @@ class ctools_stylizer_image_processor { if (is_string($plugin['actions']) && function_exists($plugin['actions'])) { $actions = $plugin['actions']($plugin, $settings); } - else if (is_array($plugin['actions'])) { + elseif (is_array($plugin['actions'])) { $actions = $plugin['actions']; } @@ -318,7 +318,7 @@ class ctools_stylizer_image_processor { function command_load($name, $file) { $this->log("New workspace: $name (from $file)"); if (!file_exists($file)) { - // Try it relative to the plugin + // Try it relative to the plugin. $file = $this->plugin['path'] . '/' . $file; if (!file_exists($file)) { $this->log("Unable to open $file"); @@ -336,7 +336,7 @@ class ctools_stylizer_image_processor { } /** - * Create a new workspace using the properties of an existing workspace + * Create a new workspace using the properties of an existing workspace. */ function command_new_from($name, $workspace) { $this->log("New workspace: $name from existing $workspace"); @@ -372,7 +372,7 @@ class ctools_stylizer_image_processor { function command_merge_from($workspace, $x = 0, $y = 0) { $this->log("Merge from: $workspace ($x, $y)"); if (empty($this->workspaces[$workspace])) { - $this->log("Workspace $name does not exist.", 'error'); + $this->log("Workspace $workspace does not exist.", 'error'); return; } @@ -382,7 +382,7 @@ class ctools_stylizer_image_processor { function command_merge_to($workspace, $x = 0, $y = 0) { $this->log("Merge to: $workspace ($x, $y)"); if (empty($this->workspaces[$workspace])) { - $this->log("Workspace $name does not exist.", 'error'); + $this->log("Workspace $workspace does not exist.", 'error'); return; } @@ -500,7 +500,7 @@ class ctools_stylizer_image_processor { $palette[$luminosity_input]['green'] = $green; $palette[$luminosity_input]['blue'] = $blue; - // Now we complete the palette, first we'll do it tothe black, and then to + // Now we complete the palette, first we'll do it to the black, and then to // the white. // From input to black @@ -822,7 +822,7 @@ function ctools_stylizer_add_plugin_forms(&$form_info, $plugin, $op) { if ($op == 'add' && isset($plugin['add form'])) { $id = $plugin['add form']; } - else if (isset($plugin['edit form'])) { + elseif (isset($plugin['edit form'])) { $id = $plugin['edit form']; } else { diff --git a/sites/all/modules/contrib/dev/ctools/includes/stylizer.theme.inc b/sites/all/modules/contrib/dev/ctools/includes/stylizer.theme.inc index 85346c1..0ceedff 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/stylizer.theme.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/stylizer.theme.inc @@ -25,4 +25,3 @@ function ctools_stylizer_theme(&$theme) { 'file' => 'includes/stylizer.inc', ); } - diff --git a/sites/all/modules/contrib/dev/ctools/includes/utility.inc b/sites/all/modules/contrib/dev/ctools/includes/utility.inc index 82fc147..3c939a4 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/utility.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/utility.inc @@ -23,7 +23,7 @@ function ctools_passthrough($module, $type, &$items) { require_once DRUPAL_ROOT . '/' . $file->uri; list($tool) = explode('.', $file->name, 2); - $function = $module . '_' . str_replace ('-', '_', $tool) . '_' . str_replace('-', '_', $type); + $function = $module . '_' . str_replace('-', '_', $tool) . '_' . str_replace('-', '_', $type); if (function_exists($function)) { $function($items); } diff --git a/sites/all/modules/contrib/dev/ctools/includes/uuid.inc b/sites/all/modules/contrib/dev/ctools/includes/uuid.inc index b0567d3..f51838b 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/uuid.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/uuid.inc @@ -9,7 +9,9 @@ /** * Pattern for detecting a valid UUID. */ -define('UUID_PATTERN', '[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}'); +if (!defined('UUID_PATTERN')) { + define('UUID_PATTERN', '[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}'); +} /** * Generates a UUID using the Windows internal GUID generator. @@ -25,7 +27,8 @@ function _ctools_uuid_generate_com() { * Generates an universally unique identifier using the PECL extension. */ function _ctools_uuid_generate_pecl() { - return uuid_create(UUID_TYPE_DEFAULT); + $uuid_type = UUID_TYPE_DEFAULT; + return uuid_create($uuid_type); } /** @@ -64,4 +67,5 @@ if (!function_exists('uuid_is_valid')) { function uuid_is_valid($uuid) { return preg_match('/^' . UUID_PATTERN . '$/', $uuid); } + } diff --git a/sites/all/modules/contrib/dev/ctools/includes/views.inc b/sites/all/modules/contrib/dev/ctools/includes/views.inc index 4ef6439..9c1ee6c 100644 --- a/sites/all/modules/contrib/dev/ctools/includes/views.inc +++ b/sites/all/modules/contrib/dev/ctools/includes/views.inc @@ -1,5 +1,9 @@ $params) { $form_info['order'][$step_id] = $params['title']; @@ -83,7 +83,7 @@ function ctools_wizard_multistep_form($form_info, $step, &$form_state) { $form_info['cache location'] = 'storage'; } - // If absolutely nothing was set for the cache area to work on + // If absolutely nothing was set for the cache area to work on. if (!isset($form_state[$form_info['cache location']])) { ctools_include('cache'); $form_state[$form_info['cache location']] = ctools_cache_get($form_info['cache mechanism'], $form_info['cache key']); @@ -199,7 +199,7 @@ function ctools_wizard_multistep_form($form_info, $step, &$form_state) { } } } - else if (isset($form_state['ajax next'])) { + elseif (isset($form_state['ajax next'])) { // Clear a few items off the form state so we don't double post: $next = $form_state['ajax next']; unset($form_state['ajax next']); @@ -299,7 +299,7 @@ function ctools_wizard_wrapper($form, &$form_state) { '#wizard type' => 'next', '#weight' => -2000, '#limit_validation_errors' => array(), - // hardcode the submit so that it doesn't try to save data. + // Hardcode the submit so that it doesn't try to save data. '#submit' => array('ctools_wizard_submit'), '#attributes' => $button_attributes, ); @@ -325,19 +325,18 @@ function ctools_wizard_wrapper($form, &$form_state) { // end of the form list (i.e, there is a next) then it's "update and return" // to be clear. If this is the end of the path and there is no next, we // call it 'Finish'. - // Even if there is no direct return path (some forms may not want you // leaving in the middle) the final button is always a Finish and it does // whatever the return action is. if (!empty($form_info['show return']) && !empty($form_state['next'])) { $form['buttons']['return'] = array( '#type' => 'submit', - '#value' => $form_info['return text'], + '#value' => $form_info['return text'], '#wizard type' => 'return', '#attributes' => $button_attributes, ); } - else if (empty($form_state['next']) || !empty($form_info['free trail'])) { + elseif (empty($form_state['next']) || !empty($form_info['free trail'])) { $form['buttons']['return'] = array( '#type' => 'submit', '#value' => $form_info['finish text'], @@ -352,7 +351,7 @@ function ctools_wizard_wrapper($form, &$form_state) { '#type' => 'submit', '#value' => $form_info['cancel text'], '#wizard type' => 'cancel', - // hardcode the submit so that it doesn't try to save data. + // Hardcode the submit so that it doesn't try to save data. '#limit_validation_errors' => array(), '#submit' => array('ctools_wizard_submit'), '#attributes' => $button_attributes, @@ -370,7 +369,6 @@ function ctools_wizard_wrapper($form, &$form_state) { // Set up our submit handler after theirs. Since putting something here will // skip Drupal's autodetect, we autodetect for it. - // We make sure ours is after theirs so that they get to change #next if // the want to. $form['#submit'] = array(); @@ -398,7 +396,7 @@ function ctools_wizard_wrapper($form, &$form_state) { $params = array($url, $options); } - $form['#action'] = call_user_func_array('url', $params); + $form['#action'] = call_user_func_array('url', $params); } if (isset($info['wrapper']) && function_exists($info['wrapper'])) { @@ -418,7 +416,7 @@ function ctools_wizard_submit(&$form, &$form_state) { if (isset($form_state['clicked_button']['#wizard type'])) { $type = $form_state['clicked_button']['#wizard type']; - // if AJAX enabled, we proceed slightly differently here. + // If AJAX enabled, we proceed slightly differently here. if (!empty($form_state['ajax'])) { if ($type == 'next') { $form_state['ajax next'] = $form_state['clicked_button']['#next']; @@ -428,7 +426,7 @@ function ctools_wizard_submit(&$form, &$form_state) { if ($type == 'cancel' && isset($form_state['form_info']['cancel path'])) { $form_state['redirect'] = $form_state['form_info']['cancel path']; } - else if ($type == 'next') { + elseif ($type == 'next') { $form_state['redirect'] = ctools_wizard_get_path($form_state['form_info'], $form_state['clicked_button']['#next']); if (!empty($_GET['destination'])) { // We don't want drupal_goto redirect this request @@ -437,10 +435,10 @@ function ctools_wizard_submit(&$form, &$form_state) { unset($_GET['destination']); } } - else if (isset($form_state['form_info']['return path'])) { + elseif (isset($form_state['form_info']['return path'])) { $form_state['redirect'] = $form_state['form_info']['return path']; } - else if ($type == 'finish' && isset($form_state['form_info']['cancel path'])) { + elseif ($type == 'finish' && isset($form_state['form_info']['cancel path'])) { $form_state['redirect'] = $form_state['form_info']['cancel path']; } } @@ -469,10 +467,8 @@ function ctools_wizard_get_path($form_info, $step) { if (!isset($path[1]) || !is_array($path[1])) { $path[1] = array(); } - // Ensure that the query part of options is an array. - $path[1] += array('query' => array()); // Add the destination parameter, if not set already. - $path[1]['query'] += drupal_get_destination(); + $path[1] += drupal_get_destination(); } return $path; @@ -505,15 +501,15 @@ function ctools_wizard_defaults(&$form_info) { } $form_info = $form_info + $defaults; - // set form callbacks if they aren't defined + // Set form callbacks if they aren't defined. foreach ($form_info['forms'] as $step => $params) { - if (!$params['form id']) { - $form_callback = $hook . '_' . $step . '_form'; - $form_info['forms'][$step]['form id'] = $form_callback; + if (empty($params['form id'])) { + $form_callback = $hook . '_' . $step . '_form'; + $form_info['forms'][$step]['form id'] = $form_callback; } } - // set button callbacks + // Set button callbacks. $callbacks = array( 'back callback' => '_back', 'next callback' => '_next', @@ -523,10 +519,10 @@ function ctools_wizard_defaults(&$form_info) { ); foreach ($callbacks as $key => $callback) { - // never overwrite if explicity defined + // Never overwrite if explicity defined. if (empty($form_info[$key])) { $wizard_callback = $hook . $callback; - if (function_exists($wizard_callback)) { + if (function_exists($wizard_callback)) { $form_info[$key] = $wizard_callback; } } diff --git a/sites/all/modules/contrib/dev/ctools/js/auto-submit.js b/sites/all/modules/contrib/dev/ctools/js/auto-submit.js index a3e9aa4..b658577 100644 --- a/sites/all/modules/contrib/dev/ctools/js/auto-submit.js +++ b/sites/all/modules/contrib/dev/ctools/js/auto-submit.js @@ -36,9 +36,11 @@ Drupal.behaviors.CToolsAutoSubmit = { attach: function(context) { // 'this' references the form element function triggerSubmit (e) { - var $this = $(this); - if (!$this.hasClass('ctools-ajaxing')) { - $this.find('.ctools-auto-submit-click').click(); + if ($.contains(document.body, this)) { + var $this = $(this); + if (!$this.hasClass('ctools-ajaxing')) { + $this.find('.ctools-auto-submit-click').click(); + } } } diff --git a/sites/all/modules/contrib/dev/ctools/js/dependent.js b/sites/all/modules/contrib/dev/ctools/js/dependent.js index f74ec81..a60fc12 100644 --- a/sites/all/modules/contrib/dev/ctools/js/dependent.js +++ b/sites/all/modules/contrib/dev/ctools/js/dependent.js @@ -97,7 +97,13 @@ else { switch ($(trigger).attr('type')) { case 'checkbox': - var val = $(trigger).attr('checked') ? true : false; + // **This check determines if using a jQuery version 1.7 or newer which requires the use of the prop function instead of the attr function when not called on an attribute + if ($().prop) { + var val = $(trigger).prop('checked') ? true : false; + } + else { + var val = $(trigger).attr('checked') ? true : false; + } if (val) { $(trigger).siblings('label').removeClass('hidden-options').addClass('expanded-options'); @@ -148,34 +154,41 @@ len++; } - var object = $('#' + id + '-wrapper'); - if (!object.size()) { - // Some elements can't use the parent() method or they can - // damage things. They are guaranteed to have wrappers but - // only if dependent.inc provided them. This check prevents - // problems when multiple AJAX calls cause settings to build - // up. - var $original = $('#' + id); - if ($original.is('fieldset') || $original.is('textarea')) { - continue; - } - - object = $('#' + id).parent(); + var $original = $('#' + id); + if ($original.is('fieldset') || $original.is('textarea')) { + continue; } + var object = $original.parent(); + if (Drupal.settings.CTools.dependent[id].type == 'disable') { if (Drupal.settings.CTools.dependent[id].num <= len) { // Show if the element if criteria is matched - object.attr('disabled', false); - object.addClass('dependent-options'); - object.children().attr('disabled', false); + // **This check determines if using a jQuery version 1.7 or newer which requires the use of the prop function instead of the attr function when not called on an attribute + if (typeof $().prop == 'function') { + object.prop('disabled', false); + object.addClass('dependent-options'); + object.children().prop('disabled', false); + } + else { + object.attr('disabled', false); + object.addClass('dependent-options'); + object.children().attr('disabled', false); + } } else { // Otherwise hide. Use css rather than hide() because hide() // does not work if the item is already hidden, for example, // in a collapsed fieldset. - object.attr('disabled', true); - object.children().attr('disabled', true); + // **This check determines if using a jQuery version 1.7 or newer which requires the use of the prop function instead of the attr function when not called on an attribute + if (typeof $().prop == 'function') { + object.prop('disabled', true); + object.children().prop('disabled', true); + } + else { + object.attr('disabled', true); + object.children().attr('disabled', true); + } } } else { diff --git a/sites/all/modules/contrib/dev/ctools/js/modal.js b/sites/all/modules/contrib/dev/ctools/js/modal.js index 37908cf..ec7b02a 100644 --- a/sites/all/modules/contrib/dev/ctools/js/modal.js +++ b/sites/all/modules/contrib/dev/ctools/js/modal.js @@ -48,7 +48,8 @@ modalOptions: { opacity: .55, background: '#fff' - } + }, + modalClass: 'default' }; var settings = {}; @@ -97,8 +98,8 @@ resize(); $('span.modal-title', Drupal.CTools.Modal.modal).html(Drupal.CTools.Modal.currentSettings.loadingText); - Drupal.CTools.Modal.modalContent(Drupal.CTools.Modal.modal, settings.modalOptions, settings.animation, settings.animationSpeed); - $('#modalContent .modal-content').html(Drupal.theme(settings.throbberTheme)); + Drupal.CTools.Modal.modalContent(Drupal.CTools.Modal.modal, settings.modalOptions, settings.animation, settings.animationSpeed, settings.modalClass); + $('#modalContent .modal-content').html(Drupal.theme(settings.throbberTheme)).addClass('ctools-modal-loading'); // Position autocomplete results based on the scroll position of the modal. $('#modalContent .modal-content').delegate('input.form-autocomplete', 'keyup', function() { @@ -120,18 +121,18 @@ */ Drupal.theme.prototype.CToolsModalDialog = function () { var html = '' - html += '
    ' - html += '
    ' // panels-modal-content - html += ' '; - html += ' '; + html += '
    ' + html += '
    ' // panels-modal-content + html += ' '; + html += ' '; html += '
    '; + html += '
    '; return html; } @@ -141,11 +142,11 @@ */ Drupal.theme.prototype.CToolsModalThrobber = function () { var html = ''; - html += '
    -
    \ No newline at end of file + diff --git a/sites/all/modules/contrib/dev/ctools/page_manager/theme/page_manager.theme.inc b/sites/all/modules/contrib/dev/ctools/page_manager/theme/page_manager.theme.inc index 6435d1c..3df55a8 100644 --- a/sites/all/modules/contrib/dev/ctools/page_manager/theme/page_manager.theme.inc +++ b/sites/all/modules/contrib/dev/ctools/page_manager/theme/page_manager.theme.inc @@ -27,10 +27,10 @@ function template_preprocess_page_manager_edit_page(&$vars) { $vars['locked'] = theme('page_manager_lock', array('page' => $page)); $vars['changed'] = theme('page_manager_changed', array('text' => t('Locked'), 'description' => t('This page is being edited by another user and you cannot make changes to it.'))); } - else if (!empty($page->new)) { + elseif (!empty($page->new)) { $vars['changed'] = theme('page_manager_changed', array('text' => t('New'), 'description' => t('This page is newly created and has not yet been saved to the database. It will not be available until you save it.'))); } - else if (!empty($page->changed)) { + elseif (!empty($page->changed)) { $vars['changed'] = theme('page_manager_changed', array('text' => t('Changed'), 'description' => t('This page has been modified, but these modifications are not yet live. While modifying this page, it is locked from modification by other users.'))); } @@ -49,9 +49,9 @@ function template_preprocess_page_manager_edit_page(&$vars) { */ function theme_page_manager_handler_rearrange($vars) { $form = &$vars['form']; - // Assemble the data for a table from everything in $form['handlers'] + // Assemble the data for a table from everything in $form['handlers']. foreach (element_children($form['handlers']) as $id) { - // provide a reference shortcut. + // Provide a reference shortcut. $element = &$form['handlers'][$id]; if (isset($element['title'])) { $row = array(); diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/compare_users.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/compare_users.inc index c271ff4..e0a7f1b 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/compare_users.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/compare_users.inc @@ -2,7 +2,7 @@ /** * @file - * Ctools access plugin to provide access/visiblity if two user contexts are equal. + * CTools access plugin to provide access/visiblity if two user contexts are equal. */ /** @@ -20,12 +20,12 @@ $plugin = array( 'summary' => 'ctools_compare_users_ctools_access_summary', 'required context' => array( new ctools_context_required(t('First User'), 'user'), - new ctools_context_required(t("Second User"), 'user') + new ctools_context_required(t("Second User"), 'user'), ), ); /** - * Settings form for the 'by perm' access plugin + * Settings form for the 'by perm' access plugin. */ function ctools_compare_users_settings($form, &$form_state, $conf) { diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/context_exists.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/context_exists.inc index aabc62d..e5d2b5e 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/context_exists.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/context_exists.inc @@ -2,7 +2,7 @@ /** * @file - * Plugin to provide access control/visibility based on existence of a specified context + * Plugin to provide access control/visibility based on existence of a specified context. */ $plugin = array( @@ -16,7 +16,7 @@ $plugin = array( ); /** - * Settings form + * Settings form. */ function ctools_context_exists_ctools_access_settings($form, &$form_state, $conf) { $form['settings']['exists'] = array( @@ -29,17 +29,17 @@ function ctools_context_exists_ctools_access_settings($form, &$form_state, $conf } /** - * Check for access + * Check for access. */ function ctools_context_exists_ctools_access_check($conf, $context) { - // xor returns false if the two bools are the same, and true if they are not. + // Xor returns false if the two bools are the same, and true if they are not. // i.e, if we asked for context_exists and it does, return true. // If we asked for context does not exist and it does, return false. return (empty($context->data) xor !empty($conf['exists'])); } /** - * Provide a summary description based upon the specified context + * Provide a summary description based upon the specified context. */ function ctools_context_exists_ctools_access_summary($conf, $context) { if (!empty($conf['exists'])) { diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/entity_bundle.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/entity_bundle.inc index e07a048..fce3419 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/entity_bundle.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/entity_bundle.inc @@ -43,7 +43,7 @@ function ctools_entity_bundle_ctools_access_get_children($plugin, $parent) { } /** - * Settings form for the 'by entity_bundle' access plugin + * Settings form for the 'by entity_bundle' access plugin. */ function ctools_entity_bundle_ctools_access_settings($form, &$form_state, $conf) { $plugin = $form_state['plugin']; @@ -133,4 +133,3 @@ function ctools_entity_bundle_ctools_access_summary($conf, $context, $plugin) { return format_plural(count($names), '@identifier is bundle "@types"', '@identifier bundle is one of "@types"', array('@types' => implode(', ', $names), '@identifier' => $context->identifier)); } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/entity_field_value.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/entity_field_value.inc index fa94a48..60d2c2e 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/entity_field_value.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/entity_field_value.inc @@ -48,7 +48,7 @@ function ctools_entity_field_value_ctools_access_get_children($plugin, $parent) } function _ctools_entity_field_value_ctools_access_get_child($plugin, $parent, $entity_type, $bundle_type, $field_name, $entity = NULL, $bundle = NULL, $field = NULL) { - // check that the entity, bundle and field arrays have a value. + // Check that the entity, bundle and field arrays have a value. // If not, load theme using machine names. if (empty($entity)) { $entity = entity_get_info($entity_type); @@ -68,14 +68,14 @@ function _ctools_entity_field_value_ctools_access_get_child($plugin, $parent, $e $plugin['description'] = t('Control access by @entity entity bundle.', array('@entity' => $entity_type)); $plugin['name'] = $parent . ':' . $entity_type . ':' . $bundle_type . ':' . $field_name; $plugin['required context'] = new ctools_context_required(t(ucfirst($entity_type)), $entity_type, array( - 'type' => $bundle_type, - )); + 'type' => $bundle_type, + )); return $plugin; } /** - * Settings form for the 'by entity_bundle' access plugin + * Settings form for the 'by entity_bundle' access plugin. */ function ctools_entity_field_value_ctools_access_settings($form, &$form_state, $conf) { $plugin = $form_state['plugin']; @@ -88,7 +88,7 @@ function ctools_entity_field_value_ctools_access_settings($form, &$form_state, $ $columns[$column] = _field_sql_storage_columnname($field_name, $column); } ctools_include('fields'); - $entity = (object)array( + $entity = (object) array( $entity_info['entity keys']['bundle'] => $bundle_type, ); @@ -115,7 +115,7 @@ function ctools_entity_field_value_ctools_access_settings($form, &$form_state, $ $form['#parents'] = array('settings'); $langcode = field_valid_language(NULL); $form['settings'] += (array) ctools_field_invoke_field($instance, 'form', $entity_type, $entity, $form, $form_state, array('default' => TRUE, 'language' => $langcode)); - // weight is really not important once this is populated and will only interfere with the form layout. + // Weight is really not important once this is populated and will only interfere with the form layout. foreach (element_children($form['settings']) as $element) { unset($form['settings'][$element]['#weight']); } @@ -145,7 +145,8 @@ function ctools_entity_field_value_ctools_access_settings_submit($form, &$form_s function _ctools_entity_field_value_get_proper_form_items($field, $form_items, $columns) { $items = array(); - if (!is_array($form_items)) { // Single value item. + // Single value item. + if (!is_array($form_items)) { foreach ($columns as $column) { $items[0][$column] = $form_items; } @@ -175,7 +176,7 @@ function _ctools_entity_field_value_get_proper_form_items($field, $form_items, $ foreach ($columns as $column) { if (isset($form_items[$column])) { $has_columns = TRUE; - $item[$column] = $form_items[$column]; + $item[$column] = $form_items[$column]; } else { $item[$column] = ''; @@ -353,7 +354,7 @@ function ctools_entity_field_value_ctools_access_summary($conf, $context, $plugi $display['type'] = 'entityreference_label'; break; - default : + default: // Use field instance formatter setting. break; } @@ -381,7 +382,7 @@ function ctools_entity_field_value_ctools_access_summary($conf, $context, $plugi if (is_array($elements)) { foreach ($elements as $element_key => $element) { if (is_numeric($element_key)) { - $value_str= strip_tags(drupal_render($element)); + $value_str = strip_tags(drupal_render($element)); if (strlen($value_str) > 0) { $output[] = $value_str; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/front.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/front.inc index 1bbc6e0..8718a14 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/front.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/front.inc @@ -19,7 +19,7 @@ $plugin = array( ); /** - * Settings form for the 'by parent term' access plugin + * Settings form for the 'by parent term' access plugin. */ function ctools_front_ctools_access_settings($form, &$form_state, $conf) { // No additional configuration necessary. diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/node_access.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/node_access.inc index fcd275d..c153cc5 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/node_access.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/node_access.inc @@ -24,7 +24,7 @@ $plugin = array( ); /** - * Settings form for the 'by node_access' access plugin + * Settings form for the 'by node_access' access plugin. */ function ctools_node_access_ctools_access_settings($form, &$form_state, $conf) { $form['settings']['type'] = array( @@ -86,4 +86,3 @@ function ctools_node_access_ctools_access_summary($conf, $context) { return t('@user can create nodes of the same type as @node.', $replacement); } } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/node_comment.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/node_comment.inc index 915ee20..6cebdaf 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/node_comment.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/node_comment.inc @@ -1,4 +1,5 @@ implode(', ', $names), '@identifier' => $context->identifier)); } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/node_status.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/node_status.inc index ad5ba40..137f282 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/node_status.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/node_status.inc @@ -30,4 +30,3 @@ function ctools_node_status_ctools_access_check($conf, $context) { function ctools_node_status_ctools_access_summary($conf, $context) { return t('Returns true if the nodes status is "published".'); } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/node_type.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/node_type.inc index 23a3845..d89532a 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/node_type.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/node_type.inc @@ -22,7 +22,7 @@ $plugin = array( ); /** - * Settings form for the 'by node_type' access plugin + * Settings form for the 'by node_type' access plugin. */ function ctools_node_type_ctools_access_settings($form, &$form_state, $conf) { $types = node_type_get_types(); @@ -114,4 +114,3 @@ function ctools_node_type_ctools_access_summary($conf, $context) { return format_plural(count($names), '@identifier is type "@types"', '@identifier type is one of "@types"', array('@types' => implode(', ', $names), '@identifier' => $context->identifier)); } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/path_visibility.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/path_visibility.inc index 60b8612..1612e1c 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/path_visibility.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/path_visibility.inc @@ -11,12 +11,12 @@ $plugin = array( 'callback' => 'ctools_path_visibility_ctools_access_check', 'settings form' => 'ctools_path_visibility_ctools_access_settings', 'summary' => 'ctools_path_visibility_ctools_access_summary', - 'required context' => new ctools_context_optional(t('Path'), 'string'), + 'required context' => new ctools_context_optional(t('Path'), 'string'), 'default' => array('visibility_setting' => 1, 'paths' => ''), ); /** - * Settings form + * Settings form. */ function ctools_path_visibility_ctools_access_settings($form, &$form_state, $conf) { $form['settings']['note'] = array( diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/perm.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/perm.inc index 67516fa..cb2bb81 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/perm.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/perm.inc @@ -20,11 +20,11 @@ $plugin = array( ); /** - * Settings form for the 'by perm' access plugin + * Settings form for the 'by perm' access plugin. */ function ctools_perm_ctools_access_settings($form, &$form_state, $conf) { $perms = array(); - // Get list of permissions + // Get list of permissions. foreach (module_list(FALSE, FALSE, TRUE) as $module) { // By keeping them keyed by module we can use optgroups with the // 'select' type. @@ -70,4 +70,3 @@ function ctools_perm_ctools_access_summary($conf, $context) { $permissions = module_invoke_all('permission'); return t('@identifier has "@perm"', array('@identifier' => $context->identifier, '@perm' => $permissions[$conf['perm']]['title'])); } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/php.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/php.inc index 35da86d..4e9d056 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/php.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/php.inc @@ -20,7 +20,7 @@ $plugin = array( ); /** - * Settings form for the 'by perm' access plugin + * Settings form for the 'by perm' access plugin. * * @todo Need a way to provide a list of all available contexts to be used by * the eval-ed PHP. @@ -38,7 +38,7 @@ function ctools_php_ctools_access_settings($form, &$form_state, $conf) { '#type' => 'textarea', '#title' => t('PHP Code'), '#default_value' => $conf['php'], - '#description' => t('Access will be granted if the following PHP code returns TRUE. Do not include <?php ?>. Note that executing incorrect PHP-code can break your Drupal site. All contexts will be available in the $contexts variable.'), + '#description' => t('Access will be granted if the following PHP code returns TRUE. Do not include <?php ?>. Note that executing incorrect PHP-code can break your Drupal site. All contexts will be available in the $contexts variable.'), ); if (!user_access('use PHP for settings')) { $form['settings']['php']['#disabled'] = TRUE; diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/query_string_exists.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/query_string_exists.inc new file mode 100644 index 0000000..fb3b433 --- /dev/null +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/query_string_exists.inc @@ -0,0 +1,44 @@ + t('Query string exists'), + 'description' => t('Control access by whether or not a query string exists.'), + 'callback' => 'ctools_query_string_exists_ctools_access_check', + 'settings form' => 'ctools_query_string_exists_ctools_access_settings', + 'summary' => 'ctools_query_string_exists_ctools_access_summary', + 'defaults' => array('key' => ''), +); + +/** + * Settings form. + */ +function ctools_query_string_exists_ctools_access_settings($form, &$form_state, $config) { + $form['settings']['key'] = array( + '#title' => t('Query string key'), + '#description' => t('Enter the key of the query string.'), + '#type' => 'textfield', + '#required' => TRUE, + '#default_value' => $config['key'], + ); + + return $form; +} + +/** + * Check for access. + */ +function ctools_query_string_exists_ctools_access_check($config, $context) { + return isset($_GET[$config['key']]); +} + +/** + * Provide a summary description. + */ +function ctools_query_string_exists_ctools_access_summary($config, $context) { + return t('@identifier exists', array('@identifier' => $config['key'])); +} diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/role.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/role.inc index b633254..4d8cbae 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/role.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/role.inc @@ -21,7 +21,7 @@ $plugin = array( ); /** - * Settings form for the 'by role' access plugin + * Settings form for the 'by role' access plugin. */ function ctools_role_ctools_access_settings($form, &$form_state, $conf) { $form['settings']['rids'] = array( @@ -76,4 +76,3 @@ function ctools_role_ctools_access_summary($conf, $context) { return format_plural(count($names), '@identifier has role "@roles"', '@identifier has one of "@roles"', array('@roles' => implode(', ', $names), '@identifier' => $context->identifier)); } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/site_language.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/site_language.inc index 9ff2f70..5b7b724 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/site_language.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/site_language.inc @@ -22,7 +22,7 @@ if (module_exists('locale')) { } /** - * Settings form for the 'by site_language' access plugin + * Settings form for the 'by site_language' access plugin. */ function ctools_site_language_ctools_access_settings($form, &$form_state, $conf) { $options = array( @@ -84,4 +84,3 @@ function ctools_site_language_ctools_access_summary($conf, $context) { return format_plural(count($names), 'Site language is "@languages"', 'Site language is one of "@languages"', array('@languages' => implode(', ', $names))); } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/string_equal.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/string_equal.inc index ad1c88d..feb48e9 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/string_equal.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/string_equal.inc @@ -2,7 +2,7 @@ /** * @file - * Plugin to provide access control/visibility based on specified context string matching user-specified string + * Plugin to provide access control/visibility based on specified context string matching user-specified string. */ $plugin = array( @@ -16,7 +16,7 @@ $plugin = array( ); /** - * Settings form + * Settings form. */ function ctools_string_equal_ctools_access_settings($form, &$form_state, $conf) { $form['settings']['operator'] = array( @@ -47,7 +47,7 @@ function ctools_string_equal_ctools_access_settings($form, &$form_state, $conf) } /** - * Check for access + * Check for access. */ function ctools_string_equal_ctools_access_check($conf, $context) { if (empty($context) || empty($context->data)) { @@ -66,29 +66,34 @@ function ctools_string_equal_ctools_access_check($conf, $context) { switch ($conf['operator']) { case '=': return $string === $value; + case '!=': return $string !== $value; + case 'regex': return preg_match($value, $string); + case '!regex': return !preg_match($value, $string); } } /** - * Provide a summary description based upon the specified context + * Provide a summary description based upon the specified context. */ function ctools_string_equal_ctools_access_summary($conf, $context) { $values = array('@identifier' => $context->identifier, '@value' => $conf['value']); switch ($conf['operator']) { case '=': return t('@identifier is "@value"', $values); + case '!=': return t('@identifier is not "@value"', $values); + case 'regex': return t('@identifier matches "@value"', $values); + case '!regex': return t('@identifier does not match "@value"', $values); } } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/string_length.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/string_length.inc index 91abf22..3af504b 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/string_length.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/string_length.inc @@ -57,14 +57,19 @@ function ctools_string_length_ctools_access_check($conf, $context) { switch ($conf['operator']) { case '<': return $length < $conf['length']; + case '<=': return $length <= $conf['length']; + case '=': return $length == $conf['length']; + case '!=': return $length != $conf['length']; + case '>': return $length > $conf['length']; + case '>=': return $length >= $conf['length']; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/term.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/term.inc index 36e70de..50ec0d9 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/term.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/term.inc @@ -22,7 +22,7 @@ $plugin = array( ); /** - * Settings form for the 'by term' access plugin + * Settings form for the 'by term' access plugin. */ function ctools_term_ctools_access_settings($form, &$form_state, $conf) { // If no configuration was saved before, set some defaults. @@ -51,7 +51,6 @@ function ctools_term_ctools_access_settings($form, &$form_state, $conf) { // A note: Dependency works strangely on these forms as they have never been // updated to a more modern system so they are not individual forms of their // own like the content types. - $form['settings']['#tree'] = TRUE; // Loop over each of the configured vocabularies. @@ -124,6 +123,8 @@ function ctools_term_ctools_access_summary($conf, $context) { return format_plural(count($terms), '@term can be the term "@terms"', '@term can be one of these terms: @terms', - array('@terms' => implode(', ', $terms), - '@term' => $context->identifier)); + array( + '@terms' => implode(', ', $terms), + '@term' => $context->identifier, + )); } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/term_has_parent.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/term_has_parent.inc index a079e92..16f4fbf 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/term_has_parent.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/term_has_parent.inc @@ -1,4 +1,5 @@ !empty($conf['vid_' . $vid]) ? $conf['vid_' . $vid] : '', '#size' => 10, '#multiple' => TRUE, - //@todo: Remove the following workaround when the following patch is in core. {@see:http://drupal.org/node/1117526} + // @todo: Remove the following workaround when the following patch is in core. {@see:http://drupal.org/node/1117526} '#name' => sprintf("settings[%u][]", $vid), '#attributes' => array('multiple' => 'multiple'), ); @@ -71,7 +71,6 @@ function ctools_term_has_parent_ctools_access_settings($form, &$form_state, $con foreach (taxonomy_get_tree($vocabulary->vid) as $term) { $terms[$term->tid] = str_repeat('-', $term->depth) . ($term->depth ? ' ' : '') . $term->name; } - //$form['settings']['vid_' . $vid]['#type'] = 'select'; $form['settings']['vid_' . $vid]['#type'] = 'checkboxes'; $form['settings']['vid_' . $vid]['#options'] = $terms; unset($terms); @@ -116,37 +115,36 @@ function ctools_term_has_parent_ctools_access_check($conf, $context) { // we'll start looking up the hierarchy from our context term id. $current_term = $context->data->tid; - $term=''; + $term = ''; - // scan up the tree. - while (true) { - // select parent as term_parent to avoid PHP5 complications with the parent keyword - //@todo: Find a way to reduce the number of queries required for really deep hierarchies. - $term = db_query("SELECT parent AS term_parent, tid AS tid FROM {taxonomy_term_hierarchy} th WHERE th.tid = :tid", array(':tid'=>$current_term))->fetchObject(); + // Scan up the tree. + while (TRUE) { + // Select parent as term_parent to avoid PHP5 complications with the parent keyword. + // @todo: Find a way to reduce the number of queries required for really deep hierarchies. + $term = db_query("SELECT parent AS term_parent, tid AS tid FROM {taxonomy_term_hierarchy} th WHERE th.tid = :tid", array(':tid' => $current_term))->fetchObject(); - // if no term is found, get out of the loop + // If no term is found, get out of the loop. if (!$term || empty($term->tid)) { break; } - // check the term selected, if the user asked it to. + // Check the term selected, if the user asked it to. if (!empty($conf['include_self']) && isset($conf['vid_' . $vid][$term->tid])) { return TRUE; } - // did we find the parent TID we were looking for? + // Did we find the parent TID we were looking for? if (isset($conf['vid_' . $vid][$term->tid])) { // YES, we're done! return TRUE; } // Nope, we didn't find it. - // If this is the top of the hierarchy, stop scanning. - if ($term->term_parent==0) { + if ($term->term_parent == 0) { break; } - // update the parent, and keep scanning. + // Update the parent, and keep scanning. $current_term = $term->term_parent; } @@ -157,7 +155,7 @@ function ctools_term_has_parent_ctools_access_check($conf, $context) { * Provide a summary description based upon the checked terms. */ function ctools_term_has_parent_ctools_access_summary($conf, $context) { - $vid = (int)$conf['vid']; + $vid = (int) $conf['vid']; $terms = array(); foreach ($conf['vid_' . $vid] as $tid) { $term = taxonomy_term_load($tid); @@ -167,6 +165,8 @@ function ctools_term_has_parent_ctools_access_summary($conf, $context) { return format_plural(count($terms), '@term can have the parent "@terms"', '@term can have one of these parents: @terms', - array('@terms' => implode(', ', $terms), - '@term' => $context->identifier)); + array( + '@terms' => implode(', ', $terms), + '@term' => $context->identifier, + )); } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/term_parent.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/term_parent.inc index acbaf87..27375df 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/term_parent.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/term_parent.inc @@ -22,7 +22,7 @@ $plugin = array( ); /** - * Settings form for the 'by parent term' access plugin + * Settings form for the 'by parent term' access plugin. */ function ctools_term_parent_ctools_access_settings($form, &$form_state, $conf) { // If no configuration was saved before, set some defaults. diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/term_vocabulary.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/term_vocabulary.inc index b003138..8e9a0ca 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/term_vocabulary.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/term_vocabulary.inc @@ -2,7 +2,7 @@ /** * @file - * Plugin to provide access control based upon term vocabulary + * Plugin to provide access control based upon term vocabulary. */ /** @@ -20,12 +20,12 @@ $plugin = array( 'required context' => new ctools_context_required(t('Vocabulary'), array( 'taxonomy_term', 'terms', - 'taxonomy_vocabulary' + 'taxonomy_vocabulary', )), ); /** - * Settings form for the 'by term_vocabulary' access plugin + * Settings form for the 'by term_vocabulary' access plugin. */ function ctools_term_vocabulary_ctools_access_settings($form, &$form_state, $conf) { $options = array(); @@ -101,7 +101,7 @@ function ctools_term_vocabulary_ctools_access_summary($conf, $context) { return format_plural(count($names), '@identifier vocabulary is "@machine_names"', '@identifier vocabulary is one of "@machine_names"', array( '@machine_names' => implode(', ', $names), - '@identifier' => $context->identifier + '@identifier' => $context->identifier, )); } @@ -123,5 +123,3 @@ function _ctools_term_vocabulary_ctools_access_map_vids(&$conf) { } } } - - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/access/theme.inc b/sites/all/modules/contrib/dev/ctools/plugins/access/theme.inc index 4f4be6d..67e3e6f 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/access/theme.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/access/theme.inc @@ -19,7 +19,7 @@ $plugin = array( ); /** - * Settings form for the 'by theme' access plugin + * Settings form for the 'by theme' access plugin. */ function ctools_theme_ctools_access_settings($form, &$form_state, $conf) { $themes = array(); @@ -44,10 +44,10 @@ function ctools_theme_ctools_access_check($conf, $context) { if (!empty($GLOBALS['theme'])) { $theme = $GLOBALS['theme']; } - else if (!empty($GLOBALS['custom_theme'])) { + elseif (!empty($GLOBALS['custom_theme'])) { $theme = $GLOBALS['custom_theme']; } - else if (!empty($GLOBALS['user']->theme)) { + elseif (!empty($GLOBALS['user']->theme)) { $theme = $GLOBALS['user']->theme; } else { diff --git a/sites/all/modules/contrib/dev/ctools/plugins/arguments/entity_id.inc b/sites/all/modules/contrib/dev/ctools/plugins/arguments/entity_id.inc index 214dc55..73bfe1e 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/arguments/entity_id.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/arguments/entity_id.inc @@ -2,8 +2,7 @@ /** * @file - * - * Plugin to provide an argument handler for all entity ids + * Plugin to provide an argument handler for all entity ids. */ /** @@ -17,7 +16,7 @@ $plugin = array( 'get child' => 'ctools_argument_entity_id_get_child', 'get children' => 'ctools_argument_entity_id_get_children', 'default' => array( - 'entity_id' => '' + 'entity_id' => '', ), 'placeholder form' => 'ctools_argument_entity_id_ctools_argument_placeholder', ); @@ -41,6 +40,7 @@ function ctools_argument_entity_id_get_children($original_plugin, $parent) { $plugins[$plugin_id] = $plugin; } drupal_alter('ctools_entity_contexts', $plugins); + return $plugins; } @@ -60,9 +60,12 @@ function ctools_argument_entity_id_context($arg = NULL, $conf = NULL, $empty = F return ctools_context_create('entity:' . $entity_type, $arg); } + // Trim spaces and other garbage. + $arg = trim($arg); + if (!is_numeric($arg)) { $preg_matches = array(); - $match = preg_match('/\[id: (\d+)\]/', $arg, $preg_matches); + $match = preg_match('/\[id: (\d+)\]/', $arg, $preg_matches); if (!$match) { $match = preg_match('/^id: (\d+)/', $arg, $preg_matches); } @@ -70,18 +73,18 @@ function ctools_argument_entity_id_context($arg = NULL, $conf = NULL, $empty = F if ($match) { $id = $preg_matches[1]; } - if (is_numeric($id)) { + if (isset($id) && is_numeric($id)) { return ctools_context_create('entity:' . $entity_type, $id); } return FALSE; } - $entity = entity_load($entity_type, array($arg)); - if (!$entity) { + $entities = entity_load($entity_type, array($arg)); + if (empty($entities)) { return FALSE; } - return ctools_context_create('entity:' . $entity_type, $entity[$arg]); + return ctools_context_create('entity:' . $entity_type, reset($entities)); } function ctools_argument_entity_id_settings_form(&$form, &$form_state, $conf) { @@ -119,7 +122,7 @@ function ctools_argument_entity_id_settings_form(&$form, &$form_state, $conf) { $form['settings']['entity_id'] = array( '#type' => 'value', - '#value' => $conf['entity_id'], + '#value' => isset($conf['entity_id']) ? $conf['entity_id'] : '', ); $form['settings']['entity_type'] = array( @@ -132,11 +135,12 @@ function ctools_argument_entity_id_settings_form(&$form, &$form_state, $conf) { function ctools_argument_entity_id_ctools_argument_placeholder($conf) { $conf = array( - '#title' => t('Enter the title or ID of a @entity entity', array('@entity' => $conf['keyword'])), - '#type' => 'textfield', - '#maxlength' => 512, + '#title' => t('Enter the title or ID of a @entity entity', array('@entity' => $conf['keyword'])), + '#type' => 'textfield', + '#maxlength' => 512, '#autocomplete_path' => 'ctools/autocomplete/' . $conf['keyword'], - '#weight' => -10, + '#weight' => -10, ); + return $conf; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/arguments/nid.inc b/sites/all/modules/contrib/dev/ctools/plugins/arguments/nid.inc index 9aaec0e..cbb56d3 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/arguments/nid.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/arguments/nid.inc @@ -2,8 +2,7 @@ /** * @file - * - * Plugin to provide an argument handler for a node id + * Plugin to provide an argument handler for a node id. */ /** @@ -47,4 +46,3 @@ function ctools_argument_nid_context($arg = NULL, $conf = NULL, $empty = FALSE) return ctools_context_create('node', $node); } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/arguments/node_add.inc b/sites/all/modules/contrib/dev/ctools/plugins/arguments/node_add.inc index c811311..f249a94 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/arguments/node_add.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/arguments/node_add.inc @@ -2,8 +2,7 @@ /** * @file - * - * Plugin to provide an argument handler for a Node add form + * Plugin to provide an argument handler for a Node add form. */ /** @@ -12,7 +11,7 @@ */ $plugin = array( 'title' => t("Node add form: node type"), - // keyword to use for %substitution + // Keyword to use for %substitution. 'keyword' => 'node_type', 'description' => t('Creates a node add form context from a node type argument.'), 'context' => 'ctools_node_add_context', @@ -29,4 +28,3 @@ function ctools_node_add_context($arg = NULL, $conf = NULL, $empty = FALSE) { return ctools_context_create('node_add_form', $arg); } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/arguments/node_edit.inc b/sites/all/modules/contrib/dev/ctools/plugins/arguments/node_edit.inc index c7cdf29..8bd0768 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/arguments/node_edit.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/arguments/node_edit.inc @@ -2,8 +2,7 @@ /** * @file - * - * Plugin to provide an argument handler for a Node edit form + * Plugin to provide an argument handler for a Node edit form. */ /** @@ -12,7 +11,7 @@ */ $plugin = array( 'title' => t("Node edit form: node ID"), - // keyword to use for %substitution + // Keyword to use for %substitution. 'keyword' => 'node', 'description' => t('Creates a node edit form context from a node ID argument.'), 'context' => 'ctools_node_edit_context', @@ -48,4 +47,3 @@ function ctools_node_edit_context($arg = NULL, $conf = NULL, $empty = FALSE) { // This will perform a node_access check, so we don't have to. return ctools_context_create('node_edit_form', $node); } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/arguments/rid.inc b/sites/all/modules/contrib/dev/ctools/plugins/arguments/rid.inc index 2661153..d0cc30c 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/arguments/rid.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/arguments/rid.inc @@ -2,8 +2,7 @@ /** * @file - * - * Plugin to provide an argument handler for a node revision id + * Plugin to provide an argument handler for a node revision id. */ /** @@ -47,4 +46,3 @@ function ctools_argument_rid_context($arg = NULL, $conf = NULL, $empty = FALSE) return ctools_context_create('node', $node); } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/arguments/string.inc b/sites/all/modules/contrib/dev/ctools/plugins/arguments/string.inc index ed4ffbb..005e40e 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/arguments/string.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/arguments/string.inc @@ -2,16 +2,16 @@ /** * @file - * - * Plugin to provide an argument handler for a raw string + * Plugin to provide an argument handler for a raw string. */ + /** * Plugins are described by creating a $plugin array which will be used * by the system that includes this file. */ $plugin = array( 'title' => t("String"), - // keyword to use for %substitution + // Keyword to use for %substitution. 'keyword' => 'string', 'description' => t('A string is a minimal context that simply holds a string that can be used for some other purpose.'), 'settings form' => 'ctools_string_settings_form', @@ -20,7 +20,8 @@ $plugin = array( '#type' => 'textfield', '#description' => t('Enter a value for this argument'), ), - 'path placeholder' => 'ctools_string_path_placeholder', // This is in pagemanager. +// This is in pagemanager. + 'path placeholder' => 'ctools_string_path_placeholder', ); /** @@ -39,7 +40,7 @@ function ctools_string_context($arg = NULL, $conf = NULL, $empty = FALSE) { } /** - * Settings form for the argument + * Settings form for the argument. */ function ctools_string_settings_form(&$form, &$form_state, $conf) { $form['settings']['use_tail'] = array( @@ -48,7 +49,6 @@ function ctools_string_settings_form(&$form, &$form_state, $conf) { '#default_value' => !empty($conf['use_tail']), '#description' => t('If checked, this string will include all arguments. For example, if the path is "path/%" and the user visits "path/foo/bar", if this is not checked the string will be "foo". If it is checked the string will be "foo/bar".'), ); -// return $form; } /** @@ -61,4 +61,4 @@ function ctools_string_path_placeholder($argument) { else { return '%pm_arg_tail'; } -} \ No newline at end of file +} diff --git a/sites/all/modules/contrib/dev/ctools/plugins/arguments/term.inc b/sites/all/modules/contrib/dev/ctools/plugins/arguments/term.inc index 868c8aa..f72e93d 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/arguments/term.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/arguments/term.inc @@ -2,8 +2,7 @@ /** * @file - * - * Plugin to provide an argument handler for a Taxonomy term + * Plugin to provide an argument handler for a Taxonomy term. */ /** @@ -12,12 +11,13 @@ */ $plugin = array( 'title' => t("Taxonomy term: ID"), - // keyword to use for %substitution + // Keyword to use for %substitution. 'keyword' => 'term', 'description' => t('Creates a single taxonomy term from a taxonomy ID or taxonomy term name.'), 'context' => 'ctools_term_context', 'default' => array('input_form' => 'tid', 'breadcrumb' => TRUE, 'transform' => FALSE), 'settings form' => 'ctools_term_settings_form', + 'settings form validate' => 'ctools_term_settings_form_validate', 'placeholder form' => 'ctools_term_ctools_argument_placeholder', 'breadcrumb' => 'ctools_term_breadcrumb', ); @@ -31,6 +31,16 @@ function ctools_term_context($arg = NULL, $conf = NULL, $empty = FALSE) { return ctools_context_create_empty('entity:taxonomy_term'); } + if (isset($conf['vocabularies'])) { + $vocabularies = $conf['vocabularies']; + } + else { + $vids = isset($conf['vids']) ? $conf['vids'] : array(); + + // Convert legacy use of vids to machine names. + $vocabularies = _ctools_term_vocabulary_machine_name_convert($vids); + } + if (is_object($arg)) { $term = $arg; } @@ -50,14 +60,13 @@ function ctools_term_context($arg = NULL, $conf = NULL, $empty = FALSE) { } $terms = taxonomy_get_term_by_name($arg); - - $conf['vids'] = is_array($conf['vids']) ? array_filter($conf['vids']) : NULL; - if ((count($terms) > 1) && isset($conf['vids'])) { + // If only one term is found, fall through to vocabulary check below. + if ((count($terms) > 1) && $vocabularies) { foreach ($terms as $potential) { - foreach ($conf['vids'] as $vid => $active) { - if ($active && $potential->vid == $vid) { + foreach ($vocabularies as $machine_name) { + if ($potential->vocabulary_machine_name == $machine_name) { $term = $potential; - // break out of the foreaches AND the case + // Break out of the foreaches AND the case. break 3; } } @@ -72,7 +81,7 @@ function ctools_term_context($arg = NULL, $conf = NULL, $empty = FALSE) { } } - if (!empty($conf['vids']) && array_filter($conf['vids']) && empty($conf['vids'][$term->vid])) { + if ($vocabularies && !isset($vocabularies[$term->vocabulary_machine_name])) { return NULL; } @@ -82,7 +91,7 @@ function ctools_term_context($arg = NULL, $conf = NULL, $empty = FALSE) { } /** - * Settings form for the argument + * Settings form for the argument. */ function ctools_term_settings_form(&$form, &$form_state, $conf) { // @todo allow synonym use like Views does. @@ -98,13 +107,20 @@ function ctools_term_settings_form(&$form, &$form_state, $conf) { $vocabularies = taxonomy_get_vocabularies(); $options = array(); foreach ($vocabularies as $vid => $vocab) { - $options[$vid] = $vocab->name; + $options[$vocab->machine_name] = $vocab->name; } - $form['settings']['vids'] = array( + + // Fallback on legacy 'vids', when no vocabularies are available. + if (empty($conf['vocabularies']) && !empty($conf['vids'])) { + $conf['vocabularies'] = _ctools_term_vocabulary_machine_name_convert(array_filter($conf['vids'])); + unset($conf['vids']); + } + + $form['settings']['vocabularies'] = array( '#title' => t('Limit to these vocabularies'), '#type' => 'checkboxes', '#options' => $options, - '#default_value' => !empty($conf['vids']) ? $conf['vids'] : array(), + '#default_value' => !empty($conf['vocabularies']) ? $conf['vocabularies'] : array(), '#description' => t('If no vocabularies are checked, terms from all vocabularies will be accepted.'), ); @@ -120,7 +136,12 @@ function ctools_term_settings_form(&$form, &$form_state, $conf) { '#type' => 'checkbox', '#default_value' => !empty($conf['transform']), ); -// return $form; +} + +function ctools_term_settings_form_validate(&$form, &$form_state) { + // Filter the selected vocabularies to avoid storing redundant data. + $vocabularies = array_filter($form_state['values']['settings']['vocabularies']); + form_set_value($form['settings']['vocabularies'], $vocabularies, $form_state); } /** @@ -134,6 +155,7 @@ function ctools_term_ctools_argument_placeholder($conf) { '#type' => 'textfield', '#description' => t('Enter a taxonomy term ID.'), ); + case 'term': return array( '#type' => 'textfield', @@ -161,3 +183,21 @@ function ctools_term_breadcrumb($conf, $context) { $breadcrumb = array_merge(drupal_get_breadcrumb(), array_reverse($breadcrumb)); drupal_set_breadcrumb($breadcrumb); } + +/** + * Helper function to convert convert legacy vocabulary ids into machine names. + * + * @param array $vids + * Array of either vids. + * + * @return array + * A keyed array of machine names. + */ +function _ctools_term_vocabulary_machine_name_convert($vids) { + $vocabularies = taxonomy_vocabulary_load_multiple($vids); + $return = array(); + foreach ($vocabularies as $vocabulary) { + $return[$vocabulary->machine_name] = $vocabulary->machine_name; + } + return $return; +} diff --git a/sites/all/modules/contrib/dev/ctools/plugins/arguments/terms.inc b/sites/all/modules/contrib/dev/ctools/plugins/arguments/terms.inc index 4298ea9..b816e1c 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/arguments/terms.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/arguments/terms.inc @@ -2,8 +2,7 @@ /** * @file - * - * Plugin to provide an argument handler for a Taxonomy term + * Plugin to provide an argument handler for a Taxonomy term. */ /** @@ -12,7 +11,7 @@ */ $plugin = array( 'title' => t("Taxonomy term (multiple): ID"), - // keyword to use for %substitution + // Keyword to use for %substitution. 'keyword' => 'term', 'description' => t('Creates a group of taxonomy terms from a list of tids separated by a comma or a plus sign. In general the first term of the list will be used for panes.'), 'context' => 'ctools_terms_context', @@ -45,7 +44,7 @@ function ctools_terms_context($arg = NULL, $conf = NULL, $empty = FALSE) { } /** - * Settings form for the argument + * Settings form for the argument. */ function ctools_terms_settings_form(&$form, &$form_state, $conf) { $form['settings']['breadcrumb'] = array( @@ -54,7 +53,6 @@ function ctools_terms_settings_form(&$form, &$form_state, $conf) { '#default_value' => !empty($conf['breadcrumb']), '#description' => t('If checked, taxonomy term parents will appear in the breadcrumb trail.'), ); -// return $form; } /** @@ -65,6 +63,7 @@ function ctools_terms_breadcrumb($conf, $context) { return; } + $current = new stdClass(); $current->tid = $context->tids[0]; $breadcrumb = array(); while ($parents = taxonomy_get_parents($current->tid)) { diff --git a/sites/all/modules/contrib/dev/ctools/plugins/arguments/uid.inc b/sites/all/modules/contrib/dev/ctools/plugins/arguments/uid.inc index f9d5315..c6ca3cc 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/arguments/uid.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/arguments/uid.inc @@ -2,8 +2,7 @@ /** * @file - * - * Plugin to provide an argument handler for a user id + * Plugin to provide an argument handler for a user id. */ /** @@ -12,7 +11,7 @@ */ $plugin = array( 'title' => t("User: ID"), - // keyword to use for %substitution + // Keyword to use for %substitution. 'keyword' => 'user', 'description' => t('Creates a user context from a user ID argument.'), 'context' => 'ctools_argument_uid_context', @@ -21,7 +20,8 @@ $plugin = array( '#description' => t('Enter the user ID of a user for this argument'), ), 'default' => array('to_arg' => TRUE), - 'path placeholder' => '%pm_uid_arg', // This is in pagemanager. +// This is in pagemanager. + 'path placeholder' => '%pm_uid_arg', 'path placeholder to_arg' => TRUE, 'no ui' => TRUE, ); diff --git a/sites/all/modules/contrib/dev/ctools/plugins/arguments/user_edit.inc b/sites/all/modules/contrib/dev/ctools/plugins/arguments/user_edit.inc index 32b2b81..9c52b58 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/arguments/user_edit.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/arguments/user_edit.inc @@ -2,8 +2,7 @@ /** * @file - * - * Plugin to provide an argument handler for a Taxonomy term + * Plugin to provide an argument handler for a Taxonomy term. */ /** @@ -12,7 +11,7 @@ */ $plugin = array( 'title' => t("User edit form: User ID"), - // keyword to use for %substitution + // Keyword to use for %substitution. 'keyword' => 'user', 'description' => t('Creates a user edit form context from a user ID argument.'), 'context' => 'ctools_user_edit_context', @@ -30,18 +29,18 @@ function ctools_user_edit_context($arg = NULL, $conf = NULL, $empty = FALSE) { if ($empty) { return ctools_context_create_empty('user_edit_form'); } - if(is_object($arg)){ + if (is_object($arg)) { return ctools_context_create('user_edit_form', $arg); } if (!is_numeric($arg)) { return FALSE; } - $account= user_load($arg); + $account = user_load($arg); if (!$account) { return NULL; } // This will perform a node_access check, so we don't have to. return ctools_context_create('user_edit_form', $account); -} \ No newline at end of file +} diff --git a/sites/all/modules/contrib/dev/ctools/plugins/arguments/user_name.inc b/sites/all/modules/contrib/dev/ctools/plugins/arguments/user_name.inc index f6f3b46..ed74d9d 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/arguments/user_name.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/arguments/user_name.inc @@ -2,8 +2,7 @@ /** * @file - * - * Plugin to provide an argument handler for a username + * Plugin to provide an argument handler for a username. */ /** @@ -12,7 +11,7 @@ */ $plugin = array( 'title' => t("User: name"), - // keyword to use for %substitution + // Keyword to use for %substitution. 'keyword' => 'user', 'description' => t('Creates a user context from a user name.'), 'context' => 'ctools_argument_user_name_context', @@ -42,6 +41,3 @@ function ctools_argument_user_name_context($arg = NULL, $conf = NULL, $empty = F } return ctools_context_create('user', $account); } - - - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/arguments/vid.inc b/sites/all/modules/contrib/dev/ctools/plugins/arguments/vid.inc index 064b22d..db68acd 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/arguments/vid.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/arguments/vid.inc @@ -2,8 +2,7 @@ /** * @file - * - * Plugin to provide an argument handler for a vocabulary id + * Plugin to provide an argument handler for a vocabulary id. */ /** @@ -12,7 +11,7 @@ */ $plugin = array( 'title' => t("Vocabulary: ID"), - // keyword to use for %substitution + // Keyword to use for %substitution. 'keyword' => 'vocabulary', 'description' => t('Creates a vocabulary context from a vocabulary ID argument.'), 'context' => 'ctools_vid_context', @@ -43,4 +42,3 @@ function ctools_vid_context($arg = NULL, $conf = NULL, $empty = FALSE) { return ctools_context_create('vocabulary', $vocabulary); } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/cache/export_ui.inc b/sites/all/modules/contrib/dev/ctools/plugins/cache/export_ui.inc index 53483a5..f21af10 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/cache/export_ui.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/cache/export_ui.inc @@ -6,7 +6,7 @@ */ $plugin = array( - // cache plugins are the rare plugin types that have no real UI but + // Cache plugins are the rare plugin types that have no real UI but // we're providing a title just in case. 'title' => t('Export UI wizard cache'), 'cache get' => 'ctools_cache_export_ui_cache_get', diff --git a/sites/all/modules/contrib/dev/ctools/plugins/cache/simple.inc b/sites/all/modules/contrib/dev/ctools/plugins/cache/simple.inc index 570398b..c44d798 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/cache/simple.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/cache/simple.inc @@ -6,7 +6,7 @@ */ $plugin = array( - // cache plugins are the rare plugin types that have no real UI but + // Cache plugins are the rare plugin types that have no real UI but // we're providing a title just in case. 'title' => t('Simple'), 'cache get' => 'ctools_cache_simple_cache_get', diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/block/block.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/block/block.inc index 4d4c31c..ff75c75 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/block/block.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/block/block.inc @@ -51,7 +51,7 @@ function ctools_block_content_type_content_types() { if ($module_blocks) { foreach ($module_blocks as $delta => $block) { $info = _ctools_block_content_type_content_type($module, $delta, $block); - // this check means modules can remove their blocks; particularly useful + // This check means modules can remove their blocks; particularly useful // if they offer the block some other way (like we do for views) if ($info) { $types["$module-$delta"] = $info; @@ -168,7 +168,7 @@ function ctools_block_content_type_render($subtype, $conf) { if ($module == 'block' && !empty($info) && isset($info->title)) { $block->title = $info->title; } - else if (isset($block->subject)) { + elseif (isset($block->subject)) { $block->title = $block->subject; } else { @@ -208,70 +208,6 @@ function ctools_block_content_type_edit_form_submit($form, &$form_state) { } } -/** - * Returns an edit form for a block. - */ -//function ctools_block_content_type_edit_form($id, $parents, $conf) { -// if (user_access('administer advanced pane settings')) { -// $form['block_visibility'] = array( -// '#type' => 'checkbox', -// '#title' => t('Use block visibility settings (see block config)'), -// '#default_value' => !empty($conf['block_visibility']), -// '#description' => t('If checked, the block visibility settings for this block will apply to this block.'), -// ); -// // Module-specific block configurations. -// if ($settings = module_invoke($module, 'block', 'configure', $delta)) { -// // Specifically modify a couple of core block forms. -// if ($module == 'block') { -// unset($settings['submit']); -// $settings['info']['#type'] = 'value'; -// $settings['info']['#value'] = $settings['info']['#default_value']; -// } -// ctools_admin_fix_block_tree($settings); -// $form['block_settings'] = array( -// '#type' => 'fieldset', -// '#title' => t('Block settings'), -// '#description' => t('Settings in this section are global and are for all blocks of this type, anywhere in the system.'), -// '#tree' => FALSE, -// ); -// -// -// $form['block_settings'] += $settings; -// } -// } -// -// return $form; -//} - -//function ctools_admin_submit_block(&$form_values) { -// if (!empty($form_values['block_settings'])) { -// module_invoke($form_values['module'], 'block', 'save', $form_values['delta'], $form_values['block_settings']); -// } -//} -// -///** -// * Because form api cannot collapse just part of a tree, and the block settings -// * assume no tree, we have to collapse the tree ourselves. -// */ -//function ctools_admin_fix_block_tree(&$form, $key = NULL) { -// if ($key) { -// if (!empty($form['#parents'])) { -// $form['#parents'] = array_merge(array('configuration', 'block_settings'), $form['#parents']); -// } -// else if (empty($form['#tree'])) { -// $form['#parents'] = array('configuration', 'block_settings', $key); -// } -// } -// -// if (isset($form['#type']) && $form['#type'] == 'textarea' && !empty($form['#rows']) && $form['#rows'] > 10) { -// $form['#rows'] = 10; -// } -// -// foreach (element_children($form) as $key) { -// ctools_admin_fix_block_tree($form[$key], $key); -// } -//} - /** * Returns the administrative title for a type. */ @@ -338,8 +274,10 @@ function ctools_default_block_info($module, $delta, &$info) { } } -// These are all on behalf of modules that don't implement ctools but that -// we care about. +/** + * These are all on behalf of modules that don't implement ctools but that + * we care about. + */ function menu_ctools_block_info($module, $delta, &$info) { $info['icon'] = 'icon_core_block_menu.png'; $info['category'] = t('Menus'); @@ -360,7 +298,7 @@ function forum_ctools_block_info($module, $delta, &$info) { break; default: - // safety net + // Safety net. ctools_default_block_info($module, $delta, $info); } } @@ -372,9 +310,9 @@ function profile_ctools_block_info($module, $delta, &$info) { } function book_ctools_block_info($module, $delta, &$info) { - // Hide the book navigation block which isn't as rich as what we can - // do with context. - $info = NULL; + $info['title'] = t('Book navigation menu'); + $info['icon'] = 'icon_core_block_menu.png'; + $info['category'] = t('Node'); } function blog_ctools_block_info($module, $delta, &$info) { @@ -455,7 +393,7 @@ function user_ctools_block_info($module, $delta, &$info) { break; default: - // safety net + // Safety net. ctools_default_block_info($module, $delta, $info); } } @@ -508,7 +446,7 @@ function ctools_user_login_pane_render($subtype, $conf, $panel_args, $contexts) return; } - $info = new stdClass; + $info = new stdClass(); $info->module = $module; $info->delta = $delta; @@ -537,7 +475,7 @@ function ctools_user_login_pane_render($subtype, $conf, $panel_args, $contexts) if ($module == 'block') { $block->title = $info->title; } - else if (isset($block->subject)) { + elseif (isset($block->subject)) { $block->title = $block->subject; } else { diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/comment/comment_created.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/comment/comment_created.inc new file mode 100644 index 0000000..b3f454a --- /dev/null +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/comment/comment_created.inc @@ -0,0 +1,76 @@ + TRUE, + 'title' => t('Comment created date'), + 'icon' => 'icon_comment.png', + 'description' => t('The date the referenced comment was created.'), + 'required context' => new ctools_context_required(t('Comment'), 'entity:comment'), + 'category' => t('Comment'), + 'defaults' => array( + 'format' => 'small', + ), +); + +/** + * Render the custom content type. + */ +function ctools_comment_created_content_type_render($subtype, $conf, $panel_args, $context) { + if (empty($context) || empty($context->data)) { + return; + } + + // Get a shortcut to the comment. + $comment = $context->data; + + // Build the content type block. + $block = new stdClass(); + $block->module = 'comment_created'; + $block->title = t('Created date'); + $block->content = format_date($comment->created, $conf['format']); + $block->delta = $comment->cid; + + return $block; +} + +/** + * Returns an edit form for custom type settings. + */ +function ctools_comment_created_content_type_edit_form($form, &$form_state) { + $conf = $form_state['conf']; + $date_types = array(); + + foreach (system_get_date_types() as $date_type => $definition) { + $date_types[$date_type] = format_date(REQUEST_TIME, $date_type); + } + $form['format'] = array( + '#title' => t('Date format'), + '#type' => 'select', + '#options' => $date_types, + '#default_value' => $conf['format'], + ); + return $form; +} + +/** + * Submit handler for the custom type settings form. + */ +function ctools_comment_created_content_type_edit_form_submit($form, &$form_state) { + // Copy everything from our defaults. + foreach (array_keys($form_state['plugin']['defaults']) as $key) { + $form_state['conf'][$key] = $form_state['values'][$key]; + } +} + +/** + * Returns the administrative title for a type. + */ +function ctools_comment_created_content_type_admin_title($subtype, $conf, $context) { + return t('"@s" created date', array('@s' => $context->identifier)); +} diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/comment/comment_links.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/comment/comment_links.inc index c8fefcc..ed454f9 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/comment/comment_links.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/comment/comment_links.inc @@ -1,9 +1,11 @@ TRUE, 'title' => t('Comment links'), @@ -26,10 +28,10 @@ function ctools_comment_links_content_type_render($subtype, $conf, $panel_args, return; } - $comment = isset($context->data) ? clone($context->data) : NULL; + $comment = isset($context->data) ? clone $context->data : NULL; $block = new stdClass(); $block->module = 'comment'; - $block->delta = $comment->cid; + $block->delta = $comment->cid; if (empty($comment)) { $block->delta = 'placeholder'; diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/comment/comment_reply_form.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/comment/comment_reply_form.inc index c05effb..a18ece9 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/comment/comment_reply_form.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/comment/comment_reply_form.inc @@ -2,7 +2,7 @@ /** * @file - * Ctools content-type plugin to provide a comment-reply form (replying either + * CTools content-type plugin to provide a comment-reply form (replying either * to a node or to another comment). */ @@ -14,21 +14,23 @@ if (module_exists('comment')) { 'icon' => 'icon_comment.png', 'description' => t('A form to add a new comment reply.'), 'required context' => array( - new ctools_context_required(t('Node'), 'node'), - new ctools_context_optional(t('Comment'), 'comment'), - ), + new ctools_context_required(t('Node'), 'node'), + new ctools_context_optional(t('Comment'), 'comment'), + ), 'category' => t('Comment'), 'render callback' => 'ctools_comment_reply_form_content_type_render', - 'defaults' => array('anon_links' => false), + 'defaults' => array('anon_links' => FALSE), ); } function ctools_comment_reply_form_content_type_render($subtype, $conf, $panel_args, $context) { - $comment = ($context[1]->identifier == t('No context')) ? NULL : clone($context[1]->data); + $comment = ($context[1]->identifier == t('No context')) ? NULL : clone $context[1]->data; $block = new stdClass(); $block->module = 'comments'; - if ($comment) $block->delta = $comment->cid; + if ($comment) { + $block->delta = $comment->cid; + } $block->title = t('Add comment'); $node = $context[0]->data; diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/contact/contact.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/contact/contact.inc index 63283f5..bac30c4 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/contact/contact.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/contact/contact.inc @@ -1,5 +1,9 @@ module = 'contact'; - $block->delta = 'form'; - $block->title = t('Contact'); + $block = new stdClass(); + $block->module = 'contact'; + $block->delta = 'form'; + $block->title = t('Contact'); module_load_include('inc', 'contact', 'contact.pages'); $block->content = drupal_get_form('contact_site_form'); @@ -45,11 +49,6 @@ function ctools_contact_content_type_edit_form($form, &$form_state) { */ function ctools_contact_content_type_edit_form_submit($form, &$form_state) { // Copy everything from our defaults. -/* - foreach (array_keys($form_state['plugin']['defaults']) as $key) { - $form_state['conf'][$key] = $form_state['values'][$key]; - } -*/ } /** diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/contact/user_contact.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/contact/user_contact.inc index 9b3726a..5bd3657 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/contact/user_contact.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/contact/user_contact.inc @@ -1,5 +1,9 @@ module = 'contact'; - $block->delta = 'form'; - $block->title = t('Contact @name', array('@name' => $context->data->name)); + $block = new stdClass(); + $block->module = 'contact'; + $block->delta = 'form'; + $block->title = t('Contact @name', array('@name' => $context->data->name)); module_load_include('inc', 'contact', 'contact.pages'); $block->content = drupal_get_form('contact_personal_form', $context->data); @@ -51,11 +55,6 @@ function ctools_user_contact_content_type_edit_form($form, &$form_state) { */ function ctools_user_contact_content_type_edit_form_submit(&$form, &$form_state) { // Copy everything from our defaults. -/* - foreach (array_keys($form_state['plugin']['defaults']) as $key) { - $form_state['conf'][$key] = $form_state['values'][$key]; - } -*/ } /** diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/custom/custom.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/custom/custom.inc index ac2f2a3..84fe3c5 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/custom/custom.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/custom/custom.inc @@ -16,7 +16,7 @@ $plugin = array( 'title' => t('Custom content'), 'no title override' => TRUE, - 'defaults' => array('admin_title' => '', 'title' => '', 'body' => '', 'format' => filter_default_format(), 'substitute' => TRUE), + 'defaults' => array('admin_title' => '', 'title' => '', 'title_heading' => 'h2', 'body' => '', 'format' => filter_default_format(), 'substitute' => TRUE), 'js' => array('misc/autocomplete.js', 'misc/textarea.js', 'misc/collapse.js'), // Make sure the edit form is only used for some subtypes. 'edit form' => '', @@ -135,6 +135,7 @@ function ctools_custom_content_type_get_conf($subtype, $conf) { $settings = array( 'admin_title' => t('Missing/deleted content'), 'title' => '', + 'title_heading' => '', 'body' => '', 'format' => filter_default_format(), 'substitute' => TRUE, @@ -177,9 +178,10 @@ function ctools_custom_content_type_render($subtype, $conf, $args, $contexts) { static $delta = 0; - $block = new stdClass(); - $block->subtype = ++$delta; - $block->title = filter_xss_admin($settings['title']); + $block = new stdClass(); + $block->subtype = ++$delta; + $block->title = filter_xss_admin($settings['title']); + $block->title_heading = isset($settings['title_heading']) ? $settings['title_heading'] : 'h2'; // Add keyword substitutions if we were configured to do so. $content = $settings['body']; @@ -267,7 +269,8 @@ function ctools_custom_content_type_edit_form($form, &$form_state) { $form_state['settings'] = $settings; if ($settings['custom_type'] == 'fixed') { - return $form; // no form for this case. + // No form for this case. + return $form; } $form['admin_title'] = array( @@ -277,10 +280,36 @@ function ctools_custom_content_type_edit_form($form, &$form_state) { '#description' => t('This title will be used administratively to identify this pane. If blank, the regular title will be used.'), ); + // Copy over the title override settings for a title heading. + $form['aligner_start'] = array( + '#markup' => '
    ', + ); + $form['title'] = array( '#type' => 'textfield', '#default_value' => $settings['title'], '#title' => t('Title'), + '#id' => 'override-title-textfield', + ); + + $form['title_heading'] = array( + '#type' => 'select', + '#default_value' => isset($settings['title_heading']) ? $settings['title_heading'] : 'h2', + '#options' => array( + 'h1' => t('h1'), + 'h2' => t('h2'), + 'h3' => t('h3'), + 'h4' => t('h4'), + 'h5' => t('h5'), + 'h6' => t('h6'), + 'div' => t('div'), + 'span' => t('span'), + ), + '#id' => 'override-title-heading', + ); + + $form['aligner_stop'] = array( + '#markup' => '
    ', ); $form['body'] = array( @@ -291,7 +320,7 @@ function ctools_custom_content_type_edit_form($form, &$form_state) { ); if (!empty($form_state['contexts'])) { - // Set extended description if both CCK and Token modules are enabled, notifying of unlisted keywords + // Set extended description if both CCK and Token modules are enabled, notifying of unlisted keywords. if (module_exists('content') && module_exists('token')) { $description = t('If checked, context keywords will be substituted in this content. Note that CCK fields may be used as keywords using patterns like %node:field_name-formatted.'); } @@ -388,7 +417,7 @@ function ctools_custom_content_type_edit_form_validate(&$form, &$form_state) { form_error($form['name'], t('Name is required.')); } - // Check for string identifier sanity + // Check for string identifier sanity. if (!preg_match('!^[a-z0-9_]+$!', $form_state['values']['name'])) { form_error($form['name'], t('The name can only consist of lowercase letters, underscores, and numbers.')); return; @@ -398,7 +427,7 @@ function ctools_custom_content_type_edit_form_validate(&$form, &$form_state) { return; } - // Check for name collision + // Check for name collision. if ($form_state['values']['name'] == 'custom' || (ctools_export_crud_load('ctools_custom_content', $form_state['values']['name']))) { form_error($form['name'], t('Content with this name already exists. Please choose another name or delete the existing item before creating a new one.')); } @@ -418,7 +447,7 @@ function ctools_custom_content_type_edit_form_submit($form, &$form_state) { } // If the 'reusable' checkbox was checked, we will create a new // custom content and give it the proper values. - else if (!empty($form_state['values']['reusable'])) { + elseif (!empty($form_state['values']['reusable'])) { $content = ctools_export_crud_new('ctools_custom_content'); $content->name = $form_state['values']['name']; _ctools_custom_content_type_edit_save($content, $form_state); @@ -426,7 +455,6 @@ function ctools_custom_content_type_edit_form_submit($form, &$form_state) { } else { // Otherwise, just save values into $conf normally. - foreach (array_keys($form_state['plugin']['defaults']) as $key) { $form_state['conf'][$key] = isset($form_state['values'][$key]) ? $form_state['values'][$key] : $form_state['plugin']['defaults'][$key]; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/entity_context/entity_field.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/entity_context/entity_field.inc index 5861d1c..80d24ef 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/entity_context/entity_field.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/entity_context/entity_field.inc @@ -34,6 +34,14 @@ function ctools_entity_field_content_type_content_types() { return $types; } + $cache_key = 'ctools_entity_field_content_type_content_types'; + if ($cache = cache_get($cache_key)) { + $types = $cache->data; + if (!empty($types)) { + return $types; + } + } + // This will hold all the individual field content types. $context_types = array(); $entities = entity_get_info(); @@ -82,12 +90,14 @@ function ctools_entity_field_content_type_content_types() { unset($context_types[$key]['types']); } + cache_set($cache_key, $types); + return $types; } /** -* Render the custom content type. -*/ + * Render the custom content type. + */ function ctools_entity_field_content_type_render($subtype, $conf, $panel_args, $context) { if (empty($context) || empty($context->data)) { return; @@ -127,34 +137,37 @@ function ctools_entity_field_content_type_render($subtype, $conf, $panel_args, $ $field_settings['settings'] = $conf['formatter_settings']; } - $all_values = field_get_items($entity_type, $entity, $field_name, $language); - if (!is_array($all_values)) { - // Do not render if the field is empty. - return; - } - - // Reverse values. - if (isset($conf['delta_reversed']) && $conf['delta_reversed']) { - $all_values = array_reverse($all_values, TRUE); - } - - if (isset($conf['delta_limit'])) { - $offset = intval($conf['delta_offset']); - $limit = !empty($conf['delta_limit']) ? $conf['delta_limit'] : NULL; - $all_values = array_slice($all_values, $offset, $limit, TRUE); - } - $clone = clone $entity; - $clone->{$field_name}[$language] = $all_values; + $all_values = field_get_items($entity_type, $entity, $field_name, $language); + + if (is_array($all_values)) { + // Reverse values. + if (isset($conf['delta_reversed']) && $conf['delta_reversed']) { + $all_values = array_reverse($all_values, TRUE); + } + + if (isset($conf['delta_limit'])) { + $offset = intval($conf['delta_offset']); + $limit = !empty($conf['delta_limit']) ? $conf['delta_limit'] : NULL; + $all_values = array_slice($all_values, $offset, $limit, TRUE); + } + + $clone->{$field_name}[$language] = $all_values; + } + $field_output = field_view_field($entity_type, $clone, $field_name, $field_settings, $language); - if (!empty($field_output) && !empty($conf['override_title'])) { - $field_output['#title'] = filter_xss_admin($conf['override_title_text']); + if (!empty($field_output)) { + if (!empty($conf['override_title'])) { + $field_output['#title'] = filter_xss_admin($conf['override_title_text']); + } + $field_output['#ctools_context'] = $context; + $field_output['#post_render'][] = 'ctools_entity_field_content_type_substitute_keywords'; } // Build the content type block. $block = new stdClass(); - $block->module = 'entity_field'; + $block->module = 'entity_field'; if ($conf['label'] == 'title' && isset($field_output['#title'])) { $block->title = $field_output['#title']; } @@ -166,8 +179,15 @@ function ctools_entity_field_content_type_render($subtype, $conf, $panel_args, $ } /** -* Returns an edit form for custom type settings. -*/ + * Replace context keywords. + */ +function ctools_entity_field_content_type_substitute_keywords($markup, array $element) { + return ctools_context_keyword_substitute($markup, array(), array($element['#ctools_context'])); +} + +/** + * Returns an edit form for custom type settings. + */ function ctools_entity_field_content_type_formatter_options($form, &$form_state) { if (empty($form_state['conf']['formatter_settings'])) { $form_state['conf']['formatter_settings'] = array(); @@ -221,6 +241,11 @@ function ctools_entity_field_content_type_formatter_styles($form, &$form_state) ctools_form_include($form_state, 'field_ui.admin', 'field_ui', ''); ctools_form_include($form_state, 'fields'); + $form['ctools_keywords'] = array( + '#type' => 'item', + '#description' => t('You may use keywords for substitutions.'), + ); + $form['ctools_field_list'] = array( '#type' => 'value', '#value' => array(), @@ -260,10 +285,7 @@ function ctools_entity_field_content_type_admin_title($subtype, $conf, $context) $identifier = $context->identifier; } else { - $type = 'ctools_entity_field_content_type_admin_title'; - $message = t('Context is missing for field: @name', array('@name' => $subtype)); - $variables = array($subtype, $conf, $context); - watchdog($type, $message, $variables, $severity = WATCHDOG_NOTICE); + watchdog('ctools_entity_field_content_type_admin_title', 'Context is missing for field: @name', array('@name' => $subtype), WATCHDOG_NOTICE); $identifier = t('Unknown'); } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/entity_context/entity_field_extra.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/entity_context/entity_field_extra.inc index 6a59ed4..efc7f4f 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/entity_context/entity_field_extra.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/entity_context/entity_field_extra.inc @@ -1,5 +1,9 @@ t('Entity extra field'), 'defaults' => array('view_mode' => NULL), @@ -119,7 +123,7 @@ function ctools_entity_field_extra_content_type_render($subtype, $conf, $panel_a if (isset($entity->content[$field_name])) { // Build the content type block. - $block = new stdClass(); + $block = new stdClass(); $block->module = 'entity_field_extra'; $block->content = $entity->content[$field_name]; $block->delta = $id; diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/form/entity_form_field.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/form/entity_form_field.inc index a030f69..b4d8338 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/form/entity_form_field.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/form/entity_form_field.inc @@ -38,10 +38,13 @@ function ctools_entity_form_field_content_type_content_types() { $types = array(); $content_types = array(); $entities = entity_get_info(); - + $field_instances = field_info_instances(); foreach ($entities as $entity_type => $entity) { foreach ($entity['bundles'] as $type => $bundle) { - foreach (field_info_instances($entity_type, $type) as $field_name => $field) { + if (!isset($field_instances[$entity_type][$type])) { + continue; + } + foreach ($field_instances[$entity_type][$type] as $field_name => $field) { if (!isset($types[$entity_type . ':' . $field_name])) { $types[$entity_type . ':' . $field_name] = array( 'category' => t('Form'), @@ -57,6 +60,26 @@ function ctools_entity_form_field_content_type_content_types() { } } + if (module_exists('field_group')) { + foreach ($entities as $entity_type => $entity) { + foreach ($entity['bundles'] as $type => $bundle) { + if ($group_info = field_group_info_groups($entity_type, $type, "form")) { + foreach ($group_info as $group_name => $group) { + if (!isset($types[$entity_type . ':' . $group_name])) { + $types[$entity_type . ':' . $group_name] = array( + 'category' => t('Form'), + 'icon' => 'icon_field.png', + 'title' => t('Group form: @widget_label', array('@widget_label' => $group->label)), + 'description' => t('Field group on the referenced entity.'), + ); + } + $content_types[$entity_type . ':' . $group_name]['types'][$type] = $bundle['label']; + } + } + } + } + } + // Create the required context for each field related to the bundle types. foreach ($types as $key => $field_content_type) { list($entity_type, $field_name) = explode(':', $key, 2); @@ -70,8 +93,8 @@ function ctools_entity_form_field_content_type_content_types() { } /** -* Render the custom content type. -*/ + * Render the custom content type. + */ function ctools_entity_form_field_content_type_render($subtype, $conf, $panel_args, $context) { if (empty($context) || empty($context->data)) { return; @@ -85,16 +108,38 @@ function ctools_entity_form_field_content_type_render($subtype, $conf, $panel_ar $ids = entity_extract_ids($entity_type, $entity); $field = field_info_instance($entity_type, $field_name, $ids[2]); - // Do not render if the entity type does not have this field. - if (empty($field)) { + // Check for field groups. + if (empty($field) && module_exists('field_group')) { + $groups = field_group_info_groups($entity_type, $entity->type, "form"); + $group = !empty($groups[$field_name]) ? $groups[$field_name] : NULL; + } + + // Do not render if the entity type does not have this field or group. + if (empty($field) && empty($group)) { return; } - $block = new stdClass(); + $block = new stdClass(); if (isset($context->form)) { $block->content = array(); - $block->content[$field_name] = $context->form[$field_name]; - unset($context->form[$field_name]); + if (!empty($field)) { + $block->content[$field_name] = $context->form[$field_name]; + unset($context->form[$field_name]); + } + else { + // Pre-render the form to populate field groups. + if (isset($context->form['#pre_render'])) { + foreach ($context->form['#pre_render'] as $function) { + if (function_exists($function)) { + $context->form = $function($context->form); + } + } + unset($context->form['#pre_render']); + } + + $block->content[$field_name] = $context->form[$field_name]; + unset($context->form[$field_name]); + } } else { $block->content = t('Entity info.'); @@ -104,8 +149,8 @@ function ctools_entity_form_field_content_type_render($subtype, $conf, $panel_ar } /** -* Returns the administrative title for a type. -*/ + * Returns the administrative title for a type. + */ function ctools_entity_form_field_content_type_admin_title($subtype, $conf, $context) { list($entity_type, $field_name) = explode(':', $subtype, 2); @@ -120,6 +165,6 @@ function ctools_entity_form_field_content_type_admin_title($subtype, $conf, $con } function ctools_entity_form_field_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/form/form.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/form/form.inc index 73f7c78..ef8cc65 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/form/form.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/form/form.inc @@ -1,11 +1,13 @@ TRUE, 'render last' => TRUE, 'title' => t('General form'), @@ -43,7 +45,7 @@ function ctools_form_content_type_render($subtype, $conf, $panel_args, &$context $block->delta = $context->form_id; } else { - $block->title = t('Form'); + $block->title = t('Form'); $block->content = t('Form goes here.'); $block->delta = 'unknown'; } @@ -56,7 +58,7 @@ function ctools_form_content_type_admin_title($subtype, $conf, $context) { } function ctools_form_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to override title + // Provide a blank form so we have a place to override title // and stuff. return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node/node.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node/node.inc index 7e77194..a114120 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node/node.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node/node.inc @@ -46,10 +46,10 @@ function ctools_node_content_type_render($subtype, $conf, $panel_args) { } } - // Support node translation + // Support node translation. if (module_exists('translation')) { if ($translations = module_invoke('translation', 'node_get_translations', $nid)) { - if (isset($translations[$GLOBALS['language']->language])) { + if (isset($translations[$GLOBALS['language']->language])) { $nid = $translations[$GLOBALS['language']->language]->nid; } } @@ -66,7 +66,7 @@ function ctools_node_content_type_render($subtype, $conf, $panel_args) { // Don't store viewed node data on the node, this can mess up other // views of the node. - $node = clone($node); + $node = clone $node; $block->module = 'node'; $block->delta = $node->nid; @@ -121,7 +121,6 @@ function ctools_node_content_type_edit_form($form, &$form_state) { '#description' => t('Check this box if you would like your pane title to link to the node.'), ); - if ($form_state['op'] == 'add') { $form['nid'] = array( '#prefix' => '
    ', @@ -179,7 +178,7 @@ function ctools_node_content_type_edit_form($form, &$form_state) { /** * Validate the node selection. */ -function ctools_node_content_type_edit_form_validate(&$form, &$form_state) { +function ctools_node_content_type_edit_form_validate(&$form, &$form_state) { if ($form_state['op'] != 'add') { return; } @@ -205,7 +204,7 @@ function ctools_node_content_type_edit_form_validate(&$form, &$form_state) { } if (!($node || preg_match('/^[@%]\d+$/', $nid)) || - // Do not allow unpublished nodes to be selected by unprivileged users + // Do not allow unpublished nodes to be selected by unprivileged users. (empty($node->status) && !user_access('administer nodes'))) { form_error($form['nid'], t('Invalid node')); } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_attachments.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_attachments.inc index 380e260..2878e16 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_attachments.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_attachments.inc @@ -1,9 +1,11 @@ TRUE, 'title' => t('Attached files'), @@ -14,7 +16,7 @@ $plugin = array( ); function ctools_node_attachments_content_type_render($subtype, $conf, $panel_args, $context) { - $node = isset($context->data) ? clone($context->data) : NULL; + $node = isset($context->data) ? clone $context->data : NULL; $block = new stdClass(); $block->module = 'attachments'; @@ -38,7 +40,6 @@ function ctools_node_attachments_content_type_admin_title($subtype, $conf, $cont } function ctools_node_attachments_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_author.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_author.inc index e98ce5a..04b387d 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_author.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_author.inc @@ -1,9 +1,11 @@ TRUE, 'title' => t('Node author'), @@ -29,7 +31,7 @@ function ctools_node_author_content_type_render($subtype, $conf, $panel_args, $c $user = user_load($node->uid); // Build the content type block. - $block = new stdClass(); + $block = new stdClass(); $block->module = 'node_author'; $block->title = t('Author'); $block->content = !empty($conf['link']) ? theme('username', array('account' => $user, 'link_path' => 'user/' . $node->uid)) : check_plain(format_username($node)); diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_body.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_body.inc index 3e69560..032bc8e 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_body.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_body.inc @@ -1,9 +1,11 @@ TRUE, 'title' => t('Node body'), @@ -28,7 +30,7 @@ function ctools_node_body_content_type_render($subtype, $conf, $panel_args, $con * Returns an edit form for custom type settings. */ function ctools_node_body_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_book_children.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_book_children.inc index 5d017c5..21f6542 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_book_children.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_book_children.inc @@ -1,5 +1,9 @@ data) ? clone($context->data) : NULL; + $node = isset($context->data) ? clone $context->data : NULL; $block = new stdClass(); $block->module = 'book_children'; @@ -38,6 +42,6 @@ function ctools_node_book_children_content_type_admin_title($subtype, $conf, $co } function ctools_node_book_children_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_book_menu.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_book_menu.inc new file mode 100644 index 0000000..b687076 --- /dev/null +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_book_menu.inc @@ -0,0 +1,105 @@ + TRUE, + 'title' => t('Book navigation menu'), + 'icon' => drupal_get_path('module', 'ctools') . '/plugins/content_types/block/icon_core_block_menu.png', + 'description' => t('The book menu belonging to the current book node.'), + 'required context' => new ctools_context_required(t('Node'), 'node'), + 'category' => t('Node'), + ); +} + +function ctools_node_book_menu_content_type_render($subtype, $conf, $panel_args, $context) { + $node = isset($context->data) ? clone $context->data : NULL; + $block = new stdClass(); + $block->module = 'book_menu'; + + if ($conf['override_title']) { + $block->title = t($conf['override_title_text']); + } + else { + $block->title = t('Book navigation menu'); + } + if ($node) { + $block->delta = $node->nid; + // TODO: the value is not available somehow?!? + $book_block_mode = isset($conf['book_block_mode']) ? $conf['book_block_mode'] : 'book pages'; + + // Code below is taken from function book_block_view(). + $current_bid = empty($node->book['bid']) ? 0 : $node->book['bid']; + + if ($book_block_mode === 'all pages') { + $block->subject = t('Book navigation'); + $book_menus = array(); + $pseudo_tree = array(0 => array('below' => FALSE)); + foreach (book_get_books() as $book_id => $book) { + if ($book['bid'] === $current_bid) { + // If the current page is a node associated with a book, the menu + // needs to be retrieved. + $book_menus[$book_id] = menu_tree_output(menu_tree_all_data($node->book['menu_name'], $node->book)); + } + else { + // Since we know we will only display a link to the top node, there + // is no reason to run an additional menu tree query for each book. + $book['in_active_trail'] = FALSE; + // Check whether user can access the book link. + $book_node = node_load($book['nid']); + $book['access'] = node_access('view', $book_node); + $pseudo_tree[0]['link'] = $book; + $book_menus[$book_id] = menu_tree_output($pseudo_tree); + } + } + $book_menus['#theme'] = 'book_all_books_block'; + $block->content = $book_menus; + } + elseif ($current_bid) { + // Only display this block when the user is browsing a book. + $select = db_select('node', 'n') + ->fields('n', array('title')) + ->condition('n.nid', $node->book['bid']) + ->addTag('node_access'); + $title = $select->execute()->fetchField(); + // Only show the block if the user has view access for the top-level node. + if ($title) { + $tree = menu_tree_all_data($node->book['menu_name'], $node->book); + // There should only be one element at the top level. + $data = array_shift($tree); + // TODO: subject is not rendered. + $block->subject = theme('book_title_link', array('link' => $data['link'])); + $block->content = ($data['below']) ? menu_tree_output($data['below']) : ''; + } + } + } + else { + $block->content = t('Book navigation pager goes here.'); + $block->delta = 'unknown'; + } + + return $block; +} + +function ctools_node_book_menu_content_type_admin_title($subtype, $conf, $context) { + return t('"@s" book navigation pager', array('@s' => $context->identifier)); +} + +function ctools_node_book_menu_content_type_edit_form($form, &$form_state) { + // Grab block form from the book module. + $block_form = book_block_configure($delta = ''); + // TODO: this does not work yet. + // See TODO in: ctools_node_book_menu_content_type_render. + if (isset($form_state['input']['book_block_mode'])) { + $block_form['book_block_mode']['#default_value'] = $form_state['input']['book_block_mode']; + } + $form += $block_form; + return $form; +} diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_book_nav.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_book_nav.inc index 6c0d50d..d7a284b 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_book_nav.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_book_nav.inc @@ -1,5 +1,9 @@ TRUE, - 'title' => t('Book navigation'), - 'icon' => 'icon_node.png', - 'description' => t('The navigation menu the book the node belongs to.'), + 'title' => t('Book navigation pager'), + 'icon' => drupal_get_path('module', 'ctools') . '/plugins/content_types/block/icon_core_booknavigation.png', + 'description' => t('The navigational pager and sub pages of the current book node.'), 'required context' => new ctools_context_required(t('Node'), 'node'), 'category' => t('Node'), ); } function ctools_node_book_nav_content_type_render($subtype, $conf, $panel_args, $context) { - $node = isset($context->data) ? clone($context->data) : NULL; + $node = isset($context->data) ? clone $context->data : NULL; $block = new stdClass(); $block->module = 'book_nav'; - $block->title = t('Book navigation'); + $block->title = t('Book navigation pager'); if ($node) { $block->content = isset($node->book) ? theme('book_navigation', array('book_link' => $node->book)) : ''; $block->delta = $node->nid; } else { - $block->content = t('Book navigation goes here.'); + $block->content = t('Book navigation pager goes here.'); $block->delta = 'unknown'; } @@ -34,10 +38,10 @@ function ctools_node_book_nav_content_type_render($subtype, $conf, $panel_args, } function ctools_node_book_nav_content_type_admin_title($subtype, $conf, $context) { - return t('"@s" book navigation', array('@s' => $context->identifier)); + return t('"@s" book navigation pager', array('@s' => $context->identifier)); } function ctools_node_book_nav_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_comment_form.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_comment_form.inc index f77b66e..ff34e4b 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_comment_form.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_comment_form.inc @@ -1,5 +1,9 @@ data) ? clone($context->data) : NULL; + if (empty($context->data->nid)) { + return; + } + $node = clone $context->data; $block = new stdClass(); $block->module = 'comments'; - $block->delta = $node->nid; - + $block->delta = $node->nid; $block->title = t('Add comment'); - if (empty($node)) { - $block->content = t('Comment form here.'); - } - else if ($node->comment == COMMENT_NODE_OPEN) { + if ($node->comment == COMMENT_NODE_OPEN) { if (user_access('post comments')) { $comment = new stdClass(); $comment->nid = $node->nid; @@ -43,7 +46,7 @@ function ctools_node_comment_form_content_type_render($subtype, $conf, $panel_ar ); $block->content = drupal_build_form('comment_node_' . $node->type . '_form', $form_state); } - else if (!empty($conf['anon_links'])) { + elseif (!empty($conf['anon_links'])) { $block->content = theme('comment_post_forbidden', array('node' => $node)); } } @@ -76,21 +79,3 @@ function ctools_node_comment_form_content_type_edit_form_submit($form, &$form_st $form_state['conf'][$key] = $form_state['values'][$key]; } } - -/** - * Alter the comment form to get a little more control over it. - */ -function ctools_form_comment_form_alter(&$form, &$form_state) { - if (!empty($form_state['ctools comment alter'])) { - // Force the form to post back to wherever we are. - $form['#action'] = url($_GET['q'], array('fragment' => 'comment-form')); - if (empty($form['#submit'])) { - $form['#submit'] = array('comment_form_submit'); - } - $form['#submit'][] = 'ctools_node_comment_form_submit'; - } -} - -function ctools_node_comment_form_submit(&$form, &$form_state) { - $form_state['redirect'][0] = $_GET['q']; -} diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_comment_wrapper.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_comment_wrapper.inc index 8e25429..4bdb3fc 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_comment_wrapper.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_comment_wrapper.inc @@ -1,5 +1,9 @@ data) ? clone($context->data) : NULL; + $node = isset($context->data) ? clone $context->data : NULL; $block = new stdClass(); $block->module = 'comments'; - $block->delta = $node->nid; + $block->delta = $node->nid; $renderable = array( '#theme' => 'comment_wrapper__node_' . $node->type, @@ -65,7 +69,7 @@ function ctools_node_comment_wrapper_content_type_render($subtype, $conf, $panel ); $renderable['comment_form'] = drupal_build_form('comment_node_' . $node->type . '_form', $form_state); } - else if (!empty($conf['anon_links'])) { + elseif (!empty($conf['anon_links'])) { $renderable['comment_form'] = theme('comment_post_forbidden', array('node' => $node)); } } @@ -77,7 +81,7 @@ function ctools_node_comment_wrapper_content_type_render($subtype, $conf, $panel /** * Returns an edit form for the comment wrapper. - */ + */ function ctools_node_comment_wrapper_content_type_edit_form($form, &$form_state) { $conf = $form_state['conf']; $form['mode'] = array( @@ -90,7 +94,8 @@ function ctools_node_comment_wrapper_content_type_edit_form($form, &$form_state) foreach (_comment_per_page() as $i) { $options[$i] = t('!a comments per page', array('!a' => $i)); } - $form['comments_per_page'] = array('#type' => 'select', + $form['comments_per_page'] = array( + '#type' => 'select', '#title' => t('Pager'), '#default_value' => $conf['comments_per_page'], '#options' => $options, diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_comments.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_comments.inc index 0f0033d..7453e79 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_comments.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_comments.inc @@ -1,5 +1,9 @@ data) ? clone($context->data) : NULL; + if (empty($context->data->nid)) { + return; + } + $node = clone $context->data; $block = new stdClass(); $block->module = 'comments'; - $block->delta = $node->nid; - + $block->delta = $node->nid; $block->title = t('Comments'); - if (empty($node)) { - $block->content = t('Node comments go here.'); - } - else if ($node->comment) { + + if ($node->comment) { $block->content = ctools_comment_render($node, $conf); - // Update the history table, stating that this user viewed this node. - node_tag_new($node); } return $block; @@ -50,7 +52,8 @@ function ctools_node_comments_content_type_edit_form($form, &$form_state) { foreach (_comment_per_page() as $i) { $options[$i] = t('!a comments per page', array('!a' => $i)); } - $form['comments_per_page'] = array('#type' => 'select', + $form['comments_per_page'] = array( + '#type' => 'select', '#title' => t('Pager'), '#default_value' => $conf['comments_per_page'], '#options' => $options, diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_content.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_content.inc index 38c5b57..ba873ed 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_content.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_content.inc @@ -1,9 +1,11 @@ TRUE, 'title' => t('Node content'), @@ -27,18 +29,18 @@ $plugin = array( * Render the node content. */ function ctools_node_content_content_type_render($subtype, $conf, $panel_args, $context) { - if (!empty($context) && empty($context->data)) { + if (!empty($context) && (empty($context->data) || empty($context->data->nid))) { return; } - $node = isset($context->data) ? clone($context->data) : NULL; + $node = isset($context->data) ? clone $context->data : NULL; $block = new stdClass(); $block->module = 'node'; - $block->delta = $node->nid; + $block->delta = $node->nid; if (empty($node)) { $block->delta = 'placeholder'; - $block->title = t('Node title.'); + $block->title = t('Node title.'); $block->content = t('Node content goes here.'); } else { @@ -86,7 +88,7 @@ function ctools_node_content_render_node($node, $conf) { $links['node-readmore'] = array( 'title' => t('Read more'), 'href' => 'node/' . $node->nid, - 'attributes' => array('rel' => 'tag', 'title' => strip_tags($node->title)) + 'attributes' => array('rel' => 'tag', 'title' => strip_tags($node->title)), ); } @@ -201,4 +203,3 @@ function ctools_node_content_content_type_edit_form_submit($form, &$form_state) function ctools_node_content_content_type_admin_title($subtype, $conf, $context) { return t('"@s" content', array('@s' => $context->identifier)); } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_created.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_created.inc index 06d1442..da3fbf8 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_created.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_created.inc @@ -1,9 +1,11 @@ TRUE, 'title' => t('Node created date'), @@ -20,7 +22,7 @@ $plugin = array( * Render the custom content type. */ function ctools_node_created_content_type_render($subtype, $conf, $panel_args, $context) { - if (empty($context) || empty($context->data)) { + if (!empty($context) && (empty($context->data) || empty($context->data->nid))) { return; } @@ -28,7 +30,7 @@ function ctools_node_created_content_type_render($subtype, $conf, $panel_args, $ $node = $context->data; // Build the content type block. - $block = new stdClass(); + $block = new stdClass(); $block->module = 'node_created'; $block->title = t('Created date'); $block->content = format_date($node->created, $conf['format']); diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_links.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_links.inc index 6096a44..384fb63 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_links.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_links.inc @@ -1,9 +1,11 @@ TRUE, 'title' => t('Node links'), @@ -29,10 +31,10 @@ function ctools_node_links_content_type_render($subtype, $conf, $panel_args, $co return; } - $node = isset($context->data) ? clone($context->data) : NULL; + $node = isset($context->data) ? clone $context->data : NULL; $block = new stdClass(); $block->module = 'node'; - $block->delta = $node->nid; + $block->delta = $node->nid; if (empty($node)) { $block->delta = 'placeholder'; @@ -102,4 +104,3 @@ function ctools_node_links_content_type_edit_form_submit($form, &$form_state) { function ctools_node_links_content_type_admin_title($subtype, $conf, $context) { return t('"@s" links', array('@s' => $context->identifier)); } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_terms.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_terms.inc index f6e7aec..e910e4c 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_terms.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_terms.inc @@ -1,9 +1,11 @@ TRUE, 'title' => t('Node terms'), @@ -30,7 +32,7 @@ function ctools_node_terms_content_type_render($subtype, $conf, $panel_args, $co // Get a shortcut to the node. $node = $context->data; - // Load all terms for this node from all vocabularies + // Load all terms for this node from all vocabularies. $query = db_select('taxonomy_index', 't'); $result = $query ->fields('t') @@ -38,22 +40,22 @@ function ctools_node_terms_content_type_render($subtype, $conf, $panel_args, $co ->execute(); $tids = array(); - foreach ($result AS $term) { + foreach ($result as $term) { $tids[] = $term->tid; } - // Get the real term objects + // Get the real term objects. $term_objects = taxonomy_term_load_multiple($tids); $terms = array(); if (empty($conf['vid'])) { // All terms. - foreach ($term_objects AS $term) { + foreach ($term_objects as $term) { $terms['taxonomy_term_' . $term->tid] = array( 'title' => check_plain($term->name), 'href' => 'taxonomy/term/' . $term->tid, - 'attributes' => array('rel' => 'tag', 'title' => strip_tags($term->description)) + 'attributes' => array('rel' => 'tag', 'title' => strip_tags($term->description)), ); } } @@ -106,7 +108,7 @@ function ctools_node_terms_content_type_render($subtype, $conf, $panel_args, $co } // Build the content type block. - $block = new stdClass(); + $block = new stdClass(); $block->module = 'node_terms'; $block->delta = $node->nid; $block->title = t('Terms'); diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_title.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_title.inc index bec8982..6956c2f 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_title.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_title.inc @@ -1,9 +1,11 @@ TRUE, 'title' => t('Node title'), @@ -23,7 +25,7 @@ $plugin = array( * Render the custom content type. */ function ctools_node_title_content_type_render($subtype, $conf, $panel_args, $context) { - if (empty($context) || empty($context->data)) { + if (!empty($context) && (empty($context->data) || empty($context->data->nid))) { return; } @@ -33,10 +35,10 @@ function ctools_node_title_content_type_render($subtype, $conf, $panel_args, $co // Load information about the node type. $type = node_type_get_type($node); - // Generate the title + // Generate the title. $content = !empty($conf['link']) ? l($node->title, 'node/' . $node->nid) : check_plain($node->title); - // Build any surrounding markup if so configured + // Build any surrounding markup if so configured. if (isset($conf['markup']) && $conf['markup'] != 'none') { $markup = '<' . $conf['markup']; if (!empty($conf['id'])) { @@ -50,7 +52,7 @@ function ctools_node_title_content_type_render($subtype, $conf, $panel_args, $co } // Build the content type block. - $block = new stdClass(); + $block = new stdClass(); $block->module = 'node_title'; $block->title = $type->title_label; $block->content = $content; diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_type_desc.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_type_desc.inc index f2005d0..0694c53 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_type_desc.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_type_desc.inc @@ -1,9 +1,11 @@ TRUE, 'title' => t('Node type description'), @@ -18,18 +20,18 @@ $plugin = array( * based on the module and delta supplied in the configuration. */ function ctools_node_type_desc_content_type_render($subtype, $conf, $panel_args, $context) { - $node = isset($context->data) ? clone($context->data) : NULL; + $node = isset($context->data) ? clone $context->data : NULL; $block = new stdClass(); $block->module = 'node_type'; if ($node) { - $type = node_type_get_type($node); - $block->title = $type->name; + $type = node_type_get_type($node); + $block->title = $type->name; $block->content = filter_xss_admin($type->description); $block->delta = $node->type; } else { - $block->title = t('Node type description'); + $block->title = t('Node type description'); $block->content = t('Node type description goes here.'); $block->delta = 'unknown'; } @@ -42,6 +44,6 @@ function ctools_node_type_desc_content_type_admin_title($subtype, $conf, $contex } function ctools_node_type_desc_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_updated.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_updated.inc index 60ac4b5..eb50669 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_updated.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_updated.inc @@ -1,9 +1,11 @@ TRUE, 'title' => t('Node last updated date'), @@ -28,7 +30,7 @@ function ctools_node_updated_content_type_render($subtype, $conf, $panel_args, $ $node = $context->data; // Build the content type block. - $block = new stdClass(); + $block = new stdClass(); $block->module = 'node_updated'; $block->title = t('Last updated date'); $block->content = format_date(!empty($node->changed) ? $node->changed : $node->created, $conf['format']); diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_attachments.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_attachments.inc index 1e248f5..51c95a5 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_attachments.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_attachments.inc @@ -1,5 +1,9 @@ module = t('node_form'); + $block->module = 'node_form'; $block->title = t('Attach files'); $block->delta = 'url-path-options'; @@ -46,6 +50,6 @@ function ctools_node_form_attachments_content_type_admin_title($subtype, $conf, } function ctools_node_form_attachments_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_author.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_author.inc index 350df40..86f3d73 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_author.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_author.inc @@ -1,9 +1,11 @@ TRUE, 'icon' => 'icon_node_form.png', @@ -15,7 +17,7 @@ $plugin = array( function ctools_node_form_author_content_type_render($subtype, $conf, $panel_args, &$context) { $block = new stdClass(); - $block->module = t('node_form'); + $block->module = 'node_form'; $block->title = t('Authoring information'); $block->delta = 'author-options'; @@ -47,6 +49,6 @@ function ctools_node_form_author_content_type_admin_title($subtype, $conf, $cont } function ctools_node_form_author_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_book.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_book.inc index ad19590..b7ad36b 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_book.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_book.inc @@ -1,5 +1,9 @@ module = t('node_form'); + $block->module = 'node_form'; $block->title = t('Book outline'); $block->delta = 'book-outline'; @@ -45,6 +49,6 @@ function ctools_node_form_book_content_type_admin_title($subtype, $conf, $contex } function ctools_node_form_book_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_buttons.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_buttons.inc index b7ac984..ed553a6 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_buttons.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_buttons.inc @@ -1,9 +1,11 @@ TRUE, 'icon' => 'icon_node_form.png', @@ -15,7 +17,7 @@ $plugin = array( function ctools_node_form_buttons_content_type_render($subtype, $conf, $panel_args, &$context) { $block = new stdClass(); - $block->module = t('node_form'); + $block->module = 'node_form'; $block->title = ''; $block->delta = 'buttons'; @@ -38,6 +40,6 @@ function ctools_node_form_buttons_content_type_admin_title($subtype, $conf, $con } function ctools_node_form_buttons_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_comment.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_comment.inc index d0f137a..eaf5c95 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_comment.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_comment.inc @@ -1,5 +1,9 @@ module = t('node_form'); + $block->module = 'node_form'; $block->title = t('Comment options'); $block->delta = 'comment-options'; @@ -45,6 +49,6 @@ function ctools_node_form_comment_content_type_admin_title($subtype, $conf, $con } function ctools_node_form_comment_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_language.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_language.inc index 2043c1c..dfef295 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_language.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_language.inc @@ -1,9 +1,11 @@ TRUE, 'icon' => 'icon_node_form.png', @@ -15,7 +17,7 @@ $plugin = array( function ctools_node_form_language_content_type_render($subtype, $conf, $panel_args, &$context) { $block = new stdClass(); - $block->module = t('node_form'); + $block->module = 'node_form'; $block->delta = 'language-options'; @@ -36,6 +38,6 @@ function ctools_node_form_language_content_type_admin_title($subtype, $conf, $co } function ctools_node_form_language_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; -} \ No newline at end of file +} diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_log.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_log.inc index 334ff54..aac42a6 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_log.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_log.inc @@ -1,9 +1,11 @@ TRUE, 'icon' => 'icon_node_form.png', @@ -15,7 +17,7 @@ $plugin = array( function ctools_node_form_log_content_type_render($subtype, $conf, $panel_args, &$context) { $block = new stdClass(); - $block->module = t('node_form'); + $block->module = 'node_form'; $block->title = t('Revision information'); if (isset($context->form)) { @@ -42,6 +44,6 @@ function ctools_node_form_log_content_type_admin_title($subtype, $conf, $context } function ctools_node_form_log_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_menu.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_menu.inc index 906ade4..9b4df43 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_menu.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_menu.inc @@ -1,5 +1,9 @@ module = t('node_form'); + $block->module = 'node_form'; $block->title = t('Menu options'); $block->delta = 'menu-options'; @@ -45,6 +49,6 @@ function ctools_node_form_menu_content_type_admin_title($subtype, $conf, $contex } function ctools_node_form_menu_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_path.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_path.inc index a1e3cba..262851f 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_path.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_path.inc @@ -1,5 +1,9 @@ module = t('node_form'); + $block->module = 'node_form'; $block->title = t('URL path options'); $block->delta = 'url-path-options'; @@ -46,6 +50,6 @@ function ctools_node_form_path_content_type_admin_title($subtype, $conf, $contex } function ctools_node_form_path_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_publishing.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_publishing.inc index e73cff2..53b584f 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_publishing.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_publishing.inc @@ -23,7 +23,7 @@ function ctools_node_form_publishing_content_type_render($subtype, $conf, $panel $block = new stdClass(); $block->title = t('Publishing options'); - $block->module = t('node_form'); + $block->module = 'node_form'; $block->delta = 'publishing-options'; if (isset($context->form)) { @@ -49,6 +49,6 @@ function ctools_node_form_publishing_content_type_admin_title($subtype, $conf, $ } function ctools_node_form_publishing_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_title.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_title.inc index f40d274..14e79ab 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_title.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/node_form/node_form_title.inc @@ -1,9 +1,11 @@ TRUE, 'icon' => 'icon_node_form.png', @@ -15,7 +17,7 @@ $plugin = array( function ctools_node_form_title_content_type_render($subtype, $conf, $panel_args, &$context) { $block = new stdClass(); - $block->module = t('node_form'); + $block->module = 'node_form'; $block->delta = 'title-options'; @@ -36,6 +38,6 @@ function ctools_node_form_title_content_type_admin_title($subtype, $conf, $conte } function ctools_node_form_title_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_help.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_help.inc index da1abe6..dcba97d 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_help.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_help.inc @@ -30,4 +30,3 @@ function ctools_page_help_content_type_render($subtype, $conf, $panel_args) { return $block; } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_logo.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_logo.inc index c00ca5e..fa7cdae 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_logo.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_logo.inc @@ -29,7 +29,7 @@ function ctools_page_logo_content_type_render($subtype, $conf, $panel_args) { $block = new stdClass(); if (!empty($logo)) { - $image = '' . t('Home') . ''; + $image = theme('image', array('path' => $logo, 'alt' => t('Home'))); $block->content = l($image, '', array('html' => TRUE, 'attributes' => array('rel' => 'home', 'id' => 'logo', 'title' => t('Home')))); } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_messages.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_messages.inc index e2fe37b..9a8bc74 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_messages.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_messages.inc @@ -30,4 +30,3 @@ function ctools_page_messages_content_type_render($subtype, $conf, $panel_args) return $block; } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_site_name.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_site_name.inc index 3053d45..cb83431 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_site_name.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_site_name.inc @@ -61,7 +61,7 @@ function ctools_page_site_name_content_type_render($subtype, $conf, $panel_args) // Optionally link the site name to the homepage. if (!empty($conf['linked'])) { - $block->content = l($block->content, ''); + $block->content = l($block->content, '', array('html' => TRUE)); } return $block; diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_tabs.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_tabs.inc index e88acd5..6a2e9a7 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_tabs.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_tabs.inc @@ -40,6 +40,7 @@ function ctools_page_tabs_content_type_render($subtype, $conf, $panel_args) { case 'primary': unset($menus['#secondary']); break; + case 'secondary': unset($menus['#primary']); break; @@ -54,7 +55,6 @@ function ctools_page_tabs_content_type_render($subtype, $conf, $panel_args) { return $block; } - function ctools_page_tabs_content_type_edit_form($form, &$form_state) { $conf = $form_state['conf']; diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_title.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_title.inc index e3032df..c32ef7e 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_title.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_title.inc @@ -29,6 +29,12 @@ $plugin = array( * Outputs the page title of the current page. */ function ctools_page_title_content_type_render($subtype, $conf, $panel_args) { + // $conf['override_title'] can have one of these three values. + // 0 i.e. hide the title, 1 i.e. no title, and 2 i.e. pane title if it's a + // panels page. + if (!drupal_get_title() && isset($conf['override_title']) && $conf['override_title'] === 1) { + return; + } // TODO: This should have a setting or something for the markup. if (empty($conf['markup'])) { $conf['markup'] = 'h1'; diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/search/search_form.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/search/search_form.inc index 2b6a322..0167100 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/search/search_form.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/search/search_form.inc @@ -1,5 +1,9 @@ module = 'search'; - $block->delta = 'form'; - $block->title = ''; + $block = new stdClass(); + $block->module = 'search'; + $block->delta = 'form'; + $block->title = ''; switch ($conf['path_type']) { default: @@ -49,6 +53,7 @@ function ctools_search_form_content_type_render($subtype, $conf, $panel_args, $c $path = $_GET['q']; $path = str_replace($keys, '', $path); break; + case 'custom': $path = $conf['path']; break; diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/search/search_result.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/search/search_result.inc index 32037bd..fb88a0f 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/search/search_result.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/search/search_result.inc @@ -1,5 +1,9 @@ data; } - $conditions = NULL; + $conditions = NULL; if (isset($info['conditions_callback']) && function_exists($info['conditions_callback'])) { // Build an optional array of more search conditions. $conditions = $info['conditions_callback']($keys); @@ -59,9 +63,9 @@ function ctools_search_result_content_type_render($subtype, $conf, $panel_args, } // Build the content type block. - $block = new stdClass(); - $block->module = 'search'; - $block->delta = 'result'; + $block = new stdClass(); + $block->module = 'search'; + $block->delta = 'result'; $results = ''; @@ -160,7 +164,6 @@ function ctools_search_result_content_type_edit_form($form, &$form_state) { '#title' => t('Display text if no search keywords were submitted'), ); - $form['no_key_title'] = array( '#title' => t('Title'), '#type' => 'textfield', diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/term_context/term_description.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/term_context/term_description.inc index 2b953ed..6367075 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/term_context/term_description.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/term_context/term_description.inc @@ -1,9 +1,11 @@ TRUE, 'title' => t('Term description'), @@ -14,12 +16,12 @@ $plugin = array( ); function ctools_term_description_content_type_render($subtype, $conf, $panel_args, $context) { - $term = isset($context->data) ? clone($context->data) : NULL; + $term = isset($context->data) ? clone $context->data : NULL; $block = new stdClass(); $block->module = 'node_type'; - $block->title = $term->name; - if ($term) { + if (!empty($term)) { + $block->title = $term->name; $block->content = check_markup($term->description, $term->format, '', TRUE); $block->delta = $term->tid; @@ -33,6 +35,7 @@ function ctools_term_description_content_type_render($subtype, $conf, $panel_arg } } else { + $block->title = ''; $block->content = t('Term description goes here.'); $block->delta = 'unknown'; } @@ -45,6 +48,6 @@ function ctools_term_description_content_type_admin_title($subtype, $conf, $cont } function ctools_term_description_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/term_context/term_list.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/term_context/term_list.inc index 7c609fb..a533074 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/term_context/term_list.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/term_context/term_list.inc @@ -1,9 +1,11 @@ TRUE, 'title' => t('List of related terms'), @@ -21,7 +23,7 @@ $plugin = array( ); function ctools_term_list_content_type_render($subtype, $conf, $panel_args, $context) { - $term = isset($context->data) ? clone($context->data) : NULL; + $term = isset($context->data) ? clone $context->data : NULL; $block = new stdClass(); $block->module = 'term-list'; $path = empty($conf['path']) ? 'taxonomy/term/%tid' : $conf['path']; @@ -40,7 +42,7 @@ function ctools_term_list_content_type_render($subtype, $conf, $panel_args, $con $block->delta = $conf['type']; switch ($conf['type']) { case 'related': - // FIXME this no longer exists, must be done with Field API + // @todo this no longer exists, must be done with Field API. // $terms = taxonomy_get_related($term->tid); break; @@ -78,7 +80,7 @@ function ctools_term_list_content_type_render($subtype, $conf, $panel_args, $con case 'synonyms': // FIXME this no longer exists, must be done with Field API -// $terms = taxonomy_get_synonyms($term->tid); + // $terms = taxonomy_get_synonyms($term->tid);. break; } @@ -169,4 +171,3 @@ function ctools_term_list_content_type_edit_form_submit($form, &$form_state) { $form_state['conf'][$key] = $form_state['values'][$key]; } } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/term_context/term_name.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/term_context/term_name.inc index a9a8876..ec30be4 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/term_context/term_name.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/term_context/term_name.inc @@ -1,9 +1,11 @@ TRUE, 'title' => t('Term name'), @@ -19,7 +21,6 @@ $plugin = array( ), ); - /** * Render the custom content type. */ @@ -34,10 +35,10 @@ function ctools_term_name_content_type_render($subtype, $conf, $panel_args, $con // Load the vocabulary. $vocab = taxonomy_vocabulary_load($term->vid); - // Generate the title + // Generate the title. $content = !empty($conf['link']) ? l($term->name, 'taxonomy/term/' . $term->tid) : check_plain($term->name); - // Build any surrounding markup if so configured + // Build any surrounding markup if so configured. if (isset($conf['markup']) && $conf['markup'] != 'none') { $markup = '<' . $conf['markup']; if (!empty($conf['id'])) { @@ -51,7 +52,7 @@ function ctools_term_name_content_type_render($subtype, $conf, $panel_args, $con } // Build the content type block. - $block = new stdClass(); + $block = new stdClass(); $block->module = 'term_name'; $block->title = t('Name'); $block->content = $content; diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/token/token.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/token/token.inc index 7b6f7fc..32026d8 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/token/token.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/token/token.inc @@ -62,8 +62,8 @@ function ctools_token_content_type_content_types() { } /** -* Render the custom content type. -*/ + * Render the custom content type. + */ function ctools_token_content_type_render($subtype, $conf, $panel_args, $context) { if (empty($context) || empty($context->data)) { return FALSE; @@ -81,7 +81,7 @@ function ctools_token_content_type_render($subtype, $conf, $panel_args, $context } // Build the content type block. - $block = new stdClass(); + $block = new stdClass(); $block->module = 'ctools'; $block->title = $info['tokens'][$entity_type][$name]['name']; $block->content = $values[$name]; @@ -91,8 +91,8 @@ function ctools_token_content_type_render($subtype, $conf, $panel_args, $context } /** -* Returns an edit form for custom type settings. -*/ + * Returns an edit form for custom type settings. + */ function ctools_token_content_type_edit_form($form, &$form_state) { $conf = $form_state['conf']; @@ -113,10 +113,9 @@ function ctools_token_content_type_edit_form_submit($form, &$form_state) { $form_state['conf']['sanitize'] = $form_state['values']['sanitize']; } - /** -* Returns the administrative title for a type. -*/ + * Returns the administrative title for a type. + */ function ctools_token_content_type_admin_title($subtype, $conf, $context) { return t('"@s" @name', array('@s' => $context->identifier, '@name' => $subtype)); } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/profile_fields.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/profile_fields.inc index 55d5593..019715f 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/profile_fields.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/profile_fields.inc @@ -1,5 +1,9 @@ data) ? clone($context->data) : NULL; + $account = isset($context->data) ? clone $context->data : NULL; $block = new stdClass(); $block->module = 'profile fields'; if ($account) { - // Get the category from the options + // Get the category from the options. $category = str_replace("_", " ", $conf['category']); - // Set the subject to the name of the category + // Set the subject to the name of the category. $block->subject = $category; - // Put all the fields in the category into an array + // Put all the fields in the category into an array. profile_view_profile($account); if (is_array($account->content[$category])) { @@ -46,11 +50,11 @@ function ctools_profile_fields_content_type_render($subtype, $conf, $panel_args, } if (count($vars) == 0) { - // Output the given empty text + // Output the given empty text. $output = $conf['empty']; } else { - // Call the theme function with the field vars + // Call the theme function with the field vars. $output = theme('profile_fields_pane', $category, $vars); } @@ -65,6 +69,7 @@ function ctools_profile_fields_content_type_render($subtype, $conf, $panel_args, return $block; } + /** * Helper function : build the list of categories for the 'edit' form. */ diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/profile_fields_pane.tpl.php b/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/profile_fields_pane.tpl.php index 3736812..bac10ce 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/profile_fields_pane.tpl.php +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/profile_fields_pane.tpl.php @@ -1,4 +1,5 @@ TRUE, 'title' => t('User links'), @@ -29,7 +31,7 @@ function ctools_user_links_content_type_render($subtype, $conf, $panel_args, $co $account = clone $context->data; $block = new stdClass(); $block->module = 'user'; - $block->delta = $account->uid; + $block->delta = $account->uid; if (empty($account)) { $block->delta = 'placeholder'; diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/user_picture.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/user_picture.inc index 0934c20..2d4d31b 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/user_picture.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/user_picture.inc @@ -1,9 +1,11 @@ TRUE, 'title' => t('User picture'), @@ -22,7 +24,7 @@ function ctools_user_picture_content_type_render($subtype, $conf, $panel_args, $ $account = clone $context->data; - // Check if user has permissions to access the user + // Check if user has permissions to access the user. if ($user->uid != $account->uid && (!user_access('access user profiles') && !user_access('administer users'))) { return; } @@ -36,19 +38,18 @@ function ctools_user_picture_content_type_render($subtype, $conf, $panel_args, $ '#account' => $account, ); - $block->content = $element; return $block; } /** - * Display the administrative title for a panel pane in the drag & drop UI + * Display the administrative title for a panel pane in the drag & drop UI. */ function ctools_user_picture_content_type_admin_title($subtype, $conf, $context) { return t('"@s" user picture', array('@s' => $context->identifier)); } function ctools_user_picture_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/user_profile.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/user_profile.inc index 6c41882..3a28ca3 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/user_profile.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/user_profile.inc @@ -1,9 +1,11 @@ TRUE, 'title' => t('User profile'), @@ -20,7 +22,7 @@ $plugin = array( * Render the user profile content type. */ function ctools_user_profile_content_type_render($subtype, $conf, $panel_args, $context) { - $account = isset($context->data) ? clone($context->data) : NULL; + $account = isset($context->data) ? clone $context->data : NULL; if (!$account) { return NULL; } @@ -84,4 +86,3 @@ function ctools_user_profile_content_type_edit_form($form, &$form_state) { function ctools_user_profile_content_type_edit_form_submit($form, &$form_state) { $form_state['conf']['view_mode'] = $form_state['values']['view_mode']; } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/user_signature.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/user_signature.inc index ca7550b..6a8e7f6 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/user_signature.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/user_context/user_signature.inc @@ -1,9 +1,11 @@ t('User signature'), 'icon' => 'icon_user.png', @@ -13,7 +15,7 @@ $plugin = array( ); function ctools_user_signature_content_type_render($subtype, $conf, $panel_args, $context) { - $account = isset($context->data) ? clone($context->data) : NULL; + $account = isset($context->data) ? clone $context->data : NULL; $block = new stdClass(); $block->module = 'user-signature'; @@ -31,13 +33,13 @@ function ctools_user_signature_content_type_render($subtype, $conf, $panel_args, } /** - * Display the administrative title for a panel pane in the drag & drop UI + * Display the administrative title for a panel pane in the drag & drop UI. */ function ctools_user_signature_content_type_admin_title($subtype, $conf, $context) { return t('"@s" user signature', array('@s' => $context->identifier)); } function ctools_user_signature_content_type_edit_form($form, &$form_state) { - // provide a blank form so we have a place to have context setting. + // Provide a blank form so we have a place to have context setting. return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/content_types/vocabulary_context/vocabulary_terms.inc b/sites/all/modules/contrib/dev/ctools/plugins/content_types/vocabulary_context/vocabulary_terms.inc index 5f33a03..6a7f8ef 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/content_types/vocabulary_context/vocabulary_terms.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/content_types/vocabulary_context/vocabulary_terms.inc @@ -1,5 +1,9 @@ data) ? clone($context->data) : NULL; - $max_depth = (!empty($conf['max_depth']) ? (int)$conf['max_depth'] : NULL); + $vocab = isset($context->data) ? clone $context->data : NULL; + $max_depth = (!empty($conf['max_depth']) ? (int) $conf['max_depth'] : NULL); if ($conf['tree'] == FALSE) { $terms = taxonomy_get_tree($vocab->vid, 0, $max_depth); $items = array(); @@ -35,9 +39,9 @@ function ctools_vocabulary_terms_content_type_render($subtype, $conf, $panel_arg $output = theme('item_list', array('items' => _ctools_content_vocabulary_terms($vocab->vid, $max_depth))); } - $block = new stdClass(); + $block = new stdClass(); $block->module = 'node_type'; - $block->title = check_plain($vocab->name); + $block->title = check_plain($vocab->name); $block->content = $output; $block->delta = $vocab->vid; @@ -97,4 +101,3 @@ function ctools_vocabulary_terms_content_type_edit_form_submit($form, &$form_sta $form_state['conf'][$key] = $form_state['values'][$key]; } } - diff --git a/sites/all/modules/contrib/dev/ctools/plugins/contexts/entity.inc b/sites/all/modules/contrib/dev/ctools/plugins/contexts/entity.inc index b214aa0..7a2b024 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/contexts/entity.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/contexts/entity.inc @@ -2,7 +2,6 @@ /** * @file - * * Plugin to provide a node context. A node context is a node wrapped in a * context object that can be utilized by anything that accepts contexts. */ @@ -23,6 +22,8 @@ $plugin = array( '#type' => 'textfield', '#description' => t('Enter the ID of an entity for this context.'), ), + // Tell ctools_context_get_context_from_context() how to interpret its $argument. + 'placeholder name' => 'entity_id', 'get child' => 'ctools_context_entity_get_child', 'get children' => 'ctools_context_entity_get_children', ); @@ -88,9 +89,9 @@ function ctools_context_create_entity($empty, $data = NULL, $conf = FALSE, $plug } if (!empty($data)) { - $context->data = $data; + $context->data = $data; if (!empty($entity['entity keys']['label'])) { - $context->title = $data->{$entity['entity keys']['label']}; + $context->title = $data->{$entity['entity keys']['label']}; } $context->argument = $id; @@ -159,7 +160,7 @@ function ctools_context_entity_settings_form($form, &$form_state) { * Validate a node. */ function ctools_context_entity_settings_form_validate($form, &$form_state) { - // Validate the autocomplete + // Validate the autocomplete. if (empty($form_state['values']['entity_id']) && empty($form_state['values']['entity'])) { form_error($form['entity'], t('You must select an entity.')); return; @@ -169,7 +170,7 @@ function ctools_context_entity_settings_form_validate($form, &$form_state) { return; } - $id = $form_state['values']['entity']; + $id = $form_state['values']['entity']; $preg_matches = array(); $match = preg_match('/\[id: (\d+)\]/', $id, $preg_matches); if (!$match) { diff --git a/sites/all/modules/contrib/dev/ctools/plugins/contexts/language.inc b/sites/all/modules/contrib/dev/ctools/plugins/contexts/language.inc new file mode 100644 index 0000000..e66f02a --- /dev/null +++ b/sites/all/modules/contrib/dev/ctools/plugins/contexts/language.inc @@ -0,0 +1,169 @@ + t('Language'), + 'description' => t('Language object.'), + 'context' => 'ctools_context_language_create', + 'context name' => 'language', + 'keyword' => 'language', + + // Provides a list of items which are exposed as keywords. + 'convert list' => 'ctools_language_context_convert_list', + // Convert keywords into data. + 'convert' => 'ctools_language_context_convert', + + 'placeholder form' => array( + '#type' => 'textfield', + '#description' => t('Enter a valid langcode.'), + '#value' => $GLOBALS['language']->language, + ), + + // Provide settings for the context. + 'edit form' => 'ctools_context_language_settings_form', + 'settings' => ctools_context_language_conf_defaults(), +); + +/** + * Ensures a full populated settings array with sane defaults. + * + * @param mixed $conf + * Array with the user defined settings, or a string identifying a language. + * + * @return array + * Array with all available settings. + */ +function ctools_context_language_conf_defaults($conf = array()) { + if (!is_array($conf)) { + $conf = array( + 'preset_langcode' => (string) $conf, + ); + } + + return $conf + array( + 'enable_cache_argument' => TRUE, + 'language_type' => 'language', + 'preset_langcode' => $GLOBALS['language']->language, + ); +} + +/** + * Create a context, either from manual configuration or the current language. + */ +function ctools_context_language_create($empty, $data = NULL, $conf = FALSE) { + $context = new ctools_context('language'); + $context->plugin = 'language'; + if ($empty) { + return $context; + } + $context->title = t('Language'); + + $settings = ctools_context_language_conf_defaults($data); + if ($settings['language_type'] != 'preset') { + $language_object = $GLOBALS[$settings['language_type']]; + } + else { + // Fetch the enabled language objects. + $languages = language_list('enabled'); + $languages = $languages[1]; + + // Set the custom language, but fallback to the interface language. + $language_object = $GLOBALS['language']; + if (isset($languages[$settings['preset_langcode']])) { + $language_object = $languages[$settings['preset_langcode']]; + } + } + // If enabled set the argument ot use in the cid. + if ($settings['enable_cache_argument']) { + $context->argument = $language_object->language; + } + $context->data = $language_object; + return $context; +} + +/** + * Provide a list of sub-keywords. + * + * This is used to provide keywords from the context for use in a content type, + * pane, etc. + */ +function ctools_language_context_convert_list() { + $context = new stdClass(); + $context->data = $GLOBALS['language']; + return array( + 'language' => t('Langcode. E.g. !example', array('!example' => ctools_language_context_convert($context, 'language'))), + 'name' => t('Name. E.g. !example', array('!example' => ctools_language_context_convert($context, 'name'))), + 'native' => t('Native name of the language. E.g. !example', array('!example' => ctools_language_context_convert($context, 'native'))), + 'direction' => t('Text direction 0=LRT, 1=RTL. E.g. !example', array('!example' => ctools_language_context_convert($context, 'direction'))), + 'enabled' => t('Status. E.g. !example', array('!example' => ctools_language_context_convert($context, 'enabled'))), + 'plurals' => t('Number of plural forms. E.g. !example', array('!example' => ctools_language_context_convert($context, 'plurals'))), + 'formula' => t('Plural formula. E.g. !example', array('!example' => ctools_language_context_convert($context, 'formula'))), + 'domain' => t('Domain prefix. E.g. !example', array('!example' => ctools_language_context_convert($context, 'domain'))), + 'prefix' => t('Url prefix . E.g. !example', array('!example' => ctools_language_context_convert($context, 'prefix'))), + 'weight' => t('The weight. E.g. !example', array('!example' => ctools_language_context_convert($context, 'weight'))), + 'javascript' => t('Key of the javascript file with the translations. E.g. !example', array('!example' => ctools_language_context_convert($context, 'javascript'))), + 'provider' => t('Negotiation method that defined the language. E.g. !example', array('!example' => ctools_language_context_convert($context, 'provider'))), + ); +} + +/** + * Convert a context property into a string to be used as a keyword. + */ +function ctools_language_context_convert($context, $type) { + if (isset($context->data->$type)) { + return $context->data->$type; + } +} + +/** + * Settings form. + */ +function ctools_context_language_settings_form($form, &$form_state) { + $conf = ctools_context_language_conf_defaults($form_state['conf']); + + $form['enable_cache_argument'] = array( + '#title' => t('Add language to cache id'), + '#description' => t('If enabled the langcode will be part of context aware caches.'), + '#type' => 'checkbox', + '#default_value' => $conf['enable_cache_argument'], + ); + + // Prepare language type options. + $language_type_options = drupal_map_assoc(language_types()); + $language_type_options['preset'] = t('Custom'); + + $form['language_type'] = array( + '#title' => t('The language type to use'), + '#type' => 'radios', + '#required' => TRUE, + '#options' => $language_type_options, + '#default_value' => $conf['language_type'], + ); + + ctools_include('language'); + $language_options = ctools_language_list(); + $form['preset_langcode'] = array( + '#title' => t('Language'), + '#type' => 'select', + '#options' => $language_options, + '#default_value' => $conf['preset_langcode'], + '#states' => array( + 'visible' => array( + ':input[name="language_type"]' => array('value' => 'preset'), + ), + ), + ); + + if (!empty($conf['preset_langcode']) && !isset($language_options[$conf['preset_langcode']])) { + drupal_set_message(t('The currently selected language %langcode is no longer available.', array('%langcode' => $conf['preset_langcode'])), 'error', FALSE); + } + return $form; +} diff --git a/sites/all/modules/contrib/dev/ctools/plugins/contexts/node.inc b/sites/all/modules/contrib/dev/ctools/plugins/contexts/node.inc index 997cd13..156f7f8 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/contexts/node.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/contexts/node.inc @@ -2,7 +2,6 @@ /** * @file - * * Plugin to provide a node context. A node context is a node wrapped in a * context object that can be utilized by anything that accepts contexts. */ @@ -106,7 +105,7 @@ function ctools_context_node_settings_form($form, &$form_state) { * Validate a node. */ function ctools_context_node_settings_form_validate($form, &$form_state) { - // Validate the autocomplete + // Validate the autocomplete. if (empty($form_state['values']['nid']) && empty($form_state['values']['node'])) { form_error($form['node'], t('You must select a node.')); return; @@ -133,7 +132,7 @@ function ctools_context_node_settings_form_validate($form, &$form_state) { $node = db_query('SELECT nid, status FROM {node} WHERE LOWER(title) = LOWER(:title)', array(':title' => $nid))->fetchObject(); } - // Do not allow unpublished nodes to be selected by unprivileged users + // Do not allow unpublished nodes to be selected by unprivileged users. if (!$node || (empty($node->status) && !(user_access('administer nodes')))) { form_error($form['node'], t('Invalid node selected.')); } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/contexts/node_add_form.inc b/sites/all/modules/contrib/dev/ctools/plugins/contexts/node_add_form.inc index f10944c..98640e2 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/contexts/node_add_form.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/contexts/node_add_form.inc @@ -2,8 +2,7 @@ /** * @file - * - * Plugin to provide a node_add_form context + * Plugin to provide a node_add_form context. */ /** @@ -65,8 +64,8 @@ function ctools_context_create_node_add_form($empty, $data = NULL, $conf = FALSE $form_state = array( 'want form' => TRUE, 'build_info' => array( - 'args' => array($node) - ) + 'args' => array($node), + ), ); // Use module_load_include so that caches and stuff can know to load this. @@ -81,10 +80,10 @@ function ctools_context_create_node_add_form($empty, $data = NULL, $conf = FALSE // These are specific pieces of data to this form. // All forms should place the form here. - $context->form = $form; - $context->form_id = $form_id; - $context->form_title = t('Submit @name', array('@name' => $types[$type]->name)); - $context->node_type = $type; + $context->form = $form; + $context->form_id = $form_id; + $context->form_title = t('Submit @name', array('@name' => $types[$type]->name)); + $context->node_type = $type; $context->restrictions['type'] = array($type); $context->restrictions['form'] = array('form'); diff --git a/sites/all/modules/contrib/dev/ctools/plugins/contexts/node_edit_form.inc b/sites/all/modules/contrib/dev/ctools/plugins/contexts/node_edit_form.inc index 7565f5c..5401ef6 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/contexts/node_edit_form.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/contexts/node_edit_form.inc @@ -2,8 +2,7 @@ /** * @file - * - * Plugin to provide a node_edit_form context + * Plugin to provide a node_edit_form context. */ /** @@ -32,7 +31,7 @@ $plugin = array( */ function ctools_context_create_node_edit_form($empty, $node = NULL, $conf = FALSE) { static $creating = FALSE; - $context = new ctools_context(array('form', 'node_edit', 'node_form', 'node_edit_form', 'node', 'entity:node')); + $context = new ctools_context(array('form', 'node_edit', 'node_form', 'node_edit_form', 'node', 'entity:node')); $context->plugin = 'node_edit_form'; if ($empty || ($creating)) { @@ -69,7 +68,7 @@ function ctools_context_create_node_edit_form($empty, $node = NULL, $conf = FALS $form = drupal_build_form($form_id, $form_state); - // Fill in the 'node' portion of the context + // Fill in the 'node' portion of the context. $context->data = $node; $context->title = isset($node->title) ? $node->title : ''; $context->argument = isset($node->nid) ? $node->nid : $node->type; @@ -126,7 +125,7 @@ function ctools_context_node_edit_form_settings_form($form, &$form_state) { * Validate a node. */ function ctools_context_node_edit_form_settings_form_validate($form, &$form_state) { - // Validate the autocomplete + // Validate the autocomplete. if (empty($form_state['values']['nid']) && empty($form_state['values']['node'])) { form_error($form['node'], t('You must select a node.')); return; @@ -153,7 +152,7 @@ function ctools_context_node_edit_form_settings_form_validate($form, &$form_stat $node = db_query('SELECT nid, status FROM {node} WHERE LOWER(title) = LOWER(:title)', array(':title' => $nid))->fetchObject(); } - // Do not allow unpublished nodes to be selected by unprivileged users + // Do not allow unpublished nodes to be selected by unprivileged users. if (!$node || (empty($node->status) && !(user_access('administer nodes')))) { form_error($form['node'], t('Invalid node selected.')); } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/contexts/query_string.inc b/sites/all/modules/contrib/dev/ctools/plugins/contexts/query_string.inc new file mode 100644 index 0000000..23ade91 --- /dev/null +++ b/sites/all/modules/contrib/dev/ctools/plugins/contexts/query_string.inc @@ -0,0 +1,90 @@ + t('Query string value'), + 'description' => t('A context that extracts a value from the query string.'), + 'context' => 'ctools_context_query_string_create_query_string', + 'context name' => 'query_string', + 'keyword' => 'query_string', + 'edit form' => 'ctools_context_query_string_settings_form', + 'convert list' => array( + 'raw' => t('Raw string'), + 'html_safe' => t('HTML-safe string'), + ), + 'convert' => 'ctools_context_query_string_convert', +); + +/** + * Create a context from manual configuration. + */ +function ctools_context_query_string_create_query_string($empty, $data = NULL, $conf = FALSE) { + $context = new ctools_context('query_string'); + $context->plugin = 'query_string'; + + if ($empty) { + return $context; + } + + if ($conf) { + if (!empty($_GET[$data['key']])) { + $context->data = $_GET[$data['key']]; + } + else { + $context->data = $data['fallback_value']; + } + } + return $context; +} + +/** + * Form builder; settings for the context. + */ +function ctools_context_query_string_settings_form($form, &$form_state) { + $form['key'] = array( + '#title' => t('Query string key'), + '#description' => t('Enter the key of the value that must be returned from the query string.'), + '#type' => 'textfield', + '#required' => TRUE, + ); + if (isset($form_state['conf']['key'])) { + $form['key']['#default_value'] = $form_state['conf']['key']; + } + $form['fallback_value'] = array( + '#title' => t('Fallback value'), + '#description' => t('Enter a value that must be returned if the above specified key does not exist in the query string.'), + '#type' => 'textfield', + ); + if (!empty($form_state['conf']['fallback_value'])) { + $form['fallback_value']['#default_value'] = $form_state['conf']['fallback_value']; + } + return $form; +} + +/** + * Submit handler; settings form for the context. + */ +function ctools_context_query_string_settings_form_submit($form, &$form_state) { + $form_state['conf']['key'] = $form_state['values']['key']; + $form_state['conf']['fallback_value'] = $form_state['values']['fallback_value']; +} + +/** + * Convert a context into a string. + */ +function ctools_context_query_string_convert($context, $type) { + switch ($type) { + case 'raw': + return $context->data; + + case 'html_safe': + return check_plain($context->data); + } +} diff --git a/sites/all/modules/contrib/dev/ctools/plugins/contexts/string.inc b/sites/all/modules/contrib/dev/ctools/plugins/contexts/string.inc index e731ab7..981b9f7 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/contexts/string.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/contexts/string.inc @@ -2,8 +2,7 @@ /** * @file - * - * Plugin to provide a string context + * Plugin to provide a string context. */ /** @@ -38,7 +37,6 @@ $plugin = array( function ctools_context_create_string($empty, $data = NULL, $conf = FALSE) { // The input is expected to be an object as created by ctools_break_phrase // which contains a group of string. - $context = new ctools_context('string'); $context->plugin = 'string'; @@ -46,7 +44,7 @@ function ctools_context_create_string($empty, $data = NULL, $conf = FALSE) { return $context; } - if ($data !== FALSE ) { + if ($data !== FALSE) { // Support the array storage from the settings form but also handle direct input from arguments. $context->data = is_array($data) ? $data['string'] : $data; $context->title = ($conf) ? check_plain($data['identifier']) : check_plain($data); @@ -61,8 +59,10 @@ function ctools_context_string_convert($context, $type) { switch ($type) { case 'raw': return $context->data; + case 'html_safe': return check_plain($context->data); + case 'uppercase_words_html_safe': return ucwords(str_replace('-', ' ', check_plain($context->data))); } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/contexts/term.inc b/sites/all/modules/contrib/dev/ctools/plugins/contexts/term.inc index 1d6d48e..56fe06c 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/contexts/term.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/contexts/term.inc @@ -2,8 +2,7 @@ /** * @file - * - * Plugin to provide a term context + * Plugin to provide a term context. */ /** @@ -116,7 +115,7 @@ function ctools_context_term_settings_form($form, &$form_state) { * Validate a term. */ function ctools_context_term_settings_form_validate($form, &$form_state) { - // Validate the autocomplete + // Validate the autocomplete. $vid = $form_state['values']['vid']; if (empty($form_state['values']['tid']) && empty($form_state['values']['taxonomy'][$vid])) { form_error($form['taxonomy'][$vid], t('You must select a term.')); @@ -154,12 +153,16 @@ function ctools_context_term_convert($context, $type) { switch ($type) { case 'tid': return $context->data->tid; + case 'name': return $context->data->name; + case 'name_dashed': return drupal_strtolower(str_replace(' ', '-', $context->data->name)); + case 'vid': return $context->data->vid; + case 'description': return $context->data->description; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/contexts/terms.inc b/sites/all/modules/contrib/dev/ctools/plugins/contexts/terms.inc index 6c3ab36..1161ded 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/contexts/terms.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/contexts/terms.inc @@ -2,8 +2,7 @@ /** * @file - * - * Plugin to provide a terms context + * Plugin to provide a terms context. */ /** @@ -37,7 +36,6 @@ $plugin = array( function ctools_context_create_terms($empty, $data = NULL, $conf = FALSE) { // The input is expected to be an object as created by ctools_break_phrase // which contains a group of terms. - $context = new ctools_context(array('terms', 'entity:taxonomy_term')); $context->plugin = 'terms'; @@ -49,7 +47,7 @@ function ctools_context_create_terms($empty, $data = NULL, $conf = FALSE) { $context->operator = $data->operator; $context->tids = $data->value; if (!isset($data->term)) { - // load the first term: + // Load the first term: reset($context->tids); $data->term = taxonomy_term_load(current($context->tids)); } @@ -67,12 +65,16 @@ function ctools_context_terms_convert($context, $type) { switch ($type) { case 'tid': return $context->data->tid; + case 'tids': return $context->argument; + case 'name': return $context->data->name; + case 'name_dashed': return drupal_strtolower(str_replace(' ', '-', $context->data->name)); + case 'names': case 'names_dashed': // We only run this query if this item was requested: @@ -92,6 +94,7 @@ function ctools_context_terms_convert($context, $type) { } } return $context->names; + case 'vid': return $context->data->vid; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/contexts/token.inc b/sites/all/modules/contrib/dev/ctools/plugins/contexts/token.inc index 0445576..6286a69 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/contexts/token.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/contexts/token.inc @@ -2,7 +2,7 @@ /** * @file - * Provide a global context to allow for token support. + * Provide a global context to allow for token support. */ $plugin = array( @@ -17,6 +17,16 @@ $plugin = array( /** * Create a context from manual configuration. + * + * @param $empty + * Unused. + * @param $data + * Unused. + * @param $conf + * Unused. + * + * @return ctools_context + * A context of type token, with the plugin set appropriately. */ function ctools_context_create_token($empty, $data = NULL, $conf = FALSE) { $context = new ctools_context('token'); @@ -27,9 +37,14 @@ function ctools_context_create_token($empty, $data = NULL, $conf = FALSE) { /** * Implementation of hook_ctools_context_convert_list(). + * + * @return array|null + * An array of token type information, keyed by 'type:id', or NULL if + * none found. */ function ctools_context_token_convert_list() { $tokens = token_info(); + // Initialise $list here? foreach ($tokens['types'] as $type => $type_info) { if (empty($type_info['needs-data'])) { $real_type = isset($type_info['type']) ? $type_info['type'] : $type; @@ -46,7 +61,17 @@ function ctools_context_token_convert_list() { } /** - * Implementation of hook_ctools_context_converter_alter(). + * Token conversion function: look up the token and return it's value. + * + * @param $context + * Unused. + * @param string $token + * The name of the token. + * + * @return array|null + * The token value, or NULL if not found. + * + * @see ctools_context_convert_context() */ function ctools_context_token_convert($context, $token) { $tokens = token_info(); @@ -59,4 +84,5 @@ function ctools_context_token_convert($context, $token) { return $values[$token]; } } + return NULL; } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/contexts/user.inc b/sites/all/modules/contrib/dev/ctools/plugins/contexts/user.inc index 02bfe98..bbdbd1a 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/contexts/user.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/contexts/user.inc @@ -2,8 +2,7 @@ /** * @file - * - * Plugin to provide a user context + * Plugin to provide a user context. */ /** @@ -43,7 +42,9 @@ function ctools_context_create_user($empty, $data = NULL, $conf = FALSE) { if ($data['type'] == 'current') { global $user; $data = user_load($user->uid); - $data->logged_in_user = TRUE; + if (user_is_logged_in()) { + $data->logged_in_user = TRUE; + } } else { $data = user_load($data['uid']); @@ -116,7 +117,7 @@ function ctools_context_user_settings_form_validate($form, &$form_state) { return; } - // Validate the autocomplete + // Validate the autocomplete. if (empty($form_state['values']['uid']) && empty($form_state['values']['user'])) { form_error($form['user'], t('You must select a user.')); return; @@ -151,6 +152,7 @@ function ctools_context_user_settings_form_submit($form, &$form_state) { */ function ctools_context_user_convert_list() { $tokens = token_info(); + $list = array(); foreach ($tokens['tokens']['user'] as $id => $info) { if (!isset($list[$id])) { $list[$id] = $info['name']; diff --git a/sites/all/modules/contrib/dev/ctools/plugins/contexts/user_edit_form.inc b/sites/all/modules/contrib/dev/ctools/plugins/contexts/user_edit_form.inc index da0cca0..61f234a 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/contexts/user_edit_form.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/contexts/user_edit_form.inc @@ -1,8 +1,8 @@ data = $user; $context->title = isset($user->name) ? $user->name : ''; $context->argument = $user->uid; @@ -107,7 +107,7 @@ function ctools_context_user_edit_form_settings_form($form, &$form_state) { ); if (!empty($conf['uid'])) { - $info = db_query('SELECT * FROM {user} WHERE uid = :uid', array(':uid' => $conf['uid']))->fetchObject(); + $info = db_query('SELECT * FROM {users} WHERE uid = :uid', array(':uid' => $conf['uid']))->fetchObject(); if ($info) { $link = l(t("'%name' [user id %uid]", array('%name' => $info->name, '%uid' => $info->uid)), "user/$info->uid", array('attributes' => array('target' => '_blank', 'title' => t('Open in new window')), 'html' => TRUE)); $form['user']['#description'] = t('Currently set to !link', array('!link' => $link)); @@ -133,7 +133,7 @@ function ctools_context_user_edit_form_settings_form($form, &$form_state) { * Validate a node. */ function ctools_context_user_edit_form_settings_form_validate($form, &$form_state) { - // Validate the autocomplete + // Validate the autocomplete. if (empty($form_state['values']['uid']) && empty($form_state['values']['user'])) { form_error($form['user'], t('You must select a user.')); return; @@ -154,14 +154,15 @@ function ctools_context_user_edit_form_settings_form_validate($form, &$form_stat $uid = $preg_matches[1]; } if (is_numeric($uid)) { - $user = db_query('SELECT uid FROM {user} WHEREuid = :uid', array(':uid' => $uid))->fetchObject(); + $user = db_query('SELECT uid FROM {users} WHERE uid = :uid', array(':uid' => $uid))->fetchObject(); } else { - $user = db_query('SELECT uid FROM {user} WHERE LOWER(name) = LOWER(:name)', array(':name' => $uid))->fetchObject(); + $user = db_query('SELECT uid FROM {users} WHERE LOWER(name) = LOWER(:name)', array(':name' => $uid))->fetchObject(); } form_set_value($form['uid'], $user->uid, $form_state); } + function ctools_context_user_edit_form_settings_form_submit($form, &$form_state) { if ($form_state['values']['set_identifier']) { $user = user_load($form_state['values']['uid']); diff --git a/sites/all/modules/contrib/dev/ctools/plugins/contexts/vocabulary.inc b/sites/all/modules/contrib/dev/ctools/plugins/contexts/vocabulary.inc index 9c5d4cf..e5b0b9c 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/contexts/vocabulary.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/contexts/vocabulary.inc @@ -2,8 +2,7 @@ /** * @file - * - * Plugin to provide a vocabulary context + * Plugin to provide a vocabulary context. */ /** diff --git a/sites/all/modules/contrib/dev/ctools/plugins/export_ui/ctools_export_ui.class.php b/sites/all/modules/contrib/dev/ctools/plugins/export_ui/ctools_export_ui.class.php index 252cdb1..0c8145e 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/export_ui/ctools_export_ui.class.php +++ b/sites/all/modules/contrib/dev/ctools/plugins/export_ui/ctools_export_ui.class.php @@ -13,7 +13,7 @@ class ctools_export_ui { * constructor because we are retaining PHP4 compatibility, which * would require all child classes to implement their own constructor. */ - function init($plugin) { + public function init($plugin) { ctools_include('export'); $this->plugin = $plugin; @@ -22,7 +22,7 @@ class ctools_export_ui { /** * Get a page title for the current page from our plugin strings. */ - function get_page_title($op, $item = NULL) { + public function get_page_title($op, $item = NULL) { if (empty($this->plugin['strings']['title'][$op])) { return; } @@ -43,13 +43,13 @@ class ctools_export_ui { * This can be overridden for modules that want to be able to export * items currently being edited, for example. */ - function load_item($item_name) { + public function load_item($item_name) { $item = ctools_export_crud_load($this->plugin['schema'], $item_name); return empty($item) ? FALSE : $item; } // ------------------------------------------------------------------------ - // Menu item manipulation + // Menu item manipulation. /** * hook_menu() entry point. @@ -57,7 +57,7 @@ class ctools_export_ui { * Child implementations that need to add or modify menu items should * probably call parent::hook_menu($items) and then modify as needed. */ - function hook_menu(&$items) { + public function hook_menu(&$items) { // During upgrades, the schema can be empty as this is called prior to // actual update functions being run. Ensure that we can cope with this // situation. @@ -99,7 +99,7 @@ class ctools_export_ui { * @return * TRUE if the current user has access, FALSE if not. */ - function access($op, $item) { + public function access($op, $item) { if (!user_access($this->plugin['access'])) { return FALSE; } @@ -122,14 +122,19 @@ class ctools_export_ui { switch ($op) { case 'import': return user_access('use ctools import'); + case 'revert': return ($item->export_type & EXPORT_IN_DATABASE) && ($item->export_type & EXPORT_IN_CODE); + case 'delete': return ($item->export_type & EXPORT_IN_DATABASE) && !($item->export_type & EXPORT_IN_CODE); + case 'disable': return empty($item->disabled); + case 'enable': return !empty($item->disabled); + default: return TRUE; } @@ -144,7 +149,7 @@ class ctools_export_ui { * It is unlikely that a child object will need to override this method, * unless the listing mechanism is going to be highly specialized. */ - function list_page($js, $input) { + public function list_page($js, $input) { $this->items = ctools_export_crud_load_all($this->plugin['schema'], $js); // Respond to a reset command by clearing session and doing a drupal goto @@ -154,7 +159,7 @@ class ctools_export_ui { if (!$js) { drupal_goto($_GET['q']); } - // clear everything but form id, form build id and form token: + // Clear everything but form id, form build id and form token. $keys = array_keys($input); foreach ($keys as $id) { if (!in_array($id, array('form_id', 'form_build_id', 'form_token'))) { @@ -224,7 +229,7 @@ class ctools_export_ui { * get the base form and then modify it as necessary to add search * gadgets for custom fields. */ - function list_form(&$form, &$form_state) { + public function list_form(&$form, &$form_state) { // This forces the form to *always* treat as submitted which is // necessary to make it work. $form['#token'] = FALSE; @@ -269,7 +274,7 @@ class ctools_export_ui { '#title' => t('Enabled'), '#options' => $all + array( '0' => t('Enabled'), - '1' => t('Disabled') + '1' => t('Disabled'), ), '#default_value' => 'all', ); @@ -324,7 +329,7 @@ class ctools_export_ui { * It is very rare that a filter form needs validation, but if it is * needed, override this. */ - function list_form_validate(&$form, &$form_state) { } + public function list_form_validate(&$form, &$form_state) { } /** * Submit the filter/sort form. @@ -336,7 +341,7 @@ class ctools_export_ui { * For the most part, you should not need to override this method, as the * fiddly bits call through to other functions. */ - function list_form_submit(&$form, &$form_state) { + public function list_form_submit(&$form, &$form_state) { // Filter and re-sort the pages. $plugin = $this->plugin; @@ -381,7 +386,7 @@ class ctools_export_ui { * @return * TRUE if the item should be excluded. */ - function list_filter($form_state, $item) { + public function list_filter($form_state, $item) { $schema = ctools_export_get_schema($this->plugin['schema']); if ($form_state['values']['storage'] != 'all' && $form_state['values']['storage'] != $item->{$schema['export']['export type string']}) { return TRUE; @@ -411,7 +416,7 @@ class ctools_export_ui { * This widget will search against whatever fields are configured here. By * default it will attempt to search against the name, title and description fields. */ - function list_search_fields() { + public function list_search_fields() { $fields = array( $this->plugin['export']['key'], ); @@ -432,7 +437,7 @@ class ctools_export_ui { * Override this if you wish to provide more or change how these work. * The actual handling of the sorting will happen in build_row(). */ - function list_sort_options() { + public function list_sort_options() { if (!empty($this->plugin['export']['admin_title'])) { $options = array( 'disabled' => t('Enabled, title'), @@ -458,14 +463,14 @@ class ctools_export_ui { * * Override this if you need custom CSS for your list. */ - function list_css() { + public function list_css() { ctools_add_css('export-ui-list'); } /** * Builds the operation links for a specific exportable item. */ - function build_operations($item) { + public function build_operations($item) { $plugin = $this->plugin; $schema = ctools_export_get_schema($plugin['schema']); $operations = $plugin['allowed operations']; @@ -515,7 +520,7 @@ class ctools_export_ui { * method, so this is building up a row suitable for theme('table'). * This doesn't have to be true if you override both. */ - function list_build_row($item, &$form_state, $operations) { + public function list_build_row($item, &$form_state, $operations) { // Set up sorting $name = $item->{$this->plugin['export']['key']}; $schema = ctools_export_get_schema($this->plugin['schema']); @@ -563,7 +568,7 @@ class ctools_export_ui { * If you've added columns via list_build_row() but are still using a * table, override this method to set up the table header. */ - function list_table_header() { + public function list_table_header() { $header = array(); if (!empty($this->plugin['export']['admin_title'])) { $header[] = array('data' => t('Title'), 'class' => array('ctools-export-ui-title')); @@ -585,7 +590,7 @@ class ctools_export_ui { * Whatever you do if this method is overridden, the ID is important for AJAX * so be sure it exists. */ - function list_render(&$form_state) { + public function list_render(&$form_state) { $table = array( 'header' => $this->list_table_header(), 'rows' => $this->rows, @@ -600,14 +605,14 @@ class ctools_export_ui { * * This will appear after the filter/sort widgets. */ - function list_header($form_state) { } + public function list_header($form_state) { } /** * Render a footer to go after thie list. * * This is a good place to add additional links. */ - function list_footer($form_state) { } + public function list_footer($form_state) { } // ------------------------------------------------------------------------ // These methods are the API for adding/editing exportable items @@ -623,7 +628,7 @@ class ctools_export_ui { * The item in use; this may be necessary as item IDs are often embedded in * redirects. */ - function redirect($op, $item = NULL) { + public function redirect($op, $item = NULL) { if (isset($this->plugin['redirect'][$op])) { $destination = (array) $this->plugin['redirect'][$op]; if ($item) { @@ -638,7 +643,8 @@ class ctools_export_ui { } } - function add_page($js, $input, $step = NULL) { + public function add_page($js, $input, $step = NULL) { + $args = func_get_args(); drupal_set_title($this->get_page_title('add'), PASS_THROUGH); // If a step not set, they are trying to create a new item. If a step @@ -661,7 +667,7 @@ class ctools_export_ui { 'no_redirect' => TRUE, 'step' => $step, // Store these in case additional args are needed. - 'function args' => func_get_args(), + 'function args' => $args, ); $output = $this->edit_execute_form($form_state); @@ -675,7 +681,8 @@ class ctools_export_ui { /** * Main entry point to edit an item. */ - function edit_page($js, $input, $item, $step = NULL) { + public function edit_page($js, $input, $item, $step = NULL) { + $args = func_get_args(); drupal_set_title($this->get_page_title('edit', $item), PASS_THROUGH); // Check to see if there is a cached item to get if we're using the wizard. @@ -697,7 +704,7 @@ class ctools_export_ui { 'no_redirect' => TRUE, 'step' => $step, // Store these in case additional args are needed. - 'function args' => func_get_args(), + 'function args' => $args, ); $output = $this->edit_execute_form($form_state); @@ -711,7 +718,8 @@ class ctools_export_ui { /** * Main entry point to clone an item. */ - function clone_page($js, $input, $original, $step = NULL) { + public function clone_page($js, $input, $original, $step = NULL) { + $args = func_get_args(); drupal_set_title($this->get_page_title('clone', $original), PASS_THROUGH); // If a step not set, they are trying to create a new clone. If a step @@ -724,7 +732,13 @@ class ctools_export_ui { // Export the handler, which is a fantastic way to clean database IDs out of it. $export = ctools_export_crud_export($this->plugin['schema'], $original); $item = ctools_export_crud_import($this->plugin['schema'], $export); - $item->{$this->plugin['export']['key']} = 'clone_of_' . $item->{$this->plugin['export']['key']}; + + if (!empty($input[$this->plugin['export']['key']])) { + $item->{$this->plugin['export']['key']} = $input[$this->plugin['export']['key']]; + } + else { + $item->{$this->plugin['export']['key']} = 'clone_of_' . $item->{$this->plugin['export']['key']}; + } } // Tabs and breadcrumb disappearing, this helps alleviate through cheating. @@ -745,7 +759,7 @@ class ctools_export_ui { 'no_redirect' => TRUE, 'step' => $step, // Store these in case additional args are needed. - 'function args' => func_get_args(), + 'function args' => $args, ); $output = $this->edit_execute_form($form_state); @@ -762,7 +776,7 @@ class ctools_export_ui { * Add and Edit both funnel into this, but they have a few different * settings. */ - function edit_execute_form(&$form_state) { + public function edit_execute_form(&$form_state) { if (!empty($this->plugin['use wizard'])) { return $this->edit_execute_form_wizard($form_state); } @@ -776,7 +790,7 @@ class ctools_export_ui { * * By default, export UI will provide a single form for editing an object. */ - function edit_execute_form_standard(&$form_state) { + public function edit_execute_form_standard(&$form_state) { $output = drupal_build_form('ctools_export_ui_edit_item_form', $form_state); if (!empty($form_state['executed']) && empty($form_state['rebuild'])) { $this->edit_save_form($form_state); @@ -797,7 +811,7 @@ class ctools_export_ui { * @param array $form_state * The already created form state. */ - function get_wizard_info(&$form_state) { + public function get_wizard_info(&$form_state) { if (!isset($form_state['step'])) { $form_state['step'] = NULL; } @@ -872,7 +886,7 @@ class ctools_export_ui { * Using 'add order' or 'edit order' can be used to ensure that add/edit order * is different. */ - function edit_execute_form_wizard(&$form_state) { + public function edit_execute_form_wizard(&$form_state) { $form_info = $this->get_wizard_info($form_state); // If there aren't any forms set, fail. @@ -895,7 +909,7 @@ class ctools_export_ui { if (!empty($form_state['complete'])) { $this->edit_save_form($form_state); } - else if ($output && !empty($form_state['item']->export_ui_item_is_cached)) { + elseif ($output && !empty($form_state['item']->export_ui_item_is_cached)) { // @todo this should be in the plugin strings drupal_set_message(t('You have unsaved changes. These changes will not be made permanent until you click Save.'), 'warning'); } @@ -913,7 +927,7 @@ class ctools_export_ui { * * The wizard callback delegates this back to the object. */ - function edit_wizard_back(&$form_state) { + public function edit_wizard_back(&$form_state) { // This only exists so child implementations can use it. } @@ -922,7 +936,7 @@ class ctools_export_ui { * * The wizard callback delegates this back to the object. */ - function edit_wizard_next(&$form_state) { + public function edit_wizard_next(&$form_state) { $this->edit_cache_set($form_state['item'], $form_state['form type']); } @@ -931,7 +945,7 @@ class ctools_export_ui { * * The wizard callback delegates this back to the object. */ - function edit_wizard_cancel(&$form_state) { + public function edit_wizard_cancel(&$form_state) { $this->edit_cache_clear($form_state['item'], $form_state['form type']); } @@ -940,7 +954,7 @@ class ctools_export_ui { * * The wizard callback delegates this back to the object. */ - function edit_wizard_finish(&$form_state) { + public function edit_wizard_finish(&$form_state) { $form_state['complete'] = TRUE; // If we are importing, and overwrite was selected, delete the original so @@ -955,7 +969,7 @@ class ctools_export_ui { /** * Retrieve the item currently being edited from the object cache. */ - function edit_cache_get($item, $op = 'edit') { + public function edit_cache_get($item, $op = 'edit') { ctools_include('object-cache'); if (is_string($item)) { $name = $item; @@ -974,20 +988,20 @@ class ctools_export_ui { /** * Cache the item currently currently being edited. */ - function edit_cache_set($item, $op = 'edit') { + public function edit_cache_set($item, $op = 'edit') { ctools_include('object-cache'); $name = $this->edit_cache_get_key($item, $op); return $this->edit_cache_set_key($item, $name); } - function edit_cache_set_key($item, $name) { + public function edit_cache_set_key($item, $name) { return ctools_object_cache_set('ctui_' . $this->plugin['name'], $name, $item); } /** * Clear the object cache for the currently edited item. */ - function edit_cache_clear($item, $op = 'edit') { + public function edit_cache_clear($item, $op = 'edit') { ctools_include('object-cache'); $name = $this->edit_cache_get_key($item, $op); return ctools_object_cache_clear('ctui_' . $this->plugin['name'], $name); @@ -996,7 +1010,7 @@ class ctools_export_ui { /** * Figure out what the cache key is for this object. */ - function edit_cache_get_key($item, $op) { + public function edit_cache_get_key($item, $op) { $export_key = $this->plugin['export']['key']; return $op == 'edit' ? $item->{$this->plugin['export']['key']} : "::$op"; } @@ -1004,7 +1018,7 @@ class ctools_export_ui { /** * Called to save the final product from the edit form. */ - function edit_save_form($form_state) { + public function edit_save_form($form_state) { $item = &$form_state['item']; $export_key = $this->plugin['export']['key']; @@ -1023,7 +1037,7 @@ class ctools_export_ui { /** * Provide the actual editing form. */ - function edit_form(&$form, &$form_state) { + public function edit_form(&$form, &$form_state) { $export_key = $this->plugin['export']['key']; $item = $form_state['item']; $schema = ctools_export_get_schema($this->plugin['schema']); @@ -1096,7 +1110,7 @@ class ctools_export_ui { /** * Validate callback for the edit form. */ - function edit_form_validate(&$form, &$form_state) { + public function edit_form_validate(&$form, &$form_state) { if (!empty($this->plugin['form']['validate'])) { // Pass $form by reference. $this->plugin['form']['validate']($form, $form_state); @@ -1107,7 +1121,7 @@ class ctools_export_ui { * Perform a final validation check before allowing the form to be * finished. */ - function edit_finish_validate(&$form, &$form_state) { + public function edit_finish_validate(&$form, &$form_state) { if ($form_state['op'] != 'edit') { // Validate the export key. Fake an element for form_error(). $export_key = $this->plugin['export']['key']; @@ -1129,7 +1143,7 @@ class ctools_export_ui { * If the keys all match up to the schema, this method will not need to be * overridden. */ - function edit_form_submit(&$form, &$form_state) { + public function edit_form_submit(&$form, &$form_state) { if (!empty($this->plugin['form']['submit'])) { // Pass $form by reference. $this->plugin['form']['submit']($form, $form_state); @@ -1151,14 +1165,14 @@ class ctools_export_ui { /** * Callback to enable a page. */ - function enable_page($js, $input, $item) { + public function enable_page($js, $input, $item) { return $this->set_item_state(FALSE, $js, $input, $item); } /** * Callback to disable a page. */ - function disable_page($js, $input, $item) { + public function disable_page($js, $input, $item) { return $this->set_item_state(TRUE, $js, $input, $item); } @@ -1168,7 +1182,7 @@ class ctools_export_ui { * If javascript is in use, this will rebuild the list and send that back * as though the filter form had been executed. */ - function set_item_state($state, $js, $input, $item) { + public function set_item_state($state, $js, $input, $item) { ctools_export_crud_set_status($this->plugin['schema'], $item, $state); if (!$js) { @@ -1182,7 +1196,7 @@ class ctools_export_ui { /** * Page callback to delete an exportable item. */ - function delete_page($js, $input, $item) { + public function delete_page($js, $input, $item) { $form_state = array( 'plugin' => $this->plugin, 'object' => &$this, @@ -1205,7 +1219,7 @@ class ctools_export_ui { /** * Deletes exportable items from the database. */ - function delete_form_submit(&$form_state) { + public function delete_form_submit(&$form_state) { $item = $form_state['item']; ctools_export_crud_delete($this->plugin['schema'], $item); @@ -1217,7 +1231,7 @@ class ctools_export_ui { /** * Page callback to display export information for an exportable item. */ - function export_page($js, $input, $item) { + public function export_page($js, $input, $item) { drupal_set_title($this->get_page_title('export', $item), PASS_THROUGH); return drupal_get_form('ctools_export_form', ctools_export_crud_export($this->plugin['schema'], $item), t('Export')); } @@ -1225,7 +1239,8 @@ class ctools_export_ui { /** * Page callback to import information for an exportable item. */ - function import_page($js, $input, $step = NULL) { + public function import_page($js, $input, $step = NULL) { + $args = func_get_args(); drupal_set_title($this->get_page_title('import'), PASS_THROUGH); // Import is basically a multi step wizard form, so let's go ahead and // use CTools' wizard.inc for it. @@ -1250,7 +1265,7 @@ class ctools_export_ui { 'no_redirect' => TRUE, 'step' => $step, // Store these in case additional args are needed. - 'function args' => func_get_args(), + 'function args' => $args, ); // import always uses the wizard. @@ -1266,7 +1281,7 @@ class ctools_export_ui { * Import form. Provides simple helptext instructions and textarea for * pasting a export definition. */ - function edit_form_import(&$form, &$form_state) { + public function edit_form_import(&$form, &$form_state) { $form['help'] = array( '#type' => 'item', '#value' => $this->plugin['strings']['help']['import'], @@ -1292,7 +1307,7 @@ class ctools_export_ui { * * Evaluates code and make sure it creates an object before we continue. */ - function edit_form_import_validate($form, &$form_state) { + public function edit_form_import_validate($form, &$form_state) { $item = ctools_export_crud_import($this->plugin['schema'], $form_state['values']['import']); if (is_string($item)) { form_error($form['import'], t('Unable to get an import from the code. Errors reported: @errors', array('@errors' => $item))); @@ -1309,7 +1324,7 @@ class ctools_export_ui { * * Stores the item in the session. */ - function edit_form_import_submit($form, &$form_state) { + public function edit_form_import_submit($form, &$form_state) { // The validate function already imported and stored the item. This // function exists simply to prevent it from going to the default // edit_form_submit() method. @@ -1441,7 +1456,7 @@ function ctools_export_ui_delete_confirm_form($form, &$form_state) { $export_key = $plugin['export']['key']; $question = str_replace('%title', check_plain($item->{$export_key}), $plugin['strings']['confirmation'][$form_state['op']]['question']); - $path = empty($_REQUEST['cancel_path']) ? ctools_export_ui_plugin_base_path($plugin) : $_REQUEST['cancel_path']; + $path = (!empty($_REQUEST['cancel_path']) && !url_is_external($_REQUEST['cancel_path'])) ? $_REQUEST['cancel_path'] : ctools_export_ui_plugin_base_path($plugin); $form = confirm_form($form, $question, diff --git a/sites/all/modules/contrib/dev/ctools/plugins/export_ui/ctools_export_ui.inc b/sites/all/modules/contrib/dev/ctools/plugins/export_ui/ctools_export_ui.inc index 4e0a849..3087cca 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/export_ui/ctools_export_ui.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/export_ui/ctools_export_ui.inc @@ -1,6 +1,7 @@ FALSE, diff --git a/sites/all/modules/contrib/dev/ctools/plugins/relationships/entity_from_field.inc b/sites/all/modules/contrib/dev/ctools/plugins/relationships/entity_from_field.inc index d4880c6..2589efd 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/relationships/entity_from_field.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/relationships/entity_from_field.inc @@ -140,7 +140,7 @@ function ctools_entity_from_field_get_children($parent_plugin, $parent) { $plugin_entities = array( 'to' => array($to_entity => $to_entity_info), - 'from' => array($from_entity => $from_entity_info) + 'from' => array($from_entity => $from_entity_info), ); drupal_alter('ctools_entity_context', $plugins[$key], $plugin_entities, $key); } @@ -185,7 +185,7 @@ function ctools_entity_from_field_context($context, $conf) { $loaded_to_entity = array_shift($loaded_to_entity); // Pass current user account and entity type to access callback. - if (function_exists($to_entity_info['access callback']) && !call_user_func($to_entity_info['access callback'], 'view', $loaded_to_entity, $account, $to_entity)) { + if (isset($to_entity_info['access callback']) && function_exists($to_entity_info['access callback']) && !call_user_func($to_entity_info['access callback'], 'view', $loaded_to_entity, $account, $to_entity)) { return ctools_context_create_empty('entity:' . $to_entity, NULL); } else { diff --git a/sites/all/modules/contrib/dev/ctools/plugins/relationships/entity_from_query_string.inc b/sites/all/modules/contrib/dev/ctools/plugins/relationships/entity_from_query_string.inc new file mode 100644 index 0000000..80c40fc --- /dev/null +++ b/sites/all/modules/contrib/dev/ctools/plugins/relationships/entity_from_query_string.inc @@ -0,0 +1,70 @@ + t('Entity from query string'), + 'keyword' => 'query_string_entity', + 'description' => t('Entity from query string.'), + 'required context' => new ctools_context_required(t('Query string'), 'query_string'), + 'context' => 'ctools_entity_from_query_string_context', + 'edit form' => 'ctools_entity_from_query_string_settings_form', +); + +/** + * Return a new context based on an existing context. + */ +function ctools_entity_from_query_string_context($context, $conf) { + $entity_type = $conf['entity_type']; + + // If unset it wants a generic, unfilled context, which is just NULL. + if (empty($context->data) || !isset($context->data) || !is_numeric($context->data)) { + return ctools_context_create_empty('entity:' . $entity_type, NULL); + } + + if (!empty($context->data) && is_numeric($context->data)) { + // Load the entity from the query string value. + $entity_id = $context->data; + $entity = entity_load_single($entity_type, $entity_id); + + // Send it to ctools. + return ctools_context_create('entity:' . $entity_type, $entity); + } +} + +/** + * Settings form for the relationship. + */ +function ctools_entity_from_query_string_settings_form($form, &$form_state) { + + // Get all avalible enity types. + foreach (entity_get_info() as $key => $value) { + $entity_types[$key] = $value['label']; + } + + $form['entity_type'] = array( + '#title' => t('Entity type'), + '#description' => t('Choose entity type to load from query value'), + '#type' => 'select', + '#options' => $entity_types, + ); + if (isset($form_state['conf']['entity_type'])) { + $form['entity_type']['#default_value'] = $form_state['conf']['entity_type']; + } + + return $form; +} + +/** + * Submit handler; settings form for the context. + */ +function ctools_entity_from_query_string_settings_form_submit($form, &$form_state) { + $form_state['conf']['entity_type'] = $form_state['values']['entity_type']; +} diff --git a/sites/all/modules/contrib/dev/ctools/plugins/relationships/entity_from_schema.inc b/sites/all/modules/contrib/dev/ctools/plugins/relationships/entity_from_schema.inc index 809473e..edf6abe 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/relationships/entity_from_schema.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/relationships/entity_from_schema.inc @@ -88,7 +88,7 @@ function ctools_entity_from_schema_get_children($parent_plugin, $parent) { $plugin_entities = array('to' => $from_entity_info, 'from' => $to_entity_info); $plugin_entities = array('to' => array($from_entity => $from_entity_info), 'from' => array($to_entity => $to_entity_info)); drupal_alter('ctools_entity_context', $plugin, $plugin_entities, $plugin_id); - $plugins[$plugin_id] = $plugin; + $plugins[$plugin_id] = $plugin; } } diff --git a/sites/all/modules/contrib/dev/ctools/plugins/relationships/term_parent.inc b/sites/all/modules/contrib/dev/ctools/plugins/relationships/term_parent.inc index f084cca..c884e0a 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/relationships/term_parent.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/relationships/term_parent.inc @@ -1,7 +1,7 @@ data; $terms = array(); - + $fields = field_info_instances('node', $node->type); foreach ($fields as $name => $info) { $field_info = field_info_field($name); @@ -43,8 +43,24 @@ function ctools_terms_from_node_context($context, $conf) { } } } + elseif ($field_info['type'] == 'entityreference' && $field_info['settings']['target_type'] == 'taxonomy_term') { + $items = field_get_items('node', $node, $name); + if (is_array($items)) { + $tids = array(); + foreach ($items as $item) { + $tids[] = $item['target_id']; + } + + $term_objects = taxonomy_term_load_multiple($tids); + foreach ($term_objects as $term) { + if (empty($conf['vocabulary']) || in_array($term->vocabulary_machine_name, $conf['vocabulary'])) { + $terms[] = $term->tid; + } + } + } + } } - + if (!empty($terms)) { $all_terms = ctools_break_phrase(implode($conf['concatenator'], $terms)); return ctools_context_create('terms', $all_terms); diff --git a/sites/all/modules/contrib/dev/ctools/plugins/relationships/user_category_edit_form_from_user.inc b/sites/all/modules/contrib/dev/ctools/plugins/relationships/user_category_edit_form_from_user.inc index 28dac72..07eb382 100644 --- a/sites/all/modules/contrib/dev/ctools/plugins/relationships/user_category_edit_form_from_user.inc +++ b/sites/all/modules/contrib/dev/ctools/plugins/relationships/user_category_edit_form_from_user.inc @@ -15,6 +15,8 @@ $plugin = array( 'description' => t('Adds user category edit form from a user context.'), 'required context' => new ctools_context_required(t('User'), 'user'), 'context' => 'ctools_user_category_edit_form_from_user_context', + 'edit form' => 'ctools_user_category_edit_form_from_user_settings_form', + 'defaults' => array('category' => NULL), ); /** @@ -25,7 +27,35 @@ function ctools_user_category_edit_form_from_user_context($context, $conf) { return ctools_context_create_empty('user_edit_form', NULL); } + if (!empty($conf['category'])) { + return ctools_context_create('user_edit_form', $context->data, array('category' => $conf['category'])); + } + if (isset($context->data->user_category)) { return ctools_context_create('user_edit_form', $context->data, array('category' => $context->data->user_category)); } + + return ctools_context_create('user_edit_form', $context->data); +} + +/** + * Settings form for the relationship. + */ +function ctools_user_category_edit_form_from_user_settings_form($form, &$form_state) { + $conf = $form_state['conf']; + + $categories = _user_categories(); + $options = array(); + foreach ($categories as $category) { + $options[$category['name']] = $category['title']; + } + $form['category'] = array( + '#type' => 'select', + '#title' => t('Category'), + '#options' => $options, + '#default_value' => isset($conf['category']) ? $conf['category'] : NULL, + '#empty_option' => 'Default', + ); + + return $form; } diff --git a/sites/all/modules/contrib/dev/ctools/stylizer/plugins/export_ui/stylizer.inc b/sites/all/modules/contrib/dev/ctools/stylizer/plugins/export_ui/stylizer.inc index a1fc1d8..8192efe 100644 --- a/sites/all/modules/contrib/dev/ctools/stylizer/plugins/export_ui/stylizer.inc +++ b/sites/all/modules/contrib/dev/ctools/stylizer/plugins/export_ui/stylizer.inc @@ -1,5 +1,9 @@ 'stylizer', 'access' => 'administer stylizer', @@ -42,4 +46,3 @@ $plugin = array( ), ), ); - diff --git a/sites/all/modules/contrib/dev/ctools/stylizer/plugins/export_ui/stylizer_ui.class.php b/sites/all/modules/contrib/dev/ctools/stylizer/plugins/export_ui/stylizer_ui.class.php index 14d6293..d4db36c 100644 --- a/sites/all/modules/contrib/dev/ctools/stylizer/plugins/export_ui/stylizer_ui.class.php +++ b/sites/all/modules/contrib/dev/ctools/stylizer/plugins/export_ui/stylizer_ui.class.php @@ -5,16 +5,18 @@ */ class stylizer_ui extends ctools_export_ui { - function access($op, $item) { + + public function access($op, $item) { $access = parent::access($op, $item); if ($op == 'add' && $access && empty($this->base_types)) { - // Make sure there are base styles defined. - $access = FALSE; + // Make sure there are base styles defined. + $access = FALSE; } return $access; } - function list_form(&$form, &$form_state) { + + public function list_form(&$form, &$form_state) { ctools_include('stylizer'); parent::list_form($form, $form_state); @@ -60,7 +62,8 @@ class stylizer_ui extends ctools_export_ui { ); } - function list_sort_options() { + + public function list_sort_options() { return array( 'disabled' => t('Enabled, title'), 'title' => t('Title'), @@ -71,7 +74,8 @@ class stylizer_ui extends ctools_export_ui { ); } - function list_filter($form_state, $item) { + + public function list_filter($form_state, $item) { if (empty($form_state['style_plugins'][$item->settings['style_base']])) { $this->style_plugin = array( 'name' => 'broken', @@ -102,30 +106,37 @@ class stylizer_ui extends ctools_export_ui { return parent::list_filter($form_state, $item); } - function list_search_fields() { + + public function list_search_fields() { $fields = parent::list_search_fields(); $fields[] = 'plugin_title'; return $fields; } - function list_build_row($item, &$form_state, $operations) { - // Set up sorting + + public function list_build_row($item, &$form_state, $operations) { + // Set up sorting. switch ($form_state['values']['order']) { case 'disabled': $this->sorts[$item->name] = empty($item->disabled) . $item->admin_title; break; + case 'title': $this->sorts[$item->name] = $item->admin_title; break; + case 'name': $this->sorts[$item->name] = $item->name; break; + case 'type': $this->sorts[$item->name] = $this->style_plugin['type'] . $item->admin_title; break; + case 'base': $this->sorts[$item->name] = $this->style_plugin['title'] . $item->admin_title; break; + case 'storage': $this->sorts[$item->name] = $item->type . $item->admin_title; break; @@ -154,7 +165,8 @@ class stylizer_ui extends ctools_export_ui { ); } - function list_table_header() { + + public function list_table_header() { return array( array('data' => t('Type'), 'class' => array('ctools-export-ui-type')), array('data' => t('Name'), 'class' => array('ctools-export-ui-name')), @@ -165,14 +177,16 @@ class stylizer_ui extends ctools_export_ui { ); } - function init($plugin) { + + public function init($plugin) { ctools_include('stylizer'); $this->base_types = ctools_get_style_base_types(); parent::init($plugin); } - function get_wizard_info(&$form_state) { + + public function get_wizard_info(&$form_state) { $form_info = parent::get_wizard_info($form_state); ctools_include('stylizer'); @@ -219,7 +233,7 @@ class stylizer_ui extends ctools_export_ui { * The stylizer wizard stores its stuff in slightly different places, so * we have to find it and move it to the right place. */ - function store_stylizer_info(&$form_state) { + public function store_stylizer_info(&$form_state) { /* foreach (array('name', 'admin_title', 'admin_description') as $key) { if (!empty($form_state['values'][$key])) { @@ -235,12 +249,14 @@ class stylizer_ui extends ctools_export_ui { $form_state['item']->settings['name'] = $form_state['item']->name; } - function edit_wizard_next(&$form_state) { + + public function edit_wizard_next(&$form_state) { $this->store_stylizer_info($form_state); parent::edit_wizard_next($form_state); } - function edit_wizard_finish(&$form_state) { + + public function edit_wizard_finish(&$form_state) { // These might be stored by the stylizer wizard, so we should clear them. if (isset($form_state['settings']['old_settings'])) { unset($form_state['settings']['old_settings']); @@ -249,7 +265,8 @@ class stylizer_ui extends ctools_export_ui { parent::edit_wizard_finish($form_state); } - function edit_form_type(&$form, &$form_state) { + + public function edit_form_type(&$form, &$form_state) { foreach ($this->base_types as $module => $info) { foreach ($info as $key => $base_type) { $types[$module . '-' . $key] = $base_type['title']; @@ -266,7 +283,9 @@ class stylizer_ui extends ctools_export_ui { ); } - function edit_form_type_submit(&$form, &$form_state) { + + public function edit_form_type_submit(&$form, &$form_state) { list($form_state['item']->style_module, $form_state['item']->style_type) = explode('-', $form_state['values']['type']); } + } diff --git a/sites/all/modules/contrib/dev/ctools/stylizer/stylizer.info b/sites/all/modules/contrib/dev/ctools/stylizer/stylizer.info index d73a641..90204a3 100644 --- a/sites/all/modules/contrib/dev/ctools/stylizer/stylizer.info +++ b/sites/all/modules/contrib/dev/ctools/stylizer/stylizer.info @@ -2,13 +2,11 @@ name = Stylizer description = Create custom styles for applications such as Panels. core = 7.x package = Chaos tool suite -version = CTOOLS_MODULE_VERSION dependencies[] = ctools dependencies[] = color -; Information added by Drupal.org packaging script on 2015-01-28 -version = "7.x-1.6" +; Information added by Drupal.org packaging script on 2019-02-08 +version = "7.x-1.15" core = "7.x" project = "ctools" -datestamp = "1422471484" - +datestamp = "1549603691" diff --git a/sites/all/modules/contrib/dev/ctools/stylizer/stylizer.install b/sites/all/modules/contrib/dev/ctools/stylizer/stylizer.install index 5cefb0d..321ba8e 100644 --- a/sites/all/modules/contrib/dev/ctools/stylizer/stylizer.install +++ b/sites/all/modules/contrib/dev/ctools/stylizer/stylizer.install @@ -1,5 +1,9 @@ t('Custom styles can be applied to Panel regions and Panel panes.'), ); - // Load all mini panels and their displays. + // Load all mini panels and their displays. ctools_include('export'); ctools_include('stylizer'); $items = ctools_export_crud_load_all('stylizer'); @@ -122,14 +122,14 @@ function stylizer_theme() { 'path' => $plugin['path'], ); - // if no theme function exists, assume template. + // If no theme function exists, assume template. if (!function_exists("theme_$plugin[theme]")) { $theme[$plugin['theme']]['template'] = str_replace('_', '-', $plugin['theme']); - $theme[$plugin['theme']]['file'] = $plugin['file']; // for preprocess. + // For preprocess. + $theme[$plugin['theme']]['file'] = $plugin['file']; } } } return $theme; } - diff --git a/sites/all/modules/contrib/dev/ctools/term_depth/plugins/access/term_depth.inc b/sites/all/modules/contrib/dev/ctools/term_depth/plugins/access/term_depth.inc index ab05b60..d63183f 100644 --- a/sites/all/modules/contrib/dev/ctools/term_depth/plugins/access/term_depth.inc +++ b/sites/all/modules/contrib/dev/ctools/term_depth/plugins/access/term_depth.inc @@ -25,28 +25,22 @@ $plugin = array( * Settings form for the 'term depth' access plugin. */ function term_depth_term_depth_ctools_access_settings($form, &$form_state, $conf) { - // If no configuration was saved before, set some defaults. - if (empty($conf)) { - $conf = array( - 'vid' => 0, - ); - } - if (!isset($conf['vid'])) { - $conf['vid'] = 0; - } - + $vocabularies = taxonomy_get_vocabularies(); + $options = array(); // Loop over each of the configured vocabularies. - foreach (taxonomy_get_vocabularies() as $vid => $vocabulary) { - $options[$vid] = $vocabulary->name; + foreach ($vocabularies as $vid => $vocab) { + $options[$vocab->machine_name] = $vocab->name; } - $form['settings']['vid'] = array( + _term_depth_convert_config_vid_to_vocabulary_name($conf); + + $form['settings']['vocabulary'] = array( '#title' => t('Vocabulary'), '#type' => 'select', '#options' => $options, '#description' => t('Select the vocabulary for this form. If there exists a parent term in that vocabulary, this access check will succeed.'), - '#id' => 'ctools-select-vid', - '#default_value' => $conf['vid'], + '#id' => 'ctools-select-vocabulary', + '#default_value' => !empty($conf['vocabulary']) ? $conf['vocabulary'] : array(), '#required' => TRUE, ); @@ -61,15 +55,24 @@ function term_depth_term_depth_ctools_access_settings($form, &$form_state, $conf return $form; } +/** + * @param $conf + */ +function _term_depth_convert_config_vid_to_vocabulary_name(&$conf) { + // Fallback on legacy 'vid', when no vocabularies are available. + if (empty($conf['vocabulary']) && !empty($conf['vid'])) { + $vocabulary = _ctools_term_vocabulary_machine_name_convert(array($conf['vid'])); + $conf['vocabulary'] = reset($vocabulary); + unset($conf['vid'], $vocabulary); + } +} + /** * Submit function for the access plugins settings. - * - * We cast all settings to numbers to ensure they can be safely handled. */ function term_depth_term_depth_ctools_access_settings_submit($form, $form_state) { - foreach (array('depth', 'vid') as $key) { - $form_state['conf'][$key] = (integer) $form_state['values']['settings'][$key]; - } + $form_state['conf']['depth'] = (integer) $form_state['values']['settings']['depth']; + $form_state['conf']['vocabulary'] = array_filter($form_state['conf']['vocabulary']); } /** @@ -82,10 +85,17 @@ function term_depth_term_depth_ctools_access_check($conf, $context) { return FALSE; } - // Get the $vid. - if (!isset($conf['vid'])) { + _term_depth_convert_config_vid_to_vocabulary_name($conf); + + // Get the $vocabulary. + if (!isset($conf['vocabulary'])) { return FALSE; } + $vocab = taxonomy_vocabulary_machine_name_load($conf['vocabulary']); + if ($vocab->vid != $context->data->vid) { + return FALSE; + } + $depth = _term_depth($context->data->tid); return ($depth == $conf['depth']); @@ -95,9 +105,9 @@ function term_depth_term_depth_ctools_access_check($conf, $context) { * Provide a summary description based upon the checked terms. */ function term_depth_term_depth_ctools_access_summary($conf, $context) { - $vocab = taxonomy_vocabulary_load($conf['vid']); - - return t('"@term" has parent in vocabulary "@vocab" at @depth', array( + _term_depth_convert_config_vid_to_vocabulary_name($conf); + $vocab = taxonomy_vocabulary_machine_name_load($conf['vocabulary']); + return t('"@term" is in vocabulary "@vocab" at depth @depth', array( '@term' => $context->identifier, '@vocab' => $vocab->name, '@depth' => $conf['depth'], diff --git a/sites/all/modules/contrib/dev/ctools/term_depth/term_depth.info b/sites/all/modules/contrib/dev/ctools/term_depth/term_depth.info index 5f4ef70..4baee51 100644 --- a/sites/all/modules/contrib/dev/ctools/term_depth/term_depth.info +++ b/sites/all/modules/contrib/dev/ctools/term_depth/term_depth.info @@ -3,11 +3,9 @@ description = Controls access to context based upon term depth core = 7.x dependencies[] = ctools package = Chaos tool suite -version = CTOOLS_MODULE_VERSION -; Information added by Drupal.org packaging script on 2015-01-28 -version = "7.x-1.6" +; Information added by Drupal.org packaging script on 2019-02-08 +version = "7.x-1.15" core = "7.x" project = "ctools" -datestamp = "1422471484" - +datestamp = "1549603691" diff --git a/sites/all/modules/contrib/dev/ctools/term_depth/term_depth.module b/sites/all/modules/contrib/dev/ctools/term_depth/term_depth.module index 10f9e31..f9f4ce7 100644 --- a/sites/all/modules/contrib/dev/ctools/term_depth/term_depth.module +++ b/sites/all/modules/contrib/dev/ctools/term_depth/term_depth.module @@ -1,5 +1,9 @@ 'Context IDs', + 'description' => 'Verify that Context IDs work properly.', + 'group' => 'ctools', + 'dependencies' => array('ctools'), + ); + } + + /** + * {@inheritdoc} + */ + protected function setUp(array $modules = array()) { + $modules[] = 'ctools'; + parent::setUp($modules); + ctools_include('context'); + } + + private function getTestContexts() { + $test_objects = array( + array(), + array( + 'name' => 'foo_bar', + ), + array( + 'id' => 25, + ), + array( + 'id' => 5, + 'name' => NULL, + ), + array( + 'id' => 'a', + 'name' => 'foo_bar', + ), + array( + 'id' => 'z', + 'name' => 'baz', + ), + array( + 'id' => 15, + 'name' => 'baz', + ), + array( + 'id' => 'z', + 'name' => 'foo', + ), + array( + 'id' => 1, + 'name' => 'foo', + ), + array( + 'id' => 47, + 'name' => 'bar', + ), + array( + 'id' => 99, + 'name' => 'bar', + ), + ); + return $test_objects; + } + + /** + * Test ctools_context_id where the context only has an id. + */ + public function testContextId() { + $context = array(); + $expected = 'context__1'; + $actual = ctools_context_id($context); + $this->assertEqual($actual, $expected, 'Empty context has id ' . $expected); + + $context = array('id' => 4); + $expected = 'context__4'; + $actual = ctools_context_id($context); + $this->assertEqual($actual, $expected, 'Context 4 has id ' . $expected); + + $context = array('id' => 'a'); + $expected = 'context__a'; + $actual = ctools_context_id($context); + $this->assertEqual($actual, $expected, 'Context "a" has id ' . $expected); + } + + /** + * Test ctools_context_id where the context has an id and a name. + */ + public function testContextIdName() { + $context = array('id' => '512', 'name' => 'foo'); + $expected = 'context_foo_512'; + $this->assertEqual(ctools_context_id($context), $expected, 'Context "512"/"foo" has id ' . $expected); + + $context = array('id' => '512', 'name' => 'foo_bar'); + $expected = 'context_foo_bar_512'; + $this->assertEqual(ctools_context_id($context), $expected, 'Context "512"/"foo_bar" has id ' . $expected); + } + + /** + * Test ctools_context_id where the context has an id & name, and a type is specified. + */ + public function testContextIdNameType() { + $type = 'sort'; + $context = array('id' => '512', 'name' => 'foo'); + $expected = 'sort_foo_512'; + $this->assertEqual(ctools_context_id($context, $type), $expected, 'Context "512" has id ' . $expected); + + $type = NULL; + $context = array('id' => '512', 'name' => 'foo'); + $expected = '_foo_512'; + $this->assertEqual(ctools_context_id($context, $type), $expected, 'Context "512" has id ' . $expected); + } + + /** + * Test ctools_context_next_id in various scenarios. + */ + public function testNextContextId() { + $test_objects = $this->getTestContexts(); + + // If no object list or name is given? + $value = ctools_context_next_id(NULL, NULL); + $expected = 1; + $this->assertEqual($value, $expected, 'NULL objects have next id ' . $expected); + + // If no object list is given? + $value = ctools_context_next_id(NULL, 'bar'); + $expected = 1; + $this->assertEqual($value, $expected, 'NULL objects have next id ' . $expected); + + // If no object list is given (another way)? + $value = ctools_context_next_id(array(), 'bar'); + $expected = 1; + $this->assertEqual($value, $expected, 'Empty objects have next id ' . $expected); + + // The name is empty... which is just another name. + $value = ctools_context_next_id($test_objects, ''); + $expected = 1; + $this->assertEqual($value, $expected, 'Unnamed objects have next id ' . $expected . ' (got ' . $value . ')'); + + // The value of the id key is not a number. + $value = ctools_context_next_id($test_objects, 'foo_bar'); + $expected = 1; + $this->assertEqual($value, $expected, 'Objects with non-integer ids are ignored ' . $expected); + + // One object's id is not a number (ignore) but another is valid. + $value = ctools_context_next_id($test_objects, 'baz'); + $expected = 16; + $this->assertEqual($value, $expected, 'Baz\'s objects have next id ' . $expected); + + // An expected case: there is one match and the id is numeric. + $value = ctools_context_next_id($test_objects, 'foo'); + $expected = 2; + $this->assertEqual($value, $expected, 'Foo\'s objects have next id ' . $expected); + + // Another expected case: there are multiple numeric IDs. + $value = ctools_context_next_id($test_objects, 'bar'); + $expected = 100; + $this->assertEqual($value, $expected, 'Bar\'s objects have next id ' . $expected); + } + +} + + +/** + * Test the keyword substitution functionality. + */ class CtoolsContextKeywordsSubstitutionTestCase extends DrupalWebTestCase { + + /** + * {@inheritdoc} + */ public static function getInfo() { return array( 'name' => 'Keywords substitution', 'description' => 'Verify that keywords are properly replaced with data.', - 'group' => 'Chaos Tools Suite', + 'group' => 'ctools', + 'dependencies' => array('ctools'), ); } - public function setUp() { - parent::setUp('ctools'); + /** + * {@inheritdoc} + */ + protected function setUp(array $modules = array()) { + $modules[] = 'ctools'; + parent::setUp($modules); ctools_include('context'); } + /** + * Test the keyword substitution. + */ public function testKeywordsSubstitution() { // Create node context for substitution. $node = $this->drupalCreateNode(); @@ -23,40 +209,164 @@ class CtoolsContextKeywordsSubstitutionTestCase extends DrupalWebTestCase { // Run tests on some edge cases. $checks = array( - '%node:changed:raw:' => array( + array( + '%node:changed:raw:', + array('%title' => ''), "{$node->changed}:", t('Multi-level token has been replaced. Colon left untouched.'), ), - '%node:title' => array( + array( + '%node:title', + array('%title' => ''), "{$node->title}", t('Keyword and converter have been replaced.'), ), - '%%node:title' => array( + array( + '%%node:title', + array('%title' => ''), "%node:title", t('Keyword after escaped percent sign left untouched.'), ), - '%node:title%node:nid' => array( + array( + '%node:title%node:nid', + array('%title' => ''), "{$node->title}{$node->nid}", t('Multiple substitutions have been replaced.'), ), - '%node:title:' => array( + array( + '%node:title:', + array('%title' => ''), "{$node->title}:", t('Colon after keyword and converter left untouched.'), ), - '%node:title%%' => array( + array( + '%node:title%%', + array('%title' => ''), "{$node->title}%", t('Escaped percent sign after keyword and converter left untouched.'), ), - '%%%node:title' => array( + array( + '%%%node:title', + array('%title' => ''), "%{$node->title}", t('Keyword after escaped and unescaped percent sign has been replaced.'), ), + array( + '%%foo:bar', + array('%title' => ''), + "%foo:bar", + t('Non-existant context ignored.'), + ), + array( + 'There was about 20%-30% difference in price.', + array('%title' => ''), + 'There was about 20%-30% difference in price.', + t('Non-keyword percent sign left untouched.'), + ), + array( + 'href="my%20file%2dname.pdf"', + array('%title' => ''), + 'href="my%20file%2dname.pdf"', + t('HTTP URL escape left untouched.'), + ), + array( + 'href="my%a0file%fdname.pdf"', + array('%title' => ''), + 'href="my%a0file%fdname.pdf"', + t('HTTP URL escape (high-chars) left untouched.'), + ), + array( + 'Click here!', + array('%title' => ''), + 'Click here!', + t('HTTP URL escape percent sign left untouched in HTML.'), + ), + array( + 'SELECT * FROM {table} WHERE field = "%s"', + array('%title' => ''), + 'SELECT * FROM {table} WHERE field = "%s"', + t('SQL percent sign left untouched.'), + ), + array( + '%title', + array('%title' => 'foobar'), + 'foobar', + t('String value in $keywords array is returned.'), + ), + array( + '%title', + array('%title' => ''), + '', + t('Empty string value in $keywords array returns empty string.'), + ), + array( + '%title', + array('%title' => NULL), + '', + t('NULL value in $keywords array returns empty string.'), + ), + array( + '%title', + array('%title' => FALSE), + '', + t('FALSE value in $keywords array returns empty string.'), + ), + array( + '%title', + array('%title' => 11), + '11', + t('Integer value in $keywords array returns string representation of the integer.'), + ), + array( + '%title', + array('%title' => 'substring %title'), + 'substring %title', + t('Input value as substring in $keywords array left untouched.'), + ), ); - foreach ($checks as $string => $expectations) { - list($expected_result, $message) = $expectations; - $actual_result = ctools_context_keyword_substitute($string, array(), $contexts); + foreach ($checks as $check) { + list($string, $keywords, $expected_result, $message) = $check; + $actual_result = ctools_context_keyword_substitute($string, $keywords, $contexts); $this->assertEqual($actual_result, $expected_result, $message); } } } + +/** + * Test the context classes. + */ +class CtoolsContextUnitTestCase extends DrupalUnitTestCase { + + /** + * {@inheritdoc} + */ + public static function getInfo() { + return array( + 'name' => 'Context unit tests', + 'description' => 'Verifies that context classes behave correctly', + 'group' => 'ctools', + 'dependencies' => array('ctools'), + ); + } + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + require_once __DIR__ . '/../includes/context.inc'; + } + + /** + * Tests that contexts have the correct required property value. + */ + public function testOptionalRequiredContext() { + $required_context = new ctools_context_required('test1'); + $this->assertTrue($required_context->required); + + $optional_context = new ctools_context_optional('test2'); + $this->assertFalse($optional_context->required); + } + +} diff --git a/sites/all/modules/contrib/dev/ctools/tests/css.test b/sites/all/modules/contrib/dev/ctools/tests/css.test index 4a5200c..6c03730 100644 --- a/sites/all/modules/contrib/dev/ctools/tests/css.test +++ b/sites/all/modules/contrib/dev/ctools/tests/css.test @@ -1,65 +1,98 @@ 'CSS Tools tests', - 'description' => '...', - 'group' => 'Chaos Tools Suite', + 'description' => 'Confirm the custom CSS handling works.', + 'group' => 'ctools', + 'dependencies' => array('ctools'), ); } - function setUp() { - // Additionally enable contact module. - parent::setUp('ctools'); + /** + * {@inheritdoc} + */ + public function setUp(array $modules = array()) { + $modules[] = 'ctools'; + parent::setUp($modules); + ctools_include('css'); } /** - * Test that cached plugins are loaded correctly. + * Test that Stored CSS snippets can be retrieved, filtered or otherwise. */ - function testCssStuff() { + public function testCssStoreFilterRetrieve() { $css = "#some-id .some-class {\n color: black;\n illegal-key: foo;\n}"; $filtered_css = '#some-id .some-class{color:black;}'; - ctools_include('css'); + $this->assertNull(ctools_css_retrieve('missing-css-test'), 'Missing css snippet is not found'); + $filename1 = ctools_css_store('unfiltered-css-test', $css, FALSE); $filename2 = ctools_css_store('filtered-css-test', $css, TRUE); + $file_contents = file_get_contents($filename1); + $this->assertEqual($css, $file_contents, 'Unfiltered css file contents are correct'); + $this->assertEqual($filename1, ctools_css_retrieve('unfiltered-css-test'), 'Unfiltered css file successfully fetched'); $file_contents = file_get_contents($filename1); $this->assertEqual($css, $file_contents, 'Unfiltered css file contents are correct'); -// $match = $filename1 == ctools_css_retrieve('unfiltered-css-test') ? 'Match' : 'No match'; -// $output .= '
    Unfiltered: ' . $filename1 . ' ' . $match . '
    '; -// $output .= '
    ' . file_get_contents($filename1) . '
    '; $this->assertEqual($filename2, ctools_css_retrieve('filtered-css-test'), 'Filtered css file succcesfully fetched'); $file_contents = file_get_contents($filename2); $this->assertEqual($filtered_css, $file_contents, 'Filtered css file contents are correct'); - // $match = $filename2 == ctools_css_retrieve('filtered-css-test') ? 'Match' : 'No match'; -// $output .= '
    Filtered: ' . $filename2 . ' ' . $match . '
    '; -// $output .= '
    ' . file_get_contents($filename2) . '
    '; -// -// drupal_add_css($filename2, array('type' => 'file')); -// return array('#markup' => $output); + } + /** + * Test that Stored CSS snippets can be correctly overwritten. + */ + public function testCssStoreOverwrite() { + $css1 = "#some-id .some-class {\n color: black;\n illegal-key: foo;\n}"; - // Test that in case that url can be used, the value surives when a colon is in it. + $css2 = "#other-id .other-class {\n color: blue;\n illegal-key: foo;\n}"; + $filtered_css2 = '#other-id .other-class{color:blue;}'; + + ctools_css_store('unfiltered-css-test', $css1, FALSE); + ctools_css_store('filtered-css-test', $css1, TRUE); + + // Now overwrite the first css with the second version. + $filename3 = ctools_css_store('unfiltered-css-test', $css2, FALSE); + $filename4 = ctools_css_store('filtered-css-test', $css2, TRUE); + + $file_contents3 = file_get_contents($filename3); + $file_contents4 = file_get_contents($filename4); + + $this->assertEqual($css2, $file_contents3, 'Unfiltered CSS has overwritten earlier contents.'); + $this->assertEqual($filtered_css2, $file_contents4, 'Filtered CSS has overwritten earlier contents.'); + } + + /** + * Test that in case that url is used, the colons survives filtering. + */ + public function testCssFilterURLHandling() { $css = "#some-id {\n background-image: url(http://example.com/example.gif);\n}"; $css_data = ctools_css_disassemble($css); $empty_array = array(); $disallowed_values_regex = '/(expression)/'; - $filtered = ctools_css_assemble(ctools_css_filter_css_data($css_data, $empty_array, $empty_array, '', $disallowed_values_regex)); + + $intermediate = ctools_css_filter_css_data($css_data, $empty_array, $empty_array, '', $disallowed_values_regex); + $filtered = ctools_css_assemble($intermediate); + $url = (strpos($filtered, 'http://example.com/example.gif') !== FALSE); $this->assertTrue($url, 'CSS with multiple colons can survive.'); + } - // Test that in case the CSS has two properties defined are merged. + /** + * Test that when the CSS has two properties defined they are merged. + */ + public function testCssFilterMergeProperties() { $css = "#some-id {\n font-size: 12px;\n}\n#some-id {\n color: blue;\n}"; $filtered = ctools_css_filter($css); $font_size = (strpos($filtered, 'font-size:12px;') !== FALSE); @@ -78,4 +111,5 @@ class CtoolsCssTestCase extends DrupalWebTestCase { $color = (strpos($filtered, 'color:red') !== FALSE); $this->assertTrue($font_size && $color, 'Multiple properties are retained.'); } + } diff --git a/sites/all/modules/contrib/dev/ctools/tests/css_cache.test b/sites/all/modules/contrib/dev/ctools/tests/css_cache.test index e289b42..2aaa304 100644 --- a/sites/all/modules/contrib/dev/ctools/tests/css_cache.test +++ b/sites/all/modules/contrib/dev/ctools/tests/css_cache.test @@ -1,30 +1,28 @@ 'Ctools CSS cache', + 'name' => 'CSS cache', 'description' => 'Tests the custom CSS cache handler.', - 'group' => 'Chaos Tools Suite', + 'group' => 'ctools', + 'dependencies' => array('ctools'), ); } /** * {@inheritdoc} */ - public function setUp() { - parent::setUp('ctools'); + public function setUp(array $modules = array()) { + $modules[] = 'ctools'; + parent::setUp($modules); } /** diff --git a/sites/all/modules/contrib/dev/ctools/tests/ctools.plugins.test b/sites/all/modules/contrib/dev/ctools/tests/ctools.plugins.test index fe1829c..a12d06a 100644 --- a/sites/all/modules/contrib/dev/ctools/tests/ctools.plugins.test +++ b/sites/all/modules/contrib/dev/ctools/tests/ctools.plugins.test @@ -1,26 +1,43 @@ 'Get plugin info', 'description' => 'Verify that plugin type definitions can properly set and overide values.', - 'group' => 'Chaos Tools Suite', + 'group' => 'ctools', + 'dependencies' => array('ctools'), ); } - function setUp() { - // Additionally enable contact module. - parent::setUp('ctools', 'ctools_plugin_test'); + /** + * {@inheritdoc} + */ + public function setUp(array $modules = array()) { + $modules[] = 'ctools'; + $modules[] = 'ctools_plugin_test'; + parent::setUp($modules); } + /** + * Assert helper to check that a specific plugin function exists. + * + * @param $module + * The module that owns the plugin. + * @param $type + * The type of plugin. + * @param $id + * The id of the specific plugin to load. + * @param $function + * The identifier of the function. For example, 'settings form'. + */ protected function assertPluginFunction($module, $type, $id, $function = 'function') { $func = ctools_plugin_load_function($module, $type, $id, $function); $this->assertTrue(function_exists($func), t('Plugin @plugin of plugin type @module:@type successfully retrieved @retrieved for @function.', array( @@ -32,6 +49,18 @@ class CtoolsPluginsGetInfoTestCase extends DrupalWebTestCase { ))); } + /** + * Assert helper to check that a specific plugin function does NOT exist. + * + * @param $module + * The module that owns the plugin. + * @param $type + * The type of plugin. + * @param $id + * The id of the specific plugin to load. + * @param $function + * The identifier of the function. For example, 'settings form'. + */ protected function assertPluginMissingFunction($module, $type, $id, $function = 'function') { $func = ctools_plugin_load_function($module, $type, $id, $function); $this->assertEqual($func, NULL, t('Plugin @plugin of plugin type @module:@type for @function with missing function successfully failed.', array( @@ -42,6 +71,18 @@ class CtoolsPluginsGetInfoTestCase extends DrupalWebTestCase { ))); } + /** + * Assert helper to check that a plugin can be loaded using a named class. + * + * @param $module + * The module that owns the plugin. + * @param $type + * The type of plugin. + * @param $id + * The id of the specific plugin to load. + * @param string $class + * The name of the PHP class to load. + */ protected function assertPluginClass($module, $type, $id, $class = 'handler') { $class_name = ctools_plugin_load_class($module, $type, $id, $class); $this->assertTrue(class_exists($class_name), t('Plugin @plugin of plugin type @module:@type successfully retrieved @retrieved for @class.', array( @@ -53,6 +94,18 @@ class CtoolsPluginsGetInfoTestCase extends DrupalWebTestCase { ))); } + /** + * Assert helper to check that a plugin DOES NOT contain the named class. + * + * @param $module + * The module that owns the plugin. + * @param $type + * The type of plugin. + * @param $id + * The id of the specific plugin to load. + * @param string $class + * The name of the PHP class to load. + */ protected function assertPluginMissingClass($module, $type, $id, $class = 'handler') { $class_name = ctools_plugin_load_class($module, $type, $id, $class); $this->assertEqual($class_name, NULL, t('Plugin @plugin of plugin type @module:@type for @class with missing class successfully failed.', array( @@ -66,7 +119,7 @@ class CtoolsPluginsGetInfoTestCase extends DrupalWebTestCase { /** * Test that plugins are loaded correctly. */ - function testPluginLoading() { + public function testPluginLoading() { ctools_include('plugins'); $module = 'ctools_plugin_test'; $type = 'not_cached'; @@ -81,7 +134,7 @@ class CtoolsPluginsGetInfoTestCase extends DrupalWebTestCase { $this->assertPluginClass($module, $type, 'plugin_array', 'handler'); $this->assertPluginClass($module, $type, 'plugin_array2', 'handler'); $this->assertPluginMissingClass($module, $type, 'plugin_array_dne', 'handler'); - // TODO Test big hook plugins. + // @todo Test big hook plugins. $type = 'cached'; @@ -95,6 +148,7 @@ class CtoolsPluginsGetInfoTestCase extends DrupalWebTestCase { $this->assertPluginClass($module, $type, 'plugin_array', 'handler'); $this->assertPluginClass($module, $type, 'plugin_array2', 'handler'); $this->assertPluginMissingClass($module, $type, 'plugin_array_dne', 'handler'); - // TODO Test big hook plugins. + // @todo Test big hook plugins. } + } diff --git a/sites/all/modules/contrib/dev/ctools/tests/ctools.test b/sites/all/modules/contrib/dev/ctools/tests/ctools.test new file mode 100644 index 0000000..4d748c0 --- /dev/null +++ b/sites/all/modules/contrib/dev/ctools/tests/ctools.test @@ -0,0 +1,239 @@ + 'Ctools module functions tests', + 'description' => 'Check functions in the ctools.module not otherwise tested.', + 'group' => 'ctools', + 'dependencies' => array('ctools'), + ); + } + + /** + * {@inheritdoc} + */ + public function setUp(array $modules = array()) { + $modules[] = 'ctools'; + parent::setUp($modules); + } + + /** + * Test that the break phrase function behaves as expected. + */ + public function testBreakPhrase() { + $tests = array( + NULL => array('value' => array()), + '' => array('value' => array()), + '1' => array('operator' => 'and', 'value' => array(1)), + '99' => array('operator' => 'and', 'value' => array(99)), + '+1' => array('invalid_input' => TRUE, 'value' => array(-1)), + ' 1' => array('invalid_input' => TRUE, 'value' => array(-1)), + '1 ' => array('invalid_input' => TRUE, 'value' => array(-1)), + '-1' => array('invalid_input' => TRUE, 'value' => array(-1)), + '-99' => array('invalid_input' => TRUE, 'value' => array(-1)), + '1,2' => array('operator' => 'and', 'value' => array(1, 2)), + '1 2' => array('operator' => 'or', 'value' => array(1, 2)), + '1+2' => array('operator' => 'or', 'value' => array(1, 2)), + '1,2,3' => array('operator' => 'and', 'value' => array(1, 2, 3)), + '1 2 3' => array('operator' => 'or', 'value' => array(1, 2, 3)), + '1+2+3' => array('operator' => 'or', 'value' => array(1, 2, 3)), + '1 , 2 , 3' => array('invalid_input' => TRUE, 'value' => array(-1)), + '1 + 2 + 3' => array('invalid_input' => TRUE, 'value' => array(-1)), + '1,2,3,4,5,6,7,8,9' => array( + 'operator' => 'and', + 'value' => array(1, 2, 3, 4, 5, 6, 7, 8, 9), + ), + '1 2,3,4 5 6 7 8 9' => array('invalid_input' => TRUE, 'value' => array(-1)), + ); + + foreach ($tests as $string => $expected) { + $result = ctools_break_phrase($string); + $expected = (object) $expected; + $this->assertEqual($result, $expected, 'Break Phrase test patterns: ' . $string); + } + } + + /** + * Test that the (deprecated) getuserroles returns expected array. + */ + public function testGetUserRoles() { + $result = ctools_get_roles(); + $this->assertTrue(is_array($result), 'get_roles returns an array'); + + // A key-value array of integers. + foreach ($result as $k => $v) { + $this->assertTrue(is_numeric($k), 'Role key is numeric; ' . $k); + $this->assertTrue(is_string($v), 'Role id is string; ' . $v); + } + } + + /** + * Test the ctools_attach_js function returns the expected paths. + */ + public function testAttachJs() { + $taxonomy_path = drupal_get_path('module', 'taxonomy'); + $ctools_path = drupal_get_path('module', 'ctools'); + + // Func should probably do a different thing but this is current behaviour. + $path = ctools_attach_js(''); + $this->assertEqual($path, $ctools_path . '/js/.js', 'Attach an empty string'); + + $path = ctools_attach_js('foo'); + $this->assertEqual($path, $ctools_path . '/js/foo.js', 'Attach simple string'); + + $path = ctools_attach_js('foo', 'ctools', ''); + $this->assertEqual($path, $ctools_path . '//foo.js', 'Attach string with empty subdir'); + + $path = ctools_attach_js('foo', 'ctools', 'javascript'); + $this->assertEqual($path, $ctools_path . '/javascript/foo.js', 'Attach string with alternate subdir'); + + $path = ctools_attach_js('foo', 'taxonomy', 'javascript'); + $this->assertEqual($path, $taxonomy_path . '/javascript/foo.js', 'Attach string from different module'); + } + + /** + * Test the ctools_attach_css function returns the expected paths. + */ + public function testAttachCss() { + $taxonomy_path = drupal_get_path('module', 'taxonomy'); + $ctools_path = drupal_get_path('module', 'ctools'); + + // Func should probably do a different thing but this is current behaviour. + $path = ctools_attach_css(''); + $this->assertEqual($path, $ctools_path . '/css/.css', 'Attach empty string'); + + $path = ctools_attach_css('foo'); + $this->assertEqual($path, $ctools_path . '/css/foo.css', 'Attach simple string'); + + $path = ctools_attach_css('foo', 'ctools', ''); + $this->assertEqual($path, $ctools_path . '//foo.css', 'Attach string with empty subdir'); + + $path = ctools_attach_css('foo', 'ctools', 'theme'); + $this->assertEqual($path, $ctools_path . '/theme/foo.css', 'Attach string with alternate subdir'); + + $path = ctools_attach_css('foo', 'taxonomy', 'theme'); + $this->assertEqual($path, $taxonomy_path . '/theme/foo.css', 'Attach string from different module'); + } + + /** + * Test the ctools version compare function. + */ + public function testApiVersionCompare() { + // We're beyond version 1. + $ok = ctools_api_version('1.0'); + $this->assertTrue($ok, 'Check API version 1.0 is ok'); + + // We're beyond version 1.0.1 too. + $ok = ctools_api_version('1.0.1'); + $this->assertTrue($ok, 'Check API version 1.0.1 is ok'); + + // Not (yet) on api version 10. + $ok = ctools_api_version('10.0'); + $this->assertFalse($ok, 'Check API version 10.0 is not ok'); + + // We are (currently) between version 1.1 and version 4.0. + $ok = ctools_api_version('1.1', '4.0'); + $this->assertTrue($ok, 'Check API is between 1 and 4'); + } + + /** + * Test that the ctools_classs_add works. + */ + public function testClassesAdd() { + ctools_class_reset(); + + ctools_class_add('testclass'); + + $classes = ctools_get_classes(); + $this->assertEqual(is_array($classes), 1, 'Classes should be an array'); + $this->assertEqual(count($classes), 1, 'Classes array has one element'); + $this->assertEqual(count($classes['html']), 1, 'Classes array has element: html'); + $this->assertTrue(isset($classes['html']['add']), 'Classes array has element: html/add'); + $this->assertEqual($classes['html']['add'], array('testclass'), 'Classes array has expected value'); + + ctools_class_add('class2 class3'); + + $classes = ctools_get_classes(); + $this->assertEqual(is_array($classes), 1, 'Classes should be an array'); + $this->assertEqual(count($classes['html']), 1, 'Classes array has element: html'); + // TODO: An undesirable result: array('testclass', 'class2', 'class3') is better. + $this->assertEqual($classes['html']['add'], array( + 'testclass', + 'class2 class3', + ), 'Classes array has expected value'); + } + + /** + * Test that the ctools_classs_remove works. + */ + public function testClassesRemove() { + ctools_class_reset(); + + ctools_class_remove('testclass'); + + $classes = ctools_get_classes(); + $this->assertEqual(is_array($classes), 1, 'Classes should be an array'); + $this->assertEqual(count($classes), 1, 'Classes array has one element'); + $this->assertEqual(count($classes['html']), 1, 'Classes array has element: html'); + $this->assertTrue(isset($classes['html']['remove']), 'Classes array has element: html/remove'); + $this->assertEqual($classes['html']['remove'], array('testclass'), 'Classes array has expected value'); + + ctools_class_remove('class2 class3'); + + $classes = ctools_get_classes(); + $this->assertEqual(count($classes), 1, 'Classes array has one element'); + $this->assertEqual(count($classes['html']), 1, 'Classes array has element: html'); + // This is an undesirable result, is array('testclass', 'class2', 'class3') better. + $this->assertEqual($classes['html']['remove'], array( + 'testclass', + 'class2 class3', + ), 'Classes array has expected value'); + } + + /** + * Test that the ctools_classs_add and ctools_classs_remove interact well. + */ + public function testClassesAddRemove() { + ctools_class_reset(); + + ctools_class_add('testclass'); + ctools_class_remove('testclass'); + + $classes = ctools_get_classes(); + $this->assertTrue(isset($classes['html']['add']), 'Classes array has an add set'); + $this->assertEqual($classes['html']['add'], array('testclass'), 'testclass is in the add set'); + $this->assertTrue(isset($classes['html']['remove']), 'Classes array has a remove set'); + // TODO: Is it really good to let this happen? + $this->assertEqual($classes['html']['remove'], array('testclass'), 'testclass is in the remove set'); + } + + /** + * Test that the ctools_classs_add and ctools_classs_remove interact well .. 2. + */ + public function testClassesAddRemove2() { + ctools_class_reset(); + + ctools_class_add('class2 class3'); + ctools_class_remove('class3'); + + $classes = ctools_get_classes(); + $this->assertTrue(isset($classes['html']['add']), 'Classes array has an add set'); + $this->assertEqual($classes['html']['add'], array('class2 class3'), 'Added class2 class3 is in add set'); + $this->assertTrue(isset($classes['html']['remove']), 'Classes array has a remove set'); + // TODO: Is it really good to let this happen? + $this->assertEqual($classes['html']['remove'], array('class3'), 'class3 in remove set'); + } + +} diff --git a/sites/all/modules/contrib/dev/ctools/tests/ctools_export_test/ctools_export.test b/sites/all/modules/contrib/dev/ctools/tests/ctools_export_test/ctools_export.test index 1accfd7..ebeee49 100644 --- a/sites/all/modules/contrib/dev/ctools/tests/ctools_export_test/ctools_export.test +++ b/sites/all/modules/contrib/dev/ctools/tests/ctools_export_test/ctools_export.test @@ -1,32 +1,34 @@ 'CTools export CRUD tests', + 'name' => 'Export CRUD', 'description' => 'Test the CRUD functionality for the ctools export system.', - 'group' => 'Chaos Tools Suite', + 'group' => 'ctools', ); } - protected function setUp() { - parent::setUp('ctools_export_test'); - $this->resetAll(); + /** + * {@inheritdoc} + */ + public function setUp(array $modules = array()) { + $modules[] = 'ctools'; + $modules[] = 'ctools_export_test'; + parent::setUp($modules); } /** * Tests CRUD operation: Load. */ - function testCrudExportLoad() { + public function testCrudExportLoad() { $loaded_export = ctools_export_crud_load('ctools_export_test', 'database_test'); $expected_export = new stdClass(); @@ -47,7 +49,7 @@ class CtoolsExportCrudTestCase extends DrupalWebTestCase { /** * Tests CRUD operation: Load multiple. */ - function testCrudExportLoadMultiple() { + public function testCrudExportLoadMultiple() { $exportable_names = array('database_test', 'overridden_test', 'default_test'); $loaded_exports = ctools_export_crud_load_multiple('ctools_export_test', $exportable_names); @@ -57,7 +59,7 @@ class CtoolsExportCrudTestCase extends DrupalWebTestCase { /** * Tests CRUD operation: Load all. */ - function testCrudExportLoadAll() { + public function testCrudExportLoadAll() { $loaded_exports = ctools_export_crud_load_all('ctools_export_test'); $this->assertEqual(count($loaded_exports), 3, 'All exportables have been loaded.'); @@ -66,10 +68,10 @@ class CtoolsExportCrudTestCase extends DrupalWebTestCase { /** * Tests CRUD operation: Save. */ - function testCrudExportSave() { + public function testCrudExportSave() { $default_export = ctools_export_crud_load('ctools_export_test', 'default_test'); - $this->assertTrue($default_export->in_code_only,'The loaded exportable is in code only.'); + $this->assertTrue($default_export->in_code_only, 'The loaded exportable is in code only.'); ctools_export_crud_save('ctools_export_test', $default_export); @@ -84,7 +86,7 @@ class CtoolsExportCrudTestCase extends DrupalWebTestCase { /** * Tests CRUD operation: New. */ - function testCrudExportNew() { + public function testCrudExportNew() { // Default exportable with defualt values. $new_export = ctools_export_crud_new('ctools_export_test'); @@ -113,7 +115,7 @@ class CtoolsExportCrudTestCase extends DrupalWebTestCase { /** * Tests CRUD operation: Revert. */ - function testCrudExportRevert() { + public function testCrudExportRevert() { // Load exportable, will come from database. $original_export = ctools_export_crud_load('ctools_export_test', 'overridden_test'); @@ -135,14 +137,15 @@ class CtoolsExportCrudTestCase extends DrupalWebTestCase { // Check the exportable is now in_code_only. $this->assertTrue($default_export->in_code_only, 'The loaded exportable is in the database only.'); - // Make sure the default object loaded matches the same overridden one in the database. + // Make sure the default object loaded matches the same overridden one in + // the database. $this->assertEqual($original_export->machine, $default_export->machine, 'The default exportable has been loaded and matches the overridden exportable.'); } /** * Tests CRUD operation: Delete. */ - function testCrudExportDelete() { + public function testCrudExportDelete() { // Create a stub entry save it and delete it from the database. $new_export = ctools_export_crud_new('ctools_export_test'); ctools_export_crud_save('ctools_export_test', $new_export); @@ -158,6 +161,7 @@ class CtoolsExportCrudTestCase extends DrupalWebTestCase { $machine = $database_export->machine; ctools_export_crud_delete('ctools_export_test', $database_export); + // Clear the exportable caches as it's been loaded above. ctools_export_load_object_reset('ctools_export_test'); $result = ctools_export_crud_load('ctools_export_test', $machine); @@ -168,7 +172,7 @@ class CtoolsExportCrudTestCase extends DrupalWebTestCase { /** * Tests CRUD operation: Set status. */ - function testCrudExportSetStatus() { + public function testCrudExportSetStatus() { // Database only object. $database_export = ctools_export_crud_load('ctools_export_test', 'database_test'); ctools_export_crud_disable('ctools_export_test', $database_export); diff --git a/sites/all/modules/contrib/dev/ctools/tests/ctools_export_test/ctools_export_test.default_ctools_export_tests.inc b/sites/all/modules/contrib/dev/ctools/tests/ctools_export_test/ctools_export_test.default_ctools_export_tests.inc index 9f2dd6c..2f5ca58 100644 --- a/sites/all/modules/contrib/dev/ctools/tests/ctools_export_test/ctools_export_test.default_ctools_export_tests.inc +++ b/sites/all/modules/contrib/dev/ctools/tests/ctools_export_test/ctools_export_test.default_ctools_export_tests.inc @@ -1,5 +1,9 @@ 'CTools export test data table', - 'export' => array( + 'description' => 'CTools export test data table', + 'export' => array( 'key' => 'machine', 'identifier' => 'ctools_export_test', 'default hook' => 'default_ctools_export_tests', @@ -53,7 +57,7 @@ function ctools_export_test_schema() { } /** - * Implments hook_install(); + * Implements hook_install(). */ function ctools_export_test_install() { $ctools_export_tests = array(); diff --git a/sites/all/modules/contrib/dev/ctools/tests/ctools_export_test/ctools_export_test.module b/sites/all/modules/contrib/dev/ctools/tests/ctools_export_test/ctools_export_test.module index 80f1adb..9cd803c 100644 --- a/sites/all/modules/contrib/dev/ctools/tests/ctools_export_test/ctools_export_test.module +++ b/sites/all/modules/contrib/dev/ctools/tests/ctools_export_test/ctools_export_test.module @@ -1,5 +1,9 @@ array( 'defaults' => array( - 'bool' => true, + 'bool' => TRUE, 'string' => 'string', 'array' => array('some value'), ), @@ -50,6 +55,11 @@ function ctools_plugin_test_ctools_plugin_type() { ); } +/** + * Plugin callback. + * + * @see ctools_plugin_test_ctools_plugin_type() + */ function ctools_plugin_test_ctools_plugin_test_big_hook_cached() { return array( 'test1' => array( @@ -59,6 +69,11 @@ function ctools_plugin_test_ctools_plugin_test_big_hook_cached() { ); } +/** + * Plugin callback. + * + * @see ctools_plugin_test_ctools_plugin_type() + */ function ctools_plugin_test_ctools_plugin_test_big_hook_not_cached() { return array( 'test1' => array( @@ -68,5 +83,18 @@ function ctools_plugin_test_ctools_plugin_test_big_hook_not_cached() { ); } -function ctools_plugin_test_hook_cached_test() {} -function ctools_plugin_test_hook_not_cached_test() {} +/** + * Callback for the big_hook_cached plugin. + * + * @see ctools_plugin_test_ctools_plugin_test_big_hook_cached + */ +function ctools_plugin_test_hook_cached_test() { +} + +/** + * Callback for the big_hook_not_cached plugin. + * + * @see ctools_plugin_test_ctools_plugin_test_big_hook_not_cached() + */ +function ctools_plugin_test_hook_not_cached_test() { +} diff --git a/sites/all/modules/contrib/dev/ctools/tests/math_expression.test b/sites/all/modules/contrib/dev/ctools/tests/math_expression.test index 730e079..b34fafb 100644 --- a/sites/all/modules/contrib/dev/ctools/tests/math_expression.test +++ b/sites/all/modules/contrib/dev/ctools/tests/math_expression.test @@ -1,24 +1,29 @@ 'CTools math expression tests', + 'name' => 'Math expressions', 'description' => 'Test the math expression library of ctools.', - 'group' => 'Chaos Tools Suite', + 'group' => 'ctools', + 'dependencies' => array('ctools'), ); } - public function setUp() { - parent::setUp('ctools', 'ctools_plugin_test'); + /** + * {@inheritdoc} + */ + public function setUp(array $modules = array()) { + $modules[] = 'ctools'; + $modules[] = 'ctools_plugin_test'; + parent::setUp($modules); } /** @@ -35,6 +40,9 @@ class CtoolsMathExpressionTestCase extends DrupalWebTestCase { return $this->assert(abs($first - $second) <= $delta, $message ? $message : t('Value @first is equal to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $group); } + /** + * Test some arithmetic handling. + */ public function testArithmetic() { $math_expression = new ctools_math_expr(); @@ -66,10 +74,13 @@ class CtoolsMathExpressionTestCase extends DrupalWebTestCase { $this->assertEqual(pow($random_number_a, $random_number_b), $math_expression->e("$random_number_a ^ $random_number_b")); } + /** + * Test the basic built-in functions in the math expression library. + */ public function testBuildInFunctions() { $math_expression = new ctools_math_expr(); - // @todo: maybe run this code multiple times to test different values. + // @todo Maybe run this code multiple times to test different values. $random_double = $this->rand01(); $random_int = rand(5, 10); $this->assertFloat(sin($random_double), $math_expression->e("sin($random_double)")); @@ -94,6 +105,9 @@ class CtoolsMathExpressionTestCase extends DrupalWebTestCase { // $this->assertFloat(min($random_double_a, $random_double_b), $math_expression->e("min($random_double_a, $random_double_b)")); } + /** + * Test variable handling. + */ public function testVariables() { $math_expression = new ctools_math_expr(); @@ -108,6 +122,9 @@ class CtoolsMathExpressionTestCase extends DrupalWebTestCase { $this->assertEqual($random_number_a / $random_number_b, $math_expression->e("var / $random_number_b")); } + /** + * Test custom function handling. + */ public function testCustomFunctions() { $math_expression = new ctools_math_expr(); @@ -126,4 +143,5 @@ class CtoolsMathExpressionTestCase extends DrupalWebTestCase { // Use a custom function in another function. $this->assertEqual(($random_number_a * 2 + $random_number_b) * 2, $math_expression->e("f(g($random_number_a, $random_number_b))")); } + } diff --git a/sites/all/modules/contrib/dev/ctools/tests/math_expression_stack.test b/sites/all/modules/contrib/dev/ctools/tests/math_expression_stack.test index 8143a55..e1e587a 100644 --- a/sites/all/modules/contrib/dev/ctools/tests/math_expression_stack.test +++ b/sites/all/modules/contrib/dev/ctools/tests/math_expression_stack.test @@ -1,26 +1,34 @@ 'CTools math expression stack tests', + 'name' => 'Math expressions stack', 'description' => 'Test the stack class of the math expression library.', - 'group' => 'Chaos Tools Suite', + 'group' => 'ctools', + 'dependencies' => array('ctools'), ); } - public function setUp() { - parent::setUp('ctools', 'ctools_plugin_test'); + /** + * {@inheritdoc} + */ + public function setUp(array $modules = array()) { + $modules[] = 'ctools'; + $modules[] = 'ctools_plugin_test'; + parent::setUp($modules); } + /** + * Test the math expression stack system. + */ public function testStack() { $stack = new ctools_math_expr_stack(); @@ -35,13 +43,15 @@ class CtoolsMathExpressionStackTestCase extends DrupalWebTestCase { $this->assertIdentical($value, $stack->pop()); $this->assertNull($stack->pop()); - // Add multiple elements and see whether they are returned in the right order. + // Add multiple elements and see whether they are returned in the right + // order. $values = array($this->randomName(), $this->randomName(), $this->randomName()); foreach ($values as $value) { $stack->push($value); } - // Test the different elements at different positions with the last() method. + // Test the different elements at different positions with the last() + // method. $count = count($values); foreach ($values as $key => $value) { $this->assertEqual($value, $stack->last($count - $key)); @@ -58,6 +68,6 @@ class CtoolsMathExpressionStackTestCase extends DrupalWebTestCase { $this->assertEqual($stack->pop(), $value); } $this->assertNull($stack->pop()); - } + } diff --git a/sites/all/modules/contrib/dev/ctools/tests/object_cache.test b/sites/all/modules/contrib/dev/ctools/tests/object_cache.test index 8791d7e..87fa415 100644 --- a/sites/all/modules/contrib/dev/ctools/tests/object_cache.test +++ b/sites/all/modules/contrib/dev/ctools/tests/object_cache.test @@ -1,27 +1,34 @@ 'Ctools object cache storage', + 'name' => 'Object cache storage (UI tests)', 'description' => 'Verify that objects are written, readable and lockable.', - 'group' => 'Chaos Tools Suite', + 'group' => 'ctools', + 'dependencies' => array('ctools'), ); } - public function setUp() { - // Additionally enable ctools module. - parent::setUp('ctools'); + /** + * {@inheritdoc} + */ + public function setUp(array $modules = array()) { + $modules[] = 'ctools'; + parent::setUp($modules); } + public function testObjectStorage() { + ctools_include('cache'); + $account1 = $this->drupalCreateUser(array()); $this->drupalLogin($account1); @@ -36,11 +43,11 @@ class CtoolsObjectCache extends DrupalWebTestCase { // TODO Test object locking somehow. // Object locking/testing works on session_id but simpletest uses // $this->session_id so can't be tested ATM. - ctools_object_cache_clear('testdata', 'one'); $this->assertFalse(ctools_object_cache_get('testdata', 'one'), 'Object cache data successfully cleared'); // TODO Test ctools_object_cache_clear_all somehow... // ctools_object_cache_clear_all requires session_id funtionality as well. } + } diff --git a/sites/all/modules/contrib/dev/ctools/tests/object_cache_unit.test b/sites/all/modules/contrib/dev/ctools/tests/object_cache_unit.test new file mode 100644 index 0000000..68b7350 --- /dev/null +++ b/sites/all/modules/contrib/dev/ctools/tests/object_cache_unit.test @@ -0,0 +1,141 @@ + 'Object cache storage (unit tests)', + 'description' => 'Verify that objects are written, readable and lockable.', + 'group' => 'ctools', + 'dependencies' => array('ctools'), + ); + } + + /** + * {@inheritdoc} + */ + public function setUp(array $modules = array()) { + $modules[] = 'ctools'; + parent::setUp($modules); + } + + /** + * Check that the supplied array looks like a ctools cache plugin. + * + * @param mixed $p + * The value to check. + * @param string $msg + * Prefix of the assertion message. + */ + public function assertValidCachePlugin($p, $msg) { + $this->assertTrue(is_array($p), $msg . ': plugin is an array'); + + $this->assertEqual($p['plugin type'], 'cache', $msg . ': type is cache'); + + $this->assertTrue(array_key_exists('title', $p), $msg . ': title element exists'); + $this->assertTrue(!empty($p['title']) && is_string($p['title']), $msg . ': title is a string'); + + $this->assertTrue(array_key_exists('cache get', $p), $msg . ': has a get function'); + $this->assertTrue(!empty($p['cache get']) && is_callable($p['cache get']), $msg . ': get is executable'); + + $this->assertTrue(array_key_exists('cache set', $p), $msg . ': has a set function'); + $this->assertTrue(!empty($p['cache set']) && is_callable($p['cache set']), $msg . ': set is executable'); + + // @todo Clear is required by the spec (cache.inc:40..48): but export_ui + // cache doesn't implement it. Enable the assertions when that problem is + // solved. + // $this->assertTrue(array_key_exists('cache clear', $p), $msg . ': has a clear function'); + // $this->assertTrue(is_callable($p['cache clear']), $msg . ': clear is executable'); + // @todo Break is optional acc'd to spec but does anything implement it? + $this->assertTrue(!array_key_exists('cache break', $p) || is_callable($p['cache break']), $msg . ': break is executable'); + + // @todo Finalize is optional so don't fail if not there?? + $this->assertTrue(!array_key_exists('cache finalize', $p) || is_callable($p['cache finalize']), $msg . ': finalize is executable'); + } + + /** + * Check the return value of the ctools_cache_find_plugin function. + * + * @param mixed $p + * The value to check. + * @param string $msg + * Prefix of the assertion message. + */ + public function assertPluginNotFound($p, $msg) { + $this->assertTrue(is_array($p), $msg . ': is an array'); + $plugin = array_shift($p); + $this->assertNull($plugin, $msg . ': no plugin info'); + $data = array_shift($p); + $this->assertTrue(empty($data) || is_string($data), $msg . ': data string-like'); + $this->assertTrue(empty($p), $msg . ': just two elements'); + } + + /** + * Check the return value of the ctools_cache_find_plugin function. + * + * @param mixed $p + * The value to check. + * @param string $msg + * Prefix of the assertion message. + */ + public function assertPluginFound($p, $msg) { + $this->assertTrue(is_array($p), $msg . ': is an array'); + $plugin = array_shift($p); + $this->assertTrue(is_array($plugin), $msg . ': has plugin data'); + $data = array_shift($p); + $this->assertTrue(empty($data) || is_string($data), $msg . ': data is string-like'); + $this->assertTrue(empty($p), $msg . ': just two elements'); + } + + /** + * Test to see that we can find the standard simple plugin. + */ + public function testFindSimpleCachePlugin() { + ctools_include('cache'); + + // The simple plugin. + $plugin = ctools_cache_find_plugin('simple'); + $this->assertPluginFound($plugin, 'The Simple Cache plugin is present'); + $this->assertValidCachePlugin($plugin[0], 'The Simple Cache plugin'); + + // The simple plugin, with ::. + $plugin = ctools_cache_find_plugin('simple::data'); + $this->assertPluginFound($plugin, 'The Simple Cache plugin is present, with data'); + } + + /** + * Test to see that we can find the standard export_ui plugin. + */ + public function testFindExportUICachePlugin() { + ctools_include('cache'); + + // The export plugin. + $plugin = ctools_cache_find_plugin('export_ui'); + $this->assertPluginFound($plugin, 'The Export UI Cache plugin is present'); + $this->assertValidCachePlugin($plugin[0], 'The Export Cache plugin'); + + // The export plugin, with ::. + $plugin = ctools_cache_find_plugin('export_ui::data'); + $this->assertTrue(is_array($plugin), 'The Export UI Cache plugin is present, with data'); + } + + /** + * Test to see that we don't find plugins that aren't there. + */ + public function testFindFoobarbazCachePlugin() { + ctools_include('cache'); + + // An imaginary foobarbaz plugin. + $plugin = ctools_cache_find_plugin('foobarbaz'); + $this->assertPluginNotFound($plugin, 'The Foobarbaz Cache plugin is absent'); + $plugin = ctools_cache_find_plugin('foobarbaz::data'); + $this->assertPluginNotFound($plugin, 'The Foobarbaz Cache plugin is absent, with data'); + } + +} diff --git a/sites/all/modules/contrib/dev/ctools/tests/page_tokens.test b/sites/all/modules/contrib/dev/ctools/tests/page_tokens.test new file mode 100644 index 0000000..7d95d9a --- /dev/null +++ b/sites/all/modules/contrib/dev/ctools/tests/page_tokens.test @@ -0,0 +1,136 @@ + 'Page token processing', + 'description' => 'Verify that page tokens can be set and read.', + 'group' => 'ctools', + 'dependencies' => array('ctools'), + ); + } + + /** + * {@inheritdoc} + */ + public function setUp(array $modules = array()) { + $modules[] = 'ctools'; + parent::setUp($modules); + + // Start each test anew. + ctools_reset_page_tokens(); + } + + /** + * Test that we can set page tokens. + */ + public function testSetPageToken() { + ctools_set_page_token('id', 'variable', 'test'); + + $tokens = ctools_set_page_token(); + $this->assertTrue(is_array($tokens) && count($tokens) === 1, 'Page tokens no longer empty.'); + $this->assertEqual($tokens['id'], array('variable', 'test'), 'Page token has correct value.'); + } + + /** + * Test that we can set page tokens. + */ + public function testSetVariableToken() { + $string = ctools_set_variable_token('title'); + $tokens = ctools_set_page_token(); + + $this->assertTrue(is_array($tokens) && count($tokens) === 1, 'Page tokens no longer empty'); + $this->assertEqual($tokens[$string], array('variable', 'title'), 'Page tokens no longer empty'); + } + + /** + * Test that we can set page tokens. + */ + public function testReplaceVariableToken() { + $string = ctools_set_variable_token('title'); + $this->assertEqual($string, '', 'Expected form of token was found'); + + $elements = array( + '#type' => 'container', + '#attributes' => array('class' => array('test')), + 'title' => '

    Title

    ', + 'content' => array( + '#type' => 'markup', + '#markup' => t('This is my test markup'), + '#prefix' => $string, + ), + 'link' => array( + '#type' => 'link', + '#title' => t('My link'), + '#href' => 'node/1', + ), + ); + $markup = '
    This is my test markupMy link
    '; + + $new_markup = ctools_page_token_processing($markup, $elements); + + $this->assertTrue(is_string($new_markup) && strlen($new_markup) > 1, 'Should return string'); + + $this->assertTrue(strstr($new_markup, '

    '), 'Variable Token Markup should contain h2 element'); + $this->assertFalse(strstr($new_markup, 'ctools-page-title'), 'Variable Token Markup should not contain comment element'); + } + + /** + * Test that we can set page tokens. + */ + public function testReplaceCallbackToken() { + $string = ctools_set_callback_token('title', 'test_ctools_page_callback_token'); + $this->assertEqual($string, '', 'Expected form of token was found'); + + $elements = array( + '#type' => 'container', + '#attributes' => array('class' => array('test')), + 'content' => array( + '#type' => 'markup', + '#markup' => t('This is my test markup'), + '#prefix' => $string, + ), + 'link' => array( + '#type' => 'link', + '#title' => t('My link'), + '#href' => 'node/1', + ), + ); + $markup = '
    This is my test markupMy link
    '; + + $new_markup = ctools_page_token_processing($markup, $elements); + + $this->assertTrue(is_string($new_markup) && strlen($new_markup) > 1, 'Should return a non-empty string'); + + $this->assertTrue(strstr($new_markup, '

    '), 'Callback Token Markup should contain h2 element'); + $this->assertFalse(strstr($new_markup, 'ctools-page-title'), 'Callback Token Markup should not contain comment element'); + } + +} + +/** + * + * @param $elements + * + * @return string + */ +function test_ctools_page_callback_token($elements) { + // Check that 'elements' array looks good. + if (isset($elements['content'])) { + return '

    Title

    '; + } + else { + return ''; + } +} diff --git a/sites/all/modules/contrib/dev/ctools/tests/plugins/cached/ctoolsCachedPluginArray.class.php b/sites/all/modules/contrib/dev/ctools/tests/plugins/cached/ctoolsCachedPluginArray.class.php index 3037daa..c6753ad 100644 --- a/sites/all/modules/contrib/dev/ctools/tests/plugins/cached/ctoolsCachedPluginArray.class.php +++ b/sites/all/modules/contrib/dev/ctools/tests/plugins/cached/ctoolsCachedPluginArray.class.php @@ -1,7 +1,8 @@ $context_info) { if (!strpos($context_info, '.')) { - // old skool: support pre-converter contexts as well. + // Old skool: support pre-converter contexts as well. $cid = $context_info; $converter = ''; } @@ -150,7 +150,7 @@ function views_content_views_content_type_render($subtype, $conf, $panel_args, $ $block = new stdClass(); $block->module = 'views'; - $block->delta = $view->name . '-' . $view->current_display; + $block->delta = $view->name . '-' . $view->current_display; if (!empty($conf['link_to_view'])) { $block->title_link = $view->get_url(); @@ -165,7 +165,7 @@ function views_content_views_content_type_render($subtype, $conf, $panel_args, $ // pager type that Views uses. if ($conf['override_pager_settings']) { if (method_exists($view, 'init_pager')) { - // Views 3 version + // Views 3 version. $view->set_items_per_page($conf['nodes_per_page']); $view->set_offset($conf['offset']); @@ -237,7 +237,7 @@ function views_content_views_select_display($form, &$form_state) { '#type' => 'select', '#title' => t('Display'), '#options' => $displays, - '#description' => t('Choose which display of this view you wish to use.') + '#description' => t('Choose which display of this view you wish to use.'), ); return $form; @@ -271,7 +271,6 @@ function views_content_views_content_type_edit_form($form, &$form_state) { // and check for that and forcibly set them to the right converter so that // it doesn't get changed to some whacky default. Oooor just let it get changed // to 'no context', I suppose. - $required = array(); if (isset($view->display_handler) && $arguments = $view->display_handler->get_handlers('argument')) { foreach ($arguments as $arg) { @@ -434,7 +433,7 @@ function views_content_views_content_type_admin_info($subtype, $conf, $contexts) } if (!strpos($context_info, '.')) { - // old skool: support pre-converter contexts as well. + // Old skool: support pre-converter contexts as well. $cid = $context_info; $converter = ''; } @@ -446,8 +445,10 @@ function views_content_views_content_type_admin_info($subtype, $conf, $contexts) $converters = ctools_context_get_converters($cid . '.', $contexts[$cid]); $converter = !empty($converters[$context_info]) ? $converters[$context_info] : t('Default'); $block->content .= '
  • ' . t('Argument @arg using context @context converted into @converter', array( - '@arg' => $argument->ui_name(), '@context' => $contexts[$cid]->get_identifier(), - '@converter' => $converter)) . '
  • '; + '@arg' => $argument->ui_name(), + '@context' => $contexts[$cid]->get_identifier(), + '@converter' => $converter, + )) . ''; } $argument = next($arguments); } @@ -494,6 +495,7 @@ function views_content_views_content_type_admin_info($subtype, $conf, $contexts) * versions it was the view name with a dash and the display ID. * If this is the case, we can use it to correct the 'display' setting * in the $conf. + * * @return * The $view with the initialized display. If the $view could not be * loaded, the name attempted will be loaded for use in errors. @@ -534,7 +536,6 @@ function _views_content_views_update_conf(&$conf, $subtype) { $view->set_display($display); // $view->current_display will now reflect this value. - // If set NOT to override, go ahead and refresh from the view. if (empty($conf['override_pager_settings'])) { if (method_exists($view, 'init_pager')) { diff --git a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_attachments.inc b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_attachments.inc index e938f53..f399fea 100644 --- a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_attachments.inc +++ b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_attachments.inc @@ -25,7 +25,7 @@ function views_content_views_attachments_content_type_render($subtype, $conf, $p } // Build the content type block. - $block = new stdClass(); + $block = new stdClass(); $block->module = 'views_attachments'; $block->delta = $context->argument; $block->title = ''; @@ -41,7 +41,6 @@ function views_content_views_attachments_content_type_render($subtype, $conf, $p return $block; } - function views_content_views_attachments_content_type_edit_form($form, &$form_state) { $conf = $form_state['conf']; diff --git a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_empty.inc b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_empty.inc index 4664d65..01e95c3 100644 --- a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_empty.inc +++ b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_empty.inc @@ -22,7 +22,7 @@ function views_content_views_empty_content_type_render($subtype, $conf, $panel_a } // Build the content type block. - $block = new stdClass(); + $block = new stdClass(); $block->module = 'views_empty'; $block->delta = $context->argument; $block->title = ''; diff --git a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_exposed.inc b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_exposed.inc index 93371e2..f6d019f 100644 --- a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_exposed.inc +++ b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_exposed.inc @@ -22,7 +22,7 @@ function views_content_views_exposed_content_type_render($subtype, $conf, $panel } // Build the content type block. - $block = new stdClass(); + $block = new stdClass(); $block->module = 'views_exposed'; $block->delta = $context->argument; $block->title = ''; diff --git a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_feed.inc b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_feed.inc index f2fdb8a..774d593 100644 --- a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_feed.inc +++ b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_feed.inc @@ -22,7 +22,7 @@ function views_content_views_feed_content_type_render($subtype, $conf, $panel_ar } // Build the content type block. - $block = new stdClass(); + $block = new stdClass(); $block->module = 'views_feed'; $block->delta = $context->argument; $block->title = ''; diff --git a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_footer.inc b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_footer.inc index 81c87a3..fc3d01b 100644 --- a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_footer.inc +++ b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_footer.inc @@ -22,7 +22,7 @@ function views_content_views_footer_content_type_render($subtype, $conf, $panel_ } // Build the content type block. - $block = new stdClass(); + $block = new stdClass(); $block->module = 'views_footer'; $block->delta = $context->argument; $block->title = ''; diff --git a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_header.inc b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_header.inc index f144d29..b42806f 100644 --- a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_header.inc +++ b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_header.inc @@ -22,7 +22,7 @@ function views_content_views_header_content_type_render($subtype, $conf, $panel_ } // Build the content type block. - $block = new stdClass(); + $block = new stdClass(); $block->module = 'views_header'; $block->delta = $context->argument; $block->title = ''; diff --git a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_pager.inc b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_pager.inc index faa6569..f5b380e 100644 --- a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_pager.inc +++ b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_pager.inc @@ -22,7 +22,7 @@ function views_content_views_pager_content_type_render($subtype, $conf, $panel_a } // Build the content type block. - $block = new stdClass(); + $block = new stdClass(); $block->module = 'views_pager'; $block->delta = $context->argument; $block->title = ''; diff --git a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_panes.inc b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_panes.inc index dc61bca..07e93f7 100644 --- a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_panes.inc +++ b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_panes.inc @@ -179,21 +179,21 @@ function views_content_views_panes_content_type_render($subtype, $conf, $panel_a $args[] = (isset($conf['arguments'][$id]) && $conf['arguments'][$id] !== '') ? ctools_context_keyword_substitute($conf['arguments'][$id], array(), $contexts) : NULL; break; - case 'wildcard': + case 'wildcard': // Put in the wildcard. - $args[] = isset($arguments[$id]['wildcard']) ? $arguments[$id]['wildcard'] : '*'; - break; + $args[] = isset($arguments[$id]['wildcard']) ? $arguments[$id]['wildcard'] : '*'; + break; - case 'none': - default: - // Put in NULL. - // views.module knows what to do with NULL (or missing) arguments - $args[] = NULL; - break; + case 'none': + default: + // Put in NULL. + // views.module knows what to do with NULL (or missing) arguments. + $args[] = NULL; + break; } } - // remove any trailing NULL arguments as these are non-args: + // Remove any trailing NULL arguments as these are non-args: while (count($args) && end($args) === NULL) { array_pop($args); } @@ -208,27 +208,32 @@ function views_content_views_panes_content_type_render($subtype, $conf, $panel_a if ($allow['path_override'] && !empty($conf['path'])) { $view->override_path = $conf['path']; } - else if ($path = $view->display_handler->get_option('inherit_panels_path')) { - $view->override_path = $_GET['q']; + elseif ($path = $view->display_handler->get_option('inherit_panels_path')) { + if (drupal_is_front_page()) { + $view->override_path = ''; + } + else { + $view->override_path = $_GET['q']; + } } $block = new stdClass(); $block->module = 'views'; - $block->delta = $view->name . '-' . $display; + $block->delta = $view->name . '-' . $display; if (($allow['link_to_view'] && !empty($conf['link_to_view'])) || (!$allow['link_to_view'] && $view->display_handler->get_option('link_to_view'))) { $block->title_link = $view->get_url(); } - // more link + // More link. if ($allow['more_link']) { if (empty($conf['more_link'])) { $view->display_handler->set_option('use_more', FALSE); } else { $view->display_handler->set_option('use_more', TRUE); - // make sure the view runs the count query so we know whether or not the + // Make sure the view runs the count query so we know whether or not the // more link applies. $view->get_total_rows = TRUE; } @@ -265,7 +270,7 @@ function views_content_views_panes_content_type_render($subtype, $conf, $panel_a if ($allow['fields_override']) { if ($conf['fields_override']) { $fields = $view->get_items('field'); - foreach ($fields as $field => $display) { + foreach ($fields as $field => $field_display) { $fields[$field]['exclude'] = empty($conf['fields_override'][$field]); } $view->display_handler->set_option('fields', $fields); @@ -321,6 +326,10 @@ function views_content_views_panes_content_type_render($subtype, $conf, $panel_a function views_content_views_panes_add_defaults(&$conf, $view) { $pager = $view->display_handler->get_option('pager'); + if (empty($conf)) { + $conf = array(); + } + $conf += array( 'link_to_view' => $view->display_handler->get_option('link_to_view'), 'more_link' => $view->display_handler->get_option('use_more'), @@ -377,7 +386,7 @@ function views_content_views_panes_content_type_edit_form($form, &$form_state) { ); } } - if ($allow['link_to_view'] ) { + if ($allow['link_to_view']) { $form['link_to_view'] = array( '#type' => 'checkbox', '#default_value' => isset($conf['link_to_view']) ? $conf['link_to_view'] : $view->display_handler->get_option('link_to_view'), @@ -428,7 +437,7 @@ function views_content_views_panes_content_type_edit_form($form, &$form_state) { $form['use_pager'] = array( '#type' => 'checkbox', '#title' => t('Use pager'), - '#default_value' => $conf['use_pager'], + '#default_value' => $conf['use_pager'], '#id' => 'use-pager-checkbox', '#prefix' => '
    ', ); @@ -549,7 +558,7 @@ function views_content_views_panes_content_type_edit_form($form, &$form_state) { $form['exposed']['sort_order'] = $exposed_form['sort_order']; } - // Add the view object to the form to allow additional customization + // Add the view object to the form to allow additional customization. $form_state['view'] = $view; return $form; @@ -561,7 +570,8 @@ function views_content_views_panes_content_type_edit_form($form, &$form_state) { function views_content_views_panes_content_type_edit_form_submit(&$form, &$form_state) { // Copy everything from our defaults. $keys = array('link_to_view', 'more_link', 'feed_icons', 'use_pager', - 'pager_id', 'items_per_page', 'offset', 'path_override', 'path', 'arguments', 'fields_override', 'exposed'); + 'pager_id', 'items_per_page', 'offset', 'path_override', 'path', 'arguments', 'fields_override', 'exposed', + ); foreach ($keys as $key) { if (isset($form_state['values'][$key])) { @@ -570,7 +580,6 @@ function views_content_views_panes_content_type_edit_form_submit(&$form, &$form_ } } - /** * Returns the administrative title for a type. */ @@ -605,21 +614,21 @@ function views_content_views_panes_content_type_admin_info($subtype, $conf, $con $view->set_display($display_name); views_content_views_panes_add_defaults($conf, $view); - // Add arguments first + // Add arguments first. if (!empty($conf['arguments'])) { $keys = array_keys($conf['arguments']); $values = array_values($conf['arguments']); $argument_input = $view->display_handler->get_option('argument_input'); foreach ($conf['arguments'] as $key => $value) { - if (!empty($value)){ + if (!empty($value)) { $label = $argument_input[$key]['label']; $info[] = $label . ': ' . $value; } } } - $block = new stdClass; + $block = new stdClass(); if ($info) { $block->title = array_shift($info); diff --git a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_row.inc b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_row.inc index 9672d02..16b97dc 100644 --- a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_row.inc +++ b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_row.inc @@ -35,7 +35,7 @@ function views_content_views_row_content_type_render($subtype, $conf, $panel_arg } // Build the content type block. - $block = new stdClass(); + $block = new stdClass(); $block->module = 'views_row'; $block->delta = $context->argument; $block->title = ''; @@ -91,7 +91,7 @@ function views_content_views_row_content_type_render($subtype, $conf, $panel_arg 'view' => $view, 'options' => $plugin->options, 'rows' => $rows, - 'title' => $title + 'title' => $title, ) ); } diff --git a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_view.inc b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_view.inc index dfb1175..54eec08 100644 --- a/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_view.inc +++ b/sites/all/modules/contrib/dev/ctools/views_content/plugins/content_types/views_view.inc @@ -22,7 +22,7 @@ function views_content_views_view_content_type_render($subtype, $conf, $panel_ar } // Build the content type block. - $block = new stdClass(); + $block = new stdClass(); $block->module = 'views_view'; $block->delta = $context->argument; $block->title = ''; diff --git a/sites/all/modules/contrib/dev/ctools/views_content/plugins/contexts/view.inc b/sites/all/modules/contrib/dev/ctools/views_content/plugins/contexts/view.inc index 1e926e4..6380478 100644 --- a/sites/all/modules/contrib/dev/ctools/views_content/plugins/contexts/view.inc +++ b/sites/all/modules/contrib/dev/ctools/views_content/plugins/contexts/view.inc @@ -2,7 +2,6 @@ /** * @file - * * Plugin to provide a node context. A node context is a node wrapped in a * context object that can be utilized by anything that accepts contexts. */ @@ -88,7 +87,7 @@ function views_content_context_view_create($empty, $data = NULL, $conf = FALSE, $data->set_display($display_id); } } - else if (!empty($plugin['view name'])) { + elseif (!empty($plugin['view name'])) { $data = views_get_view($plugin['view name']); $data->set_display($plugin['view display id']); } @@ -101,7 +100,7 @@ function views_content_context_view_create($empty, $data = NULL, $conf = FALSE, // $output = views_content_context_get_output($context); // $view = $output['view']; // @endcode - $context->data = array( + $context->data = array( 'name' => $data->name, 'display' => $data->current_display, 'args' => $data->args, @@ -158,8 +157,7 @@ function views_content_context_view_settings_form_validate($form, &$form_state) * Provide a list of ways that this context can be converted to a string. */ function views_content_context_view_convert_list() { - $list = array( - ); + $list = array(); return $list; } @@ -171,4 +169,3 @@ function views_content_context_view_convert($context, $type) { switch ($type) { } } - diff --git a/sites/all/modules/contrib/dev/ctools/views_content/plugins/relationships/node_from_view.inc b/sites/all/modules/contrib/dev/ctools/views_content/plugins/relationships/node_from_view.inc index 0f3fa8e..f54cbc6 100644 --- a/sites/all/modules/contrib/dev/ctools/views_content/plugins/relationships/node_from_view.inc +++ b/sites/all/modules/contrib/dev/ctools/views_content/plugins/relationships/node_from_view.inc @@ -33,7 +33,7 @@ function views_content_node_from_view_context($context, $conf, $placeholder = FA views_content_context_get_output($context); $row = intval($conf['row']) - 1; - if (isset($view->result[$row])) { + if (isset($view->result[$row]) && isset($view->base_field) && isset($view->result[$row]->{$view->base_field})) { $nid = $view->result[$row]->{$view->base_field}; if ($nid) { $node = node_load($nid); diff --git a/sites/all/modules/contrib/dev/ctools/views_content/plugins/relationships/view_from_argument.inc b/sites/all/modules/contrib/dev/ctools/views_content/plugins/relationships/view_from_argument.inc index cefc6db..cabd329 100644 --- a/sites/all/modules/contrib/dev/ctools/views_content/plugins/relationships/view_from_argument.inc +++ b/sites/all/modules/contrib/dev/ctools/views_content/plugins/relationships/view_from_argument.inc @@ -73,7 +73,7 @@ function views_content_view_from_argument_context($contexts, $conf) { foreach ($data->display_handler->get_argument_input() as $id => $argument) { if ($argument['type'] == 'context') { $key = array_shift($context_keys); - if (isset($contexts [$key])) { + if (isset($contexts[$key])) { if (strpos($argument['context'], '.')) { list($context, $converter) = explode('.', $argument['context'], 2); $args[] = ctools_context_convert_context($contexts[$key], $converter, array('sanitize' => FALSE)); @@ -84,7 +84,7 @@ function views_content_view_from_argument_context($contexts, $conf) { } } } - // remove any trailing NULL arguments as these are non-args: + // Remove any trailing NULL arguments as these are non-args: while (count($args) && end($args) === NULL) { array_pop($args); } diff --git a/sites/all/modules/contrib/dev/ctools/views_content/plugins/views/views_content.views.inc b/sites/all/modules/contrib/dev/ctools/views_content/plugins/views/views_content.views.inc index 644ac54..20b65a2 100644 --- a/sites/all/modules/contrib/dev/ctools/views_content/plugins/views/views_content.views.inc +++ b/sites/all/modules/contrib/dev/ctools/views_content/plugins/views/views_content.views.inc @@ -6,7 +6,7 @@ */ /** - * Implements hook_views_plugins + * Implements hook_views_plugins(). */ function views_content_views_plugins() { return array( @@ -25,7 +25,7 @@ function views_content_views_plugins() { 'use more' => TRUE, 'accept attachments' => TRUE, 'help topic' => 'display-pane', - 'contextual links locations' => array('panel_pane'), + 'contextual links locations' => array('panel_pane', 'view'), ), 'ctools_context' => array( 'title' => t('Context'), diff --git a/sites/all/modules/contrib/dev/ctools/views_content/plugins/views/views_content_plugin_display_ctools_context.inc b/sites/all/modules/contrib/dev/ctools/views_content/plugins/views/views_content_plugin_display_ctools_context.inc index 967f2fa..87ee291 100644 --- a/sites/all/modules/contrib/dev/ctools/views_content/plugins/views/views_content_plugin_display_ctools_context.inc +++ b/sites/all/modules/contrib/dev/ctools/views_content/plugins/views/views_content_plugin_display_ctools_context.inc @@ -1,4 +1,5 @@ '', 'translatable' => TRUE); @@ -47,12 +53,13 @@ class views_content_plugin_display_ctools_context extends views_plugin_display { /** * The display block handler returns the structure necessary for a block. */ - function execute() { + public function execute() { $this->executing = TRUE; return $this->view->render(); } - function preview() { + + public function preview() { $this->previewing = TRUE; return $this->view->render(); } @@ -60,7 +67,7 @@ class views_content_plugin_display_ctools_context extends views_plugin_display { /** * Render this display. */ - function render() { + public function render() { if (!empty($this->previewing)) { return theme($this->theme_functions(), array('view' => $this->view)); } @@ -71,7 +78,7 @@ class views_content_plugin_display_ctools_context extends views_plugin_display { $hooks = theme_get_registry(); $info = $hooks[$this->definition['theme']]; if (!empty($info['file'])) { - @include_once('./' . $info['path'] . '/' . $info['file']); + @include_once './' . $info['path'] . '/' . $info['file']; } $this->variables = array('view' => &$this->view); @@ -92,7 +99,7 @@ class views_content_plugin_display_ctools_context extends views_plugin_display { * * This output is returned as an array. */ - function options_summary(&$categories, &$options) { + public function options_summary(&$categories, &$options) { // It is very important to call the parent function here: parent::options_summary($categories, $options); @@ -135,7 +142,7 @@ class views_content_plugin_display_ctools_context extends views_plugin_display { /** * Provide the default form for setting options. */ - function options_form(&$form, &$form_state) { + public function options_form(&$form, &$form_state) { // It is very important to call the parent function here: parent::options_form($form, $form_state); switch ($form_state['section']) { @@ -143,6 +150,7 @@ class views_content_plugin_display_ctools_context extends views_plugin_display { // This just overwrites the existing row_plugin which is using the wrong options. $form['row_plugin']['#options'] = views_fetch_plugin_names('row', 'normal', array($this->view->base_table)); break; + case 'admin_title': $form['#title'] .= t('Administrative title'); @@ -152,6 +160,7 @@ class views_content_plugin_display_ctools_context extends views_plugin_display { '#description' => t('This is the title that will appear for this view context in the configure context dialog. If left blank, the view name will be used.'), ); break; + case 'inherit_panels_path': $form['#title'] .= t('Inherit path from panel display'); @@ -162,6 +171,7 @@ class views_content_plugin_display_ctools_context extends views_plugin_display { '#description' => t('If yes, all links generated by Views, such as more links, summary links, and exposed input links will go to the panels display path, not the view, if the display has a path.'), ); break; + case 'argument_input': $form['#title'] .= t('Choose the data source for view arguments'); $argument_input = $this->get_argument_input(); @@ -216,7 +226,7 @@ class views_content_plugin_display_ctools_context extends views_plugin_display { * Perform any necessary changes to the form values prior to storage. * There is no need for this function to actually store the data. */ - function options_submit(&$form, &$form_state) { + public function options_submit(&$form, &$form_state) { // It is very important to call the parent function here: parent::options_submit($form, $form_state); switch ($form_state['section']) { @@ -234,7 +244,7 @@ class views_content_plugin_display_ctools_context extends views_plugin_display { * the arguments doesn't cause the argument input field to just * break. */ - function get_argument_input() { + public function get_argument_input() { $arguments = $this->get_option('argument_input'); $handlers = $this->get_handlers('argument'); @@ -260,7 +270,8 @@ class views_content_plugin_display_ctools_context extends views_plugin_display { return $output; } - function get_path() { + + public function get_path() { if ($this->get_option('link_display') == 'custom_url' && $override_path = $this->get_option('link_url')) { return $override_path; } @@ -269,4 +280,5 @@ class views_content_plugin_display_ctools_context extends views_plugin_display { } return parent::get_path(); } + } diff --git a/sites/all/modules/contrib/dev/ctools/views_content/plugins/views/views_content_plugin_display_panel_pane.inc b/sites/all/modules/contrib/dev/ctools/views_content/plugins/views/views_content_plugin_display_panel_pane.inc index e02f896..05d3478 100644 --- a/sites/all/modules/contrib/dev/ctools/views_content/plugins/views/views_content_plugin_display_panel_pane.inc +++ b/sites/all/modules/contrib/dev/ctools/views_content/plugins/views/views_content_plugin_display_panel_pane.inc @@ -8,10 +8,11 @@ class views_content_plugin_display_panel_pane extends views_plugin_display { * If this variable is true, this display counts as a panel pane. We use * this variable so that other modules can create alternate pane displays. */ - var $panel_pane_display = TRUE; - var $has_pane_conf = NULL; + public $panel_pane_display = TRUE; + public $has_pane_conf = NULL; - function option_definition() { + + public function option_definition() { $options = parent::option_definition(); $options['pane_title'] = array('default' => '', 'translatable' => TRUE); @@ -34,7 +35,7 @@ class views_content_plugin_display_panel_pane extends views_plugin_display { 'title_override' => array('default' => FALSE), 'exposed_form' => array('default' => FALSE), 'fields_override' => array('default' => FALSE), - ), + ), ); $options['argument_input'] = array('default' => array()); @@ -44,11 +45,13 @@ class views_content_plugin_display_panel_pane extends views_plugin_display { return $options; } - function has_pane_conf() { + + public function has_pane_conf() { return isset($this->has_pane_conf); } - function set_pane_conf($conf = array()) { + + public function set_pane_conf($conf = array()) { $this->set_option('pane_conf', $conf); $this->has_pane_conf = TRUE; } @@ -58,7 +61,7 @@ class views_content_plugin_display_panel_pane extends views_plugin_display { * * This output is returned as an array. */ - function options_summary(&$categories, &$options) { + public function options_summary(&$categories, &$options) { // It is very important to call the parent function here: parent::options_summary($categories, $options); @@ -147,7 +150,7 @@ class views_content_plugin_display_panel_pane extends views_plugin_display { /** * Provide the default form for setting options. */ - function options_form(&$form, &$form_state) { + public function options_form(&$form, &$form_state) { // It is very important to call the parent function here: parent::options_form($form, $form_state); @@ -177,6 +180,7 @@ class views_content_plugin_display_panel_pane extends views_plugin_display { '#options' => $options, ); break; + case 'pane_title': $form['#title'] .= t('Administrative title'); @@ -318,7 +322,7 @@ class views_content_plugin_display_panel_pane extends views_plugin_display { * Perform any necessary changes to the form values prior to storage. * There is no need for this function to actually store the data. */ - function options_submit(&$form, &$form_state) { + public function options_submit(&$form, &$form_state) { // It is very important to call the parent function here: parent::options_submit($form, $form_state); switch ($form_state['section']) { @@ -340,7 +344,7 @@ class views_content_plugin_display_panel_pane extends views_plugin_display { * the arguments doesn't cause the argument input field to just * break. */ - function get_argument_input() { + public function get_argument_input() { $arguments = $this->get_option('argument_input'); $handlers = $this->get_handlers('argument'); @@ -368,7 +372,8 @@ class views_content_plugin_display_panel_pane extends views_plugin_display { return $output; } - function use_more() { + + public function use_more() { $allow = $this->get_option('allow'); if (!$allow['more_link'] || !$this->has_pane_conf()) { return parent::use_more(); @@ -377,21 +382,24 @@ class views_content_plugin_display_panel_pane extends views_plugin_display { return (bool) $conf['more_link']; } - function get_path() { + + public function get_path() { if (empty($this->view->override_path)) { return parent::get_path(); } return $this->view->override_path; } - function get_url() { + + public function get_url() { if ($this->get_option('inherit_panels_path')) { return $this->get_path(); } return parent::get_url(); } - function uses_exposed_form_in_block() { + + public function uses_exposed_form_in_block() { // We'll always allow the exposed form in a block, regardless of path. return TRUE; } @@ -405,7 +413,7 @@ class views_content_plugin_display_panel_pane extends views_plugin_display { * returns, exposed filters will not be used nor * displayed unless uses_exposed() returns TRUE. */ - function displays_exposed() { + public function displays_exposed() { $conf = $this->get_option('allow'); // If this is set, the exposed form is part of pane configuration, not // rendered normally. @@ -413,4 +421,3 @@ class views_content_plugin_display_panel_pane extends views_plugin_display { } } - diff --git a/sites/all/modules/contrib/dev/ctools/views_content/plugins/views/views_content_plugin_style_ctools_context.inc b/sites/all/modules/contrib/dev/ctools/views_content/plugins/views/views_content_plugin_style_ctools_context.inc index aafbebc..ea5323c 100644 --- a/sites/all/modules/contrib/dev/ctools/views_content/plugins/views/views_content_plugin_style_ctools_context.inc +++ b/sites/all/modules/contrib/dev/ctools/views_content/plugins/views/views_content_plugin_style_ctools_context.inc @@ -1,4 +1,5 @@ view->display_handler->previewing)) { return parent::render(); } diff --git a/sites/all/modules/contrib/dev/ctools/views_content/views_content.info b/sites/all/modules/contrib/dev/ctools/views_content/views_content.info index be82084..68f4d87 100644 --- a/sites/all/modules/contrib/dev/ctools/views_content/views_content.info +++ b/sites/all/modules/contrib/dev/ctools/views_content/views_content.info @@ -5,14 +5,12 @@ dependencies[] = ctools dependencies[] = views core = 7.x package = Chaos tool suite -version = CTOOLS_MODULE_VERSION files[] = plugins/views/views_content_plugin_display_ctools_context.inc files[] = plugins/views/views_content_plugin_display_panel_pane.inc files[] = plugins/views/views_content_plugin_style_ctools_context.inc -; Information added by Drupal.org packaging script on 2015-01-28 -version = "7.x-1.6" +; Information added by Drupal.org packaging script on 2019-02-08 +version = "7.x-1.15" core = "7.x" project = "ctools" -datestamp = "1422471484" - +datestamp = "1549603691" diff --git a/sites/all/modules/contrib/dev/ctools/views_content/views_content.module b/sites/all/modules/contrib/dev/ctools/views_content/views_content.module index 27a666a..264408a 100644 --- a/sites/all/modules/contrib/dev/ctools/views_content/views_content.module +++ b/sites/all/modules/contrib/dev/ctools/views_content/views_content.module @@ -1,8 +1,7 @@ t('Entity property or field (via Entity Metadata Wrapper)'), + 'description' => t('Creates any kind of context for entity properties and fields.'), + 'context' => 'entity_entity_property_context', + 'required context' => new ctools_context_required(t('Entity'), 'entity'), + 'edit form' => 'entity_entity_property_edit_form', + 'edit form validate' => 'entity_entity_property_edit_form_validate', + 'defaults' => array( + 'selector' => '', + 'target_context' => 'entity', + 'concatenator' => ',' + ), +); + +/** + * Return a new context based on an existing context. + */ +function entity_entity_property_context($context, $conf) { + $plugin = $conf['name']; + + // If unset it wants a generic, unfilled context, which is just NULL. + if (empty($context->data)) { + return ctools_context_create_empty(isset($conf['target_context']) ? $conf['target_context'] : 'entity', NULL); + } + + list($part1, $entity_type) = explode(':', $context->plugin); + + if (isset($context->data) && $entity = $context->data) { + // Apply the selector. + $wrapper = entity_metadata_wrapper($entity_type, $entity); + $parts = explode(':', $conf['selector']); + + try { + foreach ($parts as $part) { + if (!($wrapper instanceof EntityStructureWrapper || $wrapper instanceof EntityListWrapper)) { + $wrapper = NULL; + $value = NULL; + continue; + } + $wrapper = $wrapper->get($part); + } + } + catch (EntityMetadataWrapperException $e) { + $wrapper = NULL; + $value = NULL; + } + + if (isset($wrapper)) { + $value = $wrapper->value(); + } + + // Massage list values. + if ($wrapper instanceof EntityListWrapper) { + $value = $wrapper[0]->value(); + $argument = implode($conf['concatenator'], $wrapper->value(array('identifier' => TRUE))); + } + elseif ($wrapper instanceof EntityDrupalWrapper) { + $argument = $wrapper->getIdentifier(); + } + + // Bail out if we were unable to retrieve the value. + if (!isset($value)) { + return ctools_context_create_empty(isset($conf['target_context']) ? $conf['target_context'] : 'entity', NULL); + } + + $context = ctools_context_create($conf['target_context'], $value); + // Provide a suiting argument if context is used as argument. + if (isset($argument)) { + $context->argument = $argument; + } + return $context; + } +} + +function entity_entity_property_edit_form($form, &$form_state) { + $conf = $form_state['conf']; + + $form['selector'] = array( + '#type' => 'textfield', + '#title' => t('Data selector'), + '#description' => t('Any valid data selector, e.g. "title" to select a node\'s title, or "field_tags:1" to select the second tag.'), + '#default_value' => $conf['selector'], + '#required' => TRUE, + ); + $form['concatenator'] = array( + '#title' => t('Concatenator (if multiple)'), + '#type' => 'select', + '#options' => array(',' => ', (AND)', '+' => '+ (OR)'), + '#default_value' => $conf['concatenator'], + '#prefix' => '
    ', + '#suffix' => '
    ', + '#description' => t("When the resulting value is multiple valued and the context is passed on to a view as argument, the value can be concatenated in the form of 1+2+3 (for OR) or 1,2,3 (for AND)."), + ); + return $form; +} + +function entity_entity_property_edit_form_validate($form, &$form_state) { + $context_key = $form_state['values']['context']; + $context = $form_state['contexts'][$context_key]; + $entity_type = $context->type[2]; + + try { + $all_properties = entity_get_all_property_info($entity_type); + $wrapper = entity_metadata_wrapper($entity_type, NULL, array('property info' => $all_properties)); + $parts = explode(':', $form_state['values']['selector']); + foreach ($parts as $part) { + if (!($wrapper instanceof EntityStructureWrapper || $wrapper instanceof EntityListWrapper)) { + form_set_error('selector', t('Unable to apply the data selector part %key.'. array('%key' => $part))); + continue; + } + $wrapper = $wrapper->get($part); + } + $type = entity_entity_property_map_data_type($wrapper->type()); + $form_state['conf']['target_context'] = $type; + } + catch (EntityMetadataWrapperException $e) { + form_set_error('selector', t('Unable to apply the data selector on entity type %type. @reason', array('@reason' => $e->getMessage(), '%type' => $entity_type))); + } + + // Auto-generate a sane identifier. + if ($form_state['values']['identifier'] == $form['identifier']['#default_value']) { + $form_state['values']['identifier'] = 'Entity property ' . $entity_type . ':' . check_plain($form_state['values']['selector']); + } +} + +/** + * Maps an entity-property data type to ctools types. + */ +function entity_entity_property_map_data_type($type) { + // Remove list<> wrappers from types. + while ($item_type = entity_property_list_extract_type($type)) { + $type = $item_type; + } + // Massage data type of entites to c + if (entity_get_info($type)) { + $type = "entity:$type"; + } + if ($type == 'text') { + $type = 'string'; + } + return $type; +} diff --git a/sites/all/modules/contrib/dev/entity/entity.features.inc b/sites/all/modules/contrib/dev/entity/entity.features.inc index e266aaf..0ec3e61 100644 --- a/sites/all/modules/contrib/dev/entity/entity.features.inc +++ b/sites/all/modules/contrib/dev/entity/entity.features.inc @@ -7,6 +7,12 @@ /** * Returns the configured entity features controller. + * + * @param string $type + * The entity type to get the controller for. + * + * @return EntityDefaultFeaturesController + * The configured entity features controller. */ function entity_features_get_controller($type) { $static = &drupal_static(__FUNCTION__); @@ -85,6 +91,7 @@ class EntityDefaultFeaturesController { $fields = field_info_instances($this->info['bundle of'], $entity->{$this->bundleKey}); foreach ($fields as $name => $field) { $pipe['field'][] = "{$field['entity_type']}-{$field['bundle']}-{$field['field_name']}"; + $pipe['field_instance'][] = "{$field['entity_type']}-{$field['bundle']}-{$field['field_name']}"; } } } @@ -125,9 +132,7 @@ class EntityDefaultFeaturesController { */ function revert($module = NULL) { if ($defaults = features_get_default($this->type, $module)) { - foreach ($defaults as $name => $entity) { - entity_delete($this->type, $name); - } + entity_delete_multiple($this->type, array_keys($defaults)); } } } @@ -146,6 +151,8 @@ function entity_features_api() { } /** + * Implements hook_features_export_options(). + * * Features component callback. */ function entity_features_export_options($a1, $a2 = NULL) { @@ -157,6 +164,8 @@ function entity_features_export_options($a1, $a2 = NULL) { } /** + * Implements hook_features_export(). + * * Features component callback. */ function entity_features_export($data, &$export, $module_name = '', $entity_type) { @@ -164,6 +173,8 @@ function entity_features_export($data, &$export, $module_name = '', $entity_type } /** + * Implements hook_features_export_render(). + * * Features component callback. */ function entity_features_export_render($module, $data, $export = NULL, $entity_type) { @@ -171,8 +182,23 @@ function entity_features_export_render($module, $data, $export = NULL, $entity_t } /** + * Implements hook_features_revert(). + * * Features component callback. */ function entity_features_revert($module = NULL, $entity_type) { return entity_features_get_controller($entity_type)->revert($module); } + +/** + * Implements hook_features_post_restore(). + * + * Rebuild all defaults when a features rebuild is triggered - even the ones not + * handled by features itself. + */ +function entity_features_post_restore($op, $items = array()) { + if ($op == 'rebuild') { + // Use features rebuild to rebuild the features independent exports too. + entity_defaults_rebuild(); + } +} diff --git a/sites/all/modules/contrib/dev/entity/entity.info b/sites/all/modules/contrib/dev/entity/entity.info index a60170f..0b865a0 100644 --- a/sites/all/modules/contrib/dev/entity/entity.info +++ b/sites/all/modules/contrib/dev/entity/entity.info @@ -25,9 +25,9 @@ files[] = views/handlers/entity_views_handler_field_uri.inc files[] = views/handlers/entity_views_handler_relationship_by_bundle.inc files[] = views/handlers/entity_views_handler_relationship.inc files[] = views/plugins/entity_views_plugin_row_entity_view.inc -; Information added by Drupal.org packaging script on 2014-04-08 -version = "7.x-1.5" +; Information added by Drupal.org packaging script on 2018-02-14 +version = "7.x-1.9" core = "7.x" project = "entity" -datestamp = "1396975454" +datestamp = "1518620551" diff --git a/sites/all/modules/contrib/dev/entity/entity.info.inc b/sites/all/modules/contrib/dev/entity/entity.info.inc index fd52d06..30da655 100644 --- a/sites/all/modules/contrib/dev/entity/entity.info.inc +++ b/sites/all/modules/contrib/dev/entity/entity.info.inc @@ -169,6 +169,7 @@ function _entity_metadata_convert_schema_type($type) { switch ($type) { case 'int': case 'serial': + case 'date': return 'integer'; case 'float': case 'numeric': diff --git a/sites/all/modules/contrib/dev/entity/entity.install b/sites/all/modules/contrib/dev/entity/entity.install index f6f8cc2..118820b 100644 --- a/sites/all/modules/contrib/dev/entity/entity.install +++ b/sites/all/modules/contrib/dev/entity/entity.install @@ -13,6 +13,14 @@ function entity_enable() { entity_entitycache_installed_modules(); } +/** + * Implements hook_uninstall(). + */ +function entity_uninstall() { + // Delete variables. + variable_del('entity_rebuild_on_flush'); +} + /** * The entity API modules have been merged into a single module. */ diff --git a/sites/all/modules/contrib/dev/entity/entity.module b/sites/all/modules/contrib/dev/entity/entity.module index d4a882c..da0acf6 100644 --- a/sites/all/modules/contrib/dev/entity/entity.module +++ b/sites/all/modules/contrib/dev/entity/entity.module @@ -184,8 +184,17 @@ function entity_ui_entity_page_view($entity) { * Gets the page title for the passed operation. */ function entity_ui_get_page_title($op, $entity_type, $entity = NULL) { - module_load_include('inc', 'entity', 'includes/entity.ui'); - $label = entity_label($entity_type, $entity); + if (isset($entity)) { + module_load_include('inc', 'entity', 'includes/entity.ui'); + $label = entity_label($entity_type, $entity); + list(, , $bundle) = entity_extract_ids($entity_type, $entity); + } + else { + $info = entity_get_info($entity_type); + $label = $info['label']; + $bundle = NULL; + } + switch ($op) { case 'view': return $label; @@ -200,12 +209,7 @@ function entity_ui_get_page_title($op, $entity_type, $entity = NULL) { case 'export': return t('Export @label', array('@label' => $label)); } - if (isset($entity)) { - list(, , $bundle) = entity_extract_ids($entity_type, $entity); - } - else { - $bundle = NULL; - } + return entity_ui_get_action_title($op, $entity_type, $bundle); } @@ -600,7 +604,7 @@ function entity_id($entity_type, $entity) { * content language of the current request. * @param $page * (optional) If set will control if the entity is rendered: if TRUE - * the entity will be rendered without its title, so that it can be embeded + * the entity will be rendered without its title, so that it can be embedded * in another context. If FALSE the entity will be displayed with its title * in a mode suitable for lists. * If unset, the page mode will be enabled if the current path is the URI @@ -1076,16 +1080,18 @@ function entity_flush_caches() { // case of enabling or disabling modules we already rebuild defaults in // entity_modules_enabled() and entity_modules_disabled(), so we do not need // to do it again. - if (current_path() != 'admin/modules/list/confirm') { + // Also check if rebuilding on cache flush is explicitly disabled. + if (current_path() != 'admin/modules/list/confirm' && variable_get('entity_rebuild_on_flush', TRUE)) { entity_defaults_rebuild(); } // Care about entitycache tables. if (module_exists('entitycache')) { $tables = array(); - foreach (entity_crud_get_info() as $entity_type => $entity_info) { - if (isset($entity_info['module']) && !empty($entity_info['entity cache'])) { - $tables[] = 'cache_entity_' . $entity_type; + $tables_created = variable_get('entity_cache_tables_created'); + if (is_array($tables_created)) { + foreach ($tables_created as $module => $entity_cache_tables) { + $tables = array_merge($tables, $entity_cache_tables); } } return $tables; @@ -1372,7 +1378,7 @@ function entity_get_extra_fields_controller($type = NULL) { * Returns a property wrapper for the given data. * * If an entity is wrapped, the wrapper can be used to retrieve further wrappers - * for the entitity properties. For that the wrapper support chaining, e.g. you + * for the entity properties. For that the wrapper support chaining, e.g. you * can use a node wrapper to get the node authors mail address: * * @code @@ -1563,7 +1569,7 @@ function _entity_info_add_metadata(&$entity_info) { * Implements hook_ctools_plugin_directory(). */ function entity_ctools_plugin_directory($module, $plugin) { - if ($module == 'ctools' && $plugin == 'content_types') { - return 'ctools/content_types'; + if ($module == 'ctools') { + return "ctools/$plugin"; } } diff --git a/sites/all/modules/contrib/dev/entity/entity.test b/sites/all/modules/contrib/dev/entity/entity.test index 03c2551..fd8cea1 100644 --- a/sites/all/modules/contrib/dev/entity/entity.test +++ b/sites/all/modules/contrib/dev/entity/entity.test @@ -1342,6 +1342,74 @@ class EntityMetadataNodeRevisionAccessTestCase extends DrupalWebTestCase { } } +/** + * Tests basic entity_access() functionality for taxonomy terms. + */ +class EntityMetadataTaxonomyAccessTestCase extends EntityWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Entity Metadata Taxonomy Access', + 'description' => 'Test entity_access() for taxonomy terms', + 'group' => 'Entity API', + ); + } + + /** + * Asserts entity_access() correctly grants or denies access. + */ + function assertTaxonomyMetadataAccess($ops, $term, $account) { + foreach ($ops as $op => $result) { + $msg = t("entity_access() returns @result with operation '@op'.", array('@result' => $result ? 'TRUE' : 'FALSE', '@op' => $op)); + $access = entity_access($op, 'taxonomy_term', $term, $account); + $this->assertEqual($result, $access, $msg); + } + } + + /** + * @inheritdoc + */ + function setUp() { + parent::setUp('entity', 'taxonomy'); + // Clear permissions for authenticated users. + db_delete('role_permission') + ->condition('rid', DRUPAL_AUTHENTICATED_RID) + ->execute(); + } + + /** + * Runs basic tests for entity_access() function. + */ + function testTaxonomyMetadataAccess() { + $vocab = $this->createVocabulary(); + $term = entity_property_values_create_entity('taxonomy_term', array( + 'name' => $this->randomName(), + 'vocabulary' => $vocab, + ))->save()->value(); + // Clear permissions static cache to get new taxonomy permissions. + drupal_static_reset('checkPermissions'); + + // Check assignment of view permissions. + $user1 = $this->drupalCreateUser(array('access content')); + $this->assertTaxonomyMetadataAccess(array('create' => FALSE, 'view' => TRUE, 'update' => FALSE, 'delete' => FALSE), $term, $user1); + + // Check assignment of edit permissions. + $user2 = $this->drupalCreateUser(array('edit terms in ' . $vocab->vid)); + $this->assertTaxonomyMetadataAccess(array('create' => FALSE, 'view' => FALSE, 'update' => TRUE, 'delete' => FALSE), $term, $user2); + + // Check assignment of delete permissions. + $user3 = $this->drupalCreateUser(array('delete terms in ' . $vocab->vid)); + $this->assertTaxonomyMetadataAccess(array('create' => FALSE, 'view' => FALSE, 'update' => FALSE, 'delete' => TRUE), $term, $user3); + + // Check assignment of view, edit, delete permissions. + $user4 = $this->drupalCreateUser(array('access content', 'edit terms in ' . $vocab->vid, 'delete terms in ' . $vocab->vid)); + $this->assertTaxonomyMetadataAccess(array('create' => FALSE, 'view' => TRUE, 'update' => TRUE, 'delete' => TRUE), $term, $user4); + + // Check assignment of administration permissions. + $user5 = $this->drupalCreateUser(array('administer taxonomy')); + $this->assertTaxonomyMetadataAccess(array('create' => TRUE, 'view' => TRUE, 'update' => TRUE, 'delete' => TRUE), $term, $user5); + } +} + /** * Tests provided entity property info of the core modules. */ @@ -1466,6 +1534,7 @@ class EntityMetadataIntegrationTestCase extends EntityWebTestCase { $book = array('bid' => $node->nid, 'plid' => $node->book['mlid']); $node2 = $this->drupalCreateNode(array('type' => 'book', 'book' => $book)); $node3 = $this->drupalCreateNode(array('type' => 'page')); + $node4 = $this->drupalCreateNode(array('type' => 'book', 'book' => array('bid' => 0, 'plid' => -1))); // Test whether the properties work. $wrapper = entity_metadata_wrapper('node', $node2); @@ -1477,6 +1546,10 @@ class EntityMetadataIntegrationTestCase extends EntityWebTestCase { $wrapper = entity_metadata_wrapper('node', $node3); $this->assertEmpty($wrapper, 'book'); $this->assertEmptyArray($wrapper, 'book_ancestors'); + + // Test a book node which is not contained in a hierarchy. + $wrapper = entity_metadata_wrapper('node', $node4); + $this->assertEmptyArray($wrapper, 'book_ancestors'); } /** diff --git a/sites/all/modules/contrib/dev/entity/entity_token.info b/sites/all/modules/contrib/dev/entity/entity_token.info index 194e213..c59c650 100644 --- a/sites/all/modules/contrib/dev/entity/entity_token.info +++ b/sites/all/modules/contrib/dev/entity/entity_token.info @@ -5,9 +5,9 @@ files[] = entity_token.tokens.inc files[] = entity_token.module dependencies[] = entity -; Information added by Drupal.org packaging script on 2014-04-08 -version = "7.x-1.5" +; Information added by Drupal.org packaging script on 2018-02-14 +version = "7.x-1.9" core = "7.x" project = "entity" -datestamp = "1396975454" +datestamp = "1518620551" diff --git a/sites/all/modules/contrib/dev/entity/includes/entity.controller.inc b/sites/all/modules/contrib/dev/entity/includes/entity.controller.inc index f675a63..5e86b52 100644 --- a/sites/all/modules/contrib/dev/entity/includes/entity.controller.inc +++ b/sites/all/modules/contrib/dev/entity/includes/entity.controller.inc @@ -107,7 +107,7 @@ interface EntityAPIControllerInterface extends DrupalEntityControllerInterface { * content language of the current request. * @param $page * (optional) If set will control if the entity is rendered: if TRUE - * the entity will be rendered without its title, so that it can be embeded + * the entity will be rendered without its title, so that it can be embedded * in another context. If FALSE the entity will be displayed with its title * in a mode suitable for lists. * If unset, the page mode will be enabled if the current path is the URI diff --git a/sites/all/modules/contrib/dev/entity/includes/entity.inc b/sites/all/modules/contrib/dev/entity/includes/entity.inc index 4efd071..2f504f3 100644 --- a/sites/all/modules/contrib/dev/entity/includes/entity.inc +++ b/sites/all/modules/contrib/dev/entity/includes/entity.inc @@ -5,6 +5,168 @@ * Provides a base class for entities. */ +/** + * Interface for class based entities. + */ +interface EntityInterface { + + /** + * Returns the internal, numeric identifier. + * + * Returns the numeric identifier, even if the entity type has specified a + * name key. In the latter case, the numeric identifier is supposed to be used + * when dealing generically with entities or internally to refer to an entity, + * i.e. in a relational database. If unsure, use Entity:identifier(). + */ + public function internalIdentifier(); + + /** + * Returns the entity identifier, i.e. the entities name or numeric id. + * + * @return + * The identifier of the entity. If the entity type makes use of a name key, + * the name is returned, else the numeric id. + * + * @see entity_id() + */ + public function identifier(); + + /** + * Returns the info of the type of the entity. + * + * @see entity_get_info() + */ + public function entityInfo(); + + /** + * Returns the type of the entity. + */ + public function entityType(); + + /** + * Returns the bundle of the entity. + * + * @return + * The bundle of the entity. Defaults to the entity type if the entity type + * does not make use of different bundles. + */ + public function bundle(); + + /** + * Returns the EntityMetadataWrapper of the entity. + * + * @return EntityDrupalWrapper + * An EntityMetadataWrapper containing the entity. + */ + public function wrapper(); + + /** + * Returns the label of the entity. + * + * Modules may alter the label by specifying another 'label callback' using + * hook_entity_info_alter(). + * + * @see entity_label() + */ + public function label(); + + /** + * Returns the uri of the entity just as entity_uri(). + * + * Modules may alter the uri by specifying another 'uri callback' using + * hook_entity_info_alter(). + * + * @see entity_uri() + */ + public function uri(); + + /** + * Checks if the entity has a certain exportable status. + * + * @param $status + * A status constant, i.e. one of ENTITY_CUSTOM, ENTITY_IN_CODE, + * ENTITY_OVERRIDDEN or ENTITY_FIXED. + * + * @return bool + * For exportable entities TRUE if the entity has the status, else FALSE. + * In case the entity is not exportable, NULL is returned. + * + * @see entity_has_status() + */ + public function hasStatus($status); + + /** + * Permanently saves the entity. + * + * @see entity_save() + */ + public function save(); + + /** + * Permanently deletes the entity. + * + * @see entity_delete() + */ + public function delete(); + + /** + * Exports the entity. + * + * @see entity_export() + */ + public function export($prefix = ''); + + /** + * Generate an array for rendering the entity. + * + * @see entity_view() + */ + public function view($view_mode = 'full', $langcode = NULL, $page = NULL); + + /** + * Builds a structured array representing the entity's content. + * + * @see entity_build_content() + */ + public function buildContent($view_mode = 'full', $langcode = NULL); + + /** + * Gets the raw, translated value of a property or field. + * + * Supports retrieving field translations as well as i18n string translations. + * + * Note that this returns raw data values, which might not reflect what + * has been declared for hook_entity_property_info() as no 'getter callbacks' + * are invoked or no referenced entities are loaded. For retrieving values + * reflecting the property info make use of entity metadata wrappers, see + * entity_metadata_wrapper(). + * + * @param $property + * The name of the property to return; e.g., 'title'. + * @param $langcode + * (optional) The language code of the language to which the value should + * be translated. If set to NULL, the default display language is being + * used. + * + * @return + * The raw, translated property value; or the raw, un-translated value if no + * translation is available. + * + * @todo Implement an analogous setTranslation() method for updating. + */ + public function getTranslation($property, $langcode = NULL); + + /** + * Checks whether the entity is the default revision. + * + * @return Boolean + * + * @see entity_revision_is_default() + */ + public function isDefaultRevision(); + +} + /** * A common class for entities. * @@ -25,17 +187,16 @@ * public $count = 0; * @endcode */ -class Entity { +class Entity implements EntityInterface { protected $entityType; protected $entityInfo; protected $idKey, $nameKey, $statusKey; protected $defaultLabel = FALSE; + protected $wrapper; /** - * Creates a new entity. - * - * @see entity_create() + * {@inheritdoc} */ public function __construct(array $values = array(), $entityType = NULL) { if (empty($entityType)) { @@ -60,64 +221,56 @@ class Entity { } /** - * Returns the internal, numeric identifier. - * - * Returns the numeric identifier, even if the entity type has specified a - * name key. In the latter case, the numeric identifier is supposed to be used - * when dealing generically with entities or internally to refer to an entity, - * i.e. in a relational database. If unsure, use Entity:identifier(). + * {@inheritdoc} */ public function internalIdentifier() { return isset($this->{$this->idKey}) ? $this->{$this->idKey} : NULL; } /** - * Returns the entity identifier, i.e. the entities name or numeric id. - * - * @return - * The identifier of the entity. If the entity type makes use of a name key, - * the name is returned, else the numeric id. - * - * @see entity_id() + * {@inheritdoc} */ public function identifier() { return isset($this->{$this->nameKey}) ? $this->{$this->nameKey} : NULL; } /** - * Returns the info of the type of the entity. - * - * @see entity_get_info() + * {@inheritdoc} */ public function entityInfo() { return $this->entityInfo; } /** - * Returns the type of the entity. + * {@inheritdoc} */ public function entityType() { return $this->entityType; } /** - * Returns the bundle of the entity. - * - * @return - * The bundle of the entity. Defaults to the entity type if the entity type - * does not make use of different bundles. + * {@inheritdoc} */ public function bundle() { return !empty($this->entityInfo['entity keys']['bundle']) ? $this->{$this->entityInfo['entity keys']['bundle']} : $this->entityType; } /** - * Returns the label of the entity. - * - * Modules may alter the label by specifying another 'label callback' using - * hook_entity_info_alter(). - * - * @see entity_label() + * {@inheritdoc} + */ + public function wrapper() { + if (empty($this->wrapper)) { + $this->wrapper = entity_metadata_wrapper($this->entityType, $this); + } + elseif ($this->wrapper->value() !== $this) { + // Wrapper has been modified outside, so let's better create a new one. + $this->wrapper = entity_metadata_wrapper($this->entityType, $this); + } + return $this->wrapper; + } + + /** + * {@inheritdoc} */ public function label() { // If the default label flag is enabled, this is being invoked recursively. @@ -147,12 +300,7 @@ class Entity { } /** - * Returns the uri of the entity just as entity_uri(). - * - * Modules may alter the uri by specifying another 'uri callback' using - * hook_entity_info_alter(). - * - * @see entity_uri() + * {@inheritdoc} */ public function uri() { if (isset($this->entityInfo['uri callback']) && $this->entityInfo['uri callback'] == 'entity_class_uri') { @@ -170,17 +318,7 @@ class Entity { } /** - * Checks if the entity has a certain exportable status. - * - * @param $status - * A status constant, i.e. one of ENTITY_CUSTOM, ENTITY_IN_CODE, - * ENTITY_OVERRIDDEN or ENTITY_FIXED. - * - * @return - * For exportable entities TRUE if the entity has the status, else FALSE. - * In case the entity is not exportable, NULL is returned. - * - * @see entity_has_status() + * {@inheritdoc} */ public function hasStatus($status) { if (!empty($this->entityInfo['exportable'])) { @@ -189,18 +327,14 @@ class Entity { } /** - * Permanently saves the entity. - * - * @see entity_save() + * {@inheritdoc} */ public function save() { return entity_get_controller($this->entityType)->save($this); } /** - * Permanently deletes the entity. - * - * @see entity_delete() + * {@inheritdoc} */ public function delete() { $id = $this->identifier(); @@ -210,55 +344,28 @@ class Entity { } /** - * Exports the entity. - * - * @see entity_export() + * {@inheritdoc} */ public function export($prefix = '') { return entity_get_controller($this->entityType)->export($this, $prefix); } /** - * Generate an array for rendering the entity. - * - * @see entity_view() + * {@inheritdoc} */ public function view($view_mode = 'full', $langcode = NULL, $page = NULL) { return entity_get_controller($this->entityType)->view(array($this), $view_mode, $langcode, $page); } /** - * Builds a structured array representing the entity's content. - * - * @see entity_build_content() + * {@inheritdoc} */ public function buildContent($view_mode = 'full', $langcode = NULL) { return entity_get_controller($this->entityType)->buildContent($this, $view_mode, $langcode); } /** - * Gets the raw, translated value of a property or field. - * - * Supports retrieving field translations as well as i18n string translations. - * - * Note that this returns raw data values, which might not reflect what - * has been declared for hook_entity_property_info() as no 'getter callbacks' - * are invoked or no referenced entities are loaded. For retrieving values - * reflecting the property info make use of entity metadata wrappers, see - * entity_metadata_wrapper(). - * - * @param $property_name - * The name of the property to return; e.g., 'title'. - * @param $langcode - * (optional) The language code of the language to which the value should - * be translated. If set to NULL, the default display language is being - * used. - * - * @return - * The raw, translated property value; or the raw, un-translated value if no - * translation is available. - * - * @todo Implement an analogous setTranslation() method for updating. + * {@inheritdoc} */ public function getTranslation($property, $langcode = NULL) { $all_info = entity_get_all_property_info($this->entityType); @@ -278,11 +385,7 @@ class Entity { } /** - * Checks whether the entity is the default revision. - * - * @return Boolean - * - * @see entity_revision_is_default() + * {@inheritdoc} */ public function isDefaultRevision() { if (!empty($this->entityInfo['entity keys']['revision'])) { diff --git a/sites/all/modules/contrib/dev/entity/includes/entity.property.inc b/sites/all/modules/contrib/dev/entity/includes/entity.property.inc index 38e4fd7..e8714e6 100644 --- a/sites/all/modules/contrib/dev/entity/includes/entity.property.inc +++ b/sites/all/modules/contrib/dev/entity/includes/entity.property.inc @@ -69,7 +69,7 @@ function entity_property_info_defaults() { * (optiona) The entity type to return properties for. * * @return - * An array of info about properties. If the type is ommitted, all known + * An array of info about properties. If the type is omitted, all known * properties are returned. */ function entity_get_all_property_info($entity_type = NULL) { diff --git a/sites/all/modules/contrib/dev/entity/includes/entity.ui.inc b/sites/all/modules/contrib/dev/entity/includes/entity.ui.inc index 1826da4..24e3c2b 100644 --- a/sites/all/modules/contrib/dev/entity/includes/entity.ui.inc +++ b/sites/all/modules/contrib/dev/entity/includes/entity.ui.inc @@ -127,8 +127,8 @@ class EntityDefaultUIController { * Use per bundle form ids if possible, such that easy per bundle alterations * are supported too. * - * Note that for performance reasons, this method is only invoked for forms, - * which receive the entity_type as first argument. Thus any forms added, must + * Note that for performance reasons, this method is invoked only for forms + * which receive the entity_type as first argument. Thus any forms added must * follow that pattern. * * @see entity_forms() @@ -645,7 +645,7 @@ function entity_ui_operation_form($form, &$form_state, $entity_type, $entity, $o */ function entity_ui_main_form_defaults($form, &$form_state, $entity = NULL, $op = NULL) { // Now equals entity_ui_form_defaults() but is still here to keep backward - // compatability. + // compatibility. return entity_ui_form_defaults($form, $form_state, $form_state['entity_type'], $entity, $op); } @@ -760,7 +760,7 @@ function entity_ui_validate_machine_name($element, &$form_state) { function theme_entity_ui_overview_item($variables) { $output = $variables['url'] ? l($variables['label'], $variables['url']['path'], $variables['url']['options']) : check_plain($variables['label']); if ($variables['name']) { - $output .= ' (' . t('Machine name') . ': ' . check_plain($variables['name']) . ')'; + $output .= ' (' . t('Machine name') . ': ' . check_plain($variables['name']) . ')'; } return $output; } diff --git a/sites/all/modules/contrib/dev/entity/includes/entity.wrapper.inc b/sites/all/modules/contrib/dev/entity/includes/entity.wrapper.inc index 67764e7..860d2c3 100644 --- a/sites/all/modules/contrib/dev/entity/includes/entity.wrapper.inc +++ b/sites/all/modules/contrib/dev/entity/includes/entity.wrapper.inc @@ -119,7 +119,11 @@ abstract class EntityMetadataWrapper { */ public function set($value) { if (!$this->validate($value)) { - throw new EntityMetadataWrapperException('Invalid data value given. Be sure it matches the required data type and format.'); + throw new EntityMetadataWrapperException(t('Invalid data value given. Be sure it matches the required data type and format. Value at !location: !value.', array( + // An exception's message is output through check_plain(). + '!value' => is_array($value) || is_object($value) ? var_export($value, TRUE) : $value, + '!location' => $this->debugIdentifierLocation(), + ))); } $this->clear(); $this->data = $value; @@ -231,6 +235,21 @@ abstract class EntityMetadataWrapper { return !empty($this->info['parent']) ? $this->info['parent']->propertyAccess($this->info['name'], $op, $account) : TRUE; } + /** + * Returns a string to use to identify this wrapper in error messages. + * + * @return + * A string that identifies this wrapper and its chain of ancestors, of the + * form 'grandparentidentifier->parentidentifier->identifier'. + */ + public function debugIdentifierLocation() { + $debug = $this->info['name']; + if (isset($this->info['parent'])) { + $debug = $this->info['parent']->debugIdentifierLocation() . '->' . $debug; + } + return $debug; + } + /** * Prepare for serializiation. */ @@ -504,7 +523,7 @@ class EntityStructureWrapper extends EntityMetadataWrapper implements IteratorAg while (!($entity instanceof EntityDrupalWrapper) && isset($entity->info['parent'])) { $entity = $entity->info['parent']; } - if ($entity instanceof EntityDrupalWrapper && !$entity->entityAccess('update', $account)) { + if ($entity instanceof EntityDrupalWrapper && $entity->entityAccess('update', $account) === FALSE) { return FALSE; } } @@ -515,6 +534,7 @@ class EntityStructureWrapper extends EntityMetadataWrapper implements IteratorAg elseif ($op == 'edit' && isset($info['setter permission'])) { return user_access($info['setter permission'], $account); } + // If access is unknown, we return TRUE. return TRUE; } @@ -733,7 +753,11 @@ class EntityDrupalWrapper extends EntityStructureWrapper { */ public function set($value) { if (!$this->validate($value)) { - throw new EntityMetadataWrapperException('Invalid data value given. Be sure it matches the required data type and format.'); + throw new EntityMetadataWrapperException(t('Invalid data value given. Be sure it matches the required data type and format. Value at !location: !value.', array( + // An exception's message is output through check_plain(). + '!value' => is_array($value) || is_object($value) ? var_export($value, TRUE) : $value, + '!location' => $this->debugIdentifierLocation(), + ))); } if ($this->info['type'] == 'entity' && $value === $this) { // Nothing to do. @@ -809,11 +833,23 @@ class EntityDrupalWrapper extends EntityStructureWrapper { if (!empty($this->info['parent'])) { // If this is a property, make sure the user is able to view the // currently referenced entity also. - return $this->entityAccess('view', $account) && parent::access($op, $account); + if ($this->entityAccess('view', $account) === FALSE) { + return FALSE; + } + if (parent::access($op, $account) === FALSE) { + return FALSE; + } + // If access is unknown, we return TRUE. + return TRUE; } else { // This is not a property, so fallback on entity access. - return $this->entityAccess($op == 'edit' ? 'update' : 'view', $account); + if ($op == 'edit') { + // If the operation is "edit" determine if its actually a "create" for + // new un-saved entities, or an "update" for existing ones. + return $this->entityAccess($this->getIdentifier() ? 'update' : 'create', $account); + } + return $this->entityAccess('view', $account); } } @@ -901,6 +937,27 @@ class EntityDrupalWrapper extends EntityStructureWrapper { } } + /** + * Returns a string to use to identify this wrapper in error messages. + */ + public function debugIdentifierLocation() { + // An entity wrapper can be at the top of the chain or a part of it. + if (isset($this->info['name'])) { + // This wrapper is part of a chain, eg in the position node->author. + // Return the name. + $debug = $this->info['name']; + } + else { + // This is a wrapper for an actual entity: return its type and id. + $debug = $this->info['type'] . '(' . $this->getIdentifier() . ')'; + } + + if (isset($this->info['parent'])) { + $debug = $this->info['parent']->debugIdentifierLocation() . '->' . $debug; + } + return $debug; + } + /** * Prepare for serializiation. */ @@ -1059,7 +1116,7 @@ class EntityListWrapper extends EntityMetadataWrapper implements IteratorAggrega */ public function getIterator() { // In case there is no data available, just iterate over the first item. - return new EntityMetadataWrapperIterator($this, $this->dataAvailable() ? array_keys(parent::value()) : array(0)); + return new EntityMetadataWrapperIterator($this, ($this->dataAvailable() && is_array(parent::value())) ? array_keys(parent::value()) : array(0)); } /** diff --git a/sites/all/modules/contrib/dev/entity/modules/callbacks.inc b/sites/all/modules/contrib/dev/entity/modules/callbacks.inc index 3865efd..ee156ab 100644 --- a/sites/all/modules/contrib/dev/entity/modules/callbacks.inc +++ b/sites/all/modules/contrib/dev/entity/modules/callbacks.inc @@ -29,7 +29,7 @@ function entity_metadata_book_get_properties($node, array $options, $name, $enti case 'book_ancestors': $ancestors = array(); - while (!empty($node->book['plid'])) { + while (!empty($node->book['plid']) && $node->book['plid'] != -1) { $link = book_link_load($node->book['plid']); array_unshift($ancestors, $link['nid']); $node = node_load($link['nid']); @@ -670,9 +670,11 @@ function entity_metadata_field_file_validate_item($items, $context) { function entity_metadata_no_hook_node_access($op, $node = NULL, $account = NULL) { // First deal with the case where a $node is provided. if (isset($node)) { - if ($op == 'create') { + if (empty($node->vid) && in_array($op, array('create', 'update'))) { + // This is a new node or the original node. if (isset($node->type)) { - return node_access($op, $node->type, $account); + $op = empty($node->nid) || !empty($node->is_new) ? 'create' : 'update'; + return node_access($op, $op == 'create' ? $node->type : $node, $account); } else { throw new EntityMalformedException('Permission to create a node was requested but no node type was given.'); @@ -703,13 +705,14 @@ function entity_metadata_no_hook_node_access($op, $node = NULL, $account = NULL) /** * Access callback for the user entity. */ -function entity_metadata_user_access($op, $entity = NULL, $account = NULL, $entity_type) { +function entity_metadata_user_access($op, $entity = NULL, $account = NULL, $entity_type = NULL) { $account = isset($account) ? $account : $GLOBALS['user']; // Grant access to the users own user account and to the anonymous one. - if (isset($entity) && $op != 'delete' && (($entity->uid == $account->uid && $entity->uid) || (!$entity->uid && $op == 'view'))) { + if (isset($entity->uid) && $op != 'delete' && (($entity->uid == $account->uid && $entity->uid) || (!$entity->uid && $op == 'view'))) { return TRUE; } - if (user_access('administer users', $account) || user_access('access user profiles', $account) && $op == 'view' && $entity->status) { + if (user_access('administer users', $account) + || user_access('access user profiles', $account) && $op == 'view' && (empty($entity) || !empty($entity->status))) { return TRUE; } return FALSE; @@ -724,7 +727,7 @@ function entity_metadata_user_properties_access($op, $property, $entity = NULL, } $account = isset($account) ? $account : $GLOBALS['user']; // Flag to indicate if this user entity is the own user account. - $is_own_account = isset($entity) && $account->uid == $entity->uid; + $is_own_account = isset($entity->uid) && $account->uid == $entity->uid; switch ($property) { case 'name': // Allow view access to anyone with access to the entity. @@ -794,15 +797,36 @@ function entity_metadata_comment_properties_access($op, $property, $entity = NUL /** * Access callback for the taxonomy entities. */ -function entity_metadata_taxonomy_access($op, $entity = NULL, $account = NULL, $entity_type) { - if ($entity_type == 'taxonomy_vocabulary') { - return user_access('administer taxonomy', $account); - } - if (isset($entity) && $op == 'update' && !isset($account) && taxonomy_term_edit_access($entity)) { +function entity_metadata_taxonomy_access($op, $entity = NULL, $account = NULL, $entity_type = NULL) { + // If user has administer taxonomy permission then no further checks. + if (user_access('administer taxonomy', $account)) { return TRUE; } - if (user_access('administer taxonomy', $account) || user_access('access content', $account) && $op == 'view') { - return TRUE; + switch ($op) { + case "view": + if (user_access('access content', $account)) { + return TRUE; + } + break; + case "update": + if ($entity_type == 'taxonomy_term') { + return user_access("edit terms in $entity->vid", $account); + } + break; + case "create": + if ($entity_type == 'taxonomy_term') { + // Check for taxonomy_access_fix contrib module which adds additional + // permissions to create new terms in a given vocabulary. + if (function_exists('taxonomy_access_fix_access')) { + return taxonomy_access_fix_access('add terms', $entity->vocabulary_machine_name); + } + } + break; + case "delete": + if ($entity_type == 'taxonomy_term') { + return user_access("delete terms in $entity->vid", $account); + } + break; } return FALSE; } diff --git a/sites/all/modules/contrib/dev/entity/modules/field.info.inc b/sites/all/modules/contrib/dev/entity/modules/field.info.inc index 9ef372b..aeea79a 100644 --- a/sites/all/modules/contrib/dev/entity/modules/field.info.inc +++ b/sites/all/modules/contrib/dev/entity/modules/field.info.inc @@ -51,7 +51,10 @@ function entity_metadata_field_default_property_callback(&$info, $entity_type, $ $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$name]; $instance += array('property info' => array()); $property = $instance['property info'] + array( - 'label' => $instance['label'], + // Since the label will be exposed via hook_token_info() and it is not + // clearly defined if that should be sanitized already we prevent XSS + // right here (field labels are user provided text). + 'label' => filter_xss_admin($instance['label']), 'type' => $field_type['property_type'], 'description' => t('Field "@name".', array('@name' => $name)), 'getter callback' => 'entity_metadata_field_property_get', @@ -164,7 +167,7 @@ function entity_metadata_field_image_callback(&$info, $entity_type, $field, $ins if (empty($instance['settings']['alt_field'])) { unset($property['property info']['alt']); } - if (empty($field['settings']['title_field'])) { + if (empty($instance['settings']['title_field'])) { unset($property['property info']['title']); } } diff --git a/sites/all/modules/contrib/dev/entity/modules/node.info.inc b/sites/all/modules/contrib/dev/entity/modules/node.info.inc index 3512d9a..f146a7e 100644 --- a/sites/all/modules/contrib/dev/entity/modules/node.info.inc +++ b/sites/all/modules/contrib/dev/entity/modules/node.info.inc @@ -163,4 +163,10 @@ function entity_metadata_node_entity_property_info_alter(&$info) { 'auto creation' => 'entity_property_create_array', 'field' => TRUE, ); + + // Make it a list if cardinality is not 1. + $field_body = field_info_field('body'); + if (isset($field_body) && $field_body['cardinality'] != 1) { + $info['node']['properties']['body']['type'] = 'list'; + } } diff --git a/sites/all/modules/contrib/dev/entity/tests/entity_feature.info b/sites/all/modules/contrib/dev/entity/tests/entity_feature.info index 051ff00..524fdfa 100644 --- a/sites/all/modules/contrib/dev/entity/tests/entity_feature.info +++ b/sites/all/modules/contrib/dev/entity/tests/entity_feature.info @@ -6,9 +6,9 @@ files[] = entity_feature.module dependencies[] = entity_test hidden = TRUE -; Information added by Drupal.org packaging script on 2014-04-08 -version = "7.x-1.5" +; Information added by Drupal.org packaging script on 2018-02-14 +version = "7.x-1.9" core = "7.x" project = "entity" -datestamp = "1396975454" +datestamp = "1518620551" diff --git a/sites/all/modules/contrib/dev/entity/tests/entity_test.info b/sites/all/modules/contrib/dev/entity/tests/entity_test.info index c807506..b7cceab 100644 --- a/sites/all/modules/contrib/dev/entity/tests/entity_test.info +++ b/sites/all/modules/contrib/dev/entity/tests/entity_test.info @@ -7,9 +7,9 @@ files[] = entity_test.install dependencies[] = entity hidden = TRUE -; Information added by Drupal.org packaging script on 2014-04-08 -version = "7.x-1.5" +; Information added by Drupal.org packaging script on 2018-02-14 +version = "7.x-1.9" core = "7.x" project = "entity" -datestamp = "1396975454" +datestamp = "1518620551" diff --git a/sites/all/modules/contrib/dev/entity/tests/entity_test.install b/sites/all/modules/contrib/dev/entity/tests/entity_test.install index 42ba4b6..a969323 100644 --- a/sites/all/modules/contrib/dev/entity/tests/entity_test.install +++ b/sites/all/modules/contrib/dev/entity/tests/entity_test.install @@ -51,8 +51,14 @@ function entity_test_schema() { 'uid' => array('uid'), ), 'foreign keys' => array( - 'uid' => array('users' => 'uid'), - 'name' => array('entity_test_types' => 'name'), + 'uid' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid') + ), + 'name' => array( + 'table' => 'entity_test_types', + 'columns' => array('name' => 'name') + ), ), 'primary key' => array('pid'), ); diff --git a/sites/all/modules/contrib/dev/entity/tests/entity_test.module b/sites/all/modules/contrib/dev/entity/tests/entity_test.module index 5585568..727797a 100644 --- a/sites/all/modules/contrib/dev/entity/tests/entity_test.module +++ b/sites/all/modules/contrib/dev/entity/tests/entity_test.module @@ -2,7 +2,7 @@ /** * @file - * Test moduel for the entity API. + * Test module for the entity API. */ /** diff --git a/sites/all/modules/contrib/dev/entity/tests/entity_test_i18n.info b/sites/all/modules/contrib/dev/entity/tests/entity_test_i18n.info index af304d5..4ca048a 100644 --- a/sites/all/modules/contrib/dev/entity/tests/entity_test_i18n.info +++ b/sites/all/modules/contrib/dev/entity/tests/entity_test_i18n.info @@ -5,9 +5,9 @@ dependencies[] = i18n_string package = Multilingual - Internationalization core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2014-04-08 -version = "7.x-1.5" +; Information added by Drupal.org packaging script on 2018-02-14 +version = "7.x-1.9" core = "7.x" project = "entity" -datestamp = "1396975454" +datestamp = "1518620551" diff --git a/sites/all/modules/contrib/dev/entity/theme/entity.theme.inc b/sites/all/modules/contrib/dev/entity/theme/entity.theme.inc index d365996..fc0ba7c 100644 --- a/sites/all/modules/contrib/dev/entity/theme/entity.theme.inc +++ b/sites/all/modules/contrib/dev/entity/theme/entity.theme.inc @@ -80,9 +80,12 @@ function template_preprocess_entity_property(&$variables, $hook) { ); // Populate the content with sensible defaults. - if (!isset($variables['content'])) { + if (!isset($element['#content'])) { $variables['content'] = entity_property_default_render_value_by_type($element['#entity_wrapped']->{$element['#property_name']}); } + else { + $variables['content'] = $element['#content']; + } } /** @@ -169,7 +172,7 @@ function template_preprocess_entity(&$variables) { $variables['title'] = check_plain(entity_label($entity_type, $entity)); $uri = entity_uri($entity_type, $entity); - $variables['url'] = $uri ? url($uri['path'], $uri['options']) : FALSE; + $variables['url'] = $uri && !empty($uri['path']) ? url($uri['path'], $uri['options']) : FALSE; if (isset($variables['elements']['#page'])) { // If set by the caller, respect the page property. @@ -177,7 +180,7 @@ function template_preprocess_entity(&$variables) { } else { // Else, try to automatically detect it. - $variables['page'] = $uri && $uri['path'] == $_GET['q']; + $variables['page'] = $uri && !empty($uri['path']) && $uri['path'] == $_GET['q']; } // Helpful $content variable for templates. diff --git a/sites/all/modules/contrib/dev/entity/views/entity.views.inc b/sites/all/modules/contrib/dev/entity/views/entity.views.inc index 59ebaa4..a0179c0 100644 --- a/sites/all/modules/contrib/dev/entity/views/entity.views.inc +++ b/sites/all/modules/contrib/dev/entity/views/entity.views.inc @@ -605,6 +605,28 @@ class EntityDefaultViewsController { ); break; + case 'duration': + $return += $description + array( + 'field' => array( + 'real field' => $views_field_name, + 'handler' => 'entity_views_handler_field_duration', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_sort', + ), + 'filter' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_filter_numeric', + ), + 'argument' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_argument_numeric', + ), + ); + break; + case 'uri': $return += $description + array( 'field' => array( diff --git a/sites/all/modules/contrib/dev/entity/views/handlers/entity_views_field_handler_helper.inc b/sites/all/modules/contrib/dev/entity/views/handlers/entity_views_field_handler_helper.inc index 0077f4a..6bb4fbf 100644 --- a/sites/all/modules/contrib/dev/entity/views/handlers/entity_views_field_handler_helper.inc +++ b/sites/all/modules/contrib/dev/entity/views/handlers/entity_views_field_handler_helper.inc @@ -18,7 +18,7 @@ class EntityFieldHandlerHelper { * Provide appropriate default options for a handler. */ public static function option_definition($handler) { - if (entity_property_list_extract_type($handler->definition['type'])) { + if (isset($handler->definition['type']) && entity_property_list_extract_type($handler->definition['type'])) { $options['list']['contains']['mode'] = array('default' => 'collapse'); $options['list']['contains']['separator'] = array('default' => ', '); $options['list']['contains']['type'] = array('default' => 'ul'); @@ -32,7 +32,7 @@ class EntityFieldHandlerHelper { * Provide an appropriate default option form for a handler. */ public static function options_form($handler, &$form, &$form_state) { - if (entity_property_list_extract_type($handler->definition['type'])) { + if (isset($handler->definition['type']) && entity_property_list_extract_type($handler->definition['type'])) { $form['list']['mode'] = array( '#type' => 'select', '#title' => t('List handling'), diff --git a/sites/all/modules/contrib/dev/entity/views/handlers/entity_views_handler_area_entity.inc b/sites/all/modules/contrib/dev/entity/views/handlers/entity_views_handler_area_entity.inc index f374694..0c5b714 100644 --- a/sites/all/modules/contrib/dev/entity/views/handlers/entity_views_handler_area_entity.inc +++ b/sites/all/modules/contrib/dev/entity/views/handlers/entity_views_handler_area_entity.inc @@ -40,7 +40,7 @@ class entity_views_handler_area_entity extends views_handler_area { $form['entity_id'] = array( '#type' => 'textfield', '#title' => t('Entity id'), - '#description' => t('Choose the entity you want to display in the area.'), + '#description' => t('Choose the entity you want to display in the area. To render an entity given by a contextual filter use "%1" for the first argument, "%2" for the second, etc.'), '#default_value' => $this->options['entity_id'], ); @@ -105,6 +105,9 @@ class entity_views_handler_area_entity extends views_handler_area { * Render an entity using the view mode. */ public function render_entity($entity_type, $entity_id, $view_mode) { + $tokens = $this->get_render_tokens(); + // Replace argument tokens in entity id. + $entity_id = strtr($entity_id, $tokens); if (!empty($entity_type) && !empty($entity_id) && !empty($view_mode)) { $entity = entity_load_single($entity_type, $entity_id); if (!empty($this->options['bypass_access']) || entity_access('view', $entity_type, $entity)) { @@ -117,4 +120,31 @@ class entity_views_handler_area_entity extends views_handler_area { return ''; } } + + /** + * Get the 'render' tokens to use for advanced rendering. + * + * This runs through all of the fields and arguments that + * are available and gets their values. This will then be + * used in one giant str_replace(). + */ + function get_render_tokens() { + $tokens = array(); + if (!empty($this->view->build_info['substitutions'])) { + $tokens = $this->view->build_info['substitutions']; + } + $count = 0; + foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) { + $token = '%' . ++$count; + if (!isset($tokens[$token])) { + $tokens[$token] = ''; + } + // Use strip tags as there should never be HTML in the path. + // However, we need to preserve special characters like " that + // were removed by check_plain(). + $tokens['%' . $count] = $handler->argument; + } + + return $tokens; + } } diff --git a/sites/all/modules/contrib/dev/entity/views/handlers/entity_views_handler_relationship_by_bundle.inc b/sites/all/modules/contrib/dev/entity/views/handlers/entity_views_handler_relationship_by_bundle.inc index 55b0fc2..a4d09d3 100644 --- a/sites/all/modules/contrib/dev/entity/views/handlers/entity_views_handler_relationship_by_bundle.inc +++ b/sites/all/modules/contrib/dev/entity/views/handlers/entity_views_handler_relationship_by_bundle.inc @@ -93,7 +93,7 @@ class entity_views_handler_relationship_by_bundle extends views_handler_relation $def['extra'] = array( array( // The table and the IN operator are implicit. - 'field' => $entity_info['bundle keys']['bundle'], + 'field' => $entity_info['entity keys']['bundle'], 'value' => $this->options['bundle_types'], ), ); diff --git a/sites/all/modules/contrib/dev/entity/views/plugins/entity_views_plugin_row_entity_view.inc b/sites/all/modules/contrib/dev/entity/views/plugins/entity_views_plugin_row_entity_view.inc index db72b5f..5e738a8 100644 --- a/sites/all/modules/contrib/dev/entity/views/plugins/entity_views_plugin_row_entity_view.inc +++ b/sites/all/modules/contrib/dev/entity/views/plugins/entity_views_plugin_row_entity_view.inc @@ -88,6 +88,9 @@ class entity_views_plugin_row_entity_view extends views_plugin_row { public function render($values) { if ($entity = $this->get_value($values)) { + // Add the view object as views_plugin_row_node_view::render() would. + // Otherwise the views theme suggestions won't work properly. + $entity->view = $this->view; $render = $this->rendered_content[entity_id($this->entity_type, $entity)]; return drupal_render($render); } diff --git a/sites/all/modules/contrib/fields/entityreference/LICENSE.txt b/sites/all/modules/contrib/fields/entityreference/LICENSE.txt old mode 100755 new mode 100644 diff --git a/sites/all/modules/contrib/fields/entityreference/entityreference.devel_generate.inc b/sites/all/modules/contrib/fields/entityreference/entityreference.devel_generate.inc index f6ab014..fab46dd 100644 --- a/sites/all/modules/contrib/fields/entityreference/entityreference.devel_generate.inc +++ b/sites/all/modules/contrib/fields/entityreference/entityreference.devel_generate.inc @@ -19,9 +19,13 @@ function _entityreference_devel_generate($object, $field, $instance, $bundle) { // Get all the entity that are referencable here. $referencable_entity = entityreference_get_selection_handler($field, $instance)->getReferencableEntities(); if (is_array($referencable_entity) && !empty($referencable_entity)) { - // Get a random key. - foreach ($referencable_entity as $type => $eids) { - $object_field['target_id'] = array_rand($eids); + // $referencable_entity is keyed by bundle type. + $random_bundle = array_rand($referencable_entity); + if (!empty($random_bundle)) { + $target_id = array_rand($referencable_entity[$random_bundle]); + if (!empty($referencable_entity[$random_bundle][$target_id])) { + $object_field['target_id'] = $target_id; + } } } return $object_field; diff --git a/sites/all/modules/contrib/fields/entityreference/entityreference.feeds.inc b/sites/all/modules/contrib/fields/entityreference/entityreference.feeds.inc index 3f64141..3da1479 100644 --- a/sites/all/modules/contrib/fields/entityreference/entityreference.feeds.inc +++ b/sites/all/modules/contrib/fields/entityreference/entityreference.feeds.inc @@ -125,10 +125,15 @@ function entityreference_feeds_set_target($source, $entity, $target, $value) { break; case 'label': $options = $handler->getReferencableEntities($value, '='); - $options = reset($options); - $etids = array_keys($options); - // Use the first matching entity. - $entity_id = reset($etids); + if ($options) { + $options = reset($options); + $etids = array_keys($options); + // Use the first matching entity. + $entity_id = reset($etids); + } + else { + $entity_id = NULL; + } break; } /* diff --git a/sites/all/modules/contrib/fields/entityreference/entityreference.info b/sites/all/modules/contrib/fields/entityreference/entityreference.info index d6348ef..5b2a0f7 100644 --- a/sites/all/modules/contrib/fields/entityreference/entityreference.info +++ b/sites/all/modules/contrib/fields/entityreference/entityreference.info @@ -1,18 +1,23 @@ name = Entity Reference description = Provides a field that can reference other entities. -core = 7.x package = Fields +core = 7.x + dependencies[] = entity dependencies[] = ctools +test_dependencies[] = feeds +test_dependencies[] = views + ; Migrate handler. files[] = entityreference.migrate.inc -; Our plugins interfaces and abstract implementations. +; Plugins interfaces and abstract implementations. files[] = plugins/selection/abstract.inc files[] = plugins/selection/views.inc files[] = plugins/behavior/abstract.inc +; Views integration. files[] = views/entityreference_plugin_display.inc files[] = views/entityreference_plugin_style.inc files[] = views/entityreference_plugin_row_fields.inc @@ -22,10 +27,11 @@ files[] = tests/entityreference.handlers.test files[] = tests/entityreference.taxonomy.test files[] = tests/entityreference.admin.test files[] = tests/entityreference.feeds.test +files[] = tests/entityreference.entity_translation.test -; Information added by packaging script on 2013-11-20 -version = "7.x-1.1" +; Information added by Drupal.org packaging script on 2017-08-16 +version = "7.x-1.5" core = "7.x" project = "entityreference" -datestamp = "1384973110" +datestamp = "1502895850" diff --git a/sites/all/modules/contrib/fields/entityreference/entityreference.install b/sites/all/modules/contrib/fields/entityreference/entityreference.install index 4e248ef..ce61018 100644 --- a/sites/all/modules/contrib/fields/entityreference/entityreference.install +++ b/sites/all/modules/contrib/fields/entityreference/entityreference.install @@ -41,6 +41,7 @@ function entityreference_field_schema($field) { } // Invoke the behaviors to allow them to change the schema. + module_load_include('module', 'entityreference'); foreach (entityreference_get_behavior_handlers($field) as $handler) { $handler->schema_alter($schema, $field); } @@ -161,4 +162,29 @@ function entityreference_update_7002() { 'not null' => TRUE, )); } -} \ No newline at end of file +} + +/** + * Implements hook_update_N(). + * + * Remove duplicate rows in the taxonomy_index table. + */ +function entityreference_update_7100() { + if (db_table_exists('taxonomy_index')) { + if (db_table_exists('taxonomy_index_tmp')) { + db_drop_table('taxonomy_index_tmp'); + } + + $tx_schema = drupal_get_schema('taxonomy_index'); + db_create_table('taxonomy_index_tmp', $tx_schema); + $select = db_select('taxonomy_index', 'tx'); + $select->fields('tx', array('nid', 'tid')); + $select->groupBy('tx.nid'); + $select->groupBy('tx.tid'); + $select->addExpression('MAX(sticky)', 'sticky'); + $select->addExpression('MAX(created)', 'created'); + db_insert('taxonomy_index_tmp')->from($select)->execute(); + db_drop_table('taxonomy_index'); + db_rename_table('taxonomy_index_tmp', 'taxonomy_index'); + } +} diff --git a/sites/all/modules/contrib/fields/entityreference/entityreference.migrate.inc b/sites/all/modules/contrib/fields/entityreference/entityreference.migrate.inc index 0e95bf0..35737b5 100644 --- a/sites/all/modules/contrib/fields/entityreference/entityreference.migrate.inc +++ b/sites/all/modules/contrib/fields/entityreference/entityreference.migrate.inc @@ -1,12 +1,11 @@ 'target_id', diff --git a/sites/all/modules/contrib/fields/entityreference/entityreference.module b/sites/all/modules/contrib/fields/entityreference/entityreference.module index bdcb562..c0c9f58 100644 --- a/sites/all/modules/contrib/fields/entityreference/entityreference.module +++ b/sites/all/modules/contrib/fields/entityreference/entityreference.module @@ -1,5 +1,12 @@ array( + 'variables' => array('label' => NULL, 'item' => NULL, 'settings' => NULL, 'uri' => NULL), + ), + 'entityreference_entity_id' => array( + 'variables' => array('item' => NULL, 'settings' => NULL), + ), + ); +} + /** * Implements hook_menu(). */ @@ -163,7 +184,7 @@ function entityreference_get_behavior_handlers($field, $instance = NULL) { /** * Get the behavior handler for a given entityreference field and instance. * - * @param $handler + * @param $behavior * The behavior handler name. */ function _entityreference_get_behavior_handler($behavior) { @@ -220,13 +241,15 @@ function entityreference_field_validate($entity_type, $entity, $field, $instance if ($ids) { $valid_ids = entityreference_get_selection_handler($field, $instance, $entity_type, $entity)->validateReferencableEntities(array_keys($ids)); - $invalid_entities = array_diff_key($ids, array_flip($valid_ids)); - if ($invalid_entities) { - foreach ($invalid_entities as $id => $delta) { - $errors[$field['field_name']][$langcode][$delta][] = array( - 'error' => 'entityreference_invalid_entity', - 'message' => t('The referenced entity (@type: @id) is invalid.', array('@type' => $field['settings']['target_type'], '@id' => $id)), - ); + if (!empty($valid_ids)) { + $invalid_entities = array_diff_key($ids, array_flip($valid_ids)); + if ($invalid_entities) { + foreach ($invalid_entities as $id => $delta) { + $errors[$field['field_name']][$langcode][$delta][] = array( + 'error' => 'entityreference_invalid_entity', + 'message' => t('The referenced entity (@type: @id) is invalid.', array('@type' => $field['settings']['target_type'], '@id' => $id)), + ); + } } } } @@ -398,6 +421,9 @@ function entityreference_field_settings_form($field, $instance, $has_data) { return $form; } +/** + * Callback for custom element processing. + */ function _entityreference_field_settings_process($form, $form_state) { $field = isset($form_state['entityreference']['field']) ? $form_state['entityreference']['field'] : $form['#field']; $instance = isset($form_state['entityreference']['instance']) ? $form_state['entityreference']['instance'] : $form['#instance']; @@ -479,11 +505,17 @@ function _entityreference_field_settings_process($form, $form_state) { return $form; } +/** + * Custom callback for ajax processing. + */ function _entityreference_field_settings_ajax_process($form, $form_state) { _entityreference_field_settings_ajax_process_element($form, $form); return $form; } +/** + * Helper function for custom ajax processing. + */ function _entityreference_field_settings_ajax_process_element(&$element, $main_form) { if (isset($element['#ajax']) && $element['#ajax'] === TRUE) { $element['#ajax'] = array( @@ -498,6 +530,9 @@ function _entityreference_field_settings_ajax_process_element(&$element, $main_f } } +/** + * Custom callback for element processing. + */ function _entityreference_form_process_merge_parent($element) { $parents = $element['#parents']; array_pop($parents); @@ -505,11 +540,17 @@ function _entityreference_form_process_merge_parent($element) { return $element; } +/** + * Helper function to remove blank elements. + */ function _entityreference_element_validate_filter(&$element, &$form_state) { $element['#value'] = array_filter($element['#value']); form_set_value($element, $element['#value'], $form_state); } +/** + * Implements hook_validate(). + */ function _entityreference_field_settings_validate($form, &$form_state) { // Store the new values in the form state. $field = $form['#field']; @@ -545,6 +586,9 @@ function entityreference_field_instance_settings_form($field, $instance) { return $form; } +/** + * Implements hook_field_settings_form(). + */ function _entityreference_field_instance_settings_form($form, $form_state) { $field = isset($form_state['entityreference']['field']) ? $form_state['entityreference']['field'] : $form['#field']; $instance = isset($form_state['entityreference']['instance']) ? $form_state['entityreference']['instance'] : $form['#instance']; @@ -562,6 +606,9 @@ function _entityreference_field_instance_settings_form($form, $form_state) { return $form; } +/** + * Implements hook_validate(). + */ function _entityreference_field_instance_settings_validate($form, &$form_state) { // Store the new values in the form state. $instance = $form['#instance']; @@ -793,7 +840,7 @@ function entityreference_query_entityreference_alter(QueryAlterableInterface $qu function entityreference_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { // Ensure that the entity target type exists before displaying the widget. $entity_info = entity_get_info($field['settings']['target_type']); - if (empty($entity_info)){ + if (empty($entity_info)) { return; } $entity_type = $instance['entity_type']; @@ -818,7 +865,9 @@ function entityreference_field_widget_form(&$form, &$form_state, $field, $instan // Build an array of entities ID. foreach ($items as $item) { - $entity_ids[] = $item['target_id']; + if (isset($item['target_id'])) { + $entity_ids[] = $item['target_id']; + } } // Load those entities and loop through them to extract their labels. @@ -879,6 +928,9 @@ function entityreference_field_widget_form(&$form, &$form_state, $field, $instan } } +/** + * Implements hook_validate(). + */ function _entityreference_autocomplete_validate($element, &$form_state, $form) { // If a value was entered into the autocomplete... $value = ''; @@ -903,6 +955,9 @@ function _entityreference_autocomplete_validate($element, &$form_state, $form) { form_set_value($element, $value, $form_state); } +/** + * Implements hook_validate(). + */ function _entityreference_autocomplete_tags_validate($element, &$form_state, $form) { $value = array(); // If a value was entered into the autocomplete... @@ -949,7 +1004,8 @@ function entityreference_field_widget_error($element, $error) { * The entity type. * @param $bundle_name * The bundle name. - * @return + * + * @return bool * True if user can access this menu item. */ function entityreference_autocomplete_access_callback($type, $field_name, $entity_type, $bundle_name) { @@ -981,10 +1037,11 @@ function entityreference_autocomplete_access_callback($type, $field_name, $entit */ function entityreference_autocomplete_callback($type, $field_name, $entity_type, $bundle_name, $entity_id = '', $string = '') { // If the request has a '/' in the search text, then the menu system will have - // split it into multiple arguments and $string will only be a partial. We want - // to make sure we recover the intended $string. + // split it into multiple arguments and $string will only be a partial. + // We want to make sure we recover the intended $string. $args = func_get_args(); - // Shift off the $type, $field_name, $entity_type, $bundle_name, and $entity_id args. + // Shift off the $type, $field_name, $entity_type, + // $bundle_name, and $entity_id args. array_shift($args); array_shift($args); array_shift($args); @@ -1020,6 +1077,7 @@ function entityreference_autocomplete_callback($type, $field_name, $entity_type, */ function entityreference_autocomplete_callback_get_matches($type, $field, $instance, $entity_type, $entity_id = '', $string = '') { $matches = array(); + $prefix = ''; $entity = NULL; if ($entity_id !== 'NULL') { @@ -1034,7 +1092,8 @@ function entityreference_autocomplete_callback_get_matches($type, $field, $insta $handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity); if ($type == 'tags') { - // The user enters a comma-separated list of tags. We only autocomplete the last tag. + // The user enters a comma-separated list of tags. + // We only autocomplete the last tag. $tags_typed = drupal_explode_tags($string); $tag_last = drupal_strtolower(array_pop($tags_typed)); if (!empty($tag_last)) { @@ -1043,19 +1102,22 @@ function entityreference_autocomplete_callback_get_matches($type, $field, $insta } else { // The user enters a single tag. - $prefix = ''; $tag_last = $string; } if (isset($tag_last)) { // Get an array of matching entities. $entity_labels = $handler->getReferencableEntities($tag_last, $instance['widget']['settings']['match_operator'], 10); - + $denied_label = t(ENTITYREFERENCE_DENIED); // Loop through the products and convert them into autocomplete output. foreach ($entity_labels as $values) { foreach ($values as $entity_id => $label) { + // Never autocomplete entities that aren't accessible. + if ($label == $denied_label) { + continue; + } $key = "$label ($entity_id)"; - // Strip things like starting/trailing white spaces, line breaks and tags. + // Strip starting/trailing white spaces, line breaks and tags. $key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(decode_entities(strip_tags($key))))); // Names containing commas or quotes must be wrapped in quotes. if (strpos($key, ',') !== FALSE || strpos($key, '"') !== FALSE) { @@ -1069,6 +1131,32 @@ function entityreference_autocomplete_callback_get_matches($type, $field, $insta drupal_json_output($matches); } + /** + * Introspects field and instance settings, and determines the correct settings + * for the functioning of the formatter. + * + * Settings: + * - entity_type - The entity_type being loaded. + * - column - The name of the ref. field column that stores the entity id. + */ +function entityreference_field_type_settings($field) { + $settings = array( + 'entity_type' => NULL, + 'column' => NULL, + ); + + if ($field['type'] == 'entityreference') { + $settings['entity_type'] = $field['settings']['target_type']; + $settings['column'] = 'target_id'; + } + elseif ($field['type'] == 'taxonomy_term_reference') { + $settings['entity_type'] = 'taxonomy_term'; + $settings['column'] = 'tid'; + } + + return $settings; +} + /** * Implements hook_field_formatter_info(). */ @@ -1080,6 +1168,7 @@ function entityreference_field_formatter_info() { 'field types' => array('entityreference'), 'settings' => array( 'link' => FALSE, + 'bypass_access' => FALSE, ), ), 'entityreference_entity_id' => array( @@ -1090,10 +1179,11 @@ function entityreference_field_formatter_info() { 'entityreference_entity_view' => array( 'label' => t('Rendered entity'), 'description' => t('Display the referenced entities rendered by entity_view().'), - 'field types' => array('entityreference'), + 'field types' => array('entityreference', 'taxonomy_term_reference'), 'settings' => array( 'view_mode' => 'default', 'links' => TRUE, + 'use_content_language' => TRUE, ), ), ); @@ -1105,8 +1195,17 @@ function entityreference_field_formatter_info() { function entityreference_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) { $display = $instance['display'][$view_mode]; $settings = $display['settings']; + $field_type_settings = entityreference_field_type_settings($field); + $element = array(); if ($display['type'] == 'entityreference_label') { + $element['bypass_access'] = array( + '#title' => t('Show entity labels regardless of user access'), + '#description' => t("All entities in the field will be shown, without checking them for access. If the 'Link' setting is also enabled, an entity which the user does not have access to view will show without a link."), + '#type' => 'checkbox', + '#default_value' => $settings['bypass_access'], + ); + $element['link'] = array( '#title' => t('Link label to the referenced entity'), '#type' => 'checkbox', @@ -1115,7 +1214,7 @@ function entityreference_field_formatter_settings_form($field, $instance, $view_ } if ($display['type'] == 'entityreference_entity_view') { - $entity_info = entity_get_info($field['settings']['target_type']); + $entity_info = entity_get_info($field_type_settings['entity_type']); $options = array('default' => t('Default')); if (!empty($entity_info['view modes'])) { foreach ($entity_info['view modes'] as $view_mode => $view_mode_settings) { @@ -1136,6 +1235,12 @@ function entityreference_field_formatter_settings_form($field, $instance, $view_ '#title' => t('Show links'), '#default_value' => $settings['links'], ); + + $element['use_content_language'] = array( + '#type' => 'checkbox', + '#title' => t('Use current content language'), + '#default_value' => $settings['use_content_language'], + ); } return $element; @@ -1147,21 +1252,24 @@ function entityreference_field_formatter_settings_form($field, $instance, $view_ function entityreference_field_formatter_settings_summary($field, $instance, $view_mode) { $display = $instance['display'][$view_mode]; $settings = $display['settings']; + $field_type_settings = entityreference_field_type_settings($field); $summary = array(); if ($display['type'] == 'entityreference_label') { $summary[] = $settings['link'] ? t('Link to the referenced entity') : t('No link'); + $summary[] = $settings['bypass_access'] ? t('Show labels regardless of access') : t('Respect entity access for label visibility'); } if ($display['type'] == 'entityreference_entity_view') { - $entity_info = entity_get_info($field['settings']['target_type']); + $entity_info = entity_get_info($field_type_settings['entity_type']); $view_mode_label = $settings['view_mode'] == 'default' ? t('Default') : $settings['view_mode']; if (isset($entity_info['view modes'][$settings['view_mode']]['label'])) { $view_mode_label = $entity_info['view modes'][$settings['view_mode']]['label']; } $summary[] = t('Rendered as @mode', array('@mode' => $view_mode_label)); $summary[] = !empty($settings['links']) ? t('Display links') : t('Do not display links'); + $summary[] = !empty($settings['use_content_language']) ? t('Use current content language') : t('Use field language'); } return implode('
    ', $summary); @@ -1171,19 +1279,22 @@ function entityreference_field_formatter_settings_summary($field, $instance, $vi * Implements hook_field_formatter_prepare_view(). */ function entityreference_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) { + $field_type_settings = entityreference_field_type_settings($field); + $target_type = $field_type_settings['entity_type']; + $column = $field_type_settings['column']; $target_ids = array(); // Collect every possible entity attached to any of the entities. foreach ($entities as $id => $entity) { foreach ($items[$id] as $delta => $item) { - if (isset($item['target_id'])) { - $target_ids[] = $item['target_id']; + if (isset($item[$column])) { + $target_ids[] = $item[$column]; } } } if ($target_ids) { - $target_entities = entity_load($field['settings']['target_type'], $target_ids); + $target_entities = entity_load($target_type, $target_ids); } else { $target_entities = array(); @@ -1195,12 +1306,12 @@ function entityreference_field_formatter_prepare_view($entity_type, $entities, $ foreach ($items[$id] as $delta => $item) { // Check whether the referenced entity could be loaded. - if (isset($target_entities[$item['target_id']])) { + if (isset($target_entities[$item[$column]]) && isset($target_entities[$item[$column]])) { // Replace the instance value with the term data. - $items[$id][$delta]['entity'] = $target_entities[$item['target_id']]; + $items[$id][$delta]['entity'] = $target_entities[$item[$column]]; // Check whether the user has access to the referenced entity. - $has_view_access = (entity_access('view', $field['settings']['target_type'], $target_entities[$item['target_id']]) !== FALSE); - $has_update_access = (entity_access('update', $field['settings']['target_type'], $target_entities[$item['target_id']]) !== FALSE); + $has_view_access = (entity_access('view', $target_type, $target_entities[$item[$column]]) !== FALSE); + $has_update_access = (entity_access('update', $target_type, $target_entities[$item[$column]]) !== FALSE); $items[$id][$delta]['access'] = ($has_view_access || $has_update_access); } // Otherwise, unset the instance value, since the entity does not exist. @@ -1223,52 +1334,91 @@ function entityreference_field_formatter_prepare_view($entity_type, $entities, $ function entityreference_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { $result = array(); $settings = $display['settings']; - - // Rebuild the items list to contain only those with access. - foreach ($items as $key => $item) { - if (empty($item['access'])) { - unset($items[$key]); - } - } + $field_type_settings = entityreference_field_type_settings($field); + $target_type = $field_type_settings['entity_type']; + $column = $field_type_settings['column']; switch ($display['type']) { case 'entityreference_label': $handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity); foreach ($items as $delta => $item) { - $label = $handler->getLabel($item['entity']); - // If the link is to be displayed and the entity has a uri, display a link. - // Note the assignment ($url = ) here is intended to be an assignment. - if ($display['settings']['link'] && ($uri = entity_uri($field['settings']['target_type'], $item['entity']))) { - $result[$delta] = array('#markup' => l($label, $uri['path'], $uri['options'])); + // Skip an item that is not accessible, unless we're allowing output of + // entity labels without considering access. + if (empty($item['access']) && !$display['settings']['bypass_access']) { + continue; } - else { - $result[$delta] = array('#markup' => check_plain($label)); + + // Calling EntityReferenceHandler::getLabel() would make a repeated, + // wasteful call to entity_access(). + $label = entity_label($field['settings']['target_type'], $item['entity']); + + // Check if the settings and access allow a link to be displayed. + $display_link = $display['settings']['link'] && $item['access']; + + $uri = NULL; + + // If the link is allowed and the entity has a uri, display a link. + if ($display_link) { + $uri = entity_uri($target_type, $item['entity']); } + + $result[$delta] = array( + '#theme' => 'entityreference_label', + '#label' => $label, + '#item' => $item, + '#uri' => $uri, + '#settings' => array( + 'display' => $display['settings'], + 'field' => $field['settings'], + ), + ); } break; case 'entityreference_entity_id': foreach ($items as $delta => $item) { - $result[$delta] = array('#markup' => check_plain($item['target_id'])); + // Skip an item that is not accessible. + if (empty($item['access'])) { + continue; + } + + $result[$delta] = array( + '#theme' => 'entityreference_entity_id', + '#item' => $item, + '#settings' => array( + 'display' => $display['settings'], + 'field' => $field['settings'], + ), + ); } break; case 'entityreference_entity_view': + $target_langcode = $langcode; + if (!empty($settings['use_content_language']) && !empty($GLOBALS['language_content']->language)) { + $target_langcode = $GLOBALS['language_content']->language; + } + foreach ($items as $delta => $item) { + // Skip an item that is not accessible. + if (empty($item['access'])) { + continue; + } + // Protect ourselves from recursive rendering. static $depth = 0; $depth++; if ($depth > 20) { - throw new EntityReferenceRecursiveRenderingException(t('Recursive rendering detected when rendering entity @entity_type(@entity_id). Aborting rendering.', array('@entity_type' => $entity_type, '@entity_id' => $item['target_id']))); + throw new EntityReferenceRecursiveRenderingException(t('Recursive rendering detected when rendering entity @entity_type(@entity_id). Aborting rendering.', array('@entity_type' => $target_type, '@entity_id' => $item[$column]))); } - $entity = clone $item['entity']; - unset($entity->content); - $result[$delta] = entity_view($field['settings']['target_type'], array($item['target_id'] => $entity), $settings['view_mode'], $langcode, FALSE); + $target_entity = clone $item['entity']; + unset($target_entity->content); + $result[$delta] = entity_view($target_type, array($item[$column] => $target_entity), $settings['view_mode'], $target_langcode, FALSE); - if (empty($settings['links']) && isset($result[$delta][$field['settings']['target_type']][$item['target_id']]['links'])) { - $result[$delta][$field['settings']['target_type']][$item['target_id']]['links']['#access'] = FALSE; + if (empty($settings['links']) && isset($result[$delta][$target_type][$column]['links'])) { + $result[$delta][$target_type][$item[$column]]['links']['#access'] = FALSE; } $depth = 0; } @@ -1292,3 +1442,44 @@ function entityreference_views_api() { 'path' => drupal_get_path('module', 'entityreference') . '/views', ); } + +/** + * Theme label. + * + * @ingroup themeable. + */ +function theme_entityreference_label($vars) { + $label = $vars['label']; + $settings = $vars['settings']; + $item = $vars['item']; + $uri = $vars['uri']; + + $output = ''; + + // If the link is to be displayed and the entity has a uri, display a link. + // Note the assignment ($url = ) here is intended to be an assignment. + if ($settings['display']['link'] && isset($uri['path'])) { + $output .= l($label, $uri['path'], $uri['options']); + } + else { + $output .= check_plain($label); + } + + return $output; +} + +/** + * Theme entity_id + * + * @ingroup themeable. + */ +function theme_entityreference_entity_id($vars) { + $settings = $vars['settings']; + $item = $vars['item']; + + $output = ''; + + $output = check_plain($item['target_id']); + + return $output; +} diff --git a/sites/all/modules/contrib/fields/entityreference/examples/entityreference_behavior_example/entityreference_behavior_example.info b/sites/all/modules/contrib/fields/entityreference/examples/entityreference_behavior_example/entityreference_behavior_example.info index 88ad7b5..70bcc4f 100644 --- a/sites/all/modules/contrib/fields/entityreference/examples/entityreference_behavior_example/entityreference_behavior_example.info +++ b/sites/all/modules/contrib/fields/entityreference/examples/entityreference_behavior_example/entityreference_behavior_example.info @@ -4,9 +4,9 @@ core = 7.x package = Fields dependencies[] = entityreference -; Information added by packaging script on 2013-11-20 -version = "7.x-1.1" +; Information added by Drupal.org packaging script on 2017-08-16 +version = "7.x-1.5" core = "7.x" project = "entityreference" -datestamp = "1384973110" +datestamp = "1502895850" diff --git a/sites/all/modules/contrib/fields/entityreference/plugins/behavior/EntityReferenceBehavior_TaxonomyIndex.class.php b/sites/all/modules/contrib/fields/entityreference/plugins/behavior/EntityReferenceBehavior_TaxonomyIndex.class.php index 43ac693..075b54d 100644 --- a/sites/all/modules/contrib/fields/entityreference/plugins/behavior/EntityReferenceBehavior_TaxonomyIndex.class.php +++ b/sites/all/modules/contrib/fields/entityreference/plugins/behavior/EntityReferenceBehavior_TaxonomyIndex.class.php @@ -144,18 +144,20 @@ class EntityReferenceBehavior_TaxonomyIndex extends EntityReference_BehaviorHand // already inserted in taxonomy_build_node_index(). $tid_all = array_diff($tid_all, $original_tid_all); - // Insert index entries for all the node's terms. + // Insert index entries for all the node's terms, preventing duplicates. if (!empty($tid_all)) { - $query = db_insert('taxonomy_index')->fields(array('nid', 'tid', 'sticky', 'created')); foreach ($tid_all as $tid) { - $query->values(array( + $row = array( 'nid' => $node->nid, 'tid' => $tid, 'sticky' => $sticky, 'created' => $node->created, - )); + ); + $query = db_merge('taxonomy_index') + ->key($row) + ->fields($row); + $query->execute(); } - $query->execute(); } } } diff --git a/sites/all/modules/contrib/fields/entityreference/plugins/selection/EntityReference_SelectionHandler_Generic.class.php b/sites/all/modules/contrib/fields/entityreference/plugins/selection/EntityReference_SelectionHandler_Generic.class.php index 444a74c..6ec28a4 100644 --- a/sites/all/modules/contrib/fields/entityreference/plugins/selection/EntityReference_SelectionHandler_Generic.class.php +++ b/sites/all/modules/contrib/fields/entityreference/plugins/selection/EntityReference_SelectionHandler_Generic.class.php @@ -208,7 +208,11 @@ class EntityReference_SelectionHandler_Generic implements EntityReference_Select * Implements EntityReferenceHandler::validateAutocompleteInput(). */ public function validateAutocompleteInput($input, &$element, &$form_state, $form) { - $entities = $this->getReferencableEntities($input, '=', 6); + $bundled_entities = $this->getReferencableEntities($input, '=', 6); + $entities = array(); + foreach($bundled_entities as $entities_list) { + $entities += $entities_list; + } if (empty($entities)) { // Error if there are no entities available for a required field. form_error($element, t('There are no entities matching "%value"', array('%value' => $input))); @@ -305,7 +309,7 @@ class EntityReference_SelectionHandler_Generic implements EntityReference_Select */ public function getLabel($entity) { $target_type = $this->field['settings']['target_type']; - return entity_access('view', $target_type, $entity) ? entity_label($target_type, $entity) : t('- Restricted access -'); + return entity_access('view', $target_type, $entity) ? entity_label($target_type, $entity) : t(ENTITYREFERENCE_DENIED); } /** @@ -339,9 +343,11 @@ class EntityReference_SelectionHandler_Generic implements EntityReference_Select // Join the known base-table. $target_type = $this->field['settings']['target_type']; $entity_info = entity_get_info($target_type); + $target_type_base_table = $entity_info['base table']; $id = $entity_info['entity keys']['id']; + // Return the alias of the table. - return $query->innerJoin($target_type, NULL, "%alias.$id = $alias.entity_id"); + return $query->innerJoin($target_type_base_table, NULL, "%alias.$id = $alias.entity_id"); } } @@ -543,7 +549,7 @@ class EntityReference_SelectionHandler_Generic_taxonomy_term extends EntityRefer if ($vocabulary = taxonomy_vocabulary_machine_name_load($bundle)) { if ($terms = taxonomy_get_tree($vocabulary->vid, 0, NULL, TRUE)) { foreach ($terms as $term) { - $options[$vocabulary->machine_name][$term->tid] = str_repeat('-', $term->depth) . check_plain($term->name); + $options[$vocabulary->machine_name][$term->tid] = str_repeat('-', $term->depth) . check_plain(entity_label('taxonomy_term', $term)); } } } diff --git a/sites/all/modules/contrib/fields/entityreference/plugins/selection/EntityReference_SelectionHandler_Views.class.php b/sites/all/modules/contrib/fields/entityreference/plugins/selection/EntityReference_SelectionHandler_Views.class.php index 1b036a7..cbed33b 100644 --- a/sites/all/modules/contrib/fields/entityreference/plugins/selection/EntityReference_SelectionHandler_Views.class.php +++ b/sites/all/modules/contrib/fields/entityreference/plugins/selection/EntityReference_SelectionHandler_Views.class.php @@ -9,12 +9,13 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio * Implements EntityReferenceHandler::getInstance(). */ public static function getInstance($field, $instance = NULL, $entity_type = NULL, $entity = NULL) { - return new EntityReference_SelectionHandler_Views($field, $instance); + return new EntityReference_SelectionHandler_Views($field, $instance, $entity); } - protected function __construct($field, $instance) { + protected function __construct($field, $instance, $entity) { $this->field = $field; $this->instance = $instance; + $this->entity = $entity; } /** @@ -52,13 +53,32 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio ); $default = !empty($view_settings['args']) ? implode(', ', $view_settings['args']) : ''; + $description = t('Provide a comma separated list of arguments to pass to the view.') . '
    ' . t('This field supports tokens.'); + + if (!module_exists('token')) { + $description .= '
    ' . t('Install the token module to get more tokens and display available once.', array('@url' => 'http://drupal.org/project/token')); + } + $form['view']['args'] = array( '#type' => 'textfield', '#title' => t('View arguments'), '#default_value' => $default, '#required' => FALSE, - '#description' => t('Provide a comma separated list of arguments to pass to the view.'), + '#description' => $description, + '#maxlength' => '512', ); + if (module_exists('token')) { + // Get the token type for the entity type our field is in (a type 'taxonomy_term' has a 'term' type token). + $info = entity_get_info($instance['entity_type']); + + $form['view']['tokens'] = array( + '#theme' => 'token_tree', + '#token_types' => array($info['token type']), + '#global_types' => TRUE, + '#click_insert' => TRUE, + '#dialog' => TRUE, + ); + } } else { $form['view']['no_view_help'] = array( @@ -84,6 +104,7 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio return FALSE; } $this->view->set_display($display_name); + $this->view->pre_execute(); // Make sure the query is not cached. $this->view->is_cacheable = FALSE; @@ -104,7 +125,7 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio */ public function getReferencableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) { $display_name = $this->field['settings']['handler_settings']['view']['display_name']; - $args = $this->field['settings']['handler_settings']['view']['args']; + $args = $this->handleArgs($this->field['settings']['handler_settings']['view']['args']); $result = array(); if ($this->initializeView($match, $match_operator, $limit)) { // Get the results. @@ -133,12 +154,14 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio function validateReferencableEntities(array $ids) { $display_name = $this->field['settings']['handler_settings']['view']['display_name']; - $args = $this->field['settings']['handler_settings']['view']['args']; + $args = $this->handleArgs($this->field['settings']['handler_settings']['view']['args']); $result = array(); if ($this->initializeView(NULL, 'CONTAINS', 0, $ids)) { // Get the results. $entities = $this->view->execute_display($display_name, $args); - $result = array_keys($entities); + if (!empty($entities)) { + $result = array_keys($entities); + } } return $result; } @@ -164,6 +187,49 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio } + /** + * Handles arguments for views. + * + * Replaces tokens using token_replace(). + * + * @param array $args + * Usually $this->field['settings']['handler_settings']['view']['args']. + * + * @return array + * The arguments to be send to the View. + */ + protected function handleArgs($args) { + if (!module_exists('token')) { + return $args; + } + + // Parameters for token_replace(). + $data = array(); + $options = array('clear' => TRUE); + + if ($entity = $this->entity) { + // D7 HACK: For new entities, entity and revision id are not set. This leads to + // * token replacement emitting PHP warnings + // * views choking on empty arguments + // We workaround this by filling in '0' for these IDs + // and use a clone to leave no traces of our unholy doings. + $info = entity_get_info($this->instance['entity_type']); + if (!isset($entity->{$info['entity keys']['id']})) { + $entity = clone $entity; + $entity->{$info['entity keys']['id']} = '0'; + if (!empty($info['entity keys']['revision'])) { + $entity->{$info['entity keys']['revision']} = '0'; + } + } + + $data[$info['token type']] = $entity; + } + // Replace tokens for each argument. + foreach ($args as $key => $arg) { + $args[$key] = token_replace($arg, $data, $options); + } + return $args; + } } function entityreference_view_settings_validate($element, &$form_state, $form) { diff --git a/sites/all/modules/contrib/fields/entityreference/plugins/selection/abstract.inc b/sites/all/modules/contrib/fields/entityreference/plugins/selection/abstract.inc index 1d2ea0d..a4805b1 100644 --- a/sites/all/modules/contrib/fields/entityreference/plugins/selection/abstract.inc +++ b/sites/all/modules/contrib/fields/entityreference/plugins/selection/abstract.inc @@ -21,8 +21,9 @@ interface EntityReference_SelectionHandler { * Return a list of referencable entities. * * @return - * An array of referencable entities, which keys are entity ids and - * values (safe HTML) labels to be displayed to the user. + * A nested array of entities, the first level is keyed by the + * entity bundle, which contains an array of entity labels (safe HTML), + * keyed by the entity ID. */ public function getReferencableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0); diff --git a/sites/all/modules/contrib/fields/entityreference/tests/entityreference.admin.test b/sites/all/modules/contrib/fields/entityreference/tests/entityreference.admin.test index 9a12119..ec78e7b 100644 --- a/sites/all/modules/contrib/fields/entityreference/tests/entityreference.admin.test +++ b/sites/all/modules/contrib/fields/entityreference/tests/entityreference.admin.test @@ -21,7 +21,7 @@ class EntityReferenceAdminTestCase extends DrupalWebTestCase { parent::setUp(array('field_ui', 'entity', 'ctools', 'entityreference')); // Create test user. - $this->admin_user = $this->drupalCreateUser(array('access content', 'administer content types')); + $this->admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer fields')); $this->drupalLogin($this->admin_user); // Create content type, with underscores. diff --git a/sites/all/modules/contrib/fields/entityreference/tests/entityreference.feeds.test b/sites/all/modules/contrib/fields/entityreference/tests/entityreference.feeds.test index 4dc1901..32248cc 100644 --- a/sites/all/modules/contrib/fields/entityreference/tests/entityreference.feeds.test +++ b/sites/all/modules/contrib/fields/entityreference/tests/entityreference.feeds.test @@ -17,6 +17,7 @@ class FeedsMapperFieldTestCase extends DrupalWebTestCase{ 'name' => 'Feeds integration (field mapper)', 'description' => 'Test Feeds Mapper support for fields.', 'group' => 'Entity Reference', + 'dependencies' => array('feeds'), ); } @@ -28,10 +29,6 @@ class FeedsMapperFieldTestCase extends DrupalWebTestCase{ module_enable(array('entityreference_feeds_test'), TRUE); $this->resetAll(); - if (!module_exists('feeds')) { - return; - } - $permissions[] = 'access content'; $permissions[] = 'administer site configuration'; $permissions[] = 'administer content types'; @@ -157,10 +154,6 @@ class FeedsMapperFieldTestCase extends DrupalWebTestCase{ * Basic test loading a double entry CSV file. */ public function test() { - if (!module_exists('feeds')) { - return; - } - $this->drupalLogin($this->admin_user); $this->drupalGet('admin/structure/types/manage/article/fields'); $this->assertText('Ref - entity ID', t('Found Entity reference field %field.', array('%field' => 'field_er_id'))); diff --git a/sites/all/modules/contrib/fields/entityreference/tests/entityreference.handlers.test b/sites/all/modules/contrib/fields/entityreference/tests/entityreference.handlers.test index b88e106..1367b44 100644 --- a/sites/all/modules/contrib/fields/entityreference/tests/entityreference.handlers.test +++ b/sites/all/modules/contrib/fields/entityreference/tests/entityreference.handlers.test @@ -194,6 +194,21 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase { ), ); $this->assertReferencable($field, $referencable_tests, 'Node handler (admin)'); + + // Verify autocomplete input validation. + $handler = entityreference_get_selection_handler($field); + $element = array( + '#parents' => array('element_name'), + ); + $form_state = array(); + $form = array(); + $value = $handler->validateAutocompleteInput($nodes['published1']->title, $element, $form_state, $form); + $this->assertEqual($value, $nodes['published1']->nid); + + $invalid_input = $this->randomName(); + $value = $handler->validateAutocompleteInput($invalid_input, $element, $form_state, $form); + $this->assertNull($value); + $this->assertEqual(form_get_error($element), t('There are no entities matching "%value"', array('%value' => $invalid_input))); } /** @@ -256,7 +271,7 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase { ), 'result' => array( 'user' => array( - $users['admin']->uid => '- Restricted access -', + $users['admin']->uid => ENTITYREFERENCE_DENIED, $users['non_admin']->uid => $user_labels['non_admin'], ), ), diff --git a/sites/all/modules/contrib/fields/entityreference/tests/entityreference.taxonomy.test b/sites/all/modules/contrib/fields/entityreference/tests/entityreference.taxonomy.test index 6e4afb7..94b3f56 100644 --- a/sites/all/modules/contrib/fields/entityreference/tests/entityreference.taxonomy.test +++ b/sites/all/modules/contrib/fields/entityreference/tests/entityreference.taxonomy.test @@ -112,4 +112,52 @@ class EntityReferenceTaxonomyTestCase extends DrupalWebTestCase { $this->assertFalse(taxonomy_select_nodes(1)); } + /** + * Add a second ER field from node/article to taxonomy. + * + * This should not cause {taxonomy_index} to receive duplicate entries. + */ + protected function setupForIndexDuplicates() { + // Create an entity reference field. + $field = array( + 'entity_types' => array('node'), + 'settings' => array( + 'handler' => 'base', + 'target_type' => 'taxonomy_term', + 'handler_settings' => array( + 'target_bundles' => array(), + ), + ), + 'field_name' => 'field_entityreference_term2', + 'type' => 'entityreference', + ); + $field = field_create_field($field); + $instance = array( + 'field_name' => 'field_entityreference_term2', + 'bundle' => 'article', + 'entity_type' => 'node', + ); + + // Enable the taxonomy-index behavior. + $instance['settings']['behaviors']['taxonomy-index']['status'] = TRUE; + field_create_instance($instance); + } + + /** + * Make sure the index only contains one entry for a given node->term + * reference, even when multiple ER fields link from the node bundle to terms. + */ + public function testIndexDuplicates() { + // Extra setup for this test: add another ER field on this content type. + $this->setupForIndexDuplicates(); + + // Assert node insert with reference to term in first field. + $tid = 1; + $settings = array(); + $settings['type'] = 'article'; + $settings['field_entityreference_term'][LANGUAGE_NONE][0]['target_id'] = $tid; + $node = $this->drupalCreateNode($settings); + + $this->assertEqual(taxonomy_select_nodes($tid), array($node->nid)); + } } diff --git a/sites/all/modules/contrib/fields/entityreference/tests/modules/entityreference_feeds_test/entityreference_feeds_test.info b/sites/all/modules/contrib/fields/entityreference/tests/modules/entityreference_feeds_test/entityreference_feeds_test.info index 311ae3f..5bbf9b6 100644 --- a/sites/all/modules/contrib/fields/entityreference/tests/modules/entityreference_feeds_test/entityreference_feeds_test.info +++ b/sites/all/modules/contrib/fields/entityreference/tests/modules/entityreference_feeds_test/entityreference_feeds_test.info @@ -8,9 +8,9 @@ dependencies[] = feeds dependencies[] = feeds_ui dependencies[] = entityreference -; Information added by packaging script on 2013-11-20 -version = "7.x-1.1" +; Information added by Drupal.org packaging script on 2017-08-16 +version = "7.x-1.5" core = "7.x" project = "entityreference" -datestamp = "1384973110" +datestamp = "1502895850" diff --git a/sites/all/modules/contrib/fields/entityreference/views/entityreference_plugin_display.inc b/sites/all/modules/contrib/fields/entityreference/views/entityreference_plugin_display.inc index f13e88a..cca3650 100644 --- a/sites/all/modules/contrib/fields/entityreference/views/entityreference_plugin_display.inc +++ b/sites/all/modules/contrib/fields/entityreference/views/entityreference_plugin_display.inc @@ -81,8 +81,9 @@ class entityreference_plugin_display extends views_plugin_display { $field = $this->view->query->fields[$this->view->field[$field_alias]->field_alias]; } else { - $this->view->query->add_field($this->view->field[$field_alias]->options['table'], $this->view->field[$field_alias]->real_field, $this->view->field[$field_alias]->options['field'], array()); - $field = $this->view->query->fields[$this->view->field[$field_alias]->options['field']]; + $field_table = $this->view->query->ensure_table($this->view->field[$field_alias]->table, $this->view->field[$field_alias]->relationship); + $this->view->query->add_field($field_table, $this->view->field[$field_alias]->real_field, $this->view->field[$field_alias]->field, array()); + $field = $this->view->query->fields[$this->view->field[$field_alias]->field]; } // Add an OR condition for the field $conditions->condition($field['table'] . '.' . $field['field'], $value, 'LIKE'); diff --git a/sites/all/modules/contrib/fields/entityreference/views/entityreference_plugin_style.inc b/sites/all/modules/contrib/fields/entityreference/views/entityreference_plugin_style.inc index fadaa9e..b72a235 100644 --- a/sites/all/modules/contrib/fields/entityreference/views/entityreference_plugin_style.inc +++ b/sites/all/modules/contrib/fields/entityreference/views/entityreference_plugin_style.inc @@ -26,7 +26,7 @@ class entityreference_plugin_style extends views_plugin_style { '#title' => t('Search fields'), '#options' => $options, '#required' => TRUE, - '#default_value' => $this->options['search_fields'], + '#default_value' => isset($this->options['search_fields']) ? $this->options['search_fields'] : array(), '#description' => t('Select the field(s) that will be searched when using the autocomplete widget.'), '#weight' => -3, ); diff --git a/sites/all/modules/contrib/fields/field_group/CHANGELOG.txt b/sites/all/modules/contrib/fields/field_group/CHANGELOG.txt index 5e3ea36..5798162 100644 --- a/sites/all/modules/contrib/fields/field_group/CHANGELOG.txt +++ b/sites/all/modules/contrib/fields/field_group/CHANGELOG.txt @@ -1,6 +1,58 @@ /* $Id*/ CHANGELOG for field_group for Drupal 7 +Field Group origin/7.x-1.x, 2017-11-04 +-------------------------------------- +- Tests fail after new 'administer fields' permission. +- Update CHANGELOG.txt to 7.x-1.5 Release. +- Revert "Issue #2646106 by jribeiro: Update CHANGELOG.txt to 7.x-1.5 Release". +- Update CHANGELOG.txt to 7.x-1.5 Release. +- Fix whitespace between "&" and parameters for some functions. +- Avoid bad code pattern (that is picked up by security scanners) when getting hash. +- Should #array_parents be updated in field_group_fields_nest()?. +- "Show" appears in horizontal tab title if node form submitted through ajax. +- CSS breaks i18n string translation interface. +- "uncaught exception: Syntax error, unrecognized expression: #" after 7.x-1.4 update. +- PHP7 - Uniform Variable Syntax updates are causing exported field_groups to not have names. + +Field_group 7.x-1.5 + o Added extra formatting on html element attributes. + o Fixed bug on automatic ID generation in javascript. + o Issue #2511960 by rrfegade: spelling errors in D7. + o Issue #2194279 by Georgique: Export translatables (label and descriptions) in Features. + o Issue #2386335 by Dubs: Integer Group Array Keys can create empty node forms. + o Issue #2269133 by sammuell: Accordion is always closed when using jQuery update module. + o Issue #2258939 by Snipon: Fixed Change to preventDefault on multipage controls. + o Issue #2173937 by jantoine, omerida | Bernieman: Fixed Accordion Element has fixed height in HTML. + o Issue #2311789 by eelkeblok: Fixed Group is incorrectly determined to be empty with nested form elements. + o Issue #2272003 by karolrybak | Kwb: Fixed Fieldgroup no longer renders after using more than one 'HTML Element' on the same level. + o Issue #2309219 by czigor: Fixed Proper CTools exportable loading. + o Issue #2283245 by stefan.r | jrb: Fixed New id functionality breaks tests and CSS on existing field groups. + o Issue #2295133 by boyan.borisov, interX: Fixed field_group_update_7007 error. + o Remove field_group.css, the css is not needed anymore. + o Fix horiziontal tabs when used as standalone element. + +Field_group 7.x-1.4 + o Issue #2129805 by RaF: Incorrect markup when open div & custom classes provided. + o Issue #2037731 by maximpodorov, Zach Harkey: Remove id attribute from HTML elements. + o Issue #2099505 by borisson_ | Maks: Warning: Invalid argument supplied for foreach() in field_group_build_entity_groups() (line 1894 ..sites/all/modules/field_group. + o Issue #2102397 by thebruce: Field Group 7006 update fails with "Cannot use string offset as an array in /field_group/field_group.install on line 318. + o Issue #2122733 by Alan D.: Remove test module from admin listing. + o Issue #2212431 by tim.plunkett: Field groups that contain only elements that use #markup are hidden. + o Issue #2175731 by tobiasb, daggerhart: HTML produced by module is broken. + o Issue #2190425 by tobiasb: Fix attach behavior. + o Issue #1911530 by tobiasb, Simon Georges: Remove useless files[] directive from .info files. + o Issue #1358430 by oushen, tobiasb | FriedJam: JS "Error: uncaught exception: Syntax error, unrecognized expression:". + o Issue #2104391 by mike_san: Package descriptions. + o Issue #2189777 by gmercer: Exporting a feature with field groups sometimes leads to $field_group->data being set to an empty string. + o Issue #2168689 by Paul B: Beïng should be being. + o Issue #2037731 by rreiss, maximpodorov, Zach Harkey: Make id attribute optional. Current installations will default get the old ids + o Issue #2173351 by axe312: Add a label to html-element wrappers. + o Issue #1954056 by klonos: Proper capitalization in the project name. + o Issue #2224547 by sdrycroft: Fixed field_group_info_groups() clears all CTools caches when it should only clear the field_group caches. + o Issue #2099505 by joelpittet, borisson_ | Maks: Fixed Warning: Invalid argument supplied for foreach() in field_group_build_entity_groups() (line 1894 ..sites/all/modules/field_group. + o Issue #2223269 by barraponto: Fixed Don't filter and translate empty strings. + Field_group 7.x-1.3 o Issue #2077695 by FreekVR | ChoY: Fixed field group entity display bug after update. o Issue #2078201 by DeFr, eneko1907 | cmriley: Fixed Started getting a ton of notices. diff --git a/sites/all/modules/contrib/fields/field_group/LICENSE.txt b/sites/all/modules/contrib/fields/field_group/LICENSE.txt old mode 100755 new mode 100644 diff --git a/sites/all/modules/contrib/fields/field_group/field_group.api.php b/sites/all/modules/contrib/fields/field_group/field_group.api.php index cef5b0a..fdcbe03 100644 --- a/sites/all/modules/contrib/fields/field_group/field_group.api.php +++ b/sites/all/modules/contrib/fields/field_group/field_group.api.php @@ -220,7 +220,7 @@ function hook_field_group_format_settings($group) { * @param Array $elements by address. * @param Object $group The Field group info. */ -function hook_field_group_pre_render(& $element, $group, & $form) { +function hook_field_group_pre_render(&$element, $group, &$form) { // You can prepare some variables to use in the logic. $view_mode = isset($form['#view_mode']) ? $form['#view_mode'] : 'form'; @@ -268,7 +268,7 @@ function hook_field_group_pre_render(& $element, $group, & $form) { * * Function that fungates as last resort to alter the pre_render build. */ -function hook_field_group_pre_render_alter(&$element, $group, & $form) { +function hook_field_group_pre_render_alter(&$element, $group, &$form) { if ($group->format_type == 'htab') { $element['#theme_wrappers'] = array('my_horizontal_tab'); @@ -285,7 +285,7 @@ function hook_field_group_pre_render_alter(&$element, $group, & $form) { * * @param Array $elements by address. */ -function hook_field_group_build_pre_render_alter(& $element) { +function hook_field_group_build_pre_render_alter(&$element) { // Prepare variables. $display = isset($element['#view_mode']); diff --git a/sites/all/modules/contrib/fields/field_group/field_group.css b/sites/all/modules/contrib/fields/field_group/field_group.css deleted file mode 100644 index d47f007..0000000 --- a/sites/all/modules/contrib/fields/field_group/field_group.css +++ /dev/null @@ -1,23 +0,0 @@ -/* $Id: field_group.css,v 1.1.2.12 2010/12/22 22:22:35 stalski Exp $ */ - -/** - * Fix for fieldsets in vertical tabs. - * Note that this can only be hardcoded to the Seven theme - * where people who override this, are in trouble. - * This can be removed in next d7 release. - */ -.vertical-tabs fieldset.default-fallback, -div.field-group-tabs-wrapper div.field-type-image fieldset, -div.field-group-tabs-wrapper div.field-type-file fieldset, -div.field-group-tabs-wrapper div.field-type-datetime fieldset { - border: 1px solid #CCCCCC; - margin: 1em 0; - padding: 2.5em 0 0; - position: relative; -} - -div.field-group-tabs-wrapper div.field-type-image legend, -div.field-group-tabs-wrapper div.field-type-file legend, -div.field-group-tabs-wrapper div.field-type-datetime legend { - display: block; -} \ No newline at end of file diff --git a/sites/all/modules/contrib/fields/field_group/field_group.field_ui.inc b/sites/all/modules/contrib/fields/field_group/field_group.field_ui.inc index 557b0c6..dbd26d0 100644 --- a/sites/all/modules/contrib/fields/field_group/field_group.field_ui.inc +++ b/sites/all/modules/contrib/fields/field_group/field_group.field_ui.inc @@ -99,10 +99,10 @@ function field_group_field_ui_overview_form_alter(&$form, &$form_state, $display // Play around with form_state so we only need to hold things // between requests, until the save button was hit. if (isset($form_state['field_group'][$name])) { - $group = & $form_state['field_group'][$name]; + $group = &$form_state['field_group'][$name]; } else { - $group = & $params->groups[$name]; + $group = &$params->groups[$name]; } // Check the currently selected formatter, and merge persisted values for @@ -222,7 +222,7 @@ function field_group_field_ui_overview_form_alter(&$form, &$form_state, $display $table[$name]['format']['type']['#attributes']['class'] = array('element-invisible'); } else { - // After saving, the settings are updated here aswell. First we create + // After saving, the settings are updated here as well. First we create // the element for the table cell. $table[$name]['settings_summary'] = array('#markup' => ''); if (!empty($group->format_settings)) { @@ -456,7 +456,7 @@ function field_group_format_settings_label_validate($element, &$form_state) { * @param Object $group * @param array $settings */ -function field_group_formatter_row_update(& $group, $settings) { +function field_group_formatter_row_update(&$group, $settings) { // if the row has changed formatter type, update the group object if (!empty($settings['format']['type']) && $settings['format']['type'] != $group->format_type) { $group->format_type = $settings['format']['type']; @@ -469,7 +469,7 @@ function field_group_formatter_row_update(& $group, $settings) { * @param Object $group The group object * @param Array $settings Configuration settings */ -function field_group_formatter_settings_update(& $group, $settings) { +function field_group_formatter_settings_update(&$group, $settings) { // for format changes we load the defaults. if (empty($settings['format_settings']['settings'])) { diff --git a/sites/all/modules/contrib/fields/field_group/field_group.info b/sites/all/modules/contrib/fields/field_group/field_group.info index 96a6053..49eae8d 100644 --- a/sites/all/modules/contrib/fields/field_group/field_group.info +++ b/sites/all/modules/contrib/fields/field_group/field_group.info @@ -6,9 +6,9 @@ dependencies[] = ctools core = 7.x files[] = tests/field_group.ui.test files[] = tests/field_group.display.test -; Information added by Drupal.org packaging script on 2014-06-04 -version = "7.x-1.4" +; Information added by Drupal.org packaging script on 2017-11-03 +version = "7.x-1.6" core = "7.x" project = "field_group" -datestamp = "1401918529" +datestamp = "1509751991" diff --git a/sites/all/modules/contrib/fields/field_group/field_group.install b/sites/all/modules/contrib/fields/field_group/field_group.install index fa5371d..a7bb85c 100644 --- a/sites/all/modules/contrib/fields/field_group/field_group.install +++ b/sites/all/modules/contrib/fields/field_group/field_group.install @@ -17,6 +17,7 @@ function field_group_schema() { 'key' => 'identifier', 'identifier' => 'field_group', 'default hook' => 'field_group_info', + 'load callback' => 'field_group_group_load_by_identifier', 'save callback' => 'field_group_group_save', 'delete callback' => 'field_group_group_export_delete', 'can disable' => TRUE, @@ -229,6 +230,8 @@ function field_group_update_7001() { ->condition('name', 'field_group') ->execute(); + // Clear drupal and static cache. + field_group_info_groups(NULL, NULL, NULL, TRUE); } /** @@ -246,6 +249,8 @@ function field_group_update_7002() { // See http://drupal.org/node/1018550. _field_group_recreate_identifiers(); + // Clear drupal and static cache. + field_group_info_groups(NULL, NULL, NULL, TRUE); } /** @@ -257,6 +262,8 @@ function field_group_update_7003() { module_load_include('module', 'field_group'); _field_group_recreate_identifiers(); + // Clear drupal and static cache. + field_group_info_groups(NULL, NULL, NULL, TRUE); } /** @@ -295,6 +302,8 @@ function field_group_update_7005() { drupal_write_record('field_group', $row, array('id')); } + // Clear drupal and static cache. + field_group_info_groups(NULL, NULL, NULL, TRUE); } /** @@ -330,6 +339,8 @@ function field_group_update_7006() { } } + // Clear drupal and static cache. + field_group_info_groups(NULL, NULL, NULL, TRUE); } /** @@ -344,8 +355,18 @@ function field_group_update_7007() { // Migrate the field groups so they have a unique identifier. $field_groups = ctools_export_load_object("field_group"); foreach ($field_groups as $row) { - if ($row->data['format_type'] == 'div' || $row->data['format_type'] == 'html5' || $row->data['format_type'] == 'html-element') { + // These field group types have an ID setting to populate. + $populate_id_setting = array( + 'html-element', + 'div', + 'html5', + 'fieldset', + 'tabs', + 'htabs', + 'accordion', + ); + if (in_array($row->data['format_type'], $populate_id_setting)) { // If mode is default, we don't know what view mode it was. Take full then. $view_mode = $row->mode == 'default' ? 'full' : $row->mode; $id = $row->entity_type . '_' . $row->bundle . '_' . $view_mode . '_' . $row->group_name; @@ -360,4 +381,14 @@ function field_group_update_7007() { drupal_write_record('field_group', $row, array('id')); } } + + // Clear drupal and static cache. + field_group_info_groups(NULL, NULL, NULL, TRUE); } + +/** + * Clear cache to notice the CTools load callback. + */ +function field_group_update_7008() { + drupal_flush_all_caches(); +} \ No newline at end of file diff --git a/sites/all/modules/contrib/fields/field_group/field_group.js b/sites/all/modules/contrib/fields/field_group/field_group.js index eef6aaa..0b0a565 100644 --- a/sites/all/modules/contrib/fields/field_group/field_group.js +++ b/sites/all/modules/contrib/fields/field_group/field_group.js @@ -41,9 +41,17 @@ Drupal.FieldGroup.Effects.processAccordion = { $('div.field-group-accordion-wrapper', context).once('fieldgroup-effects', function () { var wrapper = $(this); + // Get the index to set active. + var active_index = false; + wrapper.find('.accordion-item').each(function(i) { + if ($(this).hasClass('field-group-accordion-active')) { + active_index = i; + } + }); + wrapper.accordion({ - autoHeight: false, - active: '.field-group-accordion-active', + heightStyle: "content", + active: active_index, collapsible: true, changestart: function(event, ui) { if ($(this).hasClass('effect-none')) { @@ -111,6 +119,9 @@ Drupal.FieldGroup.Effects.processHtabs = { Drupal.FieldGroup.Effects.processTabs = { execute: function (context, settings, type) { if (type == 'form') { + + var errorFocussed = false; + // Add required fields mark to any fieldsets containing required fields $('fieldset.vertical-tabs-pane', context).once('fieldgroup-effects', function(i) { if ($(this).is('.required-fields') && $(this).find('.form-required').length > 0) { @@ -118,8 +129,12 @@ Drupal.FieldGroup.Effects.processTabs = { } if ($('.error', $(this)).length) { $(this).data('verticalTab').link.parent().addClass('error'); - Drupal.FieldGroup.setGroupWithfocus($(this)); - $(this).data('verticalTab').focus(); + // Focus the first tab with error. + if (!errorFocussed) { + Drupal.FieldGroup.setGroupWithfocus($(this)); + $(this).data('verticalTab').focus(); + errorFocussed = true; + } } }); } @@ -202,15 +217,14 @@ Drupal.behaviors.fieldGroup = { $('.fieldset-wrapper .fieldset > legend').css({display: 'block'}); $('.vertical-tabs fieldset.fieldset').addClass('default-fallback'); - // Add a new ID to each fieldset. - $('.group-wrapper fieldset').each(function() { + $('.group-wrapper .horizontal-tabs-panes > fieldset', context).once('group-wrapper-panes-processed', function() { // Tats bad, but we have to keep the actual id to prevent layouts to break. - var fieldgorupID = 'field_group-' + $(this).attr('id') + ' ' + $(this).attr('id'); - $(this).attr('id', fieldgorupID); - }) + var fieldgroupID = 'field_group-' + $(this).attr('id'); + $(this).attr('id', fieldgroupID); + }); // Set the hash in url to remember last userselection. - $('.group-wrapper ul li').each(function() { + $('.group-wrapper ul li').once('group-wrapper-ul-processed', function() { var fieldGroupNavigationListIndex = $(this).index(); $(this).children('a').click(function() { var fieldset = $('.group-wrapper fieldset').get(fieldGroupNavigationListIndex); @@ -219,7 +233,8 @@ Drupal.behaviors.fieldGroup = { window.location.hash = hashUrl; }); }); + } }; -})(jQuery); \ No newline at end of file +})(jQuery); diff --git a/sites/all/modules/contrib/fields/field_group/field_group.module b/sites/all/modules/contrib/fields/field_group/field_group.module index da1a791..4fe2547 100644 --- a/sites/all/modules/contrib/fields/field_group/field_group.module +++ b/sites/all/modules/contrib/fields/field_group/field_group.module @@ -122,6 +122,20 @@ function field_group_menu_load($group_name, $entity_type, $bundle_name, $bundle_ return empty($group) ? FALSE : $group; } +/** + * Ctools load callback to load fieldgroup by identifier. + */ +function field_group_load_field_group_by_identifier($identifier) { + + $parts = explode('|', $identifier); + if (count($parts) != 4) { + return; + } + + return field_group_load_field_group($parts[0], $parts[1], $parts[2], $parts[3]); + +} + /** * Loads a group definition. * @@ -244,8 +258,6 @@ function field_group_field_attach_delete_bundle($entity_type, $bundle) { * Implements hook_field_attach_form(). */ function field_group_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) { - - $form['#attached']['css'][] = drupal_get_path('module', 'field_group') . '/field_group.field_ui.css'; field_group_attach_groups($form, 'form', $form_state); $form['#pre_render'][] = 'field_group_form_pre_render'; } @@ -292,7 +304,7 @@ function field_group_field_group_formatter_info() { 'html-element' => array( 'label' => t('HTML element'), 'description' => t('This fieldgroup renders the inner content in a HTML element with classes and attributes.'), - 'instance_settings' => array('element' => 'div', 'show_label' => 0, 'label_element' => 'div', 'classes' => '', 'attributes' => '', 'required_fields' => 1), + 'instance_settings' => array('element' => 'div', 'show_label' => 0, 'label_element' => 'div', 'classes' => '', 'attributes' => '', 'required_fields' => 1, 'id' => ''), ), 'div' => array( 'label' => t('Div'), @@ -310,13 +322,13 @@ function field_group_field_group_formatter_info() { 'label' => t('Fieldset'), 'description' => t('This fieldgroup renders the inner content in a fieldset with the title as legend.'), 'format_types' => array('open', 'collapsible', 'collapsed'), - 'instance_settings' => array('description' => '', 'classes' => '', 'required_fields' => 1), + 'instance_settings' => array('description' => '', 'classes' => '', 'required_fields' => 1, 'id' => ''), 'default_formatter' => 'collapsible', ), 'tabs' => array( 'label' => t('Vertical tabs group'), 'description' => t('This fieldgroup renders child groups in its own vertical tabs wrapper.'), - 'instance_settings' => array('classes' => ''), + 'instance_settings' => array('classes' => '', 'id' => ''), ), 'tab' => array( 'label' => t('Vertical tab'), @@ -328,14 +340,14 @@ function field_group_field_group_formatter_info() { 'htabs' => array( 'label' => t('Horizontal tabs group'), 'description' => t('This fieldgroup renders child groups in its own horizontal tabs wrapper.'), - 'instance_settings' => array('classes' => ''), + 'instance_settings' => array('classes' => '', 'id' => ''), ), 'htab' => array( 'label' => t('Horizontal tab'), 'format_types' => array('open', 'closed'), 'description' => t('This fieldgroup renders the content in a fieldset, part of horizontal tabs group.'), 'default_formatter' => 'closed', - 'instance_settings' => array('description' => '', 'classes' => '', 'required_fields' => 1, 'id' => ''), + 'instance_settings' => array('description' => '', 'classes' => '', 'required_fields' => 1), ), 'multipage-group' => array( 'label' => t('Multipage group'), @@ -352,7 +364,7 @@ function field_group_field_group_formatter_info() { 'accordion' => array( 'label' => t('Accordion group'), 'description' => t('This fieldgroup renders child groups as jQuery accordion.'), - 'instance_settings' => array('effect' => 'none', 'classes' => ''), + 'instance_settings' => array('effect' => 'none', 'classes' => '', 'id' => ''), ), 'accordion-item' => array( 'label' => t('Accordion item'), @@ -366,7 +378,7 @@ function field_group_field_group_formatter_info() { 'html-element' => array( 'label' => t('HTML element'), 'description' => t('This fieldgroup renders the inner content in a HTML element with classes and attributes.'), - 'instance_settings' => array('element' => 'div', 'show_label' => 0, 'label_element' => 'div', 'classes' => '', 'attributes' => '', 'required_fields' => 1), + 'instance_settings' => array('element' => 'div', 'show_label' => 0, 'label_element' => 'div', 'classes' => '', 'attributes' => '', 'required_fields' => 1, 'id' => ''), ), 'div' => array( 'label' => t('Div'), @@ -384,13 +396,13 @@ function field_group_field_group_formatter_info() { 'label' => t('Fieldset'), 'description' => t('This fieldgroup renders the inner content in a fieldset with the title as legend.'), 'format_types' => array('open', 'collapsible', 'collapsed'), - 'instance_settings' => array('description' => '', 'classes' => ''), + 'instance_settings' => array('description' => '', 'classes' => '', 'id' => ''), 'default_formatter' => 'collapsible', ), 'tabs' => array( 'label' => t('Vertical tabs group'), 'description' => t('This fieldgroup renders child groups in its own vertical tabs wrapper.'), - 'instance_settings' => array('classes' => ''), + 'instance_settings' => array('classes' => '', 'id' => ''), ), 'tab' => array( 'label' => t('Vertical tab'), @@ -402,7 +414,7 @@ function field_group_field_group_formatter_info() { 'htabs' => array( 'label' => t('Horizontal tabs group'), 'description' => t('This fieldgroup renders child groups in its own horizontal tabs wrapper.'), - 'instance_settings' => array('classes' => ''), + 'instance_settings' => array('classes' => '', 'id' => ''), ), 'htab' => array( 'label' => t('Horizontal tab item'), @@ -414,7 +426,7 @@ function field_group_field_group_formatter_info() { 'accordion' => array( 'label' => t('Accordion group'), 'description' => t('This fieldgroup renders child groups as jQuery accordion.'), - 'instance_settings' => array('description' => '', 'classes' => '', 'effect' => 'bounceslide'), + 'instance_settings' => array('description' => '', 'classes' => '', 'effect' => 'bounceslide', 'id' => ''), ), 'accordion-item' => array( 'label' => t('Accordion item'), @@ -706,12 +718,12 @@ function field_group_pre_render_html_element(&$element, $group, &$form) { $html_element = isset($group->format_settings['instance_settings']['element']) ? $group->format_settings['instance_settings']['element'] : 'div'; $show_label = isset($group->format_settings['instance_settings']['show_label']) ? $group->format_settings['instance_settings']['show_label'] : 0; $label_element = isset($group->format_settings['instance_settings']['label_element']) ? $group->format_settings['instance_settings']['label_element'] : 'div'; - $attributes = isset($group->format_settings['instance_settings']['attributes']) ? ' ' . $group->format_settings['instance_settings']['attributes'] : ''; + $configured_attributes = isset($group->format_settings['instance_settings']['attributes']) ? ' ' . $group->format_settings['instance_settings']['attributes'] : ''; $group->classes = trim($group->classes); // This regex split the attributes string so that we can pass that // later to drupal_attributes(). - preg_match_all('/([^\s=]+)="([^"]+)"/', $attributes, $matches); + preg_match_all('/([^\s=]+)="([^"]+)"/', $configured_attributes, $matches); $element_attributes = array(); // Put the attribute and the value together. @@ -727,7 +739,13 @@ function field_group_pre_render_html_element(&$element, $group, &$form) { $element_attributes['class'] .= ' ' . $group->classes; } - $attributes = drupal_attributes($element_attributes); + if (isset($element['#id'])) { + $element_attributes['id'] = $element['#id']; + } + + // Sanitize the attributes. + $element_attributes = _filter_xss_attributes(drupal_attributes($element_attributes)); + $attributes = $element_attributes ? ' ' . implode(' ', $element_attributes) : ''; $element['#prefix'] = '<' . $html_element . $attributes . '>'; if ($show_label) { @@ -810,9 +828,11 @@ function field_group_pre_render_accordion(&$element, $group, &$form) { // Add the jQuery UI accordion. $element['#attached']['library'][] = array('system', 'ui.accordion'); + $id = !empty($element['#id']) ? ' id="' . $element['#id'] . '"' : ''; + $element += array( '#type' => 'markup', - '#prefix' => '
    ', + '#prefix' => '
    ', '#suffix' => '
    ', ); } @@ -850,11 +870,18 @@ function field_group_pre_render_accordion_item(&$element, $group, &$form) { */ function field_group_pre_render_htabs(&$element, $group, &$form) { + $classes = 'field-group-' . $group->format_type . '-wrapper'; + if (!empty($group->classes)) { + $classes .= ' ' . $group->classes; + } + + $id = !empty($element['#id']) ? ' id="' . $element['#id'] . '"' : ''; + $element += array( '#type' => 'horizontal_tabs', '#title' => check_plain(t($group->label)), '#theme_wrappers' => array('horizontal_tabs'), - '#prefix' => '
    ', + '#prefix' => '
    ', '#suffix' => '
    ', ); @@ -964,10 +991,17 @@ function field_group_pre_render_multipage(&$element, $group, &$form) { */ function field_group_pre_render_tabs(&$element, $group, &$form) { + $classes = 'field-group-' . $group->format_type . '-wrapper'; + if (!empty($group->classes)) { + $classes .= ' ' . $group->classes; + } + + $id = !empty($element['#id']) ? ' id="' . $element['#id'] . '"' : ''; + $element += array( '#type' => 'vertical_tabs', '#theme_wrappers' => array('vertical_tabs'), - '#prefix' => '
    ', + '#prefix' => '
    ', '#suffix' => '
    ', ); @@ -1074,7 +1108,7 @@ function field_group_pre_render_tab(&$element, $group, &$form) { * Implements hook_field_group_build_pre_render_alter(). * @param Array $elements by address. */ -function field_group_field_group_build_pre_render_alter(& $element) { +function field_group_field_group_build_pre_render_alter(&$element) { // Someone is doing a node view, in a node view. Reset content. // TODO Check if this breaks something else. @@ -1096,7 +1130,6 @@ function field_group_field_group_build_pre_render_alter(& $element) { // Add the default field_group javascript and stylesheet. $element['#attached']['js'][] = drupal_get_path('module', 'field_group') . '/field_group.js'; - $element['#attached']['css'][] = drupal_get_path('module', 'field_group') . '/field_group.css'; // Move additional settings to the last multipage pane if configured that way. // Note that multipages MUST be in the root of the form. @@ -1127,19 +1160,16 @@ function field_group_field_group_build_pre_render_alter(& $element) { */ function field_group_remove_empty_form_groups($name, & $element, $groups, &$form_groups, $entity) { - $exceptions = array('user__account', 'comment__author'); - $children = element_children($element); $hasChildren = FALSE; if (count($children)) { foreach ($children as $childname) { - if (in_array($childname, $groups)) { + if (in_array($childname, $groups, TRUE)) { field_group_remove_empty_form_groups($childname, $element[$childname], $groups, $form_groups, $entity); } - $exception = $entity . '__' . $childname; - $hasChildren = $hasChildren ? TRUE : (isset($element[$childname]['#type']) || isset($element[$childname]['#markup']) || in_array($exception, $exceptions)); + $hasChildren = $hasChildren ? TRUE : _field_group_is_empty_element($element, $entity, $childname, $groups); } } @@ -1164,6 +1194,43 @@ function field_group_remove_empty_form_groups($name, & $element, $groups, &$form } +/** + * Determine if an element has non-empty children. + */ +function _field_group_is_empty_element($element, $entity, $childname, $groups) { + + $exceptions = array('user__account', 'comment__author'); + $exception = $entity . '__' . $childname; + + if (in_array($exception, $exceptions)) { + return TRUE; + } + + if (isset($element[$childname]['#type']) + || isset($element[$childname]['#markup']) + || isset($element[$childname]['#prefix']) + || isset($element[$childname]['#suffix']) + ) { + return TRUE; + } + + // Prevent a double recursive loop (groups are already recursive looped in field_group_remove_empty_form_groups. + if (in_array($childname, $groups)) { + return FALSE; + } + + $children = element_children($element[$childname]); + + foreach ($children as $child) { + if (_field_group_is_empty_element($element[$childname], $entity, $child, $groups)) { + return TRUE; + } + } + + return FALSE; + +} + /** * Remove empty groups on entity display. * @param array $element @@ -1423,7 +1490,7 @@ function form_process_horizontal_tabs($element, &$form_state) { function theme_horizontal_tabs($variables) { $element = $variables['element']; // Add required JavaScript and Stylesheet. - $element['#attached']['library'][] = array('field_group', 'horizontal-tabs'); + drupal_add_library('field_group', 'horizontal-tabs'); $output = '

    ' . (!empty($element['#title']) ? $element['#title'] : t('Horizontal Tabs')) . '

    '; $output .= '
    ' . $element['#children'] . '
    '; @@ -1861,8 +1928,10 @@ function field_group_attach_groups(&$element, $view_mode, $form_state = array()) // Create a lookup array. $group_children = array(); foreach ($element['#groups'] as $group_name => $group) { - foreach ($group->children as $child) { - $group_children[$child] = $group_name; + if (!empty($group->children)) { + foreach ($group->children as $child) { + $group_children[$child] = $group_name; + } } } $element['#group_children'] = $group_children; @@ -1951,9 +2020,25 @@ function field_group_fields_nest(&$element, &$vars = NULL) { // Create all groups and keep a flat list of references to these groups. $group_references = array(); foreach ($element['#fieldgroups'] as $group_name => $group) { - // Construct own weight, as some fields (for example preprocess fields) don't have weight set. - $element[$group_name] = array(); - $group_references[$group_name] = &$element[$group_name]; + // check for any erroneous groups from other modules + if (is_string($group_name)) { + // Construct own weight, as some fields (for example preprocess fields) don't have weight set. + $element[$group_name] = array(); + $group_references[$group_name] = &$element[$group_name]; + // Get group parents + $parents = array(); + $current_group = $group; + while (!empty($current_group)) { + array_unshift($parents, $current_group->group_name); + $current_group = isset($element['#fieldgroups'][$current_group->parent_name]) ? + $element['#fieldgroups'][$current_group->parent_name] : NULL; + } + $group_references[$group_name]['#array_parents'] = $parents; + $element['#fieldgroups'][$group_name]->array_parents = $parents; + // Remove self from parents and set #field_parents + array_pop($parents); + $group_references[$group_name]['#field_parents'] = $parents; + } } // Loop through all form children looking for those that are supposed to be @@ -2003,6 +2088,19 @@ function field_group_fields_nest(&$element, &$vars = NULL) { // list intact (but if it is a field we don't mind). $group_references[$parent_name][$child_name] = &$element[$child_name]; $group_references[$parent_name]['#weight'] = $element['#fieldgroups'][$parent_name]->weight; + // Prepend #array_parents & #field_parents of group child element & its element_children + // if those keys are set, and don't already include the group parents + $group_child = &$group_references[$parent_name][$child_name]; + $group_parents = $group_references[$parent_name]['#array_parents']; + $process_elements = array_merge(array(&$group_child), _field_group_element_children_recursive_ref($group_child)); + foreach ($process_elements as $key => $current_element) { + if (isset($current_element['#array_parents']) && !in_array($group_parents[0], $current_element['#array_parents'])) { + $process_elements[$key]['#array_parents'] = array_merge($group_parents, $current_element['#array_parents']); + } + if (isset($current_element['#field_parents']) && !in_array($group_parents[0], $current_element['#field_parents'])) { + $process_elements[$key]['#field_parents'] = array_merge($group_parents, $current_element['#field_parents']); + } + } } // The child has been copied to its parent: remove it from the root element. @@ -2019,6 +2117,23 @@ function field_group_fields_nest(&$element, &$vars = NULL) { } +/** + * Recursive element_children, returns children by reference + */ +function _field_group_element_children_recursive_ref(&$element) { + $results = array(); + $children = element_children($element); + foreach ($children as $key) { + $child = &$element[$key]; + if (is_array($child)) { + $results[] = &$child; + $results = array_merge($results, _field_group_element_children_recursive_ref($child)); + } + unset($child); + } + return $results; +} + /** * Function to pre render the field group element. * @@ -2167,3 +2282,37 @@ function _field_group_get_default_formatter_settings($format_type, $mode) { ); } +/** + * Callback to bulk export field groups. + */ +function field_group_field_group_to_hook_code($data, $module) { + ctools_include('export'); + $schema = ctools_export_get_schema('field_group'); + $export = $schema['export']; + + $translatables = array(); + $objects = ctools_export_load_object('field_group', 'names', array_values($data)); + $code = "/**\n"; + $code .= " * Implements hook_{$export['default hook']}()\n"; + $code .= " */\n"; + $code .= "function " . $module . "_{$export['default hook']}() {\n"; + $code .= " \${$export['identifier']}s = array();\n\n"; + foreach ($objects as $object) { + $code .= ctools_export_object('field_group', $object, ' '); + $code .= " \${$export['identifier']}s['" . check_plain($object->{$export['key']}) . "'] = \${$export['identifier']};\n\n"; + if (!empty($object->data['label'])) { + $translatables[] = $object->data['label']; + } + if (!empty($object->data['description'])) { + $translatables[] = $object->data['description']; + } + } + + if (!empty($translatables)) { + $code .= features_translatables_export($translatables, ' ') . "\n"; + } + + $code .= " return \${$export['identifier']}s;"; + $code .= "}\n"; + return $code; +} diff --git a/sites/all/modules/contrib/fields/field_group/horizontal-tabs/horizontal-tabs.js b/sites/all/modules/contrib/fields/field_group/horizontal-tabs/horizontal-tabs.js index 977e1b6..6429f90 100644 --- a/sites/all/modules/contrib/fields/field_group/horizontal-tabs/horizontal-tabs.js +++ b/sites/all/modules/contrib/fields/field_group/horizontal-tabs/horizontal-tabs.js @@ -29,8 +29,10 @@ Drupal.behaviors.horizontalTabs = { // Transform each fieldset into a tab. $fieldsets.each(function (i) { + var $legend = $('> legend', this); + $('.element-invisible', $legend).remove(); var horizontal_tab = new Drupal.horizontalTab({ - title: $('> legend', this).text(), + title: $legend.text(), fieldset: $(this) }); horizontal_tab.item.addClass('horizontal-tab-button-' + i); @@ -52,7 +54,7 @@ Drupal.behaviors.horizontalTabs = { // element that matches the URL fragment, activate that tab. var hash = window.location.hash.replace(/[=%;,\/]/g, ""); if (hash !== '#' && $(hash, this).length) { - tab_focus = $(window.location.hash, this).closest('.horizontal-tabs-pane'); + tab_focus = $(hash, this).closest('.horizontal-tabs-pane'); } else { tab_focus = $('> .horizontal-tabs-pane:first', this); diff --git a/sites/all/modules/contrib/fields/field_group/multipage/multipage.js b/sites/all/modules/contrib/fields/field_group/multipage/multipage.js index 5314031..b5f4d08 100644 --- a/sites/all/modules/contrib/fields/field_group/multipage/multipage.js +++ b/sites/all/modules/contrib/fields/field_group/multipage/multipage.js @@ -82,14 +82,14 @@ Drupal.multipageControl = function (settings) { var controls = Drupal.theme('multipage', settings); $.extend(self, settings, controls); - this.nextLink.click(function () { + this.nextLink.click(function (e) { + e.preventDefault(); self.nextPage(); - return false; }); - this.previousLink.click(function () { + this.previousLink.click(function (e) { + e.preventDefault(); self.previousPage(); - return false; }); /* diff --git a/sites/all/modules/contrib/fields/field_group/tests/field_group.display.test b/sites/all/modules/contrib/fields/field_group/tests/field_group.display.test index 5b82385..6058522 100644 --- a/sites/all/modules/contrib/fields/field_group/tests/field_group.display.test +++ b/sites/all/modules/contrib/fields/field_group/tests/field_group.display.test @@ -16,7 +16,7 @@ class GroupDisplayTestCase extends DrupalWebTestCase { return array( 'name' => 'Display tests', 'description' => 'Test the field group display.', - 'group' => 'Field group', + 'group' => 'Field Group', ); } @@ -209,7 +209,6 @@ class GroupDisplayTestCase extends DrupalWebTestCase { ), ); $first_tab = $this->createGroup('default', $data); - $first_tab_id = 'node_article_full_' . $first_tab->group_name; $data = array( 'label' => 'Tab 2', @@ -228,7 +227,6 @@ class GroupDisplayTestCase extends DrupalWebTestCase { ), ); $second_tab = $this->createGroup('default', $data); - $second_tab_id = 'node_article_full_' . $first_tab->group_name; $data = array( 'label' => 'Tabs', @@ -258,8 +256,8 @@ class GroupDisplayTestCase extends DrupalWebTestCase { $this->assertRaw('class="collapsible collapsed test-class-2', t('Second tab is default collapsed')); // Test if correctly nested - $this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]//fieldset[contains(@id, '$first_tab_id')]", NULL, 'First tab is displayed as child of the wrapper.'); - $this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]//fieldset[contains(@id, '$second_tab_id')]", NULL, 'Second tab is displayed as child of the wrapper.'); + $this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]//fieldset[contains(@class, 'test-class')]", NULL, 'First tab is displayed as child of the wrapper.'); + $this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]//fieldset[contains(@class, 'test-class-2')]", NULL, 'Second tab is displayed as child of the wrapper.'); } @@ -285,7 +283,7 @@ class GroupDisplayTestCase extends DrupalWebTestCase { ), ); $first_tab = $this->createGroup('default', $data); - $first_tab_id = 'node_article_full_' . $first_tab->group_name; + $first_tab_id = 'edit-' . $first_tab->group_name; $data = array( 'label' => 'Tab 2', @@ -304,7 +302,7 @@ class GroupDisplayTestCase extends DrupalWebTestCase { ), ); $second_tab = $this->createGroup('default', $data); - $second_tab_id = 'node_article_full_' . $first_tab->group_name; + $second_tab_id = 'edit-' . $second_tab->group_name; $data = array( 'label' => 'Tabs', diff --git a/sites/all/modules/contrib/fields/field_group/tests/field_group.ui.test b/sites/all/modules/contrib/fields/field_group/tests/field_group.ui.test index 55fa595..6c4436a 100644 --- a/sites/all/modules/contrib/fields/field_group/tests/field_group.ui.test +++ b/sites/all/modules/contrib/fields/field_group/tests/field_group.ui.test @@ -22,7 +22,7 @@ class GroupUITestCase extends DrupalWebTestCase { parent::setUp('field_test', 'field_group', 'field_group_test'); // Create test user. - $admin_user = $this->drupalCreateUser(array('administer content types', 'administer nodes', 'access administration pages', 'bypass node access')); + $admin_user = $this->drupalCreateUser(array('administer content types', 'administer nodes', 'access administration pages', 'bypass node access', 'administer fields')); $this->drupalLogin($admin_user); } diff --git a/sites/all/modules/contrib/fields/field_group/tests/field_group_test.info b/sites/all/modules/contrib/fields/field_group/tests/field_group_test.info index 006a7f3..22bd921 100644 --- a/sites/all/modules/contrib/fields/field_group/tests/field_group_test.info +++ b/sites/all/modules/contrib/fields/field_group/tests/field_group_test.info @@ -5,9 +5,9 @@ package = Fields hidden = TRUE -; Information added by Drupal.org packaging script on 2014-06-04 -version = "7.x-1.4" +; Information added by Drupal.org packaging script on 2017-11-03 +version = "7.x-1.6" core = "7.x" project = "field_group" -datestamp = "1401918529" +datestamp = "1509751991" diff --git a/sites/all/modules/contrib/fields/link/README.txt b/sites/all/modules/contrib/fields/link/README.txt new file mode 100644 index 0000000..a258d4d --- /dev/null +++ b/sites/all/modules/contrib/fields/link/README.txt @@ -0,0 +1,101 @@ +CONTENTS OF THIS FILE +--------------------- + + * Introduction + * Requirements + * Installation + * Configuration + * Example + * Theming and Output + * Maintainers + + +INTRODUCTION +------------ + +The link can be count to the top 50 projects in Drupal installations and +provides a standard custom content field for links. With this module links can +be added easily to any content types and profiles and include advanced +validating and different ways of storing internal or external links and URLs. It +also supports additional link text title, site wide tokens for titles and title +attributes, target attributes, css class attribution, static repeating values, +input conversion, and many more. + + + +REQUIREMENTS +------------ + +Project in Drupal 7 requires the following modules: + + * Fields API (Fields API is provided already by core) + * Panels (https://drupal.org/project/panels) + +Drupal 8: + + * Link is in core now. No installation needed. Yay! Don't forget to activate + it. It's deactivated by default. + + +INSTALLATION +------------ + +Install as you would normally install a contributed Drupal module. See: +https://drupal.org/documentation/install/modules-themes/modules-7 for further +information. + + +CONFIGURATION +------------- + + * Configuration is only slightly more complicated than a text field. Link text + titles for URLs can be made required, set as instead of URL, optional + (default), or left out entirely. If no link text title is provided, the + trimmed version of the complete URL will be displayed. The target attribute + should be set to "_blank", "top", or left out completely (checkboxes provide + info). The rel=nofollow attribute prevents the link from being followed by + certain search engines. More info at Wikipedia + (http://en.wikipedia.org/wiki/Spam_in_blogs#rel.3D.22nofollow.22). + + +EXAMPLE +------- +If you were to create a field named 'My New Link', the default display of the +link would be: + where items between [] characters would be customized +based on the user input. + +The link project supports both, internal and external URLs. URLs are validated +on input. Here are some examples of data input and the default view of a link: +http://drupal.org results in http://drupal.org, but drupal.org results in +http://drupal.org, while will convert into http://drupal.org and +node/74971 into http://drupal.org/project/link + +Anchors and query strings may also be used in any of these cases, including: +node/74971/edit?destination=node/74972#pager + + +THEMING AND OUTPUT +------------------ +Since link module is mainly a data storage field in a modular framework, the +theming and output is up to the site builder and other additional modules. There +are many modules in the Drupal repository, which control the output of fields +perfectly and can handle rules, user actions, markup dependencies, and can vary +the output under many different conditions, with much more efficience and +flexibility for different scenarios. Please check out modules like views, +display suite, panels, etc for such needs + + +MAINTAINERS +----------- + +Current maintainers: + * John C Fiala (jcfiala) - https://www.drupal.org/user/163643 + * Renato Gonçalves (RenatoG) - https://www.drupal.org/user/3326031 + * Clemens Tolboom (clemens.tolboom) - https://www.drupal.org/user/125814 + * diqidoq - https://www.drupal.org/user/1001934 + * dropcube - https://www.drupal.org/user/37031 + * Tom Kirkpatrick (mrfelton) - https://www.drupal.org/user/305669 + * Sumit Madan (sumitmadan) - https://www.drupal.org/user/1538790 + * Daniel Kudwien (sun) - https://www.drupal.org/user/54136 diff --git a/sites/all/modules/contrib/fields/link/link-rtl.css b/sites/all/modules/contrib/fields/link/link-rtl.css index 0359487..613db5d 100644 --- a/sites/all/modules/contrib/fields/link/link-rtl.css +++ b/sites/all/modules/contrib/fields/link/link-rtl.css @@ -1,7 +1,6 @@ .link-field-column { float: right; } - .link-field-column.link-field-url .form-text { direction: ltr; text-align: left; diff --git a/sites/all/modules/contrib/fields/link/link.css b/sites/all/modules/contrib/fields/link/link.css index 1590e7a..6250df7 100644 --- a/sites/all/modules/contrib/fields/link/link.css +++ b/sites/all/modules/contrib/fields/link/link.css @@ -2,7 +2,6 @@ float: left; width: 48%; } - .link-field-column .form-text { width: 95%; } diff --git a/sites/all/modules/contrib/fields/link/link.diff.inc b/sites/all/modules/contrib/fields/link/link.diff.inc index 9e34123..b01ba6e 100644 --- a/sites/all/modules/contrib/fields/link/link.diff.inc +++ b/sites/all/modules/contrib/fields/link/link.diff.inc @@ -11,11 +11,11 @@ function link_field_diff_view($items, $context) { $diff_items = array(); foreach ($items as $delta => $item) { - if ($item['url'] && $item['title']) { + if ($item['url'] && isset($item['title'])) { $diff_items[$delta] = $item['title'] . ' (' . $item['url'] . ')'; } else { - $diff_items[$delta] = $item['title'] . $item['url']; + $diff_items[$delta] = $item['url']; } } return $diff_items; diff --git a/sites/all/modules/contrib/fields/link/link.info b/sites/all/modules/contrib/fields/link/link.info index 016b799..351e178 100644 --- a/sites/all/modules/contrib/fields/link/link.info +++ b/sites/all/modules/contrib/fields/link/link.info @@ -3,7 +3,6 @@ description = Defines simple link field types. core = 7.x package = Fields -files[] = link.module files[] = link.migrate.inc ; Tests @@ -12,15 +11,15 @@ files[] = tests/link.attribute.test files[] = tests/link.crud.test files[] = tests/link.crud_browser.test files[] = tests/link.token.test +files[] = tests/link.entity_token.test files[] = tests/link.validate.test ; Views Handlers files[] = views/link_views_handler_argument_target.inc files[] = views/link_views_handler_filter_protocol.inc -; Information added by Drupal.org packaging script on 2014-10-21 -version = "7.x-1.3" +; Information added by Drupal.org packaging script on 2019-02-20 +version = "7.x-1.6" core = "7.x" project = "link" -datestamp = "1413924830" - +datestamp = "1550680687" diff --git a/sites/all/modules/contrib/fields/link/link.install b/sites/all/modules/contrib/fields/link/link.install index 14e745d..8125b69 100644 --- a/sites/all/modules/contrib/fields/link/link.install +++ b/sites/all/modules/contrib/fields/link/link.install @@ -6,10 +6,38 @@ */ /** - * Upgrade notes: - * Things we need to make sure work when upgrading from Drupal 6 to Drupal 7: + * Upgrade notes. + * + * Things we need to make sure work when upgrading from Drupal 6 to Drupal 7:. */ +/** + * Implements hook_uninstall(). + */ +function link_install() { + // Notify the user they may want to install token. + if (!module_exists('token')) { + $t = get_t(); + drupal_set_message($t('If you install the Token, static title can use any other entity field as its value.', array( + '!url' => 'http://drupal.org/project/token', + ))); + } +} + +/** + * Removes unused link_extra_domains variable. + */ +function link_update_7002() { + variable_del('link_extra_domains'); +} + +/** + * Implements hook_uninstall(). + */ +function link_uninstall() { + variable_del('link_allowed_domains'); +} + /** * Implements hook_field_schema(). */ @@ -46,19 +74,21 @@ function link_update_last_removed() { } /** - * Handles moving settings data from field_config.data to field_config_instance.data. + * Implements hook_update_N(). + * + * Handles moving settings data from field_config.data to + * field_config_instance.data. */ function link_update_7000() { - - // For each field that is a link field, we need to copy the settings from the general field level down to the instance. - //$field_data = array(); + + // For each field that is a link field, we need to copy the settings from the + // general field level down to the instance. $result = db_query("SELECT id, field_name, data FROM {field_config} WHERE module = 'link' AND type = 'link_field'"); foreach ($result as $field) { - $field_id = $field->id; - $name = $field->field_name; + $field_data = unserialize($field->data); - - $instances = db_query("SELECT id, data FROM {field_config_instance} WHERE field_id = :field_id", array(':field_id' => $field_id)); + + $instances = db_query("SELECT id, data FROM {field_config_instance} WHERE field_id = :field_id", array(':field_id' => $field->id)); foreach ($instances as $instance) { // If this field has been updated already, we want to skip it. $instance_data = unserialize($instance->data); @@ -71,8 +101,8 @@ function link_update_7000() { } } if ($update_instance) { - // update the database. - $num_updated = db_update('field_config_instance') + // Update the database. + db_update('field_config_instance') ->fields(array('data' => serialize($instance_data))) ->condition('id', $instance->id) ->execute(); @@ -80,22 +110,19 @@ function link_update_7000() { } } } - + return t("Instance settings have been set with the data from the field settings."); } /** - * Renames all displays from foobar to link_foobar + * Renames all displays from foobar to link_foobar. */ function link_update_7001() { // Update the display type for each link field type. $result = db_query("SELECT id, field_name, data FROM {field_config} WHERE module = 'link' AND type = 'link_field'"); foreach ($result as $field) { - $field_id = $field->id; - $name = $field->field_name; - $field_data = unserialize($field->data); - - $instances = db_query("SELECT id, data FROM {field_config_instance} WHERE field_id = :field_id", array(':field_id' => $field_id)); + + $instances = db_query("SELECT id, data FROM {field_config_instance} WHERE field_id = :field_id", array(':field_id' => $field->id)); foreach ($instances as $instance) { // If this field has been updated already, we want to skip it. $instance_data = unserialize($instance->data); diff --git a/sites/all/modules/contrib/fields/link/link.migrate.inc b/sites/all/modules/contrib/fields/link/link.migrate.inc index fc8363f..4900051 100644 --- a/sites/all/modules/contrib/fields/link/link.migrate.inc +++ b/sites/all/modules/contrib/fields/link/link.migrate.inc @@ -11,11 +11,9 @@ * $this->addFieldMapping('field_my_link', 'source_url'); * $this->addFieldMapping('field_my_link:title', 'source_title'); * $this->addFieldMapping('field_my_link:attributes', 'source_attributes'); - * @endcode * - * With earlier versions of Migrate, you must pass an arguments array: + * # With earlier versions of Migrate, you must pass an arguments array: * - * @code * $link_args = array( * 'title' => array('source_field' => 'source_title'), * 'attributes' => array('source_field' => 'source_attributes'), @@ -25,6 +23,10 @@ * @endcode */ +if (!class_exists('MigrateFieldHandler')) { + return; +} + /** * Implements hook_migrate_api(). */ @@ -35,12 +37,20 @@ function link_migrate_api() { ); } +// @codingStandardsIgnoreLine class MigrateLinkFieldHandler extends MigrateFieldHandler { + + /** + * Construct. + */ public function __construct() { $this->registerTypes(array('link_field')); } - static function arguments($title = NULL, $attributes = NULL, $language = NULL) { + /** + * Arguments. + */ + public static function arguments($title = NULL, $attributes = NULL, $language = NULL) { $arguments = array(); if (!is_null($title)) { $arguments['title'] = $title; @@ -57,16 +67,21 @@ class MigrateLinkFieldHandler extends MigrateFieldHandler { /** * Implementation of MigrateFieldHandler::fields(). * - * @param $type - * The field type. - * @param $instance - * Instance info for the field. + * @param array $type + * The field type. + * @param array $instance + * Instance info for the field. * @param Migration $migration - * The migration context for the parent field. We can look at the mappings - * and determine which subfields are relevant. + * The migration context for the parent field. We can look at the mappings + * and determine which subfields are relevant. + * * @return array + * Array with values. + * + * @codingStandardsIgnoreStart */ public function fields($type, $instance, $migration = NULL) { + // @codingStandardsIgnoreEnd return array( 'title' => t('Subfield: The link title attribute'), 'attributes' => t('Subfield: The attributes for this link'), @@ -74,6 +89,9 @@ class MigrateLinkFieldHandler extends MigrateFieldHandler { ); } + /** + * Prepare. + */ public function prepare($entity, array $field_info, array $instance, array $values) { if (isset($values['arguments'])) { $arguments = $values['arguments']; @@ -105,9 +123,17 @@ class MigrateLinkFieldHandler extends MigrateFieldHandler { } } $item['url'] = $value; - $return[$language][$delta] = $item; + + if (is_array($language)) { + $current_language = $language[$delta]; + } + else { + $current_language = $language; + } + $return[$current_language][$delta] = $item; } return isset($return) ? $return : NULL; } + } diff --git a/sites/all/modules/contrib/fields/link/link.module b/sites/all/modules/contrib/fields/link/link.module index b0c53c3..8b3094f 100644 --- a/sites/all/modules/contrib/fields/link/link.module +++ b/sites/all/modules/contrib/fields/link/link.module @@ -10,8 +10,6 @@ define('LINK_INTERNAL', 'internal'); define('LINK_FRONT', 'front'); define('LINK_EMAIL', 'email'); define('LINK_NEWS', 'news'); -define('LINK_DOMAINS', 'aero|arpa|asia|biz|build|com|cat|ceo|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|post|pro|tel|travel|mobi|local|xxx'); - define('LINK_TARGET_DEFAULT', 'default'); define('LINK_TARGET_NEW_WINDOW', '_blank'); define('LINK_TARGET_TOP', '_top'); @@ -22,6 +20,22 @@ define('LINK_TARGET_USER', 'user'); */ define('LINK_URL_MAX_LENGTH', 2048); +/** + * Implements hook_help(). + */ +function link_help($path, $arg) { + switch ($path) { + case 'admin/help#link': + $output = '

    About

    '; + $output .= '

    ' . 'The link provides a standard custom content field for links. Links can be easily added to any content types and profiles and include advanced validating and different ways of storing internal or external links and URLs. It also supports additional link text title, site wide tokens for titles and title attributes, target attributes, css class attribution, static repeating values, input conversion, and many more.' . '

    '; + $output .= '

    ' . 'Requirements / Dependencies' . '

    '; + $output .= '

    ' . 'Fields API is provided already by core [no dependencies].' . '

    '; + $output .= '

    Configuration

    '; + $output .= '

    ' . 'Configuration is only slightly more complicated than a text field. Link text titles for URLs can be made required, set as instead of URL, optional (default), or left out entirely. If no link text title is provided, the trimmed version of the complete URL will be displayed. The target attribute should be set to "_blank", "top", or left out completely (checkboxes provide info). The rel=nofollow attribute prevents the link from being followed by certain search engines.' . '

    '; + return $output; + } +} + /** * Implements hook_field_info(). */ @@ -98,6 +112,7 @@ function link_field_instance_settings_form($field, $instance) { 'optional' => t('Optional Title'), 'required' => t('Required Title'), 'value' => t('Static Title'), + 'select' => t('Selected Title'), 'none' => t('No Title'), ); @@ -111,9 +126,26 @@ function link_field_instance_settings_form($field, $instance) { $form['title_value'] = array( '#type' => 'textfield', - '#title' => t('Static title'), + '#title' => t('Static or default title'), '#default_value' => isset($instance['settings']['title_value']) ? $instance['settings']['title_value'] : '', - '#description' => t('This title will always be used if “Static Title” is selected above.'), + '#description' => t('This title will 1) always be used if "Static Title" is selected above, or 2) used if "Optional title" is selected above and no title is entered when creating content.'), + '#states' => array( + 'visible' => array( + ':input[name="instance[settings][title]"]' => array('value' => 'value'), + ), + ), + ); + + $form['title_allowed_values'] = array( + '#type' => 'textarea', + '#title' => t('Title allowed values'), + '#default_value' => isset($instance['settings']['title_allowed_values']) ? $instance['settings']['title_allowed_values'] : '', + '#description' => t('When using "Selected Title", you can allow users to select the title from a limited set of values (eg. Home, Office, Other). Enter here all possible values that title can take, one value per line.'), + '#states' => array( + 'visible' => array( + ':input[name="instance[settings][title]"]' => array('value' => 'select'), + ), + ), ); $form['title_label_use_field_label'] = array( @@ -181,7 +213,7 @@ function link_field_instance_settings_form($field, $instance) { $form['attributes']['rel'] = array( '#type' => 'textfield', '#title' => t('Rel Attribute'), - '#description' => t('When output, this link will have this rel attribute. The most common usage is rel="nofollow" which prevents some search engines from spidering entered links.'), + '#description' => t('When output, this link will have this rel attribute. The most common usage is rel="nofollow" which prevents some search engines from spidering entered links.'), '#default_value' => empty($instance['settings']['attributes']['rel']) ? '' : $instance['settings']['attributes']['rel'], '#field_prefix' => 'rel = "', '#field_suffix' => '"', @@ -207,7 +239,7 @@ function link_field_instance_settings_form($field, $instance) { $form['attributes']['class'] = array( '#type' => 'textfield', '#title' => t('Additional CSS Class'), - '#description' => t('When output, this link will have this class attribute. Multiple classes should be separated by spaces.'), + '#description' => t('When output, this link will have this class attribute. Multiple classes should be separated by spaces. Only alphanumeric characters and hyphens are allowed'), '#default_value' => empty($instance['settings']['attributes']['class']) ? '' : $instance['settings']['attributes']['class'], ); $form['attributes']['configurable_title'] = array( @@ -218,7 +250,7 @@ function link_field_instance_settings_form($field, $instance) { $form['attributes']['title'] = array( '#title' => t("Default link 'title' Attribute"), '#type' => 'textfield', - '#description' => t('When output, links will use this "title" attribute if the user does not provide one and when different from the link text. Read WCAG 1.0 Guidelines for links comformances. Tokens values will be evaluated.'), + '#description' => t('When output, links will use this "title" attribute if the user does not provide one and when different from the link text. Read WCAG 1.0 Guidelines for links comformances. Tokens values will be evaluated.'), '#default_value' => empty($instance['settings']['attributes']['title']) ? '' : $instance['settings']['attributes']['title'], '#field_prefix' => 'title = "', '#field_suffix' => '"', @@ -228,11 +260,17 @@ function link_field_instance_settings_form($field, $instance) { } /** + * Form validate. + * * #element_validate handler for link_field_instance_settings_form(). */ function link_field_settings_form_validate($element, &$form_state, $complete_form) { if ($form_state['values']['instance']['settings']['title'] === 'value' && empty($form_state['values']['instance']['settings']['title_value'])) { - form_set_error('title_value', t('A default title must be provided if the title is a static value.')); + form_set_error('instance][settings][title_value', t('A default title must be provided if the title is a static value.')); + } + if ($form_state['values']['instance']['settings']['title'] === 'select' + && empty($form_state['values']['instance']['settings']['title_allowed_values'])) { + form_set_error('instance][settings][title_allowed_values', t('You must enter one or more allowed values for link Title, the title is a selected value.')); } if (!empty($form_state['values']['instance']['settings']['display']['url_cutoff']) && !is_numeric($form_state['values']['instance']['settings']['display']['url_cutoff'])) { form_set_error('display', t('URL Display Cutoff value must be numeric.')); @@ -277,6 +315,16 @@ function link_field_validate($entity_type, $entity, $field, $instance, $langcode } } + foreach ($items as $delta => $value) { + if (isset($value['attributes']) && is_string($value['attributes'])) { + $errors[$field['field_name']][$langcode][$delta][] = array( + 'error' => 'link_required', + 'message' => t('String values are not acceptable for attributes.'), + 'error_element' => array('url' => TRUE, 'title' => FALSE), + ); + } + } + if ($instance['settings']['url'] === 'optional' && $instance['settings']['title'] === 'optional' && $instance['required'] && !$optional_field_found) { $errors[$field['field_name']][$langcode][0][] = array( 'error' => 'link_required', @@ -291,7 +339,7 @@ function link_field_validate($entity_type, $entity, $field, $instance, $langcode */ function link_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) { foreach ($items as $delta => $value) { - _link_process($items[$delta], $delta, $field, $entity); + _link_process($items[$delta], $delta, $field, $entity, $instance); } } @@ -300,7 +348,7 @@ function link_field_insert($entity_type, $entity, $field, $instance, $langcode, */ function link_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) { foreach ($items as $delta => $value) { - _link_process($items[$delta], $delta, $field, $entity); + _link_process($items[$delta], $delta, $field, $entity, $instance); } } @@ -343,10 +391,10 @@ function link_field_widget_form(&$form, &$form_state, $field, $instance, $langco * Implements hook_field_widget_error(). */ function link_field_widget_error($element, $error, $form, &$form_state) { - if ($error['error_element']['title']) { + if (!empty($error['error_element']['title'])) { form_error($element['title'], $error['message']); } - elseif ($error['error_element']['url']) { + elseif (!empty($error['error_element']['url'])) { form_error($element['url'], $error['message']); } } @@ -371,8 +419,22 @@ function _link_load($field, $item, $instance) { /** * Prepares the item attributes and url for storage. + * + * @param array $item + * Link field values. + * @param array $delta + * The sequence number for current values. + * @param array $field + * The field structure array. + * @param object $entity + * Entity object. + * @param array $instance + * The instance structure for $field on $entity's bundle. + * + * @codingStandardsIgnoreStart */ -function _link_process(&$item, $delta, $field, $entity) { +function _link_process(&$item, $delta, $field, $entity, $instance) { + // @codingStandardsIgnoreEnd // Trim whitespace from URL. if (!empty($item['url'])) { $item['url'] = trim($item['url']); @@ -391,7 +453,8 @@ function _link_process(&$item, $delta, $field, $entity) { // Don't save an invalid default value (e.g. 'http://'). if ((isset($field['widget']['default_value'][$delta]['url']) && $item['url'] == $field['widget']['default_value'][$delta]['url']) && is_object($entity)) { - if (!link_validate_url($item['url'])) { + $langcode = !empty($entity) ? field_language($instance['entity_type'], $entity, $instance['field_name']) : LANGUAGE_NONE; + if (!link_validate_url($item['url'], $langcode)) { unset($item['url']); } } @@ -403,7 +466,7 @@ function _link_process(&$item, $delta, $field, $entity) { function _link_validate(&$item, $delta, $field, $entity, $instance, $langcode, &$optional_field_found, &$errors) { if ($item['url'] && !(isset($instance['default_value'][$delta]['url']) && $item['url'] === $instance['default_value'][$delta]['url'] && !$instance['required'])) { // Validate the link. - if (link_validate_url(trim($item['url'])) == FALSE) { + if (!link_validate_url(trim($item['url']), $langcode)) { $errors[$field['field_name']][$langcode][$delta][] = array( 'error' => 'link_required', 'message' => t('The value %value provided for %field is not a valid URL.', array( @@ -430,9 +493,10 @@ function _link_validate(&$item, $delta, $field, $entity, $instance, $langcode, & 'error_element' => array('url' => TRUE, 'title' => FALSE), ); } - // In a totally bizzaro case, where URLs and titles are optional but the field is required, ensure there is at least one link. + // In a totally bizzaro case, where URLs and titles are optional but the field + // is required, ensure there is at least one link. if ($instance['settings']['url'] === 'optional' && $instance['settings']['title'] === 'optional' - && (strlen(trim($item['url'])) !== 0 || strlen(trim($item['title'])) !== 0)) { + && (strlen(trim($item['url'])) !== 0 || strlen(trim($item['title'])) !== 0)) { $optional_field_found = TRUE; } // Require entire field. @@ -448,16 +512,19 @@ function _link_validate(&$item, $delta, $field, $entity, $instance, $langcode, & /** * Clean up user-entered values for a link field according to field settings. * - * @param array $item + * @param array $item * A single link item, usually containing url, title, and attributes. - * @param int $delta + * @param int $delta * The delta value if this field is one of multiple fields. - * @param array $field + * @param array $field * The CCK field definition. - * @param object $entity + * @param object $entity * The entity containing this link. + * + * @codingStandardsIgnoreStart */ function _link_sanitize(&$item, $delta, &$field, $instance, &$entity) { + // @codingStandardsIgnoreEnd // Don't try to process empty links. if (empty($item['url']) && empty($item['title'])) { return; @@ -471,22 +538,24 @@ function _link_sanitize(&$item, $delta, &$field, $instance, &$entity) { $entity_info = entity_get_info($entity_type); $property_id = $entity_info['entity keys']['id']; $entity_token_type = isset($entity_info['token type']) ? $entity_info['token type'] : ( - $entity_type == 'taxonomy_term' || $entity_type == 'taxonomy_vocabulary' ? str_replace('taxonomy_', '', $entity_type) : $entity_type + $entity_type == 'taxonomy_term' || $entity_type == 'taxonomy_vocabulary' ? str_replace('taxonomy_', '', $entity_type) : $entity_type ); if (isset($instance['settings']['enable_tokens']) && $instance['settings']['enable_tokens']) { - global $user; - // Load the entity if necessary for entities in views. - if (isset($entity->{$property_id})) { - $entity_loaded = entity_load($entity_type, array($entity->{$property_id})); - $entity_loaded = array_pop($entity_loaded); + $text_tokens = token_scan($item['url']); + if (!empty($text_tokens)) { + // Load the entity if necessary for entities in views. + if (isset($entity->{$property_id})) { + $entity_loaded = entity_load($entity_type, array($entity->{$property_id})); + $entity_loaded = array_pop($entity_loaded); + } + else { + $entity_loaded = $entity; + } + $item['url'] = token_replace($item['url'], array($entity_token_type => $entity_loaded)); } - else { - $entity_loaded = $entity; - } - $item['url'] = token_replace($item['url'], array($entity_token_type => $entity_loaded)); } - $type = link_validate_url($item['url']); + $type = link_url_type($item['url']); // If the type of the URL cannot be determined and URL validation is disabled, // then assume LINK_EXTERNAL for later processing. if ($type == FALSE && $instance['settings']['validate_url'] === 0) { @@ -496,12 +565,13 @@ function _link_sanitize(&$item, $delta, &$field, $instance, &$entity) { $url_parts = _link_parse_url($url); if (!empty($url_parts['url'])) { - $item['url'] = $url_parts['url']; - $item += array( - 'query' => isset($url_parts['query']) ? $url_parts['query'] : NULL, - 'fragment' => isset($url_parts['fragment']) ? $url_parts['fragment'] : NULL, - 'absolute' => !empty($instance['settings']['absolute_url']), - 'html' => TRUE, + $item['url'] = url($url_parts['url'], + array( + 'query' => isset($url_parts['query']) ? $url_parts['query'] : NULL, + 'fragment' => isset($url_parts['fragment']) ? $url_parts['fragment'] : NULL, + 'absolute' => !empty($instance['settings']['absolute_url']), + 'html' => TRUE, + ) ); } @@ -532,28 +602,51 @@ function _link_sanitize(&$item, $delta, &$field, $instance, &$entity) { } } // Use the title defined by the user at the widget level. - elseif (isset($item['title'])) { + elseif (drupal_strlen(trim($item['title']))) { $title = $item['title']; } + // Use the static title if a user-defined title is optional and a static title + // has been defined. + elseif ($instance['settings']['title'] == 'optional' && drupal_strlen(trim($instance['settings']['title_value']))) { + $title = $instance['settings']['title_value']; + } else { $title = ''; } // Replace title tokens. + if ($title && $instance['settings']['enable_tokens']) { + $text_tokens = token_scan($title); + if (!empty($text_tokens)) { + // Load the entity if necessary for entities in views. + if (isset($entity->{$property_id})) { + $entity_loaded = entity_load($entity_type, array($entity->{$property_id})); + $entity_loaded = array_pop($entity_loaded); + } + else { + $entity_loaded = $entity; + } + $title = token_replace($title, array($entity_token_type => $entity_loaded)); + } + } if ($title && ($instance['settings']['title'] == 'value' || $instance['settings']['enable_tokens'])) { - // Load the entity if necessary for entities in views. - if (isset($entity->{$property_id})) { - $entity_loaded = entity_load($entity_type, array($entity->{$property_id})); - $entity_loaded = array_pop($entity_loaded); - } - else { - $entity_loaded = $entity; - } - $title = token_replace($title, array($entity_token_type => $entity_loaded)); - $title = filter_xss($title, array('b', 'br', 'code', 'em', 'i', 'img', 'span', 'strong', 'sub', 'sup', 'tt', 'u')); + $title = filter_xss($title, array( + 'b', + 'br', + 'code', + 'em', + 'i', + 'img', + 'span', + 'strong', + 'sub', + 'sup', + 'tt', + 'u', + )); $item['html'] = TRUE; } - $item['title'] = empty($title) ? $item['display_url'] : $title; + $item['title'] = empty($title) && $title !== '0' ? $item['display_url'] : $title; if (!isset($item['attributes'])) { $item['attributes'] = array(); @@ -599,22 +692,38 @@ function _link_sanitize(&$item, $delta, &$field, $instance, &$entity) { // Handle "title" link attribute. if (!empty($item['attributes']['title']) && module_exists('token')) { - // Load the entity (necessary for entities in views). - if (isset($entity->{$property_id})) { - $entity_loaded = entity_load($entity_type, array($entity->{$property_id})); - $entity_loaded = array_pop($entity_loaded); + $text_tokens = token_scan($item['attributes']['title']); + if (!empty($text_tokens)) { + // Load the entity (necessary for entities in views). + if (isset($entity->{$property_id})) { + $entity_loaded = entity_load($entity_type, array($entity->{$property_id})); + $entity_loaded = array_pop($entity_loaded); + } + else { + $entity_loaded = $entity; + } + $item['attributes']['title'] = token_replace($item['attributes']['title'], array($entity_token_type => $entity_loaded)); } - else { - $entity_loaded = $entity; - } - $item['attributes']['title'] = token_replace($item['attributes']['title'], array($entity_token_type => $entity_loaded)); - $item['attributes']['title'] = filter_xss($item['attributes']['title'], array('b', 'br', 'code', 'em', 'i', 'img', 'span', 'strong', 'sub', 'sup', 'tt', 'u')); + $item['attributes']['title'] = filter_xss($item['attributes']['title'], array( + 'b', + 'br', + 'code', + 'em', + 'i', + 'img', + 'span', + 'strong', + 'sub', + 'sup', + 'tt', + 'u', + )); } // Handle attribute classes. if (!empty($item['attributes']['class'])) { $classes = explode(' ', $item['attributes']['class']); foreach ($classes as &$class) { - $class = drupal_html_class($class); + $class = drupal_clean_css_identifier($class); } $item['attributes']['class'] = implode(' ', $classes); } @@ -660,7 +769,8 @@ function _link_parse_url($url) { * Replaces the PHP parse_str() function. * * Because parse_str replaces the following characters in query parameters name - * in order to maintain compability with deprecated register_globals directive: + * in order to maintain compatibility with deprecated register_globals + * directive: * * - chr(32) ( ) (space) * - chr(46) (.) (dot) @@ -700,11 +810,21 @@ function link_theme() { 'link_formatter_link_plain' => array( 'variables' => array('element' => NULL, 'field' => NULL), ), + 'link_formatter_link_host' => array( + 'variables' => array('element' => NULL), + ), 'link_formatter_link_absolute' => array( 'variables' => array('element' => NULL, 'field' => NULL), ), 'link_formatter_link_domain' => array( - 'variables' => array('element' => NULL, 'display' => NULL, 'field' => NULL), + 'variables' => array( + 'element' => NULL, + 'display' => NULL, + 'field' => NULL, + ), + ), + 'link_formatter_link_no_protocol' => array( + 'variables' => array('element' => NULL, 'field' => NULL), ), 'link_formatter_link_title_plain' => array( 'variables' => array('element' => NULL, 'field' => NULL), @@ -791,7 +911,8 @@ function _link_default_attributes() { * Build the form element. When creating a form using FAPI #process, * note that $element['#value'] is already set. * - * The $fields array is in $complete_form['#field_info'][$element['#field_name']]. + * The $fields array is in + * $complete_form['#field_info'][$element['#field_name']]. */ function link_field_process($element, $form_state, $complete_form) { $instance = field_widget_instance($element, $form_state); @@ -803,7 +924,7 @@ function link_field_process($element, $form_state, $complete_form) { '#required' => ($element['#delta'] == 0 && $settings['url'] !== 'optional') ? $element['#required'] : FALSE, '#default_value' => isset($element['#value']['url']) ? $element['#value']['url'] : NULL, ); - if ($settings['title'] !== 'none' && $settings['title'] !== 'value') { + if (in_array($settings['title'], array('optional', 'required'))) { // Figure out the label of the title field. if (!empty($settings['title_label_use_field_label'])) { // Use the element label as the title field label. @@ -815,15 +936,31 @@ function link_field_process($element, $form_state, $complete_form) { $title_label = t('Title'); } + // Default value. + $title_maxlength = 128; + if (!empty($settings['title_maxlength'])) { + $title_maxlength = $settings['title_maxlength']; + } + $element['title'] = array( '#type' => 'textfield', - '#maxlength' => $settings['title_maxlength'], + '#maxlength' => $title_maxlength, '#title' => $title_label, - '#description' => t('The link title is limited to @maxlength characters maximum.', array('@maxlength' => $settings['title_maxlength'])), + '#description' => t('The link title is limited to @maxlength characters maximum.', array('@maxlength' => $title_maxlength)), '#required' => ($settings['title'] == 'required' && (($element['#delta'] == 0 && $element['#required']) || !empty($element['#value']['url']))) ? TRUE : FALSE, '#default_value' => isset($element['#value']['title']) ? $element['#value']['title'] : NULL, ); } + elseif ($settings['title'] == 'select') { + $options = drupal_map_assoc(array_filter(explode("\n", str_replace("\r", "\n", trim($settings['title_allowed_values']))))); + $element['title'] = array( + '#type' => 'select', + '#title' => t('Title'), + '#description' => t('Select the a title for this link.'), + '#default_value' => isset($element['#value']['title']) ? $element['#value']['title'] : NULL, + '#options' => $options, + ); + } // Initialize field attributes as an array if it is not an array yet. if (!is_array($settings['attributes'])) { @@ -859,8 +996,9 @@ function link_field_process($element, $form_state, $complete_form) { ); } - // If the title field is avaliable or there are field accepts multiple values - // then allow the individual field items display the required asterisk if needed. + // If the title field is available or there are field accepts multiple values + // then allow the individual field items display the required asterisk if + // needed. if (isset($element['title']) || isset($element['_weight'])) { // To prevent an extra required indicator, disable the required flag on the // base element since all the sub-fields are already required if desired. @@ -885,6 +1023,11 @@ function link_field_formatter_info() { 'field types' => array('link_field'), 'multiple values' => FIELD_BEHAVIOR_DEFAULT, ), + 'link_host' => array( + 'label' => t('Host, as plain text'), + 'field types' => array('link_field'), + 'multiple values' => FIELD_BEHAVIOR_DEFAULT, + ), 'link_url' => array( 'label' => t('URL, as link'), 'field types' => array('link_field'), @@ -908,6 +1051,11 @@ function link_field_formatter_info() { 'strip_www' => FALSE, ), ), + 'link_no_protocol' => array( + 'label' => t('URL with the protocol removed'), + 'field types' => array('link_field'), + 'multiple values' => FIELD_BEHAVIOR_DEFAULT, + ), 'link_short' => array( 'label' => t('Short, as link with title "Link"'), 'field types' => array('link_field'), @@ -947,8 +1095,9 @@ function link_field_formatter_settings_form($field, $instance, $view_mode, $form * Implements hook_field_formatter_settings_summary(). */ function link_field_formatter_settings_summary($field, $instance, $view_mode) { + $display = $instance['display'][$view_mode]; - $settings = $display['settings']; + if ($display['type'] == 'link_domain') { if ($display['settings']['strip_www']) { return t('Strip www. from domain'); @@ -993,7 +1142,7 @@ function theme_link_formatter_link_default($vars) { } // If only a title, display the title. elseif (!empty($vars['element']['title'])) { - return $link_options['html'] ? $vars['element']['title'] : check_plain($vars['element']['title']); + return !empty($link_options['html']) ? $vars['element']['title'] : check_plain($vars['element']['title']); } elseif (!empty($vars['element']['url'])) { return l($vars['element']['title'], $vars['element']['url'], $link_options); @@ -1015,6 +1164,14 @@ function theme_link_formatter_link_plain($vars) { return empty($vars['element']['url']) ? check_plain($vars['element']['title']) : url($vars['element']['url'], $link_options); } +/** + * Theme function for 'host' text field formatter. + */ +function theme_link_formatter_link_host($vars) { + $host = @parse_url($vars['element']['url']); + return isset($host['host']) ? check_plain($host['host']) : ''; +} + /** * Formats a link as an absolute URL. */ @@ -1037,11 +1194,27 @@ function theme_link_formatter_link_domain($vars) { return $vars['element']['url'] ? l($domain, $vars['element']['url'], $link_options) : ''; } +/** + * Formats a link without the http:// or https://. + */ +function theme_link_formatter_link_no_protocol($vars) { + $link_options = $vars['element']; + unset($link_options['title']); + unset($link_options['url']); + // We drop any scheme of the url. + $scheme = parse_url($vars['element']['url']); + $search = '/' . preg_quote($scheme['scheme'] . '://', '/') . '/'; + $replace = ''; + $display_url = preg_replace($search, $replace, $vars['element']['url'], 1); + + return $vars['element']['url'] ? l($display_url, $vars['element']['url'], $link_options) : ''; +} + /** * Formats a link's title as plain text. */ function theme_link_formatter_link_title_plain($vars) { - return empty($vars['element']['title']) ? '' : check_plain($vars['element']['title']); + return empty($vars['element']['title']) ? '' : check_plain(decode_entities($vars['element']['title'])); } /** @@ -1101,7 +1274,8 @@ function theme_link_formatter_link_separate($vars) { /** * Implements hook_token_list(). * - * @TODO: hook_token_list no longer exists - this should change to hook_token_info(). + * @TODO: hook_token_list no longer exists - this should change to + * hook_token_info(). */ function link_token_list($type = 'all') { if ($type === 'field' || $type === 'all') { @@ -1116,7 +1290,8 @@ function link_token_list($type = 'all') { /** * Implements hook_token_values(). * - * @TODO: hook_token_values no longer exists - this should change to hook_tokens(). + * @TODO: hook_token_values no longer exists - this should change to + * hook_tokens(). */ function link_token_values($type, $object = NULL) { if ($type === 'field') { @@ -1142,26 +1317,27 @@ function link_views_api() { /** * Forms a valid URL if possible from an entered address. - * + * * Trims whitespace and automatically adds an http:// to addresses without a - * protocol specified + * protocol specified. * * @param string $url * The url entered by the user. * @param string $protocol - * The protocol to be prepended to the url if one is not specified + * The protocol to be prepended to the url if one is not specified. */ function link_cleanup_url($url, $protocol = 'http') { $url = trim($url); - $type = link_validate_url($url); + $type = link_url_type($url); if ($type === LINK_EXTERNAL) { // Check if there is no protocol specified. $protocol_match = preg_match("/^([a-z0-9][a-z0-9\.\-_]*:\/\/)/i", $url); if (empty($protocol_match)) { - // But should there be? Add an automatic http:// if it starts with a domain name. - $LINK_DOMAINS = _link_domains(); - $domain_match = preg_match('/^(([a-z0-9]([a-z0-9\-_]*\.)+)(' . $LINK_DOMAINS . '|[a-z]{2}))/i', $url); + // But should there be? Add an automatic http:// if it starts with a + // domain name. + $link_domains = _link_domains(); + $domain_match = preg_match('/^(([a-z0-9]([a-z0-9\-_]*\.)+)(' . $link_domains . '|[a-z]{2}))/i', $url); if (!empty($domain_match)) { $url = $protocol . "://" . $url; } @@ -1173,109 +1349,207 @@ function link_cleanup_url($url, $protocol = 'http') { /** * Validates a URL. - * + * + * @param string $text + * Url to be validated. + * @param string $langcode + * An optional language code to look up the path in. + * + * @return bool + * True if a valid link, FALSE otherwise. + */ +function link_validate_url($text, $langcode = NULL) { + + $text = _link_clean_relative($text); + $text = link_cleanup_url($text); + $type = link_url_type($text); + + if ($type && ($type == LINK_INTERNAL || $type == LINK_EXTERNAL)) { + $flag = valid_url($text, TRUE); + if (!$flag) { + $normal_path = drupal_get_normal_path($text, $langcode); + $parsed_link = parse_url($normal_path, PHP_URL_PATH); + if ($normal_path != $parsed_link) { + $normal_path = $parsed_link; + } + $flag = drupal_valid_path($normal_path); + } + if (!$flag) { + $flag = file_exists($normal_path); + } + if (!$flag) { + $uri = file_build_uri($normal_path); + $flag = file_exists($uri); + } + } + else { + $flag = (bool) $type; + } + + return $flag; +} + +/** + * Cleaner of relatives urls. + * + * @param string $url + * The url to clean up the relative protocol. + */ +function _link_clean_relative($url) { + $check = substr($url, 0, 2); + if (isset($_SERVER['HTTPS']) && + ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) || + isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && + $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') { + $protocol = 'https://'; + } + else { + $protocol = 'http://'; + } + + if ($check == '//') { + $url = str_replace('//', $protocol, $url); + } + + return $url; +} + +/** + * Type check a URL. + * * Accepts all URLs following RFC 1738 standard for URL formation and all e-mail * addresses following the RFC 2368 standard for mailto address formation. * * @param string $text - * Url to be validated. - * + * Url to be checked. + * * @return mixed * Returns boolean FALSE if the URL is not valid. On success, returns one of * the LINK_(linktype) constants. */ -function link_validate_url($text) { +function link_url_type($text) { // @TODO Complete letters. - $LINK_ICHARS_DOMAIN = (string) html_entity_decode(implode("", array( - "æ", // æ - "Æ", // Æ + // @codingStandardsIgnoreStart + $link_ichars_domain = (string) html_entity_decode(implode("", array( + "¿", // ¿ "À", // À - "à", // à "Á", // Á - "á", // á "Â", //  - "â", // â - "å", // å - "Å", // Å - "ä", // ä + "Ã", // à "Ä", // Ä + "Å", // Å + "Æ", // Æ "Ç", // Ç - "ç", // ç - "Ð", // Ð - "ð", // ð "È", // È - "è", // è "É", // É - "é", // é "Ê", // Ê - "ê", // ê "Ë", // Ë - "ë", // ë + "Ì", // Ì + "Í", // Í "Î", // Î - "î", // î "Ï", // Ï - "ï", // ï - "ø", // ø - "Ø", // Ø - "ö", // ö - "Ö", // Ö + "Ð", // Ð + "Ñ", // Ñ + "Ò", // Ò + "Ó", // Ó "Ô", // Ô - "ô", // ô "Õ", // Õ + "Ö", // Ö + // × + "Ø", // Ø + "Ù", // Ù + "Ú", // Ú + "Û", // Û + "Ü", // Ü + "Ý", // Ý + "Þ", // Þ + // ß (see LINK_ICHARS) + "à", // à + "á", // á + "â", // â + "ã", // ã + "ä", // ä + "å", // å + "æ", // æ + "ç", // ç + "è", // è + "é", // é + "ê", // ê + "ë", // ë + "ì", // ì + "í", // í + "î", // î + "ï", // ï + "ð", // ð + "ñ", // ñ + "ò", // ò + "ó", // ó + "ô", // ô "õ", // õ + "ö", // ö + // ÷ + "ø", // ø + "ù", // ù + "ú", // ú + "û", // û + "ü", // ü + "ý", // ý + "þ", // þ + "ÿ", // ÿ "Œ", // Œ "œ", // œ - "ü", // ü - "Ü", // Ü - "Ù", // Ù - "ù", // ù - "Û", // Û - "û", // û "Ÿ", // Ÿ - "ÿ", // ÿ - "Ñ", // Ñ - "ñ", // ñ - "þ", // þ - "Þ", // Þ - "ý", // ý - "Ý", // Ý - "¿", // ¿ )), ENT_QUOTES, 'UTF-8'); + // @codingStandardsIgnoreEnd - $LINK_ICHARS = $LINK_ICHARS_DOMAIN . (string) html_entity_decode(implode("", array( - "ß", // ß - )), ENT_QUOTES, 'UTF-8'); - $allowed_protocols = variable_get('filter_allowed_protocols', array('http', 'https', 'ftp', 'news', 'nntp', 'telnet', 'mailto', 'irc', 'ssh', 'sftp', 'webcal')); - $LINK_DOMAINS = _link_domains(); + $link_ichars = $link_ichars_domain . (string) html_entity_decode(implode("", array( + // ß. + "ß", + )), ENT_QUOTES, 'UTF-8'); + $allowed_protocols = variable_get('filter_allowed_protocols', array( + 'http', + 'https', + 'ftp', + 'news', + 'nntp', + 'telnet', + 'mailto', + 'irc', + 'ssh', + 'sftp', + 'webcal', + )); + $link_domains = _link_domains(); - // Starting a parenthesis group with (?: means that it is grouped, but is not captured. + // Starting a parenthesis group with (?: means that it is grouped, but is not + // captured. $protocol = '((?:' . implode("|", $allowed_protocols) . '):\/\/)'; - $authentication = "(?:(?:(?:[\w\.\-\+!$&'\(\)*\+,;=" . $LINK_ICHARS . "]|%[0-9a-f]{2})+(?::(?:[\w" . $LINK_ICHARS . "\.\-\+%!$&'\(\)*\+,;=]|%[0-9a-f]{2})*)?)?@)"; - $domain = '(?:(?:[a-z0-9' . $LINK_ICHARS_DOMAIN . ']([a-z0-9' . $LINK_ICHARS_DOMAIN . '\-_\[\]])*)(\.(([a-z0-9' . $LINK_ICHARS_DOMAIN . '\-_\[\]])+\.)*(' . $LINK_DOMAINS . '|[a-z]{2}))?)'; + $authentication = "(?:(?:(?:[\w\.\-\+!$&'\(\)*\+,;=" . $link_ichars . "]|%[0-9a-f]{2})+(?::(?:[\w" . $link_ichars . "\.\-\+%!$&'\(\)*\+,;=]|%[0-9a-f]{2})*)?)?@)"; + $domain = '(?:(?:[a-z0-9' . $link_ichars_domain . ']([a-z0-9' . $link_ichars_domain . '\-_\[\]])*)(\.(([a-z0-9' . $link_ichars_domain . '\-_\[\]])+\.)*(' . $link_domains . '|[a-z]{2}))?)'; $ipv4 = '(?:[0-9]{1,3}(\.[0-9]{1,3}){3})'; $ipv6 = '(?:[0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7})'; $port = '(?::([0-9]{1,5}))'; - // Pattern specific to external links. $external_pattern = '/^' . $protocol . '?' . $authentication . '?(' . $domain . '|' . $ipv4 . '|' . $ipv6 . ' |localhost)' . $port . '?'; // Pattern specific to internal links. - $internal_pattern = "/^(?:[a-z0-9" . $LINK_ICHARS . "_\-+\[\] ]+)"; - $internal_pattern_file = "/^(?:[a-z0-9" . $LINK_ICHARS . "_\-+\[\]\. \/\(\)][a-z0-9" . $LINK_ICHARS . "_\-+\[\]\. \(\)][a-z0-9" . $LINK_ICHARS . "_\-+\[\]\. \/\(\)]+)$/i"; + $internal_pattern = "/^(?:[a-z0-9" . $link_ichars . "_\-+\[\] ]+)"; + $internal_pattern_file = "/^(?:[a-z0-9" . $link_ichars . "_\-+\[\]\. \/\(\)][a-z0-9" . $link_ichars . "_\-+\[\]\. \(\)][a-z0-9" . $link_ichars . "_\-+\[\]\. \/\(\)]+)$/i"; - $directories = "(?:\/[a-z0-9" . $LINK_ICHARS . "_\-\.~+%=&,$'#!():;*@\[\]]*)*"; + $directories = "(?:\/[a-z0-9" . $link_ichars . "_\-\.~+%=&,$'#!():;*@\[\]]*)*"; // Yes, four backslashes == a single backslash. - $query = "(?:\/?\?([?a-z0-9" . $LINK_ICHARS . "+_|\-\.~\/\\\\%=&,$'!():;*@\[\]{} ]*))"; - $anchor = "(?:#[a-z0-9" . $LINK_ICHARS . "_\-\.~+%=&,$'():;*@\[\]\/\?]*)"; + $query = "(?:\/?\?([?a-z0-9" . $link_ichars . "+_|\-\.~\/\\\\%=&,$'!():;*@\[\]{} ]*))"; + $anchor = "(?:#[a-z0-9" . $link_ichars . "_\-\.~+%=&,$'():;*@\[\]\/\?]*)"; // The rest of the path for a standard URL. + // @codingStandardsIgnoreLine $end = $directories . '?' . $query . '?' . $anchor . '?' . '$/i'; $message_id = '[^@].*@' . $domain; $newsgroup_name = '(?:[0-9a-z+-]*\.)*[0-9a-z+-]*'; $news_pattern = '/^news:(' . $newsgroup_name . '|' . $message_id . ')$/i'; - $user = '[a-zA-Z0-9' . $LINK_ICHARS . '_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\'\[\]]+'; + $user = '[a-zA-Z0-9' . $link_ichars . '_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\'\[\]]+'; $email_pattern = '/^mailto:' . $user . '@' . '(?:' . $domain . '|' . $ipv4 . '|' . $ipv6 . '|localhost)' . $query . '?$/'; if (strpos($text, '') === 0) { @@ -1290,6 +1564,9 @@ function link_validate_url($text) { if (preg_match($internal_pattern . $end, $text)) { return LINK_INTERNAL; } + if (drupal_valid_path($text) && url_is_external($text) == FALSE) { + return LINK_INTERNAL; + } if (preg_match($external_pattern . $end, $text)) { return LINK_EXTERNAL; } @@ -1301,11 +1578,16 @@ function link_validate_url($text) { } /** - * Returns the list of allowed domains, including domains added by admins via variable_set/$config. + * Returns the list of allowed domains. + * + * If the variable link_allowed_domains is set, restrict allowed domains to the + * strings in that array. If the variable link_allowed_domains is not set, allow + * all domains between 2 and 63 characters in length. + * See https://tools.ietf.org/html/rfc1034. */ function _link_domains() { - $link_extra_domains = variable_get('link_extra_domains', array()); - return empty($link_extra_domains) ? LINK_DOMAINS : LINK_DOMAINS . '|' . implode('|', $link_extra_domains); + $link_allowed_domains = variable_get('link_allowed_domains', array()); + return empty($link_allowed_domains) ? '[a-z][a-z0-9-]{1,62}' : implode('|', $link_allowed_domains); } /** @@ -1316,7 +1598,15 @@ function link_content_migrate_field_alter(&$field_value, $instance_value) { // Adjust the field type. $field_value['type'] = 'link_field'; // Remove settings that are now on the instance. - foreach (array('attributes', 'display', 'url', 'title', 'title_value', 'enable_tokens', 'validate_url') as $setting) { + foreach (array( + 'attributes', + 'display', + 'url', + 'title', + 'title_value', + 'enable_tokens', + 'validate_url', + ) as $setting) { unset($field_value['settings'][$setting]); } } @@ -1330,7 +1620,15 @@ function link_content_migrate_field_alter(&$field_value, $instance_value) { function link_content_migrate_instance_alter(&$instance_value, $field_value) { if ($field_value['type'] == 'link') { // Grab settings that were previously on the field. - foreach (array('attributes', 'display', 'url', 'title', 'title_value', 'enable_tokens', 'validate_url') as $setting) { + foreach (array( + 'attributes', + 'display', + 'url', + 'title', + 'title_value', + 'enable_tokens', + 'validate_url', + ) as $setting) { if (isset($field_value['settings'][$setting])) { $instance_value['settings'][$setting] = $field_value['settings'][$setting]; } @@ -1341,7 +1639,15 @@ function link_content_migrate_instance_alter(&$instance_value, $field_value) { } // Adjust formatter types. foreach ($instance_value['display'] as $context => $settings) { - if (in_array($settings['type'], array('default', 'title_plain', 'url', 'plain', 'short', 'label', 'separate'))) { + if (in_array($settings['type'], array( + 'default', + 'title_plain', + 'url', + 'plain', + 'short', + 'label', + 'separate', + ))) { $instance_value['display'][$context]['type'] = 'link_' . $settings['type']; } } @@ -1357,7 +1663,7 @@ function link_field_settings_form() { /** * Additional callback to adapt the property info of link fields. - * + * * @see entity_metadata_field_entity_property_info() */ function link_field_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) { @@ -1385,7 +1691,7 @@ function link_field_property_info_callback(&$info, $entity_type, $field, $instan * @see link_field_property_info_callback() */ function link_field_item_create() { - return array('title' => NULL, 'url' => NULL); + return array('title' => NULL, 'url' => NULL, 'display_url' => NULL); } /** @@ -1398,7 +1704,7 @@ function link_field_item_property_info() { 'setter callback' => 'entity_property_verbatim_set', ); $properties['url'] = array( - 'type' => 'uri', + 'type' => 'text', 'label' => t('The URL of the link.'), 'setter callback' => 'entity_property_verbatim_set', ); @@ -1408,6 +1714,11 @@ function link_field_item_property_info() { 'setter callback' => 'entity_property_verbatim_set', 'getter callback' => 'link_attribute_property_get', ); + $properties['display_url'] = array( + 'type' => 'uri', + 'label' => t('The full URL of the link.'), + 'setter callback' => 'entity_property_verbatim_set', + ); return $properties; } @@ -1422,7 +1733,7 @@ function link_attribute_property_get($data, array $options, $name, $type, $info) * Implements hook_field_update_instance(). */ function link_field_update_instance($instance, $prior_instance) { - if (function_exists('i18n_string_update') && $instance['widget']['type'] == 'link_field' && $prior_instance['settings']['title_value'] != $instance['settings']['title_value']) { + if (function_exists('i18n_string_update') && isset($instance['widget']) && $instance['widget']['type'] == 'link_field' && $prior_instance['settings']['title_value'] != $instance['settings']['title_value']) { $i18n_string_name = "field:{$instance['field_name']}:{$instance['bundle']}:title_value"; i18n_string_update($i18n_string_name, $instance['settings']['title_value']); } diff --git a/sites/all/modules/contrib/fields/link/tests/link.attribute.test b/sites/all/modules/contrib/fields/link/tests/link.attribute.test index 603847e..f66a0e6 100644 --- a/sites/all/modules/contrib/fields/link/tests/link.attribute.test +++ b/sites/all/modules/contrib/fields/link/tests/link.attribute.test @@ -5,7 +5,11 @@ * Basic simpletests to test options on link module. */ +/** + * Attribute Crud Test. + */ class LinkAttributeCrudTest extends DrupalWebTestCase { + private $zebra; protected $permissions = array( @@ -19,6 +23,9 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { 'access administration pages', ); + /** + * Get Info. + */ public static function getInfo() { return array( 'name' => 'Link Attribute Tests', @@ -27,14 +34,23 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { ); } - function setup() { + /** + * Setup. + */ + public function setup() { parent::setup('field_ui', 'link'); $this->zebra = 0; // Create and login user. - $this->web_user = $this->drupalCreateUser(array('administer content types')); + $this->web_user = $this->drupalCreateUser(array( + 'administer content types', + 'administer fields', + )); $this->drupalLogin($this->web_user); } + /** + * Create Link. + */ protected function createLink($url, $title, $attributes = array()) { return array( 'url' => $url, @@ -43,20 +59,26 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { ); } + /** + * Assert Link On Node. + */ protected function assertLinkOnNode($field_name, $link_value, $message = '', $group = 'Other') { $this->zebra++; $zebra_string = ($this->zebra % 2 == 0) ? 'even' : 'odd'; $cssFieldLocator = 'field-' . str_replace('_', '-', $field_name); $this->assertPattern('@@is', - $message, - $group); + $message, + $group); } /** - * A simple test that just creates a new node type, adds a link field to it, creates a new node of that type, and makes sure - * that the node is being displayed. + * Test Basic. + * + * A simple test that just creates a new node type, adds a link field to it, + * creates a new node of that type, and makes sure that the node is being + * displayed. */ - function testBasic() { + public function testBasic() { $content_type_friendly = $this->randomName(20); $content_type_machine = strtolower($this->randomName(10)); $title = $this->randomName(20); @@ -76,7 +98,7 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { // Now add a singleton field. $single_field_name_friendly = $this->randomName(20); $single_field_name_machine = strtolower($this->randomName(10)); - $single_field_name = 'field_' . $single_field_name_machine; + $edit = array( 'fields[_add_new_field][label]' => $single_field_name_friendly, 'fields[_add_new_field][field_name]' => $single_field_name_machine, @@ -101,7 +123,7 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { $this->assertTrue($type_exists, 'The new content type has been created in the database.'); $permission = 'create ' . $content_type_machine . ' content'; - $permission_edit = 'edit ' . $content_type_machine . ' content'; + // Reset the permissions cache. $this->checkPermissions(array($permission), TRUE); @@ -122,7 +144,10 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { ); $this->drupalPost(NULL, $edit, t('Save')); - $this->assertText(t('@content_type_friendly @title has been created', array('@content_type_friendly' => $content_type_friendly, '@title' => $title))); + $this->assertText(t('@content_type_friendly @title has been created', array( + '@content_type_friendly' => $content_type_friendly, + '@title' => $title, + ))); $this->drupalGet('node/add/' . $content_type_machine); @@ -135,12 +160,18 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { // Now we can fill in the second item in the multivalue field and save. $this->drupalPost(NULL, $edit, t('Save')); - $this->assertText(t('@content_type_friendly @title has been created', array('@content_type_friendly' => $content_type_friendly, '@title' => $title))); + $this->assertText(t('@content_type_friendly @title has been created', array( + '@content_type_friendly' => $content_type_friendly, + '@title' => $title, + ))); $this->assertText('Display'); $this->assertLinkByHref('http://www.example.com'); } + /** + * Create Simple Link Field. + */ protected function createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine) { $this->drupalGet('admin/structure/types/manage/' . $content_type_machine . '/fields'); $edit = array( @@ -166,9 +197,11 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { $this->assertTrue($type_exists, 'The new content type has been created in the database.'); } + /** + * Create Node Type User. + */ protected function createNodeTypeUser($content_type_machine) { $permission = 'create ' . $content_type_machine . ' content'; - $permission_edit = 'edit ' . $content_type_machine . ' content'; // Reset the permissions cache. $this->checkPermissions(array($permission), TRUE); @@ -179,6 +212,9 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { $this->drupalLogin($this->web_user); } + /** + * Create Node For Testing. + */ protected function createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $title, $url, $node_title = '') { $this->drupalGet('node/add/' . $content_type_machine); @@ -196,14 +232,17 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { } $this->drupalPost(NULL, $edit, t('Save')); - $this->assertText(t('@content_type_friendly @title has been created', array('@content_type_friendly' => $content_type_friendly, '@title' => $node_title))); + $this->assertText(t('@content_type_friendly @title has been created', array( + '@content_type_friendly' => $content_type_friendly, + '@title' => $node_title, + ))); } /** * Test the link_plain formatter and it's output. */ - function testFormatterPlain() { + public function testFormatterPlain() { $content_type_friendly = $this->randomName(20); $content_type_machine = strtolower($this->randomName(10)); @@ -215,7 +254,7 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { // Now add a singleton field. $single_field_name_friendly = $this->randomName(20); $single_field_name_machine = strtolower($this->randomName(10)); - //$single_field_name = 'field_'. $single_field_name_machine; + // $single_field_name = 'field_'. $single_field_name_machine;. $this->createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine); // Okay, now we want to make sure this display is changed: @@ -227,7 +266,7 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { $this->drupalPost(NULL, $edit, t('Save')); $this->createNodeTypeUser($content_type_machine); - + $link_tests = array( 'plain' => array( 'text' => 'Display', @@ -243,18 +282,21 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { ), ); - foreach ($link_tests as $key => $link_test) { + foreach ($link_tests as $link_test) { $link_text = $link_test['text']; $link_url = $link_test['url']; $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - + $this->assertText($link_url); $this->assertNoText($link_text); $this->assertNoLinkByHref($link_url); } } - function testFormatterURL() { + /** + * Formatter Host. + */ + public function testFormatterHost() { $content_type_friendly = $this->randomName(20); $content_type_machine = strtolower($this->randomName(10)); @@ -266,7 +308,47 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { // Now add a singleton field. $single_field_name_friendly = $this->randomName(20); $single_field_name_machine = strtolower($this->randomName(10)); - //$single_field_name = 'field_'. $single_field_name_machine; + // $single_field_name = 'field_'. $single_field_name_machine;. + $this->createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine); + + // Okay, now we want to make sure this display is changed: + $this->drupalGet('admin/structure/types/manage/' . $content_type_machine . '/display'); + $edit = array( + 'fields[field_' . $single_field_name_machine . '][label]' => 'above', + 'fields[field_' . $single_field_name_machine . '][type]' => 'link_host', + ); + $this->drupalPost(NULL, $edit, t('Save')); + + $this->createNodeTypeUser($content_type_machine); + + $link_text = 'Display'; + $link_url = 'http://www.example.com/'; + $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); + + $this->assertText('www.example.com'); + $this->assertNoText($link_text); + $this->assertNoLinkByHref($link_url); + } + + /** + * Formatter URL. + * + * @codingStandardsIgnoreStart + */ + public function testFormatterURL() { + // @codingStandardsIgnoreEnd + $content_type_friendly = $this->randomName(20); + $content_type_machine = strtolower($this->randomName(10)); + + $this->drupalCreateContentType(array( + 'type' => $content_type_machine, + 'name' => $content_type_friendly, + )); + + // Now add a singleton field. + $single_field_name_friendly = $this->randomName(20); + $single_field_name_machine = strtolower($this->randomName(10)); + // $single_field_name = 'field_'. $single_field_name_machine;. $this->createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine); // Okay, now we want to make sure this display is changed: @@ -278,7 +360,7 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { $this->drupalPost(NULL, $edit, t('Save')); $this->createNodeTypeUser($content_type_machine); - + $link_tests = array( 'plain' => array( 'text' => 'Display', @@ -294,17 +376,20 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { ), ); - foreach ($link_tests as $key => $link_test) { + foreach ($link_tests as $link_test) { $link_text = $link_test['text']; $link_url = $link_test['url']; $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - + $this->assertNoText($link_text); $this->assertLinkByHref($link_url); } } - function testFormatterShort() { + /** + * Formatter Short. + */ + public function testFormatterShort() { $content_type_friendly = $this->randomName(20); $content_type_machine = strtolower($this->randomName(10)); @@ -316,7 +401,7 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { // Now add a singleton field. $single_field_name_friendly = $this->randomName(20); $single_field_name_machine = strtolower($this->randomName(10)); - //$single_field_name = 'field_'. $single_field_name_machine; + // $single_field_name = 'field_'. $single_field_name_machine;. $this->createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine); // Okay, now we want to make sure this display is changed: @@ -344,18 +429,21 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { ), ); - foreach ($link_tests as $key => $link_test) { + foreach ($link_tests as $link_test) { $link_text = $link_test['text']; $link_url = $link_test['url']; $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - + $this->assertText('Link'); $this->assertNoText($link_text); $this->assertLinkByHref($link_url); } } - function testFormatterLabel() { + /** + * Formatter Label. + */ + public function testFormatterLabel() { $content_type_friendly = $this->randomName(20); $content_type_machine = strtolower($this->randomName(10)); @@ -367,7 +455,7 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { // Now add a singleton field. $single_field_name_friendly = $this->randomName(20); $single_field_name_machine = strtolower($this->randomName(10)); - //$single_field_name = 'field_'. $single_field_name_machine; + // $single_field_name = 'field_'. $single_field_name_machine;. $this->createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine); // Okay, now we want to make sure this display is changed: @@ -395,18 +483,21 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { ), ); - foreach ($link_tests as $key => $link_test) { + foreach ($link_tests as $link_test) { $link_text = $link_test['text']; - $link_url = $link_test['url']; + $link_url = $link_test['url']; $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - + $this->assertNoText($link_text); $this->assertText($single_field_name_friendly); $this->assertLinkByHref($link_url); } } - function testFormatterSeparate() { + /** + * Formatter Separate. + */ + public function testFormatterSeparate() { $content_type_friendly = $this->randomName(20); $content_type_machine = strtolower($this->randomName(10)); @@ -418,7 +509,7 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { // Now add a singleton field. $single_field_name_friendly = $this->randomName(20); $single_field_name_machine = strtolower($this->randomName(10)); - //$single_field_name = 'field_'. $single_field_name_machine; + // $single_field_name = 'field_'. $single_field_name_machine;. $this->createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine); // Okay, now we want to make sure this display is changed: @@ -447,32 +538,35 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { ), ); - foreach ($link_tests as $key => $link_test) { + foreach ($link_tests as $link_test) { $link_text = $link_test['text']; $link_url = $link_test['url']; $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - + $this->assertText($link_text); $this->assertLink($plain_url); $this->assertLinkByHref($link_url); } } - - function testFormatterPlainTitle() { + + /** + * Formatter Plain Title. + */ + public function testFormatterPlainTitle() { $content_type_friendly = $this->randomName(20); $content_type_machine = strtolower($this->randomName(10)); - + $this->drupalCreateContentType(array( 'type' => $content_type_machine, 'name' => $content_type_friendly, )); - + // Now add a singleton field. $single_field_name_friendly = $this->randomName(20); $single_field_name_machine = strtolower($this->randomName(10)); - //$single_field_name = 'field_'. $single_field_name_machine; + // $single_field_name = 'field_'. $single_field_name_machine;. $this->createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine); - + // Okay, now we want to make sure this display is changed: $this->drupalGet('admin/structure/types/manage/' . $content_type_machine . '/display'); $edit = array( @@ -480,15 +574,16 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { 'fields[field_' . $single_field_name_machine . '][type]' => 'link_title_plain', ); $this->drupalPost(NULL, $edit, t('Save')); - + $this->createNodeTypeUser($content_type_machine); - + $link_text = 'Display'; $link_url = 'http://www.example.com/'; $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - + $this->assertText($link_text); $this->assertNoText($link_url); $this->assertNoLinkByHref($link_url); } + } diff --git a/sites/all/modules/contrib/fields/link/tests/link.crud.test b/sites/all/modules/contrib/fields/link/tests/link.crud.test index e9b7db4..6bc3867 100644 --- a/sites/all/modules/contrib/fields/link/tests/link.crud.test +++ b/sites/all/modules/contrib/fields/link/tests/link.crud.test @@ -2,11 +2,20 @@ /** * @file - * Basic CRUD simpletests for the link module, based off of content.crud.test in CCK. + * File for Crud Tests. + * + * Basic CRUD simpletests for the link module, based off of content.crud.test in + * CCK. */ +/** + * Content Crud. + */ class LinkContentCrudTest extends DrupalWebTestCase { + /** + * Get Info. + */ public static function getInfo() { return array( 'name' => 'Link CRUD - Basic API tests', @@ -15,21 +24,31 @@ class LinkContentCrudTest extends DrupalWebTestCase { ); } - function setUp() { + /** + * Setup. + */ + public function setUp() { parent::setUp('field_ui', 'link'); } /** - * All we're doing here is creating a content type, creating a simple link field - * on that content type. + * Create Field API. + * + * All we're doing here is creating a content type, creating a simple link + * field on that content type. + * + * @codingStandardsIgnoreStart */ - function testLinkCreateFieldAPI() { + public function testLinkCreateFieldAPI() { + // @codingStandardsIgnoreEnd $content_type_friendly = $this->randomName(20); $content_type_machine = strtolower($this->randomName(10)); - $title = $this->randomName(20); // Create and login user. - $this->web_user = $this->drupalCreateUser(array('administer content types')); + $this->web_user = $this->drupalCreateUser(array( + 'administer content types', + 'administer fields', + )); $this->drupalLogin($this->web_user); $this->drupalGet('admin/structure/types'); @@ -69,4 +88,5 @@ class LinkContentCrudTest extends DrupalWebTestCase { $type_exists = db_query('SELECT 1 FROM {node_type} WHERE type = :type', array(':type' => $content_type_machine))->fetchField(); $this->assertTrue($type_exists, 'The new content type has been created in the database.'); } + } diff --git a/sites/all/modules/contrib/fields/link/tests/link.crud_browser.test b/sites/all/modules/contrib/fields/link/tests/link.crud_browser.test index c90e86d..dc21857 100644 --- a/sites/all/modules/contrib/fields/link/tests/link.crud_browser.test +++ b/sites/all/modules/contrib/fields/link/tests/link.crud_browser.test @@ -6,25 +6,28 @@ */ /** - * Testing that users can not input bad URLs or labels + * Testing that users can not input bad URLs or labels. */ class LinkUITest extends DrupalWebTestcase { /** - * Link supposed to be good + * Link supposed to be good. */ const LINK_INPUT_TYPE_GOOD = 0; /** - * Link supposed to have a bad title + * Link supposed to have a bad title. */ const LINK_INPUT_TYPE_BAD_TITLE = 1; /** - * Link supposed to have a bad URL + * Link supposed to have a bad URL. */ const LINK_INPUT_TYPE_BAD_URL = 2; + /** + * Get Info. + */ public static function getInfo() { return array( 'name' => 'Link CRUD - browser test', @@ -33,26 +36,30 @@ class LinkUITest extends DrupalWebTestcase { ); } - function setUp() { + /** + * Setup. + */ + public function setUp() { parent::setUp('field_ui', 'link'); } /** * Creates a link field for the "page" type and creates a page with a link. */ - function testLinkCreate() { - //libxml_use_internal_errors(true); + public function testLinkCreate() { + // libxml_use_internal_errors(true); $this->web_user = $this->drupalCreateUser(array( 'administer content types', + 'administer fields', 'administer nodes', 'administer filters', 'access content', 'create page content', - 'access administration pages' + 'access administration pages', )); $this->drupalLogin($this->web_user); - // create field + // Create field. $name = strtolower($this->randomName()); $edit = array( 'fields[_add_new_field][label]' => $name, @@ -72,8 +79,8 @@ class LinkUITest extends DrupalWebTestcase { $permission = 'create page content'; $this->checkPermissions(array($permission), TRUE); - // create page form - //$this->drupalGet('node/add'); + // Create page form + // $this->drupalGet('node/add');. $this->drupalGet('node/add/page'); $field_name = 'field_' . $name; $this->assertField('edit-field-' . $name . '-und-0-title', 'Title found'); @@ -84,37 +91,37 @@ class LinkUITest extends DrupalWebTestcase { 'href' => 'http://example.com/' . $this->randomName(), 'label' => $this->randomName(), 'msg' => 'Link found', - 'type' => self::LINK_INPUT_TYPE_GOOD + 'type' => self::LINK_INPUT_TYPE_GOOD, ), array( 'href' => 'http://example.com/' . $this->randomName(), 'label' => $this->randomName() . '', 'msg' => 'js label', - 'type' => self::LINK_INPUT_TYPE_BAD_TITLE + 'type' => self::LINK_INPUT_TYPE_BAD_TITLE, ), array( 'href' => 'http://example.com/' . $this->randomName(), 'label' => $this->randomName() . '', 'msg' => 'js label', - 'type' => self::LINK_INPUT_TYPE_BAD_TITLE + 'type' => self::LINK_INPUT_TYPE_BAD_TITLE, ), array( 'href' => 'http://example.com/' . $this->randomName(), 'label' => $this->randomName() . '" onmouseover="alert(\'hi\')', 'msg' => 'js label', - 'type' => self::LINK_INPUT_TYPE_BAD_TITLE + 'type' => self::LINK_INPUT_TYPE_BAD_TITLE, ), array( 'href' => 'http://example.com/' . $this->randomName(), 'label' => $this->randomName() . '\' onmouseover="alert(\'hi\')', 'msg' => 'js label', - 'type' => self::LINK_INPUT_TYPE_BAD_TITLE + 'type' => self::LINK_INPUT_TYPE_BAD_TITLE, ), array( 'href' => 'javascript:alert("http://example.com/' . $this->randomName() . '")', 'label' => $this->randomName(), 'msg' => 'js url', - 'type' => self::LINK_INPUT_TYPE_BAD_URL + 'type' => self::LINK_INPUT_TYPE_BAD_URL, ), array( 'href' => 'http://ecs-es.kelkoo.es/ctl/go/sitesearchGo?.ts=1338833010331&.sig=qP9GXeEFH6syBzwmzYkxmsvp1EI-', @@ -143,23 +150,26 @@ class LinkUITest extends DrupalWebTestcase { ); $this->drupalPost(NULL, $edit, t('Save')); if ($input['type'] == self::LINK_INPUT_TYPE_BAD_URL) { - $this->assertRaw(t('The value %value provided for %field is not a valid URL.', array('%field' => $name, '%value' => trim($input['href']))), 'Not a valid URL: ' . $input['href']); + $this->assertRaw(t('The value %value provided for %field is not a valid URL.', array( + '%field' => $name, + '%value' => trim($input['href']), + )), 'Not a valid URL: ' . $input['href']); continue; } else { $this->assertRaw(' ' . t('has been created.', - array('@type' => 'Basic Page', '%title' => $edit['title'])), - 'Page created: ' . $input['href']); + array('@type' => 'Basic Page', '%title' => $edit['title'])), + 'Page created: ' . $input['href']); } $url = $this->getUrl(); - // change to Anonymous user. + // Change to Anonymous user. $this->drupalLogout(); $this->drupalGet($url); - //debug($this); - // If simpletest starts using something to override the error system, this will flag - // us and let us know it's broken. + // debug($this); + // If simpletest starts using something to override the error system, this + // will flag us and let us know it's broken. $this->assertFalse(libxml_use_internal_errors(TRUE)); if (isset($input['expected_href'])) { $path = '//a[@href="' . $input['expected_href'] . '" and text()="' . $input['label'] . '"]'; @@ -171,18 +181,27 @@ class LinkUITest extends DrupalWebTestcase { libxml_use_internal_errors(FALSE); $this->assertIdentical(isset($elements[0]), $input['type'] == self::LINK_INPUT_TYPE_GOOD, $input['msg']); } - //libxml_use_internal_errors(FALSE); + // libxml_use_internal_errors(FALSE); } /** + * Static Link Create. + * * Testing that if you use in a static title for your link, that the * title actually displays . */ - function testStaticLinkCreate() { - $this->web_user = $this->drupalCreateUser(array('administer content types', 'access content', 'create page content')); + public function testStaticLinkCreate() { + + $this->web_user = $this->drupalCreateUser(array( + 'administer content types', + 'administer fields', + 'access content', + 'create page content', + )); + $this->drupalLogin($this->web_user); - // create field + // Create field. $name = strtolower($this->randomName()); $field_name = 'field_' . $name; $edit = array( @@ -195,17 +214,18 @@ class LinkUITest extends DrupalWebTestcase { $this->drupalPost(NULL, array(), t('Save field settings')); $this->drupalPost(NULL, array( 'instance[settings][title]' => 'value', - 'instance[settings][title_value]' => '' . $name . ''), t('Save settings')); + 'instance[settings][title_value]' => '' . $name . '', + ), t('Save settings')); // Is field created? $this->assertRaw(t('Saved %label configuration', array('%label' => $name)), 'Field added'); - // create page form + // Create page form. $this->drupalGet('node/add/page'); $this->assertField($field_name . '[und][0][url]', 'URL found'); $input = array( - 'href' => 'http://example.com/' . $this->randomName() + 'href' => 'http://example.com/' . $this->randomName(), ); $edit = array( @@ -216,21 +236,32 @@ class LinkUITest extends DrupalWebTestcase { $url = $this->getUrl(); - // change to anonymous user + // Change to anonymous user. $this->drupalLogout(); $this->drupalGet($url); $this->assertRaw(l('' . $name . '', $input['href'], array('html' => TRUE))); } - + /** - * Testing that if you have the title but no url, the title is not sanitized twice. + * CRUD Title Only Title No Link. + * + * Testing that if you have the title but no url, the title is not sanitized + * twice. + * + * @codingStandardsIgnoreStart */ - function testCRUDTitleOnlyTitleNoLink() { - $this->web_user = $this->drupalCreateUser(array('administer content types', 'access content', 'create page content')); + public function testCRUDTitleOnlyTitleNoLink() { + // @codingStandardsIgnoreEnd + $this->web_user = $this->drupalCreateUser(array( + 'administer content types', + 'administer fields', + 'access content', + 'create page content', + )); $this->drupalLogin($this->web_user); - // create field + // Create field. $name = strtolower($this->randomName()); $field_name = 'field_' . $name; $edit = array( @@ -247,8 +278,8 @@ class LinkUITest extends DrupalWebTestcase { // Is field created? $this->assertRaw(t('Saved %label configuration', array('%label' => $name)), 'Field added'); - - // create page form + + // Create page form. $this->drupalGet('node/add/page'); $this->assertField($field_name . '[und][0][url]', 'URL found'); @@ -265,8 +296,8 @@ class LinkUITest extends DrupalWebTestcase { $this->drupalPost(NULL, $edit, t('Save')); $url = $this->getUrl(); - - // change to anonymous user + + // Change to anonymous user. $this->drupalLogout(); $this->drupalGet($url); @@ -274,14 +305,26 @@ class LinkUITest extends DrupalWebTestcase { } /** - * If we're creating a new field and just hit 'save' on the default options, we want to make - * sure they are set to the expected results. + * CRUD Create Field Defaults. + * + * If we're creating a new field and just hit 'save' on the default options, + * we want to make sure they are set to the expected results. + * + * @codingStandardsIgnoreStart */ - function testCRUDCreateFieldDefaults() { - $this->web_user = $this->drupalCreateUser(array('administer content types', 'access content', 'create page content')); + public function testCRUDCreateFieldDefaults() { + // @codingStandardsIgnoreEnd + + $this->web_user = $this->drupalCreateUser(array( + 'administer content types', + 'administer fields', + 'access content', + 'create page content', + )); + $this->drupalLogin($this->web_user); - // create field + // Create field. $name = strtolower($this->randomName()); $edit = array( 'fields[_add_new_field][label]' => $name, @@ -312,4 +355,169 @@ class LinkUITest extends DrupalWebTestcase { $this->assertFalse($instance['settings']['attributes']['class'], 'By default, no class should be set.'); $this->assertFalse($instance['settings']['title_value'], 'By default, no title should be set.'); } + + /** + * CRUD Create Field With Class. + * + * If we're creating a new field and just hit 'save' on the default options, + * we want to make sure they are set to the expected results. + * + * @codingStandardsIgnoreStart + */ + public function testCRUDCreateFieldWithClass() { + // @codingStandardsIgnoreEnd + $this->web_user = $this->drupalCreateUser(array( + 'administer content types', + 'administer fields', + 'access content', + 'create page content', + )); + $this->drupalLogin($this->web_user); + + // Create field. + $name = strtolower($this->randomName()); + $edit = array( + 'fields[_add_new_field][label]' => $name, + 'fields[_add_new_field][field_name]' => $name, + 'fields[_add_new_field][type]' => 'link_field', + 'fields[_add_new_field][widget_type]' => 'link_field', + ); + $this->drupalPost('admin/structure/types/manage/page/fields', $edit, t('Save')); + + $this->drupalPost(NULL, array(), t('Save field settings')); + $link_class_name = 'basic-link-' . strtolower($this->randomName()); + $edit = array( + 'instance[settings][attributes][class]' => $link_class_name, + ); + $this->drupalPost(NULL, $edit, t('Save settings')); + + // Is field created? + $this->assertRaw(t('Saved %label configuration', array('%label' => $name)), 'Field added'); + node_types_rebuild(); + menu_rebuild(); + + _field_info_collate_fields(TRUE); + $instances = field_info_instances('node', 'page'); + + $instance = $instances['field_' . $name]; + $this->assertFalse($instance['required'], 'Make sure field is not required.'); + $this->assertEqual($instance['settings']['title'], 'optional', 'Title should be optional by default.'); + $this->assertTrue($instance['settings']['validate_url'], 'Make sure validation is on.'); + $this->assertTrue($instance['settings']['enable_tokens'], 'Enable Tokens should be on by default.'); + $this->assertEqual($instance['settings']['display']['url_cutoff'], 80, 'Url cutoff should be at 80 characters.'); + $this->assertEqual($instance['settings']['attributes']['target'], 'default', 'Target should be "default"'); + $this->assertFalse($instance['settings']['attributes']['rel'], 'Rel should be blank by default.'); + $this->assertEqual($instance['settings']['attributes']['class'], $link_class_name, 'One class should be set.'); + $this->assertFalse($instance['settings']['title_value'], 'By default, no title should be set.'); + + // Now, let's create a node with this field and make sure the link shows up: + // create page form. + $field_name = 'field_' . $name; + $this->drupalGet('node/add/page'); + $this->assertField($field_name . '[und][0][url]', 'URL found'); + + $input = array( + 'title' => 'This & That', + 'href' => 'http://www.example.com/', + ); + + $edit = array( + 'title' => $field_name, + $field_name . '[und][0][title]' => $input['title'], + $field_name . '[und][0][url]' => $input['href'], + ); + $this->drupalPost(NULL, $edit, t('Save')); + + $url = $this->getUrl(); + + // Change to anonymous user. + $this->drupalLogout(); + $this->drupalGet($url); + + $this->assertRaw('This & That'); + $this->assertPattern('|class\s?=\s?"' . $link_class_name . '"|', "Class $link_class_name exists on page."); + } + + /** + * CRUD Create Field With Two Classes. + * + * If we're creating a new field and just hit 'save' on the default options, + * we want to make sure they are set to the expected results. + * + * @codingStandardsIgnoreStart + */ + public function testCRUDCreateFieldWithTwoClasses() { + // @codingStandardsIgnoreEnd + $this->web_user = $this->drupalCreateUser(array( + 'administer content types', + 'administer fields', + 'access content', + 'create page content', + )); + $this->drupalLogin($this->web_user); + + // Create field. + $name = strtolower($this->randomName()); + $edit = array( + 'fields[_add_new_field][label]' => $name, + 'fields[_add_new_field][field_name]' => $name, + 'fields[_add_new_field][type]' => 'link_field', + 'fields[_add_new_field][widget_type]' => 'link_field', + ); + $this->drupalPost('admin/structure/types/manage/page/fields', $edit, t('Save')); + + $this->drupalPost(NULL, array(), t('Save field settings')); + $link_class_name = 'basic-link ' . strtoupper($this->randomName()); + $edit = array( + 'instance[settings][attributes][class]' => $link_class_name, + ); + $this->drupalPost(NULL, $edit, t('Save settings')); + + // Is field created? + $this->assertRaw(t('Saved %label configuration', array('%label' => $name)), 'Field added'); + node_types_rebuild(); + menu_rebuild(); + + _field_info_collate_fields(TRUE); + $instances = field_info_instances('node', 'page'); + + $instance = $instances['field_' . $name]; + $this->assertFalse($instance['required'], 'Make sure field is not required.'); + $this->assertEqual($instance['settings']['title'], 'optional', 'Title should be optional by default.'); + $this->assertTrue($instance['settings']['validate_url'], 'Make sure validation is on.'); + $this->assertTrue($instance['settings']['enable_tokens'], 'Enable Tokens should be on by default.'); + $this->assertEqual($instance['settings']['display']['url_cutoff'], 80, 'Url cutoff should be at 80 characters.'); + $this->assertEqual($instance['settings']['attributes']['target'], 'default', 'Target should be "default"'); + $this->assertFalse($instance['settings']['attributes']['rel'], 'Rel should be blank by default.'); + $this->assertEqual($instance['settings']['attributes']['class'], $link_class_name, 'Two classes should be set.'); + $this->assertFalse($instance['settings']['title_value'], 'By default, no title should be set.'); + + // Now, let's create a node with this field and make sure the link shows up: + // create page form. + $field_name = 'field_' . $name; + $this->drupalGet('node/add/page'); + $this->assertField($field_name . '[und][0][url]', 'URL found'); + + $input = array( + 'title' => 'This & That', + 'href' => 'http://www.example.com/', + ); + + $edit = array( + 'title' => $field_name, + $field_name . '[und][0][title]' => $input['title'], + $field_name . '[und][0][url]' => $input['href'], + ); + $this->drupalPost(NULL, $edit, t('Save')); + + $url = $this->getUrl(); + + // Change to anonymous user. + $this->drupalLogout(); + $this->drupalGet($url); + + $this->assertRaw('This & That'); + $this->assertPattern('|class\s?=\s?"' . $link_class_name . '"|', "Classes $link_class_name exist on page."); + } + } diff --git a/sites/all/modules/contrib/fields/link/tests/link.entity_token.test b/sites/all/modules/contrib/fields/link/tests/link.entity_token.test new file mode 100644 index 0000000..6c42655 --- /dev/null +++ b/sites/all/modules/contrib/fields/link/tests/link.entity_token.test @@ -0,0 +1,164 @@ + 'Link entity tokens test', + 'description' => 'Tests that a link field appears properly in entity tokens', + 'group' => 'Link', + 'dependencies' => array('token', 'entity', 'entity_token'), + ); + } + + /** + * Setup. + */ + public function setUp($modules = array()) { + parent::setUp(array('token', 'entity', 'entity_token')); + } + + /** + * Creates a link field, fills it, then uses a loaded node to test tokens. + */ + public function testFieldTokenNodeLoaded() { + // Create field. + $settings = array( + 'instance[settings][enable_tokens]' => 0, + ); + $field_name = $this->createLinkField('page', + $settings); + + // Create page form. + $this->drupalGet('node/add/page'); + // $field_name = 'field_' . $name;. + $this->assertField($field_name . '[und][0][title]', 'Title found'); + $this->assertField($field_name . '[und][0][url]', 'URL found'); + + $token_url_tests = array( + 1 => array( + 'href' => 'http://example.com/' . $this->randomName(), + 'label' => $this->randomName(), + ), + 2 => array( + 'href' => 'http://example.com/' . $this->randomName() . '?property=value', + 'label' => $this->randomName(), + ), + 3 => array( + 'href' => 'http://example.com/' . $this->randomName() . '#position', + 'label' => $this->randomName(), + ), + 4 => array( + 'href' => 'http://example.com/' . $this->randomName() . '#lower?property=value2', + 'label' => $this->randomName(), + ), + ); + // @codingStandardsIgnoreLine + // $this->assert('pass', '
    ' . print_r($token_url_tests, TRUE) . '
    ');.
    +    foreach ($token_url_tests as &$input) {
    +      $this->drupalGet('node/add/page');
    +
    +      $edit = array(
    +        'title' => $input['label'],
    +        $field_name . '[und][0][title]' => $input['label'],
    +        $field_name . '[und][0][url]' => $input['href'],
    +      );
    +      $this->drupalPost(NULL, $edit, t('Save'));
    +      $url = $this->getUrl();
    +      $input['url'] = $url;
    +    }
    +
    +    // Change to anonymous user.
    +    $this->drupalLogout();
    +
    +    foreach ($token_url_tests as $index => $input2) {
    +      $node = node_load($index);
    +      $this->assertNotEqual(NULL, $node, "Do we have a node?");
    +      $this->assertEqual($node->nid, $index, "Test that we have a node.");
    +      $token_name = '[node:' . str_replace('_', '-', $field_name) . ':url]';
    +      $assert_data = token_replace($token_name,
    +        array('node' => $node));
    +      $this->assertEqual($input2['href'], $assert_data, "Test that the url token has been set to " . $input2['href'] . ' - ' . $assert_data);
    +    }
    +  }
    +
    +  /**
    +   * Field Token Node Viewed.
    +   *
    +   * Creates a link field, fills it, then uses a loaded and node_view'd node to
    +   * test tokens.
    +   */
    +  public function testFieldTokenNodeViewed() {
    +    // Create field.
    +    $settings = array(
    +      'instance[settings][enable_tokens]' => 0,
    +    );
    +    $field_name = $this->createLinkField('page',
    +      $settings);
    +
    +    // Create page form.
    +    $this->drupalGet('node/add/page');
    +    // $field_name = 'field_' . $name;.
    +    $this->assertField($field_name . '[und][0][title]', 'Title found');
    +    $this->assertField($field_name . '[und][0][url]', 'URL found');
    +
    +    $token_url_tests = array(
    +      1 => array(
    +        'href' => 'http://example.com/' . $this->randomName(),
    +        'label' => $this->randomName(),
    +      ),
    +      2 => array(
    +        'href' => 'http://example.com/' . $this->randomName() . '?property=value',
    +        'label' => $this->randomName(),
    +      ),
    +      3 => array(
    +        'href' => 'http://example.com/' . $this->randomName() . '#position',
    +        'label' => $this->randomName(),
    +      ),
    +      4 => array(
    +        'href' => 'http://example.com/' . $this->randomName() . '#lower?property=value2',
    +        'label' => $this->randomName(),
    +      ),
    +    );
    +
    +    //@codingStandardsIgnoreLine
    +    // $this->assert('pass', '
    ' . print_r($token_url_tests, TRUE) . '
    ');.
    +    foreach ($token_url_tests as &$input) {
    +      $this->drupalGet('node/add/page');
    +
    +      $edit = array(
    +        'title' => $input['label'],
    +        $field_name . '[und][0][title]' => $input['label'],
    +        $field_name . '[und][0][url]' => $input['href'],
    +      );
    +      $this->drupalPost(NULL, $edit, t('Save'));
    +      $url = $this->getUrl();
    +      $input['url'] = $url;
    +    }
    +
    +    // Change to anonymous user.
    +    $this->drupalLogout();
    +
    +    foreach ($token_url_tests as $index => $input2) {
    +      $node = node_load($index);
    +      $this->assertNotEqual(NULL, $node, "Do we have a node?");
    +      $this->assertEqual($node->nid, $index, "Test that we have a node.");
    +      $token_name = '[node:' . str_replace('_', '-', $field_name) . ':url]';
    +      $assert_data = token_replace($token_name,
    +        array('node' => $node));
    +      $this->assertEqual($input2['href'], $assert_data, "Test that the url token has been set to " . $input2['href'] . ' - ' . $assert_data);
    +    }
    +  }
    +
    +}
    diff --git a/sites/all/modules/contrib/fields/link/tests/link.test b/sites/all/modules/contrib/fields/link/tests/link.test
    index 962197f..dd9adb4 100644
    --- a/sites/all/modules/contrib/fields/link/tests/link.test
    +++ b/sites/all/modules/contrib/fields/link/tests/link.test
    @@ -5,10 +5,15 @@
      * Link base test file - contains common functions for testing links.
      */
     
    +/**
    + * Base Test Class.
    + */
     class LinkBaseTestClass extends DrupalWebTestCase {
    +
       protected $permissions = array(
         'access content',
         'administer content types',
    +    'administer fields',
         'administer nodes',
         'administer filters',
         'access comments',
    @@ -17,17 +22,23 @@ class LinkBaseTestClass extends DrupalWebTestCase {
         'create page content',
       );
     
    -  function setUp() {
    +  /**
    +   * Setup.
    +   */
    +  public function setUp() {
         $modules = func_get_args();
         $modules = (isset($modules[0]) && is_array($modules[0]) ? $modules[0] : $modules);
         $modules[] = 'field_ui';
         $modules[] = 'link';
         parent::setUp($modules);
    -    
    +
         $this->web_user = $this->drupalCreateUser($this->permissions);
         $this->drupalLogin($this->web_user);
       }
     
    +  /**
    +   * Create Link Field.
    +   */
       protected function createLinkField($node_type = 'page', $settings = array()) {
         $name = strtolower($this->randomName());
         $edit = array(
    @@ -48,4 +59,5 @@ class LinkBaseTestClass extends DrupalWebTestCase {
     
         return $field_name;
       }
    +
     }
    diff --git a/sites/all/modules/contrib/fields/link/tests/link.token.test b/sites/all/modules/contrib/fields/link/tests/link.token.test
    index 21bc4a0..edbb1df 100644
    --- a/sites/all/modules/contrib/fields/link/tests/link.token.test
    +++ b/sites/all/modules/contrib/fields/link/tests/link.token.test
    @@ -6,10 +6,13 @@
      */
     
     /**
    - * Testing that tokens can be used in link titles
    + * Testing that tokens can be used in link titles.
      */
     class LinkTokenTest extends LinkBaseTestClass {
     
    +  /**
    +   * Get Info.
    +   */
       public static function getInfo() {
         return array(
           'name' => 'Link tokens - browser test',
    @@ -19,34 +22,38 @@ class LinkTokenTest extends LinkBaseTestClass {
         );
       }
     
    -  function setUp($modules = array()) {
    +  /**
    +   * Setup.
    +   */
    +  public function setUp($modules = array()) {
         parent::setUp(array('token'));
       }
     
       /**
        * Creates a link field with a required title enabled for user-entered tokens.
    +   *
        * Creates a node with a token in the link title and checks the value.
        */
    -  function testUserTokenLinkCreate() {
    -    // create field
    +  public function testUserTokenLinkCreate() {
    +    // Create field.
         $settings = array(
           'instance[settings][enable_tokens]' => 1,
         );
         $field_name = $this->createLinkField('page',
    -                                        $settings);
    +      $settings);
     
    -    // create page form
    +    // Create page form.
         $this->drupalGet('node/add/page');
    -    //$field_name = 'field_' . $name;
    +    // $field_name = 'field_' . $name;.
         $this->assertField($field_name . '[und][0][title]', 'Title found');
         $this->assertField($field_name . '[und][0][url]', 'URL found');
     
         $input = array(
    -        'href' => 'http://example.com/' . $this->randomName(),
    -        'label' => $this->randomName(),
    +      'href' => 'http://example.com/' . $this->randomName(),
    +      'label' => $this->randomName(),
         );
     
    -    //$this->drupalLogin($this->web_user);
    +    // $this->drupalLogin($this->web_user);.
         $this->drupalGet('node/add/page');
     
         $edit = array(
    @@ -57,36 +64,37 @@ class LinkTokenTest extends LinkBaseTestClass {
         $this->drupalPost(NULL, $edit, t('Save'));
         $url = $this->getUrl();
     
    -    // change to anonymous user
    +    // Change to anonymous user.
         $this->drupalLogout();
         $this->drupalGet($url);
     
         $this->assertRaw(l($input['label'] . ' page', $input['href']));
       }
     
    -
       /**
        * Creates a link field with a static title and an admin-entered token.
    +   *
        * Creates a node with a link and checks the title value.
        */
    -  function testStaticTokenLinkCreate() {
    +  public function testStaticTokenLinkCreate() {
     
    -    // create field
    +    // Create field.
         $name = $this->randomName();
         $settings = array(
           'instance[settings][title]' => 'value',
    -      'instance[settings][title_value]' => $name . ' [node:content-type:machine-name]');
    +      'instance[settings][title_value]' => $name . ' [node:content-type:machine-name]',
    +    );
         $field_name = $this->createLinkField('page', $settings);
     
    -    // create page form
    +    // Create page form.
         $this->drupalGet('node/add/page');
         $this->assertField($field_name . '[und][0][url]', 'URL found');
     
         $input = array(
    -      'href' => 'http://example.com/' . $this->randomName()
    +      'href' => 'http://example.com/' . $this->randomName(),
         );
     
    -    //$this->drupalLogin($this->web_user);
    +    // $this->drupalLogin($this->web_user);.
         $this->drupalGet('node/add/page');
     
         $edit = array(
    @@ -97,7 +105,7 @@ class LinkTokenTest extends LinkBaseTestClass {
     
         $url = $this->getUrl();
     
    -    // change to anonymous user
    +    // Change to anonymous user.
         $this->drupalLogout();
         $this->drupalGet($url);
     
    @@ -106,30 +114,32 @@ class LinkTokenTest extends LinkBaseTestClass {
     
       /**
        * Creates a link field with a static title and an admin-entered token.
    +   *
        * Creates a node with a link and checks the title value.
        *
        * Basically, I want to make sure the [title-raw] token works, because it's a
        * token that changes from node to node, where [type]'s always going to be the
        * same.
        */
    -  function testStaticTokenLinkCreate2() {
    +  public function testStaticTokenLinkCreate2() {
     
    -    // create field
    +    // Create field.
         $name = $this->randomName();
         $settings = array(
           'instance[settings][title]' => 'value',
    -      'instance[settings][title_value]' => $name . ' [node:title]');
    +      'instance[settings][title_value]' => $name . ' [node:title]',
    +    );
         $field_name = $this->createLinkField('page', $settings);
     
    -    // create page form
    +    // Create page form.
         $this->drupalGet('node/add/page');
         $this->assertField($field_name . '[und][0][url]', 'URL found');
     
         $input = array(
    -      'href' => 'http://example.com/' . $this->randomName()
    +      'href' => 'http://example.com/' . $this->randomName(),
         );
     
    -    //$this->drupalLogin($this->web_user);
    +    // $this->drupalLogin($this->web_user);.
         $this->drupalGet('node/add/page');
     
         $edit = array(
    @@ -140,27 +150,32 @@ class LinkTokenTest extends LinkBaseTestClass {
     
         $url = $this->getUrl();
     
    -    // change to anonymous user
    +    // Change to anonymous user.
         $this->drupalLogout();
         $this->drupalGet($url);
     
         $this->assertRaw(l($name . ' ' . $name, $input['href']));
       }
     
    -  // This test doesn't seem to actually work, due to lack of 'title' in url.
    -  function _test_Link_With_Title_Attribute_token_url_form() {
    -   /* $this->loginWithPermissions($this->permissions);
    +  /**
    +   * This test doesn't seem to actually work, due to lack of 'title' in url.
    +   *
    +   * @codingStandardsIgnoreStart
    +   */
    +  public function _test_Link_With_Title_Attribute_token_url_form() {
    +    // @codingStandardsIgnoreEnd
    +    /* $this->loginWithPermissions($this->permissions);
         $this->acquireContentTypes(1);
         $field_settings = array(
    -      'type' => 'link',
    -      'widget_type' => 'link',
    -      'type_name' => $this->content_types[0]->name,
    -      'attributes' => array(
    -        'class' => '',
    -        'target' => 'default',
    -        'rel' => 'nofollow',
    -        'title' => '',
    -      ),
    +    'type' => 'link',
    +    'widget_type' => 'link',
    +    'type_name' => $this->content_types[0]->name,
    +    'attributes' => array(
    +    'class' => '',
    +    'target' => 'default',
    +    'rel' => 'nofollow',
    +    'title' => '',
    +    ),
         );
     
         $field = $this->createField($field_settings, 0);
    @@ -170,10 +185,10 @@ class LinkTokenTest extends LinkBaseTestClass {
         $url_type = str_replace('_', '-', $this->content_types[0]->type);
     
         $edit = array('attributes[title]' => '['. $field_name .'-url]',
    -                  'enable_tokens' => TRUE);
    -
    +    'enable_tokens' => TRUE);
    +    // @codingStandardsIgnoreLine
         $this->drupalPost('admin/content/node-type/'. $url_type .'/fields/'. $field['field_name'],
    -                      $edit, t('Save field settings'));
    +    $edit, t('Save field settings'));
         $this->assertText(t('Saved field @field_name', array('@field_name' => $field['field_name'])));*/
         $name = $this->randomName();
         $settings = array(
    @@ -183,12 +198,9 @@ class LinkTokenTest extends LinkBaseTestClass {
         $field_name = $this->createLinkField('page', $settings);
     
         // So, having saved this field_name, let's see if it works...
    -    //$this->acquireNodes(1);
    -
    -    //$node = node_load($this->nodes[0]->nid);
    -
    -    //$this->drupalGet('node/'. $this->nodes[0]->nid);
    -
    +    // $this->acquireNodes(1);
    +    // $node = node_load($this->nodes[0]->nid);
    +    // $this->drupalGet('node/'. $this->nodes[0]->nid);.
         $edit = array();
         $test_link_url = 'http://www.example.com/test';
         $edit[$field_name . '[und][0][url]'] = $test_link_url;
    @@ -200,23 +212,28 @@ class LinkTokenTest extends LinkBaseTestClass {
         $this->drupalPost(NULL, $edit, t('Save'));
     
         // Make sure we get a new version!
    -    //$node = node_load($this->nodes[0]->nid, NULL, TRUE);
    +    // $node = node_load($this->nodes[0]->nid, NULL, TRUE);.
         $this->assertText(t('Basic page @title has been updated.',
    -                        array('@title' => $name)));
    +      array('@title' => $name)));
     
    -    //$this->drupalGet('node/'. $node->nid);
    +    // $this->drupalGet('node/'. $node->nid);.
         $this->assertText($title, 'Make sure the link title/text shows');
         $this->assertRaw(' title="' . $test_link_url . '"', "Do we show the link url as the title attribute?");
         $this->assertNoRaw(' title="[' . $field_name . '-url]"');
         $this->assertTrue(module_exists('token'), t('Assure that Token Module is enabled.'));
    -    //$this->fail($this->content);
    +    // $this->fail($this->content);.
       }
     
       /**
    +   * Link With Title Attribute token title form.
    +   *
        * If the title of the link is set to the title attribute, then the title
        * attribute isn't supposed to show.
    +   *
    +   * @codingStandardsIgnoreStart
        */
    -  function _test_Link_With_Title_Attribute_token_title_form() {
    +  public function _test_Link_With_Title_Attribute_token_title_form() {
    +    // @codingStandardsIgnoreEnd
         $this->loginWithPermissions($this->permissions);
         $this->acquireContentTypes(1);
         $field_settings = array(
    @@ -233,21 +250,20 @@ class LinkTokenTest extends LinkBaseTestClass {
     
         $field = $this->createField($field_settings, 0);
         $field_name = $field['field_name'];
    -    $field_db_info = content_database_info($field);
         $url_type = str_replace('_', '-', $this->content_types[0]->type);
     
    -    $edit = array('attributes[title]' => '[' . $field_name . '-title]',
    -                  'enable_tokens' => TRUE);
    +    $edit = array(
    +      'attributes[title]' => '[' . $field_name . '-title]',
    +      'enable_tokens' => TRUE,
    +    );
     
         $this->drupalPost('admin/content/node-type/' . $url_type . '/fields/' . $field['field_name'],
    -                      $edit, t('Save field settings'));
    +      $edit, t('Save field settings'));
         $this->assertText(t('Saved field @field_name', array('@field_name' => $field['field_name'])));
     
         // So, having saved this field_name, let's see if it works...
         $this->acquireNodes(1);
     
    -    $node = node_load($this->nodes[0]->nid);
    -
         $this->drupalGet('node/' . $this->nodes[0]->nid);
     
         $edit = array();
    @@ -260,24 +276,35 @@ class LinkTokenTest extends LinkBaseTestClass {
         // Make sure we get a new version!
         $node = node_load($this->nodes[0]->nid, NULL, TRUE);
         $this->assertText(t('@type @title has been updated.',
    -                        array('@title' => $node->title,
    -                              '@type' => $this->content_types[0]->name)));
    +      array(
    +        '@title' => $node->title,
    +        '@type' => $this->content_types[0]->name,
    +      )));
     
         $this->drupalGet('node/' . $node->nid);
         $this->assertText($title, 'Make sure the link title/text shows');
         $this->assertNoRaw(' title="' . $title . '"', "We should not show the link title as the title attribute?");
         $this->assertNoRaw(' title="[' . $field_name . '-title]"');
    -    //$this->fail($this->content);
    +    // $this->fail($this->content);.
       }
     
       /**
    -   *  Trying to set the url to contain a token.
    +   * Trying to set the url to contain a token.
    +   *
    +   * @codingStandardsIgnoreStart
        */
    -  function _testUserTokenLinkCreateInURL() {
    -    $this->web_user = $this->drupalCreateUser(array('administer content types', 'access content', 'create page content'));
    +  public function _testUserTokenLinkCreateInURL() {
    +    //@codingStandardsIgnoreEnd
    +
    +    $this->web_user = $this->drupalCreateUser(array(
    +      'administer content types',
    +      'administer fields',
    +      'access content',
    +      'create page content',
    +    ));
         $this->drupalLogin($this->web_user);
     
    -    // create field
    +    // Create field.
         $name = strtolower($this->randomName());
         $edit = array(
           '_add_new_field[label]' => $name,
    @@ -288,20 +315,21 @@ class LinkTokenTest extends LinkBaseTestClass {
         $this->drupalPost('admin/content/node-type/page/fields', $edit, t('Save'));
         $this->drupalPost(NULL, array(
           'title' => 'required',
    -      'enable_tokens' => 1), t('Save field settings'));
    +      'enable_tokens' => 1,
    +    ), t('Save field settings'));
     
         // Is field created?
         $this->assertRaw(t('Added field %label.', array('%label' => $name)), 'Field added');
     
    -    // create page form
    +    // Create page form.
         $this->drupalGet('node/add/page');
         $field_name = 'field_' . $name;
         $this->assertField($field_name . '[0][title]', 'Title found');
         $this->assertField($field_name . '[0][url]', 'URL found');
     
         $input = array(
    -        'href' => 'http://example.com/' . $this->randomName(),
    -        'label' => $this->randomName(),
    +      'href' => 'http://example.com/' . $this->randomName(),
    +      'label' => $this->randomName(),
         );
     
         $this->drupalLogin($this->web_user);
    @@ -315,22 +343,31 @@ class LinkTokenTest extends LinkBaseTestClass {
         $this->drupalPost(NULL, $edit, t('Save'));
         $url = $this->getUrl();
     
    -    // change to anonymous user
    +    // Change to anonymous user.
         $this->drupalLogout();
         $this->drupalGet($url);
     
         $this->assertRaw(l($input['label'], $input['href'] . '/page'));
    -    //$this->fail($this->content);
    +    // $this->fail($this->content);.
       }
     
       /**
    -   *  Trying to set the url to contain a token.
    +   * Trying to set the url to contain a token.
    +   *
    +   * @codingStandardsIgnoreStart
        */
    -  function _testUserTokenLinkCreateInURL2() {
    -    $this->web_user = $this->drupalCreateUser(array('administer content types', 'access content', 'create page content'));
    +  public function _testUserTokenLinkCreateInURL2() {
    +    // @codingStandardsIgnoreEnd
    +
    +    $this->web_user = $this->drupalCreateUser(array(
    +      'administer content types',
    +      'administer fields',
    +      'access content',
    +      'create page content',
    +    ));
         $this->drupalLogin($this->web_user);
     
    -    // create field
    +    // Create field.
         $name = strtolower($this->randomName());
         $edit = array(
           '_add_new_field[label]' => $name,
    @@ -341,20 +378,21 @@ class LinkTokenTest extends LinkBaseTestClass {
         $this->drupalPost('admin/content/node-type/page/fields', $edit, t('Save'));
         $this->drupalPost(NULL, array(
           'title' => 'required',
    -      'enable_tokens' => 1), t('Save field settings'));
    +      'enable_tokens' => 1,
    +    ), t('Save field settings'));
     
         // Is field created?
         $this->assertRaw(t('Added field %label.', array('%label' => $name)), 'Field added');
     
    -    // create page form
    +    // Create page form.
         $this->drupalGet('node/add/page');
         $field_name = 'field_' . $name;
         $this->assertField($field_name . '[0][title]', 'Title found');
         $this->assertField($field_name . '[0][url]', 'URL found');
     
         $input = array(
    -        'href' => 'http://example.com/' . $this->randomName(),
    -        'label' => $this->randomName(),
    +      'href' => 'http://example.com/' . $this->randomName(),
    +      'label' => $this->randomName(),
         );
     
         $this->drupalLogin($this->web_user);
    @@ -368,22 +406,34 @@ class LinkTokenTest extends LinkBaseTestClass {
         $this->drupalPost(NULL, $edit, t('Save'));
         $url = $this->getUrl();
     
    -    // change to anonymous user
    +    // Change to anonymous user.
         $this->drupalLogout();
         $this->drupalGet($url);
     
         $this->assertRaw(l($input['label'], $input['href'] . '/' . $this->web_user->uid));
       }
    -  
    +
       /**
    -   *  Test that if you have a title and no url on a field which does not have tokens enabled,
    -   *  that the title is sanitized once.
    +   * CRUD Title Only Title No Link.
    +   *
    +   * Test that if you have a title and no url on a field which does not have
    +   * tokens enabled, that the title is sanitized once.
    +   *
    +   * @codingStandardsIgnoreStart
        */
    -  function testCRUDTitleOnlyTitleNoLink2() {
    -    $this->web_user = $this->drupalCreateUser(array('administer content types', 'access content', 'create page content'));
    +  public function testCRUDTitleOnlyTitleNoLink2() {
    +    //@codingStandardsIgnoreEnd
    +
    +    $this->web_user = $this->drupalCreateUser(array(
    +      'administer content types',
    +      'administer fields',
    +      'access content',
    +      'create page content',
    +    ));
    +
         $this->drupalLogin($this->web_user);
     
    -    // create field
    +    // Create field.
         $name = strtolower($this->randomName());
         $field_name = 'field_' . $name;
         $edit = array(
    @@ -401,8 +451,8 @@ class LinkTokenTest extends LinkBaseTestClass {
     
         // Is field created?
         $this->assertRaw(t('Saved %label configuration', array('%label' => $name)), 'Field added');
    -    
    -    // create page form
    +
    +    // Create page form.
         $this->drupalGet('node/add/page');
         $this->assertField($field_name . '[und][0][url]', 'URL found');
     
    @@ -419,11 +469,12 @@ class LinkTokenTest extends LinkBaseTestClass {
         $this->drupalPost(NULL, $edit, t('Save'));
     
         $url = $this->getUrl();
    -    
    -    // change to anonymous user
    +
    +    // Change to anonymous user.
         $this->drupalLogout();
         $this->drupalGet($url);
     
         $this->assertRaw('This & That');
       }
    +
     }
    diff --git a/sites/all/modules/contrib/fields/link/tests/link.validate.test b/sites/all/modules/contrib/fields/link/tests/link.validate.test
    index 764eeaa..addd7d3 100644
    --- a/sites/all/modules/contrib/fields/link/tests/link.validate.test
    +++ b/sites/all/modules/contrib/fields/link/tests/link.validate.test
    @@ -5,8 +5,14 @@
      * Tests that exercise the validation functions in the link module.
      */
     
    +/**
    + * Validate Test Case.
    + */
     class LinkValidateTestCase extends LinkBaseTestClass {
     
    +  /**
    +   * Create Link.
    +   */
       protected function createLink($url, $title, $attributes = array()) {
         return array(
           'url' => $url,
    @@ -17,35 +23,44 @@ class LinkValidateTestCase extends LinkBaseTestClass {
     
       /**
        * Takes a url, and sees if it can validate that the url is valid.
    +   *
    +   * @codingStandardsIgnoreStart
        */
       protected function link_test_validate_url($url) {
    +    // @codingStandardsIgnoreEnd
     
         $field_name = $this->createLinkField();
     
    -    $permission = 'create page content';
    -    $this->checkPermissions(array($permission), TRUE);
    -
    -    $this->drupalGet('node/add/page');
    -
         $label = $this->randomName();
    -    $edit = array(
    +    $settings = array(
           'title' => $label,
    -      $field_name . '[und][0][title]' => $label,
    -      $field_name . '[und][0][url]' => $url,
    +      $field_name => array(
    +        LANGUAGE_NONE => array(
    +          array(
    +            'title' => $label,
    +            'url' => $url,
    +          ),
    +        ),
    +      ),
         );
    -    $this->drupalPost(NULL, $edit, t('Save'));
    -    $this->assertRaw(' has been created.', 'Node created');
     
    -    $nid = 1; //$matches[1];
    +    $node = $this->drupalCreateNode($settings);
     
    -    $node = node_load($nid);
    +    $this->assertNotNull($node, ' has been created.', 'Node created');
     
    -    $this->assertEqual($url, $node->{$field_name}['und'][0]['url']);
    +    $this->assertEqual($url, $node->{$field_name}[LANGUAGE_NONE][0]['url']);
       }
    +
     }
     
    +/**
    + * Class for Validate Test.
    + */
     class LinkValidateTest extends LinkValidateTestCase {
     
    +  /**
    +   * Get Info.
    +   */
       public static function getInfo() {
         return array(
           'name' => 'Link Validation Tests',
    @@ -54,23 +69,35 @@ class LinkValidateTest extends LinkValidateTestCase {
         );
       }
     
    -  function test_link_validate_basic_url() {
    +  /**
    +   * Validate basic URL.
    +   *
    +   * @codingStandardsIgnoreStart
    +   */
    +  public function test_link_validate_basic_url() {
    +    // @codingStandardsIgnoreEnd
         $this->link_test_validate_url('http://www.example.com');
       }
     
       /**
        * Test if we're stopped from posting a bad url on default validation.
    +   *
    +   * @codingStandardsIgnoreStart
        */
    -  function test_link_validate_bad_url_validate_default() {
    -    $this->web_user = $this->drupalCreateUser(array('administer content types',
    -                                             'administer nodes',
    -                                             'administer filters',
    -                                             'access content',
    -                                             'create page content',
    -                                             'access administration pages'));
    +  public function test_link_validate_bad_url_validate_default() {
    +    // @codingStandardsIgnoreEnd
    +    $this->web_user = $this->drupalCreateUser(array(
    +      'administer content types',
    +      'administer fields',
    +      'administer nodes',
    +      'administer filters',
    +      'access content',
    +      'create page content',
    +      'access administration pages',
    +    ));
         $this->drupalLogin($this->web_user);
     
    -    // create field
    +    // Create field.
         $name = strtolower($this->randomName());
         $edit = array(
           'fields[_add_new_field][label]' => $name,
    @@ -87,35 +114,43 @@ class LinkValidateTest extends LinkValidateTestCase {
         node_types_rebuild();
         menu_rebuild();
     
    -    // create page form
    +    // Create page form.
         $this->drupalGet('node/add/page');
         $field_name = 'field_' . $name;
         $this->assertField('edit-field-' . $name . '-und-0-title', 'Title found');
         $this->assertField('edit-field-' . $name . '-und-0-url', 'URL found');
     
    -
         $edit = array(
           'title' => 'Simple Title',
           $field_name . '[und][0][url]' => 'edik:naw',
         );
     
         $this->drupalPost(NULL, $edit, t('Save'));
    -    $this->assertText(t('The value @value provided for @field is not a valid URL.', array('@value' => 'edik:naw', '@field' => $name)));
    +    $this->assertText(t('The value @value provided for @field is not a valid URL.', array(
    +      '@value' => 'edik:naw',
    +      '@field' => $name,
    +    )));
       }
     
       /**
        * Test if we're stopped from posting a bad url with validation on.
    +   *
    +   * @codingStandardsIgnoreStart
        */
    -  function test_link_validate_bad_url_validate_on() {
    -    $this->web_user = $this->drupalCreateUser(array('administer content types',
    -                                             'administer nodes',
    -                                             'administer filters',
    -                                             'access content',
    -                                             'create page content',
    -                                             'access administration pages'));
    +  public function test_link_validate_bad_url_validate_on() {
    +    // @codingStandardsIgnoreEnd
    +    $this->web_user = $this->drupalCreateUser(array(
    +      'administer content types',
    +      'administer fields',
    +      'administer nodes',
    +      'administer filters',
    +      'access content',
    +      'create page content',
    +      'access administration pages',
    +    ));
         $this->drupalLogin($this->web_user);
     
    -    // create field
    +    // Create field.
         $name = strtolower($this->randomName());
         $edit = array(
           'fields[_add_new_field][label]' => $name,
    @@ -132,36 +167,44 @@ class LinkValidateTest extends LinkValidateTestCase {
         node_types_rebuild();
         menu_rebuild();
     
    -    // create page form
    +    // Create page form.
         $this->drupalGet('node/add/page');
         $field_name = 'field_' . $name;
         $this->assertField('edit-field-' . $name . '-und-0-title', 'Title found');
         $this->assertField('edit-field-' . $name . '-und-0-url', 'URL found');
     
    -
         $edit = array(
           'title' => 'Simple Title',
           $field_name . '[und][0][url]' => 'edik:naw',
         );
     
         $this->drupalPost(NULL, $edit, t('Save'));
    -    $this->assertText(t('The value @value provided for @field is not a valid URL.', array('@field' => $name, '@value' => 'edik:naw')));
    +    $this->assertText(t('The value @value provided for @field is not a valid URL.', array(
    +      '@field' => $name,
    +      '@value' => 'edik:naw',
    +    )));
     
       }
     
       /**
        * Test if we can post a bad url if the validation is expressly turned off.
    +   *
    +   * @codingStandardsIgnoreStart
        */
    -  function test_link_validate_bad_url_validate_off() {
    -    $this->web_user = $this->drupalCreateUser(array('administer content types',
    -                                             'administer nodes',
    -                                             'administer filters',
    -                                             'access content',
    -                                             'create page content',
    -                                             'access administration pages'));
    +  public function test_link_validate_bad_url_validate_off() {
    +    // @codingStandardsIgnoreEnd
    +    $this->web_user = $this->drupalCreateUser(array(
    +      'administer content types',
    +      'administer fields',
    +      'administer nodes',
    +      'administer filters',
    +      'access content',
    +      'create page content',
    +      'access administration pages',
    +    ));
         $this->drupalLogin($this->web_user);
     
    -    // create field
    +    // Create field.
         $name = strtolower($this->randomName());
         $edit = array(
           'fields[_add_new_field][label]' => $name,
    @@ -173,6 +216,7 @@ class LinkValidateTest extends LinkValidateTestCase {
         $this->drupalPost(NULL, array(), t('Save field settings'));
         $this->drupalPost(NULL, array('instance[settings][validate_url]' => FALSE), t('Save settings'));
     
    +    // @codingStandardsIgnoreLine
         /*$instance_details = db_query("SELECT * FROM {field_config_instance} WHERE field_name = :field_name AND bundle = 'page'", array(':field_name' => 'field_'. $name))->fetchObject();
         $this->fail('
    '. print_r($instance_details, TRUE) .'
    '); $this->fail('
    '. print_r(unserialize($instance_details->data), TRUE) .'
    ');*/ @@ -182,51 +226,62 @@ class LinkValidateTest extends LinkValidateTestCase { node_types_rebuild(); menu_rebuild(); - // create page form + // Create page form. $this->drupalGet('node/add/page'); $field_name = 'field_' . $name; $this->assertField('edit-field-' . $name . '-und-0-title', 'Title found'); $this->assertField('edit-field-' . $name . '-und-0-url', 'URL found'); - $edit = array( 'title' => 'Simple Title', $field_name . '[und][0][url]' => 'edik:naw', ); $this->drupalPost(NULL, $edit, t('Save')); - $this->assertNoText(t('The value %value provided for %field is not a valid URL.', array('%field' => $name, '%value' => 'edik:naw'))); + $this->assertNoText(t('The value %value provided for %field is not a valid URL.', array( + '%field' => $name, + '%value' => 'edik:naw', + ))); } /** - * Test if a bad url can sneak through un-filtered if we play with the validation... + * Validate switching between validation status. + * + * Test if a bad url can sneak through un-filtered if we play with the + * validation... + * + * @codingStandardsIgnoreStart */ - function x_test_link_validate_switching_between_validation_status() { + public function x_test_link_validate_switching_between_validation_status() { + // @codingStandardsIgnoreEnd $this->acquireContentTypes(1); - $this->web_user = $this->drupalCreateUser(array('administer content types', - 'administer nodes', - 'access administration pages', - 'access content', - 'create ' . $this->content_types[0]->type . ' content', - 'edit any ' . $this->content_types[0]->type . ' content')); + $this->web_user = $this->drupalCreateUser(array( + 'administer content types', + 'administer fields', + 'administer nodes', + 'access administration pages', + 'access content', + 'create ' . $this->content_types[0]->type . ' content', + 'edit any ' . $this->content_types[0]->type . ' content', + )); $this->drupalLogin($this->web_user); - variable_set('node_options_' . $this->content_types[0]->name, array('status', 'promote')); + variable_set('node_options_' . $this->content_types[0]->name, array( + 'status', + 'promote', + )); $field_settings = array( 'type' => 'link', 'widget_type' => 'link', 'type_name' => $this->content_types[0]->name, - 'attributes' => array(), // <-- This is needed or we have an error + // <-- This is needed or we have an error. + 'attributes' => array(), 'validate_url' => 0, ); $field = $this->createField($field_settings, 0); - //$this->fail('
    '. print_r($field, TRUE) .'
    '); - $field_db_info = content_database_info($field); $this->acquireNodes(2); - $node = node_load($this->nodes[0]->nid); - $this->drupalGet('node/' . $this->nodes[0]->nid); $edit = array(); @@ -236,8 +291,12 @@ class LinkValidateTest extends LinkValidateTestCase { $edit[$field['field_name'] . '[0][title]'] = $title; $this->drupalPost('node/' . $this->nodes[0]->nid . '/edit', $edit, t('Save')); - //$this->pass($this->content); - $this->assertNoText(t('The value %value provided for %field is not a valid URL.', array('%field' => $name, '%value' => trim($url)))); + // $this->pass($this->content);. + // @codingStandardsIgnoreLine + $this->assertNoText(t('The value %value provided for %field is not a valid URL.', array( + '%field' => $name, + '%value' => trim($url), + ))); // Make sure we get a new version! $node = node_load($this->nodes[0]->nid, NULL, TRUE); @@ -249,8 +308,9 @@ class LinkValidateTest extends LinkValidateTestCase { // Turn the array validation back _on_. $edit = array('validate_url' => TRUE); $node_type_link = str_replace('_', '-', $node->type); - //$this->drupalGet('admin/content/node-type/'. $node_type_link .'/fields'); ///'. $field['field_name']); - //$this->fail($this->content); + // @codingStandardsIgnoreLine + // $this->drupalGet('admin/content/node-type/'. $node_type_link .'/fields'); ///'. $field['field_name']); + // $this->fail($this->content);. $this->drupalPost('admin/content/node-type/' . $node_type_link . '/fields/' . $field['field_name'], $edit, t('Save field settings')); $this->drupalGet('node/' . $node->nid); @@ -258,36 +318,75 @@ class LinkValidateTest extends LinkValidateTestCase { // url() function. But we should have a test that makes sure it continues // to work. $this->assertNoRaw($url, 'Make sure Javascript does not display.'); - //$this->fail($this->content); - + // $this->fail($this->content);. } - // Validate that '' is a valid url. - function test_link_front_url() { + /** + * Validate that '' is a valid url. + * + * @codingStandardsIgnoreStart + */ + public function test_link_front_url() { + // @codingStandardsIgnoreEnd $this->link_test_validate_url(''); } - // Validate that an internal url would be accepted. - function test_link_internal_url() { - $this->link_test_validate_url('node/32'); + /** + * Validate that an internal url would be accepted. + * + * @codingStandardsIgnoreStart + */ + public function test_link_internal_url() { + // @codingStandardsIgnoreEnd + // Create the content first. + $node = $this->drupalCreateNode(); + + $link = 'node/' . $node->nid; + $this->link_test_validate_url($link); + $type = link_url_type($link); + $this->assertEqual(LINK_INTERNAL, $type, 'Test ' . $link . ' is an internal link.'); } - // Validate a simple mailto. - function test_link_mailto() { + /** + * Validate a simple mailto. + * + * @codingStandardsIgnoreStart + */ + public function test_link_mailto() { + // @codingStandardsIgnoreEnd $this->link_test_validate_url('mailto:jcfiala@gmail.com'); } - function test_link_external_https() { + /** + * Check link external https. + * + * @codingStandardsIgnoreStart + */ + public function test_link_external_https() { + // @codingStandardsIgnoreEnd $this->link_test_validate_url('https://www.example.com/'); } - function test_link_ftp() { + /** + * Check link FTP. + * + * @codingStandardsIgnoreStart + */ + public function test_link_ftp() { + // @codingStandardsIgnoreEnd $this->link_test_validate_url('ftp://www.example.com/'); } + } +/** + * Validate Test News. + */ class LinkValidateTestNews extends LinkValidateTestCase { + /** + * Get Info. + */ public static function getInfo() { return array( 'name' => 'Link News Validation Tests', @@ -296,18 +395,36 @@ class LinkValidateTestNews extends LinkValidateTestCase { ); } - // Validate a news link to a message group - function test_link_news() { + /** + * Validate a news link to a message group. + * + * @codingStandardsIgnoreStart + */ + public function test_link_news() { + // @codingStandardsIgnoreEnd $this->link_test_validate_url('news:comp.infosystems.www.misc'); } - // Validate a news link to a message id. Said ID copied off of google groups. - function test_link_news_message() { + /** + * Validate a news link to a message id. Said ID copied off of google groups. + * + * @codingStandardsIgnoreStart + */ + public function test_link_news_message() { + // @codingStandardsIgnoreEnd $this->link_test_validate_url('news:hj0db8$vrm$1@news.eternal-september.org'); } + } +/** + * Validate Specific URL. + */ class LinkValidateSpecificURL extends LinkValidateTestCase { + + /** + * Get Info. + */ public static function getInfo() { return array( 'name' => 'Link Specific URL Validation Tests', @@ -316,33 +433,65 @@ class LinkValidateSpecificURL extends LinkValidateTestCase { ); } - // Lets throw in a lot of umlouts for testing! - function test_umlout_url() { + /** + * Lets throw in a lot of umlouts for testing! + * + * @codingStandardsIgnoreStart + */ + public function test_umlout_url() { + // @codingStandardsIgnoreEnd $this->link_test_validate_url('http://üÜü.exämple.com/nöde'); } - function test_umlout_mailto() { + /** + * Check umlout mailto. + * + * @codingStandardsIgnoreStart + */ + public function test_umlout_mailto() { + // @codingStandardsIgnoreEnd $this->link_test_validate_url('mailto:Üser@exÅmple.com'); } - function test_german_b_url() { + /** + * Check german b in url. + * + * @codingStandardsIgnoreStart + */ + public function test_german_b_url() { + // @codingStandardsIgnoreEnd $this->link_test_validate_url('http://www.test.com/ßstuff'); } - function test_special_n_url() { + /** + * Check Special in url. + * + * @codingStandardsIgnoreStart + */ + public function test_special_n_url() { + // @codingStandardsIgnoreEnd $this->link_test_validate_url('http://www.testÑñ.com/'); } - function test_curly_brackets_in_query() { + /** + * Curly Brackets in query. + * + * @codingStandardsIgnoreStart + */ + public function test_curly_brackets_in_query() { + // @codingStandardsIgnoreEnd $this->link_test_validate_url('http://www.healthyteennetwork.org/index.asp?Type=B_PR&SEC={2AE1D600-4FC6-4B4D-8822-F1D5F072ED7B}&DE={235FD1E7-208D-4363-9854-4E6775EB8A4C}'); } /** * Here, we're testing that a very long url is stored properly in the db. * - * Basicly, trying to test http://drupal.org/node/376818 + * Basically, trying to test http://drupal.org/node/376818 + * + * @codingStandardsIgnoreStart */ - function testLinkURLFieldIsBig() { + public function testLinkURLFieldIsBig() { + // @codingStandardsIgnoreEnd $long_url = 'http://th.wikipedia.org/wiki/%E0%B9%82%E0%B8%A3%E0%B8%87%E0%B9%80%E0%B8%A3%E0%B8%B5%E0%B8%A2%E0%B8%99%E0%B9%80%E0%B8%9A%E0%B8%8D%E0%B8%88%E0%B8%A1%E0%B8%A3%E0%B8%B2%E0%B8%8A%E0%B8%B9%E0%B8%97%E0%B8%B4%E0%B8%A8_%E0%B8%99%E0%B8%84%E0%B8%A3%E0%B8%A8%E0%B8%A3%E0%B8%B5%E0%B8%98%E0%B8%A3%E0%B8%A3%E0%B8%A1%E0%B8%A3%E0%B8%B2%E0%B8%8A'; $this->link_test_validate_url($long_url); } @@ -350,12 +499,18 @@ class LinkValidateSpecificURL extends LinkValidateTestCase { } /** - * A series of tests of links, only going against the link_validate_url function in link.module. + * Validate Url Light. + * + * A series of tests of links, only going against the link_validate_url function + * in link.module. * * Validation is guided by the rules in http://tools.ietf.org/html/rfc1738 ! */ class LinkValidateUrlLight extends DrupalWebTestCase { + /** + * Get Info. + */ public static function getInfo() { return array( 'name' => 'Link Light Validation Tests', @@ -365,73 +520,117 @@ class LinkValidateUrlLight extends DrupalWebTestCase { } /** - * Translates the LINK type constants to english for display and debugging of tests + * Setup. */ - function name_Link_Type($type) { + public function setUp() { + parent::setUp('link'); + } + + /** + * Name Link Type. + * + * Translates the LINK type constants to english for display and debugging of + * tests. + * + * @codingStandardsIgnoreStart + */ + public function name_Link_Type($type) { + // @codingStandardsIgnoreEnd switch ($type) { case LINK_FRONT: return "Front"; + case LINK_EMAIL: return "Email"; + case LINK_NEWS: return "Newsgroup"; + case LINK_INTERNAL: return "Internal Link"; + case LINK_EXTERNAL: return "External Link"; + case FALSE: return "Invalid Link"; + default: return "Bad Value:" . $type; } } - // Make sure that a link labelled works. - function testValidateFrontLink() { + /** + * Make sure that a link labeled works. + */ + public function testValidateFrontLink() { $valid = link_validate_url(''); - $this->assertEqual(LINK_FRONT, $valid, 'Make sure that front link is verfied and identified'); + $this->assertEqual(LINK_FRONT, $valid, 'Make sure that front link is verified and identified'); } - function testValidateEmailLink() { + /** + * Validate Email Link. + */ + public function testValidateEmailLink() { $valid = link_validate_url('mailto:bob@example.com'); $this->assertEqual(LINK_EMAIL, $valid, "Make sure a basic mailto is verified and identified"); } - function testValidateEmailLinkBad() { + /** + * Validate Email Link Bad. + */ + public function testValidateEmailLinkBad() { $valid = link_validate_url(':bob@example.com'); $this->assertEqual(FALSE, $valid, 'Make sure just a bad address is correctly failed'); } - function testValidateNewsgroupLink() { + /** + * Validate Newsgroup Link. + */ + public function testValidateNewsgroupLink() { $valid = link_validate_url('news:comp.infosystems.www.misc'); $this->assertEqual(LINK_NEWS, $valid, 'Make sure link to newsgroup validates as news.'); } - function testValidateNewsArticleLink() { + /** + * Validate News Article Link. + */ + public function testValidateNewsArticleLink() { $valid = link_validate_url('news:hj0db8$vrm$1@news.eternal-september.org'); - $this->assertEqual(LINK_NEWS, $valid, 'Make sure link to specific article valiates as news.'); + $this->assertEqual(LINK_NEWS, $valid, 'Make sure link to specific article validates as news.'); } - function testValidateBadNewsgroupLink() { + /** + * Validate Bad Newsgroup Link. + */ + public function testValidateBadNewsgroupLink() { $valid = link_validate_url('news:comp.bad_name.misc'); $this->assertEqual(FALSE, $valid, 'newsgroup names can\'t contain underscores, so it should come back as invalid.'); } - function testValidateInternalLinks() { + /** + * Validate Internal Links. + */ + public function testValidateInternalLinks() { + $tempfile = drupal_tempnam('public://files', 'test'); $links = array( - 'node/5', 'rss.xml', - 'files/test.jpg', - '/var/www/test', + file_uri_target($tempfile), + drupal_realpath($tempfile), ); - + foreach ($links as $link) { + $type = link_url_type($link); + $this->assertEqual(LINK_INTERNAL, $type, 'Test ' . $link . ' is an internal link.'); $valid = link_validate_url($link); - $this->assertEqual(LINK_INTERNAL, $valid, 'Test ' . $link . ' internal link.'); + $this->assertTrue($valid, 'Test ' . $link . ' is valid internal link.'); } } - function testValidateExternalLinks() { + /** + * Validate External Links. + */ + public function testValidateExternalLinks() { $links = array( 'http://localhost:8080/', 'www.example.com', @@ -446,9 +645,8 @@ class LinkValidateUrlLight extends DrupalWebTestCase { 'http://255.255.255.255:4823/', 'www.test-site.com', 'http://example.com/index.php?q=node/123', - 'http://example.com/index.php?page=this\that', 'http://example.com/?first_name=Joe Bob&last_name=Smith', - // Anchors + // Anchors. 'http://www.example.com/index.php#test', 'http://www.example.com/index.php#this@that.', 'http://www.example.com/index.php#', @@ -456,39 +654,61 @@ class LinkValidateUrlLight extends DrupalWebTestCase { 'http://www.archive.org/stream/aesopsfables00aesorich#page/n7/mode/2up', 'http://www.example.com/blah/#this@that?', ); + // Test all of the protocols. - $allowed_protocols = variable_get('filter_allowed_protocols', array('http', 'https', 'ftp', 'news', 'nntp', 'telnet', 'mailto', 'irc', 'ssh', 'sftp', 'webcal')); + $allowed_protocols = variable_get('filter_allowed_protocols', array( + 'http', + 'https', + 'ftp', + 'news', + 'nntp', + 'telnet', + 'mailto', + 'irc', + 'ssh', + 'sftp', + 'webcal', + )); + foreach ($allowed_protocols as $protocol) { if ($protocol !== 'news' && $protocol !== 'mailto') { $links[] = $protocol . '://www.example.com'; } } foreach ($links as $link) { + $type = link_url_type($link); + $this->assertEqual(LINK_EXTERNAL, $type, 'Testing that ' . $link . ' is an external link.'); $valid = link_validate_url($link); - $this->assertEqual(LINK_EXTERNAL, $valid, 'Testing that ' . $link . ' is a valid external link.'); - // The following two lines are commented out and only used for comparisons. - //$valid2 = valid_url($link, TRUE); - //$this->assertEqual(TRUE, $valid2, "Using valid_url() on $link."); + $this->assertTrue($valid, 'Test ' . $link . ' is valid external link.'); + // The following two lines are commented out and only used for + // comparisons. + // $valid2 = valid_url($link, TRUE); + // $this->assertEqual(TRUE, $valid2, "Using valid_url() on $link.");. } - // Test if we can make a tld valid: - variable_set('link_extra_domains', array('frog')); - $valid = link_validate_url('http://www.example.frog'); - $this->assertEqual(LINK_EXTERNAL, $valid, "Testing that http://www.example.frog is a valid external link if we've added 'frog' to the list of valid domains."); } - function testInvalidExternalLinks() { + /** + * Check Invalid External Links. + */ + public function testInvalidExternalLinks() { $links = array( 'http://www.ex ample.com/', - 'http://25.0.0/', // bad ip! + // Bad ip! + 'http://25.0.0/', 'http://4827.0.0.2/', - '//www.example.com/', - 'http://www.testß.com/', // ß not allowed in domain names! - 'http://www.example.frog/', // Bad TLD - //'http://www.-fudge.com/', // domains can't have sections starting with a dash. + // ß not allowed in domain names! + 'http://www.testß.com/', + // Bad TLD. + 'http://.www.foo.bar./', + // Domains can't have sections starting with a dash. + // 'http://www.-fudge.com/', + 'http://example.com/index.php?page=this\that', + 'example@example.com', ); foreach ($links as $link) { $valid = link_validate_url($link); $this->assertEqual(FALSE, $valid, 'Testing that ' . $link . ' is not a valid link.'); } } + } diff --git a/sites/all/modules/contrib/fields/link/views/link.views.inc b/sites/all/modules/contrib/fields/link/views/link.views.inc index 27d5182..8c080ad 100644 --- a/sites/all/modules/contrib/fields/link/views/link.views.inc +++ b/sites/all/modules/contrib/fields/link/views/link.views.inc @@ -1,4 +1,7 @@ default_actions(); $form['title'] = array( @@ -52,7 +58,7 @@ class link_views_handler_argument_target extends views_handler_argument { $form['wildcard'] = array( '#prefix' => '
    ', - // prefix and no suffix means these two items will be grouped together. + // Prefix and no suffix means these two items will be grouped together. '#type' => 'textfield', '#title' => t('Wildcard'), '#size' => 20, @@ -125,8 +131,8 @@ class link_views_handler_argument_target extends views_handler_argument { asort($validate_types); $form['validate_type']['#options'] = $validate_types; - // Show this gadget if *anything* but 'none' is selected + // Show this gadget if *anything* but 'none' is selected. $form['validate_fail'] = array( '#type' => 'select', '#title' => t('Action to take if argument does not validate'), @@ -140,10 +146,11 @@ class link_views_handler_argument_target extends views_handler_argument { * * The argument sent may be found at $this->argument. */ - function query($group_by = FALSE) { + public function query($group_by = FALSE) { $this->ensure_my_table(); // Because attributes are stored serialized, our only option is to also // serialize the data we're searching for and use LIKE to find similar data. $this->query->add_where(0, $this->table_alias . ' . ' . $this->real_field . " LIKE '%%%s%'", serialize(array('target' => $this->argument))); } + } diff --git a/sites/all/modules/contrib/fields/link/views/link_views_handler_filter_protocol.inc b/sites/all/modules/contrib/fields/link/views/link_views_handler_filter_protocol.inc index a020b4e..ae00c17 100644 --- a/sites/all/modules/contrib/fields/link/views/link_views_handler_filter_protocol.inc +++ b/sites/all/modules/contrib/fields/link/views/link_views_handler_filter_protocol.inc @@ -7,22 +7,30 @@ /** * Filter handler for limiting a view to URLs of a certain protocol. + * + * @codingStandardsIgnoreStart */ class link_views_handler_filter_protocol extends views_handler_filter_string { + /** * Set defaults for the filter options. + * + * @codingStandardsIgnoreEnd */ - function options(&$options) { - parent::options($options); + function option_definition() { + $options = parent::option_definition(); + $options['operator'] = 'OR'; $options['value'] = 'http'; $options['case'] = 0; + + return $options; } /** * Define the operators supported for protocols. */ - function operators() { + public function operators() { $operators = array( 'OR' => array( 'title' => t('Is one of'), @@ -35,7 +43,13 @@ class link_views_handler_filter_protocol extends views_handler_filter_string { return $operators; } - function options_form(&$form, &$form_state) { + /** + * Options form. + * + * @codingStandardsIgnoreStart + */ + public function options_form(&$form, &$form_state) { + //@codingStandardsIgnoreEnd parent::options_form($form, $form_state); $form['case'] = array( '#type' => 'value', @@ -45,8 +59,11 @@ class link_views_handler_filter_protocol extends views_handler_filter_string { /** * Provide a select list to choose the desired protocols. + * + * @codingStandardsIgnoreStart */ - function value_form(&$form, &$form_state) { + public function value_form(&$form, &$form_state) { + // @codingStandardsIgnoreEnd // We have to make some choices when creating this as an exposed // filter form. For example, if the operator is locked and thus // not rendered, we can't render dependencies; instead we only @@ -61,7 +78,19 @@ class link_views_handler_filter_protocol extends views_handler_filter_string { '#type' => 'select', '#title' => t('Protocol'), '#default_value' => $this->value, - '#options' => drupal_map_assoc(variable_get('filter_allowed_protocols', array('http', 'https', 'ftp', 'news', 'nntp', 'telnet', 'mailto', 'irc', 'ssh', 'sftp', 'webcal'))), + '#options' => drupal_map_assoc(variable_get('filter_allowed_protocols', array( + 'http', + 'https', + 'ftp', + 'news', + 'nntp', + 'telnet', + 'mailto', + 'irc', + 'ssh', + 'sftp', + 'webcal', + ))), '#multiple' => 1, '#size' => 4, '#description' => t('The protocols displayed here are those globally available. You may add more protocols by modifying the filter_allowed_protocols variable in your installation.'), @@ -71,8 +100,11 @@ class link_views_handler_filter_protocol extends views_handler_filter_string { /** * Filter down the query to include only the selected protocols. + * + * @codingStandardsIgnoreStart */ - function op_protocol($field, $upper) { + public function op_protocol($field, $upper) { + // @codingStandardsIgnoreEnd $db_type = db_driver(); $protocols = $this->value; @@ -82,20 +114,25 @@ class link_views_handler_filter_protocol extends views_handler_filter_string { // Simple case, the URL begins with the specified protocol. $condition = $field . ' LIKE \'' . $protocol . '%\''; - // More complex case, no protocol specified but is automatically cleaned up - // by link_cleanup_url(). RegEx is required for this search operation. + // More complex case, no protocol specified but is automatically cleaned + // up by link_cleanup_url(). RegEx is required for this search operation. if ($protocol == 'http') { - $LINK_DOMAINS = _link_domains(); + $link_domains = _link_domains(); if ($db_type == 'pgsql') { - // PostGreSQL code has NOT been tested. Please report any problems to the link issue queue. - // pgSQL requires all slashes to be double escaped in regular expressions. + // PostGreSQL code has NOT been tested. Please report any problems to + // the link issue queue. + // pgSQL requires all slashes to be double escaped in regular + // expressions. + // @codingStandardsIgnoreLine // See http://www.postgresql.org/docs/8.1/static/functions-matching.html#FUNCTIONS-POSIX-REGEXP - $condition .= ' OR ' . $field . ' ~* \'' . '^(([a-z0-9]([a-z0-9\\-_]*\\.)+)(' . $LINK_DOMAINS . '|[a-z][a-z]))' . '\''; + $condition .= ' OR ' . $field . ' ~* \'' . '^(([a-z0-9]([a-z0-9\\-_]*\\.)+)(' . $link_domains . '|[a-z][a-z]))' . '\''; } else { - // mySQL requires backslashes to be double (triple?) escaped within character classes. + // mySQL requires backslashes to be double (triple?) escaped within + // character classes. + // @codingStandardsIgnoreLine // See http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html#operator_regexp - $condition .= ' OR ' . $field . ' REGEXP \'' . '^(([a-z0-9]([a-z0-9\\\-_]*\.)+)(' . $LINK_DOMAINS . '|[a-z][a-z]))' . '\''; + $condition .= ' OR ' . $field . ' REGEXP \'' . '^(([a-z0-9]([a-z0-9\\\-_]*\.)+)(' . $link_domains . '|[a-z][a-z]))' . '\''; } } @@ -104,4 +141,5 @@ class link_views_handler_filter_protocol extends views_handler_filter_string { $this->query->add_where($this->options['group'], implode(' ' . $this->operator . ' ', $where_conditions)); } + } diff --git a/sites/all/modules/contrib/fields/title/CHANGELOG.txt b/sites/all/modules/contrib/fields/title/CHANGELOG.txt index 3ccce43..c34f12d 100644 --- a/sites/all/modules/contrib/fields/title/CHANGELOG.txt +++ b/sites/all/modules/contrib/fields/title/CHANGELOG.txt @@ -3,6 +3,46 @@ Title 7.x-1.x, xxxx-xx-xx ------------------------- +Title 7.x-1.0-alpha9, 2017-01-13 +-------------------------------- +#2757739 by dewalt, Pol: Token value is not sanitized, when replaced from title field. +#2813673 by plach, czigor, Stevel: Tests broken since new permission in drupal core. + + +Title 7.x-1.0-alpha8, 2016-03-28 +-------------------------------- +#2465141 by DuaelFr, Matthijs, jfrederick: Used entity_uri() options if given. +#2602568 by sdstyles: Fixed title_entity_label() should be documented as + callback implementation. +#2605040 by ShaxA, joelpittet, sylus: Removed recursive call to + entity_get_info(). +#2040055 by flux423, cs_shadow, visabhishek, plach, OlyN: Fixed Notice: + Undefined index: safe_value in title_field_formatter_view(). +#2426105 by Peacog, jcisio: Fixed Views "Link this field to the original entity" + doesn't work when using relationship. +#2286147 by plach: Language fallback does not work when an entity translation is + unpublished. +#2286145 by plach: Prevent empty translations from being synced into the legacy + field. +#1772116 by duellj, GaëlG | f4o: Fixed Menu link title is not getting node title + by default. +#1779268 by ndobromirov | brycesenz: Undefined index: field_name in + title_field_views_data_alter(). +#1441224 by MiroslavBanov | OPIN: Fieldgroup and Title break Manage Fields UI. +#1980520 by StoraH, pbz1912: Fixed Empty wrapper tags. +#1920096 by Johnny vd Laar | GiorgosK: Fixed Title incompatibility with the Term + reference widget +#1991712 by milesw: Fixed Title displays wrong revision using Revisioning + module. +#1907078 by sylus: Fixed Undefined index: field_name() in + title_field_replacement_enabled(). +#1961810 by peximo | fcortez: Fixed Wrap tag classes are not being inserted. +#1850866 by B-Prod: Fixed Undefined index 'format' in + title_field_text_with_summary_sync_set(). +#1269076 by plach, das-peter, danielnolde | renat: Fixed Translated + title_field replaces node->title with translated value. + + Title 7.x-1.0-alpha7, 2013-03-18 -------------------------------- #1919640 by peximo: Fixed Empty tag shown when the title field is displayed. diff --git a/sites/all/modules/contrib/fields/title/MAINTAINERS.txt b/sites/all/modules/contrib/fields/title/MAINTAINERS.txt new file mode 100644 index 0000000..f20ad65 --- /dev/null +++ b/sites/all/modules/contrib/fields/title/MAINTAINERS.txt @@ -0,0 +1,13 @@ + +Title is built and maintained by the Drupal project community. +Everyone is encouraged to submit issues and changes (patches) to improve it, and +to contribute in other ways -- see http://drupal.org/contribute to find out how. + +Project maintainers +------------------- + +The Title maintainers oversee the development of the project as a whole. The project +maintainers for Title are: + +- Francesco Placella 'plach' , branch 7.x-1.x +- Sam Becker 'Sam152' , branch 8.x-2.x diff --git a/sites/all/modules/contrib/fields/title/tests/title.test b/sites/all/modules/contrib/fields/title/tests/title.test index e88eb52..e66d095 100644 --- a/sites/all/modules/contrib/fields/title/tests/title.test +++ b/sites/all/modules/contrib/fields/title/tests/title.test @@ -9,6 +9,7 @@ * Tests for legacy field replacement. */ class TitleFieldReplacementTestCase extends DrupalWebTestCase { + public static function getInfo() { return array( 'name' => 'Field replacement', @@ -17,14 +18,17 @@ class TitleFieldReplacementTestCase extends DrupalWebTestCase { ); } - function setUp() { + /** + * {@inheritdoc} + */ + protected function setUp() { parent::setUp('entity', 'field_test', 'title', 'title_test'); } /** * Test field replacement API and workflow. */ - function testFieldReplacementWorkflow() { + public function testFieldReplacementWorkflow() { $info = entity_get_info('test_entity'); $label_key = $info['entity keys']['label']; $field_name = $label_key . '_field'; @@ -72,12 +76,13 @@ class TitleFieldReplacementTestCase extends DrupalWebTestCase { // Clear field cache so synchronization can be performed on field attach // load. cache_clear_all('*', 'cache_field'); + drupal_static_reset(); // Check that the replacing field value is correctly synchronized on load // and view. $entity = title_test_entity_test_load($entity); title_test_phase_check('after_load', $entity); - $build = entity_view('test_entity', array($entity->ftid => $entity)); + entity_view('test_entity', array($entity->ftid => $entity)); foreach (title_test_phase_store() as $phase => $value) { $this->assertTrue($value, t('Field synchronization is correctly performed on %phase.', array('%phase' => $phase))); @@ -94,8 +99,16 @@ class TitleFieldReplacementTestCase extends DrupalWebTestCase { /** * Test field replacement UI. */ - function testFieldReplacementUI() { - $admin_user = $this->drupalCreateUser(array('access administration pages', 'view the administration theme', 'administer content types', 'administer taxonomy', 'administer comments')); + public function testFieldReplacementUI() { + $permissions = array( + 'access administration pages', + 'view the administration theme', + 'administer content types', + 'administer taxonomy', + 'administer comments', + 'administer fields', + ); + $admin_user = $this->drupalCreateUser($permissions); $this->drupalLogin($admin_user); foreach (entity_get_info() as $entity_type => $entity_info) { @@ -146,12 +159,14 @@ class TitleFieldReplacementTestCase extends DrupalWebTestCase { } } } + } /** * Tests for legacy field replacement. */ class TitleAdminSettingsTestCase extends DrupalWebTestCase { + public static function getInfo() { return array( 'name' => 'Admin settings', @@ -160,7 +175,10 @@ class TitleAdminSettingsTestCase extends DrupalWebTestCase { ); } - function setUp() { + /** + * {@inheritdoc} + */ + protected function setUp() { parent::setUp('field_test', 'title', 'title_test'); $admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer taxonomy')); $this->drupalLogin($admin_user); @@ -169,15 +187,18 @@ class TitleAdminSettingsTestCase extends DrupalWebTestCase { /** * Check for automated title_field attachment. */ - function testAutomatedFieldAttachement() { - $this->assertAutomatedFieldAttachement(TRUE); - $this->assertAutomatedFieldAttachement(FALSE); + public function testAutomatedFieldAttachment() { + $this->doTestAutomatedFieldAttachment(TRUE); + $this->doTestAutomatedFieldAttachment(FALSE); } /** - * Check that the fields are replaced or skipped depdening on the given value. + * Check that the fields are replaced or skipped depending on the given value. + * + * @param bool $enabled + * Whether replacement is enabled or not. */ - function assertAutomatedFieldAttachement($enabled) { + public function doTestAutomatedFieldAttachment($enabled) { $edit = array( 'title_taxonomy_term[auto_attach][name]' => $enabled, 'title_taxonomy_term[auto_attach][description]' => $enabled, @@ -197,12 +218,14 @@ class TitleAdminSettingsTestCase extends DrupalWebTestCase { $this->assertTrue(title_field_replacement_enabled($entity_type, $bundle, 'name') == $enabled, 'Name field correctly processed.'); $this->assertTrue(title_field_replacement_enabled($entity_type, $bundle, 'description') == $enabled, 'Description field correctly processed.'); } + } /** * Tests for legacy field replacement. */ class TitleTranslationTestCase extends DrupalWebTestCase { + public static function getInfo() { return array( 'name' => 'Replaced fields translation', @@ -211,11 +234,22 @@ class TitleTranslationTestCase extends DrupalWebTestCase { ); } - function setUp() { - parent::setUp('locale', 'entity_translation', 'title'); + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp('locale', 'entity_translation', 'title', 'field_test', 'title_test'); // Create a power user. - $admin_user = $this->drupalCreateUser(array('administer modules', 'view the administration theme', 'administer languages', 'administer taxonomy', 'administer entity translation', 'translate any entity')); + $permissions = array( + 'administer modules', + 'view the administration theme', + 'administer languages', + 'administer taxonomy', + 'administer entity translation', + 'translate any entity', + ); + $admin_user = $this->drupalCreateUser($permissions); $this->drupalLogin($admin_user); // Enable a translation language. @@ -240,6 +274,7 @@ class TitleTranslationTestCase extends DrupalWebTestCase { $edit = array( 'name' => $this->randomString(), 'machine_name' => $name, + 'entity_translation_taxonomy' => 1, ); $this->drupalPost('admin/structure/taxonomy/add', $edit, t('Save')); $this->vocabulary = taxonomy_vocabulary_machine_name_load($name); @@ -252,19 +287,118 @@ class TitleTranslationTestCase extends DrupalWebTestCase { $t_args = array('%legacy_field' => $legacy_field); $this->assertTrue(field_info_instance($entity_type, $info['field']['field_name'], $name), t('The %legacy_field field has been correctly replaced.', $t_args)); } + + // Ensure static caches do not interfere with API calls. + drupal_static_reset(); } /** - * Test taxonomy translation workflow. + * Tests taxonomy programmatic translation workflow. */ - function testTranslationWorkflow() { + public function testProgrammaticTranslationWorkflow() { + // Create a taxonomy term and assign it an original language different from + // the default language. + $langcode = 'it'; + $original_values = array( + 'name' => $langcode . '_' . $this->randomName(), + 'description' => $langcode . '_' . $this->randomName(), + ); + $term = (object) ($original_values + array( + 'format' => 'filtered_html', + 'vocabulary_machine_name' => $this->vocabulary->machine_name, + 'vid' => $this->vocabulary->vid, + )); + entity_translation_get_handler('taxonomy_term', $term)->setOriginalLanguage($langcode); + taxonomy_term_save($term); + $this->assertTrue($this->checkLegacyValues($term, $original_values), 'Legacy field values correctly stored.'); + $term = $this->termLoad($term->tid); + $this->assertTrue($this->checkFieldValues($term, $original_values, $langcode), 'Replacing field values correctly created from the legacy field values.'); + + // Pollute synchronization cache to ensure the expected values are stored + // anyway. + title_entity_sync('taxonomy_term', $term, $langcode); + + // Create a translation using the default language. + $translation_langcode = language_default()->language; + $translated_values = array( + 'name' => $translation_langcode . '_' . $this->randomName(), + 'description' => $translation_langcode . '_' . $this->randomName(), + ); + foreach ($translated_values as $name => $value) { + $term->{$name} = $value; + } + $translation = array( + 'language' => $translation_langcode, + 'source' => $langcode, + 'uid' => $this->loggedInUser->uid, + 'status' => 1, + 'translate' => 0, + 'created' => REQUEST_TIME, + 'changed' => REQUEST_TIME, + ); + entity_translation_get_handler('taxonomy_term', $term)->setTranslation($translation); + taxonomy_term_save($term); + $this->assertTrue($this->checkLegacyValues($term, $original_values), 'Legacy field values correctly stored.'); + $term = $this->termLoad($term->tid, $translation_langcode); + $this->assertTrue($this->checkFieldValues($term, $translated_values, $translation_langcode), 'Replacing field translations correctly created.'); + $this->assertTrue($this->checkFieldValues($term, $original_values, $langcode, FALSE), 'Replacing field original values correctly preserved.'); + + // Delete the translation. + entity_translation_get_handler('taxonomy_term', $term)->removeTranslation($translation_langcode); + taxonomy_term_save($term); + $this->assertTrue($this->checkLegacyValues($term, $original_values), 'Legacy field values correctly stored.'); + $term = $this->termLoad($term->tid, $langcode); + $this->assertTrue($this->checkFieldValues($term, $original_values, $langcode), 'Replacing field translations correctly deleted.'); + + // Make the term language neutral. + entity_translation_get_handler('taxonomy_term', $term)->setOriginalLanguage(LANGUAGE_NONE); + foreach ($original_values as $name => $value) { + $field_name = $name . '_field'; + $term->{$field_name}[LANGUAGE_NONE] = $term->{$field_name}[$langcode]; + $term->{$field_name}[$langcode] = array(); + } + taxonomy_term_save($term); + $this->assertTrue($this->checkLegacyValues($term, $original_values), 'Legacy field values correctly stored.'); + $term = $this->termLoad($term->tid); + $this->assertTrue($this->checkFieldValues($term, $original_values, LANGUAGE_NONE), 'Term original language correctly changed to the former translation language.'); + + // Change the term language to the former translation language. + entity_translation_get_handler('taxonomy_term', $term)->setOriginalLanguage($translation_langcode); + foreach ($original_values as $name => $value) { + $field_name = $name . '_field'; + $term->{$field_name}[$translation_langcode] = $term->{$field_name}[LANGUAGE_NONE]; + $term->{$field_name}[LANGUAGE_NONE] = array(); + } + taxonomy_term_save($term); + $this->assertTrue($this->checkLegacyValues($term, $original_values), 'Legacy field values correctly stored.'); + $term = $this->termLoad($term->tid, $translation_langcode); + $this->assertTrue($this->checkFieldValues($term, $original_values, $translation_langcode), 'Term original language correctly changed to language neutral.'); + + // Make a replacing field untranslatable and change its value. + $field_name = 'name_field'; + $field = field_info_field($field_name); + $field['translatable'] = FALSE; + field_update_field($field); + $original_values['name'] = LANGUAGE_NONE . '_' . $this->randomName(); + $term->name = $original_values['name']; + taxonomy_term_save($term); + $this->assertTrue($this->checkLegacyValues($term, $original_values), 'Legacy field values correctly stored.'); + $term = $this->termLoad($term->tid); + $this->assertEqual($term->{$field_name}[LANGUAGE_NONE][0]['value'], $original_values['name'], 'Untranslatable replacing field on translatable entity correctly handled.'); + } + + /** + * Tests taxonomy form translation workflow. + */ + public function testFormTranslationWorkflow() { // Create a taxonomy term and check that legacy fields are properly // populated. $original_values = array( 'name' => $this->randomName(), 'description' => $this->randomName(), ); - $edit = $this->editValues($original_values, 'en'); + $langcode = 'en'; + $edit = $this->editValues($original_values, $langcode); $this->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/add', $edit, t('Save')); $term = current(entity_load('taxonomy_term', FALSE, array('name' => $original_values['name']), TRUE)); $this->assertEqual($term->description, $original_values['description'], t('Taxonomy term created.')); @@ -275,11 +409,12 @@ class TitleTranslationTestCase extends DrupalWebTestCase { 'name' => $this->randomName(), 'description' => $this->randomName(), ); + $translation_langcode = 'it'; $edit = $this->editValues($translated_values, 'it'); - $this->drupalPost('it/taxonomy/term/' . $term->tid . '/edit/add/en/it', $edit, t('Save')); - $term = current(entity_load('taxonomy_term', array($term->tid), array(), TRUE)); - $this->assertTrue($this->checkFieldValues($term, $translated_values, 'it'), t('Taxonomy term translation created.')); - $this->assertTrue($this->checkFieldValues($term, $original_values, 'en'), t('Taxonomy term original values preserved.')); + $this->drupalPost($translation_langcode . '/taxonomy/term/' . $term->tid . '/edit/add/' . $langcode . '/' . $translation_langcode, $edit, t('Save')); + $term = $this->termLoad($term->tid); + $this->assertTrue($this->checkFieldValues($term, $translated_values, $translation_langcode, FALSE), t('Taxonomy term translation created.')); + $this->assertTrue($this->checkFieldValues($term, $original_values, $langcode), t('Taxonomy term original values preserved.')); // Check that legacy fields have the correct values. $this->assertEqual($term->name, $original_values['name'], t('Taxonomy term name correctly stored.')); @@ -291,31 +426,69 @@ class TitleTranslationTestCase extends DrupalWebTestCase { 'name' => $this->randomName(), 'description' => $this->randomName(), ); - $edit = $this->editValues($translated_values, 'it'); - $this->drupalPost('it/taxonomy/term/' . $term->tid . '/edit/it', $edit, t('Save')); - $term = current(entity_load('taxonomy_term', array($term->tid), array(), TRUE)); - $this->assertTrue($this->checkFieldValues($term, $translated_values, 'it'), t('Taxonomy term translation updated.')); - $this->assertTrue($this->checkFieldValues($term, $original_values, 'en'), t('Taxonomy term original values preserved.')); + $edit = $this->editValues($translated_values, $translation_langcode); + $this->drupalPost($translation_langcode . '/taxonomy/term/' . $term->tid . '/edit/' . $translation_langcode, $edit, t('Save')); + $term = $this->termLoad($term->tid); + $this->assertTrue($this->checkFieldValues($term, $translated_values, $translation_langcode, FALSE), t('Taxonomy term translation updated.')); + $this->assertTrue($this->checkFieldValues($term, $original_values, $langcode), t('Taxonomy term original values preserved.')); // Check that legacy fields have the correct values. $this->assertEqual($term->name, $original_values['name'], t('Taxonomy term name correctly stored.')); $this->assertEqual($term->description, $original_values['description'], t('Taxonomy term description correctly stored.')); } + /** + * Loads a term using the given language as active language. + * + * @param int $tid + * The term identifier. + * @param string|null $langcode + * (optional) The active language to be set. Defaults to none. + * + * @return object|bool + * A term object. + */ + protected function termLoad($tid, $langcode = NULL) { + drupal_static_reset(); + title_active_language($langcode); + return current(entity_load('taxonomy_term', array($tid), array(), TRUE)); + } + + /** + * Returns the drupalPost() $edit array corresponding to the given values. + */ protected function editValues($values, $langcode) { $edit = array(); - foreach ($values as $key => $value) { - $edit["{$key}_field[{$langcode}][0][value]"] = $value; + foreach ($values as $name => $value) { + $edit["{$name}_field[{$langcode}][0][value]"] = $value; } return $edit; } - protected function checkFieldValues($term, $values, $langcode) { - foreach ($values as $key => $value) { - if ($term->{$key . '_field'}[$langcode][0]['value'] != $value) { + /** + * Checks that the field values and optionally the legacy ones match the given values. + */ + protected function checkFieldValues($term, $values, $langcode, $legacy_match = TRUE) { + foreach ($values as $name => $value) { + $field_value = $term->{$name . '_field'}[$langcode][0]['value']; + if ($field_value != $value || ($legacy_match !== ($field_value == $term->{$name}))) { return FALSE; } } return TRUE; } + + /** + * Checks that the legacy field values stored in the database match the given values. + */ + protected function checkLegacyValues($term, $values) { + $record = db_query('SELECT * FROM {taxonomy_term_data} t WHERE t.tid = :tid', array(':tid' => $term->tid))->fetchAssoc(); + foreach ($values as $name => $value) { + if ($record[$name] != $value) { + return FALSE; + } + } + return TRUE; + } + } diff --git a/sites/all/modules/contrib/fields/title/tests/title_test.info b/sites/all/modules/contrib/fields/title/tests/title_test.info index 45846d3..1e5d2e5 100644 --- a/sites/all/modules/contrib/fields/title/tests/title_test.info +++ b/sites/all/modules/contrib/fields/title/tests/title_test.info @@ -7,9 +7,9 @@ dependencies[] = title dependencies[] = entity dependencies[] = entity_translation -; Information added by drupal.org packaging script on 2013-03-18 -version = "7.x-1.0-alpha7" +; Information added by Drupal.org packaging script on 2017-01-13 +version = "7.x-1.0-alpha9" core = "7.x" project = "title" -datestamp = "1363626024" +datestamp = "1484302985" diff --git a/sites/all/modules/contrib/fields/title/title.admin.inc b/sites/all/modules/contrib/fields/title/title.admin.inc index 9cb0065..380cabe 100644 --- a/sites/all/modules/contrib/fields/title/title.admin.inc +++ b/sites/all/modules/contrib/fields/title/title.admin.inc @@ -14,20 +14,16 @@ function title_form_field_ui_overview(&$form, &$form_state) { if (!empty($entity_info['field replacement'])) { $field_replacement_info = $entity_info['field replacement']; $admin_path = _field_ui_bundle_admin_path($form['#entity_type'], $form['#bundle']); - $form['fields']['#header'][6]['colspan'] += 1; foreach (element_children($form['fields']) as $field_name) { if (isset($field_replacement_info[$field_name])) { - $form['fields'][$field_name]['field_replacement'] = array( + $form['fields'][$field_name]['delete'] = array( '#type' => 'link', '#title' => t('replace'), '#href' => $admin_path . '/fields/replace/' . $field_name, '#options' => array('attributes' => array('title' => t('Replace %field with a customizable field instance that can be translated.', array('%field' => $field_name)))), ); } - else { - $form['fields'][$field_name]['field_replacement'] = array(); - } } } } diff --git a/sites/all/modules/contrib/fields/title/title.core.inc b/sites/all/modules/contrib/fields/title/title.core.inc index d75e656..2af63f9 100644 --- a/sites/all/modules/contrib/fields/title/title.core.inc +++ b/sites/all/modules/contrib/fields/title/title.core.inc @@ -127,7 +127,7 @@ function title_field_text_sync_get($entity_type, $entity, $legacy_field, $info, $items = $entity->{$field_name}[$langcode]; $value = !empty($items[0]['value']) ? $items[0]['value'] : NULL; } - return $value; + return array($legacy_field => $value); } /** @@ -142,25 +142,31 @@ function title_field_text_sync_set($entity_type, $entity, $legacy_field, $info, */ function title_field_text_with_summary_sync_get($entity_type, $entity, $legacy_field, $info, $langcode) { $value = NULL; + $format = NULL; $format_key = $info['additional keys']['format']; $field_name = $info['field']['field_name']; // Return values only if there is any available to process for the current // language. if (!empty($entity->{$field_name}[$langcode]) && is_array($entity->{$field_name}[$langcode])) { - $entity->{$format_key} = $entity->{$field_name}[$langcode][0]['format']; $items = $entity->{$field_name}[$langcode]; $value = !empty($items[0]['value']) ? $items[0]['value'] : NULL; + $format = $entity->{$field_name}[$langcode][0]['format']; } - return $value; + return array( + $legacy_field => $value, + $format_key => $format, + ); } /** * Sync back callback for the text with summary field type. */ function title_field_text_with_summary_sync_set($entity_type, $entity, $legacy_field, $info, $langcode) { - $format_key = $info['additional keys']['format']; - $entity->{$info['field']['field_name']}[$langcode][0]['value'] = $entity->{$legacy_field}; - $entity->{$info['field']['field_name']}[$langcode][0]['format'] = $entity->{$format_key}; + foreach (array('value' => $legacy_field, 'format' => $info['additional keys']['format']) as $column => $name) { + if (isset($entity->{$name})) { + $entity->{$info['field']['field_name']}[$langcode][0][$column] = $entity->{$name}; + } + } } /** diff --git a/sites/all/modules/contrib/fields/title/title.field.inc b/sites/all/modules/contrib/fields/title/title.field.inc index d94905b..1523fc1 100644 --- a/sites/all/modules/contrib/fields/title/title.field.inc +++ b/sites/all/modules/contrib/fields/title/title.field.inc @@ -97,36 +97,48 @@ function title_field_formatter_settings_summary($field, $instance, $view_mode) { */ function title_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { $settings = $display['settings']; - $output = isset($items[0]) ? $items[0]['safe_value'] : ''; - - if (!empty($output) && $settings['title_link'] == 'content') { - $uri = entity_uri($entity_type, $entity); - $output = l($output, $uri['path'], array('html' => TRUE)); + $output = ''; + if (isset($items[0]['safe_value'])) { + $output = $items[0]['safe_value']; } + elseif (isset($items[0]['value'])) { + $output = _text_sanitize($instance, $langcode, $items[0], 'value'); + } + $element = array(); - $wrap_tag = empty($settings['title_style']) ? '_none' : $settings['title_style']; - - if ($wrap_tag != '_none') { - $element = array( - 'element' => array( - '#tag' => $wrap_tag, - '#value' => $output, - ), - ); - - if (!empty($settings['title_class'])) { - $element['#attributes'] = array('class' => $settings['title_class']); + if (!empty($output)) { + if ($settings['title_link'] == 'content') { + $uri = entity_uri($entity_type, $entity); + $options = array('html' => TRUE); + if (!empty($uri['options'])) { + $options = array_merge($options, $uri['options']); + } + $output = l($output, $uri['path'], $options); } - $output = theme('html_tag', $element); + $wrap_tag = empty($settings['title_style']) ? '_none' : $settings['title_style']; + + if ($wrap_tag != '_none') { + $variables = array( + 'element' => array( + '#tag' => $wrap_tag, + '#value' => $output, + ), + ); + + if (!empty($settings['title_class'])) { + $variables['element']['#attributes'] = array('class' => $settings['title_class']); + } + + $output = theme('html_tag', $variables); + } + + $element = array( + array( + '#markup' => $output, + ), + ); } - - $element = array( - array( - '#markup' => $output, - ), - ); - return $element; } diff --git a/sites/all/modules/contrib/fields/title/title.info b/sites/all/modules/contrib/fields/title/title.info index a331e8f..78153e3 100644 --- a/sites/all/modules/contrib/fields/title/title.info +++ b/sites/all/modules/contrib/fields/title/title.info @@ -9,9 +9,9 @@ files[] = title.module files[] = views/views_handler_title_field.inc files[] = tests/title.test -; Information added by drupal.org packaging script on 2013-03-18 -version = "7.x-1.0-alpha7" +; Information added by Drupal.org packaging script on 2017-01-13 +version = "7.x-1.0-alpha9" core = "7.x" project = "title" -datestamp = "1363626024" +datestamp = "1484302985" diff --git a/sites/all/modules/contrib/fields/title/title.module b/sites/all/modules/contrib/fields/title/title.module index 06b6b3a..2342f81 100644 --- a/sites/all/modules/contrib/fields/title/title.module +++ b/sites/all/modules/contrib/fields/title/title.module @@ -33,6 +33,7 @@ function title_module_implements_alter(&$implementations, $hook) { // The following hook implementations should be executed as last ones. case 'entity_info_alter': case 'entity_presave': + case 'field_attach_presave': $implementations['title'] = $group; break; @@ -104,17 +105,7 @@ function title_field_replacement_info($entity_type, $legacy_field = NULL) { } /** - * Return an entity label value. - * - * @param $entity - * The entity whose label has to be displayed. - * @param $type - * The name of the entity type. - * @param $langcode - * (Optional) The language the entity label has to be displayed in. - * - * @return - * The entity label as a string value. + * Implements callback_entity_info_label(). */ function title_entity_label($entity, $type, $langcode = NULL) { $entity_info = entity_get_info($type); @@ -125,7 +116,8 @@ function title_entity_label($entity, $type, $langcode = NULL) { // If field replacement is enabled we use the replacing field value. if (title_field_replacement_enabled($type, $bundle, $legacy_field)) { $langcode = field_language($type, $entity, $info['field']['field_name'], $langcode); - return $info['callbacks']['sync_get']($type, $entity, $legacy_field, $info, $langcode); + $values = $info['callbacks']['sync_get']($type, $entity, $legacy_field, $info, $langcode); + return $values[$legacy_field]; } // Otherwise if we have a fallback defined we use the original label callback. elseif (isset($entity_info['label fallback']['title']) && function_exists($entity_info['label fallback']['title'])) { @@ -139,8 +131,57 @@ function title_entity_label($entity, $type, $langcode = NULL) { /** * Implements hook_entity_presave(). */ -function title_entity_presave($entity, $type) { - title_entity_sync($type, $entity, NULL, TRUE); +function title_entity_presave($entity, $entity_type) { + $entity_langcode = title_entity_language($entity_type, $entity); + $langcode = $entity_langcode; + + // If Entity Translation is enabled and the entity type is transltable,we need + // to check if we have a translation for the current active language. If so we + // need to synchronize the legacy field values into the replacing field + // translations in the active language. + if (module_invoke('entity_translation', 'enabled', $entity_type)) { + $langcode = title_active_language(); + $translations = entity_translation_get_handler($entity_type, $entity)->getTranslations(); + // If we are removing a translation for the active language we need to skip + // reverse synchronization, as we would store empty values in the original + // replacing fields immediately afterwards. + if (!isset($translations->data[$langcode])) { + $langcode = isset($translations->hook[$langcode]['hook']) && $translations->hook[$langcode]['hook'] == 'delete' ? FALSE : $entity_langcode; + } + } + + // Perform reverse synchronization to retain any change in the legacy field + // values. We must avoid doing this twice as we might overwrite the already + // synchronized values, if we are updating an existing entity. + if ($langcode) { + title_entity_sync($entity_type, $entity, $langcode, TRUE); + } + + // If we are not dealing with the entity language, we need to synchronize the + // original values into the legacy fields to ensure they are always stored in + // the entity table. + if ($entity_langcode != $langcode) { + list($id, , ) = entity_extract_ids($entity_type, $entity); + $sync = &drupal_static('title_entity_sync', array()); + unset($sync[$entity_type][$id]); + title_entity_sync($entity_type, $entity, $entity_langcode); + } +} + +/** + * Implements hook_field_attach_update(). + */ +function title_field_attach_update($entity_type, $entity) { + // Reset the field_attach_presave static cache so that subsequent saves work + // correctly. + $sync = &drupal_static('title_field_attach_presave', array()); + list($id, , ) = entity_extract_ids($entity_type, $entity); + unset($sync[$entity_type][$id]); + + // Immediately after saving the entity we need to ensure that the legacy field + // holds a value corresponding to the current active language, as it were + // just loaded. + title_entity_sync($entity_type, $entity); } /** @@ -152,7 +193,10 @@ function title_entity_presave($entity, $type) { * @see title_entity_load() */ function title_field_attach_load($entity_type, $entities, $age, $options) { - // @todo: Do we need to handle revisions here? + // Allow values to re-sync when field_attach_load_revision() is called. + if ($age == FIELD_LOAD_REVISION) { + title_entity_sync_static_reset($entity_type, array_keys($entities)); + } title_entity_load($entities, $entity_type); } @@ -164,6 +208,9 @@ function title_field_attach_load($entity_type, $entities, $age, $options) { * replaced fields. */ function title_entity_load($entities, $type) { + // Load entity translations otherwise field language will not be computed + // correctly. + module_invoke('entity_translation', 'entity_load', $entities, $type); foreach ($entities as &$entity) { // Synchronize values from the regular field unless we are intializing it. title_entity_sync($type, $entity, NULL, !empty($GLOBALS['title_field_replacement_init'])); @@ -183,20 +230,10 @@ function title_entitycache_load($entities, $type) { /** * Implements hook_entitycache_reset(). * - * If the entity cache is reseted the field sync has to be done again. + * When the entity cache is reset the field sync has to be done again. */ function title_entitycache_reset($ids, $entity_type) { - $sync = &drupal_static('title_entity_sync', array()); - if (!empty($ids)) { - // Clear specific ids. - foreach ($ids as $id) { - unset($sync[$entity_type][$id]); - } - } - elseif (!empty($sync[$entity_type])) { - // Reset cache for an entity_type. - $sync[$entity_type] = array(); - } + title_entity_sync_static_reset($entity_type, $ids); } /** @@ -227,7 +264,9 @@ function title_entity_prepare_view($entities, $type, $langcode) { */ function title_field_replacement_enabled($entity_type, $bundle, $legacy_field) { $info = title_field_replacement_info($entity_type, $legacy_field); - $instance = field_info_instance($entity_type, $info['field']['field_name'], $bundle); + if (!empty($info['field']['field_name'])) { + $instance = field_info_instance($entity_type, $info['field']['field_name'], $bundle); + } return !empty($instance); } @@ -374,10 +413,13 @@ function title_field_replacement_init($entity_type, $bundle, $legacy_field, $ids function title_entity_sync($entity_type, &$entity, $langcode = NULL, $set = FALSE) { $sync = &drupal_static(__FUNCTION__, array()); list($id, , $bundle) = entity_extract_ids($entity_type, $entity); - $langcode = field_valid_language($langcode, FALSE); - // We do not need to perform this more than once. - if (!empty($id) && !empty($sync[$entity_type][$id][$langcode][$set])) { + if (!isset($langcode)) { + $langcode = $set ? title_entity_language($entity_type, $entity) : title_active_language(); + } + + // We do not need to perform synchronization more than once. + if (!$set && !empty($id) && !empty($sync[$entity_type][$id][$langcode][$set])) { return; } @@ -394,6 +436,26 @@ function title_entity_sync($entity_type, &$entity, $langcode = NULL, $set = FALS } } +/** + * Reset the list of entities whose fields have already been synchronized. + * + * @param $entity_type + * The name of the entity type. + * @param $entity_ids + * Either an array of entity IDs to reset or NULL to reset all. + */ +function title_entity_sync_static_reset($entity_type, $entity_ids = NULL) { + $sync = &drupal_static('title_entity_sync', array()); + if (is_array($entity_ids)) { + foreach ($entity_ids as $id) { + unset($sync[$entity_type][$id]); + } + } + else { + unset($sync[$entity_type]); + } +} + /** * Synchronize a single legacy field with its regular field value. * @@ -405,8 +467,8 @@ function title_entity_sync($entity_type, &$entity, $langcode = NULL, $set = FALS * The name of the legacy field to be replaced. * @param $field_name * The regular field to use as source value. - * @param $display - * Specifies if synchronization is being performed on display or on save. + * @param $info + * Field replacement information for the given entity. * @param $langcode * The field language to use for the source value. */ @@ -416,7 +478,16 @@ function title_field_sync_get($entity_type, $entity, $legacy_field, $info, $lang $entity->{$legacy_field . '_original'} = $entity->{$legacy_field}; // Find out the actual language to use (field might be untranslatable). $langcode = field_language($entity_type, $entity, $info['field']['field_name'], $langcode); - $entity->{$legacy_field} = $info['callbacks']['sync_get']($entity_type, $entity, $legacy_field, $info, $langcode); + $values = $info['callbacks']['sync_get']($entity_type, $entity, $legacy_field, $info, $langcode); + foreach ($values as $name => $value) { + if ($value !== NULL) { + $entity->{$name} = $value; + } + } + // Ensure we do not pollute field language static cache. + $cache = &drupal_static('field_language'); + list($id, ,) = entity_extract_ids($entity_type, $entity); + unset($cache[$entity_type][$id]); } } @@ -431,18 +502,45 @@ function title_field_sync_get($entity_type, $entity, $legacy_field, $info, $lang * The name of the legacy field to be replaced. * @param $field_name * The regular field to use as source value. - * @param $display - * Specifies if synchronization is being performed on display or on save. + * @param $info + * Field replacement information for the given entity. * @param $langcode - * The field language to use for the source value. + * The field language to use for the target value. */ -function title_field_sync_set($entity_type, $entity, $legacy_field, $info) { +function title_field_sync_set($entity_type, $entity, $legacy_field, $info, $langcode) { if (property_exists($entity, $legacy_field)) { - $langcode = title_entity_language($entity_type, $entity); + // Find out the actual language to use (field might be untranslatable). + $field = field_info_field($info['field']['field_name']); + $langcode = field_is_translatable($entity_type, $field) ? $langcode : LANGUAGE_NONE; $info['callbacks']['sync_set']($entity_type, $entity, $legacy_field, $info, $langcode); } } +/** + * Returns and optionally stores the active language. + * + * @param string $langcode + * (optional) The active language to be set. If none is provided the active + * language is just returned. + * + * @return string + * The active language code. Defaults to the current content language. + */ +function title_active_language($langcode = NULL) { + static $drupal_static_fast; + if (!isset($drupal_static_fast)) { + $drupal_static_fast['active_language'] = &drupal_static(__FUNCTION__); + } + $active_langcode = &$drupal_static_fast['active_language']; + if (isset($langcode)) { + $active_langcode = $langcode; + } + if (empty($active_langcode)) { + $active_langcode = $GLOBALS['language_content']->language; + } + return $active_langcode; +} + /** * Provide the original entity language. * @@ -485,6 +583,11 @@ function title_field_attach_form($entity_type, $entity, &$form, &$form_state, $l if (isset($form[$legacy_field]['#access'])) { $form[$info['field']['field_name']]['#access'] = $form[$legacy_field]['#access']; } + + // Add class from legacy field so behaviors can still be applied on + // title widget. + $form[$info['field']['field_name']]['#attributes']['class'] = array('form-item-' . $legacy_field); + // Restrict access to the legacy field form element and mark it as // replaced. $form[$legacy_field]['#access'] = FALSE; @@ -510,7 +613,7 @@ function title_field_attach_submit($entity_type, $entity, $form, &$form_state) { // drupal_array_set_nested_value(). $values = $form_state['values']; $values = drupal_array_get_nested_value($values, $form['#parents']); - $langcode = title_entity_language($entity_type, $entity); + $langcode = entity_language($entity_type, $entity); foreach ($fr_info as $legacy_field => $info) { if (!empty($form[$legacy_field]['#field_replacement'])) { @@ -641,23 +744,26 @@ function title_tokens_alter(array &$replacements, array $context) { $entity = $context['data'][$context['type']]; list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); $options = $context['options']; + $sanitize = !empty($options['sanitize']); - $langcode = NULL; - if (isset($options['language'])) { - $langcode = $options['language']->language; - } + // Since Title tokens are mostly used in storage contexts we default to + // the current working language, that is the entity language. Modules + // using Title tokens in display contexts need to specify the current + // display language. + $langcode = isset($options['language']) ? $options['language']->language : entity_language($entity_type, $entity); if ($fr_info) { foreach ($fr_info as $legacy_field => $info) { if (title_field_replacement_enabled($entity_type, $bundle, $legacy_field)) { if (isset($context['tokens'][$legacy_field])) { - title_field_sync_get($entity_type, $entity, $legacy_field, $info, $langcode); - $item = $entity->{$legacy_field}; + $langcode = field_language($entity_type, $entity, $info['field']['field_name'], $langcode); + $values = $info['callbacks']['sync_get']($entity_type, $entity, $legacy_field, $info, $langcode); + $item = $values[$legacy_field]; if (!empty($item)) { if (is_array($item)) { $item = reset($item); } - $replacements[$context['tokens'][$legacy_field]] = $item; + $replacements[$context['tokens'][$legacy_field]] = $sanitize ? check_plain($item) : $item; } } } @@ -851,3 +957,38 @@ function title_field_attach_create_bundle($entity_type, $bundle) { } } } + +/** + * Implements hook_field_info_alter(). + */ +function title_field_info_alter(&$info) { + $supported_types = array('taxonomy_term_reference' => TRUE); + foreach ($info as $field_type => &$field_type_info) { + if (isset($supported_types[$field_type])) { + if (!isset($field_type_info['settings'])) { + $field_type_info['settings'] = array(); + } + $field_type_info['settings'] += array('options_list_callback' => 'title_taxonomy_allowed_values'); + } + } +} + +/** + * Return taxonomy term values for taxonomy reference fields. + */ +function title_taxonomy_allowed_values($field) { + $bundle = !empty($field['settings']['allowed_values'][0]['vocabulary']) ? $field['settings']['allowed_values'][0]['vocabulary'] : NULL; + if ($bundle && ($label = title_field_replacement_get_label_field('taxonomy_term', $bundle))) { + $options = array(); + foreach ($field['settings']['allowed_values'] as $tree) { + $vocabulary = taxonomy_vocabulary_machine_name_load($tree['vocabulary']); + if ($vocabulary && ($terms = taxonomy_get_tree($vocabulary->vid, $tree['parent'], NULL, TRUE))) { + foreach ($terms as $term) { + $options[$term->tid] = str_repeat('-', $term->depth) . entity_label('taxonomy_term', $term); + } + } + } + return $options; + } + return taxonomy_allowed_values($field); +} diff --git a/sites/all/modules/contrib/fields/title/views/title.views.inc b/sites/all/modules/contrib/fields/title/views/title.views.inc index f973d9f..9ae50c8 100644 --- a/sites/all/modules/contrib/fields/title/views/title.views.inc +++ b/sites/all/modules/contrib/fields/title/views/title.views.inc @@ -6,13 +6,14 @@ */ function title_field_views_data_alter(&$data) { foreach (entity_get_info() as $entity_type => $entity_info) { - $replacements = title_field_replacement_info($entity_type); - if ($replacements) { - foreach ($replacements as $replacement) { - $field = field_info_field($replacement['field']['field_name']); - $table = _field_sql_storage_tablename($field); - if (isset($data[$table][$field['field_name']])) { - $data[$table][$field['field_name']]['field']['handler'] = 'views_handler_title_field'; + if (!empty($entity_info['field replacement'])) { + foreach ($entity_info['field replacement'] as $replacement) { + if (isset($replacement['field']['field_name'])) { + $field = field_info_field($replacement['field']['field_name']); + $table = _field_sql_storage_tablename($field); + if (isset($data[$table][$field['field_name']])) { + $data[$table][$field['field_name']]['field']['handler'] = 'views_handler_title_field'; + } } } } diff --git a/sites/all/modules/contrib/fields/title/views/views_handler_title_field.inc b/sites/all/modules/contrib/fields/title/views/views_handler_title_field.inc index 03516e9..3a11ad1 100644 --- a/sites/all/modules/contrib/fields/title/views/views_handler_title_field.inc +++ b/sites/all/modules/contrib/fields/title/views/views_handler_title_field.inc @@ -36,13 +36,13 @@ class views_handler_title_field extends views_handler_field_field { if (!empty($this->options['link_to_entity'])) { $values = $this->original_values; $entity_type = $this->definition['entity_tables'][$this->base_table]; - $entity_info = entity_get_info($entity_type); - $key = $entity_info['entity keys']['id']; + $key = $this->field_alias; if (!empty($values->_field_data[$key]['entity'])) { $entity = $values->_field_data[$key]['entity']; $uri = entity_uri($entity_type, $entity); $this->options['alter']['make_link'] = TRUE; $this->options['alter']['path'] = $uri['path']; + $this->options['alter']['options'] = !empty($uri['options']) ? $uri['options'] : array(); } } return parent::render_item($count, $item); diff --git a/sites/all/modules/contrib/fields/video_embed_field/img/vimeo.jpg b/sites/all/modules/contrib/fields/video_embed_field/img/vimeo.jpg new file mode 100644 index 0000000..dd70565 Binary files /dev/null and b/sites/all/modules/contrib/fields/video_embed_field/img/vimeo.jpg differ diff --git a/sites/all/modules/contrib/fields/video_embed_field/img/youtube.jpg b/sites/all/modules/contrib/fields/video_embed_field/img/youtube.jpg new file mode 100644 index 0000000..2012390 Binary files /dev/null and b/sites/all/modules/contrib/fields/video_embed_field/img/youtube.jpg differ diff --git a/sites/all/modules/contrib/fields/video_embed_field/plugins/export_ui/video_embed_field_export_ui.inc b/sites/all/modules/contrib/fields/video_embed_field/plugins/export_ui/video_embed_field_export_ui.inc old mode 100755 new mode 100644 index 1ac41d6..a43ca66 --- a/sites/all/modules/contrib/fields/video_embed_field/plugins/export_ui/video_embed_field_export_ui.inc +++ b/sites/all/modules/contrib/fields/video_embed_field/plugins/export_ui/video_embed_field_export_ui.inc @@ -1,16 +1,21 @@ 'vef_video_styles', // As defined in hook_schema(). - 'access' => 'administer video styles', // Define a permission users must have to access these pages. + 'schema' => 'vef_video_styles', + 'access' => 'administer video styles', // Define the menu item. 'menu' => array( - 'menu prefix' => 'admin/config/media', + 'menu prefix' => 'admin/config/media/vef', 'menu item' => 'vef_video_styles', - 'menu title' => 'Configure Video Embed Styles', + 'menu title' => 'Video Embed Styles', 'menu description' => 'Administer Video Embed Field\'s video styles.', ), // Define user interface texts. @@ -23,4 +28,7 @@ $plugin = array( 'settings' => 'video_embed_field_video_style_form', // 'submit' and 'validate' are also valid callbacks. ), + 'export' => array( + 'admin_title' => 'title', + ), ); diff --git a/sites/all/modules/contrib/fields/video_embed_field/video-embed-field-embed-code.tpl.php b/sites/all/modules/contrib/fields/video_embed_field/video-embed-field-embed-code.tpl.php index b049be9..c06ddef 100644 --- a/sites/all/modules/contrib/fields/video_embed_field/video-embed-field-embed-code.tpl.php +++ b/sites/all/modules/contrib/fields/video_embed_field/video-embed-field-embed-code.tpl.php @@ -19,4 +19,4 @@
    -
    \ No newline at end of file +
    diff --git a/sites/all/modules/contrib/fields/video_embed_field/video_embed_brightcove/README.txt b/sites/all/modules/contrib/fields/video_embed_field/video_embed_brightcove/README.txt new file mode 100644 index 0000000..2deae7f --- /dev/null +++ b/sites/all/modules/contrib/fields/video_embed_field/video_embed_brightcove/README.txt @@ -0,0 +1,5 @@ +Brightcove Video Embed Field +---------------------------- +This module parses a Brightcove URL via the Video Embed Field + +Example URL: http://link.brightcove.com/services/player/bcpid3303744435001?bckey=AQ~~,AAAAAGL7jok~,vslbwQw3pdWM_Ya9IsUVNvJJIhV9YG1S&bctid=3118695475001 diff --git a/sites/all/modules/contrib/fields/video_embed_field/video_embed_brightcove/img/brightcove.jpg b/sites/all/modules/contrib/fields/video_embed_field/video_embed_brightcove/img/brightcove.jpg new file mode 100644 index 0000000..d72cdca Binary files /dev/null and b/sites/all/modules/contrib/fields/video_embed_field/video_embed_brightcove/img/brightcove.jpg differ diff --git a/sites/all/modules/contrib/fields/video_embed_field/video_embed_brightcove/video_embed_brightcove.info b/sites/all/modules/contrib/fields/video_embed_field/video_embed_brightcove/video_embed_brightcove.info new file mode 100644 index 0000000..8ca93ff --- /dev/null +++ b/sites/all/modules/contrib/fields/video_embed_field/video_embed_brightcove/video_embed_brightcove.info @@ -0,0 +1,14 @@ +name = "Video Embed Brightcove" +description = "Provides Brightcove handler for Video Embed Fields." +core = 7.x +package = Media +configure = admin/config/media/vef_video_styles + +dependencies[] = "video_embed_field" + +; Information added by Drupal.org packaging script on 2015-09-07 +version = "7.x-2.0-beta11" +core = "7.x" +project = "video_embed_field" +datestamp = "1441639440" + diff --git a/sites/all/modules/contrib/fields/video_embed_field/video_embed_brightcove/video_embed_brightcove.module b/sites/all/modules/contrib/fields/video_embed_field/video_embed_brightcove/video_embed_brightcove.module new file mode 100644 index 0000000..f48f6c1 --- /dev/null +++ b/sites/all/modules/contrib/fields/video_embed_field/video_embed_brightcove/video_embed_brightcove.module @@ -0,0 +1,168 @@ + 'Brightcove Video', + 'function' => 'video_embed_brightcove_handle_video', + 'thumbnail_default' => drupal_get_path('module', 'video_embed_brightcove') . '/img/brightcove.jpg', + 'form' => 'video_embed_brightcove_form', + 'form_validate' => 'video_embed_field_handler_brightcove_form_validate', + 'domains' => array( + 'brightcove.com', + 'link.brightcove.com', + ), + 'defaults' => array( + 'width' => 640, + 'height' => 360, + 'class' => '', + ), + ); + + return $handlers; +} + +/** + * Form to configure out video settings. + * + * @param array $defaults + * Values for your provider. + * + * @return array + * A form as defined by form API. + */ +function video_embed_brightcove_form($defaults) { + $form = array(); + // Element for the width of the player. + $form['width'] = array( + '#type' => 'textfield', + '#title' => t('Player Width'), + '#description' => t('The width of the player.'), + '#default_value' => $defaults['width'], + ); + + // Element for the height of the player. + $form['height'] = array( + '#type' => 'textfield', + '#title' => t('Player Height'), + '#description' => t('The height of the player.'), + '#default_value' => $defaults['height'], + ); + + $form['class'] = array( + '#type' => 'textfield', + '#title' => t('Player CSS class'), + '#description' => t('CSS class to add to the player'), + '#default_value' => $defaults['class'], + ); + + return $form; +} + +/** + * Validates the form elements for the Brightcove configuration form. + */ +function video_embed_field_handler_brightcove_form_validate($element, &$form_state, $form) { + video_embed_field_validate_dimensions($element); +} + +/** + * The video handler. + * + * @param string $url + * The full video url. + * @param array $settings + * Handlers settings from the settings form. + * + * @return array|string + * The embed code for the video. + */ +function video_embed_brightcove_handle_video($url, $settings) { + $parameters = _video_embed_brightcove_get_video_properties($url); + + if (isset($parameters['id']) && isset($parameters['key'])) { + // Embed code. + $embed = ' + + + + + + + + + + '; + // Replace our placeholders with the values from the settings. + $embed = format_string($embed, array( + '!id' => $parameters['id'], + '!key' => $parameters['key'], + '@width' => $settings['width'], + '@height' => $settings['height'], + '@class' => $settings['class'], + '!videoplayer' => $parameters['player'], + )); + + $video = array( + '#markup' => $embed, + '#suffix' => '', + '#attached' => array( + 'js' => array( + '//admin.brightcove.com/js/BrightcoveExperiences.js' => array( + 'type' => 'external', + ), + ), + ), + ); + + return $video; + } + return ''; +} + +/** + * Helper function to take a brightcove video url and return its id. + * + * @param string $url + * The full brightcove video url. + * + * @return array + * The video properties. + */ +function _video_embed_brightcove_get_video_properties($url) { + // Easy way to break a url into its components. + $components = array( + 'id' => array( + 'start' => 'bcpid', + 'finish' => '\?bckey', + ), + 'key' => array( + 'start' => 'bckey=', + 'finish' => '&bctid', + ), + 'player' => array( + 'start' => 'bctid=', + 'finish' => '', + ), + ); + + $matches = array(); + $return = array(); + foreach ($components as $key => $component) { + $string = "/(.*){$component['start']}(.*){$component['finish']}/"; + preg_match($string, $url, $matches); + if ($matches && !empty($matches[2])) { + $return[$key] = check_plain($matches[2]); + } + } + return $return; +} diff --git a/sites/all/modules/contrib/fields/video_embed_field/video_embed_facebook/img/facebook.jpg b/sites/all/modules/contrib/fields/video_embed_field/video_embed_facebook/img/facebook.jpg new file mode 100644 index 0000000..79a1a13 Binary files /dev/null and b/sites/all/modules/contrib/fields/video_embed_field/video_embed_facebook/img/facebook.jpg differ diff --git a/sites/all/modules/contrib/fields/video_embed_field/video_embed_facebook/video_embed_facebook.info b/sites/all/modules/contrib/fields/video_embed_field/video_embed_facebook/video_embed_facebook.info index 30141ba..618c2d7 100644 --- a/sites/all/modules/contrib/fields/video_embed_field/video_embed_facebook/video_embed_facebook.info +++ b/sites/all/modules/contrib/fields/video_embed_field/video_embed_facebook/video_embed_facebook.info @@ -5,9 +5,9 @@ package = Media configure = admin/config/media/vef_video_styles dependencies[] = "video_embed_field" -; Information added by drupal.org packaging script on 2012-10-17 -version = "7.x-2.0-beta5+11-dev" +; Information added by Drupal.org packaging script on 2015-09-07 +version = "7.x-2.0-beta11" core = "7.x" project = "video_embed_field" -datestamp = "1350438417" +datestamp = "1441639440" diff --git a/sites/all/modules/contrib/fields/video_embed_field/video_embed_facebook/video_embed_facebook.module b/sites/all/modules/contrib/fields/video_embed_field/video_embed_facebook/video_embed_facebook.module index d4c1db8..bca0297 100644 --- a/sites/all/modules/contrib/fields/video_embed_field/video_embed_facebook/video_embed_facebook.module +++ b/sites/all/modules/contrib/fields/video_embed_field/video_embed_facebook/video_embed_facebook.module @@ -1,41 +1,31 @@ 'Facebook Video', //The title is the name to show to users - //function is a function to take the url and return the embed code + 'title' => 'Facebook Video', 'function' => 'video_embed_facebook_handle_video', - //thumbnail_function is optional and takes the url and returns the thumbnail url 'thumbnail_function' => 'video_embed_facebook_handle_thumbnail', - //data_function is optional and returns an array of extra data for the given video url - //because facebook requires oath to get this data, we'll leave it out for now - //'data_function' => 'video_embed_facebook_handle_data', - //form is the configure form to embed into video_embed styles - this is where all settings should go + 'thumbnail_default' => drupal_get_path('module', 'video_embed_facebook') . '/img/facebook.jpg', 'form' => 'video_embed_facebook_form', - //domains is how video embed determines which handler to use, its an array of domains to match - //urls against. Don't include the scheme (like http://) or www. + 'form_validate' => 'video_embed_field_handler_youtube_form_validate', 'domains' => array( 'facebook.com', ), - //defaults are the defaults to provide to your form (as defined in your form callback) 'defaults' => array( 'width' => 640, 'height' => 360, - 'allowfullscreen' => TRUE, + 'class' => '', ), ); @@ -43,107 +33,124 @@ function video_embed_facebook_video_embed_handler_info() { } /** - * Our configuration form (the callback for the form key in info) - * Provide a form to configure out video settings - * @param $defaults - default/current values for your provider, the currently saved settings - * with empty values filled with the defaults provided in info hook - * @return a form as defined by forms api + * Defines the form elements for the Facebook videos configuration form. + * + * @param array $defaults + * The form default values. + * + * @return array + * The provider settings form array. */ function video_embed_facebook_form($defaults) { $form = array(); - //form element for the width of the player - note we're using the default from defaults - $form['height'] = array( - '#type' => 'textfield', - '#title' => t('Player Width'), - '#description' => t('The width of the player.'), - '#default_value' => $defaults['height'], - ); - //element for width + $form['width'] = array( '#type' => 'textfield', '#title' => t('Player Width'), '#description' => t('The width of the player.'), '#default_value' => $defaults['width'], ); - //allow configuration of fullscreen - $form['allowfullscreen'] = array( - '#type' => 'checkbox', - '#title' => t('Allow Fullscreen'), - '#desecription' => t('This will allow the video to be fullscreened.'), - '#default_value' => $defaults['allowfullscreen'], + + $form['height'] = array( + '#type' => 'textfield', + '#title' => t('Player Height'), + '#description' => t('The height of the player.'), + '#default_value' => $defaults['height'], + ); + + $form['class'] = array( + '#type' => 'textfield', + '#title' => t('Player CSS class'), + '#description' => t('CSS class to add to the player'), + '#default_value' => $defaults['class'], ); return $form; } +/** + * Validates the form elements for the Facebook video configuration form. + * + * @param array $element + * The form element to validate. + * @param array $form_state + * The form to validate state. + * @param array $form + * The form to validate structure. + */ +function video_embed_field_handler_facebook_form_validate($element, &$form_state, $form) { + video_embed_field_validate_dimensions($element); +} /** - * This is the video handler (the 'function' key from handler_info) - * @param $url - the full video url - * @param $settings - an associative array of this handlers settings, from the settings form - * @return - the embed code for the video + * Handler for Facebook videos. + * + * @param string $url + * The video URL. + * @param array $settings + * The settings array. + * + * @return string|bool + * The video iframe, or FALSE in case the ID can't be retrieved from the URL. */ function video_embed_facebook_handle_video($url, $settings) { $id = _video_embed_facebook_get_video_id($url); if ($id) { - //our embed code - $embed = ' - - - - - - - '; - //use format_string to replace our placeholders with the values from the settings + // Our embed code. + $embed=' '; + // Use format_string to replace our placeholders with the settings values. $embed = format_string($embed, array( '!id' => $id, - '!fullscreen' => $settings['allowfullscreen'] ? 'true' : 'false', - '!width' => $settings['width'], - '!height' => $settings['height'], + '@width' => $settings['width'], + '@height' => $settings['height'], + '@class' => $settings['class'], )); - //we want to return a render array + $video = array( '#markup' => $embed, ); return $video; } - // just return an empty string if there is no id, so we don't have broken embeds showing up - return ''; + + return FALSE; } /** - * Retreive the thumbnail for the facebook video - note that based on a users permissions, this may be - * the facebook unknown thumbnail: https://s-static.ak.facebook.com/rsrc.php/v1/y0/r/XsEg9L6Ie5_.jpg - * @param $url - the url of the video as entered by the user - * @return an array with the keys: - * 'id' => an id for the video which is unique to your provider, used for naming the cached thumbnail file - * 'url' => the url to retrieve the thumbnail from + * Gets the thumbnail url for Facebook videos. + * + * @param string $url + * The video URL. + * + * @return array + * The video thumbnail information. */ function video_embed_facebook_handle_thumbnail($url) { $id = _video_embed_facebook_get_video_id($url); - // We're using a part of the graphs api to return the thumbnail. This only works for some videos + return array( - 'id' => $id, //generally the id that the provider uses for the video - 'url' => 'https://graph.facebook.com/' . $id . '/picture', //the url of the thumbnail + 'id' => $id, + 'url' => 'https://graph.facebook.com/' . $id . '/picture', ); } /** - * Helper function to take a facebook video url and return its id - * @param $url - the full facebook video url - * @return the id for the video + * Helper function to get the Facebook video's id. + * + * @param string $url + * The video URL. + * + * @return string|bool + * The video ID, or FALSE in case the ID can't be retrieved from the URL. */ function _video_embed_facebook_get_video_id($url) { - //parse_url is an easy way to break a url into its components + // Parse_url is an easy way to break a url into its components. $matches = array(); - preg_match('/(.*)?v=([^&#]*)/', $url, $matches); - //if the v get parameter is set, return it - if ($matches && !empty($matches[2])) { - return $matches[2]; + preg_match('/(?:.*)(?:v=|video_id=|videos\/|videos\/v.\.\d+\/)(\d+).*/', $url, $matches); + // If the v or video_id get parameters are set, return it. + if ($matches && !empty($matches[1])) { + return check_plain($matches[1]); } - //otherwise return false. + // Otherwise return false. return FALSE; } diff --git a/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.admin.inc b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.admin.inc index 3a3f029..861fc64 100644 --- a/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.admin.inc +++ b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.admin.inc @@ -1,11 +1,14 @@ 'vertical_tabs', '#title' => t('Playback settings'), '#tree' => TRUE, - ) + $parser_form; //add in our extra settings + ) + $parser_form; return $form; } + +/** + * VEF settings page form callback. + */ +function video_embed_field_settings_form($form, &$form_state) { + $form['video_embed_field_youtube_v3_api_key'] = array( + '#type' => 'textfield', + '#title' => t('Youtube v3 API key'), + '#default_value' => variable_get('video_embed_field_youtube_v3_api_key', ''), + ); + return system_settings_form($form); +} diff --git a/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.api.php b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.api.php old mode 100755 new mode 100644 index 0f40367..3a16016 --- a/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.api.php +++ b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.api.php @@ -7,15 +7,26 @@ /** * @function hook_video_embed_handler_info * Can be used to add more handlers for video_embed_field - * @return an array of handlers, each handler is an array with the following keys - * 'title' : required, the untranslated title of the provider, to show to the admin user - * 'function' : required, the function used to generate the embed code - * 'thumbnail_function' : optional, the function used to provide the thumbnail for a video + * @return an array of handlers, each handler is an array with the following + * keys: + * 'title' : required, the untranslated title of the provider, to show to the + * admin user. + * 'function' : required, the function used to generate the embed code. + * 'thumbnail_function' : optional, the function used to provide the thumbnail + * for a video. + * 'thumbnail_default : optional, the default thumbnail image to display in case + * thumbnail_function does not exist or has no results. * 'data_function' : optional, the function to return an array of video data. - * 'form' : required, the function that returns the settings form for your provider - * 'domains' : required, an array of domains to match against, this is used to know which provider to use - * 'defaults' : default values for each setting made configurable in your form function + * 'form' : required, the function that returns the settings form for your + * provider. + * 'form_validate: optional the function that validates the settings form for + * your provider. + * 'domains' : required, an array of domains to match against, this is used to + * know which provider to use. + * 'defaults' : default values for each setting made configurable in your form + * function. * + * @see hook_video_embed_handler_info_alter() * @see below for function definitions */ function hook_video_embed_handler_info() { @@ -25,8 +36,10 @@ function hook_video_embed_handler_info() { 'title' => 'UStream', 'function' => 'your_module_handle_ustream', 'thumbnail_function' => 'your_module_handle_ustream_thumbnail', + 'thumbnail_default' => drupal_get_path('module', 'your_module') . '/img/ustream.jpg', 'data_function' => 'your_module_handler_ustream_data', 'form' => 'your_module_handler_ustream_form', + 'form_validate' => 'your_module_handler_ustream_form_validate', 'domains' => array( 'ustream.com', ), @@ -39,8 +52,24 @@ function hook_video_embed_handler_info() { return $handlers; } -// Example callbacks for a provider (in this case for ustream) - obviously, these functions are only for example -// purposes +/** + * Performs alterations on video_embed_field handlers. + * + * @param $info + * Array of information on video handlers exposed by + * hook_video_embed_handler_info() implementations. + */ +function hook_video_embed_handler_info_alter(&$info) { + // Change the thumbnail function for 'ustream' provider. + if (isset($info['ustream'])) { + $info['ustream']['thumbnail_function'] = 'your_module_handle_ustream_thumbnail_alter'; + } +} + +/** + * Example callbacks for a provider (in this case for ustream). + * Obviously, these functions are only for example purposes. + */ /** * Generate the embed code for a video diff --git a/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.devel_generate.inc b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.devel_generate.inc new file mode 100644 index 0000000..5428883 --- /dev/null +++ b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.devel_generate.inc @@ -0,0 +1,94 @@ +entry as $entry) { + $videos[] = array( + 'video_url' => $entry->children('media', TRUE)->group->player->attributes()->url, + 'description' => $entry->title, + ); + } + if (empty($videos)) { + video_embed_field_generate_videos(); + } + } + return $videos; +} diff --git a/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.feeds.inc b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.feeds.inc index 5196383..84bdb7a 100644 --- a/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.feeds.inc +++ b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.feeds.inc @@ -7,16 +7,23 @@ /** * Implements hook_feeds_processor_targets_alter(). * - * @see FeedsNodeProcessor::getMappingTargets(). + * @see FeedsNodeProcessor::getMappingTargets() */ function video_embed_field_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_name) { foreach (field_info_instances($entity_type, $bundle_name) as $name => $instance) { $info = field_info_field($name); if ($info['type'] == 'video_embed_field') { - $targets[$name] = array( - 'name' => $instance['label'], + $targets[$name . ":video_url"] = array( + 'name' => t('@name: Embed URL', array('@name' => $instance['label'])), 'callback' => 'video_embed_field_set_target', - 'description' => t('The @label field of the node.', array('@label' => $instance['label'])), + 'description' => t('The URL for the @label field of the @entity_type.', array('@entity_type' => $entity_type, '@label' => $instance['label'])), + 'real_target' => $name, + ); + $targets[$name . ':description'] = array( + 'name' => t('@name: Embed description', array('@name' => $instance['label'])), + 'callback' => 'video_embed_field_set_target', + 'description' => t('The description for the @label field of the @entity_type.', array('@entity_type' => $entity_type, '@label' => $instance['label'])), + 'real_target' => $name, ); } } @@ -34,10 +41,33 @@ function video_embed_field_set_target($source, $entity, $target, $value) { return; } - $field = isset($entity->$target) ? $entity->$target : array(); - if (!is_array($value) && !is_object($value)) { - $field['und'][0]['video_url'] = $value; + if (!is_array($value)) { + $value = array($value); } - $entity->{$target} = $field; + list($field_name, $sub_field) = explode(':', $target, 2); + + $info = field_info_field($field_name); + + // Iterate over all values. + $field = isset($entity->$field_name) ? $entity->$field_name : array(LANGUAGE_NONE => array()); + + // Allow for multiple mappings to the same target. + $count = call_user_func_array('array_merge_recursive', $field[LANGUAGE_NONE]); + $delta = count($count[$sub_field]); + + foreach ($value as $v) { + + if ($info['cardinality'] != FIELD_CARDINALITY_UNLIMITED && $info['cardinality'] <= $delta) { + break; + } + + if (is_scalar($v)) { + $field[LANGUAGE_NONE][$delta][$sub_field] = $v; + + $delta++; + } + } + + $entity->{$field_name} = $field; } diff --git a/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.field.inc b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.field.inc old mode 100755 new mode 100644 index 9b9135a..0c23804 --- a/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.field.inc +++ b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.field.inc @@ -17,25 +17,98 @@ function video_embed_field_field_info() { 'instance_settings' => array( 'description_field' => 0, 'description_length' => 128, + 'allowed_providers' => array_keys(video_embed_get_handlers()), ), 'default_widget' => 'video_embed_field_video', 'default_formatter' => 'video_embed_field', + 'property_type' => 'video_embed_field', + 'property_callbacks' => array('video_embed_field_property_info_callback'), ), ); } +/** + * Property callback for the Entity Metadata framework. + */ +function video_embed_field_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) { + // Apply the default. + entity_metadata_field_default_property_callback($info, $entity_type, $field, $instance, $field_type); + + // Finally add in instance specific property info. + $name = $field['field_name']; + $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$name]; + $property['type'] = ($field['cardinality'] != 1) ? 'list' : 'video_embed_field'; + $property['property info'] = video_embed_field_data_property_info('Video embed'); + $property['getter callback'] = 'entity_metadata_field_verbatim_get'; + $property['setter callback'] = 'entity_metadata_field_verbatim_set'; +} + +/** + * Defines info for the properties of the video_embed_field data structure. + */ +function video_embed_field_data_property_info($name = NULL) { + // Build an array of basic property information for video_embed_field. + $properties = array( + 'video_url' => array( + 'label' => 'Video URL', + 'type' => 'uri', + 'setter callback' => 'entity_property_verbatim_set', + ), + 'thumbnail_path' => array( + 'label' => 'Thumbnail path', + 'type' => 'uri', + 'getter callback' => 'entity_property_verbatim_get_url', + ), + 'description' => array( + 'label' => 'Description', + 'type' => 'text', + 'setter callback' => 'entity_property_verbatim_set', + ), + ); + + // Add the default values for each of the video_embed_field properties. + foreach ($properties as $key => &$value) { + $value += array( + 'description' => !empty($name) ? t('!label of field %name', array('!label' => $value['label'], '%name' => $name)) : '', + ); + } + + return $properties; +} + +/** + * Gets the property just as it is set in the data and converts to absolute url. + */ +function entity_property_verbatim_get_url($data, array $options, $name, $type, $info) { + $property = entity_property_verbatim_get($data, $options, $name, $type, $info); + return file_create_url($property); +} + /** * Implements hook_field_instance_settings_form(). */ function video_embed_field_field_instance_settings_form($field, $instance) { $settings = $instance['settings']; + $providers = video_embed_get_handlers(); + $allowed_providers = array(); + foreach ($providers as $provider_id => $definition) { + $allowed_providers[$provider_id] = $definition['title']; + } + + $form['allowed_providers'] = array( + '#type' => 'checkboxes', + '#title' => t('Select the allowed video providers'), + '#options' => $allowed_providers, + '#default_value' => isset($settings['allowed_providers']) ? $settings['allowed_providers'] : array(), + '#weight' => 10, + ); + $form['description_field'] = array( '#type' => 'checkbox', '#title' => t('Enable Description field'), '#default_value' => isset($settings['description_field']) ? $settings['description_field'] : '', '#description' => t('The description field allows users to enter a description about the video.'), - '#parents' => array('instance', 'settings', 'description_field'), '#weight' => 11, ); @@ -43,10 +116,14 @@ function video_embed_field_field_instance_settings_form($field, $instance) { '#type' => 'textfield', '#title' => t('Max description length'), '#default_value' => isset($settings['description_length']) ? $settings['description_length'] : 128, - '#parents' => array('instance', 'settings', 'description_length'), '#weight' => 12, '#size' => 5, '#maxlength' => 5, + '#states' => array( + 'visible' => array( + ':input[id="edit-instance-settings-description-field"]' => array('checked' => TRUE), + ), + ), ); return $form; @@ -74,7 +151,7 @@ function video_embed_field_field_widget_info() { * Implements hook_field_widget_form(). */ function video_embed_field_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { - // Don't need to check the type right now because we're only defining one + // Don't need to check the type right now because we're only defining one. $element += array( '#type' => 'fieldset', ); @@ -90,6 +167,7 @@ function video_embed_field_field_widget_form(&$form, &$form_state, $field, $inst ), '#default_value' => isset($items[$delta]['video_url']) ? $items[$delta]['video_url'] : '', '#required' => $element['#required'], + '#maxlength' => 255, ); // Add the description field if enabled. @@ -129,7 +207,7 @@ function video_embed_field_field_validate($entity_type, $entity, $field, $instan if (stripos($host, 'www.') > -1) { $host = substr($host, 4); } - $domains = _video_embed_field_get_provider_domains(); + $domains = _video_embed_field_get_instance_provider_domains($instance); if (!array_key_exists($host, $domains)) { $errors[$field['field_name']][$langcode][$delta][] = array( 'error' => t('Unsupported Video Provider'), @@ -142,41 +220,44 @@ function video_embed_field_field_validate($entity_type, $entity, $field, $instan } /** - * Implementation of hook_field_presave(). + * Implements hook_field_presave(). * * Download and save the thumbnail if it hasn't already been stored. * Get video data. */ function video_embed_field_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) { foreach ($items as $delta => $item) { - //Trim whitespace from the video URL. + // Trim whitespace from the video URL. $items[$delta]['video_url'] = trim($item['video_url']); - // Try to load thumbnail URL + // Try to load thumbnail URL. $info = video_embed_field_thumbnail_url($item['video_url']); if (isset($info['url']) && $info['url']) { $thumb_url = $info['url']; - $local_path = 'public://video_embed_field_thumbnails/' . $info['handler'] . '/' . $info['id'] . '.jpg'; + $thumb_extension = pathinfo($thumb_url, PATHINFO_EXTENSION); + $local_path = "public://video_embed_field_thumbnails/{$info['handler']}/{$info['id']}.$thumb_extension"; $dirname = drupal_dirname($local_path); file_prepare_directory($dirname, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); $response = drupal_http_request($thumb_url); if (!isset($response->error)) { - file_save_data($response->data, $local_path, TRUE); + file_save_data($response->data, $local_path, FILE_EXISTS_REPLACE); } else { @copy($thumb_url, $local_path); } - $items[$delta]['thumbnail_path'] = $local_path; - } //couldn't get the thumbnail for whatever reason + // Delete any image derivatives at the original image path. + image_path_flush($local_path); + } + // Couldn't get the thumbnail for whatever reason. else { $items[$delta]['thumbnail_path'] = ''; } - // Try to load video data + // Try to load video data. $data = video_embed_field_get_video_data($item['video_url']); if (is_array($data) && !empty($data)) { $items[$delta]['video_data'] = serialize($data); @@ -206,31 +287,43 @@ function video_embed_field_field_formatter_info() { 'settings' => array( 'video_style' => 'normal', 'description' => 1, - 'description_position' => 'bottom' + 'description_position' => 'bottom', ), ), + 'video_embed_field_url' => array( + 'label' => t('URL to Video'), + 'field types' => array('video_embed_field'), + 'settings' => array(), + ), 'video_embed_field_thumbnail' => array( 'label' => t('Thumbnail Preview'), 'field types' => array('video_embed_field'), 'settings' => array( - 'image_style' => 'none', + 'image_style' => '', 'description' => 1, 'description_position' => 'bottom', 'image_link' => 'none', ), ), ); - if ( module_exists('colorbox') ) { + if (module_exists('colorbox')) { $info['video_embed_field_thumbnail_colorbox'] = array( 'label' => t('Thumbnail Preview w/Colorbox'), 'field types' => array('video_embed_field'), 'settings' => array( 'video_style' => 'normal', - 'image_style' => 'none', + 'image_style' => '', 'description' => 1, 'description_position' => 'bottom', ), ); + $info['video_embed_field_url_colorbox'] = array( + 'label' => t('URL to Video w/Colorbox'), + 'field types' => array('video_embed_field'), + 'settings' => array( + 'video_style' => 'normal', + ), + ); } return $info; } @@ -243,7 +336,7 @@ function video_embed_field_field_formatter_settings_form($field, $instance, $vie $settings = $display['settings']; $element = array(); - if ($display['type'] == 'video_embed_field' || $display['type'] == 'video_embed_field_thumbnail_colorbox') { + if ($display['type'] == 'video_embed_field' || $display['type'] == 'video_embed_field_thumbnail_colorbox' || $display['type'] == 'video_embed_field_url_colorbox') { $video_styles = video_embed_field_video_style_options(FALSE); $element['video_style'] = array( '#title' => t('Video style'), @@ -264,7 +357,7 @@ function video_embed_field_field_formatter_settings_form($field, $instance, $vie if ($display['type'] == 'video_embed_field_thumbnail') { $link_types = array( - 'node' => t('Node'), + 'content' => t('Content'), 'source' => t('Video Source'), ); $element['image_link'] = array( @@ -276,7 +369,7 @@ function video_embed_field_field_formatter_settings_form($field, $instance, $vie ); } - if ($instance['settings']['description_field']) { + if ($instance['settings']['description_field'] && $display['type'] != 'video_embed_field_url' && $display['type'] != 'video_embed_field_url_colorbox') { $element['description'] = array( '#title' => t('Show description'), '#type' => 'checkbox', @@ -288,9 +381,14 @@ function video_embed_field_field_formatter_settings_form($field, $instance, $vie '#type' => 'select', '#options' => array( 'top' => t('Top'), - 'bottom' => t('Bottom') + 'bottom' => t('Bottom'), ), '#default_value' => $settings['description_position'], + '#states' => array( + 'visible' => array( + ':input[name="fields[' . $field['field_name'] . '][settings_edit_form][settings][description]"]' => array('checked' => TRUE), + ), + ), ); } return $element; @@ -303,7 +401,7 @@ function video_embed_field_field_formatter_settings_summary($field, $instance, $ $settings = $display['settings']; $summary = array(); - if ($display['type'] == 'video_embed_field' || $display['type'] == 'video_embed_field_thumbnail_colorbox') { + if ($display['type'] == 'video_embed_field' || $display['type'] == 'video_embed_field_thumbnail_colorbox' || $display['type'] == 'video_embed_field_url_colorbox') { $video_styles = video_embed_field_video_style_options(FALSE); // Styles could be lost because of enabled/disabled modules that defines // their styles in code. @@ -315,23 +413,26 @@ function video_embed_field_field_formatter_settings_summary($field, $instance, $ $image_styles = image_style_options(FALSE); if (isset($image_styles[$settings['image_style']])) { $summary[] = t('Image style: @style', array('@style' => $image_styles[$settings['image_style']])); - } //No Image style (original image) + } + // No Image style (original image). else { $summary[] = t('Original Image.'); } if (isset($settings['image_link'])) { - $summary[] = t('Image link: ' . $settings['image_link']); + $summary[] = t('Image link: @image_link', array('@image_link' => $settings['image_link'])); } else { $summary[] = t('Image link: none'); } } - if ($settings['description'] && $instance['settings']['description_field']) { - $summary[] = t('Show description'); - } - elseif ($instance['settings']['description_field']) { - $summary[] = t('Hide description'); + if (isset($settings['description'])) { + if ($settings['description'] && $instance['settings']['description_field']) { + $summary[] = t('Show description'); + } + elseif ($instance['settings']['description_field']) { + $summary[] = t('Hide description'); + } } return implode('
    ', $summary); @@ -344,20 +445,26 @@ function video_embed_field_field_formatter_view($entity_type, $entity, $field, $ $element = array(); $settings = $display['settings']; + if ($display['type'] == 'video_embed_field_thumbnail' && $display['settings']['image_link'] == 'content') { + $path = entity_uri($entity_type, $entity); + } + foreach ($items as $delta => $item) { - //create the render array for the description + // Create the render array for the description. if (isset($item['description']) && $item['description'] && $settings['description'] && $instance['settings']['description_field']) { $description = array( '#prefix' => '
    ', - '#markup' => $item['description'], + '#markup' => check_plain($item['description']), '#suffix' => '
    ', ); + $alt = $item['description']; } else { $description = array(); + $alt = ''; } - //Render the field + // Render the field. if ($display['type'] == 'video_embed_field') { $element[$delta] = array( array( @@ -368,63 +475,74 @@ function video_embed_field_field_formatter_view($entity_type, $entity, $field, $ ), ); } + elseif ($display['type'] == 'video_embed_field_url') { + $element[$delta] = array( + array( + '#markup' => url($item['video_url']), + ), + ); + } elseif ($display['type'] == 'video_embed_field_thumbnail') { if (isset($item['thumbnail_path'])) { - if (empty($settings['image_style']) || $settings['image_style'] == 'none') { - $element[$delta] = array( - array( - '#theme' => 'image', - '#path' => $item['thumbnail_path'], - ), + if ($display['settings']['image_link'] == 'source') { + if ($ret = parse_url($item['video_url'])) { + if (!isset($ret["scheme"])) { + $item['video_url'] = "http://{$item['video_url']}"; + } + } + $path = array( + 'path' => $item['video_url'], + 'options' => array(), ); } - else { - $element[$delta] = array( - array( - '#theme' => 'image_style', - '#path' => $item['thumbnail_path'], - '#style_name' => $settings['image_style'], - ), - ); - } - if ($settings['image_link'] == 'source') { - $link = explode('|||', l('|||', $item['video_url'])); - $element[$delta]['#prefix'] = $link[0]; - $element[$delta]['#suffix'] = $link[1]; - } - elseif ($settings['image_link'] == 'node') { - $nid = $entity->nid; - $link = explode('|||', l('|||', 'node/' . $nid)); - $element[$delta]['#prefix'] = $link[0]; - $element[$delta]['#suffix'] = $link[1]; - } - } //incase no thumbnail was downloaded / provider doesn't support thumbnails - else { + $element[$delta] = array( + '#theme' => 'image_formatter', + '#item' => array('uri' => $item['thumbnail_path'], 'alt' => $alt), + '#image_style' => $display['settings']['image_style'], + '#path' => isset($path) ? $path : '', ); } + // Incase no thumbnail was downloaded/provider doesn't support thumbnails. + else { + $element[$delta] = array(); + } } elseif ($display['type'] == 'video_embed_field_thumbnail_colorbox') { if (isset($item['thumbnail_path'])) { + if ($ret = parse_url($item['video_url'])) { + if (!isset($ret["scheme"])) { + $item['video_url'] = "http://{$item['video_url']}"; + } + } $element[$delta] = array( array( '#theme' => 'video_embed_field_colorbox_code', '#image_url' => $item['thumbnail_path'], '#image_style' => $display['settings']['image_style'], + '#image_alt' => $alt, '#video_url' => $item['video_url'], '#video_style' => $display['settings']['video_style'], '#video_data' => unserialize($item['video_data']), ), ); } - //incase no thumbnail was downloaded / provider doesn't support thumbnails + // Incase no thumbnail was downloaded/provider doesn't support thumbnails. else { - $element[$delta] = array( - ); + $element[$delta] = array(); } } - //Get the HTML instead of the array, because we append it to the suffix. - //This way, the thumbnail link wrapper doesn't make the description a link as well. + elseif ($display['type'] == 'video_embed_field_url_colorbox') { + $path = video_embed_field_get_ajax_url($item['video_url'], $display['settings']['video_style']); + + $element[$delta] = array( + array( + '#markup' => url($path['path'], $path['options']), + ), + ); + } + // Get the HTML instead of the array, because we append it to the suffix. + // This way, the thumbnail link doesn't make the description a link as well. $description_html = drupal_render($description); $pos = isset($settings['description_position']) ? $settings['description_position'] : 'bottom'; @@ -438,6 +556,5 @@ function video_embed_field_field_formatter_view($entity_type, $entity, $field, $ } } - return $element; } diff --git a/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.handlers.inc b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.handlers.inc old mode 100755 new mode 100644 index f0824bd..1962008 --- a/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.handlers.inc +++ b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.handlers.inc @@ -1,7 +1,10 @@ 'Youtube', 'function' => 'video_embed_field_handle_youtube', 'thumbnail_function' => 'video_embed_field_handle_youtube_thumbnail', + 'thumbnail_default' => drupal_get_path('module', 'video_embed_field') . '/img/youtube.jpg', 'data_function' => 'video_embed_field_handle_youtube_data', 'form' => 'video_embed_field_handler_youtube_form', + 'form_validate' => 'video_embed_field_handler_youtube_form_validate', 'domains' => array( 'youtube.com', 'youtu.be', ), 'defaults' => array( - 'width' => '640px', - 'height' => '360px', + 'width' => 640, + 'height' => 360, 'autoplay' => 0, - 'hd' => 1, + 'vq' => 'large', 'rel' => 0, + 'controls' => 1, 'autohide' => 2, 'showinfo' => 1, 'modestbranding' => 0, 'theme' => 'dark', 'iv_load_policy' => 1, + 'class' => '', ), ); @@ -39,20 +46,24 @@ function video_embed_field_video_embed_handler_info() { 'title' => 'Vimeo', 'function' => 'video_embed_field_handle_vimeo', 'thumbnail_function' => 'video_embed_field_handle_vimeo_thumbnail', - 'data_function' => 'video_embed_field_handle_vimeo_data', + 'thumbnail_default' => drupal_get_path('module', 'video_embed_field') . '/img/vimeo.jpg', + 'data_function' => '_video_embed_field_get_vimeo_data', 'form' => 'video_embed_field_handler_vimeo_form', + 'form_validate' => 'video_embed_field_handler_vimeo_form_validate', 'domains' => array( 'vimeo.com', ), 'defaults' => array( - 'width' => '640px', - 'height' => '360px', + 'width' => 640, + 'height' => 360, 'color' => '00adef', 'portrait' => 1, 'title' => 1, 'byline' => 1, 'autoplay' => 0, 'loop' => 0, + 'froogaloop' => 0, + 'class' => '' ), ); @@ -60,11 +71,16 @@ function video_embed_field_video_embed_handler_info() { } /** - * Helper function to get the youtube video's id - * Returns false if it doesn't parse for wahtever reason + * Helper function to get the youtube video's id. + * + * @param string $url + * The video URL. + * + * @return string|bool + * The video ID, or FALSE in case the ID can't be retrieved from the URL. */ function _video_embed_field_get_youtube_id($url) { - // Find the ID of the video they want to play from the url + // Find the ID of the video they want to play from the url. if (stristr($url, 'http://')) { $url = substr($url, 7); } @@ -73,7 +89,7 @@ function _video_embed_field_get_youtube_id($url) { } if (stristr($url, 'playlist')) { - //Playlists need the appended ampersand to take the options properly. + // Playlists need the appended ampersand to take the options properly. $url = $url . '&'; $pos = strripos($url, '?list='); if ($pos !== FALSE) { @@ -84,14 +100,14 @@ function _video_embed_field_get_youtube_id($url) { return FALSE; } } - //Alternate playlist link + // Alternate playlist link. elseif (stristr($url, 'view_play_list')) { $url = $url . '&'; - //All playlist ID's are prepended with PL. view_play_list links allow you to not have that, though. + // All playlist ID's are prepended with PL. if (!stristr($url, '?p=PL')) { $url = substr_replace($url, 'PL', strpos($url, '?p=') + 3, 0); } - //Replace the links format with the embed format + // Replace the links format with the embed format. $url = str_ireplace('play_list?p=', 'videoseries?list=', $url); $pos = strripos($url, 'videoseries?list='); if ($pos !== FALSE) { @@ -133,57 +149,81 @@ function _video_embed_field_get_youtube_id($url) { $id = substr($url, $pos); } } - return $id; + return check_plain($id); } /** * Handler for Youtube videos. + * + * @param string $url + * The video URL. + * @param array $settings + * The settings array. + * + * @return array + * The video iframe render array. */ function video_embed_field_handle_youtube($url, $settings) { $output = array(); - //Grab the minutes and seconds, and just convert it down to seconds - preg_match('/#t=((?P\d+)m)?((?P\d+)s)?/', $url, $matches); - - //Give it some default data in case there is no #t=... - $matches += array( - "min" => 0, - "sec" => 0, - ); - $time = ($matches["min"] * 60) + $matches["sec"]; - $settings['start'] = $time; + if(preg_match('/#t=((?P\d+)m)?((?P\d+)s)?((?P\d+))?/', $url, $matches)){ + if(isset($matches['tinsec'])){ + $settings['start'] = $matches['tinsec']; // url already in form #t=125 for 2 minutes and 5 seconds + } else { + // url in form #t=2m5s or with other useless data, this is why we still keep adding the default data.. + // give it some default data in case there is no #t=... + $matches += array( + "min" => 0, + "sec" => 0, + ); + if ($time = ($matches["min"] * 60) + $matches["sec"]) { + $settings['start'] = $time; + } + } + } $id = _video_embed_field_get_youtube_id($url); if (!$id) { - // We can't decode the URL - just return the URL as a link + // We can't decode the URL - just return the URL as a link. $output['#markup'] = l($url, $url); return $output; } - // Construct the embed code - $settings['wmode'] = 'opaque'; - $settings_str = _video_embed_code_get_settings_str($settings); - $output['#markup'] = ''; + // Add class to variable to avoid adding it to URL param string. + $class = $settings['class']; + unset($settings['class']); + + // Construct the embed code. + $settings['wmode'] = 'opaque'; + $settings_str = urlencode(_video_embed_code_get_settings_str($settings)); + + $output['#markup'] = ''; return $output; } /** - * Get the thumbnail url for youtube videos + * Gets the thumbnail url for youtube videos. + * + * @param string $url + * The video URL. + * + * @return array + * The video thumbnail information. */ -function video_embed_field_handle_youtube_thumbnail($video_url) { +function video_embed_field_handle_youtube_thumbnail($url) { $info = array(); - $id = _video_embed_field_get_youtube_id($video_url); + $id = _video_embed_field_get_youtube_id($url); - //Playlist + // Playlist. if (stristr($id, '?list=')) { - //Strip out all but the ID, including the PL behind the ID. + // Strip out all but the ID, including the PL behind the ID. $start = strpos($id, '?list=PL') + 8; $length = strpos($id, '&') - $start; $id = substr($id, $start, $length); $info['id'] = $id; - //Playlist info is stored in XML. The thumbnail is in there. + // Playlist info is stored in XML. The thumbnail is in there. $xml = drupal_http_request('http://gdata.youtube.com/feeds/api/playlists/' . $id); if (!isset($xml->error)) { $xml = new SimpleXMLElement($xml->data); @@ -194,7 +234,7 @@ function video_embed_field_handle_youtube_thumbnail($video_url) { } } } - //Regular video + // Regular video. elseif ($id) { $info['id'] = $id; $info['url'] = 'http://img.youtube.com/vi/' . $id . '/0.jpg'; @@ -203,26 +243,30 @@ function video_embed_field_handle_youtube_thumbnail($video_url) { } /** - * Get video data for a YouTube video URL + * Gets video data for a YouTube video URL. * * @param string $url * A YouTube video URL to get data for * - * @return array|false $data + * @return array|bool * An array of video data, or FALSE if unable to fetch data */ function video_embed_field_handle_youtube_data($url) { - $data = array(); - - // Get YouTube video ID from URL + // Get YouTube video ID from URL. $id = _video_embed_field_get_youtube_id($url); if ($id) { - $response = drupal_http_request('http://gdata.youtube.com/feeds/api/videos/' . $id . '?v=2&alt=json'); + + $options['v'] = 3; + $options['key'] = variable_get('video_embed_field_youtube_v3_api_key', ''); + $options['part'] = 'snippet'; + $options['id'] = $id; + + $response = drupal_http_request(url('https://www.googleapis.com/youtube/v3/videos', array('query' => $options))); + if (!isset($response->error)) { $data = json_decode($response->data); - $data = isset($data->entry) ? (array) $data->entry : (array) $data->feed; - return _video_embed_field_clean_up_youtube_data($data); + return _video_embed_field_clean_up_youtube_data($data->items); } } @@ -230,10 +274,16 @@ function video_embed_field_handle_youtube_data($url) { } /** - * Flatten out some unnecessary nesting in the youtube data + * Flattens out some unnecessary nesting in the youtube data. + * + * @param array $data + * The unflattened data. + * + * @return array + * The flattened data. */ function _video_embed_field_clean_up_youtube_data($data) { - //make things a bit nicer for people trying to use the data + // Make things a bit nicer for people trying to use the data. foreach ($data as $key => $value) { if (is_object($value)) { $temp = (array) $value; @@ -267,6 +317,12 @@ function _video_embed_field_clean_up_youtube_data($data) { /** * Defines the form elements for the Youtube configuration form. + * + * @param array $defaults + * The form default values. + * + * @return array + * The provider settings form array. */ function video_embed_field_handler_youtube_form($defaults) { $form = array(); @@ -300,11 +356,18 @@ function video_embed_field_handler_youtube_form($defaults) { '#description' => t('Play the video immediately.'), '#default_value' => $defaults['autoplay'], ); - $form['hd'] = array( - '#type' => 'checkbox', - '#title' => t('Use HD'), - '#description' => t('Attempt to play the video in HD if available.'), - '#default_value' => $defaults['hd'], + $form['vq'] = array( + '#type' => 'select', + '#title' => t('Video quality'), + '#options' => array( + 'small' => t('Small (240p)'), + 'medium' => t('Medium (360p)'), + 'large' => t('Large (480p)'), + 'hd720' => t('HD 720p'), + 'hd1080' => t('HD 10800p'), + ), + '#default_value' => $defaults['vq'], + '#description' => t('Attempt to play the video in certain quality if available.'), ); $form['rel'] = array( '#type' => 'checkbox', @@ -334,6 +397,17 @@ function video_embed_field_handler_youtube_form($defaults) { '#description' => t('Controls the display of annotations over the video content. Only works when using the flash player.'), '#default_value' => $defaults['iv_load_policy'], ); + $form['controls'] = array( + '#type' => 'radios', + '#options' => array( + 0 => t('Hide video controls.'), + 1 => t('Show video controls. Youtube default.'), + 2 => t('Show video controls with performance improvement for iframe embeds.'), + ), + '#title' => t('Display Youtube player controls'), + '#description' => t('This parameter indicates whether the video player controls will display.'), + '#default_value' => $defaults['controls'], + ); $form['autohide'] = array( '#type' => 'radios', '#options' => array( @@ -346,93 +420,149 @@ function video_embed_field_handler_youtube_form($defaults) { '#default_value' => $defaults['autohide'], ); + $form['class'] = array( + '#type' => 'textfield', + '#title' => t('Player CSS class'), + '#description' => t('CSS class to add to the player'), + '#default_value' => $defaults['class'], + ); + return $form; } /** - * Helper function to get the Vimeo video's ID + * Validates the form elements for the Youtube configuration form. + * + * @param array $element + * The form element to validate. + * @param array $form_state + * The form to validate state. + * @param array $form + * The form to validate structure. + */ +function video_embed_field_handler_youtube_form_validate($element, &$form_state, $form) { + video_embed_field_validate_dimensions($element); +} + +/** + * Helper function to get the Vimeo video's data attributes. * * @param string $url - * A Vimeo video URL to get the ID of + * A Vimeo video URL to get the data from. * - * @return integer|false $id - * The video ID, or FALSE if unable to get the video ID + * @return integer|false + * The video's data attributes, or FALSE if unable to get the video ID. */ -function _video_embed_field_get_vimeo_id($url) { - $pos = strripos($url, '/'); - if ($pos != FALSE) { - $pos += 1; - return (int) substr($url, $pos); +function _video_embed_field_get_vimeo_data($url) { + // Set oembed endpoint + $oembed_endpoint = 'http://vimeo.com/api/oembed'; + // Fetch vimeo data + $response = drupal_http_request($oembed_endpoint . '.json?url=' . rawurlencode($url)); + + try { + return json_decode($response->data, TRUE); + } catch (Exception $e) { + return FALSE; } - return FALSE; +} + +/** + * Helper function to get the Vimeo video's data attributes. + * + * @param string $url + * A Vimeo video URL to get the ID of. + * + * @return integer|false + * The video ID, or FALSE if unable to get the video ID. + */ +function _video_embed_field_get_vimeo_id($vimeo_data) { + try { + $video_id = $vimeo_data['video_id']; + } catch (Exception $e) { + $video_id = FALSE; + } + + return $video_id; } /** * Handler for Vimeo videos. + * + * @param string $url + * The video URL. + * @param array $settings + * The settings array. + * + * @return string + * The video iframe. */ function video_embed_field_handle_vimeo($url, $settings) { - // Get ID of video from URL - $id = _video_embed_field_get_vimeo_id($url); - if (!$id) { + $vimeo_data = _video_embed_field_get_vimeo_data($url); + + // Get ID of video from URL. + $id = _video_embed_field_get_vimeo_id($vimeo_data); + + if (empty($id)) { return array( '#markup' => l($url, $url), ); } - // Construct the embed code - $settings['portrait'] = 0; + // Construct the embed code. + $settings['player_id'] = drupal_html_id('vimeo-' . $id); + if (!empty($settings['froogaloop'])) { + $settings['api'] = 1; + } + unset($settings['froogaloop']); + + // Add class to variable to avoid adding it to URL param string. + $class = $settings['class']; + unset($settings['class']); + $settings_str = _video_embed_code_get_settings_str($settings); return array( - '#markup' => '', + '#markup' => '', ); } /** - * Get the thumbnail url for youtube videos + * Gets the thumbnail url for vimeo videos. + * + * @param string $url + * The video URL. + * + * @return array + * The video thumbnail information. */ function video_embed_field_handle_vimeo_thumbnail($url) { - // Get ID of video from URL - $id = _video_embed_field_get_vimeo_id($url); + $vimeo_data = _video_embed_field_get_vimeo_data($url); + + // Get ID of video from URL. + $id = _video_embed_field_get_vimeo_id($vimeo_data); + $info = array( 'id' => $id, ); - $response = drupal_http_request('http://vimeo.com/api/v2/video/' . $id . '.php'); - if (!isset($response->error)) { - $response = unserialize($response->data); - $video = current($response); - $image_url = $video['thumbnail_large']; - $info['url'] = $image_url; + try { + $info['url'] = $vimeo_data['thumbnail_url']; + } catch (Exception $e) { + } + return $info; } -/** - * Get video data for a Vimeo video URL - * - * @param string $url - * A Vimeo video URL to get data for - * - * @return array|false $data - * An array of video data, or FALSE if unable to fetch data - */ -function video_embed_field_handle_vimeo_data($url) { - // Get ID of video from URL - $id = _video_embed_field_get_vimeo_id($url); - if ($id) { - $response = drupal_http_request('http://vimeo.com/api/v2/video/' . $id . '.php'); - if (!isset($response->error)) { - $response = unserialize($response->data); - return (array) current($response); - } - } - return FALSE; -} - /** * Defines the form elements for the Vimeo configuration form. + * + * @param array $defaults + * The form default values. + * + * @return array + * The provider settings form array. */ function video_embed_field_handler_vimeo_form($defaults) { $form = array(); @@ -444,6 +574,7 @@ function video_embed_field_handler_vimeo_form($defaults) { '#description' => t('The width of the vimeo player.'), '#default_value' => $defaults['width'], ); + $form['height'] = array( '#type' => 'textfield', '#size' => '5', @@ -451,6 +582,7 @@ function video_embed_field_handler_vimeo_form($defaults) { '#description' => t('The height of the vimeo player.'), '#default_value' => $defaults['height'], ); + $form['color'] = array( '#type' => 'select', '#options' => array( @@ -464,47 +596,90 @@ function video_embed_field_handler_vimeo_form($defaults) { '#description' => t('The color to use on the vimeo player.'), '#default_value' => $defaults['color'], ); + $form['portrait'] = array( '#type' => 'checkbox', '#title' => t('Overlay Author Thumbnail'), - '#description' => t('Overlay the author\'s thumbnail before the video is played.'), + '#description' => t("Overlay the author's thumbnail before the video is played."), '#default_value' => $defaults['portrait'], ); + $form['title'] = array( '#type' => 'checkbox', - '#title' => t('Overlay Video\'s Title'), - '#description' => t('Overlay the video\'s title before the video is played.'), + '#title' => t("Overlay Video's Title"), + '#description' => t("Overlay the video's title before the video is played."), '#default_value' => $defaults['title'], ); + $form['byline'] = array( '#type' => 'checkbox', - '#title' => t('Overlay Video\'s Byline'), - '#description' => t('Overlay the video\'s description before the video is played.'), + '#title' => t("Overlay Video's Byline"), + '#description' => t("Overlay the video's description before the video is played."), '#default_value' => $defaults['byline'], ); + $form['overridable'] = array( '#prefix' => '

    ' . t('Note') . ': ', '#markup' => t('Color, portrait, title and byline can be restricted by Vimeo Plus videos. Such videos will ignore these settings.'), '#suffix' => '

    ', ); + $form['autoplay'] = array( '#type' => 'checkbox', '#title' => t('Autoplay'), '#description' => t('Play the video immediately.'), '#default_value' => $defaults['autoplay'], ); + $form['loop'] = array( '#type' => 'checkbox', '#title' => t('Loop'), - '#description' => t('Loop the video\'s playback'), + '#description' => t("Loop the video's playback"), '#default_value' => $defaults['loop'], ); + + $form['froogaloop'] = array( + '#type' => 'checkbox', + '#title' => t('Enable froogaloop support'), + '#description' => t("Enables Froogallop Vimeo's library support"), + '#default_value' => $defaults['loop'], + ); + + $form['class'] = array( + '#type' => 'textfield', + '#title' => t('Player CSS class'), + '#description' => t('CSS class to add to the player'), + '#default_value' => $defaults['class'], + ); + return $form; } /** - * Calculate the min index for use in finding the id of a youtube video + * Validates the form elements for the Vimeo configuration form. + * + * @param array $element + * The form element to validate. + * @param array $form_state + * The form to validate state. + * @param array $form + * The form to validate structure. + */ +function video_embed_field_handler_vimeo_form_validate($element, &$form_state, $form) { + video_embed_field_validate_dimensions($element); +} + +/** + * Calculates the min index for use in finding the id of a youtube video. + * + * @param string $pos1 + * The first index. + * @param string $pos2 + * The second index. + * + * @return string + * The min index. */ function _video_embed_get_min($pos1, $pos2) { if (!$pos1) { diff --git a/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.info b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.info index da965b3..ffb8d65 100644 --- a/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.info +++ b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.info @@ -2,13 +2,17 @@ name = "Video Embed Field" description = "Expose a field type for embedding videos from youtube or vimeo." core = 7.x package = Media -configure = admin/config/media/vef_video_styles +configure = admin/config/media/vef + +files[] = video_embed_field.migrate.inc +files[] = views/handlers/views_embed_field_views_handler_field_thumbnail_path.inc dependencies[] = ctools dependencies[] = image -; Information added by drupal.org packaging script on 2012-10-17 -version = "7.x-2.0-beta5+11-dev" + +; Information added by Drupal.org packaging script on 2015-09-07 +version = "7.x-2.0-beta11" core = "7.x" project = "video_embed_field" -datestamp = "1350438417" +datestamp = "1441639440" diff --git a/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.install b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.install old mode 100755 new mode 100644 index 2537ab4..2009b2a --- a/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.install +++ b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.install @@ -1,8 +1,8 @@ 'Stores video embed styles.', 'export' => array( 'key' => 'name', - 'primary key' => 'vsid', - 'identifier' => 'video_embed_style', // Exports will be as $video_style - 'default hook' => 'default_video_embed_styles', // Function hook name. + 'identifier' => 'video_embed_style', + 'default hook' => 'default_video_embed_styles', 'api' => array( 'owner' => 'video_embed_field', - 'api' => 'default_video_embed_styles', // Base name for api include files. + 'api' => 'default_video_embed_styles', 'minimum_version' => 1, 'current_version' => 1, ), ), 'fields' => array( - 'vsid' => array( - 'description' => 'The primary identifier for a video style.', - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'no export' => TRUE, - ), 'name' => array( - 'description' => 'The style name.', + 'description' => 'The machine-readable option set name.', 'type' => 'varchar', - 'length' => '255', + 'length' => 255, + 'not null' => TRUE, + ), + 'title' => array( + 'description' => 'The human-readable title for this option set.', + 'type' => 'varchar', + 'length' => 255, 'not null' => TRUE, ), 'data' => array( @@ -93,10 +91,7 @@ function video_embed_field_schema() { 'serialize' => TRUE, ), ), - 'primary key' => array('vsid'), - 'unique keys' => array( - 'name' => array('name'), - ), + 'primary key' => array('name'), ); return $schema; } @@ -105,12 +100,11 @@ function video_embed_field_schema() { * Implements hook_uninstall(). */ function video_embed_field_uninstall() { - //do nothing right now - should eventually remove all the variables + variable_del('video_embed_field_youtube_api_key'); } /** - * Update 7000 - * Add an optional description form + * Adds an optional description form. */ function video_embed_field_update_7000() { // Get the list of fields of type 'video_embed_field'. @@ -130,7 +124,7 @@ function video_embed_field_update_7000() { $table = "field_data_{$field['field_name']}"; $revision_table = "field_revision_{$field['field_name']}"; } - $column = $field['field_name'] . '_' . 'description'; + $column = $field['field_name'] . '_description'; db_add_field($table, $column, array('type' => 'text', 'not null' => FALSE)); db_add_field($revision_table, $column, array('type' => 'text', 'not null' => FALSE)); } @@ -139,8 +133,7 @@ function video_embed_field_update_7000() { } /** - * Update 7001 - * Add video style storage table + * Adds video style storage table. */ function video_embed_field_update_7001() { if (!db_table_exists('vef_video_styles')) { @@ -151,8 +144,7 @@ function video_embed_field_update_7001() { return t('Video styles storage table created.'); } /** - * Update 7002 - * Add field for storing the path to the video thumbnail + * Adds field for storing the path to the video thumbnail. */ function video_embed_field_update_7002() { // Get the list of fields of type 'video_embed_field'. @@ -172,7 +164,7 @@ function video_embed_field_update_7002() { $table = "field_data_{$field['field_name']}"; $revision_table = "field_revision_{$field['field_name']}"; } - $column = $field['field_name'] . '_' . 'thumbnail_path'; + $column = $field['field_name'] . '_thumbnail_path'; db_add_field($table, $column, array( 'type' => 'varchar', 'length' => 512, @@ -189,22 +181,22 @@ function video_embed_field_update_7002() { } /** - * Enable inline colorbox support if colorbox is installed [NO LONGER NEEDED - This update hook does nothing] + * Enables inline colorbox support if colorbox is installed. + * + * [NO LONGER NEEDED - This update hook does nothing] */ function video_embed_field_update_7003() { - //this is no longer needed - //variable_set('colorbox_inline', 1); } /** - * Enable colorbox load support if colorbox is installed, we no longer need inline support + * Enables colorbox load support if colorbox is installed. */ function video_embed_field_update_7004() { variable_set('colorbox_load', 1); } /** - * Add data column to field database. + * Adds data column to field database. */ function video_embed_field_update_7005() { // Get the list of fields of type 'video_embed_field'. @@ -224,7 +216,7 @@ function video_embed_field_update_7005() { $table = "field_data_{$field['field_name']}"; $revision_table = "field_revision_{$field['field_name']}"; } - $column = $field['field_name'] . '_' . 'video_data'; + $column = $field['field_name'] . '_video_data'; db_add_field($table, $column, array( 'type' => 'blob', 'not null' => FALSE, @@ -241,3 +233,159 @@ function video_embed_field_update_7005() { return t('Data column added. Please clear cache.'); } + +/** + * Updates vef_video_styles table structure. + */ +function video_embed_field_update_7006() { + // Convert the table structure. + db_drop_field('vef_video_styles', 'vsid'); + db_add_primary_key('vef_video_styles', array('name')); + db_drop_unique_key('vef_video_styles', 'name'); + + db_add_field('vef_video_styles', 'title', array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + )); + // Update title and name values. + $result = db_select('vef_video_styles', 'vef') + ->fields('vef', array('name')) + ->execute(); + foreach ($result as $record) { + // Set current name as title. + db_update('vef_video_styles') + ->fields(array( + 'title' => $record->name, + )) + ->condition('name', $record->name) + ->execute(); + + // Update name to fit with machine_name constraints. + $new_name = preg_replace('/[^a-z0-9_]+/', '_', drupal_strtolower($record->name)); + if ($new_name != $record->name) { + // Check if new name already exists in the database. + $counter = 1; + $base_name = $new_name; + while (TRUE) { + $result = db_select('vef_video_styles', 'vef') + ->fields('vef', array('name')) + ->condition('name', $new_name) + ->execute(); + if ($result->rowCount()) { + $new_name = $base_name . '_' . $counter; + } + else { + db_update('vef_video_styles') + ->fields(array( + 'name' => $new_name, + )) + ->condition('name', $record->name) + ->execute(); + break; + } + } + } + } + + return t('Database schema updated successfully'); +} + +/** + * Update youtube "hd" URL deprecated parameter. + */ +function video_embed_field_update_7007() { + drupal_get_schema('vef_video_styles', TRUE); + ctools_include('export'); + $styles = ctools_export_load_object('vef_video_styles'); + foreach ($styles as $style) { + if (isset($style->data['youtube']['hd'])) { + if ($style->data['youtube']['hd']) { + $style->data['youtube']['vq'] = 'hd720'; + } + else { + $style->data['youtube']['vq'] = 'large'; + } + unset($style->data['youtube']['hd']); + ctools_export_crud_save('vef_video_styles', $style); + } + } + + return t('Parameter hd has been converted to vq.'); +} + +/** + * Updates naming of 'node' formatter setting to 'content'. + */ +function video_embed_field_update_7008() { + $instances = field_info_instances(); + foreach ($instances as $entity_type) { + foreach ($entity_type as $bundle) { + foreach ($bundle as $instance) { + $field_info = field_info_field($instance['field_name']); + if ($field_info['type'] == 'video_embed_field') { + $update = FALSE; + foreach ($instance['display'] as &$display) { + if ($display['type'] == 'video_embed_field_thumbnail') { + if ($display['settings']['image_link'] == 'node') { + $display['settings']['image_link'] = 'content'; + $update = TRUE; + } + if ($display['settings']['image_style'] == 'none') { + $display['settings']['image_style'] = ''; + $update = TRUE; + } + } + } + if ($update) { + field_update_instance($instance); + } + } + } + } + } + + return t("Updated 'node' setting to 'content'"); +} + +/** + * Adds new Allowed Providers setting to existing instances. + */ +function video_embed_field_update_7009() { + $allowed_providers = array_keys(video_embed_get_handlers()); + + $instances = field_info_instances(); + foreach ($instances as $entity_type) { + foreach ($entity_type as $bundle) { + foreach ($bundle as $instance) { + $field_info = field_info_field($instance['field_name']); + if ($field_info['type'] == 'video_embed_field') { + $instance['settings']['allowed_providers'] = $allowed_providers; + field_update_instance($instance); + } + } + } + } + + return t('Updated default instance settings'); +} + +/** + * Update styles with empty class parameter. + */ +function video_embed_field_update_7010() { + drupal_get_schema('vef_video_styles', TRUE); + ctools_include('export'); + $styles = ctools_export_load_object('vef_video_styles'); + foreach ($styles as $style) { + foreach ($style->data as &$provider) { + if (!isset($provider['class'])) { + $provider['class'] = ''; + } + } + ctools_export_crud_save('vef_video_styles', $style); + } + + return 'Parameter class added to existing styles'; +} diff --git a/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.migrate.inc b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.migrate.inc new file mode 100644 index 0000000..a714ca6 --- /dev/null +++ b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.migrate.inc @@ -0,0 +1,57 @@ + 2, + 'field handlers' => array('MigrateVideoEmbedFieldFieldHandler'), + ); + return $api; +} + +/** + * Custom extended MigrateFieldHandler class for Video Embed Field module. + */ +class MigrateVideoEmbedFieldFieldHandler extends MigrateFieldHandler { + + public function __construct() { + $this->registerTypes(array('video_embed_field')); + } + + /** + * {@inheritdoc} + */ + public function fields($type, $parent_field, $migration = NULL) { + $fields = array( + 'video_url' => t('Video: The video URL.'), + ); + return $fields; + } + + /** + * {@inheritdoc} + */ + public function prepare($entity, array $field_info, array $instance, array $values) { + $arguments = array(); + if (isset($values['arguments'])) { + $arguments = array_filter($values['arguments']); + unset($values['arguments']); + } + $language = $this->getFieldLanguage($entity, $field_info, $arguments); + + // Setup the standard Field API array for saving. + $delta = 0; + foreach ($values as $value) { + $return[$language][$delta] = array('video_url' => $value); + $delta++; + } + + return isset($return) ? $return : NULL; + } +} diff --git a/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.module b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.module old mode 100755 new mode 100644 index 8f25a71..8826ead --- a/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.module +++ b/sites/all/modules/contrib/fields/video_embed_field/video_embed_field.module @@ -1,23 +1,28 @@ disabled = FALSE; /* Edit this to true to make a default video_embed_style disabled initially */ $normal->api_version = 1; $normal->name = 'normal'; + $normal->title = 'Normal'; $normal->data = array(); - $teaser = new stdClass; + $teaser = new stdClass(); $teaser->disabled = FALSE; /* Edit this to true to make a default video_embed_style disabled initially */ $teaser->api_version = 1; $teaser->name = 'teaser'; + $teaser->title = 'Teaser'; $teaser->data = array(); - //add in our settings for each of the handlers + // Add in our settings for each of the handlers. foreach ($handlers as $name => $handler) { $normal->data[$name] = $handler['defaults']; $teaser->data[$name] = $handler['defaults']; @@ -86,71 +93,29 @@ function video_embed_field_menu() { 'type' => MENU_CALLBACK, ); + $items['admin/config/media/vef'] = array( + 'title' => 'Video Embed Field', + 'description' => 'Video Embed Field configuration', + 'page callback' => 'system_admin_menu_block_page', + 'access arguments' => array('administer video styles'), + 'file' => 'system.admin.inc', + 'file path' => drupal_get_path('module', 'system'), + 'type' => MENU_NORMAL_ITEM, + ); + + $items['admin/config/media/vef/settings'] = array( + 'title' => 'Settings', + 'description' => 'Video Embed Field module settings', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('video_embed_field_settings_form'), + 'file' => 'video_embed_field.admin.inc', + 'access arguments' => array('administer video styles'), + 'type' => MENU_NORMAL_ITEM, + ); + return $items; } -/** - * Get an array of all styles and their settings. - * - * @return - * An array of styles keyed by the video style ID (vsid). - * @see video_embed_field_video_style_load() - */ -function video_embed_field_video_styles() { - $styles = &drupal_static(__FUNCTION__); - - // Grab from cache or build the array. - if (!isset($styles)) { - // load the style via ctools - which will handle all the caching for us - - // however, because it does a bit more logic, lets still statically cache this function - ctools_include('export'); - $styles = ctools_export_load_object('vef_video_styles'); - } - - return $styles; -} - -/** - * Load a style by style name or ID. May be used as a loader for menu items. - * - * Note that you may also use ctools_export_load_object with the key being vef_video_styles - * - * @param $name - * The name of the style. - * @param $isid - * Optional. The numeric id of a style if the name is not known. - * @return - * An video style array containing the following keys: - * - "vsid": The unique image style ID. - * - "name": The unique image style name. - * - "data": An array of video settings within this video style. - * If the video style name or ID is not valid, an empty array is returned. - */ -function video_embed_field_video_style_load($name = NULL, $vsid = NULL) { - $styles = video_embed_field_video_styles(); - - // If retrieving by name. - if (isset($name) && isset($styles[$name])) { - $style = $styles[$name]; - } // If retrieving by image style id. - elseif (!isset($name) && isset($vsid)) { - foreach ($styles as $name => $database_style) { - if (isset($database_style['vsid']) && $database_style['vsid'] == $vsid) { - $style = $database_style; - break; - } - } - } - - //if we found a style return it - if (isset($style)) { - return $style; - } - - // Otherwise the style was not found. - return FALSE; -} - /** * Implements hook_permission(). */ @@ -174,36 +139,115 @@ function video_embed_field_theme() { ), 'video_embed_field_embed_code' => array( 'template' => 'video-embed-field-embed-code', - 'variables' => array('url' => NULL, 'style' => 'normal', 'video_data' => array()), + 'variables' => array( + 'url' => NULL, + 'style' => 'normal', + 'video_data' => array(), + ), ), 'video_embed_field_colorbox_code' => array( - 'variables' => array('image_url' => NULL, 'image_style' => 'normal', 'video_url' => NULL, 'video_style' => NULL, 'video_data' => array()), + 'variables' => array( + 'image_url' => NULL, + 'image_style' => 'normal', + 'image_alt' => NULL, + 'video_url' => NULL, + 'video_style' => NULL, + 'video_data' => array(), + ), ), ); } /** - * Creates a hook that other modules can implement to get handlers - hook_video_embed_handler_info - * Can be used to add more handlers if needed - from other modules and such - * @see video_embed_field.api.php for more information + * Implements hook_views_api(). + */ +function video_embed_field_views_api() { + return array( + 'api' => 3, + 'path' => drupal_get_path('module', 'video_embed_field') . '/views', + ); +} + +/** + * Get an array of all styles and their settings. + * + * @return array + * An array of styles keyed by the video style name (name). + * + * @see video_embed_field_video_style_load() + */ +function video_embed_field_video_styles() { + $styles = &drupal_static(__FUNCTION__); + + // Grab from cache or build the array. + if (!isset($styles)) { + // Load the style via ctools - which will handle all the caching for us - + // Because it does a bit more logic, lets still statically cache this. + ctools_include('export'); + $styles = ctools_export_load_object('vef_video_styles'); + } + + return $styles; +} + +/** + * Load a style by style name. May be used as a loader for menu items. + * + * Note that you may also use ctools_export_load_object with the key being + * vef_video_styles + * + * @param string $name + * The name of the style. + * + * @return array + * An video style array containing the following keys: + * - "name": The unique video style ID. + * - "title": The human readable video style name. + * - "data": An array of video settings within this video style. + * If the video style name or ID is not valid, an empty array is returned. + */ +function video_embed_field_video_style_load($name) { + $styles = video_embed_field_video_styles(); + + return isset($styles[$name]) ? $styles[$name] : FALSE; +} + +/** + * Creates a hook that other modules can implement to get handlers. + * + * Can be used to add more handlers if needed - from other modules and such. + * + * @see hook_video_embed_handler_info + * @see video_embed_field.api.php */ function video_embed_get_handlers() { - $handlers = cache_get('video_embed_field_handlers'); + $handlers = &drupal_static(__FUNCTION__); - if ($handlers === FALSE) { - $handlers = module_invoke_all('video_embed_handler_info'); - drupal_alter('video_embed_field_handlers', $handlers); - cache_set('video_embed_field_handlers', $handlers); - } - else { - $handlers = $handlers->data; + if (!isset($handlers)) { + if ($handlers = cache_get('video_embed_field_handlers')) { + $handlers = $handlers->data; + } + else { + $handlers = module_invoke_all('video_embed_handler_info'); + drupal_alter('video_embed_handler_info', $handlers); + cache_set('video_embed_field_handlers', $handlers); + } } return $handlers; } +/** + * Retrieves the video handler for a video URL. + * + * @param string $url + * The video URL. + * + * @return string|bool + * The handler name for the URL, FALSE in case there is no handler. + */ function video_embed_get_handler($url) { - // Process video URL + // Process video URL. if (!stristr($url, 'http://') && !stristr($url, 'https://')) { $url = 'http://' . $url; } @@ -231,8 +275,13 @@ function video_embed_get_handler($url) { } /** - * Create a form from the player configuration options - * $defaults will be passed in with the default settings for the various fields + * Creates a form from the player configuration options. + * + * @param array $defaults + * The default settings for the various fields. + * + * @return array + * The configuration form array. */ function video_embed_field_get_form($defaults) { $form = array(); @@ -248,8 +297,9 @@ function video_embed_field_get_form($defaults) { $form[$name] += array( '#type' => 'fieldset', - '#title' => t($handler['title']), + '#title' => t('@provider settings', array('@provider' => $handler['title'])), '#tree' => TRUE, + '#element_validate' => isset($handler['form_validate']) ? array($handler['form_validate']) : array(), ); } } @@ -257,12 +307,28 @@ function video_embed_field_get_form($defaults) { return $form; } +/** + * Validates the iframe CSS dimensions. + * + * @param array $element + * The element to validate. + */ +function video_embed_field_validate_dimensions($element) { + if (!preg_match('/^(\d*)(px|%)?$/', $element['width']['#value'], $results)) { + form_error($element['width'], t('You should use a valid CSS value for width in @plugin plugin', array('@plugin' => $element['#title']))); + } + if (!preg_match('/^(\d*)(px|%)?$/', $element['height']['#value'], $results)) { + form_error($element['height'], t('You should use a valid CSS value for height in @plugin plugin', array('@plugin' => $element['#title']))); + } +} + /** * Get an array of image styles suitable for using as select list options. * - * @param $include_empty + * @param bool $include_empty * If TRUE a option will be inserted in the options array. - * @return + * + * @return array * Array of image styles both key and value are set to style name. */ function video_embed_field_video_style_options($include_empty = TRUE) { @@ -271,7 +337,9 @@ function video_embed_field_video_style_options($include_empty = TRUE) { if ($include_empty && !empty($styles)) { $options[''] = t(''); } - $options = array_merge($options, drupal_map_assoc(array_keys($styles))); + foreach ($styles as $style) { + $options[$style->name] = $style->title; + } if (empty($options)) { $options[''] = t('No defined styles'); } @@ -286,11 +354,23 @@ function video_embed_field_filter_info() { 'title' => t('Video Embedding'), 'description' => t('Replaces [VIDEO::http://www.youtube.com/watch?v=someVideoID::aVideoStyle] tags with embedded videos.'), 'process callback' => 'video_embed_field_filter_process', + 'tips callback' => '_filter_video_embed_tips', ); return $filters; } +/** + * Implements callback_filter_tips(). + * + * Provides help for the URL filter. + * + * @see filter_filter_info() + */ +function _filter_video_embed_tips($filter, $format, $long = FALSE) { + return t('Replaces [VIDEO::http://www.youtube.com/watch?v=someVideoID::aVideoStyle] tags with embedded videos.'); +} + /** * Video Embed Field filter process callback. */ @@ -298,12 +378,11 @@ function video_embed_field_filter_process($text, $filter, $format) { preg_match_all('/ \[VIDEO:: ( [^\[\]]+ )* \] /x', $text, $matches); $tag_match = (array) array_unique($matches[1]); - $handlers = video_embed_get_handlers(); foreach ($tag_match as $tag) { $parts = explode('::', $tag); - // Get video style + // Get video style. if (isset($parts[1])) { $style = $parts[1]; } @@ -320,40 +399,41 @@ function video_embed_field_filter_process($text, $filter, $format) { } /** - * Process variables to format a video player. + * Processes variables to format a video player. * - * $variables contains the following information: - * - $url - * - $style - * - $video_data + * @param array $variables + * Contains the following information: + * - $url + * - $style + * - $video_data * * @see video-embed.tpl.php */ function template_preprocess_video_embed_field_embed_code(&$variables) { - // Get the handler + // Get the handler. $handler = video_embed_get_handler($variables['url']); $variables['handler'] = $handler['name']; - // Load the style + // Load the style. $style = video_embed_field_video_style_load($variables['style']); - // If there was an issue load in the default style + // If there was an issue load in the default style. if ($style == FALSE) { $style = video_embed_field_video_style_load('normal'); } if (isset($style->data[$variables['handler']])) { $variables['style_settings'] = $style->data[$variables['handler']]; - } //safety valve for when we add new handlers and there are styles in the database. + } + // Safety value for when we add new handlers and there are styles stored. else { $variables['style_settings'] = $handler['defaults']; } - - // Prepare the URL + // Prepare the URL. if (!stristr($variables['url'], 'http://') && !stristr($variables['url'], 'https://')) { $variables['url'] = 'http://' . $variables['url']; } - // Prepare embed code + // Prepare embed code. if ($handler && isset($handler['function']) && function_exists($handler['function'])) { $embed_code = call_user_func($handler['function'], $variables['url'], $variables['style_settings']); $variables['embed_code'] = drupal_render($embed_code); @@ -362,89 +442,76 @@ function template_preprocess_video_embed_field_embed_code(&$variables) { $variables['embed_code'] = l($variables['url'], $variables['url']); } - // Prepare video data + // Prepare video data. $variables['data'] = $variables['video_data']; unset($variables['video_data']); } /** - * Returns image style image with a link to - * an embedded video in colorbox. + * Returns image style image with a link to an embedded video in colorbox. * - * @param $variables + * @param array $variables * An associative array containing: * - image_url: The image URL. * - image_style: The image style to use. + * - image_alt: The image ALT attribute. * - video_url: The video URL. * - video_style: The video style to use. * - video_data: An array of data about the video. * + * @return string + * The themed output. + * * @ingroup themeable */ function theme_video_embed_field_colorbox_code($variables) { - $style = video_embed_field_video_style_load($variables['video_style']); + $path = video_embed_field_get_ajax_url($variables['video_url'], $variables['video_style']); - // If there was an issue load in the default style - if ($style == FALSE) { - $style = video_embed_field_video_style_load('normal'); - } + $image = array( + '#theme' => 'image_formatter', + '#item' => array('uri' => $variables['image_url'], 'alt' => $variables['image_alt']), + '#image_style' => $variables['image_style'], + '#path' => $path, + ); - $handler = video_embed_get_handler($variables['video_url']); - - $data = $style->data[$handler['name']]; - - //Create a unique ID for colorbox inline - $id = uniqid('video_embed_field-' . rand()); - - if ($variables['image_style'] == 'none') { - $image = array( - array( - '#theme' => 'image', - '#path' => $variables['image_url'], - ), - ); - } - else { - $image = array( - '#theme' => 'image_style', - '#path' => $variables['image_url'], - '#style_name' => $variables['image_style'], - ); - } - - $image = drupal_render($image); - - // Write values for later AJAX load - $hash = _video_embed_field_store_video($variables['video_url'], $variables['video_style']); - - $output = l($image, base_path() . '?q=vef/load/' . $hash . '&width=' . ($data['width']) . '&height=' . ($data['height'] + 3), array('html' => TRUE, 'external' => TRUE, 'attributes' => array('class' => array('colorbox-load')))); - - return $output; + return drupal_render($image); } /** - * Get the thumbnail url for a given video url - * @param $url - the url of the video - * @return a string representing the url of the thumbnail, or FALSE on error - */ -function video_embed_field_thumbnail_url($url) { - $handler = video_embed_get_handler($url); - if ($handler && isset($handler['thumbnail_function']) && function_exists($handler['thumbnail_function'])) { - $info = call_user_func($handler['thumbnail_function'], $url); - $info['handler'] = $handler['name']; - return $info; - } - return FALSE; -} - -/** - * Get a video data array for a given video url + * Gets the thumbnail url for a given video url. * * @param string $url - * A video URL of the data array you want returned + * The url of the video. * - * @return array|false $data - * An array of video data, or FALSE on error + * @return string + * String representing the url of the thumbnail, or FALSE on error. + */ +function video_embed_field_thumbnail_url($url) { + $info = FALSE; + if ($handler = video_embed_get_handler($url)) { + if (isset($handler['thumbnail_function']) && function_exists($handler['thumbnail_function'])) { + $info = call_user_func($handler['thumbnail_function'], $url); + $info['handler'] = $handler['name']; + } + if (empty($info['url']) && isset($handler['thumbnail_default']) && file_exists($handler['thumbnail_default'])) { + $info = array( + 'handler' => $handler['name'], + 'id' => 'default_thumbnail', + 'url' => $handler['thumbnail_default'], + ); + } + } + return $info; +} + +/** + * Gets a video data array for a given video url. + * + * @param string $url + * A video URL of the data array you want returned. + * + * @return array|false + * An array of video data, or FALSE on error. */ function video_embed_field_get_video_data($url) { $handler = video_embed_get_handler($url); @@ -457,7 +524,53 @@ function video_embed_field_get_video_data($url) { } /** - * Fetch all available provider domains. + * Generates the AJAX path array from the video URL and the video_style. + * + * @param string $video_url + * The URL to the video. + * @param string $video_style + * The video style to render the video. + * + * @return array + * The AJAX path array. + */ +function video_embed_field_get_ajax_url($video_url, $video_style) { + $style = video_embed_field_video_style_load($video_style); + + // If there was an issue load in the default style. + if ($style == FALSE) { + $style = video_embed_field_video_style_load('normal'); + } + + $handler = video_embed_get_handler($video_url); + + $data = $style->data[$handler['name']]; + + // Write values for later AJAX load. + $hash = _video_embed_field_store_video($video_url, $video_style); + + return array( + 'path' => 'vef/load/' . $hash, + 'options' => array( + 'attributes' => array( + 'class' => array( + 'colorbox-load', + 'colorbox' + ), + ), + 'query' => array( + 'width' => $data['width'], + 'height' => $data['height'] + 5, + ), + ), + ); +} + +/** + * Fetches all available provider domains. + * + * @return array + * An array containing the allowed video domains. */ function _video_embed_field_get_provider_domains() { $domains = array(); @@ -475,13 +588,32 @@ function _video_embed_field_get_provider_domains() { } /** - * Fetch settings string + * Fetches all available provider domains for certain field instance. + * + * @param array $instance + * The instance definition. + * + * @return array + * An array containing the allowed video domains. + */ +function _video_embed_field_get_instance_provider_domains($instance) { + return array_intersect(_video_embed_field_get_provider_domains(), $instance['settings']['allowed_providers']); +} + +/** + * Fetches settings string. + * + * @param array $settings + * The settings array. + * + * @return string + * The settings string generated from the settings array. */ function _video_embed_code_get_settings_str($settings = array()) { $values = array(); foreach ($settings as $name => $value) { - if (empty($value)) { + if (!isset($value)) { $values[] = $name; } else { @@ -492,19 +624,22 @@ function _video_embed_code_get_settings_str($settings = array()) { return implode('&', $values); } -//used to array filter in video_embed_field_requirements -function _video_embed_field_array_filter($item) { - return (isset($item['type']) && $item['type'] == 'video_embed_field'); -} - /** - * Store a video to be loaded later from an _video_embed_field_load_video + * Stores a video to be loaded later from an _video_embed_field_load_video. + * + * @param string $video_url + * The video URL. + * @param string $video_style + * The video style. + * + * @return string + * The hash generated for the video URL and the given style. */ function _video_embed_field_store_video($video_url, $video_style) { - //create a hash key + // Create a hash key. $hash = _video_embed_field_hash($video_url, $video_style); - //check that is record doesn't already exist before saving it + // Check that this record doesn't already exist before saving it. if (!_video_embed_field_load_video($hash)) { $record = array( 'vhash' => $hash, @@ -514,7 +649,7 @@ function _video_embed_field_store_video($video_url, $video_style) { cache_set('vef-store-' . $hash, $record); - //add it to our static cache so we won't have to go to the database + // Add it to our static cache so we won't have to go to the database. $static_cache = &drupal_static('vef_video_store', array()); $static_cache[$hash] = $record; } @@ -522,7 +657,13 @@ function _video_embed_field_store_video($video_url, $video_style) { } /** - * Callback to render a video for an Ajax call + * Renders a video for an AJAX call. + * + * @param string $hash + * The video hash. + * + * @return Null + * Null because prints an AJAX output. */ function _video_embed_field_show_video($hash) { $data = _video_embed_field_load_video($hash); @@ -537,20 +678,24 @@ function _video_embed_field_show_video($hash) { } /** - * Loads a video from the video store given its hash - * Returns either the data - an array with hash, video_url and video_style keys + * Loads a video from the video store given its hash. + * + * @param string $hash + * The video hash. + * + * @return array|bool + * An array with video definition, FALSE if the hash does not exist. */ function _video_embed_field_load_video($hash) { $static_cache = &drupal_static('vef_video_store', array()); - //check if we've already loaded it + // Check if we've already loaded it. if (isset($static_cache[$hash])) { return $static_cache[$hash]; } else { - $result = cache_get('vef-store-' . $hash); if ($result) { - //cache it before returning + // Cache it before returning. $data = $result->data; $static_cache[$hash] = $data; return $data; @@ -562,9 +707,16 @@ function _video_embed_field_load_video($hash) { } /** - * Creates a hash for storing or looking up a video in the store table + * Creates a hash for storing or looking up a video in the store table. + * + * @param string $video_url + * The video URL. + * @param string $video_style + * The video style. + * + * @return string + * The hash generated for the video URL and the given style. */ function _video_embed_field_hash($video_url, $video_style) { return md5('vef' . $video_url . $video_style); } - diff --git a/sites/all/modules/contrib/fields/video_embed_field/views/handlers/views_embed_field_views_handler_field_thumbnail_path.inc b/sites/all/modules/contrib/fields/video_embed_field/views/handlers/views_embed_field_views_handler_field_thumbnail_path.inc new file mode 100644 index 0000000..c1178fe --- /dev/null +++ b/sites/all/modules/contrib/fields/video_embed_field/views/handlers/views_embed_field_views_handler_field_thumbnail_path.inc @@ -0,0 +1,22 @@ + 'views_handler_field', + 'thumbnail_path' => 'views_embed_field_views_handler_field_thumbnail_path', + 'description' => 'views_handler_field', + ); + + // Get the translated field information. + $properties = video_embed_field_data_property_info(); + + // Iterate over video_embed_field defined tables. + foreach ($data as &$table) { + // Make sure the parent Views field (video_embed_field) is defined. + if (isset($table[$field['field_name']]['field'])) { + // Use the parent field definition as a template for component columns. + $field_def = $table[$field['field_name']]['field']; + + // Remove 'additional fields' from the field definition. We don't + // necessarily want all our sibling columns. + unset($field_def['additional fields']); + + // Define the valid columns. + $valid_columns = array(); + foreach ($implemented as $implement => $handler) { + $column_name = $field['field_name'] . '_' . $implement; + $valid_columns[$column_name] = $handler; + } + + // Iterate over the video_embed_field components. + foreach ($table as $column_name => &$column) { + if (empty($column['field']) && isset($valid_columns[$column_name])) { + // Assign the default component definition. + $column['field'] = $field_def; + $column['field']['real field'] = $column_name; + $column['field']['handler'] = $valid_columns[$column_name]; + + // Assign human-friendly labels for video_embed_field components. + $field_labels = field_views_field_label($field['field_name']); + $field_label = array_shift($field_labels); + $property = str_replace($field_def['field_name'] . '_', '', $column_name); + + if (!empty($properties[$property])) { + $property_label = $properties[$property]['label']; + + $title = t('@field-label - @property-label', array( + '@field-label' => $field_label, + '@property-label' => $property_label, + )); + + $column['title'] = $title; + $column['title short'] = $title; + } + } + } + } + } + + return $data; +} diff --git a/sites/all/modules/contrib/js/jquery_update/jquery_update.info b/sites/all/modules/contrib/js/jquery_update/jquery_update.info index 7517c48..81c86a7 100644 --- a/sites/all/modules/contrib/js/jquery_update/jquery_update.info +++ b/sites/all/modules/contrib/js/jquery_update/jquery_update.info @@ -7,9 +7,9 @@ files[] = jquery_update.install configure = admin/config/development/jquery_update -; Information added by Drupal.org packaging script on 2015-01-25 -version = "7.x-2.5" +; Information added by Drupal.org packaging script on 2015-10-20 +version = "7.x-2.7" core = "7.x" project = "jquery_update" -datestamp = "1422221882" +datestamp = "1445379855" diff --git a/sites/all/modules/contrib/js/jquery_update/jquery_update.module b/sites/all/modules/contrib/js/jquery_update/jquery_update.module index 956c9e5..9d143be 100644 --- a/sites/all/modules/contrib/js/jquery_update/jquery_update.module +++ b/sites/all/modules/contrib/js/jquery_update/jquery_update.module @@ -267,11 +267,11 @@ function jquery_update_jquery_replace(&$javascript, $cdn, $path, $min, $version) break; case '1.7': - $trueversion = '1.7.1'; + $trueversion = '1.7.2'; break; case '1.8': - $trueversion = '1.8.2'; + $trueversion = '1.8.3'; break; case '1.9': diff --git a/sites/all/modules/contrib/js/jquery_update/replace/jquery/1.7/jquery.js b/sites/all/modules/contrib/js/jquery_update/replace/jquery/1.7/jquery.js index 8ccd0ea..3774ff9 100644 --- a/sites/all/modules/contrib/js/jquery_update/replace/jquery/1.7/jquery.js +++ b/sites/all/modules/contrib/js/jquery_update/replace/jquery/1.7/jquery.js @@ -1,5 +1,5 @@ /*! - * jQuery JavaScript Library v1.7.1 + * jQuery JavaScript Library v1.7.2 * http://jquery.com/ * * Copyright 2011, John Resig @@ -11,7 +11,7 @@ * Copyright 2011, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * - * Date: Mon Nov 21 21:11:03 2011 -0500 + * Date: Wed Mar 21 12:46:34 2012 -0700 */ (function( window, undefined ) { @@ -210,7 +210,7 @@ jQuery.fn = jQuery.prototype = { selector: "", // The current version of jQuery being used - jquery: "1.7.1", + jquery: "1.7.2", // The default length of a jQuery object is 0 length: 0, @@ -497,9 +497,8 @@ jQuery.extend({ return jQuery.type(obj) === "array"; }, - // A crude way of determining if an object is a window isWindow: function( obj ) { - return obj && typeof obj === "object" && "setInterval" in obj; + return obj != null && obj == obj.window; }, isNumeric: function( obj ) { @@ -579,6 +578,9 @@ jQuery.extend({ // Cross-browser xml parsing parseXML: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } var xml, tmp; try { if ( window.DOMParser ) { // Standard @@ -822,31 +824,55 @@ jQuery.extend({ // Mutifunctional method to get and set values to a collection // The value/s can optionally be executed if it's a function - access: function( elems, key, value, exec, fn, pass ) { - var length = elems.length; + access: function( elems, fn, key, value, chainable, emptyGet, pass ) { + var exec, + bulk = key == null, + i = 0, + length = elems.length; - // Setting many attributes - if ( typeof key === "object" ) { - for ( var k in key ) { - jQuery.access( elems, k, key[k], exec, fn, value ); + // Sets many values + if ( key && typeof key === "object" ) { + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], 1, emptyGet, value ); } - return elems; - } + chainable = 1; - // Setting one attribute - if ( value !== undefined ) { + // Sets one value + } else if ( value !== undefined ) { // Optionally, function values get executed if exec is true - exec = !pass && exec && jQuery.isFunction(value); + exec = pass === undefined && jQuery.isFunction( value ); - for ( var i = 0; i < length; i++ ) { - fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + if ( bulk ) { + // Bulk operations only iterate when executing function values + if ( exec ) { + exec = fn; + fn = function( elem, key, value ) { + return exec.call( jQuery( elem ), value ); + }; + + // Otherwise they run against the entire set + } else { + fn.call( elems, value ); + fn = null; + } } - return elems; + if ( fn ) { + for (; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + } + + chainable = 1; } - // Getting an attribute - return length ? fn( elems[0], key ) : undefined; + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; }, now: function() { @@ -1005,6 +1031,8 @@ jQuery.Callbacks = function( flags ) { stack = [], // Last fire value (for non-forgettable lists) memory, + // Flag to know if list was already fired + fired, // Flag to know if list is currently firing firing, // First callback to fire (used internally by add and fireWith) @@ -1038,6 +1066,7 @@ jQuery.Callbacks = function( flags ) { fire = function( context, args ) { args = args || []; memory = !flags.memory || [ context, args ]; + fired = true; firing = true; firingIndex = firingStart || 0; firingStart = 0; @@ -1173,7 +1202,7 @@ jQuery.Callbacks = function( flags ) { }, // To know if the callbacks have already been called at least once fired: function() { - return !!memory; + return !!fired; } }; @@ -1336,7 +1365,6 @@ jQuery.support = (function() { select, opt, input, - marginDiv, fragment, tds, events, @@ -1419,9 +1447,13 @@ jQuery.support = (function() { noCloneEvent: true, inlineBlockNeedsLayout: false, shrinkWrapBlocks: false, - reliableMarginRight: true + reliableMarginRight: true, + pixelMargin: true }; + // jQuery.boxModel DEPRECATED in 1.3, use jQuery.support.boxModel instead + jQuery.boxModel = support.boxModel = (document.compatMode === "CSS1Compat"); + // Make sure checked status is properly cloned input.checked = true; support.noCloneChecked = input.cloneNode( true ).checked; @@ -1456,6 +1488,10 @@ jQuery.support = (function() { support.radioValue = input.value === "t"; input.setAttribute("checked", "checked"); + + // #11217 - WebKit loses check when the name is after the checked attribute + input.setAttribute( "name", "t" ); + div.appendChild( input ); fragment = document.createDocumentFragment(); fragment.appendChild( div.lastChild ); @@ -1470,23 +1506,6 @@ jQuery.support = (function() { fragment.removeChild( input ); fragment.appendChild( div ); - div.innerHTML = ""; - - // Check if div with explicit width and no margin-right incorrectly - // gets computed margin-right based on width of container. For more - // info see bug #3333 - // Fails in WebKit before Feb 2011 nightlies - // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right - if ( window.getComputedStyle ) { - marginDiv = document.createElement( "div" ); - marginDiv.style.width = "0"; - marginDiv.style.marginRight = "0"; - div.style.width = "2px"; - div.appendChild( marginDiv ); - support.reliableMarginRight = - ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; - } - // Technique from Juriy Zaytsev // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ // We only care about the case where non-standard event systems @@ -1494,7 +1513,7 @@ jQuery.support = (function() { // avoid an eval call (in setAttribute) which can cause CSP // to go haywire. See: https://developer.mozilla.org/en/Security/CSP if ( div.attachEvent ) { - for( i in { + for ( i in { submit: 1, change: 1, focusin: 1 @@ -1512,12 +1531,13 @@ jQuery.support = (function() { fragment.removeChild( div ); // Null elements to avoid leaks in IE - fragment = select = opt = marginDiv = div = input = null; + fragment = select = opt = div = input = null; // Run tests that need a body at doc ready jQuery(function() { var container, outer, inner, table, td, offsetSupport, - conMarginTop, ptlm, vb, style, html, + marginDiv, conMarginTop, style, html, positionTopLeftWidthHeight, + paddingMarginBorderVisibility, paddingMarginBorder, body = document.getElementsByTagName("body")[0]; if ( !body ) { @@ -1526,15 +1546,16 @@ jQuery.support = (function() { } conMarginTop = 1; - ptlm = "position:absolute;top:0;left:0;width:1px;height:1px;margin:0;"; - vb = "visibility:hidden;border:0;"; - style = "style='" + ptlm + "border:5px solid #000;padding:0;'"; - html = "
    " + - "" + + paddingMarginBorder = "padding:0;margin:0;border:"; + positionTopLeftWidthHeight = "position:absolute;top:0;left:0;width:1px;height:1px;"; + paddingMarginBorderVisibility = paddingMarginBorder + "0;visibility:hidden;"; + style = "style='" + positionTopLeftWidthHeight + paddingMarginBorder + "5px solid #000;"; + html = "
    " + + "
    " + "
    "; container = document.createElement("div"); - container.style.cssText = vb + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px"; + container.style.cssText = paddingMarginBorderVisibility + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px"; body.insertBefore( container, body.firstChild ); // Construct the test element @@ -1548,7 +1569,7 @@ jQuery.support = (function() { // display:none (it is still safe to use offsets if a parent element is // hidden; don safety goggles and see bug #4512 for more information). // (only IE 8 fails this test) - div.innerHTML = "
    t
    "; + div.innerHTML = "
    t
    "; tds = div.getElementsByTagName( "td" ); isSupported = ( tds[ 0 ].offsetHeight === 0 ); @@ -1559,28 +1580,44 @@ jQuery.support = (function() { // (IE <= 8 fail this test) support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); - // Figure out if the W3C box model works as expected - div.innerHTML = ""; - div.style.width = div.style.paddingLeft = "1px"; - jQuery.boxModel = support.boxModel = div.offsetWidth === 2; + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + if ( window.getComputedStyle ) { + div.innerHTML = ""; + marginDiv = document.createElement( "div" ); + marginDiv.style.width = "0"; + marginDiv.style.marginRight = "0"; + div.style.width = "2px"; + div.appendChild( marginDiv ); + support.reliableMarginRight = + ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; + } if ( typeof div.style.zoom !== "undefined" ) { // Check if natively block-level elements act like inline-block // elements when setting their display to 'inline' and giving // them layout // (IE < 8 does this) + div.innerHTML = ""; + div.style.width = div.style.padding = "1px"; + div.style.border = 0; + div.style.overflow = "hidden"; div.style.display = "inline"; div.style.zoom = 1; - support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 ); + support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); // Check if elements with layout shrink-wrap their children // (IE 6 does this) - div.style.display = ""; - div.innerHTML = "
    "; - support.shrinkWrapBlocks = ( div.offsetWidth !== 2 ); + div.style.display = "block"; + div.style.overflow = "visible"; + div.innerHTML = "
    "; + support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); } - div.style.cssText = ptlm + vb; + div.style.cssText = positionTopLeftWidthHeight + paddingMarginBorderVisibility; div.innerHTML = html; outer = div.firstChild; @@ -1605,8 +1642,17 @@ jQuery.support = (function() { offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 ); offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop ); + if ( window.getComputedStyle ) { + div.style.marginTop = "1%"; + support.pixelMargin = ( window.getComputedStyle( div, null ) || { marginTop: 0 } ).marginTop !== "1%"; + } + + if ( typeof container.style.zoom !== "undefined" ) { + container.style.zoom = 1; + } + body.removeChild( container ); - div = container = null; + marginDiv = div = container = null; jQuery.extend( support, offsetSupport ); }); @@ -1863,62 +1909,70 @@ jQuery.extend({ jQuery.fn.extend({ data: function( key, value ) { - var parts, attr, name, + var parts, part, attr, name, l, + elem = this[0], + i = 0, data = null; - if ( typeof key === "undefined" ) { + // Gets all values + if ( key === undefined ) { if ( this.length ) { - data = jQuery.data( this[0] ); + data = jQuery.data( elem ); - if ( this[0].nodeType === 1 && !jQuery._data( this[0], "parsedAttrs" ) ) { - attr = this[0].attributes; - for ( var i = 0, l = attr.length; i < l; i++ ) { + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + attr = elem.attributes; + for ( l = attr.length; i < l; i++ ) { name = attr[i].name; if ( name.indexOf( "data-" ) === 0 ) { name = jQuery.camelCase( name.substring(5) ); - dataAttr( this[0], name, data[ name ] ); + dataAttr( elem, name, data[ name ] ); } } - jQuery._data( this[0], "parsedAttrs", true ); + jQuery._data( elem, "parsedAttrs", true ); } } return data; + } - } else if ( typeof key === "object" ) { + // Sets multiple values + if ( typeof key === "object" ) { return this.each(function() { jQuery.data( this, key ); }); } - parts = key.split("."); + parts = key.split( ".", 2 ); parts[1] = parts[1] ? "." + parts[1] : ""; + part = parts[1] + "!"; - if ( value === undefined ) { - data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); + return jQuery.access( this, function( value ) { - // Try to fetch any internally stored data first - if ( data === undefined && this.length ) { - data = jQuery.data( this[0], key ); - data = dataAttr( this[0], key, data ); + if ( value === undefined ) { + data = this.triggerHandler( "getData" + part, [ parts[0] ] ); + + // Try to fetch any internally stored data first + if ( data === undefined && elem ) { + data = jQuery.data( elem, key ); + data = dataAttr( elem, key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; } - return data === undefined && parts[1] ? - this.data( parts[0] ) : - data; + parts[1] = value; + this.each(function() { + var self = jQuery( this ); - } else { - return this.each(function() { - var self = jQuery( this ), - args = [ parts[0], value ]; - - self.triggerHandler( "setData" + parts[1] + "!", args ); + self.triggerHandler( "setData" + part, parts ); jQuery.data( this, key, value ); - self.triggerHandler( "changeData" + parts[1] + "!", args ); + self.triggerHandler( "changeData" + part, parts ); }); - } + }, null, value, arguments.length > 1, null, false ); }, removeData: function( key ) { @@ -1942,7 +1996,7 @@ function dataAttr( elem, key, data ) { data = data === "true" ? true : data === "false" ? false : data === "null" ? null : - jQuery.isNumeric( data ) ? parseFloat( data ) : + jQuery.isNumeric( data ) ? +data : rbrace.test( data ) ? jQuery.parseJSON( data ) : data; } catch( e ) {} @@ -2077,21 +2131,27 @@ jQuery.extend({ jQuery.fn.extend({ queue: function( type, data ) { + var setter = 2; + if ( typeof type !== "string" ) { data = type; type = "fx"; + setter--; } - if ( data === undefined ) { + if ( arguments.length < setter ) { return jQuery.queue( this[0], type ); } - return this.each(function() { - var queue = jQuery.queue( this, type, data ); - if ( type === "fx" && queue[0] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - }); + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); }, dequeue: function( type ) { return this.each(function() { @@ -2145,7 +2205,7 @@ jQuery.fn.extend({ } } resolve(); - return defer.promise(); + return defer.promise( object ); } }); @@ -2164,7 +2224,7 @@ var rclass = /[\n\t\r]/g, jQuery.fn.extend({ attr: function( name, value ) { - return jQuery.access( this, name, value, true, jQuery.attr ); + return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); }, removeAttr: function( name ) { @@ -2174,7 +2234,7 @@ jQuery.fn.extend({ }, prop: function( name, value ) { - return jQuery.access( this, name, value, true, jQuery.prop ); + return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); }, removeProp: function( name ) { @@ -2314,7 +2374,7 @@ jQuery.fn.extend({ if ( !arguments.length ) { if ( elem ) { - hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ]; + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { return ret; @@ -2358,7 +2418,7 @@ jQuery.fn.extend({ }); } - hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ]; + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; // If set returns undefined, fall back to normal setting if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { @@ -2504,7 +2564,7 @@ jQuery.extend({ }, removeAttr: function( elem, value ) { - var propName, attrNames, name, l, + var propName, attrNames, name, l, isBool, i = 0; if ( value && elem.nodeType === 1 ) { @@ -2516,13 +2576,17 @@ jQuery.extend({ if ( name ) { propName = jQuery.propFix[ name ] || name; + isBool = rboolean.test( name ); // See #9699 for explanation of this approach (setting first, then removal) - jQuery.attr( elem, name, "" ); + // Do not do this for boolean attributes (see #10870) + if ( !isBool ) { + jQuery.attr( elem, name, "" ); + } elem.removeAttribute( getSetAttribute ? name : propName ); // Set corresponding property to false for boolean attributes - if ( rboolean.test( name ) && propName in elem ) { + if ( isBool && propName in elem ) { elem[ propName ] = false; } } @@ -2676,7 +2740,8 @@ if ( !getSetAttribute ) { fixSpecified = { name: true, - id: true + id: true, + coords: true }; // Use this for any attribute in IE6/7 @@ -2806,7 +2871,7 @@ jQuery.each([ "radio", "checkbox" ], function() { var rformElems = /^(?:textarea|input|select)$/i, rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/, - rhoverHack = /\bhover(\.\S+)?\b/, + rhoverHack = /(?:^|\s)hover(\.\S+)?\b/, rkeyEvent = /^key/, rmouseEvent = /^(?:mouse|contextmenu)|click/, rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, @@ -2854,6 +2919,7 @@ jQuery.event = { if ( handler.handler ) { handleObjIn = handler; handler = handleObjIn.handler; + selector = handleObjIn.selector; } // Make sure that the handler has a unique ID, used to find/remove it later @@ -2905,7 +2971,7 @@ jQuery.event = { handler: handler, guid: handler.guid, selector: selector, - quick: quickParse( selector ), + quick: selector && quickParse( selector ), namespace: namespaces.join(".") }, handleObjIn ); @@ -3194,6 +3260,7 @@ jQuery.event = { delegateCount = handlers.delegateCount, args = [].slice.call( arguments, 0 ), run_all = !event.exclusive && !event.namespace, + special = jQuery.event.special[ event.type ] || {}, handlerQueue = [], i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related; @@ -3201,34 +3268,43 @@ jQuery.event = { args[0] = event; event.delegateTarget = this; + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + // Determine handlers that should run if there are delegated events - // Avoid disabled elements in IE (#6911) and non-left-click bubbling in Firefox (#3861) - if ( delegateCount && !event.target.disabled && !(event.button && event.type === "click") ) { + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && !(event.button && event.type === "click") ) { // Pregenerate a single jQuery object for reuse with .is() jqcur = jQuery(this); jqcur.context = this.ownerDocument || this; for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { - selMatch = {}; - matches = []; - jqcur[0] = cur; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - sel = handleObj.selector; - if ( selMatch[ sel ] === undefined ) { - selMatch[ sel ] = ( - handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel ) - ); + // Don't process events on disabled elements (#6911, #8165) + if ( cur.disabled !== true ) { + selMatch = {}; + matches = []; + jqcur[0] = cur; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + sel = handleObj.selector; + + if ( selMatch[ sel ] === undefined ) { + selMatch[ sel ] = ( + handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel ) + ); + } + if ( selMatch[ sel ] ) { + matches.push( handleObj ); + } } - if ( selMatch[ sel ] ) { - matches.push( handleObj ); + if ( matches.length ) { + handlerQueue.push({ elem: cur, matches: matches }); } } - if ( matches.length ) { - handlerQueue.push({ elem: cur, matches: matches }); - } } } @@ -3266,6 +3342,11 @@ jQuery.event = { } } + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + return event.result; }, @@ -3557,16 +3638,23 @@ if ( !jQuery.support.submitBubbles ) { form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; if ( form && !form._submit_attached ) { jQuery.event.add( form, "submit._submit", function( event ) { - // If form was submitted by the user, bubble the event up the tree - if ( this.parentNode && !event.isTrigger ) { - jQuery.event.simulate( "submit", this.parentNode, event, true ); - } + event._submit_bubble = true; }); form._submit_attached = true; } }); // return undefined since we don't need an event listener }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, teardown: function() { // Only need this for delegated form submit events @@ -3671,9 +3759,9 @@ jQuery.fn.extend({ // Types can be a map of types/handlers if ( typeof types === "object" ) { // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { + if ( typeof selector !== "string" ) { // && selector != null // ( types-Object, data ) - data = selector; + data = data || selector; selector = undefined; } for ( type in types ) { @@ -3719,14 +3807,14 @@ jQuery.fn.extend({ }); }, one: function( types, selector, data, fn ) { - return this.on.call( this, types, selector, data, fn, 1 ); + return this.on( types, selector, data, fn, 1 ); }, off: function( types, selector, fn ) { if ( types && types.preventDefault && types.handleObj ) { // ( event ) dispatched jQuery.Event var handleObj = types.handleObj; jQuery( types.delegateTarget ).off( - handleObj.namespace? handleObj.type + "." + handleObj.namespace : handleObj.type, + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, handleObj.selector, handleObj.handler ); @@ -3885,7 +3973,7 @@ var Sizzle = function( selector, context, results, seed ) { if ( context.nodeType !== 1 && context.nodeType !== 9 ) { return []; } - + if ( !selector || typeof selector !== "string" ) { return results; } @@ -3895,7 +3983,7 @@ var Sizzle = function( selector, context, results, seed ) { contextXML = Sizzle.isXML( context ), parts = [], soFar = selector; - + // Reset the position of the chunker regexp (start from head) do { chunker.exec( "" ); @@ -3903,9 +3991,9 @@ var Sizzle = function( selector, context, results, seed ) { if ( m ) { soFar = m[3]; - + parts.push( m[1] ); - + if ( m[2] ) { extra = m[3]; break; @@ -3929,7 +4017,7 @@ var Sizzle = function( selector, context, results, seed ) { if ( Expr.relative[ selector ] ) { selector += parts.shift(); } - + set = posProcess( selector, set, seed ); } } @@ -4057,7 +4145,7 @@ Sizzle.find = function( expr, context, isXML ) { for ( i = 0, len = Expr.order.length; i < len; i++ ) { type = Expr.order[i]; - + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { left = match[1]; match.splice( 1, 1 ); @@ -4189,7 +4277,7 @@ var getText = Sizzle.getText = function( elem ) { ret = ""; if ( nodeType ) { - if ( nodeType === 1 || nodeType === 9 ) { + if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { // Use textContent || innerText for elements if ( typeof elem.textContent === 'string' ) { return elem.textContent; @@ -4429,7 +4517,7 @@ var Expr = Sizzle.selectors = { ATTR: function( match, curLoop, inplace, result, not, isXML ) { var name = match[1] = match[1].replace( rBackslash, "" ); - + if ( !isXML && Expr.attrMap[name] ) { match[1] = Expr.attrMap[name]; } @@ -4463,7 +4551,7 @@ var Expr = Sizzle.selectors = { } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { return true; } - + return match; }, @@ -4473,7 +4561,7 @@ var Expr = Sizzle.selectors = { return match; } }, - + filters: { enabled: function( elem ) { return elem.disabled === false && elem.type !== "hidden"; @@ -4486,14 +4574,14 @@ var Expr = Sizzle.selectors = { checked: function( elem ) { return elem.checked === true; }, - + selected: function( elem ) { // Accessing this property makes selected-by-default // options in Safari work properly if ( elem.parentNode ) { elem.parentNode.selectedIndex; } - + return elem.selected === true; }, @@ -4515,7 +4603,7 @@ var Expr = Sizzle.selectors = { text: function( elem ) { var attr = elem.getAttribute( "type" ), type = elem.type; - // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) // use getAttribute instead to test this case return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); }, @@ -4633,22 +4721,23 @@ var Expr = Sizzle.selectors = { switch ( type ) { case "only": case "first": - while ( (node = node.previousSibling) ) { - if ( node.nodeType === 1 ) { - return false; + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; } } - if ( type === "first" ) { - return true; + if ( type === "first" ) { + return true; } node = elem; + /* falls through */ case "last": - while ( (node = node.nextSibling) ) { - if ( node.nodeType === 1 ) { - return false; + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; } } @@ -4661,22 +4750,22 @@ var Expr = Sizzle.selectors = { if ( first === 1 && last === 0 ) { return true; } - + doneName = match[0]; parent = elem.parentNode; - + if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) { count = 0; - + for ( node = parent.firstChild; node; node = node.nextSibling ) { if ( node.nodeType === 1 ) { node.nodeIndex = ++count; } - } + } parent[ expando ] = doneName; } - + diff = elem.nodeIndex - last; if ( first === 0 ) { @@ -4695,7 +4784,7 @@ var Expr = Sizzle.selectors = { TAG: function( elem, match ) { return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match; }, - + CLASS: function( elem, match ) { return (" " + (elem.className || elem.getAttribute("class")) + " ") .indexOf( match ) > -1; @@ -4757,6 +4846,9 @@ for ( var type in Expr.match ) { Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); } +// Expose origPOS +// "global" as in regardless of relation to brackets/parens +Expr.match.globalPOS = origPOS; var makeArray = function( array, results ) { array = Array.prototype.slice.call( array, 0 ); @@ -4765,7 +4857,7 @@ var makeArray = function( array, results ) { results.push.apply( results, array ); return results; } - + return array; }; @@ -4997,7 +5089,7 @@ if ( document.querySelectorAll ) { if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { return; } - + Sizzle = function( query, context, extra, seed ) { context = context || document; @@ -5006,24 +5098,24 @@ if ( document.querySelectorAll ) { if ( !seed && !Sizzle.isXML(context) ) { // See if we find a selector to speed up var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); - + if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { // Speed-up: Sizzle("TAG") if ( match[1] ) { return makeArray( context.getElementsByTagName( query ), extra ); - + // Speed-up: Sizzle(".CLASS") } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { return makeArray( context.getElementsByClassName( match[2] ), extra ); } } - + if ( context.nodeType === 9 ) { // Speed-up: Sizzle("body") // The body element only exists once, optimize finding it if ( query === "body" && context.body ) { return makeArray( [ context.body ], extra ); - + // Speed-up: Sizzle("#ID") } else if ( match && match[3] ) { var elem = context.getElementById( match[3] ); @@ -5036,12 +5128,12 @@ if ( document.querySelectorAll ) { if ( elem.id === match[3] ) { return makeArray( [ elem ], extra ); } - + } else { return makeArray( [], extra ); } } - + try { return makeArray( context.querySelectorAll(query), extra ); } catch(qsaError) {} @@ -5079,7 +5171,7 @@ if ( document.querySelectorAll ) { } } } - + return oldSizzle(query, context, extra, seed); }; @@ -5106,7 +5198,7 @@ if ( document.querySelectorAll ) { // This should fail with an exception // Gecko does not error, returns false instead matches.call( document.documentElement, "[test!='']:sizzle" ); - + } catch( pseudoError ) { pseudoWorks = true; } @@ -5116,7 +5208,7 @@ if ( document.querySelectorAll ) { expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); if ( !Sizzle.isXML( node ) ) { - try { + try { if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { var ret = matches.call( node, expr ); @@ -5153,7 +5245,7 @@ if ( document.querySelectorAll ) { if ( div.getElementsByClassName("e").length === 1 ) { return; } - + Expr.order.splice(1, 0, "CLASS"); Expr.find.CLASS = function( match, context, isXML ) { if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { @@ -5204,7 +5296,7 @@ function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { if ( elem ) { var match = false; - + elem = elem[dir]; while ( elem ) { @@ -5257,7 +5349,7 @@ if ( document.documentElement.contains ) { Sizzle.isXML = function( elem ) { // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) + // (such as loading iframes in IE - #4833) var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; return documentElement ? documentElement.nodeName !== "HTML" : false; @@ -5307,7 +5399,7 @@ var runtil = /Until$/, rmultiselector = /,/, isSimple = /^.[^:#\[\.,]*$/, slice = Array.prototype.slice, - POS = jQuery.expr.match.POS, + POS = jQuery.expr.match.globalPOS, // methods guaranteed to produce a unique set when starting from a unique set guaranteedUnique = { children: true, @@ -5374,11 +5466,11 @@ jQuery.fn.extend({ }, is: function( selector ) { - return !!selector && ( + return !!selector && ( typeof selector === "string" ? // If this is a positional selector, check membership in the returned set // so $("p:first").is("p:last") won't return true for a doc with two "p". - POS.test( selector ) ? + POS.test( selector ) ? jQuery( selector, this.context ).index( this[0] ) >= 0 : jQuery.filter( selector, this ).length > 0 : this.filter( selector ).length > 0 ); @@ -5386,7 +5478,7 @@ jQuery.fn.extend({ closest: function( selectors, context ) { var ret = [], i, l, cur = this[0]; - + // Array (deprecated as of jQuery 1.7) if ( jQuery.isArray( selectors ) ) { var level = 1; @@ -5505,7 +5597,7 @@ jQuery.each({ return jQuery.dir( elem, "previousSibling", until ); }, siblings: function( elem ) { - return jQuery.sibling( elem.parentNode.firstChild, elem ); + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); }, children: function( elem ) { return jQuery.sibling( elem.firstChild ); @@ -5639,7 +5731,7 @@ function createSafeFragment( document ) { return safeFrag; } -var nodeNames = "abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|" + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, rleadingWhitespace = /^\s+/, @@ -5649,7 +5741,7 @@ var nodeNames = "abbr|article|aside|audio|canvas|datalist|details|figcaption|fig rhtml = /<|&#?\w+;/, rnoInnerhtml = /<(?:script|style)/i, rnocache = /<(?:script|object|embed|option|style)/i, - rnoshimcache = new RegExp("<(?:" + nodeNames + ")", "i"), + rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), // checked="checked" or checked rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, rscriptType = /\/(java|ecma)script/i, @@ -5676,20 +5768,12 @@ if ( !jQuery.support.htmlSerialize ) { } jQuery.fn.extend({ - text: function( text ) { - if ( jQuery.isFunction(text) ) { - return this.each(function(i) { - var self = jQuery( this ); - - self.text( text.call(this, i, self.text()) ); - }); - } - - if ( typeof text !== "object" && text !== undefined ) { - return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); - } - - return jQuery.text( this ); + text: function( value ) { + return jQuery.access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); + }, null, value, arguments.length ); }, wrapAll: function( html ) { @@ -5841,44 +5925,44 @@ jQuery.fn.extend({ }, html: function( value ) { - if ( value === undefined ) { - return this[0] && this[0].nodeType === 1 ? - this[0].innerHTML.replace(rinlinejQuery, "") : - null; + return jQuery.access( this, function( value ) { + var elem = this[0] || {}, + i = 0, + l = this.length; - // See if we can take a shortcut and just use innerHTML - } else if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) && - !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) { - - value = value.replace(rxhtmlTag, "<$1>"); - - try { - for ( var i = 0, l = this.length; i < l; i++ ) { - // Remove element nodes and prevent memory leaks - if ( this[i].nodeType === 1 ) { - jQuery.cleanData( this[i].getElementsByTagName("*") ); - this[i].innerHTML = value; - } - } - - // If using innerHTML throws an exception, use the fallback method - } catch(e) { - this.empty().append( value ); + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + null; } - } else if ( jQuery.isFunction( value ) ) { - this.each(function(i){ - var self = jQuery( this ); - self.html( value.call(this, i, self.html()) ); - }); + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) { - } else { - this.empty().append( value ); - } + value = value.replace( rxhtmlTag, "<$1>" ); - return this; + try { + for (; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + elem = this[i] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName( "*" ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch(e) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); }, replaceWith: function( value ) { @@ -5981,7 +6065,23 @@ jQuery.fn.extend({ } if ( scripts.length ) { - jQuery.each( scripts, evalScript ); + jQuery.each( scripts, function( i, elem ) { + if ( elem.src ) { + jQuery.ajax({ + type: "GET", + global: false, + url: elem.src, + async: false, + dataType: "script" + }); + } else { + jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "/*$0*/" ) ); + } + + if ( elem.parentNode ) { + elem.parentNode.removeChild( elem ); + } + }); } } @@ -6013,7 +6113,7 @@ function cloneCopyEvent( src, dest ) { for ( type in events ) { for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type + ( events[ type ][ i ].namespace ? "." : "" ) + events[ type ][ i ].namespace, events[ type ][ i ], events[ type ][ i ].data ); + jQuery.event.add( dest, type, events[ type ][ i ] ); } } } @@ -6075,11 +6175,20 @@ function cloneFixAttributes( src, dest ) { // cloning other types of input fields } else if ( nodeName === "input" || nodeName === "textarea" ) { dest.defaultValue = src.defaultValue; + + // IE blanks contents when cloning scripts + } else if ( nodeName === "script" && dest.text !== src.text ) { + dest.text = src.text; } // Event data gets referenced instead of copied if the expando // gets copied too dest.removeAttribute( jQuery.expando ); + + // Clear flags for bubbling special change/submit events, they must + // be reattached when the newly cloned events are first activated + dest.removeAttribute( "_submit_attached" ); + dest.removeAttribute( "_change_attached" ); } jQuery.buildFragment = function( args, nodes, scripts ) { @@ -6204,7 +6313,7 @@ jQuery.extend({ destElements, i, // IE<=8 does not properly clone detached, unknown element nodes - clone = jQuery.support.html5Clone || !rnoshimcache.test( "<" + elem.nodeName ) ? + clone = jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ? elem.cloneNode( true ) : shimCloneNode( elem ); @@ -6254,7 +6363,8 @@ jQuery.extend({ }, clean: function( elems, context, fragment, scripts ) { - var checkScriptType; + var checkScriptType, script, j, + ret = []; context = context || document; @@ -6263,8 +6373,6 @@ jQuery.extend({ context = context.ownerDocument || context[0] && context[0].ownerDocument || document; } - var ret = [], j; - for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { if ( typeof elem === "number" ) { elem += ""; @@ -6286,7 +6394,9 @@ jQuery.extend({ var tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(), wrap = wrapMap[ tag ] || wrapMap._default, depth = wrap[0], - div = context.createElement("div"); + div = context.createElement("div"), + safeChildNodes = safeFragment.childNodes, + remove; // Append wrapper element to unknown element safe doc fragment if ( context === document ) { @@ -6331,6 +6441,21 @@ jQuery.extend({ } elem = div.childNodes; + + // Clear elements from DocumentFragment (safeFragment or otherwise) + // to avoid hoarding elements. Fixes #11356 + if ( div ) { + div.parentNode.removeChild( div ); + + // Guard against -1 index exceptions in FF3.6 + if ( safeChildNodes.length > 0 ) { + remove = safeChildNodes[ safeChildNodes.length - 1 ]; + + if ( remove && remove.parentNode ) { + remove.parentNode.removeChild( remove ); + } + } + } } } @@ -6359,16 +6484,17 @@ jQuery.extend({ return !elem.type || rscriptType.test( elem.type ); }; for ( i = 0; ret[i]; i++ ) { - if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) { - scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] ); + script = ret[i]; + if ( scripts && jQuery.nodeName( script, "script" ) && (!script.type || rscriptType.test( script.type )) ) { + scripts.push( script.parentNode ? script.parentNode.removeChild( script ) : script ); } else { - if ( ret[i].nodeType === 1 ) { - var jsTags = jQuery.grep( ret[i].getElementsByTagName( "script" ), checkScriptType ); + if ( script.nodeType === 1 ) { + var jsTags = jQuery.grep( script.getElementsByTagName( "script" ), checkScriptType ); ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) ); } - fragment.appendChild( ret[i] ); + fragment.appendChild( script ); } } } @@ -6422,22 +6548,6 @@ jQuery.extend({ } }); -function evalScript( i, elem ) { - if ( elem.src ) { - jQuery.ajax({ - url: elem.src, - async: false, - dataType: "script" - }); - } else { - jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "/*$0*/" ) ); - } - - if ( elem.parentNode ) { - elem.parentNode.removeChild( elem ); - } -} - @@ -6445,29 +6555,27 @@ var ralpha = /alpha\([^)]*\)/i, ropacity = /opacity=([^)]*)/, // fixed for IE9, see #8346 rupper = /([A-Z]|^ms)/g, - rnumpx = /^-?\d+(?:px)?$/i, - rnum = /^-?\d/, + rnum = /^[\-+]?(?:\d*\.)?\d+$/i, + rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i, rrelNum = /^([\-+])=([\-+.\de]+)/, + rmargin = /^margin/, cssShow = { position: "absolute", visibility: "hidden", display: "block" }, - cssWidth = [ "Left", "Right" ], - cssHeight = [ "Top", "Bottom" ], + + // order is important! + cssExpand = [ "Top", "Right", "Bottom", "Left" ], + curCSS, getComputedStyle, currentStyle; jQuery.fn.css = function( name, value ) { - // Setting 'undefined' is a no-op - if ( arguments.length === 2 && value === undefined ) { - return this; - } - - return jQuery.access( this, name, value, true, function( elem, name, value ) { + return jQuery.access( this, function( elem, name, value ) { return value !== undefined ? jQuery.style( elem, name, value ) : jQuery.css( elem, name ); - }); + }, name, value, arguments.length > 1 ); }; jQuery.extend({ @@ -6478,7 +6586,7 @@ jQuery.extend({ get: function( elem, computed ) { if ( computed ) { // We should always get a number back from opacity - var ret = curCSS( elem, "opacity", "opacity" ); + var ret = curCSS( elem, "opacity" ); return ret === "" ? "1" : ret; } else { @@ -6586,56 +6694,174 @@ jQuery.extend({ // A method for quickly swapping in/out CSS properties to get correct calculations swap: function( elem, options, callback ) { - var old = {}; + var old = {}, + ret, name; // Remember the old values, and insert the new ones - for ( var name in options ) { + for ( name in options ) { old[ name ] = elem.style[ name ]; elem.style[ name ] = options[ name ]; } - callback.call( elem ); + ret = callback.call( elem ); // Revert the old values for ( name in options ) { elem.style[ name ] = old[ name ]; } + + return ret; } }); -// DEPRECATED, Use jQuery.css() instead +// DEPRECATED in 1.3, Use jQuery.css() instead jQuery.curCSS = jQuery.css; -jQuery.each(["height", "width"], function( i, name ) { +if ( document.defaultView && document.defaultView.getComputedStyle ) { + getComputedStyle = function( elem, name ) { + var ret, defaultView, computedStyle, width, + style = elem.style; + + name = name.replace( rupper, "-$1" ).toLowerCase(); + + if ( (defaultView = elem.ownerDocument.defaultView) && + (computedStyle = defaultView.getComputedStyle( elem, null )) ) { + + ret = computedStyle.getPropertyValue( name ); + if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) { + ret = jQuery.style( elem, name ); + } + } + + // A tribute to the "awesome hack by Dean Edwards" + // WebKit uses "computed value (percentage if specified)" instead of "used value" for margins + // which is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values + if ( !jQuery.support.pixelMargin && computedStyle && rmargin.test( name ) && rnumnonpx.test( ret ) ) { + width = style.width; + style.width = ret; + ret = computedStyle.width; + style.width = width; + } + + return ret; + }; +} + +if ( document.documentElement.currentStyle ) { + currentStyle = function( elem, name ) { + var left, rsLeft, uncomputed, + ret = elem.currentStyle && elem.currentStyle[ name ], + style = elem.style; + + // Avoid setting ret to empty string here + // so we don't default to auto + if ( ret == null && style && (uncomputed = style[ name ]) ) { + ret = uncomputed; + } + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + if ( rnumnonpx.test( ret ) ) { + + // Remember the original values + left = style.left; + rsLeft = elem.runtimeStyle && elem.runtimeStyle.left; + + // Put in the new values to get a computed value out + if ( rsLeft ) { + elem.runtimeStyle.left = elem.currentStyle.left; + } + style.left = name === "fontSize" ? "1em" : ret; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + if ( rsLeft ) { + elem.runtimeStyle.left = rsLeft; + } + } + + return ret === "" ? "auto" : ret; + }; +} + +curCSS = getComputedStyle || currentStyle; + +function getWidthOrHeight( elem, name, extra ) { + + // Start with offset property + var val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + i = name === "width" ? 1 : 0, + len = 4; + + if ( val > 0 ) { + if ( extra !== "border" ) { + for ( ; i < len; i += 2 ) { + if ( !extra ) { + val -= parseFloat( jQuery.css( elem, "padding" + cssExpand[ i ] ) ) || 0; + } + if ( extra === "margin" ) { + val += parseFloat( jQuery.css( elem, extra + cssExpand[ i ] ) ) || 0; + } else { + val -= parseFloat( jQuery.css( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0; + } + } + } + + return val + "px"; + } + + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name ); + if ( val < 0 || val == null ) { + val = elem.style[ name ]; + } + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test(val) ) { + return val; + } + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; + + // Add padding, border, margin + if ( extra ) { + for ( ; i < len; i += 2 ) { + val += parseFloat( jQuery.css( elem, "padding" + cssExpand[ i ] ) ) || 0; + if ( extra !== "padding" ) { + val += parseFloat( jQuery.css( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0; + } + if ( extra === "margin" ) { + val += parseFloat( jQuery.css( elem, extra + cssExpand[ i ]) ) || 0; + } + } + } + + return val + "px"; +} + +jQuery.each([ "height", "width" ], function( i, name ) { jQuery.cssHooks[ name ] = { get: function( elem, computed, extra ) { - var val; - if ( computed ) { if ( elem.offsetWidth !== 0 ) { - return getWH( elem, name, extra ); + return getWidthOrHeight( elem, name, extra ); } else { - jQuery.swap( elem, cssShow, function() { - val = getWH( elem, name, extra ); + return jQuery.swap( elem, cssShow, function() { + return getWidthOrHeight( elem, name, extra ); }); } - - return val; } }, set: function( elem, value ) { - if ( rnumpx.test( value ) ) { - // ignore negative width and height values #1599 - value = parseFloat( value ); - - if ( value >= 0 ) { - return value + "px"; - } - - } else { - return value; - } + return rnum.test( value ) ? + value + "px" : + value; } }; }); @@ -6689,130 +6915,18 @@ jQuery(function() { get: function( elem, computed ) { // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right // Work around by temporarily setting element display to inline-block - var ret; - jQuery.swap( elem, { "display": "inline-block" }, function() { + return jQuery.swap( elem, { "display": "inline-block" }, function() { if ( computed ) { - ret = curCSS( elem, "margin-right", "marginRight" ); + return curCSS( elem, "margin-right" ); } else { - ret = elem.style.marginRight; + return elem.style.marginRight; } }); - return ret; } }; } }); -if ( document.defaultView && document.defaultView.getComputedStyle ) { - getComputedStyle = function( elem, name ) { - var ret, defaultView, computedStyle; - - name = name.replace( rupper, "-$1" ).toLowerCase(); - - if ( (defaultView = elem.ownerDocument.defaultView) && - (computedStyle = defaultView.getComputedStyle( elem, null )) ) { - ret = computedStyle.getPropertyValue( name ); - if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) { - ret = jQuery.style( elem, name ); - } - } - - return ret; - }; -} - -if ( document.documentElement.currentStyle ) { - currentStyle = function( elem, name ) { - var left, rsLeft, uncomputed, - ret = elem.currentStyle && elem.currentStyle[ name ], - style = elem.style; - - // Avoid setting ret to empty string here - // so we don't default to auto - if ( ret === null && style && (uncomputed = style[ name ]) ) { - ret = uncomputed; - } - - // From the awesome hack by Dean Edwards - // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 - - // If we're not dealing with a regular pixel number - // but a number that has a weird ending, we need to convert it to pixels - if ( !rnumpx.test( ret ) && rnum.test( ret ) ) { - - // Remember the original values - left = style.left; - rsLeft = elem.runtimeStyle && elem.runtimeStyle.left; - - // Put in the new values to get a computed value out - if ( rsLeft ) { - elem.runtimeStyle.left = elem.currentStyle.left; - } - style.left = name === "fontSize" ? "1em" : ( ret || 0 ); - ret = style.pixelLeft + "px"; - - // Revert the changed values - style.left = left; - if ( rsLeft ) { - elem.runtimeStyle.left = rsLeft; - } - } - - return ret === "" ? "auto" : ret; - }; -} - -curCSS = getComputedStyle || currentStyle; - -function getWH( elem, name, extra ) { - - // Start with offset property - var val = name === "width" ? elem.offsetWidth : elem.offsetHeight, - which = name === "width" ? cssWidth : cssHeight, - i = 0, - len = which.length; - - if ( val > 0 ) { - if ( extra !== "border" ) { - for ( ; i < len; i++ ) { - if ( !extra ) { - val -= parseFloat( jQuery.css( elem, "padding" + which[ i ] ) ) || 0; - } - if ( extra === "margin" ) { - val += parseFloat( jQuery.css( elem, extra + which[ i ] ) ) || 0; - } else { - val -= parseFloat( jQuery.css( elem, "border" + which[ i ] + "Width" ) ) || 0; - } - } - } - - return val + "px"; - } - - // Fall back to computed then uncomputed css if necessary - val = curCSS( elem, name, name ); - if ( val < 0 || val == null ) { - val = elem.style[ name ] || 0; - } - // Normalize "", auto, and prepare for extra - val = parseFloat( val ) || 0; - - // Add padding, border, margin - if ( extra ) { - for ( ; i < len; i++ ) { - val += parseFloat( jQuery.css( elem, "padding" + which[ i ] ) ) || 0; - if ( extra !== "padding" ) { - val += parseFloat( jQuery.css( elem, "border" + which[ i ] + "Width" ) ) || 0; - } - if ( extra === "margin" ) { - val += parseFloat( jQuery.css( elem, extra + which[ i ] ) ) || 0; - } - } - } - - return val + "px"; -} - if ( jQuery.expr && jQuery.expr.filters ) { jQuery.expr.filters.hidden = function( elem ) { var width = elem.offsetWidth, @@ -6826,6 +6940,31 @@ if ( jQuery.expr && jQuery.expr.filters ) { }; } +// These hooks are used by animate to expand properties +jQuery.each({ + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i, + + // assumes a single number if not a string + parts = typeof value === "string" ? value.split(" ") : [ value ], + expanded = {}; + + for ( i = 0; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; +}); + @@ -7144,7 +7283,7 @@ jQuery.extend({ isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ), global: true, type: "GET", - contentType: "application/x-www-form-urlencoded", + contentType: "application/x-www-form-urlencoded; charset=UTF-8", processData: true, async: true, /* @@ -7470,7 +7609,7 @@ jQuery.extend({ // Apply prefilters inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); - // If request was aborted inside a prefiler, stop there + // If request was aborted inside a prefilter, stop there if ( state === 2 ) { return false; } @@ -7643,11 +7782,11 @@ function buildParams( prefix, obj, traditional, add ) { // a server error. Possible fixes are to modify rack's // deserialization algorithm or to provide an option or flag // to force array serialization to be shallow. - buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v, traditional, add ); + buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add ); } }); - } else if ( !traditional && obj != null && typeof obj === "object" ) { + } else if ( !traditional && jQuery.type( obj ) === "object" ) { // Serialize object item. for ( var name in obj ) { buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); @@ -7843,8 +7982,7 @@ jQuery.ajaxSetup({ // Detect, normalize options and install callbacks for jsonp requests jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { - var inspectData = s.contentType === "application/x-www-form-urlencoded" && - ( typeof s.data === "string" ); + var inspectData = ( typeof s.data === "string" ) && /^application\/x\-www\-form\-urlencoded/.test( s.contentType ); if ( s.dataTypes[ 0 ] === "jsonp" || s.jsonp !== false && ( jsre.test( s.url ) || @@ -8145,7 +8283,13 @@ if ( jQuery.support.ajax ) { if ( xml && xml.documentElement /* #4958 */ ) { responses.xml = xml; } - responses.text = xhr.responseText; + + // When requesting binary data, IE6-9 will throw an exception + // on any attempt to access responseText (#11426) + try { + responses.text = xhr.responseText; + } catch( _ ) { + } // Firefox throws an exception when accessing // statusText for faulty cross-domain requests @@ -8253,7 +8397,8 @@ jQuery.fn.extend({ // Set elements which have been overridden with display: none // in a stylesheet to whatever the default browser style is // for such an element - if ( display === "" && jQuery.css(elem, "display") === "none" ) { + if ( (display === "" && jQuery.css(elem, "display") === "none") || + !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) { jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) ); } } @@ -8357,24 +8502,37 @@ jQuery.fn.extend({ var opt = jQuery.extend( {}, optall ), isElement = this.nodeType === 1, hidden = isElement && jQuery(this).is(":hidden"), - name, val, p, e, + name, val, p, e, hooks, replace, parts, start, end, unit, method; // will store per property easing and be used to determine when an animation is complete opt.animatedProperties = {}; + // first pass over propertys to expand / normalize for ( p in prop ) { - - // property name normalization name = jQuery.camelCase( p ); if ( p !== name ) { prop[ name ] = prop[ p ]; delete prop[ p ]; } - val = prop[ name ]; + if ( ( hooks = jQuery.cssHooks[ name ] ) && "expand" in hooks ) { + replace = hooks.expand( prop[ name ] ); + delete prop[ name ]; + // not quite $.extend, this wont overwrite keys already present. + // also - reusing 'p' from above because we have the correct "name" + for ( p in replace ) { + if ( ! ( p in prop ) ) { + prop[ p ] = replace[ p ]; + } + } + } + } + + for ( name in prop ) { + val = prop[ name ]; // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default) if ( jQuery.isArray( val ) ) { opt.animatedProperties[ name ] = val[ 1 ]; @@ -8601,11 +8759,11 @@ jQuery.extend({ }, easing: { - linear: function( p, n, firstNum, diff ) { - return firstNum + diff * p; + linear: function( p ) { + return p; }, - swing: function( p, n, firstNum, diff ) { - return ( ( -Math.cos( p*Math.PI ) / 2 ) + 0.5 ) * diff + firstNum; + swing: function( p ) { + return ( -Math.cos( p*Math.PI ) / 2 ) + 0.5; } }, @@ -8663,8 +8821,12 @@ jQuery.fx.prototype = { t.queue = this.options.queue; t.elem = this.elem; t.saveState = function() { - if ( self.options.hide && jQuery._data( self.elem, "fxshow" + self.prop ) === undefined ) { - jQuery._data( self.elem, "fxshow" + self.prop, self.start ); + if ( jQuery._data( self.elem, "fxshow" + self.prop ) === undefined ) { + if ( self.options.hide ) { + jQuery._data( self.elem, "fxshow" + self.prop, self.start ); + } else if ( self.options.show ) { + jQuery._data( self.elem, "fxshow" + self.prop, self.end ); + } } }; @@ -8831,12 +8993,14 @@ jQuery.extend( jQuery.fx, { } }); -// Adds width/height step functions -// Do not set anything below 0 -jQuery.each([ "width", "height" ], function( i, prop ) { - jQuery.fx.step[ prop ] = function( fx ) { - jQuery.style( fx.elem, prop, Math.max(0, fx.now) + fx.unit ); - }; +// Ensure props that can't be negative don't go there on undershoot easing +jQuery.each( fxAttrs.concat.apply( [], fxAttrs ), function( i, prop ) { + // exclude marginTop, marginLeft, marginBottom and marginRight from this list + if ( prop.indexOf( "margin" ) ) { + jQuery.fx.step[ prop ] = function( fx ) { + jQuery.style( fx.elem, prop, Math.max(0, fx.now) + fx.unit ); + }; + } }); if ( jQuery.expr && jQuery.expr.filters ) { @@ -8873,7 +9037,7 @@ function defaultDisplay( nodeName ) { // document to it; WebKit & Firefox won't allow reusing the iframe document. if ( !iframeDoc || !iframe.createElement ) { iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document; - iframeDoc.write( ( document.compatMode === "CSS1Compat" ? "" : "" ) + "" ); + iframeDoc.write( ( jQuery.support.boxModel ? "" : "" ) + "" ); iframeDoc.close(); } @@ -8895,41 +9059,23 @@ function defaultDisplay( nodeName ) { -var rtable = /^t(?:able|d|h)$/i, +var getOffset, + rtable = /^t(?:able|d|h)$/i, rroot = /^(?:body|html)$/i; if ( "getBoundingClientRect" in document.documentElement ) { - jQuery.fn.offset = function( options ) { - var elem = this[0], box; - - if ( options ) { - return this.each(function( i ) { - jQuery.offset.setOffset( this, options, i ); - }); - } - - if ( !elem || !elem.ownerDocument ) { - return null; - } - - if ( elem === elem.ownerDocument.body ) { - return jQuery.offset.bodyOffset( elem ); - } - + getOffset = function( elem, doc, docElem, box ) { try { box = elem.getBoundingClientRect(); } catch(e) {} - var doc = elem.ownerDocument, - docElem = doc.documentElement; - // Make sure we're not dealing with a disconnected DOM node if ( !box || !jQuery.contains( docElem, elem ) ) { return box ? { top: box.top, left: box.left } : { top: 0, left: 0 }; } var body = doc.body, - win = getWindow(doc), + win = getWindow( doc ), clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0, scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop, @@ -8941,28 +9087,10 @@ if ( "getBoundingClientRect" in document.documentElement ) { }; } else { - jQuery.fn.offset = function( options ) { - var elem = this[0]; - - if ( options ) { - return this.each(function( i ) { - jQuery.offset.setOffset( this, options, i ); - }); - } - - if ( !elem || !elem.ownerDocument ) { - return null; - } - - if ( elem === elem.ownerDocument.body ) { - return jQuery.offset.bodyOffset( elem ); - } - + getOffset = function( elem, doc, docElem ) { var computedStyle, offsetParent = elem.offsetParent, prevOffsetParent = elem, - doc = elem.ownerDocument, - docElem = doc.documentElement, body = doc.body, defaultView = doc.defaultView, prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle, @@ -9013,6 +9141,29 @@ if ( "getBoundingClientRect" in document.documentElement ) { }; } +jQuery.fn.offset = function( options ) { + if ( arguments.length ) { + return options === undefined ? + this : + this.each(function( i ) { + jQuery.offset.setOffset( this, options, i ); + }); + } + + var elem = this[0], + doc = elem && elem.ownerDocument; + + if ( !doc ) { + return null; + } + + if ( elem === doc.body ) { + return jQuery.offset.bodyOffset( elem ); + } + + return getOffset( elem, doc, doc.documentElement ); +}; + jQuery.offset = { bodyOffset: function( body ) { @@ -9118,42 +9269,30 @@ jQuery.fn.extend({ // Create scrollLeft and scrollTop methods -jQuery.each( ["Left", "Top"], function( i, name ) { - var method = "scroll" + name; +jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) { + var top = /Y/.test( prop ); jQuery.fn[ method ] = function( val ) { - var elem, win; + return jQuery.access( this, function( elem, method, val ) { + var win = getWindow( elem ); - if ( val === undefined ) { - elem = this[ 0 ]; - - if ( !elem ) { - return null; + if ( val === undefined ) { + return win ? (prop in win) ? win[ prop ] : + jQuery.support.boxModel && win.document.documentElement[ method ] || + win.document.body[ method ] : + elem[ method ]; } - win = getWindow( elem ); - - // Return the scroll offset - return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] : - jQuery.support.boxModel && win.document.documentElement[ method ] || - win.document.body[ method ] : - elem[ method ]; - } - - // Set the scroll offset - return this.each(function() { - win = getWindow( this ); - if ( win ) { win.scrollTo( - !i ? val : jQuery( win ).scrollLeft(), - i ? val : jQuery( win ).scrollTop() + !top ? val : jQuery( win ).scrollLeft(), + top ? val : jQuery( win ).scrollTop() ); } else { - this[ method ] = val; + elem[ method ] = val; } - }); + }, method, val, arguments.length, null ); }; }); @@ -9169,9 +9308,10 @@ function getWindow( elem ) { // Create width, height, innerHeight, innerWidth, outerHeight and outerWidth methods -jQuery.each([ "Height", "Width" ], function( i, name ) { - - var type = name.toLowerCase(); +jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { + var clientProp = "client" + name, + scrollProp = "scroll" + name, + offsetProp = "offset" + name; // innerHeight and innerWidth jQuery.fn[ "inner" + name ] = function() { @@ -9193,50 +9333,48 @@ jQuery.each([ "Height", "Width" ], function( i, name ) { null; }; - jQuery.fn[ type ] = function( size ) { - // Get window width or height - var elem = this[0]; - if ( !elem ) { - return size == null ? null : this; - } + jQuery.fn[ type ] = function( value ) { + return jQuery.access( this, function( elem, type, value ) { + var doc, docElemProp, orig, ret; - if ( jQuery.isFunction( size ) ) { - return this.each(function( i ) { - var self = jQuery( this ); - self[ type ]( size.call( this, i, self[ type ]() ) ); - }); - } + if ( jQuery.isWindow( elem ) ) { + // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat + doc = elem.document; + docElemProp = doc.documentElement[ clientProp ]; + return jQuery.support.boxModel && docElemProp || + doc.body && doc.body[ clientProp ] || docElemProp; + } - if ( jQuery.isWindow( elem ) ) { - // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode - // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat - var docElemProp = elem.document.documentElement[ "client" + name ], - body = elem.document.body; - return elem.document.compatMode === "CSS1Compat" && docElemProp || - body && body[ "client" + name ] || docElemProp; + // Get document width or height + if ( elem.nodeType === 9 ) { + // Either scroll[Width/Height] or offset[Width/Height], whichever is greater + doc = elem.documentElement; - // Get document width or height - } else if ( elem.nodeType === 9 ) { - // Either scroll[Width/Height] or offset[Width/Height], whichever is greater - return Math.max( - elem.documentElement["client" + name], - elem.body["scroll" + name], elem.documentElement["scroll" + name], - elem.body["offset" + name], elem.documentElement["offset" + name] - ); + // when a window > document, IE6 reports a offset[Width/Height] > client[Width/Height] + // so we can't use max, as it'll choose the incorrect offset[Width/Height] + // instead we use the correct client[Width/Height] + // support:IE6 + if ( doc[ clientProp ] >= doc[ scrollProp ] ) { + return doc[ clientProp ]; + } - // Get or set width or height on the element - } else if ( size === undefined ) { - var orig = jQuery.css( elem, type ), + return Math.max( + elem.body[ scrollProp ], doc[ scrollProp ], + elem.body[ offsetProp ], doc[ offsetProp ] + ); + } + + // Get width or height on the element + if ( value === undefined ) { + orig = jQuery.css( elem, type ); ret = parseFloat( orig ); + return jQuery.isNumeric( ret ) ? ret : orig; + } - return jQuery.isNumeric( ret ) ? ret : orig; - - // Set the width or height on the element (default to pixels if value is unitless) - } else { - return this.css( type, typeof size === "string" ? size : size + "px" ); - } + // Set the width or height on the element + jQuery( elem ).css( type, value ); + }, type, value, arguments.length, null ); }; - }); diff --git a/sites/all/modules/contrib/js/jquery_update/replace/jquery/1.7/jquery.min.js b/sites/all/modules/contrib/js/jquery_update/replace/jquery/1.7/jquery.min.js index 198b3ff..cf22687 100644 --- a/sites/all/modules/contrib/js/jquery_update/replace/jquery/1.7/jquery.min.js +++ b/sites/all/modules/contrib/js/jquery_update/replace/jquery/1.7/jquery.min.js @@ -1,4 +1,4 @@ -/*! jQuery v1.7.1 jquery.com | jquery.org/license */ -(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"":"")+""),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;g=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
    a",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="
    "+""+"
    ",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="
    t
    ",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="
    ",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")}; -f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&i.push({elem:this,matches:d.slice(e)});for(j=0;j0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

    ";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
    ";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/",""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
    ","
    "]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function() -{for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
    ").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file +/*! jQuery v1.7.2 jquery.com | jquery.org/license */ +(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"":"")+""),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;e=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
    a",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="
    "+""+"
    ",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="
    t
    ",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="
    ",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function( +a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&j.push({elem:this,matches:d.slice(e)});for(k=0;k0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

    ";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
    ";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*",""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
    ","
    "]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f +.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(;d1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]===""&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
    ").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file diff --git a/sites/all/modules/contrib/js/jquery_update/replace/jquery/1.8/jquery.js b/sites/all/modules/contrib/js/jquery_update/replace/jquery/1.8/jquery.js index d4f3bb3..8c24ffc 100644 --- a/sites/all/modules/contrib/js/jquery_update/replace/jquery/1.8/jquery.js +++ b/sites/all/modules/contrib/js/jquery_update/replace/jquery/1.8/jquery.js @@ -1,5 +1,5 @@ /*! - * jQuery JavaScript Library v1.8.2 + * jQuery JavaScript Library v1.8.3 * http://jquery.com/ * * Includes Sizzle.js @@ -9,7 +9,7 @@ * Released under the MIT license * http://jquery.org/license * - * Date: Thu Sep 20 2012 21:13:05 GMT-0400 (Eastern Daylight Time) + * Date: Tue Nov 13 2012 08:20:33 GMT-0500 (Eastern Standard Time) */ (function( window, undefined ) { var @@ -186,7 +186,7 @@ jQuery.fn = jQuery.prototype = { selector: "", // The current version of jQuery being used - jquery: "1.8.2", + jquery: "1.8.3", // The default length of a jQuery object is 0 length: 0, @@ -999,8 +999,10 @@ jQuery.Callbacks = function( options ) { (function add( args ) { jQuery.each( args, function( _, arg ) { var type = jQuery.type( arg ); - if ( type === "function" && ( !options.unique || !self.has( arg ) ) ) { - list.push( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } } else if ( arg && arg.length && type !== "string" ) { // Inspect recursively add( arg ); @@ -1253,24 +1255,23 @@ jQuery.support = (function() { clickFn, div = document.createElement("div"); - // Preliminary tests + // Setup div.setAttribute( "className", "t" ); div.innerHTML = "
    a"; + // Support tests won't run in some limited or non-browser environments all = div.getElementsByTagName("*"); a = div.getElementsByTagName("a")[ 0 ]; - a.style.cssText = "top:1px;float:left;opacity:.5"; - - // Can't get basic test support - if ( !all || !all.length ) { + if ( !all || !a || !all.length ) { return {}; } - // First batch of supports tests + // First batch of tests select = document.createElement("select"); opt = select.appendChild( document.createElement("option") ); input = div.getElementsByTagName("input")[ 0 ]; + a.style.cssText = "top:1px;float:left;opacity:.5"; support = { // IE strips leading whitespace when .innerHTML is used leadingWhitespace: ( div.firstChild.nodeType === 3 ), @@ -1312,7 +1313,7 @@ jQuery.support = (function() { // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) getSetAttribute: div.className !== "t", - // Tests for enctype support on a form(#6743) + // Tests for enctype support on a form (#6743) enctype: !!document.createElement("form").enctype, // Makes sure cloning an html5 element does not cause problems @@ -2217,26 +2218,25 @@ jQuery.extend({ }, select: { get: function( elem ) { - var value, i, max, option, - index = elem.selectedIndex, - values = [], + var value, option, options = elem.options, - one = elem.type === "select-one"; - - // Nothing was selected - if ( index < 0 ) { - return null; - } + index = elem.selectedIndex, + one = elem.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; // Loop through all the selected options - i = one ? index : 0; - max = one ? index + 1 : options.length; for ( ; i < max; i++ ) { option = options[ i ]; - // Don't return options that are disabled or in a disabled optgroup - if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && - (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { + // oldIE doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + // Don't return options that are disabled or in a disabled optgroup + ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && + ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { // Get the specific value for the option value = jQuery( option ).val(); @@ -2251,11 +2251,6 @@ jQuery.extend({ } } - // Fixes Bug #2551 -- select.val() broken in IE after form.reset() - if ( one && !values.length && options.length ) { - return jQuery( options[ index ] ).val(); - } - return values; }, @@ -3233,7 +3228,7 @@ jQuery.removeEvent = document.removeEventListener ? if ( elem.detachEvent ) { - // #8545, #7054, preventing memory leaks for custom events in IE6-8 – + // #8545, #7054, preventing memory leaks for custom events in IE6-8 // detachEvent needed property on element, by name of that event, to properly expose it to GC if ( typeof elem[ name ] === "undefined" ) { elem[ name ] = null; @@ -3665,1667 +3660,1685 @@ jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblcl jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; } }); -/*! - * Sizzle CSS Selector Engine - * Copyright 2012 jQuery Foundation and other contributors - * Released under the MIT license - * http://sizzlejs.com/ - */ -(function( window, undefined ) { - -var cachedruns, - assertGetIdNotName, - Expr, - getText, - isXML, - contains, - compile, - sortOrder, - hasDuplicate, - outermostContext, - - baseHasDuplicate = true, - strundefined = "undefined", - - expando = ( "sizcache" + Math.random() ).replace( ".", "" ), - - Token = String, - document = window.document, - docElem = document.documentElement, - dirruns = 0, - done = 0, - pop = [].pop, - push = [].push, - slice = [].slice, - // Use a stripped-down indexOf if a native one is unavailable - indexOf = [].indexOf || function( elem ) { - var i = 0, - len = this.length; - for ( ; i < len; i++ ) { - if ( this[i] === elem ) { - return i; - } - } - return -1; - }, - - // Augment a function for special use by Sizzle - markFunction = function( fn, value ) { - fn[ expando ] = value == null || value; - return fn; - }, - - createCache = function() { - var cache = {}, - keys = []; - - return markFunction(function( key, value ) { - // Only keep the most recent entries - if ( keys.push( key ) > Expr.cacheLength ) { - delete cache[ keys.shift() ]; - } - - return (cache[ key ] = value); - }, cache ); - }, - - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - - // Regex - - // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - // http://www.w3.org/TR/css3-syntax/#characters - characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+", - - // Loosely modeled on CSS identifier characters - // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors) - // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = characterEncoding.replace( "w", "w#" ), - - // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors - operators = "([*^$|!~]?=)", - attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + - "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", - - // Prefer arguments not in parens/brackets, - // then attribute selectors and non-pseudos (denoted by :), - // then anything else - // These preferences are here to reduce the number of selectors - // needing tokenize in the PSEUDO preFilter - pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)", - - // For matchExpr.POS and matchExpr.needsContext - pos = ":(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + - "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), - - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ), - rpseudo = new RegExp( pseudos ), - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/, - - rnot = /^:not/, - rsibling = /[\x20\t\r\n\f]*[+~]/, - rendsWithNot = /:not\($/, - - rheader = /h\d/i, - rinputs = /input|select|textarea|button/i, - - rbackslash = /\\(?!\\)/g, - - matchExpr = { - "ID": new RegExp( "^#(" + characterEncoding + ")" ), - "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), - "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), - "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "POS": new RegExp( pos, "i" ), - "CHILD": new RegExp( "^:(only|nth|first|last)-child(?:\\(" + whitespace + - "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + - "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - // For use in libraries implementing .is() - "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" ) - }, - - // Support - - // Used for testing something on an element - assert = function( fn ) { - var div = document.createElement("div"); - - try { - return fn( div ); - } catch (e) { - return false; - } finally { - // release memory in IE - div = null; - } - }, - - // Check if getElementsByTagName("*") returns only elements - assertTagNameNoComments = assert(function( div ) { - div.appendChild( document.createComment("") ); - return !div.getElementsByTagName("*").length; - }), - - // Check if getAttribute returns normalized href attributes - assertHrefNotNormalized = assert(function( div ) { - div.innerHTML = ""; - return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && - div.firstChild.getAttribute("href") === "#"; - }), - - // Check if attributes should be retrieved by attribute nodes - assertAttributes = assert(function( div ) { - div.innerHTML = ""; - var type = typeof div.lastChild.getAttribute("multiple"); - // IE8 returns a string for some attributes even when not present - return type !== "boolean" && type !== "string"; - }), - - // Check if getElementsByClassName can be trusted - assertUsableClassName = assert(function( div ) { - // Opera can't find a second classname (in 9.6) - div.innerHTML = ""; - if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) { - return false; - } - - // Safari 3.2 caches class attributes and doesn't catch changes - div.lastChild.className = "e"; - return div.getElementsByClassName("e").length === 2; - }), - - // Check if getElementById returns elements by name - // Check if getElementsByName privileges form controls or returns elements by ID - assertUsableName = assert(function( div ) { - // Inject content - div.id = expando + 0; - div.innerHTML = "
    "; - docElem.insertBefore( div, docElem.firstChild ); - - // Test - var pass = document.getElementsByName && - // buggy browsers will return fewer than the correct 2 - document.getElementsByName( expando ).length === 2 + - // buggy browsers will return more than the correct 0 - document.getElementsByName( expando + 0 ).length; - assertGetIdNotName = !document.getElementById( expando ); - - // Cleanup - docElem.removeChild( div ); - - return pass; - }); - -// If slice is not available, provide a backup -try { - slice.call( docElem.childNodes, 0 )[0].nodeType; -} catch ( e ) { - slice = function( i ) { - var elem, - results = []; - for ( ; (elem = this[i]); i++ ) { - results.push( elem ); - } - return results; - }; -} - -function Sizzle( selector, context, results, seed ) { - results = results || []; - context = context || document; - var match, elem, xml, m, - nodeType = context.nodeType; - - if ( !selector || typeof selector !== "string" ) { - return results; - } - - if ( nodeType !== 1 && nodeType !== 9 ) { - return []; - } - - xml = isXML( context ); - - if ( !xml && !seed ) { - if ( (match = rquickExpr.exec( selector )) ) { - // Speed-up: Sizzle("#ID") - if ( (m = match[1]) ) { - if ( nodeType === 9 ) { - elem = context.getElementById( m ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE, Opera, and Webkit return items - // by name instead of ID - if ( elem.id === m ) { - results.push( elem ); - return results; - } - } else { - return results; - } - } else { - // Context is not a document - if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && - contains( context, elem ) && elem.id === m ) { - results.push( elem ); - return results; - } - } - - // Speed-up: Sizzle("TAG") - } else if ( match[2] ) { - push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) ); - return results; - - // Speed-up: Sizzle(".CLASS") - } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) { - push.apply( results, slice.call(context.getElementsByClassName( m ), 0) ); - return results; - } - } - } - - // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed, xml ); -} - -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { - return Sizzle( expr, null, null, [ elem ] ).length > 0; -}; - -// Returns a function to use in pseudos for input types -function createInputPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === type; - }; -} - -// Returns a function to use in pseudos for buttons -function createButtonPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && elem.type === type; - }; -} - -// Returns a function to use in pseudos for positionals -function createPositionalPseudo( fn ) { - return markFunction(function( argument ) { - argument = +argument; - return markFunction(function( seed, matches ) { - var j, - matchIndexes = fn( [], seed.length, argument ), - i = matchIndexes.length; - - // Match elements found at the specified indexes - while ( i-- ) { - if ( seed[ (j = matchIndexes[i]) ] ) { - seed[j] = !(matches[j] = seed[j]); - } - } - }); - }); -} - -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( nodeType ) { - if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - // Use textContent for elements - // innerText usage removed for consistency of new lines (see #11153) - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - // Traverse its children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - // Do not include comment or processing instruction nodes - } else { - - // If no nodeType, this is expected to be an array - for ( ; (node = elem[i]); i++ ) { - // Do not traverse comment nodes - ret += getText( node ); - } - } - return ret; -}; - -isXML = Sizzle.isXML = function( elem ) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = elem && (elem.ownerDocument || elem).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -// Element contains another -contains = Sizzle.contains = docElem.contains ? - function( a, b ) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b && b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) ); - } : - docElem.compareDocumentPosition ? - function( a, b ) { - return b && !!( a.compareDocumentPosition( b ) & 16 ); - } : - function( a, b ) { - while ( (b = b.parentNode) ) { - if ( b === a ) { - return true; - } - } - return false; - }; - -Sizzle.attr = function( elem, name ) { - var val, - xml = isXML( elem ); - - if ( !xml ) { - name = name.toLowerCase(); - } - if ( (val = Expr.attrHandle[ name ]) ) { - return val( elem ); - } - if ( xml || assertAttributes ) { - return elem.getAttribute( name ); - } - val = elem.getAttributeNode( name ); - return val ? - typeof elem[ name ] === "boolean" ? - elem[ name ] ? name : null : - val.specified ? val.value : null : - null; -}; - -Expr = Sizzle.selectors = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - // IE6/7 return a modified href - attrHandle: assertHrefNotNormalized ? - {} : - { - "href": function( elem ) { - return elem.getAttribute( "href", 2 ); - }, - "type": function( elem ) { - return elem.getAttribute("type"); - } - }, - - find: { - "ID": assertGetIdNotName ? - function( id, context, xml ) { - if ( typeof context.getElementById !== strundefined && !xml ) { - var m = context.getElementById( id ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [m] : []; - } - } : - function( id, context, xml ) { - if ( typeof context.getElementById !== strundefined && !xml ) { - var m = context.getElementById( id ); - - return m ? - m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ? - [m] : - undefined : - []; - } - }, - - "TAG": assertTagNameNoComments ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== strundefined ) { - return context.getElementsByTagName( tag ); - } - } : - function( tag, context ) { - var results = context.getElementsByTagName( tag ); - - // Filter out possible comments - if ( tag === "*" ) { - var elem, - tmp = [], - i = 0; - - for ( ; (elem = results[i]); i++ ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - }, - - "NAME": assertUsableName && function( tag, context ) { - if ( typeof context.getElementsByName !== strundefined ) { - return context.getElementsByName( name ); - } - }, - - "CLASS": assertUsableClassName && function( className, context, xml ) { - if ( typeof context.getElementsByClassName !== strundefined && !xml ) { - return context.getElementsByClassName( className ); - } - } - }, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - "ATTR": function( match ) { - match[1] = match[1].replace( rbackslash, "" ); - - // Move the given value to match[3] whether quoted or unquoted - match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" ); - - if ( match[2] === "~=" ) { - match[3] = " " + match[3] + " "; - } - - return match.slice( 0, 4 ); - }, - - "CHILD": function( match ) { - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 3 xn-component of xn+y argument ([+-]?\d*n|) - 4 sign of xn-component - 5 x of xn-component - 6 sign of y-component - 7 y of y-component - */ - match[1] = match[1].toLowerCase(); - - if ( match[1] === "nth" ) { - // nth-child requires argument - if ( !match[2] ) { - Sizzle.error( match[0] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) ); - match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" ); - - // other types prohibit arguments - } else if ( match[2] ) { - Sizzle.error( match[0] ); - } - - return match; - }, - - "PSEUDO": function( match ) { - var unquoted, excess; - if ( matchExpr["CHILD"].test( match[0] ) ) { - return null; - } - - if ( match[3] ) { - match[2] = match[3]; - } else if ( (unquoted = match[4]) ) { - // Only check arguments that contain a pseudo - if ( rpseudo.test(unquoted) && - // Get excess from tokenize (recursively) - (excess = tokenize( unquoted, true )) && - // advance to the next closing parenthesis - (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { - - // excess is a negative index - unquoted = unquoted.slice( 0, excess ); - match[0] = match[0].slice( 0, excess ); - } - match[2] = unquoted; - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, - - filter: { - "ID": assertGetIdNotName ? - function( id ) { - id = id.replace( rbackslash, "" ); - return function( elem ) { - return elem.getAttribute("id") === id; - }; - } : - function( id ) { - id = id.replace( rbackslash, "" ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); - return node && node.value === id; - }; - }, - - "TAG": function( nodeName ) { - if ( nodeName === "*" ) { - return function() { return true; }; - } - nodeName = nodeName.replace( rbackslash, "" ).toLowerCase(); - - return function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function( className ) { - var pattern = classCache[ expando ][ className ]; - if ( !pattern ) { - pattern = classCache( className, new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)") ); - } - return function( elem ) { - return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); - }; - }, - - "ATTR": function( name, operator, check ) { - return function( elem, context ) { - var result = Sizzle.attr( elem, name ); - - if ( result == null ) { - return operator === "!="; - } - if ( !operator ) { - return true; - } - - result += ""; - - return operator === "=" ? result === check : - operator === "!=" ? result !== check : - operator === "^=" ? check && result.indexOf( check ) === 0 : - operator === "*=" ? check && result.indexOf( check ) > -1 : - operator === "$=" ? check && result.substr( result.length - check.length ) === check : - operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : - operator === "|=" ? result === check || result.substr( 0, check.length + 1 ) === check + "-" : - false; - }; - }, - - "CHILD": function( type, argument, first, last ) { - - if ( type === "nth" ) { - return function( elem ) { - var node, diff, - parent = elem.parentNode; - - if ( first === 1 && last === 0 ) { - return true; - } - - if ( parent ) { - diff = 0; - for ( node = parent.firstChild; node; node = node.nextSibling ) { - if ( node.nodeType === 1 ) { - diff++; - if ( elem === node ) { - break; - } - } - } - } - - // Incorporate the offset (or cast to NaN), then check against cycle size - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 0 ); - }; - } - - return function( elem ) { - var node = elem; - - switch ( type ) { - case "only": - case "first": - while ( (node = node.previousSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - - if ( type === "first" ) { - return true; - } - - node = elem; - - /* falls through */ - case "last": - while ( (node = node.nextSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - - return true; - } - }; - }, - - "PSEUDO": function( pseudo, argument ) { - // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - // Remember that setFilters inherits from pseudos - var args, - fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || - Sizzle.error( "unsupported pseudo: " + pseudo ); - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as Sizzle does - if ( fn[ expando ] ) { - return fn( argument ); - } - - // But maintain support for old signatures - if ( fn.length > 1 ) { - args = [ pseudo, pseudo, "", argument ]; - return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? - markFunction(function( seed, matches ) { - var idx, - matched = fn( seed, argument ), - i = matched.length; - while ( i-- ) { - idx = indexOf.call( seed, matched[i] ); - seed[ idx ] = !( matches[ idx ] = matched[i] ); - } - }) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - - return fn; - } - }, - - pseudos: { - "not": markFunction(function( selector ) { - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var input = [], - results = [], - matcher = compile( selector.replace( rtrim, "$1" ) ); - - return matcher[ expando ] ? - markFunction(function( seed, matches, context, xml ) { - var elem, - unmatched = matcher( seed, null, xml, [] ), - i = seed.length; - - // Match elements unmatched by `matcher` - while ( i-- ) { - if ( (elem = unmatched[i]) ) { - seed[i] = !(matches[i] = elem); - } - } - }) : - function( elem, context, xml ) { - input[0] = elem; - matcher( input, null, xml, results ); - return !results.pop(); - }; - }), - - "has": markFunction(function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - }), - - "contains": markFunction(function( text ) { - return function( elem ) { - return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; - }; - }), - - "enabled": function( elem ) { - return elem.disabled === false; - }, - - "disabled": function( elem ) { - return elem.disabled === true; - }, - - "checked": function( elem ) { - // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - var nodeName = elem.nodeName.toLowerCase(); - return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); - }, - - "selected": function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - "parent": function( elem ) { - return !Expr.pseudos["empty"]( elem ); - }, - - "empty": function( elem ) { - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), - // not comment, processing instructions, or others - // Thanks to Diego Perini for the nodeName shortcut - // Greater than "@" means alpha characters (specifically not starting with "#" or "?") - var nodeType; - elem = elem.firstChild; - while ( elem ) { - if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) { - return false; - } - elem = elem.nextSibling; - } - return true; - }, - - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, - - "text": function( elem ) { - var type, attr; - // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) - // use getAttribute instead to test this case - return elem.nodeName.toLowerCase() === "input" && - (type = elem.type) === "text" && - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type ); - }, - - // Input types - "radio": createInputPseudo("radio"), - "checkbox": createInputPseudo("checkbox"), - "file": createInputPseudo("file"), - "password": createInputPseudo("password"), - "image": createInputPseudo("image"), - - "submit": createButtonPseudo("submit"), - "reset": createButtonPseudo("reset"), - - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - "focus": function( elem ) { - var doc = elem.ownerDocument; - return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href); - }, - - "active": function( elem ) { - return elem === elem.ownerDocument.activeElement; - }, - - // Positional types - "first": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ 0 ]; - }), - - "last": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ length - 1 ]; - }), - - "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - }), - - "even": createPositionalPseudo(function( matchIndexes, length, argument ) { - for ( var i = 0; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "odd": createPositionalPseudo(function( matchIndexes, length, argument ) { - for ( var i = 1; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { - for ( var i = argument < 0 ? argument + length : argument; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { - for ( var i = argument < 0 ? argument + length : argument; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }) - } -}; - -function siblingCheck( a, b, ret ) { - if ( a === b ) { - return ret; - } - - var cur = a.nextSibling; - - while ( cur ) { - if ( cur === b ) { - return -1; - } - - cur = cur.nextSibling; - } - - return 1; -} - -sortOrder = docElem.compareDocumentPosition ? - function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - return ( !a.compareDocumentPosition || !b.compareDocumentPosition ? - a.compareDocumentPosition : - a.compareDocumentPosition(b) & 4 - ) ? -1 : 1; - } : - function( a, b ) { - // The nodes are identical, we can exit early - if ( a === b ) { - hasDuplicate = true; - return 0; - - // Fallback to using sourceIndex (in IE) if it's available on both nodes - } else if ( a.sourceIndex && b.sourceIndex ) { - return a.sourceIndex - b.sourceIndex; - } - - var al, bl, - ap = [], - bp = [], - aup = a.parentNode, - bup = b.parentNode, - cur = aup; - - // If the nodes are siblings (or identical) we can do a quick check - if ( aup === bup ) { - return siblingCheck( a, b ); - - // If no parents were found then the nodes are disconnected - } else if ( !aup ) { - return -1; - - } else if ( !bup ) { - return 1; - } - - // Otherwise they're somewhere else in the tree so we need - // to build up a full list of the parentNodes for comparison - while ( cur ) { - ap.unshift( cur ); - cur = cur.parentNode; - } - - cur = bup; - - while ( cur ) { - bp.unshift( cur ); - cur = cur.parentNode; - } - - al = ap.length; - bl = bp.length; - - // Start walking down the tree looking for a discrepancy - for ( var i = 0; i < al && i < bl; i++ ) { - if ( ap[i] !== bp[i] ) { - return siblingCheck( ap[i], bp[i] ); - } - } - - // We ended someplace up the tree so do a sibling check - return i === al ? - siblingCheck( a, bp[i], -1 ) : - siblingCheck( ap[i], b, 1 ); - }; - -// Always assume the presence of duplicates if sort doesn't -// pass them to our comparison function (as in Google Chrome). -[0, 0].sort( sortOrder ); -baseHasDuplicate = !hasDuplicate; - -// Document sorting and removing duplicates -Sizzle.uniqueSort = function( results ) { - var elem, - i = 1; - - hasDuplicate = baseHasDuplicate; - results.sort( sortOrder ); - - if ( hasDuplicate ) { - for ( ; (elem = results[i]); i++ ) { - if ( elem === results[ i - 1 ] ) { - results.splice( i--, 1 ); - } - } - } - - return results; -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -function tokenize( selector, parseOnly ) { - var matched, match, tokens, type, soFar, groups, preFilters, - cached = tokenCache[ expando ][ selector ]; - - if ( cached ) { - return parseOnly ? 0 : cached.slice( 0 ); - } - - soFar = selector; - groups = []; - preFilters = Expr.preFilter; - - while ( soFar ) { - - // Comma and first run - if ( !matched || (match = rcomma.exec( soFar )) ) { - if ( match ) { - soFar = soFar.slice( match[0].length ); - } - groups.push( tokens = [] ); - } - - matched = false; - - // Combinators - if ( (match = rcombinators.exec( soFar )) ) { - tokens.push( matched = new Token( match.shift() ) ); - soFar = soFar.slice( matched.length ); - - // Cast descendant combinators to space - matched.type = match[0].replace( rtrim, " " ); - } - - // Filters - for ( type in Expr.filter ) { - if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || - // The last two arguments here are (context, xml) for backCompat - (match = preFilters[ type ]( match, document, true ))) ) { - - tokens.push( matched = new Token( match.shift() ) ); - soFar = soFar.slice( matched.length ); - matched.type = type; - matched.matches = match; - } - } - - if ( !matched ) { - break; - } - } - - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - return parseOnly ? - soFar.length : - soFar ? - Sizzle.error( selector ) : - // Cache the tokens - tokenCache( selector, groups ).slice( 0 ); -} - -function addCombinator( matcher, combinator, base ) { - var dir = combinator.dir, - checkNonElements = base && combinator.dir === "parentNode", - doneName = done++; - - return combinator.first ? - // Check against closest ancestor/preceding element - function( elem, context, xml ) { - while ( (elem = elem[ dir ]) ) { - if ( checkNonElements || elem.nodeType === 1 ) { - return matcher( elem, context, xml ); - } - } - } : - - // Check against all ancestor/preceding elements - function( elem, context, xml ) { - // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching - if ( !xml ) { - var cache, - dirkey = dirruns + " " + doneName + " ", - cachedkey = dirkey + cachedruns; - while ( (elem = elem[ dir ]) ) { - if ( checkNonElements || elem.nodeType === 1 ) { - if ( (cache = elem[ expando ]) === cachedkey ) { - return elem.sizset; - } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) { - if ( elem.sizset ) { - return elem; - } - } else { - elem[ expando ] = cachedkey; - if ( matcher( elem, context, xml ) ) { - elem.sizset = true; - return elem; - } - elem.sizset = false; - } - } - } - } else { - while ( (elem = elem[ dir ]) ) { - if ( checkNonElements || elem.nodeType === 1 ) { - if ( matcher( elem, context, xml ) ) { - return elem; - } - } - } - } - }; -} - -function elementMatcher( matchers ) { - return matchers.length > 1 ? - function( elem, context, xml ) { - var i = matchers.length; - while ( i-- ) { - if ( !matchers[i]( elem, context, xml ) ) { - return false; - } - } - return true; - } : - matchers[0]; -} - -function condense( unmatched, map, filter, context, xml ) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; - - for ( ; i < len; i++ ) { - if ( (elem = unmatched[i]) ) { - if ( !filter || filter( elem, context, xml ) ) { - newUnmatched.push( elem ); - if ( mapped ) { - map.push( i ); - } - } - } - } - - return newUnmatched; -} - -function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { - if ( postFilter && !postFilter[ expando ] ) { - postFilter = setMatcher( postFilter ); - } - if ( postFinder && !postFinder[ expando ] ) { - postFinder = setMatcher( postFinder, postSelector ); - } - return markFunction(function( seed, results, context, xml ) { - // Positional selectors apply to seed elements, so it is invalid to follow them with relative ones - if ( seed && postFinder ) { - return; - } - - var i, elem, postFilterIn, - preMap = [], - postMap = [], - preexisting = results.length, - - // Get initial elements from seed or context - elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [], seed ), - - // Prefilter to get matcher input, preserving a map for seed-results synchronization - matcherIn = preFilter && ( seed || !selector ) ? - condense( elems, preMap, preFilter, context, xml ) : - elems, - - matcherOut = matcher ? - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - - // ...intermediate processing is necessary - [] : - - // ...otherwise use results directly - results : - matcherIn; - - // Find primary matches - if ( matcher ) { - matcher( matcherIn, matcherOut, context, xml ); - } - - // Apply postFilter - if ( postFilter ) { - postFilterIn = condense( matcherOut, postMap ); - postFilter( postFilterIn, [], context, xml ); - - // Un-match failing elements by moving them back to matcherIn - i = postFilterIn.length; - while ( i-- ) { - if ( (elem = postFilterIn[i]) ) { - matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); - } - } - } - - // Keep seed and results synchronized - if ( seed ) { - // Ignore postFinder because it can't coexist with seed - i = preFilter && matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) ) { - seed[ preMap[i] ] = !(results[ preMap[i] ] = elem); - } - } - } else { - matcherOut = condense( - matcherOut === results ? - matcherOut.splice( preexisting, matcherOut.length ) : - matcherOut - ); - if ( postFinder ) { - postFinder( null, results, matcherOut, xml ); - } else { - push.apply( results, matcherOut ); - } - } - }); -} - -function matcherFromTokens( tokens ) { - var checkContext, matcher, j, - len = tokens.length, - leadingRelative = Expr.relative[ tokens[0].type ], - implicitRelative = leadingRelative || Expr.relative[" "], - i = leadingRelative ? 1 : 0, - - // The foundational matcher ensures that elements are reachable from top-level context(s) - matchContext = addCombinator( function( elem ) { - return elem === checkContext; - }, implicitRelative, true ), - matchAnyContext = addCombinator( function( elem ) { - return indexOf.call( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( - (checkContext = context).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - } ]; - - for ( ; i < len; i++ ) { - if ( (matcher = Expr.relative[ tokens[i].type ]) ) { - matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; - } else { - // The concatenated values are (context, xml) for backCompat - matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); - - // Return special upon seeing a positional matcher - if ( matcher[ expando ] ) { - // Find the next relative operator (if any) for proper handling - j = ++i; - for ( ; j < len; j++ ) { - if ( Expr.relative[ tokens[j].type ] ) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher( matchers ), - i > 1 && tokens.slice( 0, i - 1 ).join("").replace( rtrim, "$1" ), - matcher, - i < j && matcherFromTokens( tokens.slice( i, j ) ), - j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), - j < len && tokens.join("") - ); - } - matchers.push( matcher ); - } - } - - return elementMatcher( matchers ); -} - -function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - var bySet = setMatchers.length > 0, - byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, expandContext ) { - var elem, j, matcher, - setMatched = [], - matchedCount = 0, - i = "0", - unmatched = seed && [], - outermost = expandContext != null, - contextBackup = outermostContext, - // We must always have either seed elements or context - elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), - // Nested matchers should use non-integer dirruns - dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.E); - - if ( outermost ) { - outermostContext = context !== document && context; - cachedruns = superMatcher.el; - } - - // Add elements passing elementMatchers directly to results - for ( ; (elem = elems[i]) != null; i++ ) { - if ( byElement && elem ) { - for ( j = 0; (matcher = elementMatchers[j]); j++ ) { - if ( matcher( elem, context, xml ) ) { - results.push( elem ); - break; - } - } - if ( outermost ) { - dirruns = dirrunsUnique; - cachedruns = ++superMatcher.el; - } - } - - // Track unmatched elements for set filters - if ( bySet ) { - // They will have gone through all possible matchers - if ( (elem = !matcher && elem) ) { - matchedCount--; - } - - // Lengthen the array for every element, matched or not - if ( seed ) { - unmatched.push( elem ); - } - } - } - - // Apply set filters to unmatched elements - matchedCount += i; - if ( bySet && i !== matchedCount ) { - for ( j = 0; (matcher = setMatchers[j]); j++ ) { - matcher( unmatched, setMatched, context, xml ); - } - - if ( seed ) { - // Reintegrate element matches to eliminate the need for sorting - if ( matchedCount > 0 ) { - while ( i-- ) { - if ( !(unmatched[i] || setMatched[i]) ) { - setMatched[i] = pop.call( results ); - } - } - } - - // Discard index placeholder values to get only actual matches - setMatched = condense( setMatched ); - } - - // Add matches to results - push.apply( results, setMatched ); - - // Seedless set matches succeeding multiple successful matchers stipulate sorting - if ( outermost && !seed && setMatched.length > 0 && - ( matchedCount + setMatchers.length ) > 1 ) { - - Sizzle.uniqueSort( results ); - } - } - - // Override manipulation of globals by nested matchers - if ( outermost ) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } - - return unmatched; - }; - - superMatcher.el = 0; - return bySet ? - markFunction( superMatcher ) : - superMatcher; -} - -compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[ expando ][ selector ]; - - if ( !cached ) { - // Generate a function of recursive functions that can be used to check each element - if ( !group ) { - group = tokenize( selector ); - } - i = group.length; - while ( i-- ) { - cached = matcherFromTokens( group[i] ); - if ( cached[ expando ] ) { - setMatchers.push( cached ); - } else { - elementMatchers.push( cached ); - } - } - - // Cache the compiled function - cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); - } - return cached; -}; - -function multipleContexts( selector, contexts, results, seed ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[i], results, seed ); - } - return results; -} - -function select( selector, context, results, seed, xml ) { - var i, tokens, token, type, find, - match = tokenize( selector ), - j = match.length; - - if ( !seed ) { - // Try to minimize operations if there is only one group - if ( match.length === 1 ) { - - // Take a shortcut and set the context if the root selector is an ID - tokens = match[0] = match[0].slice( 0 ); - if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && - context.nodeType === 9 && !xml && - Expr.relative[ tokens[1].type ] ) { - - context = Expr.find["ID"]( token.matches[0].replace( rbackslash, "" ), context, xml )[0]; - if ( !context ) { - return results; - } - - selector = selector.slice( tokens.shift().length ); - } - - // Fetch a seed set for right-to-left matching - for ( i = matchExpr["POS"].test( selector ) ? -1 : tokens.length - 1; i >= 0; i-- ) { - token = tokens[i]; - - // Abort if we hit a combinator - if ( Expr.relative[ (type = token.type) ] ) { - break; - } - if ( (find = Expr.find[ type ]) ) { - // Search, expanding context for leading sibling combinators - if ( (seed = find( - token.matches[0].replace( rbackslash, "" ), - rsibling.test( tokens[0].type ) && context.parentNode || context, - xml - )) ) { - - // If seed is empty or no tokens remain, we can return early - tokens.splice( i, 1 ); - selector = seed.length && tokens.join(""); - if ( !selector ) { - push.apply( results, slice.call( seed, 0 ) ); - return results; - } - - break; - } - } - } - } - } - - // Compile and execute a filtering function - // Provide `match` to avoid retokenization if we modified the selector above - compile( selector, match )( - seed, - context, - xml, - results, - rsibling.test( selector ) - ); - return results; -} - -if ( document.querySelectorAll ) { - (function() { - var disconnectedMatch, - oldSelect = select, - rescape = /'|\\/g, - rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, - - // qSa(:focus) reports false when true (Chrome 21), - // A support test would require too much code (would include document ready) - rbuggyQSA = [":focus"], - - // matchesSelector(:focus) reports false when true (Chrome 21), - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - // A support test would require too much code (would include document ready) - // just skip matchesSelector for :active - rbuggyMatches = [ ":active", ":focus" ], - matches = docElem.matchesSelector || - docElem.mozMatchesSelector || - docElem.webkitMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector; - - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert(function( div ) { - // Select is set to empty string on purpose - // This is to test IE's treatment of not explictly - // setting a boolean content attribute, - // since its presence should be enough - // http://bugs.jquery.com/ticket/12359 - div.innerHTML = ""; - - // IE8 - Some boolean attributes are not treated correctly - if ( !div.querySelectorAll("[selected]").length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" ); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here (do not put tests after this one) - if ( !div.querySelectorAll(":checked").length ) { - rbuggyQSA.push(":checked"); - } - }); - - assert(function( div ) { - - // Opera 10-12/IE9 - ^= $= *= and empty values - // Should not select anything - div.innerHTML = "

    "; - if ( div.querySelectorAll("[test^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" ); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here (do not put tests after this one) - div.innerHTML = ""; - if ( !div.querySelectorAll(":enabled").length ) { - rbuggyQSA.push(":enabled", ":disabled"); - } - }); - - // rbuggyQSA always contains :focus, so no need for a length check - rbuggyQSA = /* rbuggyQSA.length && */ new RegExp( rbuggyQSA.join("|") ); - - select = function( selector, context, results, seed, xml ) { - // Only use querySelectorAll when not filtering, - // when this is not xml, - // and when no QSA bugs apply - if ( !seed && !xml && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - var groups, i, - old = true, - nid = expando, - newContext = context, - newSelector = context.nodeType === 9 && selector; - - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - groups = tokenize( selector ); - - if ( (old = context.getAttribute("id")) ) { - nid = old.replace( rescape, "\\$&" ); - } else { - context.setAttribute( "id", nid ); - } - nid = "[id='" + nid + "'] "; - - i = groups.length; - while ( i-- ) { - groups[i] = nid + groups[i].join(""); - } - newContext = rsibling.test( selector ) && context.parentNode || context; - newSelector = groups.join(","); - } - - if ( newSelector ) { - try { - push.apply( results, slice.call( newContext.querySelectorAll( - newSelector - ), 0 ) ); - return results; - } catch(qsaError) { - } finally { - if ( !old ) { - context.removeAttribute("id"); - } - } - } - } - - return oldSelect( selector, context, results, seed, xml ); - }; - - if ( matches ) { - assert(function( div ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - disconnectedMatch = matches.call( div, "div" ); - - // This should fail with an exception - // Gecko does not error, returns false instead - try { - matches.call( div, "[test!='']:sizzle" ); - rbuggyMatches.push( "!=", pseudos ); - } catch ( e ) {} - }); - - // rbuggyMatches always contains :active and :focus, so no need for a length check - rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") ); - - Sizzle.matchesSelector = function( elem, expr ) { - // Make sure that attribute selectors are quoted - expr = expr.replace( rattributeQuotes, "='$1']" ); - - // rbuggyMatches always contains :active, so no need for an existence check - if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && (!rbuggyQSA || !rbuggyQSA.test( expr )) ) { - try { - var ret = matches.call( elem, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch(e) {} - } - - return Sizzle( expr, null, null, [ elem ] ).length > 0; - }; - } - })(); -} - -// Deprecated -Expr.pseudos["nth"] = Expr.pseudos["eq"]; - -// Back-compat -function setFilters() {} -Expr.filters = setFilters.prototype = Expr.pseudos; -Expr.setFilters = new setFilters(); - +/*! + * Sizzle CSS Selector Engine + * Copyright 2012 jQuery Foundation and other contributors + * Released under the MIT license + * http://sizzlejs.com/ + */ +(function( window, undefined ) { + +var cachedruns, + assertGetIdNotName, + Expr, + getText, + isXML, + contains, + compile, + sortOrder, + hasDuplicate, + outermostContext, + + baseHasDuplicate = true, + strundefined = "undefined", + + expando = ( "sizcache" + Math.random() ).replace( ".", "" ), + + Token = String, + document = window.document, + docElem = document.documentElement, + dirruns = 0, + done = 0, + pop = [].pop, + push = [].push, + slice = [].slice, + // Use a stripped-down indexOf if a native one is unavailable + indexOf = [].indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + // Augment a function for special use by Sizzle + markFunction = function( fn, value ) { + fn[ expando ] = value == null || value; + return fn; + }, + + createCache = function() { + var cache = {}, + keys = []; + + return markFunction(function( key, value ) { + // Only keep the most recent entries + if ( keys.push( key ) > Expr.cacheLength ) { + delete cache[ keys.shift() ]; + } + + // Retrieve with (key + " ") to avoid collision with native Object.prototype properties (see Issue #157) + return (cache[ key + " " ] = value); + }, cache ); + }, + + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + + // Regex + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors) + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors + operators = "([*^$|!~]?=)", + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + + "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", + + // Prefer arguments not in parens/brackets, + // then attribute selectors and non-pseudos (denoted by :), + // then anything else + // These preferences are here to reduce the number of selectors + // needing tokenize in the PSEUDO preFilter + pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)", + + // For matchExpr.POS and matchExpr.needsContext + pos = ":(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ), + rpseudo = new RegExp( pseudos ), + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/, + + rnot = /^:not/, + rsibling = /[\x20\t\r\n\f]*[+~]/, + rendsWithNot = /:not\($/, + + rheader = /h\d/i, + rinputs = /input|select|textarea|button/i, + + rbackslash = /\\(?!\\)/g, + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "POS": new RegExp( pos, "i" ), + "CHILD": new RegExp( "^:(only|nth|first|last)-child(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + // For use in libraries implementing .is() + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" ) + }, + + // Support + + // Used for testing something on an element + assert = function( fn ) { + var div = document.createElement("div"); + + try { + return fn( div ); + } catch (e) { + return false; + } finally { + // release memory in IE + div = null; + } + }, + + // Check if getElementsByTagName("*") returns only elements + assertTagNameNoComments = assert(function( div ) { + div.appendChild( document.createComment("") ); + return !div.getElementsByTagName("*").length; + }), + + // Check if getAttribute returns normalized href attributes + assertHrefNotNormalized = assert(function( div ) { + div.innerHTML = ""; + return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && + div.firstChild.getAttribute("href") === "#"; + }), + + // Check if attributes should be retrieved by attribute nodes + assertAttributes = assert(function( div ) { + div.innerHTML = ""; + var type = typeof div.lastChild.getAttribute("multiple"); + // IE8 returns a string for some attributes even when not present + return type !== "boolean" && type !== "string"; + }), + + // Check if getElementsByClassName can be trusted + assertUsableClassName = assert(function( div ) { + // Opera can't find a second classname (in 9.6) + div.innerHTML = ""; + if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) { + return false; + } + + // Safari 3.2 caches class attributes and doesn't catch changes + div.lastChild.className = "e"; + return div.getElementsByClassName("e").length === 2; + }), + + // Check if getElementById returns elements by name + // Check if getElementsByName privileges form controls or returns elements by ID + assertUsableName = assert(function( div ) { + // Inject content + div.id = expando + 0; + div.innerHTML = "
    "; + docElem.insertBefore( div, docElem.firstChild ); + + // Test + var pass = document.getElementsByName && + // buggy browsers will return fewer than the correct 2 + document.getElementsByName( expando ).length === 2 + + // buggy browsers will return more than the correct 0 + document.getElementsByName( expando + 0 ).length; + assertGetIdNotName = !document.getElementById( expando ); + + // Cleanup + docElem.removeChild( div ); + + return pass; + }); + +// If slice is not available, provide a backup +try { + slice.call( docElem.childNodes, 0 )[0].nodeType; +} catch ( e ) { + slice = function( i ) { + var elem, + results = []; + for ( ; (elem = this[i]); i++ ) { + results.push( elem ); + } + return results; + }; +} + +function Sizzle( selector, context, results, seed ) { + results = results || []; + context = context || document; + var match, elem, xml, m, + nodeType = context.nodeType; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( nodeType !== 1 && nodeType !== 9 ) { + return []; + } + + xml = isXML( context ); + + if ( !xml && !seed ) { + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) { + push.apply( results, slice.call(context.getElementsByClassName( m ), 0) ); + return results; + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed, xml ); +} + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + return Sizzle( expr, null, null, [ elem ] ).length > 0; +}; + +// Returns a function to use in pseudos for input types +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +// Returns a function to use in pseudos for buttons +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +// Returns a function to use in pseudos for positionals +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( nodeType ) { + if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (see #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + } else { + + // If no nodeType, this is expected to be an array + for ( ; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } + return ret; +}; + +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +// Element contains another +contains = Sizzle.contains = docElem.contains ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) ); + } : + docElem.compareDocumentPosition ? + function( a, b ) { + return b && !!( a.compareDocumentPosition( b ) & 16 ); + } : + function( a, b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + return false; + }; + +Sizzle.attr = function( elem, name ) { + var val, + xml = isXML( elem ); + + if ( !xml ) { + name = name.toLowerCase(); + } + if ( (val = Expr.attrHandle[ name ]) ) { + return val( elem ); + } + if ( xml || assertAttributes ) { + return elem.getAttribute( name ); + } + val = elem.getAttributeNode( name ); + return val ? + typeof elem[ name ] === "boolean" ? + elem[ name ] ? name : null : + val.specified ? val.value : null : + null; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + // IE6/7 return a modified href + attrHandle: assertHrefNotNormalized ? + {} : + { + "href": function( elem ) { + return elem.getAttribute( "href", 2 ); + }, + "type": function( elem ) { + return elem.getAttribute("type"); + } + }, + + find: { + "ID": assertGetIdNotName ? + function( id, context, xml ) { + if ( typeof context.getElementById !== strundefined && !xml ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + } : + function( id, context, xml ) { + if ( typeof context.getElementById !== strundefined && !xml ) { + var m = context.getElementById( id ); + + return m ? + m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ? + [m] : + undefined : + []; + } + }, + + "TAG": assertTagNameNoComments ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + var elem, + tmp = [], + i = 0; + + for ( ; (elem = results[i]); i++ ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }, + + "NAME": assertUsableName && function( tag, context ) { + if ( typeof context.getElementsByName !== strundefined ) { + return context.getElementsByName( name ); + } + }, + + "CLASS": assertUsableClassName && function( className, context, xml ) { + if ( typeof context.getElementsByClassName !== strundefined && !xml ) { + return context.getElementsByClassName( className ); + } + } + }, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( rbackslash, "" ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 3 xn-component of xn+y argument ([+-]?\d*n|) + 4 sign of xn-component + 5 x of xn-component + 6 sign of y-component + 7 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1] === "nth" ) { + // nth-child requires argument + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) ); + match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" ); + + // other types prohibit arguments + } else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var unquoted, excess; + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + if ( match[3] ) { + match[2] = match[3]; + } else if ( (unquoted = match[4]) ) { + // Only check arguments that contain a pseudo + if ( rpseudo.test(unquoted) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + unquoted = unquoted.slice( 0, excess ); + match[0] = match[0].slice( 0, excess ); + } + match[2] = unquoted; + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + "ID": assertGetIdNotName ? + function( id ) { + id = id.replace( rbackslash, "" ); + return function( elem ) { + return elem.getAttribute("id") === id; + }; + } : + function( id ) { + id = id.replace( rbackslash, "" ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === id; + }; + }, + + "TAG": function( nodeName ) { + if ( nodeName === "*" ) { + return function() { return true; }; + } + nodeName = nodeName.replace( rbackslash, "" ).toLowerCase(); + + return function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ expando ][ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem, context ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.substr( result.length - check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.substr( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, argument, first, last ) { + + if ( type === "nth" ) { + return function( elem ) { + var node, diff, + parent = elem.parentNode; + + if ( first === 1 && last === 0 ) { + return true; + } + + if ( parent ) { + diff = 0; + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + diff++; + if ( elem === node ) { + break; + } + } + } + } + + // Incorporate the offset (or cast to NaN), then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + }; + } + + return function( elem ) { + var node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + /* falls through */ + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), + // not comment, processing instructions, or others + // Thanks to Diego Perini for the nodeName shortcut + // Greater than "@" means alpha characters (specifically not starting with "#" or "?") + var nodeType; + elem = elem.firstChild; + while ( elem ) { + if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) { + return false; + } + elem = elem.nextSibling; + } + return true; + }, + + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "text": function( elem ) { + var type, attr; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && + (type = elem.type) === "text" && + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type ); + }, + + // Input types + "radio": createInputPseudo("radio"), + "checkbox": createInputPseudo("checkbox"), + "file": createInputPseudo("file"), + "password": createInputPseudo("password"), + "image": createInputPseudo("image"), + + "submit": createButtonPseudo("submit"), + "reset": createButtonPseudo("reset"), + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "focus": function( elem ) { + var doc = elem.ownerDocument; + return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + "active": function( elem ) { + return elem === elem.ownerDocument.activeElement; + }, + + // Positional types + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + for ( var i = 0; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + for ( var i = 1; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + for ( var i = argument < 0 ? argument + length : argument; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + for ( var i = argument < 0 ? argument + length : argument; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +function siblingCheck( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; +} + +sortOrder = docElem.compareDocumentPosition ? + function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + return ( !a.compareDocumentPosition || !b.compareDocumentPosition ? + a.compareDocumentPosition : + a.compareDocumentPosition(b) & 4 + ) ? -1 : 1; + } : + function( a, b ) { + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // If the nodes are siblings (or identical) we can do a quick check + if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + +// Always assume the presence of duplicates if sort doesn't +// pass them to our comparison function (as in Google Chrome). +[0, 0].sort( sortOrder ); +baseHasDuplicate = !hasDuplicate; + +// Document sorting and removing duplicates +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + i = 1, + j = 0; + + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( ; (elem = results[i]); i++ ) { + if ( elem === results[ i - 1 ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + return results; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +function tokenize( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ expando ][ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( tokens = [] ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + tokens.push( matched = new Token( match.shift() ) ); + soFar = soFar.slice( matched.length ); + + // Cast descendant combinators to space + matched.type = match[0].replace( rtrim, " " ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + + tokens.push( matched = new Token( match.shift() ) ); + soFar = soFar.slice( matched.length ); + matched.type = type; + matched.matches = match; + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && combinator.dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( !xml ) { + var cache, + dirkey = dirruns + " " + doneName + " ", + cachedkey = dirkey + cachedruns; + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + if ( (cache = elem[ expando ]) === cachedkey ) { + return elem.sizset; + } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) { + if ( elem.sizset ) { + return elem; + } + } else { + elem[ expando ] = cachedkey; + if ( matcher( elem, context, xml ) ) { + elem.sizset = true; + return elem; + } + elem.sizset = false; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + if ( matcher( elem, context, xml ) ) { + return elem; + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && tokens.slice( 0, i - 1 ).join("").replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && tokens.join("") + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, expandContext ) { + var elem, j, matcher, + setMatched = [], + matchedCount = 0, + i = "0", + unmatched = seed && [], + outermost = expandContext != null, + contextBackup = outermostContext, + // We must always have either seed elements or context + elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), + // Nested matchers should use non-integer dirruns + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.E); + + if ( outermost ) { + outermostContext = context !== document && context; + cachedruns = superMatcher.el; + } + + // Add elements passing elementMatchers directly to results + for ( ; (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + for ( j = 0; (matcher = elementMatchers[j]); j++ ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + cachedruns = ++superMatcher.el; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + for ( j = 0; (matcher = setMatchers[j]); j++ ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + superMatcher.el = 0; + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ expando ][ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !group ) { + group = tokenize( selector ); + } + i = group.length; + while ( i-- ) { + cached = matcherFromTokens( group[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + } + return cached; +}; + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function select( selector, context, results, seed, xml ) { + var i, tokens, token, type, find, + match = tokenize( selector ), + j = match.length; + + if ( !seed ) { + // Try to minimize operations if there is only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + context.nodeType === 9 && !xml && + Expr.relative[ tokens[1].type ] ) { + + context = Expr.find["ID"]( token.matches[0].replace( rbackslash, "" ), context, xml )[0]; + if ( !context ) { + return results; + } + + selector = selector.slice( tokens.shift().length ); + } + + // Fetch a seed set for right-to-left matching + for ( i = matchExpr["POS"].test( selector ) ? -1 : tokens.length - 1; i >= 0; i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( rbackslash, "" ), + rsibling.test( tokens[0].type ) && context.parentNode || context, + xml + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && tokens.join(""); + if ( !selector ) { + push.apply( results, slice.call( seed, 0 ) ); + return results; + } + + break; + } + } + } + } + } + + // Compile and execute a filtering function + // Provide `match` to avoid retokenization if we modified the selector above + compile( selector, match )( + seed, + context, + xml, + results, + rsibling.test( selector ) + ); + return results; +} + +if ( document.querySelectorAll ) { + (function() { + var disconnectedMatch, + oldSelect = select, + rescape = /'|\\/g, + rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, + + // qSa(:focus) reports false when true (Chrome 21), no need to also add to buggyMatches since matches checks buggyQSA + // A support test would require too much code (would include document ready) + rbuggyQSA = [ ":focus" ], + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + // A support test would require too much code (would include document ready) + // just skip matchesSelector for :active + rbuggyMatches = [ ":active" ], + matches = docElem.matchesSelector || + docElem.mozMatchesSelector || + docElem.webkitMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector; + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explictly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + + // IE8 - Some boolean attributes are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here (do not put tests after this one) + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + + // Opera 10-12/IE9 - ^= $= *= and empty values + // Should not select anything + div.innerHTML = "

    "; + if ( div.querySelectorAll("[test^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here (do not put tests after this one) + div.innerHTML = ""; + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push(":enabled", ":disabled"); + } + }); + + // rbuggyQSA always contains :focus, so no need for a length check + rbuggyQSA = /* rbuggyQSA.length && */ new RegExp( rbuggyQSA.join("|") ); + + select = function( selector, context, results, seed, xml ) { + // Only use querySelectorAll when not filtering, + // when this is not xml, + // and when no QSA bugs apply + if ( !seed && !xml && !rbuggyQSA.test( selector ) ) { + var groups, i, + old = true, + nid = expando, + newContext = context, + newSelector = context.nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + groups[i].join(""); + } + newContext = rsibling.test( selector ) && context.parentNode || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, slice.call( newContext.querySelectorAll( + newSelector + ), 0 ) ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + + return oldSelect( selector, context, results, seed, xml ); + }; + + if ( matches ) { + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + try { + matches.call( div, "[test!='']:sizzle" ); + rbuggyMatches.push( "!=", pseudos ); + } catch ( e ) {} + }); + + // rbuggyMatches always contains :active and :focus, so no need for a length check + rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") ); + + Sizzle.matchesSelector = function( elem, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + // rbuggyMatches always contains :active, so no need for an existence check + if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && !rbuggyQSA.test( expr ) ) { + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, null, null, [ elem ] ).length > 0; + }; + } + })(); +} + +// Deprecated +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Back-compat +function setFilters() {} +Expr.filters = setFilters.prototype = Expr.pseudos; +Expr.setFilters = new setFilters(); + // Override sizzle attribute retrieval Sizzle.attr = jQuery.attr; jQuery.find = Sizzle; @@ -5335,9 +5348,9 @@ jQuery.unique = Sizzle.uniqueSort; jQuery.text = Sizzle.getText; jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; - - -})( window ); + + +})( window ); var runtil = /Until$/, rparentsprev = /^(?:parents|prev(?:Until|All))/, isSimple = /^.[^:#\[\.,]*$/, @@ -6533,7 +6546,7 @@ var curCSS, iframe, iframeDoc, rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), rrelNum = new RegExp( "^([-+])=(" + core_pnum + ")", "i" ), - elemdisplay = {}, + elemdisplay = { BODY: "block" }, cssShow = { position: "absolute", visibility: "hidden", display: "block" }, cssNormalTransform = { @@ -6814,7 +6827,9 @@ if ( window.getComputedStyle ) { if ( computed ) { - ret = computed[ name ]; + // getPropertyValue is only needed for .css('filter') in IE9, see #12537 + ret = computed.getPropertyValue( name ) || computed[ name ]; + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { ret = jQuery.style( elem, name ); } @@ -7843,9 +7858,12 @@ jQuery.extend({ // A cross-domain request is in order when we have a protocol:host:port mismatch if ( s.crossDomain == null ) { - parts = rurl.exec( s.url.toLowerCase() ) || false; - s.crossDomain = parts && ( parts.join(":") + ( parts[ 3 ] ? "" : parts[ 1 ] === "http:" ? 80 : 443 ) ) !== - ( ajaxLocParts.join(":") + ( ajaxLocParts[ 3 ] ? "" : ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ); + parts = rurl.exec( s.url.toLowerCase() ); + s.crossDomain = !!( parts && + ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] || + ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) != + ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) ) + ); } // Convert data if not already a string @@ -8464,7 +8482,7 @@ if ( jQuery.support.ajax ) { // on any attempt to access responseText (#11426) try { responses.text = xhr.responseText; - } catch( _ ) { + } catch( e ) { } // Firefox throws an exception when accessing @@ -8617,7 +8635,9 @@ function Animation( elem, properties, options ) { tick = function() { var currentTime = fxNow || createFxNow(), remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), - percent = 1 - ( remaining / animation.duration || 0 ), + // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, index = 0, length = animation.tweens.length; @@ -8769,7 +8789,7 @@ jQuery.Animation = jQuery.extend( Animation, { }); function defaultPrefilter( elem, props, opts ) { - var index, prop, value, length, dataShow, tween, hooks, oldfire, + var index, prop, value, length, dataShow, toggle, tween, hooks, oldfire, anim = this, style = elem.style, orig = {}, @@ -8843,6 +8863,7 @@ function defaultPrefilter( elem, props, opts ) { value = props[ index ]; if ( rfxtypes.exec( value ) ) { delete props[ index ]; + toggle = toggle || value === "toggle"; if ( value === ( hidden ? "hide" : "show" ) ) { continue; } @@ -8853,6 +8874,14 @@ function defaultPrefilter( elem, props, opts ) { length = handled.length; if ( length ) { dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} ); + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + + // store state if its toggle - enables .stop().toggle() to "reverse" + if ( toggle ) { + dataShow.hidden = !hidden; + } if ( hidden ) { jQuery( elem ).show(); } else { @@ -9149,6 +9178,8 @@ jQuery.fx.tick = function() { timers = jQuery.timers, i = 0; + fxNow = jQuery.now(); + for ( ; i < timers.length; i++ ) { timer = timers[ i ]; // Checks the timer has not already been removed @@ -9160,6 +9191,7 @@ jQuery.fx.tick = function() { if ( !timers.length ) { jQuery.fx.stop(); } + fxNow = undefined; }; jQuery.fx.timer = function( timer ) { diff --git a/sites/all/modules/contrib/js/jquery_update/replace/jquery/1.8/jquery.min.js b/sites/all/modules/contrib/js/jquery_update/replace/jquery/1.8/jquery.min.js index f65cf1d..3883779 100644 --- a/sites/all/modules/contrib/js/jquery_update/replace/jquery/1.8/jquery.min.js +++ b/sites/all/modules/contrib/js/jquery_update/replace/jquery/1.8/jquery.min.js @@ -1,2 +1,2 @@ -/*! jQuery v1.8.2 jquery.com | jquery.org/license */ -(function(a,b){function G(a){var b=F[a]={};return p.each(a.split(s),function(a,c){b[c]=!0}),b}function J(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(I,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:+d+""===d?+d:H.test(d)?p.parseJSON(d):d}catch(f){}p.data(a,c,d)}else d=b}return d}function K(a){var b;for(b in a){if(b==="data"&&p.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function ba(){return!1}function bb(){return!0}function bh(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function bi(a,b){do a=a[b];while(a&&a.nodeType!==1);return a}function bj(a,b,c){b=b||0;if(p.isFunction(b))return p.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return p.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=p.grep(a,function(a){return a.nodeType===1});if(be.test(b))return p.filter(b,d,!c);b=p.filter(b,d)}return p.grep(a,function(a,d){return p.inArray(a,b)>=0===c})}function bk(a){var b=bl.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function bC(a,b){return a.getElementsByTagName(b)[0]||a.appendChild(a.ownerDocument.createElement(b))}function bD(a,b){if(b.nodeType!==1||!p.hasData(a))return;var c,d,e,f=p._data(a),g=p._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;d").appendTo(e.body),c=b.css("display");b.remove();if(c==="none"||c===""){bI=e.body.appendChild(bI||p.extend(e.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!bJ||!bI.createElement)bJ=(bI.contentWindow||bI.contentDocument).document,bJ.write(""),bJ.close();b=bJ.body.appendChild(bJ.createElement(a)),c=bH(b,"display"),e.body.removeChild(bI)}return bS[a]=c,c}function ci(a,b,c,d){var e;if(p.isArray(b))p.each(b,function(b,e){c||ce.test(a)?d(a,e):ci(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&p.type(b)==="object")for(e in b)ci(a+"["+e+"]",b[e],c,d);else d(a,b)}function cz(a){return function(b,c){typeof b!="string"&&(c=b,b="*");var d,e,f,g=b.toLowerCase().split(s),h=0,i=g.length;if(p.isFunction(c))for(;h)[^>]*$|#([\w\-]*)$)/,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,y=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,z=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,A=/^-ms-/,B=/-([\da-z])/gi,C=function(a,b){return(b+"").toUpperCase()},D=function(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",D,!1),p.ready()):e.readyState==="complete"&&(e.detachEvent("onreadystatechange",D),p.ready())},E={};p.fn=p.prototype={constructor:p,init:function(a,c,d){var f,g,h,i;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?f=[null,a,null]:f=u.exec(a);if(f&&(f[1]||!c)){if(f[1])return c=c instanceof p?c[0]:c,i=c&&c.nodeType?c.ownerDocument||c:e,a=p.parseHTML(f[1],i,!0),v.test(f[1])&&p.isPlainObject(c)&&this.attr.call(a,c,!0),p.merge(this,a);g=e.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return d.find(a);this.length=1,this[0]=g}return this.context=e,this.selector=a,this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}return p.isFunction(a)?d.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),p.makeArray(a,this))},selector:"",jquery:"1.8.2",length:0,size:function(){return this.length},toArray:function(){return k.call(this)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=p.merge(this.constructor(),a);return d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d},each:function(a,b){return p.each(this,a,b)},ready:function(a){return p.ready.promise().done(a),this},eq:function(a){return a=+a,a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(k.apply(this,arguments),"slice",k.call(arguments).join(","))},map:function(a){return this.pushStack(p.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:j,sort:[].sort,splice:[].splice},p.fn.init.prototype=p.fn,p.extend=p.fn.extend=function(){var a,c,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;typeof h=="boolean"&&(k=h,h=arguments[1]||{},i=2),typeof h!="object"&&!p.isFunction(h)&&(h={}),j===i&&(h=this,--i);for(;i0)return;d.resolveWith(e,[p]),p.fn.trigger&&p(e).trigger("ready").off("ready")},isFunction:function(a){return p.type(a)==="function"},isArray:Array.isArray||function(a){return p.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):E[m.call(a)]||"object"},isPlainObject:function(a){if(!a||p.type(a)!=="object"||a.nodeType||p.isWindow(a))return!1;try{if(a.constructor&&!n.call(a,"constructor")&&!n.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||n.call(a,d)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},error:function(a){throw new Error(a)},parseHTML:function(a,b,c){var d;return!a||typeof a!="string"?null:(typeof b=="boolean"&&(c=b,b=0),b=b||e,(d=v.exec(a))?[b.createElement(d[1])]:(d=p.buildFragment([a],b,c?null:[]),p.merge([],(d.cacheable?p.clone(d.fragment):d.fragment).childNodes)))},parseJSON:function(b){if(!b||typeof b!="string")return null;b=p.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(w.test(b.replace(y,"@").replace(z,"]").replace(x,"")))return(new Function("return "+b))();p.error("Invalid JSON: "+b)},parseXML:function(c){var d,e;if(!c||typeof c!="string")return null;try{a.DOMParser?(e=new DOMParser,d=e.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(f){d=b}return(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&p.error("Invalid XML: "+c),d},noop:function(){},globalEval:function(b){b&&r.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(A,"ms-").replace(B,C)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,c,d){var e,f=0,g=a.length,h=g===b||p.isFunction(a);if(d){if(h){for(e in a)if(c.apply(a[e],d)===!1)break}else for(;f0&&a[0]&&a[i-1]||i===0||p.isArray(a));if(j)for(;h-1)i.splice(c,1),e&&(c<=g&&g--,c<=h&&h--)}),this},has:function(a){return p.inArray(a,i)>-1},empty:function(){return i=[],this},disable:function(){return i=j=c=b,this},disabled:function(){return!i},lock:function(){return j=b,c||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return b=b||[],b=[a,b.slice?b.slice():b],i&&(!d||j)&&(e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!d}};return l},p.extend({Deferred:function(a){var b=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return p.Deferred(function(c){p.each(b,function(b,d){var f=d[0],g=a[b];e[d[1]](p.isFunction(g)?function(){var a=g.apply(this,arguments);a&&p.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f+"With"](this===e?c:this,[a])}:c[f])}),a=null}).promise()},promise:function(a){return a!=null?p.extend(a,d):d}},e={};return d.pipe=d.then,p.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[a^1][2].disable,b[2][2].lock),e[f[0]]=g.fire,e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=k.call(arguments),d=c.length,e=d!==1||a&&p.isFunction(a.promise)?d:0,f=e===1?a:p.Deferred(),g=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?k.call(arguments):d,c===h?f.notifyWith(b,c):--e||f.resolveWith(b,c)}},h,i,j;if(d>1){h=new Array(d),i=new Array(d),j=new Array(d);for(;b
    a",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="
    t
    ",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="
    ",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||p.guid++:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.length,e=c.shift(),f=p._queueHooks(a,b),g=function(){p.dequeue(a,b)};e==="inprogress"&&(e=c.shift(),d--),e&&(b==="fx"&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c=0)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c=0)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,d+""),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;j=0:p.find(m,this,null,[f]).length),h[m]&&j.push(l);j.length&&u.push({elem:f,matches:j})}o.length>q&&u.push({elem:this,matches:o.slice(q)});for(d=0;d0?this.on(b,null,a,c):this.trigger(b)},Y.test(b)&&(p.event.fixHooks[b]=p.event.keyHooks),Z.test(b)&&(p.event.fixHooks[b]=p.event.mouseHooks)}),function(a,b){function bc(a,b,c,d){c=c||[],b=b||r;var e,f,i,j,k=b.nodeType;if(!a||typeof a!="string")return c;if(k!==1&&k!==9)return[];i=g(b);if(!i&&!d)if(e=P.exec(a))if(j=e[1]){if(k===9){f=b.getElementById(j);if(!f||!f.parentNode)return c;if(f.id===j)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(j))&&h(b,f)&&f.id===j)return c.push(f),c}else{if(e[2])return w.apply(c,x.call(b.getElementsByTagName(a),0)),c;if((j=e[3])&&_&&b.getElementsByClassName)return w.apply(c,x.call(b.getElementsByClassName(j),0)),c}return bp(a.replace(L,"$1"),b,c,d,i)}function bd(a){return function(b){var c=b.nodeName.toLowerCase();return c==="input"&&b.type===a}}function be(a){return function(b){var c=b.nodeName.toLowerCase();return(c==="input"||c==="button")&&b.type===a}}function bf(a){return z(function(b){return b=+b,z(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function bg(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}function bh(a,b){var c,d,f,g,h,i,j,k=C[o][a];if(k)return b?0:k.slice(0);h=a,i=[],j=e.preFilter;while(h){if(!c||(d=M.exec(h)))d&&(h=h.slice(d[0].length)),i.push(f=[]);c=!1;if(d=N.exec(h))f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=d[0].replace(L," ");for(g in e.filter)(d=W[g].exec(h))&&(!j[g]||(d=j[g](d,r,!0)))&&(f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=g,c.matches=d);if(!c)break}return b?h.length:h?bc.error(a):C(a,i).slice(0)}function bi(a,b,d){var e=b.dir,f=d&&b.dir==="parentNode",g=u++;return b.first?function(b,c,d){while(b=b[e])if(f||b.nodeType===1)return a(b,c,d)}:function(b,d,h){if(!h){var i,j=t+" "+g+" ",k=j+c;while(b=b[e])if(f||b.nodeType===1){if((i=b[o])===k)return b.sizset;if(typeof i=="string"&&i.indexOf(j)===0){if(b.sizset)return b}else{b[o]=k;if(a(b,d,h))return b.sizset=!0,b;b.sizset=!1}}}else while(b=b[e])if(f||b.nodeType===1)if(a(b,d,h))return b}}function bj(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function bk(a,b,c,d,e){var f,g=[],h=0,i=a.length,j=b!=null;for(;h-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==l)||((b=c).nodeType?j(a,c,d):k(a,c,d))}];for(;i1&&bj(m),i>1&&a.slice(0,i-1).join("").replace(L,"$1"),c,i0,f=a.length>0,g=function(h,i,j,k,m){var n,o,p,q=[],s=0,u="0",x=h&&[],y=m!=null,z=l,A=h||f&&e.find.TAG("*",m&&i.parentNode||i),B=t+=z==null?1:Math.E;y&&(l=i!==r&&i,c=g.el);for(;(n=A[u])!=null;u++){if(f&&n){for(o=0;p=a[o];o++)if(p(n,i,j)){k.push(n);break}y&&(t=B,c=++g.el)}d&&((n=!p&&n)&&s--,h&&x.push(n))}s+=u;if(d&&u!==s){for(o=0;p=b[o];o++)p(x,q,i,j);if(h){if(s>0)while(u--)!x[u]&&!q[u]&&(q[u]=v.call(k));q=bk(q)}w.apply(k,q),y&&!h&&q.length>0&&s+b.length>1&&bc.uniqueSort(k)}return y&&(t=B,l=z),x};return g.el=0,d?z(g):g}function bo(a,b,c,d){var e=0,f=b.length;for(;e2&&(j=h[0]).type==="ID"&&b.nodeType===9&&!f&&e.relative[h[1].type]){b=e.find.ID(j.matches[0].replace(V,""),b,f)[0];if(!b)return c;a=a.slice(h.shift().length)}for(g=W.POS.test(a)?-1:h.length-1;g>=0;g--){j=h[g];if(e.relative[k=j.type])break;if(l=e.find[k])if(d=l(j.matches[0].replace(V,""),R.test(h[0].type)&&b.parentNode||b,f)){h.splice(g,1),a=d.length&&h.join("");if(!a)return w.apply(c,x.call(d,0)),c;break}}}return i(a,m)(d,b,f,c,R.test(a)),c}function bq(){}var c,d,e,f,g,h,i,j,k,l,m=!0,n="undefined",o=("sizcache"+Math.random()).replace(".",""),q=String,r=a.document,s=r.documentElement,t=0,u=0,v=[].pop,w=[].push,x=[].slice,y=[].indexOf||function(a){var b=0,c=this.length;for(;be.cacheLength&&delete a[b.shift()],a[c]=d},a)},B=A(),C=A(),D=A(),E="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",G=F.replace("w","w#"),H="([*^$|!~]?=)",I="\\["+E+"*("+F+")"+E+"*(?:"+H+E+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+G+")|)|)"+E+"*\\]",J=":("+F+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+I+")|[^:]|\\\\.)*|.*))\\)|)",K=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+E+"*((?:-\\d)?\\d*)"+E+"*\\)|)(?=[^-]|$)",L=new RegExp("^"+E+"+|((?:^|[^\\\\])(?:\\\\.)*)"+E+"+$","g"),M=new RegExp("^"+E+"*,"+E+"*"),N=new RegExp("^"+E+"*([\\x20\\t\\r\\n\\f>+~])"+E+"*"),O=new RegExp(J),P=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,Q=/^:not/,R=/[\x20\t\r\n\f]*[+~]/,S=/:not\($/,T=/h\d/i,U=/input|select|textarea|button/i,V=/\\(?!\\)/g,W={ID:new RegExp("^#("+F+")"),CLASS:new RegExp("^\\.("+F+")"),NAME:new RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:new RegExp("^("+F.replace("w","w*")+")"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+J),POS:new RegExp(K,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+E+"*(even|odd|(([+-]|)(\\d*)n|)"+E+"*(?:([+-]|)"+E+"*(\\d+)|))"+E+"*\\)|)","i"),needsContext:new RegExp("^"+E+"*[>+~]|"+K,"i")},X=function(a){var b=r.createElement("div");try{return a(b)}catch(c){return!1}finally{b=null}},Y=X(function(a){return a.appendChild(r.createComment("")),!a.getElementsByTagName("*").length}),Z=X(function(a){return a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!==n&&a.firstChild.getAttribute("href")==="#"}),$=X(function(a){a.innerHTML="";var b=typeof a.lastChild.getAttribute("multiple");return b!=="boolean"&&b!=="string"}),_=X(function(a){return a.innerHTML="",!a.getElementsByClassName||!a.getElementsByClassName("e").length?!1:(a.lastChild.className="e",a.getElementsByClassName("e").length===2)}),ba=X(function(a){a.id=o+0,a.innerHTML="
    ",s.insertBefore(a,s.firstChild);var b=r.getElementsByName&&r.getElementsByName(o).length===2+r.getElementsByName(o+0).length;return d=!r.getElementById(o),s.removeChild(a),b});try{x.call(s.childNodes,0)[0].nodeType}catch(bb){x=function(a){var b,c=[];for(;b=this[a];a++)c.push(b);return c}}bc.matches=function(a,b){return bc(a,null,null,b)},bc.matchesSelector=function(a,b){return bc(b,null,null,[a]).length>0},f=bc.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(e===1||e===9||e===11){if(typeof a.textContent=="string")return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=f(a)}else if(e===3||e===4)return a.nodeValue}else for(;b=a[d];d++)c+=f(b);return c},g=bc.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?b.nodeName!=="HTML":!1},h=bc.contains=s.contains?function(a,b){var c=a.nodeType===9?a.documentElement:a,d=b&&b.parentNode;return a===d||!!(d&&d.nodeType===1&&c.contains&&c.contains(d))}:s.compareDocumentPosition?function(a,b){return b&&!!(a.compareDocumentPosition(b)&16)}:function(a,b){while(b=b.parentNode)if(b===a)return!0;return!1},bc.attr=function(a,b){var c,d=g(a);return d||(b=b.toLowerCase()),(c=e.attrHandle[b])?c(a):d||$?a.getAttribute(b):(c=a.getAttributeNode(b),c?typeof a[b]=="boolean"?a[b]?b:null:c.specified?c.value:null:null)},e=bc.selectors={cacheLength:50,createPseudo:z,match:W,attrHandle:Z?{}:{href:function(a){return a.getAttribute("href",2)},type:function(a){return a.getAttribute("type")}},find:{ID:d?function(a,b,c){if(typeof b.getElementById!==n&&!c){var d=b.getElementById(a);return d&&d.parentNode?[d]:[]}}:function(a,c,d){if(typeof c.getElementById!==n&&!d){var e=c.getElementById(a);return e?e.id===a||typeof e.getAttributeNode!==n&&e.getAttributeNode("id").value===a?[e]:b:[]}},TAG:Y?function(a,b){if(typeof b.getElementsByTagName!==n)return b.getElementsByTagName(a)}:function(a,b){var c=b.getElementsByTagName(a);if(a==="*"){var d,e=[],f=0;for(;d=c[f];f++)d.nodeType===1&&e.push(d);return e}return c},NAME:ba&&function(a,b){if(typeof b.getElementsByName!==n)return b.getElementsByName(name)},CLASS:_&&function(a,b,c){if(typeof b.getElementsByClassName!==n&&!c)return b.getElementsByClassName(a)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(V,""),a[3]=(a[4]||a[5]||"").replace(V,""),a[2]==="~="&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),a[1]==="nth"?(a[2]||bc.error(a[0]),a[3]=+(a[3]?a[4]+(a[5]||1):2*(a[2]==="even"||a[2]==="odd")),a[4]=+(a[6]+a[7]||a[2]==="odd")):a[2]&&bc.error(a[0]),a},PSEUDO:function(a){var b,c;if(W.CHILD.test(a[0]))return null;if(a[3])a[2]=a[3];else if(b=a[4])O.test(b)&&(c=bh(b,!0))&&(c=b.indexOf(")",b.length-c)-b.length)&&(b=b.slice(0,c),a[0]=a[0].slice(0,c)),a[2]=b;return a.slice(0,3)}},filter:{ID:d?function(a){return a=a.replace(V,""),function(b){return b.getAttribute("id")===a}}:function(a){return a=a.replace(V,""),function(b){var c=typeof b.getAttributeNode!==n&&b.getAttributeNode("id");return c&&c.value===a}},TAG:function(a){return a==="*"?function(){return!0}:(a=a.replace(V,"").toLowerCase(),function(b){return b.nodeName&&b.nodeName.toLowerCase()===a})},CLASS:function(a){var b=B[o][a];return b||(b=B(a,new RegExp("(^|"+E+")"+a+"("+E+"|$)"))),function(a){return b.test(a.className||typeof a.getAttribute!==n&&a.getAttribute("class")||"")}},ATTR:function(a,b,c){return function(d,e){var f=bc.attr(d,a);return f==null?b==="!=":b?(f+="",b==="="?f===c:b==="!="?f!==c:b==="^="?c&&f.indexOf(c)===0:b==="*="?c&&f.indexOf(c)>-1:b==="$="?c&&f.substr(f.length-c.length)===c:b==="~="?(" "+f+" ").indexOf(c)>-1:b==="|="?f===c||f.substr(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d){return a==="nth"?function(a){var b,e,f=a.parentNode;if(c===1&&d===0)return!0;if(f){e=0;for(b=f.firstChild;b;b=b.nextSibling)if(b.nodeType===1){e++;if(a===b)break}}return e-=d,e===c||e%c===0&&e/c>=0}:function(b){var c=b;switch(a){case"only":case"first":while(c=c.previousSibling)if(c.nodeType===1)return!1;if(a==="first")return!0;c=b;case"last":while(c=c.nextSibling)if(c.nodeType===1)return!1;return!0}}},PSEUDO:function(a,b){var c,d=e.pseudos[a]||e.setFilters[a.toLowerCase()]||bc.error("unsupported pseudo: "+a);return d[o]?d(b):d.length>1?(c=[a,a,"",b],e.setFilters.hasOwnProperty(a.toLowerCase())?z(function(a,c){var e,f=d(a,b),g=f.length;while(g--)e=y.call(a,f[g]),a[e]=!(c[e]=f[g])}):function(a){return d(a,0,c)}):d}},pseudos:{not:z(function(a){var b=[],c=[],d=i(a.replace(L,"$1"));return d[o]?z(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)if(f=g[h])a[h]=!(b[h]=f)}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:z(function(a){return function(b){return bc(a,b).length>0}}),contains:z(function(a){return function(b){return(b.textContent||b.innerText||f(b)).indexOf(a)>-1}}),enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&!!a.checked||b==="option"&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},parent:function(a){return!e.pseudos.empty(a)},empty:function(a){var b;a=a.firstChild;while(a){if(a.nodeName>"@"||(b=a.nodeType)===3||b===4)return!1;a=a.nextSibling}return!0},header:function(a){return T.test(a.nodeName)},text:function(a){var b,c;return a.nodeName.toLowerCase()==="input"&&(b=a.type)==="text"&&((c=a.getAttribute("type"))==null||c.toLowerCase()===b)},radio:bd("radio"),checkbox:bd("checkbox"),file:bd("file"),password:bd("password"),image:bd("image"),submit:be("submit"),reset:be("reset"),button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&a.type==="button"||b==="button"},input:function(a){return U.test(a.nodeName)},focus:function(a){var b=a.ownerDocument;return a===b.activeElement&&(!b.hasFocus||b.hasFocus())&&(!!a.type||!!a.href)},active:function(a){return a===a.ownerDocument.activeElement},first:bf(function(a,b,c){return[0]}),last:bf(function(a,b,c){return[b-1]}),eq:bf(function(a,b,c){return[c<0?c+b:c]}),even:bf(function(a,b,c){for(var d=0;d=0;)a.push(d);return a}),gt:bf(function(a,b,c){for(var d=c<0?c+b:c;++d",a.querySelectorAll("[selected]").length||e.push("\\["+E+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),a.querySelectorAll(":checked").length||e.push(":checked")}),X(function(a){a.innerHTML="

    ",a.querySelectorAll("[test^='']").length&&e.push("[*^$]="+E+"*(?:\"\"|'')"),a.innerHTML="",a.querySelectorAll(":enabled").length||e.push(":enabled",":disabled")}),e=new RegExp(e.join("|")),bp=function(a,d,f,g,h){if(!g&&!h&&(!e||!e.test(a))){var i,j,k=!0,l=o,m=d,n=d.nodeType===9&&a;if(d.nodeType===1&&d.nodeName.toLowerCase()!=="object"){i=bh(a),(k=d.getAttribute("id"))?l=k.replace(c,"\\$&"):d.setAttribute("id",l),l="[id='"+l+"'] ",j=i.length;while(j--)i[j]=l+i[j].join("");m=R.test(a)&&d.parentNode||d,n=i.join(",")}if(n)try{return w.apply(f,x.call(m.querySelectorAll(n),0)),f}catch(p){}finally{k||d.removeAttribute("id")}}return b(a,d,f,g,h)},h&&(X(function(b){a=h.call(b,"div");try{h.call(b,"[test!='']:sizzle"),f.push("!=",J)}catch(c){}}),f=new RegExp(f.join("|")),bc.matchesSelector=function(b,c){c=c.replace(d,"='$1']");if(!g(b)&&!f.test(c)&&(!e||!e.test(c)))try{var i=h.call(b,c);if(i||a||b.document&&b.document.nodeType!==11)return i}catch(j){}return bc(c,null,null,[b]).length>0})}(),e.pseudos.nth=e.pseudos.eq,e.filters=bq.prototype=e.pseudos,e.setFilters=new bq,bc.attr=p.attr,p.find=bc,p.expr=bc.selectors,p.expr[":"]=p.expr.pseudos,p.unique=bc.uniqueSort,p.text=bc.getText,p.isXMLDoc=bc.isXML,p.contains=bc.contains}(a);var bc=/Until$/,bd=/^(?:parents|prev(?:Until|All))/,be=/^.[^:#\[\.,]*$/,bf=p.expr.match.needsContext,bg={children:!0,contents:!0,next:!0,prev:!0};p.fn.extend({find:function(a){var b,c,d,e,f,g,h=this;if(typeof a!="string")return p(a).filter(function(){for(b=0,c=h.length;b0)for(e=d;e=0:p.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c,d=0,e=this.length,f=[],g=bf.test(a)||typeof a!="string"?p(a,b||this.context):0;for(;d-1:p.find.matchesSelector(c,a)){f.push(c);break}c=c.parentNode}}return f=f.length>1?p.unique(f):f,this.pushStack(f,"closest",a)},index:function(a){return a?typeof a=="string"?p.inArray(this[0],p(a)):p.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?d:p.unique(d))},addBack:function(a){return this.add(a==null?this.prevObject:this.prevObject.filter(a))}}),p.fn.andSelf=p.fn.addBack,p.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return p.dir(a,"parentNode")},parentsUntil:function(a,b,c){return p.dir(a,"parentNode",c)},next:function(a){return bi(a,"nextSibling")},prev:function(a){return bi(a,"previousSibling")},nextAll:function(a){return p.dir(a,"nextSibling")},prevAll:function(a){return p.dir(a,"previousSibling")},nextUntil:function(a,b,c){return p.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return p.dir(a,"previousSibling",c)},siblings:function(a){return p.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return p.sibling(a.firstChild)},contents:function(a){return p.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:p.merge([],a.childNodes)}},function(a,b){p.fn[a]=function(c,d){var e=p.map(this,b,c);return bc.test(a)||(d=c),d&&typeof d=="string"&&(e=p.filter(d,e)),e=this.length>1&&!bg[a]?p.unique(e):e,this.length>1&&bd.test(a)&&(e=e.reverse()),this.pushStack(e,a,k.call(arguments).join(","))}}),p.extend({filter:function(a,b,c){return c&&(a=":not("+a+")"),b.length===1?p.find.matchesSelector(b[0],a)?[b[0]]:[]:p.find.matches(a,b)},dir:function(a,c,d){var e=[],f=a[c];while(f&&f.nodeType!==9&&(d===b||f.nodeType!==1||!p(f).is(d)))f.nodeType===1&&e.push(f),f=f[c];return e},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var bl="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",bm=/ jQuery\d+="(?:null|\d+)"/g,bn=/^\s+/,bo=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bp=/<([\w:]+)/,bq=/]","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*\s*$/g,bz={option:[1,""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X
    ","
    "]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1>");try{for(;d1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=b===e&&bA,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(f=0;(h=a[f])!=null;f++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{s=s||bk(b),l=b.createElement("div"),s.appendChild(l),h=h.replace(bo,"<$1>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]===""&&!m?l.childNodes:[];for(g=n.length-1;g>=0;--g)p.nodeName(n[g],"tbody")&&!n[g].childNodes.length&&n[g].parentNode.removeChild(n[g])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l.parentNode.removeChild(l)}h.nodeType?t.push(h):p.merge(t,h)}l&&(h=l=s=null);if(!p.support.appendChecked)for(f=0;(h=t[f])!=null;f++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(f=0;(h=t[f])!=null;f++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[f+1,0].concat(r)),f+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.chrome?b.webkit=!0:b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^(none|table(?!-c[ea]).+)/,bO=/^margin/,bP=new RegExp("^("+q+")(.*)$","i"),bQ=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bR=new RegExp("^([-+])=("+q+")","i"),bS={},bT={position:"absolute",visibility:"hidden",display:"block"},bU={letterSpacing:0,fontWeight:400},bV=["Top","Right","Bottom","Left"],bW=["Webkit","O","Moz","ms"],bX=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return b$(this,!0)},hide:function(){return b$(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bX.apply(this,arguments):this.each(function(){(c?a:bZ(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bY(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bR.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bY(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bU&&(f=bU[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(b,c){var d,e,f,g,h=a.getComputedStyle(b,null),i=b.style;return h&&(d=h[c],d===""&&!p.contains(b.ownerDocument,b)&&(d=p.style(b,c)),bQ.test(d)&&bO.test(c)&&(e=i.width,f=i.minWidth,g=i.maxWidth,i.minWidth=i.maxWidth=i.width=d,d=h.width,i.width=e,i.minWidth=f,i.maxWidth=g)),d}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bQ.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth===0&&bN.test(bH(a,"display"))?p.swap(a,bT,function(){return cb(a,b,d)}):cb(a,b,d)},set:function(a,c,d){return b_(a,c,d?ca(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bQ.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bV[d]+b]=e[d]||e[d-2]||e[0];return f}},bO.test(a)||(p.cssHooks[a+b].set=b_)});var cd=/%20/g,ce=/\[\]$/,cf=/\r?\n/g,cg=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,ch=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ch.test(this.nodeName)||cg.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(cf,"\r\n")}}):{name:b.name,value:c.replace(cf,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ci(d,a[d],c,f);return e.join("&").replace(cd,"+")};var cj,ck,cl=/#.*$/,cm=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,co=/^(?:GET|HEAD)$/,cp=/^\/\//,cq=/\?/,cr=/)<[^<]*)*<\/script>/gi,cs=/([?&])_=[^&]*/,ct=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,cu=p.fn.load,cv={},cw={},cx=["*/"]+["*"];try{ck=f.href}catch(cy){ck=e.createElement("a"),ck.href="",ck=ck.href}cj=ct.exec(ck.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&cu)return cu.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):c&&typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("
    ").append(a.replace(cr,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cB(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cB(a,b),a},ajaxSettings:{url:ck,isLocal:cn.test(cj[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cx},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cz(cv),ajaxTransport:cz(cw),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cC(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cD(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=(c||y)+"",k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cm.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(cl,"").replace(cp,cj[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=ct.exec(l.url.toLowerCase())||!1,l.crossDomain=i&&i.join(":")+(i[3]?"":i[1]==="http:"?80:443)!==cj.join(":")+(cj[3]?"":cj[1]==="http:"?80:443)),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cA(cv,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!co.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cq.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cs,"$1_="+z);l.url=A+(A===l.url?(cq.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cx+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cA(cw,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cE=[],cF=/\?/,cG=/(=)\?(?=&|$)|\?\?/,cH=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cE.pop()||p.expando+"_"+cH++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cG.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cG.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cG,"$1"+f):m?c.data=i.replace(cG,"$1"+f):k&&(c.url+=(cF.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cE.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cI,cJ=a.ActiveXObject?function(){for(var a in cI)cI[a](0,1)}:!1,cK=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cL()||cM()}:cL,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cJ&&delete cI[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cK,cJ&&(cI||(cI={},p(a).unload(cJ)),cI[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cN,cO,cP=/^(?:toggle|show|hide)$/,cQ=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cR=/queueHooks$/,cS=[cY],cT={"*":[function(a,b){var c,d,e=this.createTween(a,b),f=cQ.exec(b),g=e.cur(),h=+g||0,i=1,j=20;if(f){c=+f[2],d=f[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&h){h=p.css(e.elem,a,!0)||c||1;do i=i||".5",h=h/i,p.style(e.elem,a,h+d);while(i!==(i=e.cur()/g)&&i!==1&&--j)}e.unit=d,e.start=h,e.end=f[1]?h+(f[1]+1)*c:c}return e}]};p.Animation=p.extend(cW,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c_.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c_.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=da(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g,null)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window); \ No newline at end of file +/*! jQuery v1.8.3 jquery.com | jquery.org/license */ +(function(e,t){function _(e){var t=M[e]={};return v.each(e.split(y),function(e,n){t[n]=!0}),t}function H(e,n,r){if(r===t&&e.nodeType===1){var i="data-"+n.replace(P,"-$1").toLowerCase();r=e.getAttribute(i);if(typeof r=="string"){try{r=r==="true"?!0:r==="false"?!1:r==="null"?null:+r+""===r?+r:D.test(r)?v.parseJSON(r):r}catch(s){}v.data(e,n,r)}else r=t}return r}function B(e){var t;for(t in e){if(t==="data"&&v.isEmptyObject(e[t]))continue;if(t!=="toJSON")return!1}return!0}function et(){return!1}function tt(){return!0}function ut(e){return!e||!e.parentNode||e.parentNode.nodeType===11}function at(e,t){do e=e[t];while(e&&e.nodeType!==1);return e}function ft(e,t,n){t=t||0;if(v.isFunction(t))return v.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return v.grep(e,function(e,r){return e===t===n});if(typeof t=="string"){var r=v.grep(e,function(e){return e.nodeType===1});if(it.test(t))return v.filter(t,r,!n);t=v.filter(t,r)}return v.grep(e,function(e,r){return v.inArray(e,t)>=0===n})}function lt(e){var t=ct.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function At(e,t){if(t.nodeType!==1||!v.hasData(e))return;var n,r,i,s=v._data(e),o=v._data(t,s),u=s.events;if(u){delete o.handle,o.events={};for(n in u)for(r=0,i=u[n].length;r").appendTo(i.body),n=t.css("display");t.remove();if(n==="none"||n===""){Pt=i.body.appendChild(Pt||v.extend(i.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!Ht||!Pt.createElement)Ht=(Pt.contentWindow||Pt.contentDocument).document,Ht.write(""),Ht.close();t=Ht.body.appendChild(Ht.createElement(e)),n=Dt(t,"display"),i.body.removeChild(Pt)}return Wt[e]=n,n}function fn(e,t,n,r){var i;if(v.isArray(t))v.each(t,function(t,i){n||sn.test(e)?r(e,i):fn(e+"["+(typeof i=="object"?t:"")+"]",i,n,r)});else if(!n&&v.type(t)==="object")for(i in t)fn(e+"["+i+"]",t[i],n,r);else r(e,t)}function Cn(e){return function(t,n){typeof t!="string"&&(n=t,t="*");var r,i,s,o=t.toLowerCase().split(y),u=0,a=o.length;if(v.isFunction(n))for(;u)[^>]*$|#([\w\-]*)$)/,E=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,S=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,T=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,N=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,C=/^-ms-/,k=/-([\da-z])/gi,L=function(e,t){return(t+"").toUpperCase()},A=function(){i.addEventListener?(i.removeEventListener("DOMContentLoaded",A,!1),v.ready()):i.readyState==="complete"&&(i.detachEvent("onreadystatechange",A),v.ready())},O={};v.fn=v.prototype={constructor:v,init:function(e,n,r){var s,o,u,a;if(!e)return this;if(e.nodeType)return this.context=this[0]=e,this.length=1,this;if(typeof e=="string"){e.charAt(0)==="<"&&e.charAt(e.length-1)===">"&&e.length>=3?s=[null,e,null]:s=w.exec(e);if(s&&(s[1]||!n)){if(s[1])return n=n instanceof v?n[0]:n,a=n&&n.nodeType?n.ownerDocument||n:i,e=v.parseHTML(s[1],a,!0),E.test(s[1])&&v.isPlainObject(n)&&this.attr.call(e,n,!0),v.merge(this,e);o=i.getElementById(s[2]);if(o&&o.parentNode){if(o.id!==s[2])return r.find(e);this.length=1,this[0]=o}return this.context=i,this.selector=e,this}return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e)}return v.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),v.makeArray(e,this))},selector:"",jquery:"1.8.3",length:0,size:function(){return this.length},toArray:function(){return l.call(this)},get:function(e){return e==null?this.toArray():e<0?this[this.length+e]:this[e]},pushStack:function(e,t,n){var r=v.merge(this.constructor(),e);return r.prevObject=this,r.context=this.context,t==="find"?r.selector=this.selector+(this.selector?" ":"")+n:t&&(r.selector=this.selector+"."+t+"("+n+")"),r},each:function(e,t){return v.each(this,e,t)},ready:function(e){return v.ready.promise().done(e),this},eq:function(e){return e=+e,e===-1?this.slice(e):this.slice(e,e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(l.apply(this,arguments),"slice",l.call(arguments).join(","))},map:function(e){return this.pushStack(v.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:[].sort,splice:[].splice},v.fn.init.prototype=v.fn,v.extend=v.fn.extend=function(){var e,n,r,i,s,o,u=arguments[0]||{},a=1,f=arguments.length,l=!1;typeof u=="boolean"&&(l=u,u=arguments[1]||{},a=2),typeof u!="object"&&!v.isFunction(u)&&(u={}),f===a&&(u=this,--a);for(;a0)return;r.resolveWith(i,[v]),v.fn.trigger&&v(i).trigger("ready").off("ready")},isFunction:function(e){return v.type(e)==="function"},isArray:Array.isArray||function(e){return v.type(e)==="array"},isWindow:function(e){return e!=null&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return e==null?String(e):O[h.call(e)]||"object"},isPlainObject:function(e){if(!e||v.type(e)!=="object"||e.nodeType||v.isWindow(e))return!1;try{if(e.constructor&&!p.call(e,"constructor")&&!p.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||p.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw new Error(e)},parseHTML:function(e,t,n){var r;return!e||typeof e!="string"?null:(typeof t=="boolean"&&(n=t,t=0),t=t||i,(r=E.exec(e))?[t.createElement(r[1])]:(r=v.buildFragment([e],t,n?null:[]),v.merge([],(r.cacheable?v.clone(r.fragment):r.fragment).childNodes)))},parseJSON:function(t){if(!t||typeof t!="string")return null;t=v.trim(t);if(e.JSON&&e.JSON.parse)return e.JSON.parse(t);if(S.test(t.replace(T,"@").replace(N,"]").replace(x,"")))return(new Function("return "+t))();v.error("Invalid JSON: "+t)},parseXML:function(n){var r,i;if(!n||typeof n!="string")return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(s){r=t}return(!r||!r.documentElement||r.getElementsByTagName("parsererror").length)&&v.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&g.test(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(C,"ms-").replace(k,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,n,r){var i,s=0,o=e.length,u=o===t||v.isFunction(e);if(r){if(u){for(i in e)if(n.apply(e[i],r)===!1)break}else for(;s0&&e[0]&&e[a-1]||a===0||v.isArray(e));if(f)for(;u-1)a.splice(n,1),i&&(n<=o&&o--,n<=u&&u--)}),this},has:function(e){return v.inArray(e,a)>-1},empty:function(){return a=[],this},disable:function(){return a=f=n=t,this},disabled:function(){return!a},lock:function(){return f=t,n||c.disable(),this},locked:function(){return!f},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],a&&(!r||f)&&(i?f.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},v.extend({Deferred:function(e){var t=[["resolve","done",v.Callbacks("once memory"),"resolved"],["reject","fail",v.Callbacks("once memory"),"rejected"],["notify","progress",v.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return v.Deferred(function(n){v.each(t,function(t,r){var s=r[0],o=e[t];i[r[1]](v.isFunction(o)?function(){var e=o.apply(this,arguments);e&&v.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===i?n:this,[e])}:n[s])}),e=null}).promise()},promise:function(e){return e!=null?v.extend(e,r):r}},i={};return r.pipe=r.then,v.each(t,function(e,s){var o=s[2],u=s[3];r[s[1]]=o.add,u&&o.add(function(){n=u},t[e^1][2].disable,t[2][2].lock),i[s[0]]=o.fire,i[s[0]+"With"]=o.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=l.call(arguments),r=n.length,i=r!==1||e&&v.isFunction(e.promise)?r:0,s=i===1?e:v.Deferred(),o=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?l.call(arguments):r,n===u?s.notifyWith(t,n):--i||s.resolveWith(t,n)}},u,a,f;if(r>1){u=new Array(r),a=new Array(r),f=new Array(r);for(;t
    a",n=p.getElementsByTagName("*"),r=p.getElementsByTagName("a")[0];if(!n||!r||!n.length)return{};s=i.createElement("select"),o=s.appendChild(i.createElement("option")),u=p.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:r.getAttribute("href")==="/a",opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:u.value==="on",optSelected:o.selected,getSetAttribute:p.className!=="t",enctype:!!i.createElement("form").enctype,html5Clone:i.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:i.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},u.checked=!0,t.noCloneChecked=u.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!o.disabled;try{delete p.test}catch(d){t.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",h=function(){t.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick"),p.detachEvent("onclick",h)),u=i.createElement("input"),u.value="t",u.setAttribute("type","radio"),t.radioValue=u.value==="t",u.setAttribute("checked","checked"),u.setAttribute("name","t"),p.appendChild(u),a=i.createDocumentFragment(),a.appendChild(p.lastChild),t.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,t.appendChecked=u.checked,a.removeChild(u),a.appendChild(p);if(p.attachEvent)for(l in{submit:!0,change:!0,focusin:!0})f="on"+l,c=f in p,c||(p.setAttribute(f,"return;"),c=typeof p[f]=="function"),t[l+"Bubbles"]=c;return v(function(){var n,r,s,o,u="padding:0;margin:0;border:0;display:block;overflow:hidden;",a=i.getElementsByTagName("body")[0];if(!a)return;n=i.createElement("div"),n.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",a.insertBefore(n,a.firstChild),r=i.createElement("div"),n.appendChild(r),r.innerHTML="
    t
    ",s=r.getElementsByTagName("td"),s[0].style.cssText="padding:0;margin:0;border:0;display:none",c=s[0].offsetHeight===0,s[0].style.display="",s[1].style.display="none",t.reliableHiddenOffsets=c&&s[0].offsetHeight===0,r.innerHTML="",r.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=r.offsetWidth===4,t.doesNotIncludeMarginInBodyOffset=a.offsetTop!==1,e.getComputedStyle&&(t.pixelPosition=(e.getComputedStyle(r,null)||{}).top!=="1%",t.boxSizingReliable=(e.getComputedStyle(r,null)||{width:"4px"}).width==="4px",o=i.createElement("div"),o.style.cssText=r.style.cssText=u,o.style.marginRight=o.style.width="0",r.style.width="1px",r.appendChild(o),t.reliableMarginRight=!parseFloat((e.getComputedStyle(o,null)||{}).marginRight)),typeof r.style.zoom!="undefined"&&(r.innerHTML="",r.style.cssText=u+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=r.offsetWidth===3,r.style.display="block",r.style.overflow="visible",r.innerHTML="
    ",r.firstChild.style.width="5px",t.shrinkWrapBlocks=r.offsetWidth!==3,n.style.zoom=1),a.removeChild(n),n=r=s=o=null}),a.removeChild(p),n=r=s=o=u=a=p=null,t}();var D=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;v.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(v.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?v.cache[e[v.expando]]:e[v.expando],!!e&&!B(e)},data:function(e,n,r,i){if(!v.acceptData(e))return;var s,o,u=v.expando,a=typeof n=="string",f=e.nodeType,l=f?v.cache:e,c=f?e[u]:e[u]&&u;if((!c||!l[c]||!i&&!l[c].data)&&a&&r===t)return;c||(f?e[u]=c=v.deletedIds.pop()||v.guid++:c=u),l[c]||(l[c]={},f||(l[c].toJSON=v.noop));if(typeof n=="object"||typeof n=="function")i?l[c]=v.extend(l[c],n):l[c].data=v.extend(l[c].data,n);return s=l[c],i||(s.data||(s.data={}),s=s.data),r!==t&&(s[v.camelCase(n)]=r),a?(o=s[n],o==null&&(o=s[v.camelCase(n)])):o=s,o},removeData:function(e,t,n){if(!v.acceptData(e))return;var r,i,s,o=e.nodeType,u=o?v.cache:e,a=o?e[v.expando]:v.expando;if(!u[a])return;if(t){r=n?u[a]:u[a].data;if(r){v.isArray(t)||(t in r?t=[t]:(t=v.camelCase(t),t in r?t=[t]:t=t.split(" ")));for(i=0,s=t.length;i1,null,!1))},removeData:function(e){return this.each(function(){v.removeData(this,e)})}}),v.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=v._data(e,t),n&&(!r||v.isArray(n)?r=v._data(e,t,v.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=v.queue(e,t),r=n.length,i=n.shift(),s=v._queueHooks(e,t),o=function(){v.dequeue(e,t)};i==="inprogress"&&(i=n.shift(),r--),i&&(t==="fx"&&n.unshift("inprogress"),delete s.stop,i.call(e,o,s)),!r&&s&&s.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return v._data(e,n)||v._data(e,n,{empty:v.Callbacks("once memory").add(function(){v.removeData(e,t+"queue",!0),v.removeData(e,n,!0)})})}}),v.fn.extend({queue:function(e,n){var r=2;return typeof e!="string"&&(n=e,e="fx",r--),arguments.length1)},removeAttr:function(e){return this.each(function(){v.removeAttr(this,e)})},prop:function(e,t){return v.access(this,v.prop,e,t,arguments.length>1)},removeProp:function(e){return e=v.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,s,o,u;if(v.isFunction(e))return this.each(function(t){v(this).addClass(e.call(this,t,this.className))});if(e&&typeof e=="string"){t=e.split(y);for(n=0,r=this.length;n=0)r=r.replace(" "+n[s]+" "," ");i.className=e?v.trim(r):""}}}return this},toggleClass:function(e,t){var n=typeof e,r=typeof t=="boolean";return v.isFunction(e)?this.each(function(n){v(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if(n==="string"){var i,s=0,o=v(this),u=t,a=e.split(y);while(i=a[s++])u=r?u:!o.hasClass(i),o[u?"addClass":"removeClass"](i)}else if(n==="undefined"||n==="boolean")this.className&&v._data(this,"__className__",this.className),this.className=this.className||e===!1?"":v._data(this,"__className__")||""})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;n=0)return!0;return!1},val:function(e){var n,r,i,s=this[0];if(!arguments.length){if(s)return n=v.valHooks[s.type]||v.valHooks[s.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(s,"value"))!==t?r:(r=s.value,typeof r=="string"?r.replace(R,""):r==null?"":r);return}return i=v.isFunction(e),this.each(function(r){var s,o=v(this);if(this.nodeType!==1)return;i?s=e.call(this,r,o.val()):s=e,s==null?s="":typeof s=="number"?s+="":v.isArray(s)&&(s=v.map(s,function(e){return e==null?"":e+""})),n=v.valHooks[this.type]||v.valHooks[this.nodeName.toLowerCase()];if(!n||!("set"in n)||n.set(this,s,"value")===t)this.value=s})}}),v.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,s=e.type==="select-one"||i<0,o=s?null:[],u=s?i+1:r.length,a=i<0?u:s?i:0;for(;a=0}),n.length||(e.selectedIndex=-1),n}}},attrFn:{},attr:function(e,n,r,i){var s,o,u,a=e.nodeType;if(!e||a===3||a===8||a===2)return;if(i&&v.isFunction(v.fn[n]))return v(e)[n](r);if(typeof e.getAttribute=="undefined")return v.prop(e,n,r);u=a!==1||!v.isXMLDoc(e),u&&(n=n.toLowerCase(),o=v.attrHooks[n]||(X.test(n)?F:j));if(r!==t){if(r===null){v.removeAttr(e,n);return}return o&&"set"in o&&u&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r)}return o&&"get"in o&&u&&(s=o.get(e,n))!==null?s:(s=e.getAttribute(n),s===null?t:s)},removeAttr:function(e,t){var n,r,i,s,o=0;if(t&&e.nodeType===1){r=t.split(y);for(;o=0}})});var $=/^(?:textarea|input|select)$/i,J=/^([^\.]*|)(?:\.(.+)|)$/,K=/(?:^|\s)hover(\.\S+|)\b/,Q=/^key/,G=/^(?:mouse|contextmenu)|click/,Y=/^(?:focusinfocus|focusoutblur)$/,Z=function(e){return v.event.special.hover?e:e.replace(K,"mouseenter$1 mouseleave$1")};v.event={add:function(e,n,r,i,s){var o,u,a,f,l,c,h,p,d,m,g;if(e.nodeType===3||e.nodeType===8||!n||!r||!(o=v._data(e)))return;r.handler&&(d=r,r=d.handler,s=d.selector),r.guid||(r.guid=v.guid++),a=o.events,a||(o.events=a={}),u=o.handle,u||(o.handle=u=function(e){return typeof v=="undefined"||!!e&&v.event.triggered===e.type?t:v.event.dispatch.apply(u.elem,arguments)},u.elem=e),n=v.trim(Z(n)).split(" ");for(f=0;f=0&&(y=y.slice(0,-1),a=!0),y.indexOf(".")>=0&&(b=y.split("."),y=b.shift(),b.sort());if((!s||v.event.customEvent[y])&&!v.event.global[y])return;n=typeof n=="object"?n[v.expando]?n:new v.Event(y,n):new v.Event(y),n.type=y,n.isTrigger=!0,n.exclusive=a,n.namespace=b.join("."),n.namespace_re=n.namespace?new RegExp("(^|\\.)"+b.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,h=y.indexOf(":")<0?"on"+y:"";if(!s){u=v.cache;for(f in u)u[f].events&&u[f].events[y]&&v.event.trigger(n,r,u[f].handle.elem,!0);return}n.result=t,n.target||(n.target=s),r=r!=null?v.makeArray(r):[],r.unshift(n),p=v.event.special[y]||{};if(p.trigger&&p.trigger.apply(s,r)===!1)return;m=[[s,p.bindType||y]];if(!o&&!p.noBubble&&!v.isWindow(s)){g=p.delegateType||y,l=Y.test(g+y)?s:s.parentNode;for(c=s;l;l=l.parentNode)m.push([l,g]),c=l;c===(s.ownerDocument||i)&&m.push([c.defaultView||c.parentWindow||e,g])}for(f=0;f=0:v.find(h,this,null,[s]).length),u[h]&&f.push(c);f.length&&w.push({elem:s,matches:f})}d.length>m&&w.push({elem:this,matches:d.slice(m)});for(r=0;r0?this.on(t,null,e,n):this.trigger(t)},Q.test(t)&&(v.event.fixHooks[t]=v.event.keyHooks),G.test(t)&&(v.event.fixHooks[t]=v.event.mouseHooks)}),function(e,t){function nt(e,t,n,r){n=n||[],t=t||g;var i,s,a,f,l=t.nodeType;if(!e||typeof e!="string")return n;if(l!==1&&l!==9)return[];a=o(t);if(!a&&!r)if(i=R.exec(e))if(f=i[1]){if(l===9){s=t.getElementById(f);if(!s||!s.parentNode)return n;if(s.id===f)return n.push(s),n}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(f))&&u(t,s)&&s.id===f)return n.push(s),n}else{if(i[2])return S.apply(n,x.call(t.getElementsByTagName(e),0)),n;if((f=i[3])&&Z&&t.getElementsByClassName)return S.apply(n,x.call(t.getElementsByClassName(f),0)),n}return vt(e.replace(j,"$1"),t,n,r,a)}function rt(e){return function(t){var n=t.nodeName.toLowerCase();return n==="input"&&t.type===e}}function it(e){return function(t){var n=t.nodeName.toLowerCase();return(n==="input"||n==="button")&&t.type===e}}function st(e){return N(function(t){return t=+t,N(function(n,r){var i,s=e([],n.length,t),o=s.length;while(o--)n[i=s[o]]&&(n[i]=!(r[i]=n[i]))})})}function ot(e,t,n){if(e===t)return n;var r=e.nextSibling;while(r){if(r===t)return-1;r=r.nextSibling}return 1}function ut(e,t){var n,r,s,o,u,a,f,l=L[d][e+" "];if(l)return t?0:l.slice(0);u=e,a=[],f=i.preFilter;while(u){if(!n||(r=F.exec(u)))r&&(u=u.slice(r[0].length)||u),a.push(s=[]);n=!1;if(r=I.exec(u))s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=r[0].replace(j," ");for(o in i.filter)(r=J[o].exec(u))&&(!f[o]||(r=f[o](r)))&&(s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=o,n.matches=r);if(!n)break}return t?u.length:u?nt.error(e):L(e,a).slice(0)}function at(e,t,r){var i=t.dir,s=r&&t.dir==="parentNode",o=w++;return t.first?function(t,n,r){while(t=t[i])if(s||t.nodeType===1)return e(t,n,r)}:function(t,r,u){if(!u){var a,f=b+" "+o+" ",l=f+n;while(t=t[i])if(s||t.nodeType===1){if((a=t[d])===l)return t.sizset;if(typeof a=="string"&&a.indexOf(f)===0){if(t.sizset)return t}else{t[d]=l;if(e(t,r,u))return t.sizset=!0,t;t.sizset=!1}}}else while(t=t[i])if(s||t.nodeType===1)if(e(t,r,u))return t}}function ft(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function lt(e,t,n,r,i){var s,o=[],u=0,a=e.length,f=t!=null;for(;u-1&&(s[f]=!(o[f]=c))}}else g=lt(g===o?g.splice(d,g.length):g),i?i(null,o,g,a):S.apply(o,g)})}function ht(e){var t,n,r,s=e.length,o=i.relative[e[0].type],u=o||i.relative[" "],a=o?1:0,f=at(function(e){return e===t},u,!0),l=at(function(e){return T.call(t,e)>-1},u,!0),h=[function(e,n,r){return!o&&(r||n!==c)||((t=n).nodeType?f(e,n,r):l(e,n,r))}];for(;a1&&ft(h),a>1&&e.slice(0,a-1).join("").replace(j,"$1"),n,a0,s=e.length>0,o=function(u,a,f,l,h){var p,d,v,m=[],y=0,w="0",x=u&&[],T=h!=null,N=c,C=u||s&&i.find.TAG("*",h&&a.parentNode||a),k=b+=N==null?1:Math.E;T&&(c=a!==g&&a,n=o.el);for(;(p=C[w])!=null;w++){if(s&&p){for(d=0;v=e[d];d++)if(v(p,a,f)){l.push(p);break}T&&(b=k,n=++o.el)}r&&((p=!v&&p)&&y--,u&&x.push(p))}y+=w;if(r&&w!==y){for(d=0;v=t[d];d++)v(x,m,a,f);if(u){if(y>0)while(w--)!x[w]&&!m[w]&&(m[w]=E.call(l));m=lt(m)}S.apply(l,m),T&&!u&&m.length>0&&y+t.length>1&&nt.uniqueSort(l)}return T&&(b=k,c=N),x};return o.el=0,r?N(o):o}function dt(e,t,n){var r=0,i=t.length;for(;r2&&(f=u[0]).type==="ID"&&t.nodeType===9&&!s&&i.relative[u[1].type]){t=i.find.ID(f.matches[0].replace($,""),t,s)[0];if(!t)return n;e=e.slice(u.shift().length)}for(o=J.POS.test(e)?-1:u.length-1;o>=0;o--){f=u[o];if(i.relative[l=f.type])break;if(c=i.find[l])if(r=c(f.matches[0].replace($,""),z.test(u[0].type)&&t.parentNode||t,s)){u.splice(o,1),e=r.length&&u.join("");if(!e)return S.apply(n,x.call(r,0)),n;break}}}return a(e,h)(r,t,s,n,z.test(e)),n}function mt(){}var n,r,i,s,o,u,a,f,l,c,h=!0,p="undefined",d=("sizcache"+Math.random()).replace(".",""),m=String,g=e.document,y=g.documentElement,b=0,w=0,E=[].pop,S=[].push,x=[].slice,T=[].indexOf||function(e){var t=0,n=this.length;for(;ti.cacheLength&&delete e[t.shift()],e[n+" "]=r},e)},k=C(),L=C(),A=C(),O="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",_=M.replace("w","w#"),D="([*^$|!~]?=)",P="\\["+O+"*("+M+")"+O+"*(?:"+D+O+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+_+")|)|)"+O+"*\\]",H=":("+M+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+P+")|[^:]|\\\\.)*|.*))\\)|)",B=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+O+"*((?:-\\d)?\\d*)"+O+"*\\)|)(?=[^-]|$)",j=new RegExp("^"+O+"+|((?:^|[^\\\\])(?:\\\\.)*)"+O+"+$","g"),F=new RegExp("^"+O+"*,"+O+"*"),I=new RegExp("^"+O+"*([\\x20\\t\\r\\n\\f>+~])"+O+"*"),q=new RegExp(H),R=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,U=/^:not/,z=/[\x20\t\r\n\f]*[+~]/,W=/:not\($/,X=/h\d/i,V=/input|select|textarea|button/i,$=/\\(?!\\)/g,J={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),NAME:new RegExp("^\\[name=['\"]?("+M+")['\"]?\\]"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+H),POS:new RegExp(B,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+O+"*(even|odd|(([+-]|)(\\d*)n|)"+O+"*(?:([+-]|)"+O+"*(\\d+)|))"+O+"*\\)|)","i"),needsContext:new RegExp("^"+O+"*[>+~]|"+B,"i")},K=function(e){var t=g.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}},Q=K(function(e){return e.appendChild(g.createComment("")),!e.getElementsByTagName("*").length}),G=K(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==p&&e.firstChild.getAttribute("href")==="#"}),Y=K(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return t!=="boolean"&&t!=="string"}),Z=K(function(e){return e.innerHTML="",!e.getElementsByClassName||!e.getElementsByClassName("e").length?!1:(e.lastChild.className="e",e.getElementsByClassName("e").length===2)}),et=K(function(e){e.id=d+0,e.innerHTML="
    ",y.insertBefore(e,y.firstChild);var t=g.getElementsByName&&g.getElementsByName(d).length===2+g.getElementsByName(d+0).length;return r=!g.getElementById(d),y.removeChild(e),t});try{x.call(y.childNodes,0)[0].nodeType}catch(tt){x=function(e){var t,n=[];for(;t=this[e];e++)n.push(t);return n}}nt.matches=function(e,t){return nt(e,null,null,t)},nt.matchesSelector=function(e,t){return nt(t,null,null,[e]).length>0},s=nt.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(i===1||i===9||i===11){if(typeof e.textContent=="string")return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=s(e)}else if(i===3||i===4)return e.nodeValue}else for(;t=e[r];r++)n+=s(t);return n},o=nt.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?t.nodeName!=="HTML":!1},u=nt.contains=y.contains?function(e,t){var n=e.nodeType===9?e.documentElement:e,r=t&&t.parentNode;return e===r||!!(r&&r.nodeType===1&&n.contains&&n.contains(r))}:y.compareDocumentPosition?function(e,t){return t&&!!(e.compareDocumentPosition(t)&16)}:function(e,t){while(t=t.parentNode)if(t===e)return!0;return!1},nt.attr=function(e,t){var n,r=o(e);return r||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):r||Y?e.getAttribute(t):(n=e.getAttributeNode(t),n?typeof e[t]=="boolean"?e[t]?t:null:n.specified?n.value:null:null)},i=nt.selectors={cacheLength:50,createPseudo:N,match:J,attrHandle:G?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},find:{ID:r?function(e,t,n){if(typeof t.getElementById!==p&&!n){var r=t.getElementById(e);return r&&r.parentNode?[r]:[]}}:function(e,n,r){if(typeof n.getElementById!==p&&!r){var i=n.getElementById(e);return i?i.id===e||typeof i.getAttributeNode!==p&&i.getAttributeNode("id").value===e?[i]:t:[]}},TAG:Q?function(e,t){if(typeof t.getElementsByTagName!==p)return t.getElementsByTagName(e)}:function(e,t){var n=t.getElementsByTagName(e);if(e==="*"){var r,i=[],s=0;for(;r=n[s];s++)r.nodeType===1&&i.push(r);return i}return n},NAME:et&&function(e,t){if(typeof t.getElementsByName!==p)return t.getElementsByName(name)},CLASS:Z&&function(e,t,n){if(typeof t.getElementsByClassName!==p&&!n)return t.getElementsByClassName(e)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace($,""),e[3]=(e[4]||e[5]||"").replace($,""),e[2]==="~="&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),e[1]==="nth"?(e[2]||nt.error(e[0]),e[3]=+(e[3]?e[4]+(e[5]||1):2*(e[2]==="even"||e[2]==="odd")),e[4]=+(e[6]+e[7]||e[2]==="odd")):e[2]&&nt.error(e[0]),e},PSEUDO:function(e){var t,n;if(J.CHILD.test(e[0]))return null;if(e[3])e[2]=e[3];else if(t=e[4])q.test(t)&&(n=ut(t,!0))&&(n=t.indexOf(")",t.length-n)-t.length)&&(t=t.slice(0,n),e[0]=e[0].slice(0,n)),e[2]=t;return e.slice(0,3)}},filter:{ID:r?function(e){return e=e.replace($,""),function(t){return t.getAttribute("id")===e}}:function(e){return e=e.replace($,""),function(t){var n=typeof t.getAttributeNode!==p&&t.getAttributeNode("id");return n&&n.value===e}},TAG:function(e){return e==="*"?function(){return!0}:(e=e.replace($,"").toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[d][e+" "];return t||(t=new RegExp("(^|"+O+")"+e+"("+O+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==p&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r,i){var s=nt.attr(r,e);return s==null?t==="!=":t?(s+="",t==="="?s===n:t==="!="?s!==n:t==="^="?n&&s.indexOf(n)===0:t==="*="?n&&s.indexOf(n)>-1:t==="$="?n&&s.substr(s.length-n.length)===n:t==="~="?(" "+s+" ").indexOf(n)>-1:t==="|="?s===n||s.substr(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r){return e==="nth"?function(e){var t,i,s=e.parentNode;if(n===1&&r===0)return!0;if(s){i=0;for(t=s.firstChild;t;t=t.nextSibling)if(t.nodeType===1){i++;if(e===t)break}}return i-=r,i===n||i%n===0&&i/n>=0}:function(t){var n=t;switch(e){case"only":case"first":while(n=n.previousSibling)if(n.nodeType===1)return!1;if(e==="first")return!0;n=t;case"last":while(n=n.nextSibling)if(n.nodeType===1)return!1;return!0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||nt.error("unsupported pseudo: "+e);return r[d]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?N(function(e,n){var i,s=r(e,t),o=s.length;while(o--)i=T.call(e,s[o]),e[i]=!(n[i]=s[o])}):function(e){return r(e,0,n)}):r}},pseudos:{not:N(function(e){var t=[],n=[],r=a(e.replace(j,"$1"));return r[d]?N(function(e,t,n,i){var s,o=r(e,null,i,[]),u=e.length;while(u--)if(s=o[u])e[u]=!(t[u]=s)}):function(e,i,s){return t[0]=e,r(t,null,s,n),!n.pop()}}),has:N(function(e){return function(t){return nt(e,t).length>0}}),contains:N(function(e){return function(t){return(t.textContent||t.innerText||s(t)).indexOf(e)>-1}}),enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&!!e.checked||t==="option"&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},parent:function(e){return!i.pseudos.empty(e)},empty:function(e){var t;e=e.firstChild;while(e){if(e.nodeName>"@"||(t=e.nodeType)===3||t===4)return!1;e=e.nextSibling}return!0},header:function(e){return X.test(e.nodeName)},text:function(e){var t,n;return e.nodeName.toLowerCase()==="input"&&(t=e.type)==="text"&&((n=e.getAttribute("type"))==null||n.toLowerCase()===t)},radio:rt("radio"),checkbox:rt("checkbox"),file:rt("file"),password:rt("password"),image:rt("image"),submit:it("submit"),reset:it("reset"),button:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&e.type==="button"||t==="button"},input:function(e){return V.test(e.nodeName)},focus:function(e){var t=e.ownerDocument;return e===t.activeElement&&(!t.hasFocus||t.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},active:function(e){return e===e.ownerDocument.activeElement},first:st(function(){return[0]}),last:st(function(e,t){return[t-1]}),eq:st(function(e,t,n){return[n<0?n+t:n]}),even:st(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:st(function(e,t,n){for(var r=n<0?n+t:n;++r",e.querySelectorAll("[selected]").length||i.push("\\["+O+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||i.push(":checked")}),K(function(e){e.innerHTML="

    ",e.querySelectorAll("[test^='']").length&&i.push("[*^$]="+O+"*(?:\"\"|'')"),e.innerHTML="",e.querySelectorAll(":enabled").length||i.push(":enabled",":disabled")}),i=new RegExp(i.join("|")),vt=function(e,r,s,o,u){if(!o&&!u&&!i.test(e)){var a,f,l=!0,c=d,h=r,p=r.nodeType===9&&e;if(r.nodeType===1&&r.nodeName.toLowerCase()!=="object"){a=ut(e),(l=r.getAttribute("id"))?c=l.replace(n,"\\$&"):r.setAttribute("id",c),c="[id='"+c+"'] ",f=a.length;while(f--)a[f]=c+a[f].join("");h=z.test(e)&&r.parentNode||r,p=a.join(",")}if(p)try{return S.apply(s,x.call(h.querySelectorAll(p),0)),s}catch(v){}finally{l||r.removeAttribute("id")}}return t(e,r,s,o,u)},u&&(K(function(t){e=u.call(t,"div");try{u.call(t,"[test!='']:sizzle"),s.push("!=",H)}catch(n){}}),s=new RegExp(s.join("|")),nt.matchesSelector=function(t,n){n=n.replace(r,"='$1']");if(!o(t)&&!s.test(n)&&!i.test(n))try{var a=u.call(t,n);if(a||e||t.document&&t.document.nodeType!==11)return a}catch(f){}return nt(n,null,null,[t]).length>0})}(),i.pseudos.nth=i.pseudos.eq,i.filters=mt.prototype=i.pseudos,i.setFilters=new mt,nt.attr=v.attr,v.find=nt,v.expr=nt.selectors,v.expr[":"]=v.expr.pseudos,v.unique=nt.uniqueSort,v.text=nt.getText,v.isXMLDoc=nt.isXML,v.contains=nt.contains}(e);var nt=/Until$/,rt=/^(?:parents|prev(?:Until|All))/,it=/^.[^:#\[\.,]*$/,st=v.expr.match.needsContext,ot={children:!0,contents:!0,next:!0,prev:!0};v.fn.extend({find:function(e){var t,n,r,i,s,o,u=this;if(typeof e!="string")return v(e).filter(function(){for(t=0,n=u.length;t0)for(i=r;i=0:v.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,s=[],o=st.test(e)||typeof e!="string"?v(e,t||this.context):0;for(;r-1:v.find.matchesSelector(n,e)){s.push(n);break}n=n.parentNode}}return s=s.length>1?v.unique(s):s,this.pushStack(s,"closest",e)},index:function(e){return e?typeof e=="string"?v.inArray(this[0],v(e)):v.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(e,t){var n=typeof e=="string"?v(e,t):v.makeArray(e&&e.nodeType?[e]:e),r=v.merge(this.get(),n);return this.pushStack(ut(n[0])||ut(r[0])?r:v.unique(r))},addBack:function(e){return this.add(e==null?this.prevObject:this.prevObject.filter(e))}}),v.fn.andSelf=v.fn.addBack,v.each({parent:function(e){var t=e.parentNode;return t&&t.nodeType!==11?t:null},parents:function(e){return v.dir(e,"parentNode")},parentsUntil:function(e,t,n){return v.dir(e,"parentNode",n)},next:function(e){return at(e,"nextSibling")},prev:function(e){return at(e,"previousSibling")},nextAll:function(e){return v.dir(e,"nextSibling")},prevAll:function(e){return v.dir(e,"previousSibling")},nextUntil:function(e,t,n){return v.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return v.dir(e,"previousSibling",n)},siblings:function(e){return v.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return v.sibling(e.firstChild)},contents:function(e){return v.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:v.merge([],e.childNodes)}},function(e,t){v.fn[e]=function(n,r){var i=v.map(this,t,n);return nt.test(e)||(r=n),r&&typeof r=="string"&&(i=v.filter(r,i)),i=this.length>1&&!ot[e]?v.unique(i):i,this.length>1&&rt.test(e)&&(i=i.reverse()),this.pushStack(i,e,l.call(arguments).join(","))}}),v.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),t.length===1?v.find.matchesSelector(t[0],e)?[t[0]]:[]:v.find.matches(e,t)},dir:function(e,n,r){var i=[],s=e[n];while(s&&s.nodeType!==9&&(r===t||s.nodeType!==1||!v(s).is(r)))s.nodeType===1&&i.push(s),s=s[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)e.nodeType===1&&e!==t&&n.push(e);return n}});var ct="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",ht=/ jQuery\d+="(?:null|\d+)"/g,pt=/^\s+/,dt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,vt=/<([\w:]+)/,mt=/]","i"),Et=/^(?:checkbox|radio)$/,St=/checked\s*(?:[^=]|=\s*.checked.)/i,xt=/\/(java|ecma)script/i,Tt=/^\s*\s*$/g,Nt={option:[1,""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]},Ct=lt(i),kt=Ct.appendChild(i.createElement("div"));Nt.optgroup=Nt.option,Nt.tbody=Nt.tfoot=Nt.colgroup=Nt.caption=Nt.thead,Nt.th=Nt.td,v.support.htmlSerialize||(Nt._default=[1,"X
    ","
    "]),v.fn.extend({text:function(e){return v.access(this,function(e){return e===t?v.text(this):this.empty().append((this[0]&&this[0].ownerDocument||i).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(v.isFunction(e))return this.each(function(t){v(this).wrapAll(e.call(this,t))});if(this[0]){var t=v(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&e.firstChild.nodeType===1)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return v.isFunction(e)?this.each(function(t){v(this).wrapInner(e.call(this,t))}):this.each(function(){var t=v(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=v.isFunction(e);return this.each(function(n){v(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){v.nodeName(this,"body")||v(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(e,this.firstChild)})},before:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(e,this),"before",this.selector)}},after:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this.nextSibling)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(this,e),"after",this.selector)}},remove:function(e,t){var n,r=0;for(;(n=this[r])!=null;r++)if(!e||v.filter(e,[n]).length)!t&&n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),v.cleanData([n])),n.parentNode&&n.parentNode.removeChild(n);return this},empty:function(){var e,t=0;for(;(e=this[t])!=null;t++){e.nodeType===1&&v.cleanData(e.getElementsByTagName("*"));while(e.firstChild)e.removeChild(e.firstChild)}return this},clone:function(e,t){return e=e==null?!1:e,t=t==null?e:t,this.map(function(){return v.clone(this,e,t)})},html:function(e){return v.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return n.nodeType===1?n.innerHTML.replace(ht,""):t;if(typeof e=="string"&&!yt.test(e)&&(v.support.htmlSerialize||!wt.test(e))&&(v.support.leadingWhitespace||!pt.test(e))&&!Nt[(vt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(dt,"<$1>");try{for(;r1&&typeof f=="string"&&St.test(f))return this.each(function(){v(this).domManip(e,n,r)});if(v.isFunction(f))return this.each(function(i){var s=v(this);e[0]=f.call(this,i,n?s.html():t),s.domManip(e,n,r)});if(this[0]){i=v.buildFragment(e,this,l),o=i.fragment,s=o.firstChild,o.childNodes.length===1&&(o=s);if(s){n=n&&v.nodeName(s,"tr");for(u=i.cacheable||c-1;a0?this.clone(!0):this).get(),v(o[i])[t](r),s=s.concat(r);return this.pushStack(s,e,o.selector)}}),v.extend({clone:function(e,t,n){var r,i,s,o;v.support.html5Clone||v.isXMLDoc(e)||!wt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(kt.innerHTML=e.outerHTML,kt.removeChild(o=kt.firstChild));if((!v.support.noCloneEvent||!v.support.noCloneChecked)&&(e.nodeType===1||e.nodeType===11)&&!v.isXMLDoc(e)){Ot(e,o),r=Mt(e),i=Mt(o);for(s=0;r[s];++s)i[s]&&Ot(r[s],i[s])}if(t){At(e,o);if(n){r=Mt(e),i=Mt(o);for(s=0;r[s];++s)At(r[s],i[s])}}return r=i=null,o},clean:function(e,t,n,r){var s,o,u,a,f,l,c,h,p,d,m,g,y=t===i&&Ct,b=[];if(!t||typeof t.createDocumentFragment=="undefined")t=i;for(s=0;(u=e[s])!=null;s++){typeof u=="number"&&(u+="");if(!u)continue;if(typeof u=="string")if(!gt.test(u))u=t.createTextNode(u);else{y=y||lt(t),c=t.createElement("div"),y.appendChild(c),u=u.replace(dt,"<$1>"),a=(vt.exec(u)||["",""])[1].toLowerCase(),f=Nt[a]||Nt._default,l=f[0],c.innerHTML=f[1]+u+f[2];while(l--)c=c.lastChild;if(!v.support.tbody){h=mt.test(u),p=a==="table"&&!h?c.firstChild&&c.firstChild.childNodes:f[1]===""&&!h?c.childNodes:[];for(o=p.length-1;o>=0;--o)v.nodeName(p[o],"tbody")&&!p[o].childNodes.length&&p[o].parentNode.removeChild(p[o])}!v.support.leadingWhitespace&&pt.test(u)&&c.insertBefore(t.createTextNode(pt.exec(u)[0]),c.firstChild),u=c.childNodes,c.parentNode.removeChild(c)}u.nodeType?b.push(u):v.merge(b,u)}c&&(u=c=y=null);if(!v.support.appendChecked)for(s=0;(u=b[s])!=null;s++)v.nodeName(u,"input")?_t(u):typeof u.getElementsByTagName!="undefined"&&v.grep(u.getElementsByTagName("input"),_t);if(n){m=function(e){if(!e.type||xt.test(e.type))return r?r.push(e.parentNode?e.parentNode.removeChild(e):e):n.appendChild(e)};for(s=0;(u=b[s])!=null;s++)if(!v.nodeName(u,"script")||!m(u))n.appendChild(u),typeof u.getElementsByTagName!="undefined"&&(g=v.grep(v.merge([],u.getElementsByTagName("script")),m),b.splice.apply(b,[s+1,0].concat(g)),s+=g.length)}return b},cleanData:function(e,t){var n,r,i,s,o=0,u=v.expando,a=v.cache,f=v.support.deleteExpando,l=v.event.special;for(;(i=e[o])!=null;o++)if(t||v.acceptData(i)){r=i[u],n=r&&a[r];if(n){if(n.events)for(s in n.events)l[s]?v.event.remove(i,s):v.removeEvent(i,s,n.handle);a[r]&&(delete a[r],f?delete i[u]:i.removeAttribute?i.removeAttribute(u):i[u]=null,v.deletedIds.push(r))}}}}),function(){var e,t;v.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e=v.uaMatch(o.userAgent),t={},e.browser&&(t[e.browser]=!0,t.version=e.version),t.chrome?t.webkit=!0:t.webkit&&(t.safari=!0),v.browser=t,v.sub=function(){function e(t,n){return new e.fn.init(t,n)}v.extend(!0,e,this),e.superclass=this,e.fn=e.prototype=this(),e.fn.constructor=e,e.sub=this.sub,e.fn.init=function(r,i){return i&&i instanceof v&&!(i instanceof e)&&(i=e(i)),v.fn.init.call(this,r,i,t)},e.fn.init.prototype=e.fn;var t=e(i);return e}}();var Dt,Pt,Ht,Bt=/alpha\([^)]*\)/i,jt=/opacity=([^)]*)/,Ft=/^(top|right|bottom|left)$/,It=/^(none|table(?!-c[ea]).+)/,qt=/^margin/,Rt=new RegExp("^("+m+")(.*)$","i"),Ut=new RegExp("^("+m+")(?!px)[a-z%]+$","i"),zt=new RegExp("^([-+])=("+m+")","i"),Wt={BODY:"block"},Xt={position:"absolute",visibility:"hidden",display:"block"},Vt={letterSpacing:0,fontWeight:400},$t=["Top","Right","Bottom","Left"],Jt=["Webkit","O","Moz","ms"],Kt=v.fn.toggle;v.fn.extend({css:function(e,n){return v.access(this,function(e,n,r){return r!==t?v.style(e,n,r):v.css(e,n)},e,n,arguments.length>1)},show:function(){return Yt(this,!0)},hide:function(){return Yt(this)},toggle:function(e,t){var n=typeof e=="boolean";return v.isFunction(e)&&v.isFunction(t)?Kt.apply(this,arguments):this.each(function(){(n?e:Gt(this))?v(this).show():v(this).hide()})}}),v.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Dt(e,"opacity");return n===""?"1":n}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":v.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(!e||e.nodeType===3||e.nodeType===8||!e.style)return;var s,o,u,a=v.camelCase(n),f=e.style;n=v.cssProps[a]||(v.cssProps[a]=Qt(f,a)),u=v.cssHooks[n]||v.cssHooks[a];if(r===t)return u&&"get"in u&&(s=u.get(e,!1,i))!==t?s:f[n];o=typeof r,o==="string"&&(s=zt.exec(r))&&(r=(s[1]+1)*s[2]+parseFloat(v.css(e,n)),o="number");if(r==null||o==="number"&&isNaN(r))return;o==="number"&&!v.cssNumber[a]&&(r+="px");if(!u||!("set"in u)||(r=u.set(e,r,i))!==t)try{f[n]=r}catch(l){}},css:function(e,n,r,i){var s,o,u,a=v.camelCase(n);return n=v.cssProps[a]||(v.cssProps[a]=Qt(e.style,a)),u=v.cssHooks[n]||v.cssHooks[a],u&&"get"in u&&(s=u.get(e,!0,i)),s===t&&(s=Dt(e,n)),s==="normal"&&n in Vt&&(s=Vt[n]),r||i!==t?(o=parseFloat(s),r||v.isNumeric(o)?o||0:s):s},swap:function(e,t,n){var r,i,s={};for(i in t)s[i]=e.style[i],e.style[i]=t[i];r=n.call(e);for(i in t)e.style[i]=s[i];return r}}),e.getComputedStyle?Dt=function(t,n){var r,i,s,o,u=e.getComputedStyle(t,null),a=t.style;return u&&(r=u.getPropertyValue(n)||u[n],r===""&&!v.contains(t.ownerDocument,t)&&(r=v.style(t,n)),Ut.test(r)&&qt.test(n)&&(i=a.width,s=a.minWidth,o=a.maxWidth,a.minWidth=a.maxWidth=a.width=r,r=u.width,a.width=i,a.minWidth=s,a.maxWidth=o)),r}:i.documentElement.currentStyle&&(Dt=function(e,t){var n,r,i=e.currentStyle&&e.currentStyle[t],s=e.style;return i==null&&s&&s[t]&&(i=s[t]),Ut.test(i)&&!Ft.test(t)&&(n=s.left,r=e.runtimeStyle&&e.runtimeStyle.left,r&&(e.runtimeStyle.left=e.currentStyle.left),s.left=t==="fontSize"?"1em":i,i=s.pixelLeft+"px",s.left=n,r&&(e.runtimeStyle.left=r)),i===""?"auto":i}),v.each(["height","width"],function(e,t){v.cssHooks[t]={get:function(e,n,r){if(n)return e.offsetWidth===0&&It.test(Dt(e,"display"))?v.swap(e,Xt,function(){return tn(e,t,r)}):tn(e,t,r)},set:function(e,n,r){return Zt(e,n,r?en(e,t,r,v.support.boxSizing&&v.css(e,"boxSizing")==="border-box"):0)}}}),v.support.opacity||(v.cssHooks.opacity={get:function(e,t){return jt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=v.isNumeric(t)?"alpha(opacity="+t*100+")":"",s=r&&r.filter||n.filter||"";n.zoom=1;if(t>=1&&v.trim(s.replace(Bt,""))===""&&n.removeAttribute){n.removeAttribute("filter");if(r&&!r.filter)return}n.filter=Bt.test(s)?s.replace(Bt,i):s+" "+i}}),v(function(){v.support.reliableMarginRight||(v.cssHooks.marginRight={get:function(e,t){return v.swap(e,{display:"inline-block"},function(){if(t)return Dt(e,"marginRight")})}}),!v.support.pixelPosition&&v.fn.position&&v.each(["top","left"],function(e,t){v.cssHooks[t]={get:function(e,n){if(n){var r=Dt(e,t);return Ut.test(r)?v(e).position()[t]+"px":r}}}})}),v.expr&&v.expr.filters&&(v.expr.filters.hidden=function(e){return e.offsetWidth===0&&e.offsetHeight===0||!v.support.reliableHiddenOffsets&&(e.style&&e.style.display||Dt(e,"display"))==="none"},v.expr.filters.visible=function(e){return!v.expr.filters.hidden(e)}),v.each({margin:"",padding:"",border:"Width"},function(e,t){v.cssHooks[e+t]={expand:function(n){var r,i=typeof n=="string"?n.split(" "):[n],s={};for(r=0;r<4;r++)s[e+$t[r]+t]=i[r]||i[r-2]||i[0];return s}},qt.test(e)||(v.cssHooks[e+t].set=Zt)});var rn=/%20/g,sn=/\[\]$/,on=/\r?\n/g,un=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,an=/^(?:select|textarea)/i;v.fn.extend({serialize:function(){return v.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?v.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||an.test(this.nodeName)||un.test(this.type))}).map(function(e,t){var n=v(this).val();return n==null?null:v.isArray(n)?v.map(n,function(e,n){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),v.param=function(e,n){var r,i=[],s=function(e,t){t=v.isFunction(t)?t():t==null?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};n===t&&(n=v.ajaxSettings&&v.ajaxSettings.traditional);if(v.isArray(e)||e.jquery&&!v.isPlainObject(e))v.each(e,function(){s(this.name,this.value)});else for(r in e)fn(r,e[r],n,s);return i.join("&").replace(rn,"+")};var ln,cn,hn=/#.*$/,pn=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,dn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,vn=/^(?:GET|HEAD)$/,mn=/^\/\//,gn=/\?/,yn=/)<[^<]*)*<\/script>/gi,bn=/([?&])_=[^&]*/,wn=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,En=v.fn.load,Sn={},xn={},Tn=["*/"]+["*"];try{cn=s.href}catch(Nn){cn=i.createElement("a"),cn.href="",cn=cn.href}ln=wn.exec(cn.toLowerCase())||[],v.fn.load=function(e,n,r){if(typeof e!="string"&&En)return En.apply(this,arguments);if(!this.length)return this;var i,s,o,u=this,a=e.indexOf(" ");return a>=0&&(i=e.slice(a,e.length),e=e.slice(0,a)),v.isFunction(n)?(r=n,n=t):n&&typeof n=="object"&&(s="POST"),v.ajax({url:e,type:s,dataType:"html",data:n,complete:function(e,t){r&&u.each(r,o||[e.responseText,t,e])}}).done(function(e){o=arguments,u.html(i?v("
    ").append(e.replace(yn,"")).find(i):e)}),this},v.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,t){v.fn[t]=function(e){return this.on(t,e)}}),v.each(["get","post"],function(e,n){v[n]=function(e,r,i,s){return v.isFunction(r)&&(s=s||i,i=r,r=t),v.ajax({type:n,url:e,data:r,success:i,dataType:s})}}),v.extend({getScript:function(e,n){return v.get(e,t,n,"script")},getJSON:function(e,t,n){return v.get(e,t,n,"json")},ajaxSetup:function(e,t){return t?Ln(e,v.ajaxSettings):(t=e,e=v.ajaxSettings),Ln(e,t),e},ajaxSettings:{url:cn,isLocal:dn.test(ln[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":Tn},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":v.parseJSON,"text xml":v.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:Cn(Sn),ajaxTransport:Cn(xn),ajax:function(e,n){function T(e,n,s,a){var l,y,b,w,S,T=n;if(E===2)return;E=2,u&&clearTimeout(u),o=t,i=a||"",x.readyState=e>0?4:0,s&&(w=An(c,x,s));if(e>=200&&e<300||e===304)c.ifModified&&(S=x.getResponseHeader("Last-Modified"),S&&(v.lastModified[r]=S),S=x.getResponseHeader("Etag"),S&&(v.etag[r]=S)),e===304?(T="notmodified",l=!0):(l=On(c,w),T=l.state,y=l.data,b=l.error,l=!b);else{b=T;if(!T||e)T="error",e<0&&(e=0)}x.status=e,x.statusText=(n||T)+"",l?d.resolveWith(h,[y,T,x]):d.rejectWith(h,[x,T,b]),x.statusCode(g),g=t,f&&p.trigger("ajax"+(l?"Success":"Error"),[x,c,l?y:b]),m.fireWith(h,[x,T]),f&&(p.trigger("ajaxComplete",[x,c]),--v.active||v.event.trigger("ajaxStop"))}typeof e=="object"&&(n=e,e=t),n=n||{};var r,i,s,o,u,a,f,l,c=v.ajaxSetup({},n),h=c.context||c,p=h!==c&&(h.nodeType||h instanceof v)?v(h):v.event,d=v.Deferred(),m=v.Callbacks("once memory"),g=c.statusCode||{},b={},w={},E=0,S="canceled",x={readyState:0,setRequestHeader:function(e,t){if(!E){var n=e.toLowerCase();e=w[n]=w[n]||e,b[e]=t}return this},getAllResponseHeaders:function(){return E===2?i:null},getResponseHeader:function(e){var n;if(E===2){if(!s){s={};while(n=pn.exec(i))s[n[1].toLowerCase()]=n[2]}n=s[e.toLowerCase()]}return n===t?null:n},overrideMimeType:function(e){return E||(c.mimeType=e),this},abort:function(e){return e=e||S,o&&o.abort(e),T(0,e),this}};d.promise(x),x.success=x.done,x.error=x.fail,x.complete=m.add,x.statusCode=function(e){if(e){var t;if(E<2)for(t in e)g[t]=[g[t],e[t]];else t=e[x.status],x.always(t)}return this},c.url=((e||c.url)+"").replace(hn,"").replace(mn,ln[1]+"//"),c.dataTypes=v.trim(c.dataType||"*").toLowerCase().split(y),c.crossDomain==null&&(a=wn.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===ln[1]&&a[2]===ln[2]&&(a[3]||(a[1]==="http:"?80:443))==(ln[3]||(ln[1]==="http:"?80:443)))),c.data&&c.processData&&typeof c.data!="string"&&(c.data=v.param(c.data,c.traditional)),kn(Sn,c,n,x);if(E===2)return x;f=c.global,c.type=c.type.toUpperCase(),c.hasContent=!vn.test(c.type),f&&v.active++===0&&v.event.trigger("ajaxStart");if(!c.hasContent){c.data&&(c.url+=(gn.test(c.url)?"&":"?")+c.data,delete c.data),r=c.url;if(c.cache===!1){var N=v.now(),C=c.url.replace(bn,"$1_="+N);c.url=C+(C===c.url?(gn.test(c.url)?"&":"?")+"_="+N:"")}}(c.data&&c.hasContent&&c.contentType!==!1||n.contentType)&&x.setRequestHeader("Content-Type",c.contentType),c.ifModified&&(r=r||c.url,v.lastModified[r]&&x.setRequestHeader("If-Modified-Since",v.lastModified[r]),v.etag[r]&&x.setRequestHeader("If-None-Match",v.etag[r])),x.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+(c.dataTypes[0]!=="*"?", "+Tn+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)x.setRequestHeader(l,c.headers[l]);if(!c.beforeSend||c.beforeSend.call(h,x,c)!==!1&&E!==2){S="abort";for(l in{success:1,error:1,complete:1})x[l](c[l]);o=kn(xn,c,n,x);if(!o)T(-1,"No Transport");else{x.readyState=1,f&&p.trigger("ajaxSend",[x,c]),c.async&&c.timeout>0&&(u=setTimeout(function(){x.abort("timeout")},c.timeout));try{E=1,o.send(b,T)}catch(k){if(!(E<2))throw k;T(-1,k)}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var Mn=[],_n=/\?/,Dn=/(=)\?(?=&|$)|\?\?/,Pn=v.now();v.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Mn.pop()||v.expando+"_"+Pn++;return this[e]=!0,e}}),v.ajaxPrefilter("json jsonp",function(n,r,i){var s,o,u,a=n.data,f=n.url,l=n.jsonp!==!1,c=l&&Dn.test(f),h=l&&!c&&typeof a=="string"&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Dn.test(a);if(n.dataTypes[0]==="jsonp"||c||h)return s=n.jsonpCallback=v.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,o=e[s],c?n.url=f.replace(Dn,"$1"+s):h?n.data=a.replace(Dn,"$1"+s):l&&(n.url+=(_n.test(f)?"&":"?")+n.jsonp+"="+s),n.converters["script json"]=function(){return u||v.error(s+" was not called"),u[0]},n.dataTypes[0]="json",e[s]=function(){u=arguments},i.always(function(){e[s]=o,n[s]&&(n.jsonpCallback=r.jsonpCallback,Mn.push(s)),u&&v.isFunction(o)&&o(u[0]),u=o=t}),"script"}),v.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(e){return v.globalEval(e),e}}}),v.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),v.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=i.head||i.getElementsByTagName("head")[0]||i.documentElement;return{send:function(s,o){n=i.createElement("script"),n.async="async",e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,i){if(i||!n.readyState||/loaded|complete/.test(n.readyState))n.onload=n.onreadystatechange=null,r&&n.parentNode&&r.removeChild(n),n=t,i||o(200,"success")},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(0,1)}}}});var Hn,Bn=e.ActiveXObject?function(){for(var e in Hn)Hn[e](0,1)}:!1,jn=0;v.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&Fn()||In()}:Fn,function(e){v.extend(v.support,{ajax:!!e,cors:!!e&&"withCredentials"in e})}(v.ajaxSettings.xhr()),v.support.ajax&&v.ajaxTransport(function(n){if(!n.crossDomain||v.support.cors){var r;return{send:function(i,s){var o,u,a=n.xhr();n.username?a.open(n.type,n.url,n.async,n.username,n.password):a.open(n.type,n.url,n.async);if(n.xhrFields)for(u in n.xhrFields)a[u]=n.xhrFields[u];n.mimeType&&a.overrideMimeType&&a.overrideMimeType(n.mimeType),!n.crossDomain&&!i["X-Requested-With"]&&(i["X-Requested-With"]="XMLHttpRequest");try{for(u in i)a.setRequestHeader(u,i[u])}catch(f){}a.send(n.hasContent&&n.data||null),r=function(e,i){var u,f,l,c,h;try{if(r&&(i||a.readyState===4)){r=t,o&&(a.onreadystatechange=v.noop,Bn&&delete Hn[o]);if(i)a.readyState!==4&&a.abort();else{u=a.status,l=a.getAllResponseHeaders(),c={},h=a.responseXML,h&&h.documentElement&&(c.xml=h);try{c.text=a.responseText}catch(p){}try{f=a.statusText}catch(p){f=""}!u&&n.isLocal&&!n.crossDomain?u=c.text?200:404:u===1223&&(u=204)}}}catch(d){i||s(-1,d)}c&&s(u,f,c,l)},n.async?a.readyState===4?setTimeout(r,0):(o=++jn,Bn&&(Hn||(Hn={},v(e).unload(Bn)),Hn[o]=r),a.onreadystatechange=r):r()},abort:function(){r&&r(0,1)}}}});var qn,Rn,Un=/^(?:toggle|show|hide)$/,zn=new RegExp("^(?:([-+])=|)("+m+")([a-z%]*)$","i"),Wn=/queueHooks$/,Xn=[Gn],Vn={"*":[function(e,t){var n,r,i=this.createTween(e,t),s=zn.exec(t),o=i.cur(),u=+o||0,a=1,f=20;if(s){n=+s[2],r=s[3]||(v.cssNumber[e]?"":"px");if(r!=="px"&&u){u=v.css(i.elem,e,!0)||n||1;do a=a||".5",u/=a,v.style(i.elem,e,u+r);while(a!==(a=i.cur()/o)&&a!==1&&--f)}i.unit=r,i.start=u,i.end=s[1]?u+(s[1]+1)*n:n}return i}]};v.Animation=v.extend(Kn,{tweener:function(e,t){v.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;r-1,f={},l={},c,h;a?(l=i.position(),c=l.top,h=l.left):(c=parseFloat(o)||0,h=parseFloat(u)||0),v.isFunction(t)&&(t=t.call(e,n,s)),t.top!=null&&(f.top=t.top-s.top+c),t.left!=null&&(f.left=t.left-s.left+h),"using"in t?t.using.call(e,f):i.css(f)}},v.fn.extend({position:function(){if(!this[0])return;var e=this[0],t=this.offsetParent(),n=this.offset(),r=er.test(t[0].nodeName)?{top:0,left:0}:t.offset();return n.top-=parseFloat(v.css(e,"marginTop"))||0,n.left-=parseFloat(v.css(e,"marginLeft"))||0,r.top+=parseFloat(v.css(t[0],"borderTopWidth"))||0,r.left+=parseFloat(v.css(t[0],"borderLeftWidth"))||0,{top:n.top-r.top,left:n.left-r.left}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||i.body;while(e&&!er.test(e.nodeName)&&v.css(e,"position")==="static")e=e.offsetParent;return e||i.body})}}),v.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);v.fn[e]=function(i){return v.access(this,function(e,i,s){var o=tr(e);if(s===t)return o?n in o?o[n]:o.document.documentElement[i]:e[i];o?o.scrollTo(r?v(o).scrollLeft():s,r?s:v(o).scrollTop()):e[i]=s},e,i,arguments.length,null)}}),v.each({Height:"height",Width:"width"},function(e,n){v.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){v.fn[i]=function(i,s){var o=arguments.length&&(r||typeof i!="boolean"),u=r||(i===!0||s===!0?"margin":"border");return v.access(this,function(n,r,i){var s;return v.isWindow(n)?n.document.documentElement["client"+e]:n.nodeType===9?(s=n.documentElement,Math.max(n.body["scroll"+e],s["scroll"+e],n.body["offset"+e],s["offset"+e],s["client"+e])):i===t?v.css(n,r,i,u):v.style(n,r,i,u)},n,o?i:t,o,null)}})}),e.jQuery=e.$=v,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return v})})(window); \ No newline at end of file diff --git a/sites/all/modules/contrib/js/jquery_update/replace/misc/1.9/overlay-parent.js b/sites/all/modules/contrib/js/jquery_update/replace/misc/1.9/overlay-parent.js index 9929f84..5c4932f 100644 --- a/sites/all/modules/contrib/js/jquery_update/replace/misc/1.9/overlay-parent.js +++ b/sites/all/modules/contrib/js/jquery_update/replace/misc/1.9/overlay-parent.js @@ -384,6 +384,8 @@ Drupal.overlay.isAdminLink = function (url) { /** * Determine whether a link is external to the site. * + * Deprecated. Use Drupal.urlIsLocal() instead. + * * @param url * The URL to be tested. * @@ -391,10 +393,37 @@ Drupal.overlay.isAdminLink = function (url) { * TRUE if the URL is external to the site, FALSE otherwise. */ Drupal.overlay.isExternalLink = function (url) { + + // Drupal.urlIsLocal() was added in Drupal 7.39. + if (typeof Drupal.urlIsLocal === 'function') { + return !Drupal.urlIsLocal(url); + } + var re = RegExp('^((f|ht)tps?:)?//(?!' + window.location.host + ')'); return re.test(url); }; +/** + * Constructs an internal URL (relative to this site) from the provided path. + * + * For example, if the provided path is 'admin' and the site is installed at + * http://example.com/drupal, this function will return '/drupal/admin'. + * + * @param path + * The internal path, without any leading slash. + * + * @return + * The internal URL derived from the provided path, or null if a valid + * internal path cannot be constructed (for example, if an attempt to create + * an external link is detected). + */ +Drupal.overlay.getInternalUrl = function (path) { + var url = Drupal.settings.basePath + path; + if (!this.isExternalLink(url)) { + return url; + } +}; + /** * Event handler: resizes overlay according to the size of the parent window. * @@ -583,7 +612,7 @@ Drupal.overlay.eventhandlerOverrideLink = function (event) { // If the link contains the overlay-restore class and the overlay-context // state is set, also update the parent window's location. var parentLocation = ($target.hasClass('overlay-restore') && typeof $.bbq.getState('overlay-context') == 'string') - ? Drupal.settings.basePath + $.bbq.getState('overlay-context') + ? this.getInternalUrl($.bbq.getState('overlay-context')) : null; href = this.fragmentizeLink($target.get(0), parentLocation); // Only override default behavior when left-clicking and user is not @@ -663,11 +692,15 @@ Drupal.overlay.eventhandlerOperateByURLFragment = function (event) { } // Get the overlay URL from the current URL fragment. + var internalUrl = null; var state = $.bbq.getState('overlay'); if (state) { + internalUrl = this.getInternalUrl(state); + } + if (internalUrl) { // Append render variable, so the server side can choose the right // rendering and add child frame code to the page if needed. - var url = $.param.querystring(Drupal.settings.basePath + state, { render: 'overlay' }); + var url = $.param.querystring(internalUrl, { render: 'overlay' }); this.open(url); this.resetActiveClass(this.getPath(Drupal.settings.basePath + state)); diff --git a/sites/all/modules/contrib/js/jquery_update/replace/ui/external/globalize.culture.de-DE.js b/sites/all/modules/contrib/js/jquery_update/replace/ui/external/globalize.culture.de-DE.js deleted file mode 100644 index 5466bd7..0000000 --- a/sites/all/modules/contrib/js/jquery_update/replace/ui/external/globalize.culture.de-DE.js +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Globalize Culture de-DE - * - * http://github.com/jquery/globalize - * - * Copyright Software Freedom Conservancy, Inc. - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * This file was generated by the Globalize Culture Generator - * Translation: bugs found in this file need to be fixed in the generator - */ - -(function( window, undefined ) { - -var Globalize; - -if ( typeof require !== "undefined" - && typeof exports !== "undefined" - && typeof module !== "undefined" ) { - // Assume CommonJS - Globalize = require( "globalize" ); -} else { - // Global variable - Globalize = window.Globalize; -} - -Globalize.addCultureInfo( "de-DE", "default", { - name: "de-DE", - englishName: "German (Germany)", - nativeName: "Deutsch (Deutschland)", - language: "de", - numberFormat: { - ",": ".", - ".": ",", - NaN: "n. def.", - negativeInfinity: "-unendlich", - positiveInfinity: "+unendlich", - percent: { - pattern: ["-n%","n%"], - ",": ".", - ".": "," - }, - currency: { - pattern: ["-n $","n $"], - ",": ".", - ".": ",", - symbol: "€" - } - }, - calendars: { - standard: { - "/": ".", - firstDay: 1, - days: { - names: ["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"], - namesAbbr: ["So","Mo","Di","Mi","Do","Fr","Sa"], - namesShort: ["So","Mo","Di","Mi","Do","Fr","Sa"] - }, - months: { - names: ["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember",""], - namesAbbr: ["Jan","Feb","Mrz","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez",""] - }, - AM: null, - PM: null, - eras: [{"name":"n. Chr.","start":null,"offset":0}], - patterns: { - d: "dd.MM.yyyy", - D: "dddd, d. MMMM yyyy", - t: "HH:mm", - T: "HH:mm:ss", - f: "dddd, d. MMMM yyyy HH:mm", - F: "dddd, d. MMMM yyyy HH:mm:ss", - M: "dd MMMM", - Y: "MMMM yyyy" - } - } - } -}); - -}( this )); diff --git a/sites/all/modules/contrib/js/jquery_update/replace/ui/external/globalize.culture.ja-JP.js b/sites/all/modules/contrib/js/jquery_update/replace/ui/external/globalize.culture.ja-JP.js deleted file mode 100644 index a9469d7..0000000 --- a/sites/all/modules/contrib/js/jquery_update/replace/ui/external/globalize.culture.ja-JP.js +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Globalize Culture ja-JP - * - * http://github.com/jquery/globalize - * - * Copyright Software Freedom Conservancy, Inc. - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * This file was generated by the Globalize Culture Generator - * Translation: bugs found in this file need to be fixed in the generator - */ - -(function( window, undefined ) { - -var Globalize; - -if ( typeof require !== "undefined" - && typeof exports !== "undefined" - && typeof module !== "undefined" ) { - // Assume CommonJS - Globalize = require( "globalize" ); -} else { - // Global variable - Globalize = window.Globalize; -} - -Globalize.addCultureInfo( "ja-JP", "default", { - name: "ja-JP", - englishName: "Japanese (Japan)", - nativeName: "日本語 (日本)", - language: "ja", - numberFormat: { - NaN: "NaN (非数値)", - negativeInfinity: "-∞", - positiveInfinity: "+∞", - percent: { - pattern: ["-n%","n%"] - }, - currency: { - pattern: ["-$n","$n"], - decimals: 0, - symbol: "¥" - } - }, - calendars: { - standard: { - days: { - names: ["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"], - namesAbbr: ["日","月","火","水","木","金","土"], - namesShort: ["日","月","火","水","木","金","土"] - }, - months: { - names: ["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月",""], - namesAbbr: ["1","2","3","4","5","6","7","8","9","10","11","12",""] - }, - AM: ["午前","午前","午前"], - PM: ["午後","午後","午後"], - eras: [{"name":"西暦","start":null,"offset":0}], - patterns: { - d: "yyyy/MM/dd", - D: "yyyy'年'M'月'd'日'", - t: "H:mm", - T: "H:mm:ss", - f: "yyyy'年'M'月'd'日' H:mm", - F: "yyyy'年'M'月'd'日' H:mm:ss", - M: "M'月'd'日'", - Y: "yyyy'年'M'月'" - } - }, - Japanese: { - name: "Japanese", - days: { - names: ["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"], - namesAbbr: ["日","月","火","水","木","金","土"], - namesShort: ["日","月","火","水","木","金","土"] - }, - months: { - names: ["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月",""], - namesAbbr: ["1","2","3","4","5","6","7","8","9","10","11","12",""] - }, - AM: ["午前","午前","午前"], - PM: ["午後","午後","午後"], - eras: [{"name":"平成","start":null,"offset":1867},{"name":"昭和","start":-1812153600000,"offset":1911},{"name":"大正","start":-1357603200000,"offset":1925},{"name":"明治","start":60022080000,"offset":1988}], - twoDigitYearMax: 99, - patterns: { - d: "gg y/M/d", - D: "gg y'年'M'月'd'日'", - t: "H:mm", - T: "H:mm:ss", - f: "gg y'年'M'月'd'日' H:mm", - F: "gg y'年'M'月'd'日' H:mm:ss", - M: "M'月'd'日'", - Y: "gg y'年'M'月'" - } - } - } -}); - -}( this )); diff --git a/sites/all/modules/contrib/js/jquery_update/replace/ui/external/globalize.js b/sites/all/modules/contrib/js/jquery_update/replace/ui/external/globalize.js deleted file mode 100644 index ebaca17..0000000 --- a/sites/all/modules/contrib/js/jquery_update/replace/ui/external/globalize.js +++ /dev/null @@ -1,1573 +0,0 @@ -/*! - * Globalize - * - * http://github.com/jquery/globalize - * - * Copyright Software Freedom Conservancy, Inc. - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - */ - -(function( window, undefined ) { - -var Globalize, - // private variables - regexHex, - regexInfinity, - regexParseFloat, - regexTrim, - // private JavaScript utility functions - arrayIndexOf, - endsWith, - extend, - isArray, - isFunction, - isObject, - startsWith, - trim, - truncate, - zeroPad, - // private Globalization utility functions - appendPreOrPostMatch, - expandFormat, - formatDate, - formatNumber, - getTokenRegExp, - getEra, - getEraYear, - parseExact, - parseNegativePattern; - -// Global variable (Globalize) or CommonJS module (globalize) -Globalize = function( cultureSelector ) { - return new Globalize.prototype.init( cultureSelector ); -}; - -if ( typeof require !== "undefined" - && typeof exports !== "undefined" - && typeof module !== "undefined" ) { - // Assume CommonJS - module.exports = Globalize; -} else { - // Export as global variable - window.Globalize = Globalize; -} - -Globalize.cultures = {}; - -Globalize.prototype = { - constructor: Globalize, - init: function( cultureSelector ) { - this.cultures = Globalize.cultures; - this.cultureSelector = cultureSelector; - - return this; - } -}; -Globalize.prototype.init.prototype = Globalize.prototype; - -// 1. When defining a culture, all fields are required except the ones stated as optional. -// 2. Each culture should have a ".calendars" object with at least one calendar named "standard" -// which serves as the default calendar in use by that culture. -// 3. Each culture should have a ".calendar" object which is the current calendar being used, -// it may be dynamically changed at any time to one of the calendars in ".calendars". -Globalize.cultures[ "default" ] = { - // A unique name for the culture in the form - - name: "en", - // the name of the culture in the english language - englishName: "English", - // the name of the culture in its own language - nativeName: "English", - // whether the culture uses right-to-left text - isRTL: false, - // "language" is used for so-called "specific" cultures. - // For example, the culture "es-CL" means "Spanish, in Chili". - // It represents the Spanish-speaking culture as it is in Chili, - // which might have different formatting rules or even translations - // than Spanish in Spain. A "neutral" culture is one that is not - // specific to a region. For example, the culture "es" is the generic - // Spanish culture, which may be a more generalized version of the language - // that may or may not be what a specific culture expects. - // For a specific culture like "es-CL", the "language" field refers to the - // neutral, generic culture information for the language it is using. - // This is not always a simple matter of the string before the dash. - // For example, the "zh-Hans" culture is netural (Simplified Chinese). - // And the "zh-SG" culture is Simplified Chinese in Singapore, whose lanugage - // field is "zh-CHS", not "zh". - // This field should be used to navigate from a specific culture to it's - // more general, neutral culture. If a culture is already as general as it - // can get, the language may refer to itself. - language: "en", - // numberFormat defines general number formatting rules, like the digits in - // each grouping, the group separator, and how negative numbers are displayed. - numberFormat: { - // [negativePattern] - // Note, numberFormat.pattern has no "positivePattern" unlike percent and currency, - // but is still defined as an array for consistency with them. - // negativePattern: one of "(n)|-n|- n|n-|n -" - pattern: [ "-n" ], - // number of decimal places normally shown - decimals: 2, - // string that separates number groups, as in 1,000,000 - ",": ",", - // string that separates a number from the fractional portion, as in 1.99 - ".": ".", - // array of numbers indicating the size of each number group. - // TODO: more detailed description and example - groupSizes: [ 3 ], - // symbol used for positive numbers - "+": "+", - // symbol used for negative numbers - "-": "-", - // symbol used for NaN (Not-A-Number) - NaN: "NaN", - // symbol used for Negative Infinity - negativeInfinity: "-Infinity", - // symbol used for Positive Infinity - positiveInfinity: "Infinity", - percent: { - // [negativePattern, positivePattern] - // negativePattern: one of "-n %|-n%|-%n|%-n|%n-|n-%|n%-|-% n|n %-|% n-|% -n|n- %" - // positivePattern: one of "n %|n%|%n|% n" - pattern: [ "-n %", "n %" ], - // number of decimal places normally shown - decimals: 2, - // array of numbers indicating the size of each number group. - // TODO: more detailed description and example - groupSizes: [ 3 ], - // string that separates number groups, as in 1,000,000 - ",": ",", - // string that separates a number from the fractional portion, as in 1.99 - ".": ".", - // symbol used to represent a percentage - symbol: "%" - }, - currency: { - // [negativePattern, positivePattern] - // negativePattern: one of "($n)|-$n|$-n|$n-|(n$)|-n$|n-$|n$-|-n $|-$ n|n $-|$ n-|$ -n|n- $|($ n)|(n $)" - // positivePattern: one of "$n|n$|$ n|n $" - pattern: [ "($n)", "$n" ], - // number of decimal places normally shown - decimals: 2, - // array of numbers indicating the size of each number group. - // TODO: more detailed description and example - groupSizes: [ 3 ], - // string that separates number groups, as in 1,000,000 - ",": ",", - // string that separates a number from the fractional portion, as in 1.99 - ".": ".", - // symbol used to represent currency - symbol: "$" - } - }, - // calendars defines all the possible calendars used by this culture. - // There should be at least one defined with name "standard", and is the default - // calendar used by the culture. - // A calendar contains information about how dates are formatted, information about - // the calendar's eras, a standard set of the date formats, - // translations for day and month names, and if the calendar is not based on the Gregorian - // calendar, conversion functions to and from the Gregorian calendar. - calendars: { - standard: { - // name that identifies the type of calendar this is - name: "Gregorian_USEnglish", - // separator of parts of a date (e.g. "/" in 11/05/1955) - "/": "/", - // separator of parts of a time (e.g. ":" in 05:44 PM) - ":": ":", - // the first day of the week (0 = Sunday, 1 = Monday, etc) - firstDay: 0, - days: { - // full day names - names: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], - // abbreviated day names - namesAbbr: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], - // shortest day names - namesShort: [ "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" ] - }, - months: { - // full month names (13 months for lunar calendards -- 13th month should be "" if not lunar) - names: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", "" ], - // abbreviated month names - namesAbbr: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "" ] - }, - // AM and PM designators in one of these forms: - // The usual view, and the upper and lower case versions - // [ standard, lowercase, uppercase ] - // The culture does not use AM or PM (likely all standard date formats use 24 hour time) - // null - AM: [ "AM", "am", "AM" ], - PM: [ "PM", "pm", "PM" ], - eras: [ - // eras in reverse chronological order. - // name: the name of the era in this culture (e.g. A.D., C.E.) - // start: when the era starts in ticks (gregorian, gmt), null if it is the earliest supported era. - // offset: offset in years from gregorian calendar - { - "name": "A.D.", - "start": null, - "offset": 0 - } - ], - // when a two digit year is given, it will never be parsed as a four digit - // year greater than this year (in the appropriate era for the culture) - // Set it as a full year (e.g. 2029) or use an offset format starting from - // the current year: "+19" would correspond to 2029 if the current year 2010. - twoDigitYearMax: 2029, - // set of predefined date and time patterns used by the culture - // these represent the format someone in this culture would expect - // to see given the portions of the date that are shown. - patterns: { - // short date pattern - d: "M/d/yyyy", - // long date pattern - D: "dddd, MMMM dd, yyyy", - // short time pattern - t: "h:mm tt", - // long time pattern - T: "h:mm:ss tt", - // long date, short time pattern - f: "dddd, MMMM dd, yyyy h:mm tt", - // long date, long time pattern - F: "dddd, MMMM dd, yyyy h:mm:ss tt", - // month/day pattern - M: "MMMM dd", - // month/year pattern - Y: "yyyy MMMM", - // S is a sortable format that does not vary by culture - S: "yyyy\u0027-\u0027MM\u0027-\u0027dd\u0027T\u0027HH\u0027:\u0027mm\u0027:\u0027ss" - } - // optional fields for each calendar: - /* - monthsGenitive: - Same as months but used when the day preceeds the month. - Omit if the culture has no genitive distinction in month names. - For an explaination of genitive months, see http://blogs.msdn.com/michkap/archive/2004/12/25/332259.aspx - convert: - Allows for the support of non-gregorian based calendars. This convert object is used to - to convert a date to and from a gregorian calendar date to handle parsing and formatting. - The two functions: - fromGregorian( date ) - Given the date as a parameter, return an array with parts [ year, month, day ] - corresponding to the non-gregorian based year, month, and day for the calendar. - toGregorian( year, month, day ) - Given the non-gregorian year, month, and day, return a new Date() object - set to the corresponding date in the gregorian calendar. - */ - } - }, - // For localized strings - messages: {} -}; - -Globalize.cultures[ "default" ].calendar = Globalize.cultures[ "default" ].calendars.standard; - -Globalize.cultures[ "en" ] = Globalize.cultures[ "default" ]; - -Globalize.cultureSelector = "en"; - -// -// private variables -// - -regexHex = /^0x[a-f0-9]+$/i; -regexInfinity = /^[+-]?infinity$/i; -regexParseFloat = /^[+-]?\d*\.?\d*(e[+-]?\d+)?$/; -regexTrim = /^\s+|\s+$/g; - -// -// private JavaScript utility functions -// - -arrayIndexOf = function( array, item ) { - if ( array.indexOf ) { - return array.indexOf( item ); - } - for ( var i = 0, length = array.length; i < length; i++ ) { - if ( array[i] === item ) { - return i; - } - } - return -1; -}; - -endsWith = function( value, pattern ) { - return value.substr( value.length - pattern.length ) === pattern; -}; - -extend = function( deep ) { - var options, name, src, copy, copyIsArray, clone, - target = arguments[0] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !isFunction(target) ) { - target = {}; - } - - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( isObject(copy) || (copyIsArray = isArray(copy)) ) ) { - if ( copyIsArray ) { - copyIsArray = false; - clone = src && isArray(src) ? src : []; - - } else { - clone = src && isObject(src) ? src : {}; - } - - // Never move original objects, clone them - target[ name ] = extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -isArray = Array.isArray || function( obj ) { - return Object.prototype.toString.call( obj ) === "[object Array]"; -}; - -isFunction = function( obj ) { - return Object.prototype.toString.call( obj ) === "[object Function]" -} - -isObject = function( obj ) { - return Object.prototype.toString.call( obj ) === "[object Object]"; -}; - -startsWith = function( value, pattern ) { - return value.indexOf( pattern ) === 0; -}; - -trim = function( value ) { - return ( value + "" ).replace( regexTrim, "" ); -}; - -truncate = function( value ) { - return value | 0; -}; - -zeroPad = function( str, count, left ) { - var l; - for ( l = str.length; l < count; l += 1 ) { - str = ( left ? ("0" + str) : (str + "0") ); - } - return str; -}; - -// -// private Globalization utility functions -// - -appendPreOrPostMatch = function( preMatch, strings ) { - // appends pre- and post- token match strings while removing escaped characters. - // Returns a single quote count which is used to determine if the token occurs - // in a string literal. - var quoteCount = 0, - escaped = false; - for ( var i = 0, il = preMatch.length; i < il; i++ ) { - var c = preMatch.charAt( i ); - switch ( c ) { - case "\'": - if ( escaped ) { - strings.push( "\'" ); - } - else { - quoteCount++; - } - escaped = false; - break; - case "\\": - if ( escaped ) { - strings.push( "\\" ); - } - escaped = !escaped; - break; - default: - strings.push( c ); - escaped = false; - break; - } - } - return quoteCount; -}; - -expandFormat = function( cal, format ) { - // expands unspecified or single character date formats into the full pattern. - format = format || "F"; - var pattern, - patterns = cal.patterns, - len = format.length; - if ( len === 1 ) { - pattern = patterns[ format ]; - if ( !pattern ) { - throw "Invalid date format string \'" + format + "\'."; - } - format = pattern; - } - else if ( len === 2 && format.charAt(0) === "%" ) { - // %X escape format -- intended as a custom format string that is only one character, not a built-in format. - format = format.charAt( 1 ); - } - return format; -}; - -formatDate = function( value, format, culture ) { - var cal = culture.calendar, - convert = cal.convert; - - if ( !format || !format.length || format === "i" ) { - var ret; - if ( culture && culture.name.length ) { - if ( convert ) { - // non-gregorian calendar, so we cannot use built-in toLocaleString() - ret = formatDate( value, cal.patterns.F, culture ); - } - else { - var eraDate = new Date( value.getTime() ), - era = getEra( value, cal.eras ); - eraDate.setFullYear( getEraYear(value, cal, era) ); - ret = eraDate.toLocaleString(); - } - } - else { - ret = value.toString(); - } - return ret; - } - - var eras = cal.eras, - sortable = format === "s"; - format = expandFormat( cal, format ); - - // Start with an empty string - ret = []; - var hour, - zeros = [ "0", "00", "000" ], - foundDay, - checkedDay, - dayPartRegExp = /([^d]|^)(d|dd)([^d]|$)/g, - quoteCount = 0, - tokenRegExp = getTokenRegExp(), - converted; - - function padZeros( num, c ) { - var r, s = num + ""; - if ( c > 1 && s.length < c ) { - r = ( zeros[c - 2] + s); - return r.substr( r.length - c, c ); - } - else { - r = s; - } - return r; - } - - function hasDay() { - if ( foundDay || checkedDay ) { - return foundDay; - } - foundDay = dayPartRegExp.test( format ); - checkedDay = true; - return foundDay; - } - - function getPart( date, part ) { - if ( converted ) { - return converted[ part ]; - } - switch ( part ) { - case 0: return date.getFullYear(); - case 1: return date.getMonth(); - case 2: return date.getDate(); - } - } - - if ( !sortable && convert ) { - converted = convert.fromGregorian( value ); - } - - for ( ; ; ) { - // Save the current index - var index = tokenRegExp.lastIndex, - // Look for the next pattern - ar = tokenRegExp.exec( format ); - - // Append the text before the pattern (or the end of the string if not found) - var preMatch = format.slice( index, ar ? ar.index : format.length ); - quoteCount += appendPreOrPostMatch( preMatch, ret ); - - if ( !ar ) { - break; - } - - // do not replace any matches that occur inside a string literal. - if ( quoteCount % 2 ) { - ret.push( ar[0] ); - continue; - } - - var current = ar[ 0 ], - clength = current.length; - - switch ( current ) { - case "ddd": - //Day of the week, as a three-letter abbreviation - case "dddd": - // Day of the week, using the full name - var names = ( clength === 3 ) ? cal.days.namesAbbr : cal.days.names; - ret.push( names[value.getDay()] ); - break; - case "d": - // Day of month, without leading zero for single-digit days - case "dd": - // Day of month, with leading zero for single-digit days - foundDay = true; - ret.push( - padZeros( getPart(value, 2), clength ) - ); - break; - case "MMM": - // Month, as a three-letter abbreviation - case "MMMM": - // Month, using the full name - var part = getPart( value, 1 ); - ret.push( - ( cal.monthsGenitive && hasDay() ) - ? - cal.monthsGenitive[ clength === 3 ? "namesAbbr" : "names" ][ part ] - : - cal.months[ clength === 3 ? "namesAbbr" : "names" ][ part ] - ); - break; - case "M": - // Month, as digits, with no leading zero for single-digit months - case "MM": - // Month, as digits, with leading zero for single-digit months - ret.push( - padZeros( getPart(value, 1) + 1, clength ) - ); - break; - case "y": - // Year, as two digits, but with no leading zero for years less than 10 - case "yy": - // Year, as two digits, with leading zero for years less than 10 - case "yyyy": - // Year represented by four full digits - part = converted ? converted[ 0 ] : getEraYear( value, cal, getEra(value, eras), sortable ); - if ( clength < 4 ) { - part = part % 100; - } - ret.push( - padZeros( part, clength ) - ); - break; - case "h": - // Hours with no leading zero for single-digit hours, using 12-hour clock - case "hh": - // Hours with leading zero for single-digit hours, using 12-hour clock - hour = value.getHours() % 12; - if ( hour === 0 ) hour = 12; - ret.push( - padZeros( hour, clength ) - ); - break; - case "H": - // Hours with no leading zero for single-digit hours, using 24-hour clock - case "HH": - // Hours with leading zero for single-digit hours, using 24-hour clock - ret.push( - padZeros( value.getHours(), clength ) - ); - break; - case "m": - // Minutes with no leading zero for single-digit minutes - case "mm": - // Minutes with leading zero for single-digit minutes - ret.push( - padZeros( value.getMinutes(), clength ) - ); - break; - case "s": - // Seconds with no leading zero for single-digit seconds - case "ss": - // Seconds with leading zero for single-digit seconds - ret.push( - padZeros( value.getSeconds(), clength ) - ); - break; - case "t": - // One character am/pm indicator ("a" or "p") - case "tt": - // Multicharacter am/pm indicator - part = value.getHours() < 12 ? ( cal.AM ? cal.AM[0] : " " ) : ( cal.PM ? cal.PM[0] : " " ); - ret.push( clength === 1 ? part.charAt(0) : part ); - break; - case "f": - // Deciseconds - case "ff": - // Centiseconds - case "fff": - // Milliseconds - ret.push( - padZeros( value.getMilliseconds(), 3 ).substr( 0, clength ) - ); - break; - case "z": - // Time zone offset, no leading zero - case "zz": - // Time zone offset with leading zero - hour = value.getTimezoneOffset() / 60; - ret.push( - ( hour <= 0 ? "+" : "-" ) + padZeros( Math.floor(Math.abs(hour)), clength ) - ); - break; - case "zzz": - // Time zone offset with leading zero - hour = value.getTimezoneOffset() / 60; - ret.push( - ( hour <= 0 ? "+" : "-" ) + padZeros( Math.floor(Math.abs(hour)), 2 ) - // Hard coded ":" separator, rather than using cal.TimeSeparator - // Repeated here for consistency, plus ":" was already assumed in date parsing. - + ":" + padZeros( Math.abs(value.getTimezoneOffset() % 60), 2 ) - ); - break; - case "g": - case "gg": - if ( cal.eras ) { - ret.push( - cal.eras[ getEra(value, eras) ].name - ); - } - break; - case "/": - ret.push( cal["/"] ); - break; - default: - throw "Invalid date format pattern \'" + current + "\'."; - break; - } - } - return ret.join( "" ); -}; - -// formatNumber -(function() { - var expandNumber; - - expandNumber = function( number, precision, formatInfo ) { - var groupSizes = formatInfo.groupSizes, - curSize = groupSizes[ 0 ], - curGroupIndex = 1, - factor = Math.pow( 10, precision ), - rounded = Math.round( number * factor ) / factor; - - if ( !isFinite(rounded) ) { - rounded = number; - } - number = rounded; - - var numberString = number+"", - right = "", - split = numberString.split( /e/i ), - exponent = split.length > 1 ? parseInt( split[1], 10 ) : 0; - numberString = split[ 0 ]; - split = numberString.split( "." ); - numberString = split[ 0 ]; - right = split.length > 1 ? split[ 1 ] : ""; - - var l; - if ( exponent > 0 ) { - right = zeroPad( right, exponent, false ); - numberString += right.slice( 0, exponent ); - right = right.substr( exponent ); - } - else if ( exponent < 0 ) { - exponent = -exponent; - numberString = zeroPad( numberString, exponent + 1 ); - right = numberString.slice( -exponent, numberString.length ) + right; - numberString = numberString.slice( 0, -exponent ); - } - - if ( precision > 0 ) { - right = formatInfo[ "." ] + - ( (right.length > precision) ? right.slice(0, precision) : zeroPad(right, precision) ); - } - else { - right = ""; - } - - var stringIndex = numberString.length - 1, - sep = formatInfo[ "," ], - ret = ""; - - while ( stringIndex >= 0 ) { - if ( curSize === 0 || curSize > stringIndex ) { - return numberString.slice( 0, stringIndex + 1 ) + ( ret.length ? (sep + ret + right) : right ); - } - ret = numberString.slice( stringIndex - curSize + 1, stringIndex + 1 ) + ( ret.length ? (sep + ret) : "" ); - - stringIndex -= curSize; - - if ( curGroupIndex < groupSizes.length ) { - curSize = groupSizes[ curGroupIndex ]; - curGroupIndex++; - } - } - - return numberString.slice( 0, stringIndex + 1 ) + sep + ret + right; - }; - - formatNumber = function( value, format, culture ) { - if ( !isFinite(value) ) { - if ( value === Infinity ) { - return culture.numberFormat.positiveInfinity; - } - if ( value === -Infinity ) { - return culture.numberFormat.negativeInfinity; - } - return culture.numberFormat.NaN; - } - if ( !format || format === "i" ) { - return culture.name.length ? value.toLocaleString() : value.toString(); - } - format = format || "D"; - - var nf = culture.numberFormat, - number = Math.abs( value ), - precision = -1, - pattern; - if ( format.length > 1 ) precision = parseInt( format.slice(1), 10 ); - - var current = format.charAt( 0 ).toUpperCase(), - formatInfo; - - switch ( current ) { - case "D": - pattern = "n"; - number = truncate( number ); - if ( precision !== -1 ) { - number = zeroPad( "" + number, precision, true ); - } - if ( value < 0 ) number = "-" + number; - break; - case "N": - formatInfo = nf; - // fall through - case "C": - formatInfo = formatInfo || nf.currency; - // fall through - case "P": - formatInfo = formatInfo || nf.percent; - pattern = value < 0 ? formatInfo.pattern[ 0 ] : ( formatInfo.pattern[1] || "n" ); - if ( precision === -1 ) precision = formatInfo.decimals; - number = expandNumber( number * (current === "P" ? 100 : 1), precision, formatInfo ); - break; - default: - throw "Bad number format specifier: " + current; - } - - var patternParts = /n|\$|-|%/g, - ret = ""; - for ( ; ; ) { - var index = patternParts.lastIndex, - ar = patternParts.exec( pattern ); - - ret += pattern.slice( index, ar ? ar.index : pattern.length ); - - if ( !ar ) { - break; - } - - switch ( ar[0] ) { - case "n": - ret += number; - break; - case "$": - ret += nf.currency.symbol; - break; - case "-": - // don't make 0 negative - if ( /[1-9]/.test(number) ) { - ret += nf[ "-" ]; - } - break; - case "%": - ret += nf.percent.symbol; - break; - } - } - - return ret; - }; - -}()); - -getTokenRegExp = function() { - // regular expression for matching date and time tokens in format strings. - return /\/|dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|y|hh|h|HH|H|mm|m|ss|s|tt|t|fff|ff|f|zzz|zz|z|gg|g/g; -}; - -getEra = function( date, eras ) { - if ( !eras ) return 0; - var start, ticks = date.getTime(); - for ( var i = 0, l = eras.length; i < l; i++ ) { - start = eras[ i ].start; - if ( start === null || ticks >= start ) { - return i; - } - } - return 0; -}; - -getEraYear = function( date, cal, era, sortable ) { - var year = date.getFullYear(); - if ( !sortable && cal.eras ) { - // convert normal gregorian year to era-shifted gregorian - // year by subtracting the era offset - year -= cal.eras[ era ].offset; - } - return year; -}; - -// parseExact -(function() { - var expandYear, - getDayIndex, - getMonthIndex, - getParseRegExp, - outOfRange, - toUpper, - toUpperArray; - - expandYear = function( cal, year ) { - // expands 2-digit year into 4 digits. - var now = new Date(), - era = getEra( now ); - if ( year < 100 ) { - var twoDigitYearMax = cal.twoDigitYearMax; - twoDigitYearMax = typeof twoDigitYearMax === "string" ? new Date().getFullYear() % 100 + parseInt( twoDigitYearMax, 10 ) : twoDigitYearMax; - var curr = getEraYear( now, cal, era ); - year += curr - ( curr % 100 ); - if ( year > twoDigitYearMax ) { - year -= 100; - } - } - return year; - }; - - getDayIndex = function ( cal, value, abbr ) { - var ret, - days = cal.days, - upperDays = cal._upperDays; - if ( !upperDays ) { - cal._upperDays = upperDays = [ - toUpperArray( days.names ), - toUpperArray( days.namesAbbr ), - toUpperArray( days.namesShort ) - ]; - } - value = toUpper( value ); - if ( abbr ) { - ret = arrayIndexOf( upperDays[1], value ); - if ( ret === -1 ) { - ret = arrayIndexOf( upperDays[2], value ); - } - } - else { - ret = arrayIndexOf( upperDays[0], value ); - } - return ret; - }; - - getMonthIndex = function( cal, value, abbr ) { - var months = cal.months, - monthsGen = cal.monthsGenitive || cal.months, - upperMonths = cal._upperMonths, - upperMonthsGen = cal._upperMonthsGen; - if ( !upperMonths ) { - cal._upperMonths = upperMonths = [ - toUpperArray( months.names ), - toUpperArray( months.namesAbbr ) - ]; - cal._upperMonthsGen = upperMonthsGen = [ - toUpperArray( monthsGen.names ), - toUpperArray( monthsGen.namesAbbr ) - ]; - } - value = toUpper( value ); - var i = arrayIndexOf( abbr ? upperMonths[1] : upperMonths[0], value ); - if ( i < 0 ) { - i = arrayIndexOf( abbr ? upperMonthsGen[1] : upperMonthsGen[0], value ); - } - return i; - }; - - getParseRegExp = function( cal, format ) { - // converts a format string into a regular expression with groups that - // can be used to extract date fields from a date string. - // check for a cached parse regex. - var re = cal._parseRegExp; - if ( !re ) { - cal._parseRegExp = re = {}; - } - else { - var reFormat = re[ format ]; - if ( reFormat ) { - return reFormat; - } - } - - // expand single digit formats, then escape regular expression characters. - var expFormat = expandFormat( cal, format ).replace( /([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g, "\\\\$1" ), - regexp = [ "^" ], - groups = [], - index = 0, - quoteCount = 0, - tokenRegExp = getTokenRegExp(), - match; - - // iterate through each date token found. - while ( (match = tokenRegExp.exec(expFormat)) !== null ) { - var preMatch = expFormat.slice( index, match.index ); - index = tokenRegExp.lastIndex; - - // don't replace any matches that occur inside a string literal. - quoteCount += appendPreOrPostMatch( preMatch, regexp ); - if ( quoteCount % 2 ) { - regexp.push( match[0] ); - continue; - } - - // add a regex group for the token. - var m = match[ 0 ], - len = m.length, - add; - switch ( m ) { - case "dddd": case "ddd": - case "MMMM": case "MMM": - case "gg": case "g": - add = "(\\D+)"; - break; - case "tt": case "t": - add = "(\\D*)"; - break; - case "yyyy": - case "fff": - case "ff": - case "f": - add = "(\\d{" + len + "})"; - break; - case "dd": case "d": - case "MM": case "M": - case "yy": case "y": - case "HH": case "H": - case "hh": case "h": - case "mm": case "m": - case "ss": case "s": - add = "(\\d\\d?)"; - break; - case "zzz": - add = "([+-]?\\d\\d?:\\d{2})"; - break; - case "zz": case "z": - add = "([+-]?\\d\\d?)"; - break; - case "/": - add = "(\\" + cal[ "/" ] + ")"; - break; - default: - throw "Invalid date format pattern \'" + m + "\'."; - break; - } - if ( add ) { - regexp.push( add ); - } - groups.push( match[0] ); - } - appendPreOrPostMatch( expFormat.slice(index), regexp ); - regexp.push( "$" ); - - // allow whitespace to differ when matching formats. - var regexpStr = regexp.join( "" ).replace( /\s+/g, "\\s+" ), - parseRegExp = { "regExp": regexpStr, "groups": groups }; - - // cache the regex for this format. - return re[ format ] = parseRegExp; - }; - - outOfRange = function( value, low, high ) { - return value < low || value > high; - }; - - toUpper = function( value ) { - // "he-IL" has non-breaking space in weekday names. - return value.split( "\u00A0" ).join( " " ).toUpperCase(); - }; - - toUpperArray = function( arr ) { - var results = []; - for ( var i = 0, l = arr.length; i < l; i++ ) { - results[ i ] = toUpper( arr[i] ); - } - return results; - }; - - parseExact = function( value, format, culture ) { - // try to parse the date string by matching against the format string - // while using the specified culture for date field names. - value = trim( value ); - var cal = culture.calendar, - // convert date formats into regular expressions with groupings. - // use the regexp to determine the input format and extract the date fields. - parseInfo = getParseRegExp( cal, format ), - match = new RegExp( parseInfo.regExp ).exec( value ); - if ( match === null ) { - return null; - } - // found a date format that matches the input. - var groups = parseInfo.groups, - era = null, year = null, month = null, date = null, weekDay = null, - hour = 0, hourOffset, min = 0, sec = 0, msec = 0, tzMinOffset = null, - pmHour = false; - // iterate the format groups to extract and set the date fields. - for ( var j = 0, jl = groups.length; j < jl; j++ ) { - var matchGroup = match[ j + 1 ]; - if ( matchGroup ) { - var current = groups[ j ], - clength = current.length, - matchInt = parseInt( matchGroup, 10 ); - switch ( current ) { - case "dd": case "d": - // Day of month. - date = matchInt; - // check that date is generally in valid range, also checking overflow below. - if ( outOfRange(date, 1, 31) ) return null; - break; - case "MMM": case "MMMM": - month = getMonthIndex( cal, matchGroup, clength === 3 ); - if ( outOfRange(month, 0, 11) ) return null; - break; - case "M": case "MM": - // Month. - month = matchInt - 1; - if ( outOfRange(month, 0, 11) ) return null; - break; - case "y": case "yy": - case "yyyy": - year = clength < 4 ? expandYear( cal, matchInt ) : matchInt; - if ( outOfRange(year, 0, 9999) ) return null; - break; - case "h": case "hh": - // Hours (12-hour clock). - hour = matchInt; - if ( hour === 12 ) hour = 0; - if ( outOfRange(hour, 0, 11) ) return null; - break; - case "H": case "HH": - // Hours (24-hour clock). - hour = matchInt; - if ( outOfRange(hour, 0, 23) ) return null; - break; - case "m": case "mm": - // Minutes. - min = matchInt; - if ( outOfRange(min, 0, 59) ) return null; - break; - case "s": case "ss": - // Seconds. - sec = matchInt; - if ( outOfRange(sec, 0, 59) ) return null; - break; - case "tt": case "t": - // AM/PM designator. - // see if it is standard, upper, or lower case PM. If not, ensure it is at least one of - // the AM tokens. If not, fail the parse for this format. - pmHour = cal.PM && ( matchGroup === cal.PM[0] || matchGroup === cal.PM[1] || matchGroup === cal.PM[2] ); - if ( - !pmHour && ( - !cal.AM || ( matchGroup !== cal.AM[0] && matchGroup !== cal.AM[1] && matchGroup !== cal.AM[2] ) - ) - ) return null; - break; - case "f": - // Deciseconds. - case "ff": - // Centiseconds. - case "fff": - // Milliseconds. - msec = matchInt * Math.pow( 10, 3 - clength ); - if ( outOfRange(msec, 0, 999) ) return null; - break; - case "ddd": - // Day of week. - case "dddd": - // Day of week. - weekDay = getDayIndex( cal, matchGroup, clength === 3 ); - if ( outOfRange(weekDay, 0, 6) ) return null; - break; - case "zzz": - // Time zone offset in +/- hours:min. - var offsets = matchGroup.split( /:/ ); - if ( offsets.length !== 2 ) return null; - hourOffset = parseInt( offsets[0], 10 ); - if ( outOfRange(hourOffset, -12, 13) ) return null; - var minOffset = parseInt( offsets[1], 10 ); - if ( outOfRange(minOffset, 0, 59) ) return null; - tzMinOffset = ( hourOffset * 60 ) + ( startsWith(matchGroup, "-") ? -minOffset : minOffset ); - break; - case "z": case "zz": - // Time zone offset in +/- hours. - hourOffset = matchInt; - if ( outOfRange(hourOffset, -12, 13) ) return null; - tzMinOffset = hourOffset * 60; - break; - case "g": case "gg": - var eraName = matchGroup; - if ( !eraName || !cal.eras ) return null; - eraName = trim( eraName.toLowerCase() ); - for ( var i = 0, l = cal.eras.length; i < l; i++ ) { - if ( eraName === cal.eras[i].name.toLowerCase() ) { - era = i; - break; - } - } - // could not find an era with that name - if ( era === null ) return null; - break; - } - } - } - var result = new Date(), defaultYear, convert = cal.convert; - defaultYear = convert ? convert.fromGregorian( result )[ 0 ] : result.getFullYear(); - if ( year === null ) { - year = defaultYear; - } - else if ( cal.eras ) { - // year must be shifted to normal gregorian year - // but not if year was not specified, its already normal gregorian - // per the main if clause above. - year += cal.eras[( era || 0 )].offset; - } - // set default day and month to 1 and January, so if unspecified, these are the defaults - // instead of the current day/month. - if ( month === null ) { - month = 0; - } - if ( date === null ) { - date = 1; - } - // now have year, month, and date, but in the culture's calendar. - // convert to gregorian if necessary - if ( convert ) { - result = convert.toGregorian( year, month, date ); - // conversion failed, must be an invalid match - if ( result === null ) return null; - } - else { - // have to set year, month and date together to avoid overflow based on current date. - result.setFullYear( year, month, date ); - // check to see if date overflowed for specified month (only checked 1-31 above). - if ( result.getDate() !== date ) return null; - // invalid day of week. - if ( weekDay !== null && result.getDay() !== weekDay ) { - return null; - } - } - // if pm designator token was found make sure the hours fit the 24-hour clock. - if ( pmHour && hour < 12 ) { - hour += 12; - } - result.setHours( hour, min, sec, msec ); - if ( tzMinOffset !== null ) { - // adjust timezone to utc before applying local offset. - var adjustedMin = result.getMinutes() - ( tzMinOffset + result.getTimezoneOffset() ); - // Safari limits hours and minutes to the range of -127 to 127. We need to use setHours - // to ensure both these fields will not exceed this range. adjustedMin will range - // somewhere between -1440 and 1500, so we only need to split this into hours. - result.setHours( result.getHours() + parseInt(adjustedMin / 60, 10), adjustedMin % 60 ); - } - return result; - }; -}()); - -parseNegativePattern = function( value, nf, negativePattern ) { - var neg = nf[ "-" ], - pos = nf[ "+" ], - ret; - switch ( negativePattern ) { - case "n -": - neg = " " + neg; - pos = " " + pos; - // fall through - case "n-": - if ( endsWith(value, neg) ) { - ret = [ "-", value.substr(0, value.length - neg.length) ]; - } - else if ( endsWith(value, pos) ) { - ret = [ "+", value.substr(0, value.length - pos.length) ]; - } - break; - case "- n": - neg += " "; - pos += " "; - // fall through - case "-n": - if ( startsWith(value, neg) ) { - ret = [ "-", value.substr(neg.length) ]; - } - else if ( startsWith(value, pos) ) { - ret = [ "+", value.substr(pos.length) ]; - } - break; - case "(n)": - if ( startsWith(value, "(") && endsWith(value, ")") ) { - ret = [ "-", value.substr(1, value.length - 2) ]; - } - break; - } - return ret || [ "", value ]; -}; - -// -// public instance functions -// - -Globalize.prototype.findClosestCulture = function( cultureSelector ) { - return Globalize.findClosestCulture.call( this, cultureSelector ); -}; - -Globalize.prototype.format = function( value, format, cultureSelector ) { - return Globalize.format.call( this, value, format, cultureSelector ); -}; - -Globalize.prototype.localize = function( key, cultureSelector ) { - return Globalize.localize.call( this, key, cultureSelector ); -}; - -Globalize.prototype.parseInt = function( value, radix, cultureSelector ) { - return Globalize.parseInt.call( this, value, radix, cultureSelector ); -}; - -Globalize.prototype.parseFloat = function( value, radix, cultureSelector ) { - return Globalize.parseFloat.call( this, value, radix, cultureSelector ); -}; - -Globalize.prototype.culture = function( cultureSelector ) { - return Globalize.culture.call( this, cultureSelector ); -}; - -// -// public singleton functions -// - -Globalize.addCultureInfo = function( cultureName, baseCultureName, info ) { - - var base = {}, - isNew = false; - - if ( typeof cultureName !== "string" ) { - // cultureName argument is optional string. If not specified, assume info is first - // and only argument. Specified info deep-extends current culture. - info = cultureName; - cultureName = this.culture().name; - base = this.cultures[ cultureName ]; - } else if ( typeof baseCultureName !== "string" ) { - // baseCultureName argument is optional string. If not specified, assume info is second - // argument. Specified info deep-extends specified culture. - // If specified culture does not exist, create by deep-extending default - info = baseCultureName; - isNew = ( this.cultures[ cultureName ] == null ); - base = this.cultures[ cultureName ] || this.cultures[ "default" ]; - } else { - // cultureName and baseCultureName specified. Assume a new culture is being created - // by deep-extending an specified base culture - isNew = true; - base = this.cultures[ baseCultureName ]; - } - - this.cultures[ cultureName ] = extend(true, {}, - base, - info - ); - // Make the standard calendar the current culture if it's a new culture - if ( isNew ) { - this.cultures[ cultureName ].calendar = this.cultures[ cultureName ].calendars.standard; - } -}; - -Globalize.findClosestCulture = function( name ) { - var match; - if ( !name ) { - return this.cultures[ this.cultureSelector ] || this.cultures[ "default" ]; - } - if ( typeof name === "string" ) { - name = name.split( "," ); - } - if ( isArray(name) ) { - var lang, - cultures = this.cultures, - list = name, - i, l = list.length, - prioritized = []; - for ( i = 0; i < l; i++ ) { - name = trim( list[i] ); - var pri, parts = name.split( ";" ); - lang = trim( parts[0] ); - if ( parts.length === 1 ) { - pri = 1; - } - else { - name = trim( parts[1] ); - if ( name.indexOf("q=") === 0 ) { - name = name.substr( 2 ); - pri = parseFloat( name ); - pri = isNaN( pri ) ? 0 : pri; - } - else { - pri = 1; - } - } - prioritized.push({ lang: lang, pri: pri }); - } - prioritized.sort(function( a, b ) { - return a.pri < b.pri ? 1 : -1; - }); - - // exact match - for ( i = 0; i < l; i++ ) { - lang = prioritized[ i ].lang; - match = cultures[ lang ]; - if ( match ) { - return match; - } - } - - // neutral language match - for ( i = 0; i < l; i++ ) { - lang = prioritized[ i ].lang; - do { - var index = lang.lastIndexOf( "-" ); - if ( index === -1 ) { - break; - } - // strip off the last part. e.g. en-US => en - lang = lang.substr( 0, index ); - match = cultures[ lang ]; - if ( match ) { - return match; - } - } - while ( 1 ); - } - - // last resort: match first culture using that language - for ( i = 0; i < l; i++ ) { - lang = prioritized[ i ].lang; - for ( var cultureKey in cultures ) { - var culture = cultures[ cultureKey ]; - if ( culture.language == lang ) { - return culture; - } - } - } - } - else if ( typeof name === "object" ) { - return name; - } - return match || null; -}; - -Globalize.format = function( value, format, cultureSelector ) { - culture = this.findClosestCulture( cultureSelector ); - if ( value instanceof Date ) { - value = formatDate( value, format, culture ); - } - else if ( typeof value === "number" ) { - value = formatNumber( value, format, culture ); - } - return value; -}; - -Globalize.localize = function( key, cultureSelector ) { - return this.findClosestCulture( cultureSelector ).messages[ key ] || - this.cultures[ "default" ].messages[ key ]; -}; - -Globalize.parseDate = function( value, formats, culture ) { - culture = this.findClosestCulture( culture ); - - var date, prop, patterns; - if ( formats ) { - if ( typeof formats === "string" ) { - formats = [ formats ]; - } - if ( formats.length ) { - for ( var i = 0, l = formats.length; i < l; i++ ) { - var format = formats[ i ]; - if ( format ) { - date = parseExact( value, format, culture ); - if ( date ) { - break; - } - } - } - } - } else { - patterns = culture.calendar.patterns; - for ( prop in patterns ) { - date = parseExact( value, patterns[prop], culture ); - if ( date ) { - break; - } - } - } - - return date || null; -}; - -Globalize.parseInt = function( value, radix, cultureSelector ) { - return truncate( Globalize.parseFloat(value, radix, cultureSelector) ); -}; - -Globalize.parseFloat = function( value, radix, cultureSelector ) { - // radix argument is optional - if ( typeof radix !== "number" ) { - cultureSelector = radix; - radix = 10; - } - - var culture = this.findClosestCulture( cultureSelector ); - var ret = NaN, - nf = culture.numberFormat; - - if ( value.indexOf(culture.numberFormat.currency.symbol) > -1 ) { - // remove currency symbol - value = value.replace( culture.numberFormat.currency.symbol, "" ); - // replace decimal seperator - value = value.replace( culture.numberFormat.currency["."], culture.numberFormat["."] ); - } - - // trim leading and trailing whitespace - value = trim( value ); - - // allow infinity or hexidecimal - if ( regexInfinity.test(value) ) { - ret = parseFloat( value ); - } - else if ( !radix && regexHex.test(value) ) { - ret = parseInt( value, 16 ); - } - else { - - // determine sign and number - var signInfo = parseNegativePattern( value, nf, nf.pattern[0] ), - sign = signInfo[ 0 ], - num = signInfo[ 1 ]; - - // #44 - try parsing as "(n)" - if ( sign === "" && nf.pattern[0] !== "(n)" ) { - signInfo = parseNegativePattern( value, nf, "(n)" ); - sign = signInfo[ 0 ]; - num = signInfo[ 1 ]; - } - - // try parsing as "-n" - if ( sign === "" && nf.pattern[0] !== "-n" ) { - signInfo = parseNegativePattern( value, nf, "-n" ); - sign = signInfo[ 0 ]; - num = signInfo[ 1 ]; - } - - sign = sign || "+"; - - // determine exponent and number - var exponent, - intAndFraction, - exponentPos = num.indexOf( "e" ); - if ( exponentPos < 0 ) exponentPos = num.indexOf( "E" ); - if ( exponentPos < 0 ) { - intAndFraction = num; - exponent = null; - } - else { - intAndFraction = num.substr( 0, exponentPos ); - exponent = num.substr( exponentPos + 1 ); - } - // determine decimal position - var integer, - fraction, - decSep = nf[ "." ], - decimalPos = intAndFraction.indexOf( decSep ); - if ( decimalPos < 0 ) { - integer = intAndFraction; - fraction = null; - } - else { - integer = intAndFraction.substr( 0, decimalPos ); - fraction = intAndFraction.substr( decimalPos + decSep.length ); - } - // handle groups (e.g. 1,000,000) - var groupSep = nf[ "," ]; - integer = integer.split( groupSep ).join( "" ); - var altGroupSep = groupSep.replace( /\u00A0/g, " " ); - if ( groupSep !== altGroupSep ) { - integer = integer.split( altGroupSep ).join( "" ); - } - // build a natively parsable number string - var p = sign + integer; - if ( fraction !== null ) { - p += "." + fraction; - } - if ( exponent !== null ) { - // exponent itself may have a number patternd - var expSignInfo = parseNegativePattern( exponent, nf, "-n" ); - p += "e" + ( expSignInfo[0] || "+" ) + expSignInfo[ 1 ]; - } - if ( regexParseFloat.test(p) ) { - ret = parseFloat( p ); - } - } - return ret; -}; - -Globalize.culture = function( cultureSelector ) { - // setter - if ( typeof cultureSelector !== "undefined" ) { - this.cultureSelector = cultureSelector; - } - // getter - return this.findClosestCulture( cultureSelector ) || this.culture[ "default" ]; -}; - -}( this )); diff --git a/sites/all/modules/contrib/js/jquery_update/replace/ui/external/jquery.bgiframe-2.1.2.js b/sites/all/modules/contrib/js/jquery_update/replace/ui/external/jquery.bgiframe-2.1.2.js deleted file mode 100644 index 5cd38bb..0000000 --- a/sites/all/modules/contrib/js/jquery_update/replace/ui/external/jquery.bgiframe-2.1.2.js +++ /dev/null @@ -1,39 +0,0 @@ -/*! Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net) - * Licensed under the MIT License (LICENSE.txt). - * - * Version 2.1.2 - */ - -(function($){ - -$.fn.bgiframe = ($.browser.msie && /msie 6\.0/i.test(navigator.userAgent) ? function(s) { - s = $.extend({ - top : 'auto', // auto == .currentStyle.borderTopWidth - left : 'auto', // auto == .currentStyle.borderLeftWidth - width : 'auto', // auto == offsetWidth - height : 'auto', // auto == offsetHeight - opacity : true, - src : 'javascript:false;' - }, s); - var html = '