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:
". $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"] = '' . t('Choose the type of destination you would like to create:') . '
'; foreach ($items as $group) { if (count($group['items'])) { - $group['body'] = '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 = '\n" . print_r($message, TRUE) . "\n\n"; + } + + // Optional title to go before the output. + if (!empty($title)) { + $title = '
~ 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 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) ? '' : - "theme') .'theme') . '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
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:
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' => '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' => '
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, '' . 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
+
' . 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| t |
| t |
| t |
| Source: | " + escapeText( source ) + " |
|---|
| Expected: | " + expected + " |
|---|---|
| Result: | " + actual + " |
| Diff: | " + QUnit.diff( expected, actual ) + " |
| Source: | " + escapeText( source ) + " |
| Result: | " + escapeText( actual ) + " |
|---|---|
| Source: | " + escapeText( source ) + " |
' . t('Any items marked "Unknown" are configurations in the system for entity types or bundles which have been disabled via the API or the Settings page; they will not be used.', array('@url' => url('admin/config/search/metatags/settings'))) . '
', ); return $build; @@ -281,7 +264,8 @@ function metatag_config_edit_form($form, &$form_state, $config) { else { $options['token types'] = array($contexts[0]); } - // Allow hook_metatag_token_types_alter() to modify the defined tokens. + // Trigger hook_metatag_token_types_alter(). + // Allow the defined tokens to be modified. drupal_alter('metatag_token_types', $options); } @@ -308,10 +292,6 @@ function metatag_config_edit_form($form, &$form_state, $config) { metatag_metatags_form($form, $config->instance, $config->config, $options); $form['metatags']['#type'] = 'container'; - // Make the token browser available. - $form['metatags']['#prefix'] = $form['metatags'][LANGUAGE_NONE]['#description']; - unset($form['metatags'][LANGUAGE_NONE]['#description']); - $form['actions']['#type'] = 'actions'; $form['actions']['save'] = array( '#type' => 'submit', @@ -333,7 +313,7 @@ function metatag_config_edit_form_submit($form, &$form_state) { $config = (object) $form_state['values']; // @todo Consider renaming the config field from 'config' to 'metatags' $config->config = $config->metatags[LANGUAGE_NONE]; - unset($config->metatags[LANGUAGE_NONE]); + unset($config->metatags); metatag_config_save($config); $label = metatag_config_instance_label($config->instance); @@ -362,7 +342,7 @@ function metatag_config_disable($config) { ctools_export_crud_disable('metatag_config', $config); $label = metatag_config_instance_label($config->instance); - drupal_set_message(t('The meta tag defaults for @label have been disabed.', array('@label' => $label))); + drupal_set_message(t('The meta tag defaults for @label have been disabled.', array('@label' => $label))); drupal_goto(); } @@ -384,7 +364,7 @@ function metatag_config_delete_form($form, &$form_state, $config) { function metatag_config_delete_form_submit($form, &$form_state) { $config = metatag_config_load($form_state['values']['instance']); - metatag_config_delete($config->instance); + metatag_config_delete($config); $label = metatag_config_instance_label($config->instance); $delete = metatag_config_access('delete', $config); @@ -403,16 +383,16 @@ function metatag_config_export_form($config) { * Form constructor to revert nodes to their default metatags. * * @see metatag_bulk_revert_form_submit() + * * @ingroup forms */ function metatag_bulk_revert_form() { - // Get the list of entity:bundle options + // Get the list of entity:bundle options. $options = array(); foreach (entity_get_info() as $entity_type => $entity_info) { foreach (array_keys($entity_info['bundles']) as $bundle) { if (metatag_entity_supports_metatags($entity_type, $bundle)) { - $options[$entity_type . ':' . $bundle] = - $entity_info['label'] . ': ' . $entity_info['bundles'][$bundle]['label']; + $options[$entity_type . ':' . $bundle] = $entity_info['label'] . ': ' . $entity_info['bundles'][$bundle]['label']; } } } @@ -426,6 +406,41 @@ function metatag_bulk_revert_form() { '#description' => t('All meta tags will be removed for all content of the selected entities.'), ); + $metatags = metatag_get_info(); + $options = array(); + foreach ($metatags['tags'] as $tag_name => $tag) { + $options[$tag_name] = t('@group_label: @tag_label', array( + '@group_label' => $metatags['groups'][$tag['group']]['label'], + '@tag_label' => $tag['label'], + )); + } + + if (count($options) > 0) { + $form['tags'] = array( + '#type' => 'checkboxes', + '#required' => FALSE, + '#title' => t('Select the tags to revert'), + '#description' => t('If you select any tags, only those tags will be reverted.'), + '#options' => $options, + ); + } + + $languages = language_list(); + $options = array( + LANGUAGE_NONE => t('Language neutral'), + ); + foreach ($languages as $language) { + $options[$language->language] = $language->name; + } + + $form['languages'] = array( + '#type' => 'checkboxes', + '#required' => FALSE, + '#title' => t('Select the languages to revert'), + '#description' => t('If you select any languages, only tags for those languages will be reverted.'), + '#options' => $options, + ); + $form['submit'] = array( '#type' => 'submit', '#value' => t('Revert'), @@ -448,10 +463,13 @@ function metatag_bulk_revert_form_submit($form, &$form_state) { 'file' => drupal_get_path('module', 'metatag') . '/metatag.admin.inc', ); + $tags = isset($form_state['values']['tags']) ? array_filter($form_state['values']['tags']) : array(); + $languages = isset($form_state['values']['languages']) ? array_filter($form_state['values']['languages']) : array(); + // Set a batch operation per entity:bundle. foreach (array_filter($form_state['values']['update']) as $option) { list($entity_type, $bundle) = explode(':', $option); - $batch['operations'][] = array('metatag_bulk_revert_batch_operation', array($entity_type, $bundle)); + $batch['operations'][] = array('metatag_bulk_revert_batch_operation', array($entity_type, $bundle, $tags, $languages)); } batch_set($batch); @@ -460,7 +478,7 @@ function metatag_bulk_revert_form_submit($form, &$form_state) { /** * Batch callback: delete custom metatags for selected bundles. */ -function metatag_bulk_revert_batch_operation($entity_type, $bundle, &$context) { +function metatag_bulk_revert_batch_operation($entity_type, $bundle, $tags, $languages, &$context) { if (!isset($context['sandbox']['current'])) { $context['sandbox']['count'] = 0; $context['sandbox']['current'] = 0; @@ -498,14 +516,53 @@ function metatag_bulk_revert_batch_operation($entity_type, $bundle, &$context) { foreach ($entity_ids as $entity_id) { $metatags = metatag_metatags_load($entity_type, $entity_id); if (!empty($metatags)) { - db_delete('metatag')->condition('entity_type', $entity_type) - ->condition('entity_id', $entity_id) - ->execute(); - metatag_metatags_cache_clear($entity_type, $entity_id); - $context['results'][] = t('Reverted metatags for @bundle with id @id.', array( - '@bundle' => $entity_type . ': ' . $bundle, - '@id' => $entity_id, - )); + $reset = FALSE; + if (empty($tags)) { + // All tags should be reset, so we just delete any records from the db. + $query = db_delete('metatag') + ->condition('entity_type', $entity_type) + ->condition('entity_id', $entity_id); + if (!empty($languages)) { + $query->condition('language', $languages, 'IN'); + } + $query->execute(); + metatag_metatags_cache_clear($entity_type, $entity_id); + $reset = TRUE; + } + else { + // Iterate over tags and unset those, that we want to reset. + $needs_reset = FALSE; + foreach ($metatags as $metatags_language => $metatags_tags) { + if (empty($languages) || in_array($metatags_language, $languages)) { + foreach ($metatags_tags as $metatags_tag => $metatags_value) { + if (in_array($metatags_tag, $tags)) { + unset($metatags[$metatags_language][$metatags_tag]); + $needs_reset = TRUE; + } + } + } + } + // Save modified metatags. + if ($needs_reset) { + // We don't have a revision id, so we'll get the active one. + // Unfortunately, the only way of getting the active revision ID is to + // first load the entity, and then extract the ID. This is a bit + // performance intensive, but it seems to be the only way of doing it. + $entities = entity_load($entity_type, array($entity_id)); + if (!empty($entities[$entity_id])) { + // We only care about the revision_id. + list(, $revision_id,) = entity_extract_ids($entity_type, $entities[$entity_id]); + } + metatag_metatags_save($entity_type, $entity_id, $revision_id, $metatags, $bundle); + $reset = TRUE; + } + } + if ($reset) { + $context['results'][] = t('Reverted metatags for @bundle with id @id.', array( + '@bundle' => $entity_type . ': ' . $bundle, + '@id' => $entity_id, + )); + } } } @@ -552,11 +609,108 @@ function metatag_admin_settings_form() { ), ); + $form['entities'] = array( + '#type' => 'fieldset', + '#title' => t('Master controls for all entities'), + '#description' => t('By enabling and disabling items here, it is possible to control which entities (e.g. nodes, taxonomy terms) and bundles (e.g. content types, vocabularies) will have the meta tag form available on their respective edit pages. If an entity type is disabled it also disables it for all of that entity type\'s bundles.' . t('Configure the meta tags below. Tokens, e.g. "[node:summary]", automatically insert the corresponding information from that field or value, which helps to avoid redundant meta data and possible search engine penalization; see the "Browse available tokens" popup for more details.') . '
', '#weight' => -10, ); } else { - $form['metatags'][$langcode]['intro_text'] = array( + $form['metatags']['intro_text'] = array( '#markup' => '' . t('Configure the meta tags below. Use tokens (see the "Browse available tokens" popup) to avoid redundant meta data and search engine penalization. For example, a \'keyword\' value of "example" will be shown on all content using this configuration, whereas using the [node:field_keywords] automatically inserts the "keywords" values from the current entity (node, term, etc).') . '
', '#weight' => -10, ); @@ -1185,9 +1576,16 @@ function metatag_metatags_form(array &$form, $instance, array $metatags = array( // Get the form element from the meta tag class. $metatag_form = $metatag_instance->getForm($options); + if (isset($metatag_form['value']['#default_value']) && is_array($metatag_form['value']['#default_value'])) { + $metatag_form['value']['#default_value'] = array_filter($metatag_form['value']['#default_value']); + } // Add a default value form element. if (isset($options['defaults'][$metatag]['value'])) { + if (is_array($options['defaults'][$metatag]['value'])) { + $options['defaults'][$metatag]['value'] = array_filter($options['defaults'][$metatag]['value']); + } + $metatag_form['default'] = array( '#type' => 'hidden', '#value' => $options['defaults'][$metatag]['value'], @@ -1208,13 +1606,15 @@ function metatag_metatags_form(array &$form, $instance, array $metatags = array( $group = $info['groups'][$group_key] + array('form' => array(), 'description' => NULL); $form['metatags'][$langcode][$group_key] = $group['form'] + array( '#type' => 'fieldset', - '#title' => check_plain($group['label']), - '#description' => filter_xss($group['description']), + '#title' => $group['label'], + '#description' => !empty($group['description']) ? $group['description'] : '', '#collapsible' => TRUE, '#collapsed' => TRUE, ); } - $form['metatags'][$langcode][$group_key][$metatag] = $metatag_form + array('#parents' => array('metatags', $langcode, $metatag)); + $form['metatags'][$langcode][$group_key][$metatag] = $metatag_form + array( + '#parents' => array('metatags', $langcode, $metatag), + ); // Hide the fieldset itself if there is not at least one of the meta tag // fields visible. @@ -1243,14 +1643,20 @@ function metatag_metatags_form(array &$form, $instance, array $metatags = array( // Check the #access of each group. If it passed, we display options for // tokens. By this we update the #description of each group. if ($form['metatags']['#access']) { - // Built the token list. - $token_listing_link = theme('token_tree', array('token_types' => $options['token types'], 'dialog' => TRUE)); - - // Add the token list to the top of the fieldset. - $form['metatags'][$langcode]['#description'] = $token_listing_link; - // Check if each meta tag group is being displayed. if (!empty($group_metatag_access)) { + // Built the token browser link. For value "all" theme_token_tree() + // compares with string, not array. + if (in_array('all', $options['token types'])) { + $options['token types'] = 'all'; + } + $token_listing_link = theme('token_tree', + array( + 'token_types' => $options['token types'], + 'dialog' => TRUE, + ) + ); + foreach ($group_metatag_access as $group_key => $token_access) { if ($token_access) { // Update the description. @@ -1260,16 +1666,23 @@ function metatag_metatags_form(array &$form, $instance, array $metatags = array( else { $form['metatags'][$langcode][$group_key]['#description'] = ''; } + + // Add the token browser popup link. $form['metatags'][$langcode][$group_key]['#description'] .= $token_listing_link; } } } } - // Add a submit handler to compare the submitted values against the deafult + // Add a submit handler to compare the submitted values against the default // values. $form += array('#submit' => array()); - array_unshift($form['#submit'], 'metatag_metatags_form_submit'); + if (module_exists('commerce') && isset($form['#entity_type']) && $form['#entity_type'] == 'commerce_product') { + $form['actions']['submit']['#submit'][] = 'metatag_commerce_product_form_submit'; + } + else { + array_unshift($form['#submit'], 'metatag_metatags_form_submit'); + } } /** @@ -1302,200 +1715,193 @@ function metatag_metatags_form_submit($form, &$form_state) { } } +/** + * Form API submission callback for Commerce product. + * + * Unlike metatag_metatags_form_submit. + * + * @see metatag_metatags_save() + */ +function metatag_commerce_product_form_submit($form, &$form_state) { + // Trigger the normal meta tag form submission. + metatag_metatags_form_submit($form, $form_state); + + // The entity being saved. + $entity_type = 'commerce_product'; + $product = $form_state[$entity_type]; + $entity_id = $product->product_id; + $revision_id = $product->revision_id; + + // Get the full entity details. + list(, , $bundle) = entity_extract_ids($entity_type, $product); + + // Update the meta tags for this entity type. + metatag_metatags_save($entity_type, $entity_id, $revision_id, $form_state['values']['metatags'], $bundle); +} + /** * Implements hook_field_extra_fields(). */ function metatag_field_extra_fields() { $extra = array(); foreach (entity_get_info() as $entity_type => $entity_info) { - foreach (array_keys($entity_info['bundles']) as $bundle) { - if (metatag_entity_supports_metatags($entity_type, $bundle)) { - $extra[$entity_type][$bundle]['form']['metatags'] = array( - 'label' => t('Meta tags'), - 'description' => t('Meta tag module form elements.'), - 'weight' => 40, - ); + if (!empty($entity_info['bundles'])) { + foreach (array_keys($entity_info['bundles']) as $bundle) { + if (metatag_entity_supports_metatags($entity_type, $bundle)) { + $extra[$entity_type][$bundle]['form']['metatags'] = array( + 'label' => t('Meta tags'), + 'description' => t('Meta tag module form elements.'), + 'weight' => 40, + ); + } } } } return $extra; } -/** - * Check if an individual entity has meta tags defined, or has defaults. - * - * @param string $entity_type - * An entity type. - * @param object $entity - * An entity object. - * - * @return boolean - * TRUE or FALSE if the entity should have a form for or process meta tags. - */ -function metatag_entity_has_metatags($entity_type, $entity) { - // If an entity has custom meta tags assigned, then we should return TRUE. - if (!empty($entity->metatags)) { - return TRUE; - } - - // Otherwise, check to see if there exists any enabed configuration for - // either the entity type, or bundle (even if the configuration is empty). - // If no configuration exists, then we should not be displaying the meta tag - // forms or processing meta tags on entity view. - $config_exists = &drupal_static(__FUNCTION__, array()); - list( , , $bundle) = entity_extract_ids($entity_type, $entity); - // Do not pretend to have metatags when the bundle does not support them. - if (!metatag_entity_supports_metatags($entity_type, $bundle)) { - return FALSE; - } - $instance = "{$entity_type}:{$bundle}"; - if (!isset($config_exists[$instance])) { - // Check if the intstance or its parents (excluding global) are enabled. - $config_exists[$instance] = metatag_config_is_enabled($instance, TRUE, FALSE); - } - - return !empty($config_exists[$instance]); -} - /** * Check whether the requested entity type (and bundle) support metatag. * - * By default this will be FALSE, support has to be specifically enabled by - * assigning 'metatag' => TRUE within the hook_entity_info() definition for the - * entity. + * By default the entities are disabled, only certain entities will have been + * enabled during installation. If an entity type is enabled it is assumed that + * the entity bundles will also be enabled by default. + * + * @see metatag_entity_type_is_suitable() */ function metatag_entity_supports_metatags($entity_type = NULL, $bundle = NULL) { $entity_types = &drupal_static(__FUNCTION__); + // Identify which entities & bundles are supported the first time the + // function is called. if (!isset($entity_types)) { - $entity_types = array(); - } + foreach (entity_get_info() as $entity_name => $entity_info) { + // Verify that this entity type is suitable. + $entity_types[$entity_name] = metatag_entity_type_is_suitable($entity_name, $entity_info); - // Everything else depends upon a specific entity type being checked. - if (isset($entity_type)) { - if (!isset($entity_types[$entity_type])) { - $entity_info = entity_get_info($entity_type); - if (empty($entity_info['metatags'])) { - $entity_types[$entity_type] = FALSE; - } - else { - $entity_types[$entity_type] = array(); - foreach ($entity_info['bundles'] as $bundle_key => $bundle_info) { - $entity_types[$entity_type][$bundle_key] = !isset($bundle_info['metatags']) || !empty($bundle_info['metatags']); + // The entity type technically supports entities. + if (!empty($entity_types[$entity_name])) { + // Entiy types are enabled by default. + // Allow entities to be disabled by assigning a variable + // 'metatag_enable_{$entity_type}' the value FALSE, e.g.: + // + // // Disable metatags for file_entity. + // $conf['metatag_enable_file'] = FALSE; + // + // @see Settings page. + if (variable_get('metatag_enable_' . $entity_name, FALSE) == FALSE) { + $entity_types[$entity_name] = FALSE; + } + + // Check each bundle. + else { + $entity_types[$entity_name] = array(); + foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) { + // If only one bundle exists, take configuration for entity, to + // reflect options as they are available in the UI. + if (count($entity_info['bundles']) === 1) { + $entity_types[$entity_name][$bundle_name] = variable_get('metatag_enable_' . $entity_name, FALSE) == TRUE; + continue; + } + + // Allow bundles to be disabled by assigning a variable + // 'metatag_enable_{$entity_type}__{$bundle}' the value FALSE, e.g.: + // + // // Disable metatags for carousel nodes. + // $conf['metatag_enable_node__carousel'] = FALSE; + // + // @see Settings page. + if (variable_get('metatag_enable_' . $entity_name . '__' . $bundle_name, TRUE) == FALSE) { + $entity_types[$entity_name][$bundle_name] = FALSE; + } + else { + $entity_types[$entity_name][$bundle_name] = TRUE; + } + } } } } - - // If a specific entity bundle is being compared, check it first. - if (isset($bundle)) { - // Allow entities to be disabled by assigning a variable - // 'metatag_enable_{$entity_type}__{$bundle}' the value FALSE, e.g.: - // - // // Disable metatags for carousel nodes. - // $conf['metatag_enable_node__carousel'] = FALSE; - // - // @see Advanced settings page. - if (variable_get('metatag_enable_' . $entity_type . '__' . $bundle, 'monkey') == FALSE) { - return FALSE; - } - - return isset($entity_types[$entity_type][$bundle]) ? $entity_types[$entity_type][$bundle] : FALSE; - } - - // Otherwise check the entity type itself. - else { - // Allow entities to be disabled by assigning a variable - // 'metatag_enable_{$entity_type}' the value FALSE, e.g.: - // - // // Disable metatags for file_entity. - // $conf['metatag_enable_file'] = FALSE; - // - // @see Advanced settings page. - if (variable_get('metatag_enable_' . $entity_type, 'monkey') == FALSE) { - return FALSE; - } - - return isset($entity_types[$entity_type]) ? ($entity_types[$entity_type] !== FALSE) : FALSE; - } } + // It was requested to check a specific entity. + if (isset($entity_type)) { + // It was also requested to check a specific bundle for this entity. + if (isset($bundle)) { + $supported = !empty($entity_types[$entity_type][$bundle]); + } + + // Check the entity. + else { + $supported = !empty($entity_types[$entity_type]); + } + + return $supported; + } + + // If nothing specific was requested, return the complete list of supported + // entities & bundles. return $entity_types; } /** - * Implements hook_entity_info_alter(). + * Enable support for a specific entity type if setting does not exist. * - * Enables Metatag support for the core entities. + * @param string $entity_type + * The entity type. + * @param string $bundle + * The bundle of the entity. + * @param bool $force_enable + * If TRUE, then the type is enabled regardless of any stored variables. + * + * @return bool + * TRUE if either the bundle or entity type was enabled by this function. */ -function metatag_entity_info_alter(&$info) { - $defaults['node'] = array( - 'path' => 'node/%node', - 'metatags' => TRUE, - ); - $defaults['user'] = array( - 'path' => 'user/%user', - 'metatags' => TRUE, - ); - $defaults['taxonomy_term'] = array( - 'path' => 'taxonomy/term/%taxonomy_term', - 'metatags' => TRUE, - ); - - // Just running taxonomy_vocabulary_load() here would cause problems for - // EntityCache in certain circumstances, so instead the query is executed - // directly instead. - if (module_exists('forum') && ($vocab_id = variable_get('forum_nav_vocabulary', 0))) { - $vocab_name = db_query("SELECT machine_name FROM {taxonomy_vocabulary} WHERE vid = :vid", array(':vid' => $vocab_id))->fetchField(); - if (!empty($vocab_name)) { - $defaults['taxonomy_term']['bundles'][$vocab_name]['path'] = 'forum/%taxonomy_term'; +function metatag_entity_type_enable($entity_type, $bundle = NULL, $force_enable = FALSE) { + // The bundle was defined. + $bundle_set = FALSE; + if (isset($bundle)) { + $stored_bundle = variable_get('metatag_enable_' . $entity_type . '__' . $bundle, NULL); + if ($force_enable || !isset($stored_bundle)) { + variable_set('metatag_enable_' . $entity_type . '__' . $bundle, TRUE); + $bundle_set = TRUE; } } - foreach ($defaults as $key => $entity_defaults) { - if (isset($info[$key])) { - $info[$key] = drupal_array_merge_deep($entity_defaults, $info[$key]); - } + // Always enable the entity type, because otherwise there's no point in + // enabling the bundle. + $entity_type_set = FALSE; + $stored_entity_type = variable_get('metatag_enable_' . $entity_type, NULL); + if ($force_enable || !isset($stored_entity_type)) { + variable_set('metatag_enable_' . $entity_type, TRUE); + $entity_type_set = TRUE; } + + // Clear the static cache so that the entity type / bundle will work. + drupal_static_reset('metatag_entity_supports_metatags'); + + return $bundle_set || $entity_type_set; } /** - * Given a path determine if it is an entity default path. + * Disable support for a specific entity type. * - * @param $path - * The internal path. The id of the entity should be in the string as '[id]'. - * @return - * An array with the entity type and the loaded entity object. + * @param string $entity_type + * The entity type. + * @param string $bundle + * The bundle of the entity. */ -function metatag_load_entity_from_path($path) { - $entity_paths = &drupal_static(__FUNCTION__); - $result = FALSE; - - if (!isset($entity_paths)) { - $entity_paths = array(); - foreach (entity_get_info() as $entity_type => $entity_info) { - if (isset($entity_info['default path'])) { - $default_path = $entity_info['default path']; - $default_path = preg_quote($default_path, '/'); - $default_path = str_replace('\[id\]', '(\d+)', $default_path); - $entity_paths[$entity_type] = $default_path; - } - } +function metatag_entity_type_disable($entity_type, $bundle = NULL) { + // The bundle was defined. + if (isset($bundle)) { + variable_set('metatag_enable_' . $entity_type . '__' . $bundle, FALSE); + } + // The bundle was not defined. + else { + variable_set('metatag_enable_' . $entity_type, FALSE); } - foreach ($entity_paths as $entity_type => $default_path) { - if (preg_match("/^{$default_path}$/", $path, $matches)) { - if ($entity = entity_load($entity_type, array($matches[1]))) { - $result = array('entity_type' => $entity_type, 'entity' => reset($entity)); - } - break; - } - } - - // Allow other modules to customize the data using - // hook_metatag_load_entity_from_path_alter(). - drupal_alter('metatag_load_entity_from_path', $path, $result); - - return $result; + // Clear the static cache so that the entity type / bundle will work. + drupal_static_reset('metatag_entity_supports_metatags'); } /** @@ -1529,6 +1935,12 @@ function metatag_page_build(&$page) { return; } + // Special consideration for the Me module, which uses the "user/me" path and + // will cause problems. + if (arg(0) == 'user' && arg(1) == 'me' && function_exists('me_menu_alter')) { + return; + } + // The page region can be changed. $region = variable_get('metatag_page_region', 'content'); @@ -1557,7 +1969,10 @@ function metatag_page_build(&$page) { } else { $metatags = metatag_metatags_view($instance, array()); - metatag_cache_set($cid, $metatags); + // If output caching is enabled, save this for later. + if (variable_get('metatag_cache_output', FALSE)) { + metatag_cache_set($cid, $metatags); + } } $page[$region]['metatags'][$instance] = $metatags; @@ -1586,7 +2001,10 @@ function metatag_page_build(&$page) { } else { $metatags = metatag_metatags_view($instance, array()); - metatag_cache_set($cid, $metatags); + // If output caching is enabled, save this for later. + if (variable_get('metatag_cache_output', FALSE)) { + metatag_cache_set($cid, $metatags); + } } $page[$region]['metatags'][$instance] = $metatags; } @@ -1595,14 +2013,14 @@ function metatag_page_build(&$page) { /** * Returns whether the current page is the page of the passed in entity. * - * @param $entity_type + * @param string $entity_type * The entity type; e.g. 'node' or 'user'. - * @param $entity + * @param object $entity * The entity object. * - * @return + * @return mixed * TRUE if the current page is the page of the specified entity, or FALSE - * otherwise. + * otherwise. If $entity_type == 'comment' return empty (FALSE). */ function _metatag_entity_is_page($entity_type, $entity) { // Exclude comment entities as this conflicts with comment_fragment.module. @@ -1613,8 +2031,8 @@ function _metatag_entity_is_page($entity_type, $entity) { $uri = entity_uri($entity_type, $entity); $current_path = current_path(); - // Support for Workbench Moderation v1 - if this is a node, check if the - // content type supports moderation. + // Support for Workbench Moderation - if this is a node, check if the content + // type supports moderation. if ($entity_type == 'node' && function_exists('workbench_moderation_node_type_moderated') && workbench_moderation_node_type_moderated($entity->type) === TRUE) { return !empty($uri['path']) && ($current_path == $uri['path'] || $current_path == $uri['path'] . '/draft'); } @@ -1639,7 +2057,8 @@ function metatag_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_n if ($config = metatag_config_load($instance_old)) { $config->instance = $instance_new; metatag_config_save($config); - metatag_config_delete($instance_old); + $config->instance = $instance_old; + metatag_config_delete($config); } } @@ -1648,23 +2067,27 @@ function metatag_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_n */ function metatag_field_attach_delete_bundle($entity_type, $bundle) { $instance = $entity_type . ':' . $bundle; - metatag_config_delete($instance); + if ($config = metatag_config_load($instance)) { + metatag_config_delete($config); + } } /** * Implements hook_field_attach_form(). */ function metatag_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) { - if (!metatag_entity_has_metatags($entity_type, $entity)) { - return; - } // Entity_Translation will trigger this hook again, skip it. if (!empty($form_state['entity_translation']['is_translation'])) { return; } list($entity_id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entity); - $instance = "{$entity_type}:{$bundle}"; + + if (!metatag_entity_supports_metatags($entity_type, $bundle)) { + return; + } + + $instance = metatag_get_entity_metatags_instance($entity, $entity_type, $bundle); // Grab the meta tags for display in the form if there are any. if (!empty($entity->metatags)) { @@ -1698,14 +2121,18 @@ function metatag_field_attach_form($entity_type, $entity, &$form, &$form_state, $metatags = array(); } - $options['token types'] = array(token_get_entity_mapping('entity', $entity_type)); + // Certain circumstances can result in $metatags not being an array. + if (!is_array($metatags)) { + $metatags = array(); + } + + $options['token types'] = array( + token_get_entity_mapping('entity', $entity_type), + ); $options['context'] = $entity_type; - // Allow hook_metatag_token_types_alter() to modify the defined tokens. - drupal_alter('metatag_token_types', $options); // @todo Remove metatag_form_alter() when https://www.drupal.org/node/1284642 is fixed in core. - //metatag_metatags_form($form, $instance, $metatags, $options); - + // metatag_metatags_form($form, $instance, $metatags, $options); $form['#metatags'] = array( 'instance' => $instance, 'metatags' => $metatags, @@ -1729,7 +2156,7 @@ function metatag_form_alter(&$form, $form_state, $form_id) { /** * Get the meta tag information array of a meta tag. * - * @param $metatag + * @param string $name * The meta tag name, e.g. description, for which the info shall be returned, * or NULL to return an array with info about all meta tags. */ @@ -1743,8 +2170,8 @@ function metatag_get_info($type = NULL, $name = NULL) { global $language; if (!isset($info)) { - // hook_metatag_info() includes translated strings, so each language is cached - // separately. + // hook_metatag_info() includes translated strings, so each language is + // cached separately. $cid = 'info:' . $language->language; if ($cache = metatag_cache_get($cid)) { @@ -1784,6 +2211,9 @@ function metatag_get_info($type = NULL, $name = NULL) { } } +/** + * Return instance of metatag. + */ function metatag_get_instance($metatag, array $data = array()) { $info = metatag_get_info('tags', $metatag); if (!empty($info['class']) && class_exists($info['class'])) { @@ -1795,16 +2225,16 @@ function metatag_get_instance($metatag, array $data = array()) { /** * Return the string value of a meta tag. * - * @param $metatag + * @param string $metatag * The meta tag string. - * @param $data + * @param array $data * The array of data for the meta tag class instance. - * @param $options + * @param array $options * An optional array of additional options to pass to the getValue() method * of the meta tag class instance. * - raw: A boolean if TRUE will not perform token replacement. * - * @return + * @return string * A string value. */ function metatag_get_value($metatag, array $data, array $options = array()) { @@ -1812,6 +2242,9 @@ function metatag_get_value($metatag, array $data, array $options = array()) { if ($metatag_instance = metatag_get_instance($metatag, $data)) { $options["instance"] = $metatag; $value = $metatag_instance->getValue($options); + if (is_array($value)) { + $value = implode(', ', $value); + } } return $value; } @@ -1860,31 +2293,95 @@ function metatag_preprocess_maintenance_page(&$variables) { /** * Implements hook_html_head_alter(). + * + * Hide tags added by core that are now handled by metatag. */ function metatag_html_head_alter(&$elements) { - // Remove duplicate link tags if found. - $metatags = metatag_get_info('tags'); - foreach (array_keys($metatags) as $name) { - if (!isset($elements['metatag_' . $name]) || $elements['metatag_' . $name]['#tag'] != 'link') { - // Only check for link tags added by the metatags module. - continue; + $metatags = array(); + $other_tags = array(); + foreach ($elements as $key => $data) { + // Identify meta tags added by the Metatag module. + if (strpos($key, 'metatag_') === 0) { + $metatags[] = $key; } - foreach (array_keys($elements) as $key) { - if (strpos($key, 'drupal_add_html_head_link:' . $name . ':') === 0) { - unset($elements[$key]); + // Identify meta tags added by other modules. + else { + $other_tags[] = $key; + } + } + + // The meta tag keys to look for. + $metatag_keys = array('name', 'property'); + + // Attributes to look for. + $attributes = array('name', 'rel'); + + // Look for meta tags that were added by other modules and hide them. + foreach ($metatags as $metatag_name) { + $metatag = &$elements[$metatag_name]; + + // Setting the #access attribute to these will stop them from being output + // but still leave the tags present for other modules to interact with. + foreach ($other_tags as $other_tag) { + $other = &$elements[$other_tag]; + + // Look for other meta tags that have one of the defined attributes which + // matches one of the values from Metatag's tag. + foreach ($attributes as $attribute) { + if (isset($other['#attributes'], $other['#attributes'][$attribute]) && is_string($other['#attributes'][$attribute])) { + foreach ($metatag_keys as $metatag_key) { + if (isset($metatag[$metatag_key])) { + if (strtolower($other['#attributes'][$attribute]) == $metatag[$metatag_key]) { + $other['#access'] = FALSE; + } + } + } + } } } } - // Remove the default generator meta tag. - unset($elements['system_meta_generator']); + // If the 'leave core tags' option is disabled then the following meta tags + // will be removed if they're provided by core. + if (!variable_get('metatag_leave_core_tags', FALSE)) { + $core_tags = array( + 'generator', + 'canonical', + 'shortlink', + // Leave the shortcut icon, that's more of a theming thing. + // 'shortcut icon',. + ); + foreach ($elements as $name => &$element) { + // Ignore meta tags provided by Metatag. + if (strpos($name, 'metatag_') === 0) { + continue; + } + + // Setting the #access attribute to these will stop them from being output + // but still leave the tags present for other modules to interact with. + foreach ($core_tags as $tag) { + if (!empty($element['#attributes']['rel']) && $element['#attributes']['rel'] == $tag) { + $element['#access'] = FALSE; + } + elseif (!empty($element['#attributes']['name']) && strtolower($element['#attributes']['name']) == $tag) { + $element['#access'] = FALSE; + } + } + } + } } +/** + * Implements hook_get_form(). + */ function metatag_metatag_get_form($metatag, array $data = array(), array $options = array()) { $instance = metatag_get_instance($metatag, $data); return $instance->getForm($options); } +/** + * Returns Instance info if exists otherwise return FALSE. + */ function metatag_config_instance_info($instance = NULL) { global $language; @@ -1945,10 +2442,10 @@ function metatag_filter_values_from_defaults(array &$values, array $defaults = a /** * Return all the parents of a given configuration instance. * - * @param $instance + * @param string $instance * A meta tag configuration instance. * - * @return + * @return array * An array of instances starting with the $instance parameter, with the end * of the array being the global instance. */ @@ -1969,7 +2466,7 @@ function metatag_config_get_parent_instances($instance, $include_global = TRUE) /** * Get the proper label of a configuration instance. * - * @param $instance + * @param string $instance * A meta tag configuration instance. */ function metatag_config_instance_label($instance) { @@ -2058,20 +2555,20 @@ function metatag_config_is_enabled($instance, $include_defaults = FALSE, $includ } /** - * Wrapper around entity_language() to use LANGUAGE_NONE if the entity does not - * have a language assigned. + * Wrapper around entity_language(). * - * @param $entity_type + * @param mixed $entity_type * An entity type's machine name. - * @param $entity - * The entity to review; + * @param object $entity + * The entity to review; will be typecast to an object if an array is passed. * - * @return - * A string indicating the language code to be used. + * @return string + * A string indicating the language code to be used; returns LANGUAGE_NONE if + * the entity does not have a language assigned. */ function metatag_entity_get_language($entity_type, $entity) { - // Determine the entity's language. - $langcode = entity_language($entity_type, $entity); + // Determine the entity's language, af. + $langcode = entity_language($entity_type, (object) $entity); // If no matching language was found, which will happen for e.g. terms and // users, it is normally recommended to use the system default language. @@ -2103,36 +2600,80 @@ function metatag_features_api() { /** * Implements hook_views_post_render(). + * + * Try loading meta tags from a Views page display. */ function metatag_views_post_render(&$view, &$output, &$cache) { + // By default do not add meta tags to admin pages. To enable meta tags on + // admin pages set the 'metatag_tag_admin_pages' variable to TRUE. + if (path_is_admin(current_path()) && !variable_get('metatag_tag_admin_pages', FALSE)) { + return; + } + + // If display is not set for some reason, get out to prevent PHP notices. + if (!isset($view->display[$view->current_display])) { + return; + } + // Build a shortcut to the current display object. $display = $view->display[$view->current_display]; - // Only proceed if this view is a full page, don't process block or other - // Views display objects. - if ($display->display_plugin == 'page' && isset($display->display_options['path'])) { - // Check if this is an entity display page, if so trigger - // hook_entity_view(). - foreach (entity_get_info() as $entity_type => $entity_info) { - // Entity paths will include an auto-loader that matches the entity's - // name, thus the path will be 'some/path/%entity_name'. - if (isset($entity_info['path']) && ($display->display_options['path'] . $entity_type) == $entity_info['path']) { - // Only proceed if this entity type supports meta tags. - if (metatag_entity_supports_metatags($entity_type)) { - // There must be at least one argument and the first argument must be - // numerical. - if (!empty($view->args) && is_numeric($view->args[0])) { - // Only the first argument is used. - $entities = entity_load($entity_type, array($view->args[0])); - // Only if the entity actually exists. - if (!empty($entities)) { - $entity = array_pop($entities); - metatag_entity_view($entity, $entity_type, 'full', NULL, TRUE); - } + // Only proceed if this view is a full page and has a valid path, don't + // process block or other Views display objects. + if ($display->display_plugin == 'page' && !empty($display->display_options['path'])) { + // Try to work out what entity type this is. + $entity_type = ''; + + // All paths must have a placeholder. + $placeholder_pos = strpos($display->display_options['path'], '%'); + if ($placeholder_pos !== FALSE) { + // The first argument must be a numeric entity ID. + if (!empty($view->args) && is_numeric($view->args[0])) { + // The first argument should be an entity ID. + $id = $view->args[0]; + + // Taxonomy terms are the most commonly used item, so check that first. + if ($display->display_options['path'] == 'taxonomy/term/%' || $display->display_options['path'] == 'taxonomy/term/%/%') { + $entity_type = 'taxonomy_term'; + } + // Node pages. + elseif ($display->display_options['path'] == 'node/%') { + $entity_type = 'node'; + } + // User pages. + elseif ($display->display_options['path'] == 'user/%') { + $entity_type = 'user'; + } + } + } + + // Check for other types of entities. + if (empty($entity_type)) { + // Trigger hook_metatag_views_post_render_get_entity(). + $hook = 'metatag_views_post_render_get_entity'; + foreach (module_implements($hook) as $module) { + $function = $module . '_' . $hook; + if (function_exists($function)) { + $entity_type = call_user_func($function, $view); + // If an entity type was identified, stop checking. + if (!empty($entity_type)) { + break; } } } } + + // If an entity type was identified, try loading the entity. + if (!empty($entity_type)) { + // Try loading the requested entity. + $entities = entity_load($entity_type, array($id)); + if (!empty($entities)) { + $entity = array_pop($entities); + + // Trigger our hook_entity_view(). + metatag_entity_view($entity, $entity_type, 'full', NULL, TRUE); + } + } } } @@ -2143,27 +2684,32 @@ function metatag_views_post_render(&$view, &$output, &$cache) { * CTools display handlers. */ function metatag_ctools_render_alter(&$info, $page, $context) { + // By default do not add meta tags to admin pages. To enable meta tags on + // admin pages set the 'metatag_tag_admin_pages' variable to TRUE. + if (path_is_admin(current_path()) && !variable_get('metatag_tag_admin_pages', FALSE)) { + return; + } + // Only proceed if this is a full page (don't process individual panes) and // there's an 'admin path' for the current task. if ($page && !empty($context['task']['admin path'])) { - // Check if this is an entity display page, if so trigger - // hook_entity_view(). - foreach (entity_get_info() as $entity_type => $entity_info) { - // Entity paths will include an auto-loader that matches the entity's - // name, thus the path will be 'some/path/%entity_name'. - if (isset($entity_info['path']) && $context['task']['admin path'] == $entity_info['path']) { - // Only proceed if this entity type supports meta tags. - if (metatag_entity_supports_metatags($entity_type)) { - // There must be at least one argument and the first argument must be - // numerical. - if (!empty($context['args']) && is_numeric($context['args'][0])) { - // We need to pop entity from contexts array. - $first_context = array_pop($context['contexts']); - // Only if the context actually exists, which would be an entity. - if (!empty($first_context->data) && is_object($first_context->data)) { - $langcode = $GLOBALS['language_content']->language; - metatag_entity_view($first_context->data, $entity_type, 'full', $langcode, TRUE); - } + // Loop over each context. + foreach ($context['contexts'] as $context_argument) { + if (is_array($context_argument->type) && !empty($context_argument->data)) { + if (in_array('entity', $context_argument->type)) { + $entity = $context_argument->data; + $entity_type = str_replace('entity:', '', $context_argument->plugin); + + // Verify this is an appropriate entity. + $entity_info = entity_get_info($entity_type); + if (!empty($entity_info) && _metatag_entity_is_page($entity_type, $entity)) { + // Load the meta tags for this entity. + global $language; + metatag_entity_view($entity, $entity_type, 'full', $language->language, TRUE); + + // Don't bother looping over any more contexts, an entity has been + // found. + break; } } } @@ -2171,47 +2717,11 @@ function metatag_ctools_render_alter(&$info, $page, $context) { } } -/** - * Implements hook_entity_translation_delete(). - * - * Required for content translations being handled via Entity_Translation to - * remove the appropriate record when a translation is removed without the - * corresponding entity record also being removed. - */ -function metatag_entity_translation_delete($entity_type, $entity, $langcode) { - // Get the entity's ID. - list($entity_id, $revision_id) = entity_extract_ids($entity_type, $entity); - $revision_id = intval($revision_id); - - // Delete the translation. - metatag_metatags_delete($entity_type, $entity_id, $revision_id, $langcode); -} - -/** - * Translates the metatag if i18n_string is enabled. - * @param $name - * Array or string concatenated with ':' that contains textgroup and string context - * @param string $string - * String in default language or array of strings to be translated - * @param $options - * An associative array of additional options. @see i18n_string_translate() - */ -function metatag_translate($name, $string, $langcode = NULL, $update = FALSE) { - if (function_exists('i18n_string_translate')) { - $options = array( - 'langcode' => $langcode, - 'update' => $update, - ); - return i18n_string_translate($name, $string, $options); - } - else { - return $string; - } -} - /** * Checks if this entity is the default revision (published). * + * Only needed when running Workbench Moderation v1; v3 is skipped. + * * @param object $entity * The entity object, e.g., $node. * @@ -2237,12 +2747,16 @@ function _metatag_isdefaultrevision($entity) { // // Every moderation module saving a forward revision needs to return FALSE. // @todo: Refactor this workaround under D8. - - // Support for Workbench Moderation v1 - if this is a node, check if the - // content type supports moderation. - if (function_exists('workbench_moderation_node_type_moderated') && workbench_moderation_node_type_moderated($entity->type) === TRUE) { - return !empty($entity->workbench_moderation['updating_live_revision']); + // Workbench Moderation v1 uses the hook_node_presave() for some custom logic. + // This was replaced with hook_entity_presave() in v3, so only proceed if the + // old hook implementation is present. + if (function_exists('workbench_moderation_node_presave')) { + // If this is a node, check if the content type supports moderation. + if (function_exists('workbench_moderation_node_type_moderated') && workbench_moderation_node_type_moderated($entity->type) === TRUE) { + return !empty($entity->workbench_moderation['updating_live_revision']); + } } + return FALSE; } @@ -2280,8 +2794,8 @@ function metatag_cache_default_cid_parts(array $cid_parts = array()) { } // Allow each page in a sequence to have different values. - if (isset($_GET['page'])) { - $cid_parts['page'] = $_GET['page']; + if (isset($_GET['page']) && is_numeric($_GET['page'])) { + $cid_parts['page'] = intval($_GET['page']); } // Allow other modules to customize the data using @@ -2291,24 +2805,28 @@ function metatag_cache_default_cid_parts(array $cid_parts = array()) { // Remove empty parts. $cid_parts = array_filter($cid_parts); - // Concatenate the cache ID parts, trim the results to 128 chars. - return substr(implode(':', $cid_parts), 0, 128); + // Concatenate the cache ID parts, trim the results to 255 chars. + return substr(implode(':', $cid_parts), 0, 255); } /** * Wrapper for cache_set. * - * @see cache_set(). + * @see cache_set() */ function metatag_cache_set($cid, $data) { - // Cache the data for later. - return cache_set($cid, $data, 'cache_metatag'); + // By default the cached data will not expire. + $expire = CACHE_PERMANENT; + + // Triggers hook_metatag_cache_set_expire_alter(). + drupal_alter("metatag_cache_set_expire", $expire, $cid, $data); + return cache_set($cid, $data, 'cache_metatag', $expire); } /** * Wrapper for cache_get. * - * @see cache_get(). + * @see cache_get() */ function metatag_cache_get($cid) { // Try to load the object. @@ -2319,6 +2837,7 @@ function metatag_cache_get($cid) { * Determines if we are in an error page and return the appropriate instance. * * @return string + * String of error. */ function metatag_is_error_page() { $known_errors = array( @@ -2335,3 +2854,434 @@ function metatag_is_error_page() { } return ''; } + +/** + * Implements hook_admin_menu_cache_info(). + */ +function metatag_admin_menu_cache_info() { + $caches['metatag'] = array( + 'title' => t('Metatag'), + 'callback' => 'metatag_config_cache_clear', + ); + return $caches; +} + +/** + * Identify whether an entity type is technically capable of having meta tags. + * + * In order to be capable of having meta tags, an entity type must have view + * modes, must be fieldable, and may not be a configuration entity. + * + * @param string $entity_type + * The entity type. + * @param array $entity_info + * Entity information. + * + * @return bool + * Return TRUE if suitable. + */ +function metatag_entity_type_is_suitable($entity_type, $entity_info = array()) { + $suitable = TRUE; + + // If the entity info was not passed along, load it. + if (empty($entity_info)) { + $entity_info = entity_get_info($entity_type); + } + + // Configuration entities may not have meta tags. + if (isset($entity_info['configuration']) && $entity_info['configuration'] == TRUE) { + $suitable = FALSE; + } + + // Entities must have bundles. + elseif (empty($entity_info['bundles'])) { + $suitable = FALSE; + } + + // The entity type must be fieldable. + elseif (empty($entity_info['fieldable'])) { + $suitable = FALSE; + } + + else { + // Ignore some view modes that are automatically added by certain modules. + unset($entity_info['view modes']['ical']); + unset($entity_info['view modes']['diff_standard']); + unset($entity_info['view modes']['token']); + + // There must be view modes. + if (empty($entity_info['view modes'])) { + $suitable = FALSE; + } + + else { + // Specifically disable some entity types. + $excluded = array( + // Comment module. + 'comment', + // Field Collection module. + 'field_collection_item', + // Paragraphs module. + 'paragraphs_item', + ); + if (in_array($entity_type, $excluded)) { + $suitable = FALSE; + } + } + } + + // Trigger hook_metatag_entity_type_is_supported_alter() to allow other + // modules to either enable or disable certain entity types. + drupal_alter('metatag_entity_type_is_supported', $suitable, $entity_type, $entity_info); + + return $suitable; +} + +/** + * Implements hook_node_type_insert(). + * + * When a content type is created, enable it for use with Metatag. + */ +function metatag_node_type_insert($info) { + if (metatag_entity_supports_metatags('node')) { + if (metatag_entity_type_enable('node', $info->type)) { + drupal_set_message(t('Metatag support has been enabled for the @label content type.', array('@label' => $info->name))); + } + } +} + +/** + * Implements hook_node_type_delete(). + * + * When a content type is deleted, remove the corresponding Metatag variable. + */ +function metatag_node_type_delete($info) { + variable_del('metatag_enable_node__' . $info->type); +} + +/** + * Implements hook_taxonomy_vocabulary_insert(). + * + * When a vocabulary is created, enable it for use with Metatag. + */ +function metatag_taxonomy_vocabulary_insert($vocabulary) { + if (metatag_entity_supports_metatags('taxonomy_term')) { + if (metatag_entity_type_enable('taxonomy_term', $vocabulary->machine_name)) { + drupal_set_message(t('Metatag support has been enabled for the @label vocabulary.', array('@label' => $vocabulary->name))); + } + } +} + +/** + * Implements hook_taxonomy_vocabulary_delete(). + * + * When a vocabulary is deleted, remove the corresponding Metatag variable. + */ +function metatag_taxonomy_vocabulary_delete($info) { + variable_del('metatag_enable_taxonomy_term__' . $info->machine_name); +} + +/** + * Implements hook_workbench_moderation_transition(). + * + * Clear a node's caches when its Workbench Moderation state is changed. + */ +function metatag_workbench_moderation_transition($node, $previous_state, $new_state) { + metatag_metatags_cache_clear('node', array($node->nid)); +} + +/** + * Sort callback for sorting by metatag instance string values. + */ +function _metatag_config_instance_sort($a, $b) { + $a_contexts = explode(':', $a); + $b_contexts = explode(':', $b); + + // Global config always comes first. + if ($a_contexts[0] == 'global' && $b_contexts[0] != 'global') { + return -1; + } + elseif ($b_contexts[0] == 'global' && $a_contexts[0] != 'global') { + return 1; + } + + for ($i = 0; $i < max(count($a_contexts), count($b_contexts)); $i++) { + $a_context = isset($a_contexts[$i]) ? $a_contexts[$i] : ''; + $b_context = isset($b_contexts[$i]) ? $b_contexts[$i] : ''; + if ($a_context == $b_context) { + continue; + } + else { + return strcmp($a_context, $b_context); + } + } +} + +/** + * Translations & internationalization (i18n). + */ + +/** + * Implements hook_entity_translation_delete(). + * + * Required for content translations being handled via Entity_Translation to + * remove the appropriate record when a translation is removed without the + * corresponding entity record also being removed. + */ +function metatag_entity_translation_delete($entity_type, $entity, $langcode) { + // Get the entity's ID. + list($entity_id, $revision_id) = entity_extract_ids($entity_type, $entity); + $revision_id = intval($revision_id); + + // Delete the translation. + metatag_metatags_delete($entity_type, $entity_id, $revision_id, $langcode); +} + +/** + * Translates the metatag if i18n_string integration is enabled. + * + * @param string $string + * String in default language or array of strings to be translated. + * @param string $tag_name + * The internal name of the meta tag being translated. + * @param mixed $context + * A context string to use with i18n, or the $options array from a Metatag:: + * getValue() method; if the latter it will be used to generate the full + * context. + * @param string $langcode + * The language code to submit instead of the current page's language. + * @param bool $update + * Whether or not to create/update records in {locales_source}. + * + * @return string + * The translated string if i18n_string is enabled, otherwise just returns the + * original string. + * + * @see i18n_string_translate() + */ +function metatag_translate_metatag($string, $tag_name, $context, $langcode = NULL, $update = TRUE) { + if (module_exists('i18n_string') && !variable_get('metatag_i18n_disabled', FALSE)) { + // By default do not add meta tags to admin pages. To enable meta tags on + // admin pages set the 'metatag_tag_admin_pages' variable to TRUE. + static $page_is_admin; + if (is_null($page_is_admin)) { + $page_is_admin = FALSE; + if (path_is_admin(current_path()) && !variable_get('metatag_tag_admin_pages', FALSE)) { + $page_is_admin = TRUE; + } + } + if ($page_is_admin) { + return $string; + } + + // If the context is an array then it is the $options from the meta tag + // generator and needs some custom tailoring. Doing it this way to avoid an + // unnecessary entity_extract_ids() call when i18n isn't being used. + if (is_array($context)) { + // Optionally disable output generation. + if (!variable_get('metatag_i18n_translate_output', FALSE)) { + return $string; + } + + // Output generation was enabled, so continue as normal. + $new_context = 'output:'; + if (drupal_is_front_page()) { + $new_context .= 'frontpage'; + } + // If this is an entity page, use the entity as the context. + elseif (!empty($context['entity_type']) && !empty($context['entity'])) { + list($entity_id, $revision_id, $bundle) = entity_extract_ids($context['entity_type'], $context['entity']); + $new_context .= $context['entity_type'] . ':' . $entity_id; + } + // Otherwise, use the page URL. + else { + // Trim this to avoid SQL errors on the {locales_source} table. + // length = 255 - strlen('metatag:output:page:') - strlen(metatag); + $strlen = 255 - strlen('metatag:output:page:' . $tag_name); + $new_context .= 'page:' . drupal_substr(current_path(), 0, $strlen); + } + $context = $new_context; + } + + $options = array( + // Automatically create/update the {locales_source} record if one wasn't + // found. + 'update' => $update, + // Translate the string. + 'translate' => TRUE, + ); + + // If the langcode was passed in, add it to the options passed to i18n. + if (!empty($langcode)) { + $options['langcode'] = $langcode; + } + + // By default disable the watchdog logging of translation messages. + $options['watchdog'] = variable_get('metatag_i18n_enable_watchdog', FALSE); + + // Triggers hook_metatag_i18n_context_alter() - allows the i18n string to + // be altered before being used. + drupal_alter('metatag_i18n_context', $context, $tag_name); + + // If the context was erased just send back the original string - it's + // unlikely, but it could happen. + if (empty($context)) { + return $string; + } + + // The 'name' is split up by i18n_string into two components - the textgroup + // is the first item, the others are joined together with a ':' separator + // to make the context. In order to have the contexts show with "metatag" as + // the first part of the context, it has to be added twice to the name. + $name = array( + 'metatag', + $context, + $tag_name, + ); + + // Notify i18n of the string, and obtain a translation if one is available. + return i18n_string($name, $string, $options); + } + + // If the i18n_string module isn't enabled then just pass back the string + // as-is. + else { + return $string; + } +} + +/** + * Translate a set of metatags to the current language. + * + * @param array $metatags + * List of meta tags to be translated. + * @param string $context + * An optional context to use with i18n. + * @param string $langcode + * The language code to submit instead of the current page's language. + * @param bool $update + * Whether or not to create/update records in {locales_source}. + */ +function metatag_translate_metatags(&$metatags, $context = NULL, $langcode = NULL, $update = TRUE) { + if (!empty($metatags)) { + foreach ($metatags as $key => $data) { + if (!empty($data['value']) && is_string($data['value'])) { + $metatags[$key] = array( + 'value' => metatag_translate_metatag($data['value'], $key, $context, $langcode, $update), + ); + } + } + } +} + +/** + * Update the translated definitions of meta tags. + * + * @param array $metatags + * List of meta tags to have their translations updated. + * @param string $context + * The string that will be used to group strings in the translation UI. + */ +function metatag_translations_update($metatags, $context) { + // Store the context as it was originally provided. + $original_context = $context; + + // Update the i18n string. + if (module_exists('i18n_string') && !variable_get('metatag_i18n_disabled', FALSE)) { + $options = array(); + + // By default disable the watchdog logging of translation messages. + $options['watchdog'] = variable_get('metatag_i18n_enable_watchdog', FALSE); + + foreach ($metatags as $tag_name => $data) { + // Revert the context, so that it can be changed if needed. + $context = $original_context; + + if (!empty($data['value']) && is_string($data['value'])) { + // Triggers hook_metatag_i18n_context_alter() - allows the i18n string + // to be altered before being used. + drupal_alter('metatag_i18n_context', $context, $tag_name); + + // Don't do anything if the context was erased - it's unlikely, but it + // could happen. + if (empty($context)) { + continue; + } + + // The textgroup is the first part of the string. + i18n_string_update("metatag:{$context}:{$tag_name}", $data['value'], $options); + } + } + } +} + +/** + * Remove the translated definitions of meta tags. + * + * @param array $metatags + * List of meta tags to have their translations updated. + * @param string $context + * The string that will be used to group strings in the translation UI. + */ +function metatag_translations_delete($metatags, $context) { + // Store the context as it was originally provided. + $original_context = $context; + + // Update the i18n string. + if (module_exists('i18n_string') && !variable_get('metatag_i18n_disabled', FALSE)) { + $options = array(); + + // By default disable the watchdog logging of translation messages. + $options['watchdog'] = variable_get('metatag_i18n_enable_watchdog', FALSE); + + foreach ($metatags as $tag_name => $data) { + // Revert the context, so that it can be changed if needed. + $context = $original_context; + + if (!empty($data['value']) && is_string($data['value'])) { + // Triggers hook_metatag_i18n_context_alter() - allows the i18n string + // to be altered before being used. + drupal_alter('metatag_i18n_context', $context, $tag_name); + + // Don't do anything if the context was erased - it's unlikely, but it + // could happen. + if (empty($context)) { + continue; + } + + // The textgroup is the first part of the string. + i18n_string_remove("metatag:{$context}:{$tag_name}", $data['value'], $options); + } + } + } +} + +/** + * Implements hook_config_insert(). + * + * Implements hook_metatag_config_insert() on behalf of i18n_string. + */ +function i18n_string_metatag_config_insert($config) { + $context = 'metatag_config:' . $config->instance; + metatag_translations_update($config->config, $context); +} + +/** + * Implements hook_config_update(). + * + * Implements hook_metatag_config_update() on behalf of i18n_string. + */ +function i18n_string_metatag_config_update($config) { + // Defer to the 'insert' function. + i18n_string_metatag_config_insert($config); +} + +/** + * Implements hook_config_delete(). + * + * Implements hook_metatag_config_delete() on behalf of i18n_string. + */ +function i18n_string_metatag_config_delete($config) { + $context = 'metatag_config:' . $config->instance; + metatag_translations_delete($config->config, $context); +} diff --git a/sites/all/modules/contrib/seo/metatag/metatag.search_api.inc b/sites/all/modules/contrib/seo/metatag/metatag.search_api.inc new file mode 100644 index 0000000..91c8546 --- /dev/null +++ b/sites/all/modules/contrib/seo/metatag/metatag.search_api.inc @@ -0,0 +1,85 @@ + array( + 'name' => t('Meta tags'), + 'description' => t("Adds the item's meta tags to the indexed data."), + 'class' => 'MetatagSearchAlterCallback', + ), + ); +} + +/** + * Only add the class if Search API is installed. + */ +if (class_exists('SearchApiAbstractAlterCallback')) { + + /** + * Adds meta tag values to the indexed items. + */ + class MetatagSearchAlterCallback extends SearchApiAbstractAlterCallback { + + /** + * {@inheritdoc} + */ + public function supportsIndex(SearchApiIndex $index) { + // Only works on entities. + return (bool) $index->getEntityType(); + } + + /** + * {@inheritdoc} + */ + public function alterItems(array &$items) { + $entity_type = $this->index->getEntityType(); + $tags = metatag_get_info('tags'); + foreach ($items as $id => $item) { + foreach (array_keys($tags) as $tag) { + $items[$id]->{'metatag_' . $tag} = NULL; + if (isset($item->language) && isset($item->metatags[$item->language][$tag])) { + $instance = metatag_get_instance($tag, $item->metatags[$item->language][$tag]); + $items[$id]->{'metatag_' . $tag} = $instance->getValue(array('token data' => array($entity_type => $item))); + } + } + } + } + + /** + * {@inheritdoc} + */ + public function propertyInfo() { + $properties = array(); + + // Get available meta tags. + $tags = metatag_get_info('tags'); + foreach ($tags as $id => $tag) { + switch ($tag['class']) { + case 'DrupalLinkMetaTag': + $type = 'uri'; + break; + + default: + $type = 'text'; + break; + } + $properties['metatag_' . $id] = array( + 'label' => t('Meta tag: @label', array('@label' => $tag['label'])), + 'description' => t('@label meta tag attached to an item.', array('@label' => $tag['label'])), + 'type' => $type, + ); + } + + return $properties; + } + + } +} diff --git a/sites/all/modules/contrib/seo/metatag/metatag.test b/sites/all/modules/contrib/seo/metatag/metatag.test deleted file mode 100644 index 6baebba..0000000 --- a/sites/all/modules/contrib/seo/metatag/metatag.test +++ /dev/null @@ -1,390 +0,0 @@ - 'Metatag tests for nodes', - 'description' => 'Test Metatag edit functionality for nodes.', - 'group' => 'Metatag', - ); - } - - /** - * {@inheritdoc} - */ - function setUp(array $modules = array()) { - $modules = array( - 'metatag_dc', - 'metatag_facebook', - 'metatag_google_plus', - 'metatag_opengraph', - 'metatag_twitter_cards', - ); - parent::setUp($modules); - } - - /** - * Tests filtering of values using metatag_filter_values_from_defaults(). - */ - public function testMetatagFilterValuesFromDefaults() { - // Create a content type. - $this->drupalCreateContentType(array( - 'type' => 'metatag_test', - 'name' => 'Test', - )); - - // Create an admin-level user. - $this->adminUser = $this->drupalCreateUser(array( - 'administer meta tags', - 'edit meta tags', - 'create metatag_test content', - 'delete any metatag_test content', - 'edit any metatag_test content', - )); - $this->drupalLogin($this->adminUser); - - // Assign default values for the new content type. - $this->drupalGet('admin/config/search/metatags/config/add'); - $this->drupalPost(NULL, array( - 'instance' => 'node:metatag_test', - ), t('Add and configure')); - $this->drupalPost(NULL, array( - 'metatags[und][dcterms.title][value]' => '[node:title]', - 'metatags[und][dcterms.creator][value]' => '[node:author]', - 'metatags[und][dcterms.date][value]' => '[node:created:custom:Y-m-d\TH:iP]', - 'metatags[und][dcterms.format][value]' => 'text/html', - 'metatags[und][dcterms.identifier][value]' => '[current-page:url:absolute]', - 'metatags[und][dcterms.language][value]' => '[node:language]', - ), t('Save')); - - // Create a test node. - $this->drupalGet('node/add/metatag-test'); - $this->drupalPost(NULL, array( - 'metatags[und][dcterms.title][value]' => '[node:title] ponies', - 'title' => 'Who likes magic', - ), t('Save')); - - // Verify that the node saved correctly. - $xpath = $this->xpath("//h1"); - $t_args = array('@type' => 'Test', '%title' => 'Who likes magic'); - $this->assertText(strip_tags(t('@type %title has been created.', $t_args))); - // This doesn't work for some reason, it seems the HTML is stripped off - // during output so the %title's standard Drupal wrappers don't match. - // $this->assertText(t('@type %title has been created.', $t_args)); - - // Verify the node data saved correctly. - $matches = array(); - if (preg_match('@node/(\d+)$@', $this->getUrl(), $matches)) { - $nid = end($matches); - $node = node_load($nid); - // Only the non-default values are stored. - $expected = array( - 'und' => array( - 'dcterms.title' => array( - 'value' => '[node:title] ponies', - ), - ), - ); - $this->assertEqual($expected, $node->metatags); - } - else { - $this->fail(t('Could not determine node ID for created node.')); - } - - // Verify the title is using the custom default for this content type. - $xpath = $this->xpath("//meta[@name='dcterms.title']"); - $this->assertEqual(count($xpath), 1); - $this->assertEqual($xpath[0]['content'], "Who likes magic ponies"); - } -} - -class MetaTagsUnitTest extends MetaTagsTestHelper { - public static function getInfo() { - return array( - 'name' => 'Metatag unit tests', - 'description' => 'Test basic meta tag functionality.', - 'group' => 'Metatag', - ); - } - - /** - * Test the metatag_config_load_with_defaults() function. - */ - public function testConfigLoadDefaults() { - $defaults = metatag_config_load_with_defaults('test:foo'); - $this->assertEqual($defaults, array( - // Basic meta tags. - 'title' => array('value' => 'Test altered title'), - 'description' => array('value' => 'Test foo description'), - 'abstract' => array('value' => 'Test foo abstract'), - // 'keywords' => array('value' => ''), - - // Advanced meta tags. - // 'robots' => array('value' => ''), - // 'news_keywords' => array('value' => ''), - // 'standout' => array('value' => ''), - // 'robots' => array('value' => ''), - // 'standout' => array('value' => ''), - 'generator' => array('value' => 'Drupal 7 (http://drupal.org)'), - // 'standout' => array('value' => ''), - // 'image_src' => array('value' => ''), - 'canonical' => array('value' => '[current-page:url:absolute]'), - 'shortlink' => array('value' => '[current-page:url:unaliased]'), - // 'publisher' => array('value' => ''), - // 'author' => array('value' => ''), - // 'original-source' => array('value' => ''), - // 'revisit-after' => array('value' => ''), - // 'content-language' => array('value' => ''), - - // Fake meta tag. - 'test:foo' => array('value' => 'foobar'), - )); - } - - public function testEntitySupport() { - $test_cases[1] = array('type' => 'node', 'bundle' => 'article', 'expected' => TRUE); - $test_cases[2] = array('type' => 'node', 'bundle' => 'page', 'expected' => TRUE); - $test_cases[3] = array('type' => 'node', 'bundle' => 'invalid-bundle', 'expected' => FALSE); - $test_cases[4] = array('type' => 'user', 'expected' => TRUE); - $test_cases[5] = array('type' => 'taxonomy_term', 'bundle' => 'tags', 'expected' => TRUE); - $test_cases[6] = array('type' => 'taxonomy_term', 'bundle' => 'invalid-bundle', 'expected' => FALSE); - foreach ($test_cases as $test_case) { - $test_case += array('bundle' => NULL); - $this->assertMetatagEntityHasMetatags($test_case['type'], $test_case['bundle'], $test_case['expected']); - } - - variable_set('metatag_test_entity_info_disable', TRUE); - drupal_static_reset('metatag_entity_has_metatags'); - drupal_static_reset('metatag_entity_supports_metatags'); - entity_info_cache_clear(); - - $test_cases[2]['expected'] = FALSE; - $test_cases[4]['expected'] = FALSE; - $test_cases[6]['expected'] = FALSE; - foreach ($test_cases as $test_case) { - $test_case += array('bundle' => NULL); - $this->assertMetatagEntityHasMetatags($test_case['type'], $test_case['bundle'], $test_case['expected']); - } - } - - function assertMetatagEntityHasMetatags($entity_type, $bundle, $expected) { - $entity = entity_create_stub_entity($entity_type, array(0, NULL, $bundle)); - return $this->assertEqual( - metatag_entity_has_metatags($entity_type, $entity), - $expected, - t("metatag_entity_has_metatags(:type, :entity) is :expected", array( - ':type' => var_export($entity_type, TRUE), - ':entity' => var_export($entity, TRUE), - ':expected' => var_export($expected, TRUE), - )) - ); - } - - /** - * Test the metatag_config_instance_label() function. - */ - public function testConfigLabels() { - $test_cases = array( - 'node' => 'Node', - 'node:article' => 'Node: Article', - 'node:article:c' => 'Node: Article: Unknown (c)', - 'node:b' => 'Node: Unknown (b)', - 'node:b:c' => 'Node: Unknown (b): Unknown (c)', - 'a' => 'Unknown (a)', - 'a:b' => 'Unknown (a): Unknown (b)', - 'a:b:c' => 'Unknown (a): Unknown (b): Unknown (c)', - 'a:b:c:d' => 'Unknown (a): Unknown (b): Unknown (c): Unknown (d)', - ); - - foreach ($test_cases as $input => $expected_output) { - drupal_static_reset('metatag_config_instance_label'); - $actual_output = metatag_config_instance_label($input); - $this->assertEqual($actual_output, $expected_output); - } - } -} - - -// TODO: Test each meta tag. -// TODO: Scenarios. -// -// 1. Node -// * No language assignment. -// * First save. -// -// 2. Node -// * No language assignment. -// * Edit existing revision. -// -// 3. Node -// * No language assignment. -// * Create new revision. -// * Publish new revision. -// -// 4. Node -// * No language assignment. -// * Create new revision. -// * Delete new revision. -// -// 5. Node + Translation -// * No language assignment -// * Change language assignment. -// * Edit existing revision. -// -// 6. Node + Translation -// * No language assignment -// * Change language assignment. -// * Create new revision. -// * Publish new revision. -// -// 7. Node + Translation -// * No language assignment -// * Change language assignment. -// * Create new revision. -// * Delete new revision. -// -// 8. Node + Translation -// * Initial language assignment -// -// 9. Node + Translation -// * Initial language assignment -// * Create new revision. -// * Publish new revision. -// -// 10. Node + Translation -// * Initial language assignment -// * Create new revision. -// * Delete new revision. -// -// 11. Node + Translation -// * Initial language assignment -// * Change language assignment. -// * Create new revision. -// * Publish new revision. -// -// 12. Node + Translation -// * Initial language assignment -// * Change language assignment. -// * Create new revision. -// * Delete new revision. -// -// 13. Node + Translation -// * Initial language assignment -// * Create translated node. -// -// 14. Node + Translation -// * Initial language assignment -// * Create new revision. -// * Publish new revision. -// * Create translated node. -// -// 15. Node + Translation -// * Initial language assignment -// * Create new revision. -// * Publish new revision. -// * Create translated node. -// * Delete translated node. -// -// 16. Node + Translation -// * Initial language assignment -// * Create translated node. -// * Delete original node. -// -// 17. Node + Translation -// * Initial language assignment -// * Create new revision. -// * Publish new revision. -// * Create translated node. -// * Delete original node. -// -// 18. Node + entity_translation -// * Initial language assignment -// * Create translated node. -// -// 19. Node + entity_translation -// * Initial language assignment -// * Create translated node. -// * Delete original. -// -// 20. Node + entity_translation -// * Initial language assignment -// * Create translated node. -// * Create new revision. -// * Publish new revision. -// -// 21. Node + entity_translation -// * Initial language assignment -// * Create translated node. -// * Create new revision. -// * Publish new revision. -// * Delete new revision. -// -// 22. Node + entity_translation -// * Initial language assignment -// * Create translated node. -// * Create new revision. -// * Publish new revision. -// * Delete original. -// -// 23. Node + entity_translation -// * Initial language assignment -// * Create translated node. -// * Create new revision. -// * Publish new revision. -// * Delete original. -// -// 24. Node + entity_translation -// * Initial language assignment -// * Create new revision. -// * Publish new revision. -// * Create translated node. -// -// 25. Node + entity_translation -// * Initial language assignment -// * Create new revision. -// * Publish new revision. -// * Create translated node. -// * Delete new revision. -// -// -// 30. Node + i18n -// -// -// 50. Term -// * Create term. -// -// 51. Term -// * Create term. -// * Change values. -// -// -// 60. User -// * Create user. -// -// 61. User -// * Create user. -// * Change values. -// -// -// 70. Custom path -// * Defaults loaded. diff --git a/sites/all/modules/contrib/seo/metatag/metatag.tokens.inc b/sites/all/modules/contrib/seo/metatag/metatag.tokens.inc index 498bdf1..80310c3 100644 --- a/sites/all/modules/contrib/seo/metatag/metatag.tokens.inc +++ b/sites/all/modules/contrib/seo/metatag/metatag.tokens.inc @@ -1,4 +1,5 @@ $value['label'], - 'description' => $value['description'] + 'name' => $label, + 'description' => isset($value['description']) ? $value['description'] : '', ); } if (module_exists('taxonomy')) { $info['tokens']['term']['metatag'] = array( - 'name' => t('Metatag.'), - 'description' => t('Metatag.'), - 'type' => 'metatag' + 'name' => t('Meta tags'), + 'description' => t('Meta tags for this taxonomy term.'), + 'type' => 'metatag', ); } if (module_exists('node')) { $info['tokens']['node']['metatag'] = array( - 'name' => t('Metatag.'), - 'description' => t('Metatag.'), - 'type' => 'metatag' + 'name' => t('Meta tags'), + 'description' => t('Meta tags for this node.'), + 'type' => 'metatag', ); } + if (module_exists('user')) { + $info['tokens']['user']['metatag'] = array( + 'name' => t('Meta tags'), + 'description' => t('Meta tags for this user.'), + 'type' => 'metatag', + ); + } + + // A custom pager. + $pager = variable_get('metatag_pager_string', 'Page PAGER | '); + $page = str_replace('PAGER', 12, $pager); + $info['tokens']['current-page']['pager'] = array( + 'name' => t('Custom pager'), + 'description' => t('A custom pager (from the Metatag module). Currently set to "@pager" which would be output as e.g. "@page".', array('@pager' => $pager, '@page' => $page)), + ); + return $info; } @@ -55,7 +78,7 @@ function metatag_tokens($type, $tokens, array $data = array(), array $options = if ($type == 'metatag' && !empty($data['metatag'])) { $metatag = $data['metatag']; foreach ($tokens as $name => $original) { - if(isset($metatag[$name])){ + if (isset($metatag[$name])) { $replacements[$original] = $sanitize ? filter_xss($metatag[$name]) : $metatag[$name]; } } @@ -81,6 +104,30 @@ function metatag_tokens($type, $tokens, array $data = array(), array $options = } } + // User tokens. + if ($type == 'user' && !empty($data['user'])) { + $account = $data['user']; + + if ($metatag_tokens = token_find_with_prefix($tokens, 'metatag')) { + $result = metatag_token_generate_array($account, 'user', 'user'); + $replacements += token_generate('metatag', $metatag_tokens, array('metatag' => $result), $options); + } + } + + // Custom pager. + if ($type == 'current-page') { + foreach ($tokens as $name => $original) { + switch ($name) { + case 'pager': + $pager = metatag_get_current_pager(); + if (!empty($pager)) { + $replacements[$original] = $pager; + } + break; + } + } + } + return $replacements; } @@ -91,7 +138,7 @@ function metatag_token_generate_array($entity, $entity_type, $bundle) { if (metatag_entity_supports_metatags($entity_type, $bundle)) { $token_type = token_get_entity_mapping('entity', $entity_type); - $instance = "{$entity_type}:{$bundle}"; + $instance = metatag_get_entity_metatags_instance($entity, $entity_type, $bundle); $options = array(); $options['token data'][$token_type] = $entity; $options['entity'] = $entity; @@ -133,18 +180,29 @@ function metatag_token_generate_array($entity, $entity_type, $bundle) { */ function _metatag_token_process_metatag($metatags, $token_type) { foreach ($metatags as $metatag => $data) { + // Skip values that are not strings. + if (!is_string($data['value'])) { + continue; + } + // Explode all metatag token in field. $data_tokens = token_scan($data['value']); if (isset($data_tokens[$token_type])) { - foreach($data_tokens[$token_type] as $key => $value) { + foreach ($data_tokens[$token_type] as $key => $value) { $metatag_parts = explode(':', $key); // Check entity metatag token. Like [', - '#markup' => t('Values assigned here inherit from the global defaults and will override any other meta tags assigned elsewhere.', array('@url' => url('admin/config/search/metatags/config/global'))), + '#markup' => t('Values assigned here inherit from the global defaults.', array('@url' => url('admin/config/search/metatags/config/global'))), '#suffix' => '
', '#weight' => -99, ); - // Show all tokens. - $form['metatags']['tokens']['#token_types'] = 'all'; - $form['metatags']['#type'] = 'container'; unset($form['metatags']['#collapsed']); unset($form['metatags']['#collapsible']); @@ -175,6 +184,12 @@ function metatag_context_config_edit_form_submit($form, &$form_state) { $paths = array_combine($paths, $paths); $context->conditions['path']['values'] = $paths; context_save($context); + + // Update the i18n strings. + if (!empty($context->reactions['metatag_context_reaction']['metatags'][LANGUAGE_NONE])) { + metatag_translations_update($context->reactions['metatag_context_reaction']['metatags'][LANGUAGE_NONE], 'metatag_context:' . $context->name); + } + $form_state['redirect'] = 'admin/config/search/metatags/context'; } @@ -185,7 +200,7 @@ function metatag_context_delete_form($form, &$form_state, $context) { $form_state['metatag_context']['context'] = $context; $form['delete'] = array( - '#value' => 'This action will permanently remove this item from your database.' + '#value' => 'This action will permanently remove this item from your database.', ); $form['actions']['#type'] = 'actions'; @@ -227,8 +242,7 @@ function metatag_context_load_default_context() { $context->metatag = TRUE; $context->conditions = array( 'path' => array( - 'values' => array( - ), + 'values' => array(), ), ); $context->reactions = array( diff --git a/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.context.inc b/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.context.inc index 15984fb..c3d2bca 100644 --- a/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.context.inc +++ b/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.context.inc @@ -1,9 +1,13 @@ array('node', 'term', 'user'), + ); $form['help'] = array( '#prefix' => '',
@@ -90,6 +96,20 @@ class metatag_context_reaction extends context_reaction {
'#default_value' => isset($data['metatag_admin']) ? $data['metatag_admin'] : '',
);
+ // Add weight for current metatag_context.
+ $default_weight = 0;
+ if (!empty($context->reactions['metatag_context_reaction']['weight'])) {
+ $default_weight = (int) $context->reactions['metatag_context_reaction']['weight'];
+ }
+ $form['weight'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Weight'),
+ '#size' => 2,
+ '#default_value' => $default_weight,
+ '#description' => t('A higher weight will make this context be executed later, overriding other context meta tags.'),
+ '#weight' => -99,
+ );
+
return $form;
}
@@ -112,14 +132,23 @@ class metatag_context_reaction extends context_reaction {
if (isset($metadata_array[LANGUAGE_NONE])) {
$metadata_array = $metadata_array[LANGUAGE_NONE];
}
- foreach ($metadata_array as $key => $data) {
- if (!empty($data['value'])) {
- $metatags[$key] = $data;//t(check_plain($data['value']));
+
+ // Translate all of the meta tags using i18n, but don't update the
+ // strings.
+ metatag_translate_metatags($metadata_array, 'metatag_context:' . $context->name, NULL, FALSE);
+
+ // Add the meta tags to the output.
+ foreach ($metadata_array as $langcode => $tags) {
+ foreach ($tags as $name => $value) {
+ $metatags[$langcode][$name] = $value;
}
}
// Add this context to the list of instances.
- $instance_names[] = $context->name;
+ $weight = isset($context->reactions['metatag_context_reaction']['weight'])
+ ? $context->reactions['metatag_context_reaction']['weight']
+ : 0;
+ $instance_names[] = array('name' => $context->name, 'weight' => $weight);
}
}
@@ -128,15 +157,37 @@ class metatag_context_reaction extends context_reaction {
// Load the global defaults.
$metatags += metatag_config_load_with_defaults('');
+ // Sort by weight.
+ uasort($instance_names, 'drupal_sort_weight');
+
+ // Keep names only.
+ $instance_names = array_map('current', $instance_names);
+
// Compile the identifier for this combination based on the context
// names.
- asort($instance_names);
$instance = 'context:' . implode(',', $instance_names);
$options['instance'] = $instance;
+ // If an entity & entity type were saved elsewhere, grab them for later.
+ // @see hook_entity_prepare_view().
+ $entities = drupal_static('metatag_context_entities');
+ if (!empty($entities) && (count($entities[1]) == 1)) {
+ $data = array_values($entities[1]);
+ $options['entity'] = $data[0];
+ $options['entity_type'] = $entities[0];
+ $options['token data'] = array($entities[0] => $data[0]);
+ }
+
+ // Don't output meta tags that only contain the pager.
+ $current_pager = metatag_get_current_pager();
+
foreach ($metatags as $metatag => $data) {
if ($metatag_instance = metatag_get_instance($metatag, $data)) {
- $output[$metatag] = $metatag_instance->getElement($options);
+ $tag_output = $metatag_instance->getElement($options);
+ // Don't output the pager if that's all there is.
+ if ($tag_output != $current_pager) {
+ $output[$metatag] = $tag_output;
+ }
}
}
@@ -146,6 +197,7 @@ class metatag_context_reaction extends context_reaction {
}
}
}
+
}
/**
@@ -157,7 +209,7 @@ function metatag_context_context_default_contexts() {
$context = new stdClass();
$context->disabled = TRUE; /* Edit this to true to make a default context disabled initially */
$context->api_version = 3;
- $context->name = 'user_login';
+ $context->name = 'metatag_context_user_login';
$context->description = 'A default Metatag:Context definition for the user login page. This needs to be enabled and then it can be customized as needed.';
$context->tag = 'Metatag';
$context->conditions = array(
@@ -176,9 +228,11 @@ function metatag_context_context_default_contexts() {
$context->reactions = array(
'metatag_context_reaction' => array(
'metatags' => array(
- 'title' => array(
- 'value' => '[current-page:title] | [site:name]',
- 'default' => '[current-page:title] | [site:name]',
+ 'und' => array(
+ 'title' => array(
+ 'value' => '[current-page:title] | [site:name]',
+ 'default' => '[current-page:title] | [site:name]',
+ ),
),
),
'metatag_admin' => 1,
@@ -190,7 +244,7 @@ function metatag_context_context_default_contexts() {
$context = new stdClass();
$context->disabled = TRUE; /* Edit this to true to make a default context disabled initially */
$context->api_version = 3;
- $context->name = 'user_register';
+ $context->name = 'metatag_context_user_register';
$context->description = 'A default Metatag:Context definition for the user registration page. This needs to be enabled and then it can be customized as needed.';
$context->tag = 'Metatag';
$context->conditions = array(
@@ -208,9 +262,11 @@ function metatag_context_context_default_contexts() {
$context->reactions = array(
'metatag_context_reaction' => array(
'metatags' => array(
- 'title' => array(
- 'value' => '[current-page:title] | [site:name]',
- 'default' => '[current-page:title] | [site:name]',
+ 'und' => array(
+ 'title' => array(
+ 'value' => '[current-page:title] | [site:name]',
+ 'default' => '[current-page:title] | [site:name]',
+ ),
),
),
'metatag_admin' => 1,
@@ -223,7 +279,7 @@ function metatag_context_context_default_contexts() {
$context = new stdClass();
$context->disabled = TRUE; /* Edit this to true to make a default context disabled initially */
$context->api_version = 3;
- $context->name = 'forum';
+ $context->name = 'metatag_context_forum';
$context->description = 'A default Metatag:Context definition for the main forum page. This needs to be enabled and then it can be customized as needed.';
$context->tag = 'Metatag';
$context->conditions = array(
@@ -236,9 +292,11 @@ function metatag_context_context_default_contexts() {
$context->reactions = array(
'metatag_context_reaction' => array(
'metatags' => array(
- 'title' => array(
- 'value' => '[current-page:title] | [site:name]',
- 'default' => '[current-page:title] | [site:name]',
+ 'und' => array(
+ 'title' => array(
+ 'value' => '[current-page:title] | [site:name]',
+ 'default' => '[current-page:title] | [site:name]',
+ ),
),
),
'metatag_admin' => 1,
@@ -252,7 +310,7 @@ function metatag_context_context_default_contexts() {
$context = new stdClass();
$context->disabled = TRUE; /* Edit this to true to make a default context disabled initially */
$context->api_version = 3;
- $context->name = 'blog';
+ $context->name = 'metatag_context_blog';
$context->description = 'A default Metatag:Context definition for the main blog page. This needs to be enabled and then it can be customized as needed. Note: this does not cover the individual user blog pages, only the main blog page.';
$context->tag = 'Metatag';
$context->conditions = array(
@@ -265,9 +323,11 @@ function metatag_context_context_default_contexts() {
$context->reactions = array(
'metatag_context_reaction' => array(
'metatags' => array(
- 'title' => array(
- 'value' => '[current-page:title] | [site:name]',
- 'default' => '[current-page:title] | [site:name]',
+ 'und' => array(
+ 'title' => array(
+ 'value' => '[current-page:title] | [site:name]',
+ 'default' => '[current-page:title] | [site:name]',
+ ),
),
),
'metatag_admin' => 1,
diff --git a/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.i18n.inc b/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.i18n.inc
new file mode 100644
index 0000000..df8bfd9
--- /dev/null
+++ b/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.i18n.inc
@@ -0,0 +1,132 @@
+ t('Metatag:Context configurations'),
+ // Callback to load all config objects.
+ 'list callback' => 'metatag_context_i18n_list_contexts',
+ // The object load callback.
+ // 'load callback' => 'metatag_context_i18n_load',
+ // @todo Custom i18n object overrides.
+ // 'class' => 'metatag_context_i18n_metatag',
+ // @todo Is this needed? What does it do?
+ // 'translation set' => TRUE,
+
+ // The object key field.
+ 'key' => 'name',
+ // Placeholders for automatic paths. This connects the 'key' to strings in
+ // the paths listed below.
+ // 'placeholders' => array(
+ // '%name' => 'name',
+ // ),
+ // To produce edit links automatically.
+ // 'edit path' => 'admin/config/search/metatags/config/%instance',
+ // Auto-generate a 'translate' tab.
+ // 'translate tab' => 'admin/config/search/metatags/config/%instance/translate',
+
+ // Properties for string translation.
+ 'string translation' => array(
+ // The textgroup, type and (below) name will be concatenated into a single
+ // string as the {locales_source} context.
+ 'textgroup' => 'metatag',
+ 'type' => 'metatag_context',
+ // Table where the object is stored, to automate string lists.
+ 'table' => 'context',
+ // Translatable properties of these objects, this will be added later.
+ 'properties' => array(),
+ // The path to translate individual strings.
+ // 'translate path' => 'admin/config/search/metatags/config/%instance/translate/%i18n_language',
+ ),
+ );
+
+ // Compile all of the tags to add to the translation stack.
+ $meta_tag_info = metatag_get_info();
+ $groups = $meta_tag_info['groups'];
+ foreach ($meta_tag_info['tags'] as $tag_info) {
+ // Ignore certain field types that aren't translatable, mostly fields that
+ // list predetermined options in various forms.
+ if (!empty($tag_info['class']) && $tag_info['class'] == 'DrupalListMetaTag') {
+ continue;
+ }
+ elseif (!empty($tag_info['form']['#type']) && $tag_info['form']['#type'] == 'select') {
+ continue;
+ }
+ elseif (!empty($tag_info['form']['#options'])) {
+ continue;
+ }
+
+ // Build a suitable structure for this meta tag.
+ $tag_name = $tag_info['name'];
+ $title = $tag_info['label'];
+ if (!empty($tag_info['group'])) {
+ $tag_group = $tag_info['group'];
+ $group_label = !empty($groups[$tag_group]['label']) ? $groups[$tag_group]['label'] : $tag_group;
+ $title = $group_label . ': ' . $title;
+ }
+
+ $info['metatag_context']['string translation']['properties'][$tag_name] = array(
+ 'title' => $title,
+ 'field' => "reactions.metatag_context_reaction.und.{$tag_name}.value",
+ );
+ }
+
+ return $info;
+}
+
+/**
+ * Implements hook_i18n_string_list().
+ *
+ * @todo Functionality to delete translation records when Panels are deleted.
+ */
+function metatag_context_i18n_string_list($group) {
+ if ($group == 'metatag' || $group == 'all') {
+ $strings = array();
+
+ foreach (context_context_list() as $context_name) {
+ $context = context_load($context_name);
+ if (!empty($context->reactions['metatag_context_reaction']['metatags'][LANGUAGE_NONE])) {
+ $new_strings = array();
+ foreach ($context->reactions['metatag_context_reaction']['metatags'][LANGUAGE_NONE] as $name => $value) {
+ if (isset($value['value'])) {
+ // Don't translate meta tags that are arrays.
+ if (is_array($value['value'])) {
+ continue;
+ }
+ // Collapse the array down one level.
+ else {
+ $new_strings[$name] = $value['value'];
+ }
+ }
+ }
+ $strings['metatag']['metatag_context'][$context->name] = $new_strings;
+ }
+ }
+
+ return $strings;
+ }
+}
+
+/**
+ * List callback.
+ */
+function metatag_context_i18n_list_contexts() {
+ ctools_include('export');
+ $configs = ctools_export_crud_load_all('metatag_config');
+ if (!empty($configs)) {
+ // Unserialize the config array.
+ foreach ($configs as &$config) {
+ if (is_string($config->config)) {
+ $config->config = unserialize($config->config);
+ }
+ }
+ return $configs;
+ }
+}
diff --git a/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.info b/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.info
index 9289e30..62b620b 100644
--- a/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.info
+++ b/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.info
@@ -2,14 +2,18 @@ name = Metatag: Context
description = "Assigned Metatag using Context definitions, allowing them to be assigned by path and other criteria."
package = SEO
core = 7.x
-dependencies[] = context
-dependencies[] = metatag
-files[] = metatag_context.test
+
configure = admin/config/search/metatags/context
-; Information added by Drupal.org packaging script on 2015-05-01
-version = "7.x-1.5-beta1+22-dev"
+dependencies[] = metatag:metatag
+dependencies[] = context:context
+
+; Tests.
+files[] = tests/metatag_context.test
+files[] = tests/metatag_context.i18n.test
+
+; Information added by Drupal.org packaging script on 2018-04-04
+version = "7.x-1.25"
core = "7.x"
project = "metatag"
-datestamp = "1430441289"
-
+datestamp = "1522849089"
diff --git a/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.install b/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.install
index 78415ce..b422985 100644
--- a/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.install
+++ b/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.install
@@ -1,4 +1,5 @@
t('Context'));
+ return $info;
+}
diff --git a/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.module b/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.module
index dfa9b82..f70ab65 100644
--- a/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.module
+++ b/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.module
@@ -1,4 +1,5 @@
"3");
}
}
+
+/**
+ * Implements hook_module_implements_alter().
+ */
+function metatag_context_module_implements_alter(&$implementations, $hook) {
+ if ($hook == 'page_alter') {
+ // Run metatag_context_page_alter() last.
+ $group = $implementations['metatag_context'];
+ unset($implementations['metatag_context']);
+ $implementations['metatag_context'] = $group;
+ }
+}
diff --git a/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.test b/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.test
deleted file mode 100644
index 7dfc963..0000000
--- a/sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.test
+++ /dev/null
@@ -1,145 +0,0 @@
- 'Meta tag context tests',
- 'description' => 'Test basic meta tag context functionality.',
- 'group' => 'Metatag',
- );
- }
-
- /**
- * Prepares the testing environment
- */
- public function setUp(array $modules = array()) {
- $modules[] = 'metatag';
- $modules[] = 'metatag_context';
- parent::setUp($modules);
- // Create user.
- $this->privileged_user = $this->drupalCreateUser(array(
- 'bypass node access',
- 'administer content types',
- 'administer meta tags',
- ));
- $this->drupalLogin($this->privileged_user);
- // Create content type, with underscores.
- $type_name = strtolower($this->randomName(8)) . '_test';
- $type = $this->drupalCreateContentType(array('name' => $type_name, 'type' => $type_name));
- $this->type = $type->type;
- // Store a valid URL name, with hyphens instead of underscores.
- $this->hyphen_type = str_replace('_', '-', $this->type);
- }
-
- /**
- * Performs the basic tests.
- */
- public function testMetatagContextBasic() {
- // Create content type node.
- $this->drupalPost('node/add/' . $this->hyphen_type, array('title' => $this->randomName(8)), t('Save'));
- $this->context_name = drupal_strtolower($this->randomName(8));
-
- // Generate metatags and check content.
- $this->metatag_pages['node'] = $this->createMetatagObject('node/1', 'node_metatags');
- $this->metatag_pages['page'] = $this->createMetatagObject(' ',
+ '#suffix' => ' ',
+ );
+ return $output;
+}
diff --git a/sites/all/modules/contrib/seo/metatag/metatag_importer/metatag_importer.drush.inc b/sites/all/modules/contrib/seo/metatag/metatag_importer/metatag_importer.drush.inc
new file mode 100644
index 0000000..883f6cb
--- /dev/null
+++ b/sites/all/modules/contrib/seo/metatag/metatag_importer/metatag_importer.drush.inc
@@ -0,0 +1,94 @@
+ dt('Convert data from Metatags Quick into Metatag'),
+ 'drupal dependencies' => array('metatag', 'metatag_importer'),
+ 'aliases' => array('mtcmq'),
+ );
+
+ $items['metatag-convert-nodewords'] = array(
+ 'description' => dt('Convert data from Nodewords into Metatag.'),
+ 'drupal dependencies' => array('metatag', 'metatag_importer'),
+ 'aliases' => array('mtcnw'),
+ );
+
+ $items['metatag-convert-page-title'] = array(
+ 'description' => dt('Convert data from Page Title into Metatag.'),
+ 'drupal dependencies' => array('metatag', 'metatag_importer'),
+ 'aliases' => array('mtcpt'),
+ );
+
+ return $items;
+}
+
+/**
+ * Callback to convert all Metatags Quick data.
+ */
+function drush_metatag_importer_metatag_convert_metatags_quick() {
+ if (!drush_confirm(dt('Ready to convert all data from Metatags Quick?'))) {
+ return;
+ }
+
+ include 'metatag_importer.metatags_quick.inc';
+ metatag_importer_metatags_quick_import();
+}
+
+/**
+ * Callback to convert all Nodewords data.
+ */
+function drush_metatag_importer_metatag_convert_nodewords() {
+ if (!drush_confirm(dt('Ready to convert all data from Nodewords?'))) {
+ return;
+ }
+
+ // Need to make sure the Nodewords table actually exists.
+ if (!db_table_exists('nodewords')) {
+ drush_set_error('metatag_importer', dt('Could not find the nodewords table!'));
+ return;
+ }
+
+ // Offload all of the logic to the code contained in the admin file.
+ include 'metatag_importer.nodewords.inc';
+
+ // Start the import.
+ $types = array_keys(_metatag_importer_list_nodewords());
+ _metatag_importer_import($types);
+
+ drush_print(dt('Data converesion finished.'));
+}
+
+/**
+ * Callback to convert Page Title data.
+ */
+function drush_metatag_importer_metatag_convert_page_title() {
+ if (!db_table_exists('page_title')) {
+ drush_set_error('metatag_importer', dt('Could not find the page_title table!'));
+ return;
+ }
+
+ $records = db_query("SELECT COUNT(id) FROM {page_title} WHERE type IN ('node', 'taxonomy_term', 'user')")->fetchField();
+
+ if (empty($records)) {
+ return dt('There are no page_title records to convert!');
+ }
+
+ if (!drush_confirm(dt('Ready to convert !count record(s) from Page Title?', array('!count' => $records)))) {
+ return;
+ }
+
+ include 'metatag_importer.page_title.inc';
+
+ // Start the importer.
+ $count = metatag_importer_for_page_title();
+
+ drush_print(dt('Converted !count record(s) from the Page Title module.', array('!count' => $count)));
+}
diff --git a/sites/all/modules/contrib/seo/metatag/metatag_importer/metatag_importer.info b/sites/all/modules/contrib/seo/metatag/metatag_importer/metatag_importer.info
new file mode 100644
index 0000000..2eda31c
--- /dev/null
+++ b/sites/all/modules/contrib/seo/metatag/metatag_importer/metatag_importer.info
@@ -0,0 +1,16 @@
+name = Metatag Importer
+description = Import data from other modules into Metatag.
+core = 7.x
+package = SEO
+
+; Need Metatag.
+dependencies[] = metatag:metatag
+
+; Tests.
+files[] = tests/metatag_importer.test
+
+; Information added by Drupal.org packaging script on 2018-04-04
+version = "7.x-1.25"
+core = "7.x"
+project = "metatag"
+datestamp = "1522849089"
diff --git a/sites/all/modules/contrib/seo/metatag/metatag_importer/metatag_importer.metatags_quick.inc b/sites/all/modules/contrib/seo/metatag/metatag_importer/metatag_importer.metatags_quick.inc
new file mode 100644
index 0000000..7b5b506
--- /dev/null
+++ b/sites/all/modules/contrib/seo/metatag/metatag_importer/metatag_importer.metatags_quick.inc
@@ -0,0 +1,225 @@
+ count($data));
+ if (drupal_is_cli() && function_exists('drush_print')) {
+ drush_print(dt($message, $vars));
+ }
+ else {
+ drupal_set_message(t($message, $vars));
+ }
+
+ // Get all of the data for these fields.
+ $data = metatag_importer_get_quick_data($fields);
+
+ // Process the data.
+ foreach ($data as $quick) {
+ $message = 'Converting @count records from Metatags Quick..';
+ $vars = array('@count' => count($quick));
+ if (drupal_is_cli() && function_exists('drush_print')) {
+ drush_print(dt($message, $vars));
+ }
+ else {
+ drupal_set_message(t($message, $vars));
+ }
+
+ metatag_importer_metatags_quick_process($quick);
+ }
+
+ if (drupal_is_cli() && function_exists('drush_print')) {
+ drush_print(dt('All done!'));
+ }
+ else {
+ drupal_set_message(t('All done!'));
+ }
+}
+
+/**
+ * Process a row from Metatags Quick.
+ *
+ * Imports data into metatag structure, and deletes Metatags Quick data.
+ *
+ * @param array $quick
+ * Row of data from Metatags Quick.
+ */
+function metatag_importer_metatags_quick_process(array $quick) {
+ // Identify which Metatag meta tags will be filled by Metatags Quick values.
+ $tag_map = array(
+ 'title' => 'title',
+ 'keywords' => 'keywords',
+ 'abstract' => 'abstract',
+ 'description' => 'description',
+ 'canonical' => 'canonical',
+ );
+ if (module_exists('metatag_opengraph')) {
+ $tag_map['og:title'] = 'title';
+ $tag_map['og:description'] = 'description';
+ }
+ if (module_exists('metatag_twitter_cards')) {
+ $tag_map['twitter:title'] = 'title';
+ $tag_map['twitter:description'] = 'description';
+ }
+
+ $entity_type = $quick['entity_type'];
+ $entity_id = $quick['entity_id'];
+ $revision_id = $quick['revision_id'];
+ $langcode = $quick['language'];
+
+ // Fallback to entity language if no field language is set.
+ if (LANGUAGE_NONE == $langcode) {
+ $entities = entity_load($entity_type, array($entity_id));
+ if (!empty($entities[$entity_id])) {
+ $langcode = entity_language($entity_type, $entities[$entity_id]);
+ }
+ }
+
+ // Check for an existing record.
+ $data = metatag_metatags_load($entity_type, $entity_id);
+
+ // Drop back one level because the results will be keyed by revision_id.
+ if (!empty($data)) {
+ $data = reset($data);
+ }
+
+ // Map the Quick meta tags.
+ foreach ($tag_map as $dest => $source) {
+ if (!empty($quick['fields'][$source]['value'])) {
+ $data[$langcode][$dest] = array('value' => $quick['fields'][$source]['value']);
+ // Add the default suffix to the page title.
+ if ($dest == 'title') {
+ $data[$langcode][$dest]['value'] .= ' | [site:name]';
+ }
+ }
+ }
+
+ // Create or update the {metatag} record.
+ if (!empty($data)) {
+ metatag_metatags_save($entity_type, $entity_id, $revision_id, $data);
+ }
+
+ if (!empty($quick['fields'])) {
+ metatag_importer_delete_quick_data($quick['fields']);
+ }
+
+ // Reset the entity cache. If entitycache module is used, this also resets
+ // its permanent cache.
+ entity_get_controller($entity_type)->resetCache(array($entity_id));
+}
+
+/**
+ * Get all fields from Metatags Quick.
+ *
+ * @return array
+ * Array of field names, keyed by name.
+ */
+function metatag_importer_get_quick_fields() {
+ $fields = array();
+
+ // Get a list of all entities that use a Metatags Quick field.
+ foreach (field_info_instances() as $entity_type => $bundles) {
+ // Skip the custon entity type provided by Metatags Quick.
+ if ($entity_type == 'metatags_path_based') {
+ continue;
+ }
+ foreach ($bundles as $bundle_name => $bundle) {
+ foreach ($bundle as $field_name => $field) {
+ if ($field['widget']['module'] == 'metatags_quick') {
+ $fields[$field_name] = $field_name;
+ }
+ }
+ }
+ }
+
+ return $fields;
+}
+
+/**
+ * Get metatags_quick data from the database.
+ *
+ * @param array $fields
+ * Array of field names.
+ *
+ * @return array
+ * Metadata
+ */
+function metatag_importer_get_quick_data(array $fields) {
+ $data = array();
+ foreach ($fields as $field_name) {
+ $meta_tag = str_replace('meta_', '', str_replace('field_', '', $field_name));
+ $results = db_select('field_data_' . $field_name, 'f')
+ ->fields('f', array(
+ 'entity_type',
+ 'bundle',
+ 'entity_id',
+ 'revision_id',
+ 'language',
+ $field_name . '_metatags_quick',
+ ))
+ ->condition('f.entity_type', array('metatags_path_based'), '<>')
+ ->orderBy('f.entity_type', 'ASC')
+ ->orderBy('f.entity_id', 'ASC')
+ ->orderBy('f.revision_id', 'ASC')
+ ->execute();
+
+ foreach ($results as $result) {
+ $id = implode(':', array(
+ $result->entity_type,
+ $result->entity_id,
+ $result->revision_id,
+ $result->language,
+ ));
+
+ if (!isset($data[$id])) {
+ $data[$id] = array(
+ 'entity_type' => $result->entity_type,
+ 'bundle' => $result->bundle,
+ 'entity_id' => $result->entity_id,
+ 'revision_id' => $result->revision_id,
+ 'language' => $result->language,
+ 'fields' => array(),
+ );
+ }
+ $data[$id]['fields'][$meta_tag] = array(
+ 'field_name' => $field_name,
+ 'value' => $result->{$field_name . '_metatags_quick'},
+ 'meta_tag' => $meta_tag,
+ );
+ }
+ }
+
+ return $data;
+}
+
+/**
+ * Delete the old metatags_quick records.
+ *
+ * @param array $quick
+ * Records to delete.
+ */
+function metatag_importer_delete_quick_data(array $quick) {
+ foreach ($quick as $field) {
+ db_delete('field_data_' . $field['field_name'])
+ ->condition('entity_type', $quick['entity_type'])
+ ->condition('entity_id', $quick['entity_id'])
+ ->condition('language', $quick['language'])
+ ->execute();
+
+ db_delete('field_revision_' . $field['field_name'])
+ ->condition('entity_type', $quick['entity_type'])
+ ->condition('entity_id', $quick['entity_id'])
+ ->condition('language', $quick['language'])
+ ->execute();
+ }
+}
diff --git a/sites/all/modules/contrib/seo/metatag/metatag_importer/metatag_importer.module b/sites/all/modules/contrib/seo/metatag/metatag_importer/metatag_importer.module
new file mode 100644
index 0000000..31d0ef6
--- /dev/null
+++ b/sites/all/modules/contrib/seo/metatag/metatag_importer/metatag_importer.module
@@ -0,0 +1,42 @@
+ 'Importer',
+ 'description' => 'Migrate settings and data from various modules to the Metatag module.',
+ 'page callback' => 'metatag_importer_admin_page',
+ 'access arguments' => array('administer meta tags'),
+ 'file' => 'metatag_importer.admin.inc',
+ 'type' => MENU_LOCAL_TASK,
+ );
+
+ $items['admin/config/search/metatags/importer/nodewords'] = array(
+ 'title' => 'Import from Nodewords',
+ 'description' => 'Migrate settings and data from the Drupal 6 Nodewords module to the Drupal 7 Metatag module.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('metatag_importer_nodewords_form'),
+ 'access arguments' => array('administer meta tags'),
+ 'file' => 'metatag_importer.nodewords.inc',
+ 'type' => MENU_LOCAL_ACTION,
+ );
+
+ $items['admin/config/search/metatags/importer/page-title'] = array(
+ 'title' => 'Import from Page Title',
+ 'description' => 'Migrate settings and data from the Drupal 6 Nodewords module to the Drupal 7 Metatag module.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('metatag_importer_page_title_form'),
+ 'access arguments' => array('administer meta tags'),
+ 'file' => 'metatag_importer.page_title.inc',
+ 'type' => MENU_LOCAL_ACTION,
+ );
+
+ return $items;
+}
diff --git a/sites/all/modules/contrib/seo/metatag/metatag_importer/metatag_importer.nodewords.inc b/sites/all/modules/contrib/seo/metatag/metatag_importer/metatag_importer.nodewords.inc
new file mode 100644
index 0000000..93ae682
--- /dev/null
+++ b/sites/all/modules/contrib/seo/metatag/metatag_importer/metatag_importer.nodewords.inc
@@ -0,0 +1,581 @@
+ 'checkboxes',
+ '#title' => t('Records to import'),
+ '#options' => $types,
+ );
+
+ $form['notes'] = array(
+ '#markup' => ' ' . t('Notes') . ':' . ' ',
+ '#suffix' => ' ',
+ '#suffix' => ' ' . t('The following tokens are available. If you would like to have the characters \'[\' and \']\' please use the html entity codes \'%5B\' or \'%5D\' or they will get replaced with empty space.' . ''
+ . '
',
+ );
+
+ $form['actions']['migrate'] = array(
+ '#type' => 'submit',
+ '#value' => t('Migrate all records'),
+ );
+ }
+ else {
+ $form['ohbother'] = array(
+ '#markup' => t('Nothing has been found that needs to be imported.'),
+ '#prefix' => '' . check_plain($title) . "
\n";
+ }
+
+ parent::verbose($title . $message);
+ }
+
+ /**
+ * Load the Performance admin page and clear all caches.
+ */
+ public function clearAllCaches() {
+ $this->drupalGet('admin/config/development/performance');
+ $this->assertResponse(200);
+ $this->assertText(t('Performance'));
+ $this->assertText(t('Clear cache'));
+ $this->drupalPost(NULL, array(), t('Clear all caches'));
+ $this->assertResponse(200);
+ $this->assertText(t('Caches cleared'));
+ }
+
+ /**
+ * Create a content type for the tests.
+ */
+ function createContentType($machine_name, $label) {
+ // Create a content type.
+ $content_type = $this->drupalCreateContentType(array(
+ 'type' => $machine_name,
+ 'name' => $label,
+ ));
+
+ // Enable meta tags for this new content type.
+ metatag_entity_type_enable('node', $machine_name, TRUE);
+
+ return $content_type;
+ }
+
+ /**
+ * Create an admin user for the tests.
+ *
+ * @param array $extra_permissions
+ * An array of permission strings to be added to the user.
+ *
+ * @return object
+ * A user object.
+ */
+ function createAdminUser($extra_permissions = array()) {
+ $permissions = array(
+ // Basic permissions for the module.
+ 'administer meta tags',
+ 'edit meta tags',
+
+ // General admin access.
+ 'access administration pages',
+ );
+
+ // Reset the static variable used to identify permissions, otherwise it's
+ // possible the permissions check in drupalCreateUser will fail.
+ $this->checkPermissions(array(), TRUE);
+ cache_clear_all();
+
+ return $this->drupalCreateUser(array_merge($permissions, $extra_permissions));
+ }
+
+ /**
+ * Create a normal user for the tests.
+ *
+ * @param array $extra_permissions
+ * An array of permission strings to be added to the user.
+ *
+ * @return object
+ * A user object.
+ */
+ function createUser($extra_permissions) {
+ // Basic permissions for the module.
+ $permissions = array(
+ 'edit meta tags',
+ );
+
+ // Reset the static variable used to identify permissions, otherwise it's
+ // possible the permissions check in drupalCreateUser will fail.
+ $this->checkPermissions(array(), TRUE);
+ cache_clear_all();
+
+ return $this->drupalCreateUser(array_merge($permissions, $extra_permissions));
+ }
+
+ /**
+ * Returns a new vocabulary with random properties.
+ *
+ * @param $vocab_name
+ * If empty a random string will be used.
+ * @param $content_type
+ * Any content types listed will have a Taxonomy Term reference field added
+ * that points to the new vocabulary.
+ *
+ * @return object
+ * A vocabulary object.
+ */
+ function createVocabulary($vocab_name = NULL, $content_type = NULL) {
+ if (empty($vocab_name)) {
+ $vocab_name = $this->randomName();
+ }
+
+ // Create a vocabulary.
+ $vocabulary = new stdClass();
+ $vocabulary->name = $vocab_name;
+ $vocabulary->description = $vocab_name;
+ $vocabulary->machine_name = drupal_strtolower($vocab_name);
+ $vocabulary->help = '';
+ $vocabulary->weight = mt_rand(0, 10);
+ if (!empty($content_type)) {
+ $vocabulary->nodes = array($content_type => $content_type);
+ }
+ taxonomy_vocabulary_save($vocabulary);
+
+ // Enable meta tags for this new vocabulary.
+ metatag_entity_type_enable('taxonomy_term', $vocab_name, TRUE);
+
+ return $vocabulary;
+ }
+
+ /**
+ * Returns a new taxonomy term in a specific vocabulary.
+ *
+ * @param object $vocabulary
+ * The vocabulary to add the term to.
+ * @param string $term_name
+ * The name to use for the new vocabulary. If none is provided one will be
+ * generated randomly.
+ *
+ * @return object
+ * A taxonomy term object.
+ */
+ function createTerm($vocabulary, $term_name = NULL) {
+ if (empty($term_name)) {
+ $term_name = $this->randomName();
+ }
+
+ // Create an object to save.
+ $term = new stdClass();
+ $term->name = $term_name;
+ $term->description = $term_name;
+ // Use the first available text format.
+ $term->format = db_query_range('SELECT format FROM {filter_format}', 0, 1)->fetchField();
+ $term->vid = $vocabulary->vid;
+
+ // Save the term.
+ taxonomy_term_save($term);
+
+ return $term;
+ }
+
+ /**
+ * Return an list of default values.
+ *
+ * This should cover all of the default meta tags provided for a test:foo
+ * entity.
+ *
+ * @todo Expand to cover more meta tags.
+ *
+ * @see metatag_test_metatag_config_default()
+ */
+ function getTestDefaults() {
+ return array(
+ // Basic meta tags.
+ 'title' => array('value' => 'Test altered title'),
+ 'description' => array('value' => 'Test foo description'),
+ 'abstract' => array('value' => 'Test foo abstract'),
+ // 'keywords' => array('value' => ''),
+ // Advanced meta tags.
+ // 'robots' => array('value' => ''),
+ // 'news_keywords' => array('value' => ''),
+ // 'standout' => array('value' => ''),
+ // 'robots' => array('value' => ''),
+ // 'standout' => array('value' => ''),
+ 'generator' => array('value' => 'Drupal 7 (https://www.drupal.org)'),
+ // 'standout' => array('value' => ''),
+ // 'image_src' => array('value' => ''),
+ 'canonical' => array('value' => '[current-page:url:absolute]'),
+ 'shortlink' => array('value' => '[current-page:url:unaliased]'),
+ // 'publisher' => array('value' => ''),
+ // 'author' => array('value' => ''),
+ // 'original-source' => array('value' => ''),
+ // 'revisit-after' => array('value' => ''),
+ // 'content-language' => array('value' => ''),'
+ );
+ }
+
+ /**
+ * Add a locale to the site.
+ *
+ * This assumes the Locale module is enabled.
+ */
+ public function addSiteLanguage($langcode) {
+ // Load the language-add page.
+ $this->drupalGet('admin/config/regional/language/add');
+ $this->assertResponse(200, 'Loaded the language-add admin page.');
+
+ // Submit the language-add form.
+ $args = array(
+ 'langcode' => $langcode,
+ );
+ $this->drupalPost(NULL, $args, t('Add language'));
+ $this->assertResponse(200);
+
+ // Verify that the browser was returned to the main languages admin page.
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Redirected back to the main languages admin page.');
+
+ // Clear the language list cache so it can be reloaded.
+ drupal_static_reset('language_list');
+
+ // Get all language definitions.
+ $languages = language_list();
+ $language = $languages[$langcode]->name;
+ $this->assertText(strip_tags(t('The language %language has been created and can now be used. More information is available on the help screen.', array('%language' => t($language), '@locale-help' => url('admin/help/locale')))), 'A new language has been added.');
+ }
+
+ /**
+ * Set up a basic starting point for the locales.
+ *
+ * This assumes the Locale module is enabled. This also must be done before
+ * other user accounts are logged in.
+ *
+ * @param array $locales
+ * A list of locales to be enabled, in langcode format.
+ */
+ public function setupLocales(array $locales = array()) {
+ // If no locales were requested, add Spanish and French.
+ if (empty($locales)) {
+ $locales[] = 'es';
+ $locales[] = 'fr';
+ }
+
+ // Log in as an admin user with privs to just set up the locales.
+ $perms = array(
+ 'administer languages',
+ 'translate interface',
+ 'access administration pages',
+ );
+ $admin_user = $this->drupalCreateUser($perms);
+ $this->drupalLogin($admin_user);
+
+ // Load the admin page, just to have a point of reference.
+ $this->drupalGet('admin');
+ $this->assertResponse(200, 'Loaded the main admin page.');
+
+ // Identify the site's default language.
+ $default_language = language_default('language');
+
+ // Add the locales.
+ foreach ($locales as $langcode) {
+ // Don't create the default language, it's already present.
+ if ($langcode != $default_language) {
+ $this->addSiteLanguage($langcode);
+ }
+ }
+
+ // Enable URL language detection and selection.
+ $this->drupalGet('admin/config/regional/language/configure');
+ $this->assertResponse(200);
+ $edit = array(
+ 'language[enabled][locale-url]' => TRUE,
+ );
+ // Also enable path handling for Entity Translation if it is installed.
+ if (module_exists('entity_translation')) {
+ $edit['language_content[enabled][locale-url]'] = TRUE;
+ }
+ $this->drupalPost(NULL, $edit, t('Save settings'));
+ $this->assertResponse(200);
+
+ // Once all the setup is done, log out the user.
+ $this->drupalLogout();
+ }
+
+ /**
+ * Get the {locales_source} lid value for a specific context.
+ *
+ * @param string $context
+ * The context string to search for.
+ * @param string $textgroup
+ * This string's textgroup; defaults to 'metatag'.
+ *
+ * @return integer
+ * The {locales_source}.lid value for this string.
+ */
+ function getTranslationLidByContext($context, $textgroup = 'metatag') {
+ // Extra debug output.
+ $this->debugLocalesSourcesByContext($context);
+
+ // Look for the string that's actually being requested.
+ return (int) db_query("SELECT lid
+ FROM {locales_source}
+ WHERE textgroup = :textgroup
+ AND context = :context",
+ array(
+ ':textgroup' => $textgroup,
+ ':context' => $context,
+ ))
+ ->fetchField();
+ }
+
+ /**
+ * Get the {locales_source} lid value for a specific source.
+ *
+ * @param string $string
+ * The translation string to search for.
+ * @param string $textgroup
+ * This string's textgroup; defaults to 'metatag'.
+ *
+ * @return integer
+ * The {locales_source}.lid value for this string.
+ */
+ function getTranslationLidBySource($string, $textgroup = 'metatag') {
+ // Extra debug output.
+ $this->debugLocalesSourcesByContext('', $textgroup);
+
+ // Look for the string that's actually being requested.
+ return (int) db_query("SELECT lid
+ FROM {locales_source}
+ WHERE textgroup = :textgroup
+ AND source = :source",
+ array(
+ ':textgroup' => $textgroup,
+ ':source' => $string,
+ ))
+ ->fetchField();
+ }
+
+ /**
+ * Get the {locales_source} lid values for a specific context.
+ *
+ * @param string $context
+ * The context string to search for.
+ * @param string $textgroup
+ * This string's textgroup; defaults to 'metatag'.
+ *
+ * @return integer
+ * The {locales_source}.lid value for this string.
+ */
+ function getTranslationsByContext($context, $textgroup = 'metatag') {
+ return db_query("SELECT lid
+ FROM {locales_source}
+ WHERE textgroup = :textgroup
+ AND context = :context",
+ array(
+ ':textgroup' => $textgroup,
+ ':context' => $context,
+ ))
+ ->fetchCol();
+ }
+
+ /**
+ * Generate a debug dump of the {locales_source} records for a specific context.
+ *
+ * @param string $context
+ * The translation context to search against.
+ * @param string $textgroup
+ * This string's textgroup; defaults to 'metatag'.
+ */
+ function debugLocalesSourcesByContext($context, $textgroup = 'metatag') {
+ // Get a dump of all i18n strings for Metatag.
+ $records = db_query("SELECT lid, location, textgroup, source, context, version
+ FROM {locales_source}
+ WHERE textgroup = :textgroup",
+ array(
+ ':textgroup' => $textgroup,
+ ))
+ ->fetchAllAssoc('lid');
+ foreach ($records as $key => $record) {
+ $records[$key] = (array) $record;
+ }
+ $args = array(
+ 'caption' => 'i18n source check for . ' . $context,
+ 'header' => array(
+ 'lid',
+ 'location',
+ 'textgroup',
+ 'source',
+ 'context',
+ 'version',
+ ),
+ 'rows' => $records,
+ );
+ $this->verbose(theme('table', $args));
+ }
+
+ /**
+ * Save a {locales_target} translation string to the database.
+ *
+ * @param int $lid
+ * The {locales_source}.lid primary key.
+ * @param string $context
+ * The {locales_source}.context value for this string.
+ * @param string $langcode
+ * The language the string is being translated into.
+ * @param string $string_source
+ * The string that is being translated.
+ * @param string $string_target
+ * The destination string.
+ */
+ function saveTranslationString($lid, $context, $langcode, $string_source, $string_target) {
+ // Load the translation page for the front page's title tag.
+ $this->drupalGet('admin/config/regional/translate/edit/' . $lid);
+ $this->assertResponse(200, 'Loaded the front page title tag string translation page.');
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/edit/' . $lid, array('absolute' => TRUE)));
+
+ // Confirm that the permission-check text is not found.
+ $this->assertNoText(t('This is a user-defined string. You are not allowed to translate these strings.'));
+
+ // Look for the existing string. The string gets mungled by the Locale
+ // module, so need to replicate its behaviour.
+ $this->assertText(check_plain(wordwrap($string_source, 0)));
+
+ // Look for the context value; the context value is empty for all default
+ // i.e. interface strings, so don't test this when the context is empty.
+ if (!empty($context)) {
+ $this->assertText($context);
+ }
+
+ // Confirm that the destination strings exist.
+ $source_locale = language_default('language');
+ if (function_exists('i18n_string_source_language')) {
+ $source_locale = i18n_string_source_language();
+ }
+ if ($source_locale != 'en') {
+ $this->assertField('translations[en]', 'Found the English translation string field.');
+ }
+ if ($source_locale != 'fr') {
+ $this->assertField('translations[fr]', 'Found the French translation string field.');
+ }
+ if ($source_locale != 'es') {
+ $this->assertField('translations[es]', 'Found the Spanish translation string field.');
+ }
+
+ // Translate the string.
+ $edit = array(
+ "translations[{$langcode}]" => $string_target,
+ );
+ $this->drupalPost(NULL, $edit, t('Save translations'));
+ $this->assertResponse(200);
+
+ // Confirm the save worked.
+ $this->assertText(t('The string has been saved.'));
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)));
+
+ // Debug output.
+ $this->debugLocalesTargetsByContext($context);
+
+ // Clear the Metatag caches.
+ metatag_flush_caches();
+ }
+
+ /**
+ * Generate a debug dump of the {locales_target} records for a specific context.
+ *
+ * @param string $context
+ * The translation context to search against.
+ */
+ function debugLocalesTargetsByContext($context) {
+ // , lt.i18n_status
+ $records = db_query("SELECT lt.lid, lt.translation, lt.language, lt.plid, lt.plural
+ FROM {locales_target} lt
+ INNER JOIN {locales_source} ls
+ ON lt.lid = ls.lid
+ WHERE ls.textgroup = 'metatag'
+ AND ls.context = :context",
+ array(':context' => $context))
+ ->fetchAllAssoc('lid');
+ foreach ($records as $key => $record) {
+ $records[$key] = (array) $record;
+ }
+ $args = array(
+ 'caption' => 'Locale target check for . ' . $context,
+ 'header' => array(
+ 'lid',
+ 'translation',
+ 'language',
+ 'plid',
+ 'plural',
+ // 'i18n_status',
+ ),
+ 'rows' => $records,
+ );
+ $this->verbose(theme('table', $args));
+ }
+
+ /**
+ * Creates an object which can be used for generating and checking behavior.
+ *
+ * @param string $identifier
+ * The machine name to identify this object in source code.
+ * @param string $path
+ * Path where generate metatags.
+ *
+ * @return object
+ * A mapping object.
+ */
+ function createTestObject($identifier, $path) {
+ $test_object = new stdClass();
+ $test_object->name = $identifier;
+ $test_object->path = $path;
+ $test_object->title = "My $identifier title";
+ $test_object->description = "My $identifier description";
+ $test_object->abstract = "My $identifier abstract";
+ $test_object->keywords = "My $identifier keywords";
+
+ return $test_object;
+ }
+
+ /**
+ * Generates meta tags by path from a test_object.
+ *
+ * @return $test_object
+ * Metatag mapping object.
+ */
+ function generateByPathConfig($test_object) {
+ // Verify the "add context" page works.
+ $this->drupalGet('admin/config/search/metatags/context');
+ $this->assertResponse(200);
+ $this->assertText(t('Add a meta tag by path'));
+
+ // Verify the "add context" page works.
+ $this->drupalGet('admin/config/search/metatags/context/add');
+ $this->assertResponse(200);
+ $this->assertText(t('The unique ID for this metatag path context rule. This must contain only lower case letters, numbers and underscores.'));
+
+ // Add new Metatag object for this configuration.
+ $values = array(
+ 'name' => $test_object->name,
+ );
+
+ $this->drupalPost('admin/config/search/metatags/context/add', $values, t('Add and configure'));
+ $this->assertResponse(200);
+ }
+
+ /**
+ * Edits meta tags by path from a test_object.
+ *
+ * @return $test_object
+ * Metatag mapping object.
+ */
+ function editByPathConfig($test_object) {
+ $edit = array(
+ 'paths' => $test_object->path,
+ 'metatags[und][title][value]' => $test_object->title,
+ 'metatags[und][description][value]' => $test_object->description,
+ 'metatags[und][abstract][value]' => $test_object->abstract,
+ 'metatags[und][keywords][value]' => $test_object->keywords,
+ );
+ $this->drupalPost('admin/config/search/metatags/context/' . $test_object->name, $edit, t('Save'));
+ $this->assertResponse(200);
+ }
+
+ /**
+ * Checks if meta tags have been added correctly from a test_object.
+ *
+ * @return $test_object
+ * Metatag mapping object.
+ */
+ function checkByPathConfig($test_object) {
+ $this->drupalGet($test_object->path);
+ $this->assertResponse(200);
+
+ // Manually test the page title.
+ if (!empty($test_object->title)) {
+ $this->assertTitle($test_object->title, 'Title found in ' . $test_object->path);
+ }
+
+ // Test the other meta tags.
+ $tags = array('description', 'abstract', 'keywords');
+ foreach ($tags as $tag) {
+ if (!empty($test_object->{$tag})) {
+ $this->assertRaw($test_object->{$tag}, $tag . ' found in ' . $test_object->path);
+ }
+ else {
+ $this->assertNoRaw('assertTrue($count >= 1, 'Found Metatag strings to translate.');
+ $this->assertNoText(t('No strings available.'));
+ $xpath = $this->xpath("//body//div[@class='content']//table//tbody//tr");
+ $this->verbose("Found {$count} items to translate.");
+ if (!empty($string)) {
+ $this->assertText($context);
+ $this->assertText('metatag:' . $context);
+ }
+ }
+ // If the string is not supposed to be found, then confirm the context is
+ // not available.
+ else {
+ $this->assertNoText($context);
+ $this->assertNoText('metatag:' . $context);
+ }
+ }
+
+ /**
+ * Create an image of a specific size & type.
+ *
+ * @param string $image_size
+ * The size of the requested image in 'XxY' format; defaults to '200x200'.
+ * @param string $format
+ * The image format to use, defaults to 'png'.
+ *
+ * @return string
+ * The URL to a public file.
+ */
+ function generateImage($image_size = '200x200', $format = 'png') {
+ // Only proceed if the Devel Generate module is installed.
+ if (module_exists('devel_generate')) {
+ // Load the Devel Generate image generator logic.
+ module_load_include('inc', 'devel_generate', 'image.devel_generate');
+
+ $image_format = 'png';
+ $image_size = '200x200';
+ $temp_image = devel_generate_image($image_format, $image_size, $image_size);
+
+ return file_unmanaged_move($temp_image, 'public://');
+ }
+ else {
+ $this->error('The Devel Generate module is not enabled, it must be added to the $modules array in the setUp() method for this test class.');
+ }
+ }
+
+ /**
+ * Create an image file object of a specific size & type.
+ *
+ * @param string
+ * The size of the requested image in 'XxY' format; defaults to '200x200'.
+ * @param string
+ * The image format to use, defaults to 'png'.
+ *
+ * @return object
+ * The file object for the generated image.
+ */
+ function generateImageFile($image_size = '200x200', $format = 'png') {
+ // Generate a test image.
+ $image_uri = $this->generateImage();
+
+ // Create a file object for this image.
+ $file = new StdClass();
+ $file->fid = NULL;
+ $file->uid = 1;
+ $file->uri = $image_uri;
+ $file->filemime = file_get_mimetype($image_uri);
+ $file->filesize = filesize($image_uri);
+ $file->status = 1;
+ $file->timestamp = filemtime($image_uri);
+ $saved_file = file_save($file);
+
+ return $saved_file;
+ }
+
+ /**
+ * Verify a user entity's meta tags load correctly.
+ *
+ * @param object $user
+ * A user object that is to be tested.
+ */
+ function assertUserEntityTags($user) {
+ // Load the user's profile page.
+ $this->drupalGet('user/' . $user->uid);
+ $this->assertResponse(200);
+
+ // Verify the title is using the custom default for this vocabulary.
+ $xpath = $this->xpath("//meta[@name='abstract']");
+ $this->assertEqual(count($xpath), 1, 'Exactly one abstract meta tag found.');
+ $this->assertEqual($xpath[0]['content'], $user->name . " ponies");
+ }
+
+ /**
+ * Generate a string that is allowable as a machine name.
+ *
+ * @param int $length
+ * How long the machine name will be, defaults to eight characters.
+ *
+ * @return string
+ * A string that contains lowercase letters and numbers, with a letter as
+ * the first character.
+ */
+ function randomMachineName($length = 8) {
+ return strtolower($this->randomName($length));
+ }
+
+}
diff --git a/sites/all/modules/contrib/seo/metatag/tests/metatag.image.test b/sites/all/modules/contrib/seo/metatag/tests/metatag.image.test
new file mode 100644
index 0000000..bdf8ad7
--- /dev/null
+++ b/sites/all/modules/contrib/seo/metatag/tests/metatag.image.test
@@ -0,0 +1,491 @@
+ 'Metatag core tests for images',
+ 'description' => 'Test Metatag integration with image files.',
+ 'group' => 'Metatag',
+ 'dependencies' => array('ctools', 'token', 'devel_generate'),
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function setUp(array $modules = array()) {
+ // Needs the OpenGraph submodule because of testNodeFieldValueMultiple().
+ $modules[] = 'metatag_opengraph';
+
+ // Need image handling.
+ $modules[] = 'image';
+
+ // Need the Devel Generate image generator.
+ $modules[] = 'devel_generate';
+
+ parent::setUp($modules);
+ }
+
+ /**
+ * Confirm that an image can be added to a global configuration using the
+ * image's absolute URL.
+ */
+ function testConfigAbsoluteURL() {
+ // Generate a test image.
+ $image_uri = $this->generateImage();
+ $this->verbose($image_uri);
+
+ // Work out the web-accessible URL for this image.
+ $image_url = file_create_url($image_uri);
+
+ // Update the global config to add an image meta tag.
+ $config = metatag_config_load('global');
+ $config->config['image_src']['value'] = $image_url;
+ metatag_config_save($config);
+
+ // Dump out the current config, to aid with debugging.
+ $this->verbose($config);
+
+ // Load the front page.
+ $this->drupalGet('