diff --git a/sites/all/modules/date/CHANGELOG.txt b/sites/all/modules/date/CHANGELOG.txt index 38ecaf947210b5b8c4b6092ffb718f387cfcd90c..99e8fb04613a1042bf525889a0a1ef877e226499 100644 --- a/sites/all/modules/date/CHANGELOG.txt +++ b/sites/all/modules/date/CHANGELOG.txt @@ -5,6 +5,30 @@ Date Module 7.x Version 7.x-2.x-dev =================== +====================== +Version 7.x-2.6 +====================== + +- Issue #1423364 by catmat, Add file and path to hook_context_plugins(). +- Issue #1713248, Remove incorrect use of date_popup() function in previous commit. +- Issue #1143680 by kristiaanvandeneynde, Make Date Popup widget able to accept custom settings. +- Issue #1596546 by applicity_sam, Make sure incorrect month name creates validation error. +- Issue #1432702, Fix some miscellaneous problems with date example dates and formats. +- Issue #1512464 by Liam Moreland, Allow for undefined formatter in Date upgrade handling. +- Issue #1659638 by bojanz, Hide install message if Drupal is not installed (i.e. in install profiles). +- Issue #1605158 by covenantd, Be sure value2 is initiated in Date Context handler. +- Issue #1355256 by barraponto develCuy and KarenS, set default value for date text widget increment to 1, and update existing fields. +- Issue #1561306 by Cyberwolf, Date pager replacement was not working correctly when used with multiple dates. +- Issue #1541740 by hass, Make Date requirements more easily translatable. +- Issue #1543524 by iamEAP, Add update hook to remove the D6 date_timezone field from users. +- Issue #1603640 by bevan, Don't return anything for empty date interval. +- Issue #1235508, Make sure that ISO strings with '00' in the month and/or day don't create dates for the previous month and day. +- Issue #1289270 by pdrake: Fixed date arguments and filters do not work with relationships. +- Issue #895760 by Reinette: Added RFC2447 option 'STATUS' to date_api_ical().inc. +- Issue #606658 by johnmunro and KarenS, Make sure ical import processes multi-day all-day events correctly. +- Issue #1408216 follow up, Need to be sure that NULL is the default state for the sql functions. +- Issue #1540410, Update the Date Tools calendar creation wizard to use the new calendar path. + ====================== Version 7.x-2.5 ====================== diff --git a/sites/all/modules/date/date.field.inc b/sites/all/modules/date/date.field.inc index 5a4fa8134c9282d714f72149b47a3d321665283e..b104702a146acd99b8e4d0d49a8c4ffa12985834 100644 --- a/sites/all/modules/date/date.field.inc +++ b/sites/all/modules/date/date.field.inc @@ -294,12 +294,17 @@ function date_field_widget_info() { 'field types' => array('date', 'datestamp', 'datetime'), ) + $settings, ); + if (module_exists('date_popup')) { $info['date_popup'] = array( 'label' => t('Pop-up calendar'), 'field types' => array('date', 'datestamp', 'datetime'), ) + $settings; } + + // The date text widget should use an increment of 1. + $info['date_text']['increment'] = 1; + return $info; } diff --git a/sites/all/modules/date/date.info b/sites/all/modules/date/date.info index 88eaa6c2ef49f773627a6b863c87376dd83476b2..e5b00c21e51b5ba37f615b2ef82cd28e9359b8f3 100644 --- a/sites/all/modules/date/date.info +++ b/sites/all/modules/date/date.info @@ -10,9 +10,9 @@ files[] = tests/date_field.test files[] = tests/date_validation.test files[] = tests/date_timezone.test -; Information added by drupal.org packaging script on 2012-04-19 -version = "7.x-2.5" +; Information added by drupal.org packaging script on 2012-08-13 +version = "7.x-2.6" core = "7.x" project = "date" -datestamp = "1334835098" +datestamp = "1344850024" diff --git a/sites/all/modules/date/date.install b/sites/all/modules/date/date.install index 3f250585c2017ff05de89ff4a67f11633b8c140c..23fb07eb29c1ecb7f5de3208598a674a7a048ebf 100644 --- a/sites/all/modules/date/date.install +++ b/sites/all/modules/date/date.install @@ -160,3 +160,35 @@ function date_update_7003() { module_enable(array('date_repeat_field')); } } + +/** + * Date text widgets should always use an increment of 1. + */ +function date_update_7004() { + + // Select date fields. + $query = db_select('field_config_instance', 'fci', array('fetch' => PDO::FETCH_ASSOC)); + $query->join('field_config', 'fc', 'fc.id = fci.field_id'); + $query->fields('fci'); + $query->condition(db_or()->condition('fc.type', 'date')->condition('fc.type', 'datestamp')->condition('fc.type', 'datetime')); + $results = $query->execute(); + + // Find the ones that use the date_text widget. + foreach ($results as $record) { + $instance = unserialize($record['data']); + if (in_array($instance['widget']['type'], array('date_text'))) { + $instance['widget']['settings']['increment'] = 1; + db_update('field_config_instance') + ->fields(array( + 'data' => serialize($instance), + )) + ->condition('field_name', $record['field_name']) + ->condition('entity_type', $record['entity_type']) + ->condition('bundle', $record['bundle']) + ->execute(); + } + } + field_cache_clear(); + drupal_set_message(t('Date text widgets have been updated to use an increment of 1.')); +} + diff --git a/sites/all/modules/date/date.theme b/sites/all/modules/date/date.theme index 0d28be021f238fbffc34d4b6cb7340403d13c0ba..70c6be41f7b87dcc98801330c5de44c8833dc52c 100644 --- a/sites/all/modules/date/date.theme +++ b/sites/all/modules/date/date.theme @@ -301,7 +301,13 @@ function theme_date_display_interval($variables) { 'interval' => $options['interval'], 'interval_display' => $options['interval_display'], ); - return '<span class="date-display-interval"' . drupal_attributes($attributes) . '>' . theme('date_time_ago', $time_ago_vars) . '</span>'; + + if ($return = theme('date_time_ago', $time_ago_vars)) { + return '<span class="date-display-interval"' . drupal_attributes($attributes) . ">$return</span>"; + } + else { + return ''; + } } /** diff --git a/sites/all/modules/date/date_admin.inc b/sites/all/modules/date/date_admin.inc index f5a345b4e1bfb5f5f05e9e364fb858eb3bf38349..993aa091151606d1fe20a997fef38b568e0ea20a 100644 --- a/sites/all/modules/date/date_admin.inc +++ b/sites/all/modules/date/date_admin.inc @@ -122,6 +122,7 @@ function date_default_formatter_settings_summary($field, $instance, $view_mode) $formatter = $display['type']; $format_types = date_format_type_options(); $summary = array(); + $format = FALSE; switch ($formatter) { case 'date_plain': $format = t('Plain'); @@ -130,9 +131,16 @@ function date_default_formatter_settings_summary($field, $instance, $view_mode) $format = t('Interval'); break; default: - $format = $format_types[$settings['format_type']]; + if (!empty($format_types[$settings['format_type']])) { + $format = $format_types[$settings['format_type']]; + } + } + if ($format) { + $summary[] = t('Display dates using the @format format', array('@format' => $format)); + } + else { + $summary[] = t('Display dates using the default format because the specified format (@format) is not defined', array('@format' => $settings['format_type'])); } - $summary[] = t('Display dates using the @format format', array('@format' => $format)); if (array_key_exists('fromto', $settings) && $field['settings']['todate']) { $options = array( diff --git a/sites/all/modules/date/date_all_day/date_all_day.info b/sites/all/modules/date/date_all_day/date_all_day.info index ba5d0f185e26f1f8b8a7177db83c447a2be6ad06..8f8bf48b72019bcbe73b831cdd4900629f4a431d 100644 --- a/sites/all/modules/date/date_all_day/date_all_day.info +++ b/sites/all/modules/date/date_all_day/date_all_day.info @@ -5,9 +5,9 @@ dependencies[] = date package = Date/Time core = 7.x -; Information added by drupal.org packaging script on 2012-04-19 -version = "7.x-2.5" +; Information added by drupal.org packaging script on 2012-08-13 +version = "7.x-2.6" core = "7.x" project = "date" -datestamp = "1334835098" +datestamp = "1344850024" diff --git a/sites/all/modules/date/date_api/date_api.info b/sites/all/modules/date/date_api/date_api.info index 09c0983ba95656b9ecbe8dd35240987f2e393db0..ee1a38423bf2c5de0b06785ee9974dced551fac7 100644 --- a/sites/all/modules/date/date_api/date_api.info +++ b/sites/all/modules/date/date_api/date_api.info @@ -9,9 +9,9 @@ stylesheets[all][] = date.css files[] = date_api.module files[] = date_api_sql.inc -; Information added by drupal.org packaging script on 2012-04-19 -version = "7.x-2.5" +; Information added by drupal.org packaging script on 2012-08-13 +version = "7.x-2.6" core = "7.x" project = "date" -datestamp = "1334835098" +datestamp = "1344850024" diff --git a/sites/all/modules/date/date_api/date_api.install b/sites/all/modules/date/date_api/date_api.install index d2bfbb36794f2c1d00565a4a017f68a24ea51c20..ce5b746dd77b95e7c84cdf37ec22708afaf7a614 100644 --- a/sites/all/modules/date/date_api/date_api.install +++ b/sites/all/modules/date/date_api/date_api.install @@ -61,17 +61,20 @@ function date_api_requirements($phase) { * Implements hook_install(). */ function date_api_install() { - // Ensure translations don't break at install time. - $t = get_t(); + // Only set the message if Drupal itself is already installed. + if (variable_get('install_task') == 'done') { + // Ensure translations don't break at install time. + $t = get_t(); - // date_api_set_variables can install date_timezone. The - // date_timezone_install() function does a module_enable('date_api'). This - // means that date_api_enable() can be called before date_api_install() - // finishes! So the date_api schema needs to be installed before this line! - date_api_set_variables(); + // date_api_set_variables can install date_timezone. The + // date_timezone_install() function does a module_enable('date_api'). This + // means that date_api_enable() can be called before date_api_install() + // finishes! So the date_api schema needs to be installed before this line! + date_api_set_variables(); - $message = $t('The Date API requires that you set up the !timezone_link and the !format_link to function correctly.', array('!timezone_link' => l($t('site timezone and first day of week settings'), 'admin/config/regional/settings'), '!format_link' => l($t('date format settings'), 'admin/config/regional/date-time'))); - drupal_set_message(filter_xss_admin($message), 'warning'); + $message = $t('The Date API requires that you set up the <a href="@regional_settings">site timezone and first day of week settings</a> and the <a href="@regional_date_time">date format settings</a> to function correctly.', array('@regional_settings' => url('admin/config/regional/settings'), '@regional_date_time' => url('admin/config/regional/date-time'))); + drupal_set_message(filter_xss_admin($message), 'warning'); + } } /** @@ -237,3 +240,13 @@ function date_api_update_7000() { db_drop_table('d6_date_format_locale'); } } + + +/** + * Drop D6 timezone_name field on {users} after upgrading to D7. + */ +function date_api_update_7001() { + if (db_field_exists('users', 'timezone_name')) { + db_drop_field('users', 'timezone_name'); + } +} diff --git a/sites/all/modules/date/date_api/date_api.module b/sites/all/modules/date/date_api/date_api.module index b8c95f775fb68ed1d58dde4a191e73b886ce8c9d..d85a8a5e5a132400f59f18373ec1d4a47f9d413c 100644 --- a/sites/all/modules/date/date_api/date_api.module +++ b/sites/all/modules/date/date_api/date_api.module @@ -83,28 +83,28 @@ function date_api_status() { $value = variable_get('date_default_timezone'); if (isset($value)) { - $success_messages[] = $t('The timezone has been set to !value_link.', array('!value_link' => l($value, 'admin/config/regional/settings'))); + $success_messages[] = $t('The timezone has been set to <a href="@regional_settings">@timezone</a>.', array('@regional_settings' => url('admin/config/regional/settings'), '@timezone' => $value)); } else { - $error_messages[] = $t('The Date API requires that you set up the !timezone_link to function correctly.', array('!timezone_link' => l($t('site timezone'), 'admin/config/regional/settings'))); + $error_messages[] = $t('The Date API requires that you set up the <a href="@regional_settings">site timezone</a> to function correctly.', array('@regional_settings' => url('admin/config/regional/settings'))); } $value = variable_get('date_first_day'); if (isset($value)) { $days = date_week_days(); - $success_messages[] = $t('The first day of the week has been set to !value_link.', array('!value_link' => l($days[$value], 'admin/config/regional/settings'))); + $success_messages[] = $t('The first day of the week has been set to <a href="@regional_settings">@day</a>.', array('@regional_settings' => url('admin/config/regional/settings'), '@day' => $days[$value])); } else { - $error_messages[] = $t('The Date API requires that you set up the !first_day_link to function correctly.', array('!first_day_link' => l($t('site first day of week settings'), 'admin/config/regional/settings'))); + $error_messages[] = $t('The Date API requires that you set up the <a href="@regional_settings">site first day of week settings</a> to function correctly.', array('@regional_settings' => url('admin/config/regional/settings'))); } $value = variable_get('date_format_medium'); if (isset($value)) { $now = date_now(); - $success_messages[] = $t('The medium date format type has been set to to @value. You may find it helpful to add new format types like Date, Time, Month, or Year, with appropriate formats, at !value_link.', array('@value' => $now->format($value), '!value_link' => l(t('Date and time'), 'admin/config/regional/date-time'))); + $success_messages[] = $t('The medium date format type has been set to to @value. You may find it helpful to add new format types like Date, Time, Month, or Year, with appropriate formats, at <a href="@regional_date_time">Date and time</a> settings.', array('@value' => $now->format($value), '@regional_date_time' => url('admin/config/regional/date-time'))); } else { - $error_messages[] = $t('The Date API requires that you set up the !format_link to function correctly.', array('!format_link' => l($t('system date formats'), 'admin/config/regional/date-time'))); + $error_messages[] = $t('The Date API requires that you set up the <a href="@regional_date_time">system date formats</a> to function correctly.', array('@regional_date_time' => url('admin/config/regional/date-time'))); } return array('errors', $error_messages, 'success' => $success_messages); @@ -185,7 +185,7 @@ class DateObject extends DateTime { * Constructs a date object. * * @param string $time - * A date/time string. Defaults to 'now'. + * A date/time string or array. Defaults to 'now'. * @param object|string|null $tz * PHP DateTimeZone object, string or NULL allowed. Defaults to NULL. * @param string $format @@ -198,6 +198,9 @@ class DateObject extends DateTime { $this->timeOnly = FALSE; $this->dateOnly = FALSE; + // Store the raw time input so it is available for validation. + $this->originalTime = $time; + // Allow string timezones. if (!empty($tz) && !is_object($tz)) { $tz = new DateTimeZone($tz); @@ -221,8 +224,7 @@ class DateObject extends DateTime { $format = DATE_FORMAT_DATETIME; $this->addGranularity('timezone'); } - - if (is_array($time)) { + elseif (is_array($time)) { // Assume we were passed an indexed array. if (empty($time['year']) && empty($time['month']) && empty($time['day'])) { $this->timeOnly = TRUE; @@ -237,6 +239,12 @@ class DateObject extends DateTime { // We checked for errors already, skip parsing the input values. $format = NULL; } + else { + // Make sure dates like 2010-00-00T00:00:00 get converted to + // 2010-01-01T00:00:00 before creating a date object + // to avoid unintended changes in the month or day. + $time = date_make_iso_valid($time); + } // The parse function will also set errors on the date parts. if (!empty($format)) { @@ -590,12 +598,12 @@ class DateObject extends DateTime { break; case 'F': $array_month_long = array_flip(date_month_names()); - $final_date['month'] = array_key_exists($value, $array_month_long) ? $array_month_long[$value] : ''; + $final_date['month'] = array_key_exists($value, $array_month_long) ? $array_month_long[$value] : -1; $this->addGranularity('month'); break; case 'M': $array_month = array_flip(date_month_names_abbr()); - $final_date['month'] = array_key_exists($value, $array_month) ? $array_month[$value] : ''; + $final_date['month'] = array_key_exists($value, $array_month) ? $array_month[$value] : -1; $this->addGranularity('month'); break; case 'Y': @@ -2667,3 +2675,33 @@ function date_is_date($date) { } return TRUE; } + +/** + * This function will replace ISO values that have the pattern 9999-00-00T00:00:00 + * with a pattern like 9999-01-01T00:00:00, to match the behavior of non-ISO + * dates and ensure that date objects created from this value contain a valid month + * and day. Without this fix, the ISO date '2020-00-00T00:00:00' would be created as + * November 30, 2019 (the previous day in the previous month). + * + * @param string $iso_string + * An ISO string that needs to be made into a complete, valid date. + * + * @TODO Expand on this to work with all sorts of partial ISO dates. + */ +function date_make_iso_valid($iso_string) { + // If this isn't a value that uses an ISO pattern, there is nothing to do. + if (is_numeric($iso_string) || !preg_match(DATE_REGEX_ISO, $iso_string)) { + return $iso_string; + } + // First see if month and day parts are '-00-00'. + if (substr($iso_string, 4, 6) == '-00-00') { + return preg_replace('/([\d]{4}-)(00-00)(T[\d]{2}:[\d]{2}:[\d]{2})/', '${1}01-01${3}', $iso_string); + } + // Then see if the day part is '-00'. + elseif (substr($iso_string, 7, 3) == '-00') { + return preg_replace('/([\d]{4}-[\d]{2}-)(00)(T[\d]{2}:[\d]{2}:[\d]{2})/', '${1}01${3}', $iso_string); + } + + // Fall through, no changes required. + return $iso_string; +} diff --git a/sites/all/modules/date/date_api/date_api_elements.inc b/sites/all/modules/date/date_api/date_api_elements.inc index b5a90a0e8a9e37c8e39b637b6c5c1446a86aac8a..7da4e5892c79dbc4c9997f202ee69e161b1be6cf 100644 --- a/sites/all/modules/date/date_api/date_api_elements.inc +++ b/sites/all/modules/date/date_api/date_api_elements.inc @@ -294,7 +294,7 @@ function date_text_element_value_callback($element, $input = FALSE, &$form_state $date = date_default_date($element); } if (date_is_date($date)) { - $return['date'] = $date->format($element['#date_format']); + $return['date'] = date_format_date($date, 'custom', $element['#date_format']); } return $return; } @@ -320,7 +320,7 @@ function date_text_element_process($element, &$form_state, $form) { $element['date']['#weight'] = !empty($element['date']['#weight']) ? $element['date']['#weight'] : $element['#weight']; $element['date']['#attributes'] = array('class' => isset($element['#attributes']['class']) ? $element['#attributes']['class'] += array('date-date') : array('date-date')); $now = date_example_date(); - $element['date']['#description'] = ' ' . t('Format: @date', array('@date' => date_now()->format($element['#date_format']))); + $element['date']['#description'] = ' ' . t('Format: @date', array('@date' => date_format_date(date_example_date(), 'custom', $element['#date_format']))); $element['date']['#ajax'] = !empty($element['#ajax']) ? $element['#ajax'] : FALSE; // Keep the system from creating an error message for the sub-element. diff --git a/sites/all/modules/date/date_api/date_api_ical.inc b/sites/all/modules/date/date_api/date_api_ical.inc index 7cb2b957745d9d7ed22459e15c4e7d84484dc0f5..2ca484e7fdc320b525ad9fc1b7cdc5c28daba4d5 100644 --- a/sites/all/modules/date/date_api/date_api_ical.inc +++ b/sites/all/modules/date/date_api/date_api_ical.inc @@ -215,15 +215,31 @@ function date_ical_parse($icaldatafolded = array()) { // even when you are working with only a portion of the VEVENT // array, like in Feed API parsers. $subgroup['all_day'] = FALSE; - if (!empty($subgroup['DTSTART']) && (!empty($subgroup['DTSTART']['all_day']) || - (empty($subgroup['DTEND']) && empty($subgroup['RRULE']) && empty($subgroup['RRULE']['COUNT'])))) { - // Many programs output DTEND for an all day event as the - // following day, reset this to the same day for internal use. - $subgroup['all_day'] = TRUE; + + // iCal spec states 'The "DTEND" property for a "VEVENT" calendar + // component specifies the non-inclusive end of the event'. Adjust + // multi-day events to remove the extra day because the Date code + // assumes the end date is inclusive. + if (!empty($subgroup['DTEND']) && (!empty($subgroup['DTEND']['all_day']))) { + // Make the end date one day earlier. + $date = new DateObject ($subgroup['DTEND']['datetime'] . ' 00:00:00', $subgroup['DTEND']['tz']); + date_modify($date, '-1 day'); + $subgroup['DTEND']['datetime'] = date_format($date, 'Y-m-d'); + } + // If a start datetime is defined AND there is no definition for + // the end datetime THEN make the end datetime equal the start + // datetime and if it is an all day event define the entire event + // as a single all day event. + if (!empty($subgroup['DTSTART']) && + (empty($subgroup['DTEND']) && empty($subgroup['RRULE']) && empty($subgroup['RRULE']['COUNT']))) { $subgroup['DTEND'] = $subgroup['DTSTART']; } // Add this element to the parent as an array under the component // name. + if (!empty($subgroup['DTSTART']['all_day'])) { + $subgroup['all_day'] = TRUE; + } + // Add this element to the parent as an array under the prev($subgroups); $parent = &$subgroups[key($subgroups)]; @@ -291,6 +307,7 @@ function date_ical_parse($icaldatafolded = array()) { $parse_result = date_ical_parse_rrule($field, $data); break; + case 'STATUS': case 'SUMMARY': case 'DESCRIPTION': $parse_result = date_ical_parse_text($field, $data); @@ -712,9 +729,12 @@ function date_api_ical_build_rrule($form_values) { // We only collect a date for UNTIL, but we need it to be inclusive, so // force it to a full datetime element at the last second of the day. if (!is_object($form_values['UNTIL']['datetime'])) { - $form_values['UNTIL']['datetime'] .= ' 23:59:59'; - $form_values['UNTIL']['granularity'] = serialize(drupal_map_assoc(array('year', 'month', 'day', 'hour', 'minute', 'second'))); - $form_values['UNTIL']['all_day'] = 0; + // If this is a date without time, give it time. + if (strlen($form_values['UNTIL']['datetime']) < 11) { + $form_values['UNTIL']['datetime'] .= ' 23:59:59'; + $form_values['UNTIL']['granularity'] = serialize(drupal_map_assoc(array('year', 'month', 'day', 'hour', 'minute', 'second'))); + $form_values['UNTIL']['all_day'] = FALSE; + } $until = date_ical_date($form_values['UNTIL'], 'UTC'); } else { @@ -745,7 +765,7 @@ function date_api_ical_build_rrule($form_values) { foreach ($form_values['EXDATE'] as $value) { if (!empty($value['datetime'])) { $date = !is_object($value['datetime']) ? date_ical_date($value, 'UTC') : $value['datetime']; - $ex_date = !empty($date) ? $date->format(DATE_FORMAT_ICAL) . 'Z': ''; + $ex_date = !empty($date) ? date_format($date, DATE_FORMAT_ICAL) . 'Z': ''; if (!empty($ex_date)) { $ex_dates[] = $ex_date; } @@ -765,7 +785,7 @@ function date_api_ical_build_rrule($form_values) { $ex_dates = array(); foreach ($form_values['RDATE'] as $value) { $date = !is_object($value['datetime']) ? date_ical_date($value, 'UTC') : $value['datetime']; - $ex_date = !empty($date) ? $date->format(DATE_FORMAT_ICAL) . 'Z': ''; + $ex_date = !empty($date) ? date_format($date, DATE_FORMAT_ICAL) . 'Z': ''; if (!empty($ex_date)) { $ex_dates[] = $ex_date; } diff --git a/sites/all/modules/date/date_api/date_api_sql.inc b/sites/all/modules/date/date_api/date_api_sql.inc index ede8b1ef33fbd1071b828de1c863078693b300d9..a2adc652b414c3f72ae3e8b91af3735b6aeab9af 100644 --- a/sites/all/modules/date/date_api/date_api_sql.inc +++ b/sites/all/modules/date/date_api/date_api_sql.inc @@ -598,7 +598,7 @@ class date_sql_handler { * @return string * SQL for the where clause for this operation. */ - function sql_where_date($type, $field, $operator, $value, $adjustment = 0) { + function sql_where_date($type, $field, $operator, $value, $adjustment = NULL) { $type = strtoupper($type); if (strtoupper($value) == 'NOW') { $value = $this->sql_field('NOW', $adjustment); @@ -644,12 +644,12 @@ class date_sql_handler { * @return string * SQL for the where clause for this operation. */ - function sql_where_extract($part, $field, $operator, $value) { - if ($this->local_timezone != $this->db_timezone) { + function sql_where_extract($part, $field, $operator, $value, $adjustment = NULL) { + if (empty($adjustment) && $this->local_timezone != $this->db_timezone) { $field = $this->sql_field($field); } else { - $field = $this->sql_field($field, 0); + $field = $this->sql_field($field, $adjustment); } return $this->sql_extract($part, $field) . " $operator $value"; } @@ -670,12 +670,12 @@ class date_sql_handler { * @return string * SQL for the where clause for this operation. */ - function sql_where_format($format, $field, $operator, $value) { - if ($this->local_timezone != $this->db_timezone) { + function sql_where_format($format, $field, $operator, $value, $adjustment = NULL) { + if (empty($adjustment) && $this->local_timezone != $this->db_timezone) { $field = $this->sql_field($field); } else { - $field = $this->sql_field($field, 0); + $field = $this->sql_field($field, $adjustment); } return $this->sql_format($format, $field) . " $operator '$value'"; } diff --git a/sites/all/modules/date/date_context/date_context.info b/sites/all/modules/date/date_context/date_context.info index 63f012901831d930e3b9a4f374a3673df64143c4..a69a599b74ee048a9babe66521e4772a57e6b071 100644 --- a/sites/all/modules/date/date_context/date_context.info +++ b/sites/all/modules/date/date_context/date_context.info @@ -8,9 +8,9 @@ dependencies[] = context files[] = date_context.module files[] = plugins/date_context_date_condition.inc -; Information added by drupal.org packaging script on 2012-04-19 -version = "7.x-2.5" +; Information added by drupal.org packaging script on 2012-08-13 +version = "7.x-2.6" core = "7.x" project = "date" -datestamp = "1334835098" +datestamp = "1344850024" diff --git a/sites/all/modules/date/date_context/date_context.module b/sites/all/modules/date/date_context/date_context.module index a5d265084083123a4a40fe63b5f55121f3a84037..44e975acd84ecbe39175584bcdc007a7d3a46591 100644 --- a/sites/all/modules/date/date_context/date_context.module +++ b/sites/all/modules/date/date_context/date_context.module @@ -30,6 +30,8 @@ function date_context_context_plugins() { 'handler' => array( 'class' => 'date_context_date_condition', 'parent' => 'context_condition_node', + 'path' => drupal_get_path('module', 'date_context') . '/plugins', + 'file' => 'date_context_date_condition.inc', ), ); return $plugins; diff --git a/sites/all/modules/date/date_context/plugins/date_context_date_condition.inc b/sites/all/modules/date/date_context/plugins/date_context_date_condition.inc index 3fcb042857105e04007ad75afb0ff4b1a3dce4e6..733fb5a84709a4e6aaeae24f7c390406f3d7f3e3 100644 --- a/sites/all/modules/date/date_context/plugins/date_context_date_condition.inc +++ b/sites/all/modules/date/date_context/plugins/date_context_date_condition.inc @@ -82,6 +82,9 @@ class date_context_date_condition extends context_condition_node { $date = new DateObject($item['value'], $timezone_db); date_timezone_set($date, timezone_open($timezone)); $date1 = $date->format(DATE_FORMAT_DATETIME); + if (empty($item['value2'])) { + $item['value2'] = $item['value']; + } $date = new DateObject($item['value2'], $timezone_db); date_timezone_set($date, timezone_open($timezone)); $date2 = $date->format(DATE_FORMAT_DATETIME); diff --git a/sites/all/modules/date/date_migrate/date_migrate.info b/sites/all/modules/date/date_migrate/date_migrate.info index 487fec8446743635a0395b76d7e667b6d545a47d..c0d536f5b7090acc08aacce717f48f00bd702348 100644 --- a/sites/all/modules/date/date_migrate/date_migrate.info +++ b/sites/all/modules/date/date_migrate/date_migrate.info @@ -8,9 +8,9 @@ dependencies[] = date files[] = date.migrate.inc files[] = date_migrate.test -; Information added by drupal.org packaging script on 2012-04-19 -version = "7.x-2.5" +; Information added by drupal.org packaging script on 2012-08-13 +version = "7.x-2.6" core = "7.x" project = "date" -datestamp = "1334835098" +datestamp = "1344850024" diff --git a/sites/all/modules/date/date_migrate/date_migrate_example/date_migrate_example.info b/sites/all/modules/date/date_migrate/date_migrate_example/date_migrate_example.info index ad43d983fa49cd94ecf8f245e24e2b002bb2f496..b54a3f7515d045c4dd048224a7cf34d81202e77a 100644 --- a/sites/all/modules/date/date_migrate/date_migrate_example/date_migrate_example.info +++ b/sites/all/modules/date/date_migrate/date_migrate_example/date_migrate_example.info @@ -21,9 +21,9 @@ package = "Features" project = "date_migrate_example" version = "7.x-2.0" -; Information added by drupal.org packaging script on 2012-04-19 -version = "7.x-2.5" +; Information added by drupal.org packaging script on 2012-08-13 +version = "7.x-2.6" core = "7.x" project = "date" -datestamp = "1334835098" +datestamp = "1344850024" diff --git a/sites/all/modules/date/date_popup/date_popup.info b/sites/all/modules/date/date_popup/date_popup.info index f781721292ad9385fb9cee69af3564fc59c8fcb5..767aacac22470bc8ae08115ea6841a5700c4cf6c 100644 --- a/sites/all/modules/date/date_popup/date_popup.info +++ b/sites/all/modules/date/date_popup/date_popup.info @@ -7,9 +7,9 @@ configure = admin/config/date/date_popup stylesheets[all][] = themes/datepicker.1.7.css -; Information added by drupal.org packaging script on 2012-04-19 -version = "7.x-2.5" +; Information added by drupal.org packaging script on 2012-08-13 +version = "7.x-2.6" core = "7.x" project = "date" -datestamp = "1334835098" +datestamp = "1344850024" diff --git a/sites/all/modules/date/date_popup/date_popup.module b/sites/all/modules/date/date_popup/date_popup.module index aa780333361d3ebf464df26e55a0e2decbc704d7..ca292ef9ad75c83fe9d90422fa2d36260c537c8f 100644 --- a/sites/all/modules/date/date_popup/date_popup.module +++ b/sites/all/modules/date/date_popup/date_popup.module @@ -185,6 +185,15 @@ function date_popup_theme() { * The number of years to go back and forward in a year selector, * default is -3:+3 (3 back and 3 forward). * + * #datepicker_options + * An associative array representing the jQuery datepicker options you want + * to set for this element. Use the jQuery datepicker option names as keys. + * Hard coded defaults are: + * - changeMonth => TRUE + * - changeYear => TRUE + * - autoPopUp => 'focus' + * - closeAtTop => FALSE + * - speed => 'immediate' */ function date_popup_element_info() { $timepicker = date_popup_get_preferred_timepicker(); @@ -194,6 +203,7 @@ function date_popup_element_info() { '#date_timezone' => date_default_timezone(), '#date_flexible' => 0, '#date_format' => variable_get('date_format_short', 'm/d/Y - H:i'), + '#datepicker_options' => array(), '#timepicker' => variable_get('date_popup_timepicker', $timepicker), '#date_increment' => 1, '#date_year_range' => '-3:+3', @@ -317,20 +327,22 @@ function date_popup_process_date_part(&$element) { $range = date_range_years($element['#date_year_range'], $date); $year_range = date_range_string($range); - $settings = array( + // Add the dynamic datepicker options. Allow element-specific datepicker + // preferences to override these options for whatever reason they see fit. + $settings = $element['#datepicker_options'] + array( 'changeMonth' => TRUE, 'changeYear' => TRUE, - 'firstDay' => intval(variable_get('date_first_day', 0)), - //'buttonImage' => base_path() . drupal_get_path('module', 'date_api') ."/images/calendar.png", - //'buttonImageOnly' => TRUE, 'autoPopUp' => 'focus', 'closeAtTop' => FALSE, 'speed' => 'immediate', + 'firstDay' => intval(variable_get('date_first_day', 0)), + //'buttonImage' => base_path() . drupal_get_path('module', 'date_api') ."/images/calendar.png", + //'buttonImageOnly' => TRUE, 'dateFormat' => date_popup_format_to_popup(date_popup_date_format($element), 'datepicker'), 'yearRange' => $year_range, // Custom setting, will be expanded in Drupal.behaviors.date_popup() 'fromTo' => isset($fromto), - ); + ); // Create a unique id for each set of custom settings. $id = date_popup_js_settings_id($element['#id'], 'datepicker', $settings); @@ -353,7 +365,7 @@ function date_popup_process_date_part(&$element) { ); $sub_element['#value'] = $sub_element['#default_value']; // TODO, figure out exactly when we want this description. In many places it is not desired. - $sub_element['#description'] = ' '. t('E.g., @date', array('@date' => date_format_date(date_now(), 'custom', date_popup_date_format($element)))); + $sub_element['#description'] = ' '. t('E.g., @date', array('@date' => date_format_date(date_example_date(), 'custom', date_popup_date_format($element)))); return $sub_element; } diff --git a/sites/all/modules/date/date_repeat/date_repeat.info b/sites/all/modules/date/date_repeat/date_repeat.info index c1ff662e19769a6c144bdf220097953430ca633f..1d1720a47f82467b75afda26f91a26000d0f8aca 100644 --- a/sites/all/modules/date/date_repeat/date_repeat.info +++ b/sites/all/modules/date/date_repeat/date_repeat.info @@ -7,9 +7,9 @@ php = 5.2 files[] = tests/date_repeat.test files[] = tests/date_repeat_form.test -; Information added by drupal.org packaging script on 2012-04-19 -version = "7.x-2.5" +; Information added by drupal.org packaging script on 2012-08-13 +version = "7.x-2.6" core = "7.x" project = "date" -datestamp = "1334835098" +datestamp = "1344850024" diff --git a/sites/all/modules/date/date_repeat_field/date_repeat_field.info b/sites/all/modules/date/date_repeat_field/date_repeat_field.info index 0b57d62d74d320f302bdb706fa41e02a02836397..803105c24ccc8a2d49f53cd04ff4f7da0c59a296 100644 --- a/sites/all/modules/date/date_repeat_field/date_repeat_field.info +++ b/sites/all/modules/date/date_repeat_field/date_repeat_field.info @@ -7,9 +7,9 @@ stylesheets[all][] = date_repeat_field.css package = Date/Time core = 7.x -; Information added by drupal.org packaging script on 2012-04-19 -version = "7.x-2.5" +; Information added by drupal.org packaging script on 2012-08-13 +version = "7.x-2.6" core = "7.x" project = "date" -datestamp = "1334835098" +datestamp = "1344850024" diff --git a/sites/all/modules/date/date_tools/date_tools.info b/sites/all/modules/date/date_tools/date_tools.info index a34dcc8339b8eb035e768d0af587a0c4ee14ae99..3f392a4257d310a1fcc0e6d2a25a5dba1b247bd2 100644 --- a/sites/all/modules/date/date_tools/date_tools.info +++ b/sites/all/modules/date/date_tools/date_tools.info @@ -6,9 +6,9 @@ core = 7.x configure = admin/config/date/tools files[] = tests/date_tools.test -; Information added by drupal.org packaging script on 2012-04-19 -version = "7.x-2.5" +; Information added by drupal.org packaging script on 2012-08-13 +version = "7.x-2.6" core = "7.x" project = "date" -datestamp = "1334835098" +datestamp = "1344850024" diff --git a/sites/all/modules/date/date_tools/date_tools.wizard.inc b/sites/all/modules/date/date_tools/date_tools.wizard.inc index 034cf3756494507dbdadc360dd4624ec2eade829..14bc2759ead0ffe598303ff3a0690ca7f8bcf849 100644 --- a/sites/all/modules/date/date_tools/date_tools.wizard.inc +++ b/sites/all/modules/date/date_tools/date_tools.wizard.inc @@ -264,7 +264,7 @@ function date_tools_wizard_build($form_values) { $field = field_create_field($field); $instance = field_create_instance($instance); - $view_name = 'calendar_' . $field_name; + $view_name = 'calendar_node_' . $field_name; field_info_cache_clear(TRUE); field_cache_clear(TRUE); diff --git a/sites/all/modules/date/date_views/date_views.info b/sites/all/modules/date/date_views/date_views.info index 9216f8e01a38d8ea2f3e18e4415ef42e84134576..ef7da6c0c137f27ea2a3d19c93c5b828f6efbc19 100644 --- a/sites/all/modules/date/date_views/date_views.info +++ b/sites/all/modules/date/date_views/date_views.info @@ -13,9 +13,9 @@ files[] = includes/date_views.views_default.inc files[] = includes/date_views.views.inc files[] = includes/date_views_plugin_pager.inc -; Information added by drupal.org packaging script on 2012-04-19 -version = "7.x-2.5" +; Information added by drupal.org packaging script on 2012-08-13 +version = "7.x-2.6" core = "7.x" project = "date" -datestamp = "1334835098" +datestamp = "1344850024" diff --git a/sites/all/modules/date/date_views/includes/date_views_argument_handler.inc b/sites/all/modules/date/date_views/includes/date_views_argument_handler.inc index bba39a6be8380d817004ede7eb15fac684e861b4..61aeafc1802b1bcf50bc25c8f8206b1bf1fe7bd8 100644 --- a/sites/all/modules/date/date_views/includes/date_views_argument_handler.inc +++ b/sites/all/modules/date/date_views/includes/date_views_argument_handler.inc @@ -150,6 +150,9 @@ class date_views_argument_handler extends date_views_argument_handler_simple { $this->query->set_where_group($this->options['date_method'], $this->options['date_group']); $this->granularity = $this->date_handler->arg_granularity($this->argument); $format = $this->date_handler->views_formats($this->granularity, 'sql'); + + $this->placeholders = array(); + if (!empty($this->query_fields)) { // Use set_where_group() with the selected date_method // of 'AND' or 'OR' to create the where clause. @@ -161,7 +164,7 @@ class date_views_argument_handler extends date_views_argument_handler_simple { $this->table = $field['table_name']; $this->original_table = $field['table_name']; if ($field['table_name'] != $this->table || !empty($this->relationship)) { - $this->table = $this->query->queue_table($field['table_name'], $this->relationship); + $this->table = $this->query->ensure_table($field['table_name'], $this->relationship); } // $this->table_alias gets set when the first field is processed if otherwise empty. // For subsequent fields, we need to be sure it is emptied again. @@ -169,6 +172,8 @@ class date_views_argument_handler extends date_views_argument_handler_simple { $this->table_alias = NULL; } parent::query($group_by); + + $this->placeholders = array_merge($this->placeholders, $this->date_handler->placeholders); } } } diff --git a/sites/all/modules/date/date_views/includes/date_views_filter_handler.inc b/sites/all/modules/date/date_views/includes/date_views_filter_handler.inc index 63467a2615b1cce2148b00394de3eb64d7b194b3..5eb5ebc90242a59c86acb7bbadbf43279949b7e4 100644 --- a/sites/all/modules/date/date_views/includes/date_views_filter_handler.inc +++ b/sites/all/modules/date/date_views/includes/date_views_filter_handler.inc @@ -58,7 +58,7 @@ class date_views_filter_handler extends date_views_filter_handler_simple { // Respect relationships when determining the table alias. if ($field['table_name'] != $this->table || !empty($this->relationship)) { - $this->related_table_alias = $this->query->queue_table($field['table_name'], $this->relationship); + $this->related_table_alias = $this->query->ensure_table($field['table_name'], $this->relationship); } $table_alias = !empty($this->related_table_alias) ? $this->related_table_alias : $field['table_name']; $field_name = $table_alias . '.' . $field['field_name']; @@ -174,4 +174,4 @@ class date_views_filter_handler extends date_views_filter_handler_simple { } } } -} \ No newline at end of file +} diff --git a/sites/all/modules/date/date_views/includes/date_views_plugin_pager.inc b/sites/all/modules/date/date_views/includes/date_views_plugin_pager.inc index 10c6581be39c74f5e4c9b6f05768b3dc1b1b3e5f..f9594a72f208e001a8cdadf64b89d5d5fee58b71 100644 --- a/sites/all/modules/date/date_views/includes/date_views_plugin_pager.inc +++ b/sites/all/modules/date/date_views/includes/date_views_plugin_pager.inc @@ -168,7 +168,7 @@ class date_views_plugin_pager extends views_plugin_pager { if (empty($this->view->date_info)) $this->view->date_info = new stdClass(); $this->view->date_info->granularity = $argument->date_handler->granularity; $format = $this->view->date_info->granularity == 'week' ? DATE_FORMAT_DATETIME : $argument->sql_format; - $this->view->date_info->placeholders = $argument->date_handler->placeholders; + $this->view->date_info->placeholders = isset($argument->placeholders) ? $argument->placeholders : $argument->date_handler->placeholders; $this->view->date_info->date_arg = $argument->argument; $this->view->date_info->date_arg_pos = $i; $this->view->date_info->year = date_format($argument->min_date, 'Y'); diff --git a/sites/all/modules/date/tests/date_api.test b/sites/all/modules/date/tests/date_api.test index aa39ac319abf32581eda851554b295f6e075df39..f50020c15db78d7b356360e1b47f466785cc29a3 100644 --- a/sites/all/modules/date/tests/date_api.test +++ b/sites/all/modules/date/tests/date_api.test @@ -388,6 +388,14 @@ class DateAPITestCase extends DrupalWebTestCase { foreach ($invalid as $range) { $this->assertFalse(date_range_valid($range), "$range recognized as an invalid date range."); } + + // Test for invalid month names when we are using a short version of the month + $input = '23 abc 2012'; + $timezone = NULL; + $format = 'd M Y'; + $date = new dateObject($input, $timezone, $format); + $this->assertNotEqual(count($date->errors), 0, '23 abc 2012 should be an invalid date'); + } /** diff --git a/sites/all/modules/imce/imce.info b/sites/all/modules/imce/imce.info index 6394ad4e73a3918942fc9aa1231ea54733a832f2..60b6ac8cf3a3e21d753740190a340d87bea65525 100644 --- a/sites/all/modules/imce/imce.info +++ b/sites/all/modules/imce/imce.info @@ -4,9 +4,9 @@ core = "7.x" package = "Media" configure = "admin/config/media/imce" -; Information added by drupal.org packaging script on 2011-10-20 -version = "7.x-1.5" +; Information added by drupal.org packaging script on 2013-01-29 +version = "7.x-1.7" core = "7.x" project = "imce" -datestamp = "1319104232" +datestamp = "1359476607" diff --git a/sites/all/modules/imce/imce.install b/sites/all/modules/imce/imce.install index 8d3e420199b77a47da67e786042c9a7c081b3118..688c6b5b137e52b390afa0ca58cd3f3518402517 100644 --- a/sites/all/modules/imce/imce.install +++ b/sites/all/modules/imce/imce.install @@ -11,13 +11,13 @@ function imce_install() { module_load_include('inc', 'imce', 'inc/imce.core.profiles'); imce_install_profiles(); - drupal_set_message(st('!module has been installed.', array('!module' => l(st('IMCE'), 'admin/config/media/imce')))); } /** * Implements hook_uninstall(). */ function imce_uninstall() { + db_delete('file_usage')->condition('module', 'imce')->execute(); variable_del('imce_profiles'); variable_del('imce_roles_profiles'); variable_del('imce_settings_textarea'); diff --git a/sites/all/modules/imce/imce.module b/sites/all/modules/imce/imce.module index fe93a1204a4b0cef71cd755bec7ee1489705f870..f3dc4bf79dc3755c53a252de3f629f878e6f35d6 100644 --- a/sites/all/modules/imce/imce.module +++ b/sites/all/modules/imce/imce.module @@ -15,6 +15,7 @@ function imce_menu() { 'title' => 'File browser', 'page callback' => 'imce', 'access callback' => 'imce_access', + 'access arguments' => array(FALSE, 1), 'file' => 'inc/imce.page.inc', 'type' => MENU_CALLBACK, ); @@ -118,7 +119,7 @@ function imce_textarea($element) { if (!isset($regexp)) { $regexp = FALSE; if (imce_access() && $regexp = str_replace(' ', '', variable_get('imce_settings_textarea', ''))) { - $regexp = '@^' . str_replace(',', '|', implode('.*', array_map('preg_quote', explode('*', $regexp)))) . '$@'; + $regexp = '@^(' . str_replace(',', '|', implode('.*', array_map('preg_quote', explode('*', $regexp)))) . ')$@'; } } if ($regexp && preg_match($regexp, $element['#id'])) { @@ -132,58 +133,39 @@ function imce_textarea($element) { * Returns the configuration profile assigned to a user for a specific file scheme. */ function imce_user_profile($user, $scheme = NULL) { - $profiles = variable_get('imce_profiles', array()); - $swrappers = file_get_stream_wrappers(); - $default_scheme = variable_get('file_default_scheme', 'public'); + static $ups = array(); - //handle user#1 separately - if ($user->uid == 1) { - $scheme = empty($scheme) ? $default_scheme : $scheme; - if (isset($profiles[1]) && isset($swrappers[$scheme])) { - return $profiles[1] + array('scheme' => $scheme); - } - return FALSE; + // Set scheme + if (empty($scheme)) { + $scheme = variable_get('file_default_scheme', 'public'); } - //handle regular users - $roles_profiles = variable_get('imce_roles_profiles', array()); - //store assigned configuration - $conf = array(); - foreach ($roles_profiles as $rid => $role) { - if (isset($user->roles[$rid])) { - $conf = $role; - break; - } + // Return from cache. + if (isset($ups[$scheme][$user->uid])) { + return $ups[$scheme][$user->uid]; } + $ups[$scheme][$user->uid] = FALSE; - //no scheme-profile assignment - if (empty($conf)) { + // Check scheme + $swrappers = file_get_stream_wrappers(); + if (!isset($swrappers[$scheme])) { return FALSE; } - //return the profile for the specified scheme - if (!empty($scheme)) { - $key = $scheme . '_pid'; - if (isset($conf[$key]) && isset($profiles[$conf[$key]]) && isset($swrappers[$scheme])) { - return $profiles[$conf[$key]] + array('scheme' => $scheme); - } - return FALSE; - } + $profiles = variable_get('imce_profiles', array()); + $scinfo = array('scheme' => $scheme); - //no scheme specified. check the default - $scheme = $default_scheme; - $key = $scheme . '_pid'; - if (isset($conf[$key]) && isset($profiles[$conf[$key]]) && isset($swrappers[$scheme])) { - return $profiles[$conf[$key]] + array('scheme' => $scheme); + // Handle user#1 separately + if ($user->uid == 1) { + return $ups[$scheme][$user->uid] = isset($profiles[1]) ? $profiles[1] + $scinfo : FALSE; } - //check if any of the schemes has a profile assigned. - foreach ($conf as $key => $pid) { - if (substr($key, -4) == '_pid' && isset($profiles[$pid])) { - $scheme = substr($key, 0, -4); - if (isset($swrappers[$scheme])) { - return $profiles[$pid] + array('scheme' => $scheme); - } + // Handle regular users. + $roles_profiles = variable_get('imce_roles_profiles', array()); + $sckey = $scheme . '_pid'; + foreach ($roles_profiles as $rid => $conf) { + if (isset($user->roles[$rid]) && isset($conf[$sckey]) && isset($profiles[$conf[$sckey]])) { + return $ups[$scheme][$user->uid] = $profiles[$conf[$sckey]] + $scinfo; } } @@ -194,28 +176,11 @@ function imce_user_profile($user, $scheme = NULL) { * Checks if the user is assigned an imce profile. * A more detailed assignment check is performed before imce loads. */ -function imce_access($user = FALSE) { +function imce_access($user = FALSE, $scheme = NULL) { if ($user === FALSE) { global $user; } - - if ($user->uid == 1) { - return TRUE; - } - - $roles_profiles = variable_get('imce_roles_profiles', array()); - foreach ($roles_profiles as $rid => $role) { - if (isset($user->roles[$rid])) { - foreach ($role as $key => $pid) { - if (substr($key, -4) == '_pid' && $pid) { - return TRUE; - } - } - break; - } - } - - return FALSE; + return imce_user_profile($user, $scheme) ? TRUE : FALSE; } /** @@ -225,7 +190,6 @@ function imce_user_page_access($account, $user = FALSE) { if ($user === FALSE) { global $user; } - return ($user->uid == 1 || $account->uid == $user->uid) && ($profile = imce_user_profile($account)) && $profile['usertab']; } @@ -233,5 +197,5 @@ function imce_user_page_access($account, $user = FALSE) { * Check if the directory name is regular. */ function imce_reg_dir($dirname) { - return $dirname == '.' || (is_string($dirname) && $dirname != '' && !preg_match('@(^\s)|(^/)|(^\./)|(\s$)|(/$)|(/\.$)|(\.\.)|(//)|(\\\\)|(/\./)@', $dirname)); + return $dirname == '.' || is_int($dirname) || (is_string($dirname) && $dirname != '' && !preg_match('@(^\s)|(^/)|(^\./)|(\s$)|(/$)|(/\.$)|(\.\.)|(//)|(\\\\)|(/\./)@', $dirname)); } \ No newline at end of file diff --git a/sites/all/modules/imce/inc/imce.admin.inc b/sites/all/modules/imce/inc/imce.admin.inc index 4ff3077bb9c9ebe1ac21cab47327877f6a00f9a9..ff4961836ff255986b5398cf4e5a4f03779c9597 100644 --- a/sites/all/modules/imce/inc/imce.admin.inc +++ b/sites/all/modules/imce/inc/imce.admin.inc @@ -249,7 +249,7 @@ function imce_profile_form($form, &$form_state, $pid = 0) { ); $form['dimensions'] = array( '#type' => 'textfield', - '#title' => t('Maximum image resolution'), + '#title' => t('Maximum image dimensions'), '#default_value' => $profile['dimensions'], '#description' => t('The maximum allowed image size (e.g. 640x480). Set to 0 for no restriction. If an <a href="!image-toolkit-link">image toolkit</a> is installed, files exceeding this value will be scaled down to fit.', array('!image-toolkit-link' => url('admin/config/media/image-toolkit'))), '#field_suffix' => '<kbd>' . t('WIDTHxHEIGHT') . '</kbd>', diff --git a/sites/all/modules/imce/inc/imce.page.inc b/sites/all/modules/imce/inc/imce.page.inc index 27b27adaa7ca088f96bb60bdbcaa4b08fe0d87dc..bcb00b2d2e6eb7a43b48329877d59fa31929ff71 100644 --- a/sites/all/modules/imce/inc/imce.page.inc +++ b/sites/all/modules/imce/inc/imce.page.inc @@ -11,6 +11,7 @@ function imce($scheme = NULL) { module_invoke('admin_menu', 'suppress');//suppress admin_menu $jsop = isset($_GET['jsop']) ? $_GET['jsop'] : NULL; + drupal_add_http_header('Content-Type', 'text/html; charset=utf-8'); print imce_page($GLOBALS['user'], $scheme, $jsop); exit(); } @@ -258,7 +259,7 @@ function imce_fileop_form_validate($form, &$form_state) { return form_error($form['filenames'], t('Please select a file.')); } - //filenames come seperated by colon + //filenames come separated by colon $filenames = explode(':', $form_state['values']['filenames']); $cnt = count($filenames); //check the number of files. @@ -359,7 +360,7 @@ function imce_resize_submit($form, &$form_state) { //check dimensions $width = (int) $form_state['values']['width']; $height = (int) $form_state['values']['height']; - list($maxw, $maxh) = explode('x', $imce['dimensions']); + list($maxw, $maxh) = $imce['dimensions'] ? explode('x', $imce['dimensions']) : array(0, 0); if ($width < 1 || $height < 1 || ($maxw && ($width > $maxw || $height > $maxh))) { drupal_set_message(t('Please specify dimensions within the allowed range that is from 1x1 to @dimensions.', array('@dimensions' => $imce['dimensions'] ? $imce['dimensions'] : t('unlimited'))), 'error'); return; @@ -424,10 +425,6 @@ function imce_delete_filepath($uri) { if (!file_delete($file, TRUE)) { return FALSE; } - // Remove imce usage - if ($is_imce) { - file_usage_delete($file, 'imce'); - } } // Not in db. Probably loaded via ftp. elseif (!file_unmanaged_delete($uri)) { @@ -676,13 +673,25 @@ function imce_validate_quotas($file, &$imce, $add = 0) { } /** - * Check if the file is an image and return info. + * Checks if the file is an image and returns info. + * There are two switchable versions that use image_get_info() and getimagesize() */ -function imce_image_info($file) { - if (is_file($file) && ($dot = strrpos($file, '.')) && in_array(strtolower(substr($file, $dot+1)), array('jpg', 'jpeg', 'gif', 'png')) && ($info = @getimagesize($file)) && in_array($info[2], array(IMAGETYPE_JPEG, IMAGETYPE_GIF, IMAGETYPE_PNG)) ) { - return array('width' => $info[0], 'height' => $info[1], 'type' => $info[2], 'mime' => $info['mime']); +if (variable_get('imce_image_get_info', 0)) { + function imce_image_info($file) { + $mimes = array('image/jpeg' => IMAGETYPE_JPEG, 'image/gif' => IMAGETYPE_GIF, 'image/png' => IMAGETYPE_PNG); + if (is_file($file) && ($dot = strrpos($file, '.')) && in_array(strtolower(substr($file, $dot+1)), array('jpg', 'jpeg', 'gif', 'png')) && ($info = @image_get_info($file)) && isset($mimes[$info['mime_type']]) ) { + return array('width' => $info['width'], 'height' => $info['height'], 'type' => $mimes[$info['mime_type']], 'mime' => $info['mime_type']); + } + return FALSE; + } +} +else { + function imce_image_info($file) { + if (is_file($file) && ($dot = strrpos($file, '.')) && in_array(strtolower(substr($file, $dot+1)), array('jpg', 'jpeg', 'gif', 'png')) && ($info = @getimagesize($file)) && in_array($info[2], array(IMAGETYPE_JPEG, IMAGETYPE_GIF, IMAGETYPE_PNG)) ) { + return array('width' => $info[0], 'height' => $info[1], 'type' => $info[2], 'mime' => $info['mime']); + } + return FALSE; } - return FALSE; } /** @@ -838,6 +847,7 @@ function imce_working_directory(&$imce) { //or the whole list. foreach ($imce['directories'] as $dirname => $info) { + $dirname = (string) $dirname; if (imce_check_directory($dirname, $imce)) { if ($sess) { $_SESSION['imce_directory'] = rawurlencode($dirname); diff --git a/sites/all/modules/imce/js/imce.js b/sites/all/modules/imce/js/imce.js index 2a2f9b4011c74df0eddae5583effeaf7e5d3993e..f0380873375d20c531891217ef81059065c90747 100644 --- a/sites/all/modules/imce/js/imce.js +++ b/sites/all/modules/imce/js/imce.js @@ -424,7 +424,7 @@ navCache: function (dir, newdir) { //validate upload form uploadValidate: function (data, form, options) { - var path = data[0].value; + var path = $('#edit-imce').val(); if (!path) return false; if (imce.conf.extensions != '*') { var ext = path.substr(path.lastIndexOf('.') + 1); @@ -492,7 +492,7 @@ fopSettings: function (fop) { //toggle loading state fopLoading: function(fop, state) { - var el = imce.el('edit-'+ fop), func = state ? 'addClass' : 'removeClass' + var el = imce.el('edit-'+ fop), func = state ? 'addClass' : 'removeClass'; if (el) { $(el)[func]('loading').attr('disabled', state); } diff --git a/sites/all/modules/imce/js/imce_extras.js b/sites/all/modules/imce/js/imce_extras.js index 349de2d1edc48d94be6d3204246c907aec100ef5..bccf1c1d3ff2313072dec05dda9556a27efffdc4 100644 --- a/sites/all/modules/imce/js/imce_extras.js +++ b/sites/all/modules/imce/js/imce_extras.js @@ -117,15 +117,10 @@ imce.firstSort = function() { //sort file list according to column index. imce.columnSort = function(cid, dsc) { if (imce.findex.length < 2) return; - if (cid == imce.vars.cid && dsc != imce.vars.dsc) { - imce.findex.reverse(); - } - else { - var func = 'sort'+ (cid == 0 ? 'Str' : 'Num') + (dsc ? 'Dsc' : 'Asc'); - var prop = cid == 2 || cid == 3 ? 'innerHTML' : 'id'; - //sort rows - imce.findex.sort(cid ? function(r1, r2) {return imce[func](r1.cells[cid][prop], r2.cells[cid][prop])} : function(r1, r2) {return imce[func](r1.id, r2.id)}); - } + var func = 'sort'+ (cid == 0 ? 'Str' : 'Num') + (dsc ? 'Dsc' : 'Asc'); + var prop = cid == 2 || cid == 3 ? 'innerHTML' : 'id'; + //sort rows + imce.findex.sort(cid ? function(r1, r2) {return imce[func](r1.cells[cid][prop], r2.cells[cid][prop])} : function(r1, r2) {return imce[func](r1.id, r2.id)}); //insert sorted rows for (var row, i=0; row = imce.findex[i]; i++) { imce.tbody.appendChild(row); diff --git a/sites/all/modules/link/link.css b/sites/all/modules/link/link.css index 7bdec9e18a560a2c7384c1eb377b158c36066d71..1590e7ad51bc27bf461157af85bcfabd6846d0b7 100644 --- a/sites/all/modules/link/link.css +++ b/sites/all/modules/link/link.css @@ -1,8 +1,8 @@ -div.link-field-column { +.link-field-column { float: left; width: 48%; } -div.link-field-column .form-text { +.link-field-column .form-text { width: 95%; } diff --git a/sites/all/modules/link/link.devel_generate.inc b/sites/all/modules/link/link.devel_generate.inc new file mode 100644 index 0000000000000000000000000000000000000000..7be4a0dde2eb763f2e48c08410861783e4463a9f --- /dev/null +++ b/sites/all/modules/link/link.devel_generate.inc @@ -0,0 +1,29 @@ +<?php + +/** + * @file + * Devel Generate support for Link module. + */ + +/** + * Implements hook_devel_generate(). + */ +function link_devel_generate($object, $field, $instance, $bundle) { + if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_CUSTOM) { + return devel_generate_multiple('_link_devel_generate', $object, $field, $instance, $bundle); + } + else { + return _link_devel_generate($object, $field, $instance, $bundle); + } +} + +/** + * Callback for hook_devel_generate(). + */ +function _link_devel_generate($object, $field, $instance, $bundle) { + return array( + 'url' => url('<front>', array('absolute' => TRUE)), + 'title' => devel_create_greeking(mt_rand(1, 3), TRUE), + 'attributes' => _link_default_attributes(), + ); +} diff --git a/sites/all/modules/link/link.diff.inc b/sites/all/modules/link/link.diff.inc new file mode 100644 index 0000000000000000000000000000000000000000..9e34123ae4d9fd4324f60fe2919af89c4299bf58 --- /dev/null +++ b/sites/all/modules/link/link.diff.inc @@ -0,0 +1,22 @@ +<?php + +/** + * @file + * Provide diff field functions for the Link module. + */ + +/** + * Diff field callback for parsing link fields comparative values. + */ +function link_field_diff_view($items, $context) { + $diff_items = array(); + foreach ($items as $delta => $item) { + if ($item['url'] && $item['title']) { + $diff_items[$delta] = $item['title'] . ' (' . $item['url'] . ')'; + } + else { + $diff_items[$delta] = $item['title'] . $item['url']; + } + } + return $diff_items; +} diff --git a/sites/all/modules/link/link.info b/sites/all/modules/link/link.info index 8a5d0c18ccfbddac667cf29f77072cc5a5eb6aec..57aedb3983ded3bd22e46062992a68aa2273132c 100644 --- a/sites/all/modules/link/link.info +++ b/sites/all/modules/link/link.info @@ -4,7 +4,8 @@ core = 7.x package = Fields files[] = link.module -files[] = link.install +files[] = link.migrate.inc + ; Tests files[] = tests/link.test files[] = tests/link.attribute.test @@ -17,9 +18,9 @@ files[] = tests/link.validate.test files[] = views/link_views_handler_argument_target.inc files[] = views/link_views_handler_filter_protocol.inc -; Information added by drupal.org packaging script on 2011-10-23 -version = "7.x-1.0" +; Information added by drupal.org packaging script on 2013-02-09 +version = "7.x-1.1" core = "7.x" project = "link" -datestamp = "1319392535" +datestamp = "1360444361" diff --git a/sites/all/modules/link/link.install b/sites/all/modules/link/link.install index fdfc5d1e2da4ab883317d9fd25a2a1b42be2cbc9..14e745d42158981b34657879dcb846f66f83e62c 100644 --- a/sites/all/modules/link/link.install +++ b/sites/all/modules/link/link.install @@ -18,20 +18,21 @@ function link_field_schema($field) { 'columns' => array( 'url' => array( 'type' => 'varchar', - 'length' => 2048, // Maximum URLs length. + // Maximum URLs length. + 'length' => 2048, 'not null' => FALSE, - 'sortable' => TRUE + 'sortable' => TRUE, ), 'title' => array( 'type' => 'varchar', 'length' => 255, 'not null' => FALSE, - 'sortable' => TRUE + 'sortable' => TRUE, ), 'attributes' => array( 'type' => 'text', 'size' => 'medium', - 'not null' => FALSE + 'not null' => FALSE, ), ), ); @@ -87,8 +88,7 @@ function link_update_7000() { * Renames all displays from foobar to link_foobar */ function link_update_7001() { - // for each field that is a link field, we need to update the display types: - + // Update the display type for each link field type. $result = db_query("SELECT id, field_name, data FROM {field_config} WHERE module = 'link' AND type = 'link_field'"); foreach ($result as $field) { $field_id = $field->id; @@ -102,13 +102,12 @@ function link_update_7001() { $update_instance = FALSE; foreach ($instance_data['display'] as $display_name => $display_data) { if ($display_data['type'] && (0 !== strpos($display_data['type'], 'link_'))) { - $instance_data['display'][$display_name]['type'] = 'link_'. $display_data['type']; + $instance_data['display'][$display_name]['type'] = 'link_' . $display_data['type']; $update_instance = TRUE; } } if ($update_instance) { - // update the database. - $num_updated = db_update('field_config_instance') + db_update('field_config_instance') ->fields(array('data' => serialize($instance_data))) ->condition('id', $instance->id) ->execute(); diff --git a/sites/all/modules/link/link.migrate.inc b/sites/all/modules/link/link.migrate.inc new file mode 100644 index 0000000000000000000000000000000000000000..6388d11e44aa0f7429b7cf34520de7c4bea48796 --- /dev/null +++ b/sites/all/modules/link/link.migrate.inc @@ -0,0 +1,108 @@ +<?php + +/** + * @file + * Support for migrate module. + * + * With Migrate 2.4 or later, you can use the subfield syntax to set the title + * and attributes: + * + * @code + * $this->addFieldMapping('field_my_link', 'source_url'); + * $this->addFieldMapping('field_my_link:title', 'source_title'); + * $this->addFieldMapping('field_my_link:attributes', 'source_attributes'); + * @endcode + * + * With earlier versions of Migrate, you must pass an arguments array: + * + * @code + * $link_args = array( + * 'title' => array('source_field' => 'source_title'), + * 'attributes' => array('source_field' => 'source_attributes'), + * ); + * $this->addFieldMapping('field_my_link', 'source_url') + * ->arguments($link_args); + * @endcode + */ + +/** + * Implements hook_migrate_api(). + */ +function link_migrate_api() { + return array( + 'api' => 2, + 'field handlers' => array('MigrateLinkFieldHandler'), + ); +} + +class MigrateLinkFieldHandler extends MigrateFieldHandler { + public function __construct() { + $this->registerTypes(array('link_field')); + } + + static function arguments($title = NULL, $attributes = NULL, $language = NULL) { + $arguments = array(); + if (!is_null($title)) { + $arguments['title'] = $title; + } + if (!is_null($attributes)) { + $arguments['attributes'] = $attributes; + } + if (!is_null($language)) { + $arguments['language'] = $language; + } + return $arguments; + } + + /** + * Implementation of MigrateFieldHandler::fields(). + * + * @param $type + * The field type. + * @param $instance + * Instance info for the field. + * @param Migration $migration + * The migration context for the parent field. We can look at the mappings + * and determine which subfields are relevant. + * @return array + */ + public function fields($type, $instance, $migration = NULL) { + return array( + 'title' => t('Subfield: The link title attribute'), + 'attributes' => t('Subfield: The attributes for this link'), + 'language' => t('Subfield: The language for the field'), + ); + } + + public function prepare($entity, array $field_info, array $instance, array $values) { + if (isset($values['arguments'])) { + $arguments = $values['arguments']; + unset($values['arguments']); + } + else { + $arguments = array(); + } + + $language = $this->getFieldLanguage($entity, $field_info, $arguments); + $values = array_filter($values); + + foreach ($values as $delta => $value) { + $item = array(); + if (isset($arguments['title'])) { + if (!is_array($arguments['title'])) { + $item['title'] = $arguments['title']; + } + elseif (isset($arguments['title'][$delta])) { + $item['title'] = $arguments['title'][$delta]; + } + } + if (isset($arguments['attributes'])) { + $item['attributes'] = $arguments['attributes']; + } + $item['url'] = $value; + $return[$language][$delta] = $item; + } + + return isset($return) ? $return : NULL; + } +} diff --git a/sites/all/modules/link/link.module b/sites/all/modules/link/link.module index c7af1ffbfbe667e3647581ec3b299a511ace9780..241066d55c7c1797e5908099318735e0f62b507e 100644 --- a/sites/all/modules/link/link.module +++ b/sites/all/modules/link/link.module @@ -35,7 +35,7 @@ function link_field_info() { 'url' => 0, 'title' => 'optional', 'title_value' => '', - 'title_maxlength' => 128, //patch #1307788 from nmc + 'title_maxlength' => 128, 'enable_tokens' => 1, 'display' => array( 'url_cutoff' => 80, @@ -46,7 +46,7 @@ function link_field_info() { 'url' => 0, 'title' => 'optional', 'title_value' => '', - 'title_maxlength' => 128, // patch #1307788 from nmc + 'title_maxlength' => 128, 'enable_tokens' => 1, 'display' => array( 'url_cutoff' => 80, @@ -69,7 +69,7 @@ function link_field_instance_settings_form($field, $instance) { $form = array( '#element_validate' => array('link_field_settings_form_validate'), ); - + $form['validate_url'] = array( '#type' => 'checkbox', '#title' => t('Validate URL'), @@ -97,7 +97,7 @@ function link_field_instance_settings_form($field, $instance) { '#title' => t('Link Title'), '#default_value' => isset($instance['settings']['title']) ? $instance['settings']['title'] : 'optional', '#options' => $title_options, - '#description' => t('If the link title is optional or required, a field will be displayed to the end user. If the link title is static, the link will always use the same title. If <a href="http://drupal.org/project/token">token module</a> is installed, the static title value may use any other node field as its value. Static and token-based titles may include most inline XHTML tags such as <em>strong</em>, <em>em</em>, <em>img</em>, <em>span</em>, etc.'), + '#description' => t('If the link title is optional or required, a field will be displayed to the end user. If the link title is static, the link will always use the same title. If <a href="http://drupal.org/project/token">token module</a> is installed, the static title value may use any other entity field as its value. Static and token-based titles may include most inline XHTML tags such as <em>strong</em>, <em>em</em>, <em>img</em>, <em>span</em>, etc.'), ); $form['title_value'] = array( @@ -107,14 +107,14 @@ function link_field_instance_settings_form($field, $instance) { '#description' => t('This title will always be used if “Static Title” is selected above.'), ); - $form['title_maxlength'] = array( // patch #1307788 from nmc + $form['title_maxlength'] = array( '#type' => 'textfield', '#title' => t('Max length of title field'), '#default_value' => isset($instance['settings']['title_maxlength']) ? $instance['settings']['title_maxlength'] : '128', '#description' => t('Set a maximum length on the title field (applies only if Link Title is optional or required). The maximum limit is 255 characters.'), '#maxlength' => 3, '#size' => 3, - ); + ); if (module_exists('token')) { // Add token module replacements fields @@ -125,23 +125,19 @@ function link_field_instance_settings_form($field, $instance) { '#title' => t('Placeholder tokens'), '#description' => t("The following placeholder tokens can be used in both paths and titles. When used in a path or title, they will be replaced with the appropriate values."), ); - $token_type = array( - 'theme' => 'token_tree', - 'token_types' => array($instance['entity_type']), - 'global_types' => TRUE, - 'click_insert' => TRUE, - 'recursion_limit' => 2, - ); + $entity_info = entity_get_info($instance['entity_type']); $form['tokens']['help'] = array( - '#type' => 'markup', - '#markup' => theme('token_tree', $token_type), + '#theme' => 'token_tree', + '#token_types' => array($entity_info['token type']), + '#global_types' => TRUE, + '#click_insert' => TRUE, ); $form['enable_tokens'] = array( '#type' => 'checkbox', '#title' => t('Allow user-entered tokens'), '#default_value' => isset($instance['settings']['enable_tokens']) ? $instance['settings']['enable_tokens'] : 1, - '#description' => t('Checking will allow users to enter tokens in URLs and Titles on the node edit form. This does not affect the field settings on this page.'), + '#description' => t('Checking will allow users to enter tokens in URLs and Titles on the entity edit form. This does not affect the field settings on this page.'), ); } @@ -154,7 +150,7 @@ function link_field_instance_settings_form($field, $instance) { '#default_value' => isset($instance['settings']['display']['url_cutoff']) ? $instance['settings']['display']['url_cutoff'] : '80', '#description' => t('If the user does not include a title for this link, the URL will be used as the title. When should the link title be trimmed and finished with an elipsis (…)? Leave blank for no limit.'), '#maxlength' => 3, - '#size' => 3, + '#size' => 3, ); $target_options = array( @@ -181,6 +177,18 @@ function link_field_instance_settings_form($field, $instance) { '#field_suffix' => '"', '#size' => 20, ); + $rel_remove_options = array( + 'default' => t('Keep rel as set up above (untouched/default)'), + 'rel_remove_external' => t('Remove rel if given link is external'), + 'rel_remove_internal' => t('Remove rel if given link is internal'), + ); + $form['rel_remove'] = array( + '#type' => 'radios', + '#title' => t('Remove rel attribute automatically'), + '#default_value' => !isset($instance['settings']['rel_remove']) ? 'default' : $instance['settings']['rel_remove'], + '#description' => t('Turn on/off if rel attribute should be removed automatically, if user given link is internal/external'), + '#options' => $rel_remove_options, + ); $form['attributes']['class'] = array( '#type' => 'textfield', '#title' => t('Additional CSS Class'), @@ -205,29 +213,28 @@ function link_field_instance_settings_form($field, $instance) { } /** - * Validate the field settings form. + * #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'])) { + 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.')); } - if (!empty($form_state['values']['instance']['settings']['display']['url_cutoff']) // patch #1307788 from nmc - && !is_numeric($form_state['values']['instance']['settings']['display']['url_cutoff'])) { + 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.')); } - if (empty($form_state['values']['instance']['settings']['title_maxlength'])) { // patch #1307788 from nmc + if (empty($form_state['values']['instance']['settings']['title_maxlength'])) { form_set_value($element['title_maxlength'], '128', $form_state); - } elseif (!is_numeric($form_state['values']['instance']['settings']['title_maxlength'])) { + } + elseif (!is_numeric($form_state['values']['instance']['settings']['title_maxlength'])) { form_set_error('title_maxlength', t('The max length of the link title must be numeric.')); - } elseif ($form_state['values']['instance']['settings']['title_maxlength'] > 255) { + } + elseif ($form_state['values']['instance']['settings']['title_maxlength'] > 255) { form_set_error('title_maxlength', t('The max length of the link title cannot be greater than 255 characters.')); } - } /** - * Implement hook_field_is_empty(). + * Implements hook_field_is_empty(). */ function link_field_is_empty($item, $field) { return empty($item['title']) && empty($item['url']); @@ -251,19 +258,28 @@ function link_field_validate($entity_type, $entity, $field, $instance, $langcode $optional_field_found = FALSE; if ($instance['settings']['validate_url'] !== 0 || is_null($instance['settings']['validate_url']) || !isset($instance['settings']['validate_url'])) { foreach ($items as $delta => $value) { - _link_validate($items[$delta], $delta, $field, $entity, $instance, $optional_field_found); + _link_validate($items[$delta], $delta, $field, $entity, $instance, $langcode, $optional_field_found); } } if ($instance['settings']['url'] === 'optional' && $instance['settings']['title'] === 'optional' && $instance['required'] && !$optional_field_found) { - form_set_error($field['field_name'] .'][0][title', t('At least one title or URL must be entered.')); + form_set_error($field['field_name'] . '][' . $langcode . '][0][title', t('At least one title or URL must be entered.')); + } +} + +/** + * Implements hook_field_insert(). + */ +function link_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) { + foreach ($items as $delta => $value) { + _link_process($items[$delta], $delta, $field, $entity); } } /** - * Implements hook_field_presave(). + * Implements hook_field_update(). */ -function link_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) { +function link_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) { foreach ($items as $delta => $value) { _link_process($items[$delta], $delta, $field, $entity); } @@ -308,13 +324,13 @@ function link_field_widget_form(&$form, &$form_state, $field, $instance, $langco * Unpacks the item attributes for use. */ function _link_load($field, $item, $instance) { - /*return $item['attributes'] = isset($item['attributes']) ? - unserialize($item['attributes']) : - $instance['settings']['attributes'];*/ if (isset($item['attributes'])) { - return unserialize($item['attributes']); + if (!is_array($item['attributes'])) { + $item['attributes'] = unserialize($item['attributes']); + } + return $item['attributes']; } - else if (isset($instance['settings']['attributes'])) { + elseif (isset($instance['settings']['attributes'])) { return $instance['settings']['attributes']; } else { @@ -329,7 +345,8 @@ function _link_process(&$item, $delta = 0, $field, $entity) { // Trim whitespace from URL. $item['url'] = trim($item['url']); - // if no attributes are set then make sure $item['attributes'] is an empty array - this lets $field['attributes'] override it. + // If no attributes are set then make sure $item['attributes'] is an empty + // array, so $field['attributes'] can override it. if (empty($item['attributes'])) { $item['attributes'] = array(); } @@ -340,8 +357,7 @@ function _link_process(&$item, $delta = 0, $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($node)) { + 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'])) { unset($item['url']); } @@ -351,45 +367,33 @@ function _link_process(&$item, $delta = 0, $field, $entity) { /** * Validates that the link field has been entered properly. */ -function _link_validate(&$item, $delta, $field, $node, $instance, &$optional_field_found) { - if ($item['url'] - && !(isset($instance['default_value'][$delta]['url']) - && $item['url'] === $instance['default_value'][$delta]['url'] - && !$instance['required'])) { +function _link_validate(&$item, $delta, $field, $entity, $instance, $langcode, &$optional_field_found) { + 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) { - form_set_error($field['field_name'] .']['. $delta .'][url', t('Not a valid URL.')); + form_set_error($field['field_name'] . '][' . $langcode . '][' . $delta . '][url', t('The value provided for %field is not a valid URL.', array('%field' => $instance['label']))); } // Require a title for the link if necessary. if ($instance['settings']['title'] == 'required' && strlen(trim($item['title'])) == 0) { - form_set_error($field['field_name'] .']['. $delta .'][title', t('Titles are required for all links.')); + form_set_error($field['field_name'] . '][' . $langcode . '][' . $delta . '][title', t('Titles are required for all links.')); } } // Require a link if we have a title. - if ($instance['settings']['url'] !== 'optional' - && strlen(isset($item['title']) ? $item['title'] : NULL) > 0 - && strlen(trim($item['url'])) == 0) { - form_set_error($field['field_name'] .']['. $delta .'][url', t('You cannot enter a title without a link url.')); + if ($instance['settings']['url'] !== 'optional' && strlen(isset($item['title']) ? $item['title'] : NULL) > 0 && strlen(trim($item['url'])) == 0) { + form_set_error($field['field_name'] . '][' . $langcode . '][' . $delta . '][url', t('You cannot enter a title without a link url.')); } // 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)) { + if ($instance['settings']['url'] === 'optional' && $instance['settings']['title'] === 'optional' && (strlen(trim($item['url'])) !== 0 || strlen(trim($item['title'])) !== 0)) { $optional_field_found = TRUE; } // Require entire field - if ($instance['settings']['url'] === 'optional' - && $instance['settings']['title'] === 'optional' - && $instance['required'] == 1 - && !$optional_field_found - && isset($instance['id'])) { - form_set_error($instance['field_name'] .'][0][title', - t('At least one title or URL must be entered.')); + if ($instance['settings']['url'] === 'optional' && $instance['settings']['title'] === 'optional' && $instance['required'] == 1 && !$optional_field_found && isset($instance['id'])) { + form_set_error($instance['field_name'] . '][' . $langcode . '][0][title', t('At least one title or URL must be entered.')); } } /** - * Cleanup user-entered values for a link field according to field settings. + * Clean up user-entered values for a link field according to field settings. * * @param $item * A single link item, usually containing url, title, and attributes. @@ -397,67 +401,88 @@ function _link_validate(&$item, $delta, $field, $node, $instance, &$optional_fie * The delta value if this field is one of multiple fields. * @param $field * The CCK field definition. - * @param $node - * The node containing this link. + * @param $entity + * The entity containing this link. */ -function _link_sanitize(&$item, $delta, &$field, $instance, &$node) { +function _link_sanitize(&$item, $delta, &$field, $instance, &$entity) { // Don't try to process empty links. if (empty($item['url']) && empty($item['title'])) { return; } // Replace URL tokens. + $entity_type = $instance['entity_type']; + $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 + ); if (isset($instance['settings']['enable_tokens']) && $instance['settings']['enable_tokens']) { global $user; - // Load the node if necessary for nodes in views. - $token_node = isset($node->nid) ? node_load($node->nid) : $node; - $item['url'] = token_replace($item['url'], array('node' => $token_node)); + // 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)); } $type = link_validate_url($item['url']); - // If we can't determine the type of url, and we've been told not to validate it, - // then we assume it's a LINK_EXTERNAL type for later processing. #357604 + // 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) { $type = LINK_EXTERNAL; } $url = link_cleanup_url($item['url']); + $url_parts = _link_parse_url($url); - // Separate out the anchor if any. - if (strpos($url, '#') !== FALSE) { - $item['fragment'] = substr($url, strpos($url, '#') + 1); - $url = substr($url, 0, strpos($url, '#')); - } - // Separate out the query string if any. - if (strpos($url, '?') !== FALSE) { - $query = substr($url, strpos($url, '?') + 1); - parse_str($query, $query_array); - $item['query'] = $query_array; - $url = substr($url, 0, strpos($url, '?')); + // We can't check_plain('<front>') because it'll break. + if ($type != LINK_FRONT) { + $url_parts['url'] = check_plain($url_parts['url']); } - $item['url'] = check_plain($url); + if (!empty($url_parts['url'])) { + $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' => TRUE, + 'html' => TRUE, + ) + ); + } // Create a shortened URL for display. - $display_url = $type == LINK_EMAIL ? - str_replace('mailto:', '', $url) : - url($url, array('query' => isset($item['query']) ? - $item['query'] : - NULL, - 'fragment' => isset($item['fragment']) ? - $item['fragment'] : - NULL, - 'absolute' => TRUE)); + if ($type == LINK_EMAIL) { + $display_url = str_replace('mailto:', '', $url); + } + else { + $display_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' => TRUE, + ) + ); + } if ($instance['settings']['display']['url_cutoff'] && strlen($display_url) > $instance['settings']['display']['url_cutoff']) { - $display_url = substr($display_url, 0, $instance['settings']['display']['url_cutoff']) ."..."; + $display_url = substr($display_url, 0, $instance['settings']['display']['url_cutoff']) . "..."; } $item['display_url'] = $display_url; // Use the title defined at the instance level. if ($instance['settings']['title'] == 'value' && strlen(trim($instance['settings']['title_value']))) { $title = $instance['settings']['title_value']; + if (function_exists('i18n_string_translate')) { + $i18n_string_name = "field:{$instance['field_name']}:{$instance['bundle']}:title_value"; + $title = i18n_string_translate($i18n_string_name, $title); + } } // Use the title defined by the user at the widget level. - else if (isset($item['title'])) { + elseif (isset($item['title'])) { $title = $item['title']; } else { @@ -466,10 +491,16 @@ function _link_sanitize(&$item, $delta, &$field, $instance, &$node) { // Replace tokens. if ($title && ($instance['settings']['title'] == 'value' || $instance['settings']['enable_tokens'])) { - // Load the node if necessary for nodes in views. - $token_node = isset($node->nid) ? node_load($node->nid) : $node; - $title = filter_xss(token_replace($title, array('node' => $token_node)), - array('b', 'br', 'code', 'em', 'i', 'img', 'span', 'strong', 'sub', 'sup', 'tt', 'u')); + // 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')); $item['html'] = TRUE; } $item['title'] = empty($title) ? $item['display_url'] : $title; @@ -484,7 +515,7 @@ function _link_sanitize(&$item, $delta, &$field, $instance, &$node) { } // Add default attributes. - if (!is_array($instance['settings']['attributes'])){ + if (!is_array($instance['settings']['attributes'])) { $instance['settings']['attributes'] = _link_default_attributes(); } else { @@ -499,23 +530,35 @@ function _link_sanitize(&$item, $delta, &$field, $instance, &$node) { if ($instance['settings']['attributes']['target'] != LINK_TARGET_USER) { $item['attributes']['target'] = $instance['settings']['attributes']['target']; } + elseif ($item['attributes']['target'] == LINK_TARGET_USER) { + $item['attributes']['target'] = LINK_TARGET_DEFAULT; + } // Remove the target attribute if the default (no target) is selected. - if (empty($item['attributes']) || $item['attributes']['target'] == LINK_TARGET_DEFAULT) { + if (empty($item['attributes']) || (isset($item['attributes']['target']) && $item['attributes']['target'] == LINK_TARGET_DEFAULT)) { unset($item['attributes']['target']); } - // Remove the rel=nofollow for internal links. - if ($type != LINK_EXTERNAL && strpos($item['attributes']['rel'], 'nofollow') !== FALSE) { - $item['attributes']['rel'] = str_replace('nofollow', '', $item['attributes']); + // Remove rel attribute for internal or external links if selected. + if (isset($item['attributes']['rel']) && isset($instance['settings']['rel_remove']) && $instance['settings']['rel_remove'] != 'default') { + if (($instance['settings']['rel_remove'] != 'rel_remove_internal' && $type != LINK_INTERNAL) || + ($instance['settings']['rel_remove'] != 'rel_remove_external' && $type != LINK_EXTERNAL)) { + unset($item['attributes']['rel']); + } } // Handle "title" link attribute. if (!empty($item['attributes']['title']) && module_exists('token')) { - // Load the node (necessary for nodes in views). - $token_node = isset($node->nid) ? node_load($node->nid) : $node; - $item['attributes']['title'] = filter_xss(token_replace($item['attributes']['title'], array('node' => $token_node)), - array('b', 'br', 'code', 'em', 'i', 'img', 'span', 'strong', 'sub', 'sup', 'tt', 'u')); + // 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)); + $item['attributes']['title'] = filter_xss($item['attributes']['title'], array('b', 'br', 'code', 'em', 'i', 'img', 'span', 'strong', 'sub', 'sup', 'tt', 'u')); } // Remove title attribute if it's equal to link text. if (isset($item['attributes']['title']) && $item['attributes']['title'] == $item['title']) { @@ -527,14 +570,46 @@ function _link_sanitize(&$item, $delta, &$field, $instance, &$node) { $item['attributes'] = array_filter($item['attributes']); // Sets title to trimmed url if one exists - // @TODO: Do we need this? It seems not. + // @todo: Obsolete? /*if(!empty($item['display_url']) && empty($item['title'])) { $item['title'] = $item['display_url']; } elseif(!isset($item['title'])) { $item['title'] = $item['url']; }*/ +} +/** + * Because parse_url doesn't work with relative urls. + * + * @param string $url + * URL to parse. + * + * @return Array + * Array of url pieces - only 'url', 'query', and 'fragment'. + */ +function _link_parse_url($url) { + $url_parts = array(); + // Separate out the anchor, if any. + if (strpos($url, '#') !== FALSE) { + $url_parts['fragment'] = substr($url, strpos($url, '#') + 1); + $url = substr($url, 0, strpos($url, '#')); + } + // Separate out the query string, if any. + if (strpos($url, '?') !== FALSE) { + $query = substr($url, strpos($url, '?') + 1); + parse_str($query, $query_array); + // See http://drupal.org/node/1710578 + foreach ($query_array as $key=> &$value) { + if ($value === '' && FALSE === strpos($query, $key . '=')) { + $value = NULL; + } + } + $url_parts['query'] = $query_array; + $url = substr($url, 0, strpos($url, '?')); + } + $url_parts['url'] = $url; + return $url_parts; } /** @@ -542,15 +617,18 @@ function _link_sanitize(&$item, $delta, &$field, $instance, &$node) { */ function link_theme() { return array( - /*'link_field_settings' => array( - 'variables' => array('element' => NULL), - ),*/ 'link_formatter_link_default' => array( 'variables' => array('element' => NULL), ), 'link_formatter_link_plain' => array( 'variables' => array('element' => NULL), ), + 'link_formatter_link_absolute' => array( + 'variables' => array('element' => NULL), + ), + 'link_formatter_link_domain' => array( + 'variables' => array('element' => NULL, 'display' => NULL), + ), 'link_formatter_link_title_plain' => array( 'variables' => array('element' => NULL), ), @@ -561,7 +639,7 @@ function link_theme() { 'variables' => array('element' => NULL), ), 'link_formatter_link_label' => array( - 'variables' => array('element' => NULL), + 'variables' => array('element' => NULL, 'field' => NULL), ), 'link_formatter_link_separate' => array( 'variables' => array('element' => NULL), @@ -573,31 +651,30 @@ function link_theme() { } /** - * FAPI theme for an individual text elements. + * Formats a link field widget. */ function theme_link_field($vars) { - drupal_add_css(drupal_get_path('module', 'link') .'/link.css'); - + drupal_add_css(drupal_get_path('module', 'link') . '/link.css'); $element = $vars['element']; // Prefix single value link fields with the name of the field. if (empty($element['#field']['multiple'])) { if (isset($element['url']) && !isset($element['title'])) { - unset($element['url']['#title']); + $element['url']['#title_display'] = 'invisible'; } } $output = ''; $output .= '<div class="link-field-subrow clearfix">'; if (isset($element['title'])) { - $output .= '<div class="link-field-title link-field-column">'. drupal_render($element['title']) .'</div>'; + $output .= '<div class="link-field-title link-field-column">' . drupal_render($element['title']) . '</div>'; } - $output .= '<div class="link-field-url'. (isset($element['title']) ? ' link-field-column' : '') .'">'. drupal_render($element['url']) .'</div>'; + $output .= '<div class="link-field-url' . (isset($element['title']) ? ' link-field-column' : '') . '">'. drupal_render($element['url']) . '</div>'; $output .= '</div>'; if (!empty($element['attributes']['target'])) { - $output .= '<div class="link-attributes">'. drupal_render($element['attributes']['target']) .'</div>'; + $output .= '<div class="link-attributes">' . drupal_render($element['attributes']['target']) . '</div>'; } if (!empty($element['attributes']['title'])) { - $output .= '<div class="link-attributes">'. drupal_render($element['attributes']['title']) .'</div>'; + $output .= '<div class="link-attributes">' . drupal_render($element['attributes']['title']) . '</div>'; } return $output; } @@ -607,7 +684,7 @@ function theme_link_field($vars) { */ function link_element_info() { $elements = array(); - $elements['link_field'] = array( + $elements['link_field'] = array( '#input' => TRUE, '#process' => array('link_field_process'), '#theme' => 'link_field', @@ -616,6 +693,9 @@ function link_element_info() { return $elements; } +/** + * Returns the default attributes and their values. + */ function _link_default_attributes() { return array( 'target' => LINK_TARGET_DEFAULT, @@ -625,7 +705,7 @@ function _link_default_attributes() { } /** - * Process the link type element before displaying the field. + * Processes the link type element before displaying the field. * * Build the form element. When creating a form using FAPI #process, * note that $element['#value'] is already set. @@ -645,10 +725,10 @@ function link_field_process($element, $form_state, $complete_form) { if ($settings['title'] !== 'none' && $settings['title'] !== 'value') { $element['title'] = array( '#type' => 'textfield', - '#maxlength' => $settings['title_maxlength'], // patch #1307788 from nmc + '#maxlength' => $settings['title_maxlength'], '#title' => t('Title'), - '#description' => t('The link title is limited to '.$settings['title_maxlength'].' characters maximum.'), // patch #1307788 from nmc - '#required' => ($settings['title'] == 'required' && (($element['#delta'] == 0 && $element['#required']) || !empty($element['#value']['url']))) ? TRUE : FALSE, // davereids patch from jan 2011 + '#description' => t('The link title is limited to @maxlength characters maximum.', array('@maxlength' => $settings['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, ); } @@ -678,15 +758,19 @@ function link_field_process($element, $form_state, $complete_form) { ); } - // To prevent an extra required indicator, disable the required flag on the - // base element since all the sub-fields are already required if desired. - $element['#required'] = FALSE; // davereids patch from jan 2011 + // 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 (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. + $element['#required'] = FALSE; + } return $element; } /** - * Implementation of hook_field_formatter_info(). + * Implements hook_field_formatter_info(). */ function link_field_formatter_info() { return array( @@ -710,6 +794,19 @@ function link_field_formatter_info() { 'field types' => array('link_field'), 'multiple values' => FIELD_BEHAVIOR_DEFAULT, ), + 'link_absolute' => array( + 'label' => t('URL, absolute'), + 'field types' => array('link_field'), + 'multiple values' => FIELD_BEHAVIOR_DEFAULT, + ), + 'link_domain' => array( + 'label' => t('Domain, as link'), + 'field types' => array('link_field'), + 'multiple values' => FIELD_BEHAVIOR_DEFAULT, + 'settings' => array( + 'strip_www' => FALSE, + ), + ), 'link_short' => array( 'label' => t('Short, as link with title "Link"'), 'field types' => array('link_field'), @@ -728,6 +825,40 @@ function link_field_formatter_info() { ); } +/** + * Implements hook_field_formatter_settings_form(). + */ +function link_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) { + $display = $instance['display'][$view_mode]; + $settings = $display['settings']; + $element = array(); + if ($display['type'] == 'link_domain') { + $element['strip_www'] = array( + '#title' => t('Strip www. from domain'), + '#type' => 'checkbox', + '#default_value' => $settings['strip_www'], + ); + } + return $element; +} + +/** + * 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'); + } + else { + return t('Leave www. in domain'); + } + } + return ''; +} + /** * Implements hook_field_formatter_view(). */ @@ -735,25 +866,27 @@ function link_field_formatter_view($entity_type, $entity, $field, $instance, $la $elements = array(); foreach ($items as $delta => $item) { $elements[$delta] = array( - '#markup' => theme('link_formatter_'. $display['type'], array('element' => $item, 'field' => $instance)), + '#theme' => 'link_formatter_' . $display['type'], + '#element' => $item, + '#field' => $instance, + '#display' => $display, ); } return $elements; } /** - * Theme function for 'default' text field formatter. + * Formats a link. */ function theme_link_formatter_link_default($vars) { $link_options = $vars['element']; - unset($link_options['element']['title']); - unset($link_options['element']['url']); - - // Issue #1199806 by ss81: Fixes fatal error when the link URl is equal to page URL + unset($link_options['title']); + unset($link_options['url']); + if (isset($link_options['attributes']['class'])) { $link_options['attributes']['class'] = array($link_options['attributes']['class']); } - + // Display a normal link if both title and URL are available. if (!empty($vars['element']['title']) && !empty($vars['element']['url'])) { return l($vars['element']['title'], $vars['element']['url'], $link_options); @@ -768,89 +901,114 @@ function theme_link_formatter_link_default($vars) { } /** - * Theme function for 'plain' text field formatter. + * Formats a link (or its title) as plain text. */ function theme_link_formatter_link_plain($vars) { $link_options = $vars['element']; - unset($link_options['element']['title']); - unset($link_options['element']['url']); - return empty($vars['element']['url']) ? - check_plain($vars['element']['title']) : - url($vars['element']['url'], $link_options); + if (isset($link_options['title'])) { + unset($link_options['title']); + } + else { + $vars['element']['title'] = ''; + } + unset($link_options['url']); + return empty($vars['element']['url']) ? check_plain($vars['element']['title']) : url($vars['element']['url'], $link_options); +} + +/** + * Formats a link as an absolute URL + */ +function theme_link_formatter_link_absolute($vars) { + $absolute = array('absolute' => TRUE); + return empty($vars['element']['url']) ? '' : url($vars['element']['url'], $absolute + $vars['element']); } /** - * Theme function for 'title_plain' text field formatter. + * Formats a link using the URL's domain for it's link text. + */ +function theme_link_formatter_link_domain($vars) { + $link_options = $vars['element']; + unset($link_options['title']); + unset($link_options['url']); + $domain = parse_url($vars['element']['display_url'], PHP_URL_HOST); + if (!empty($vars['display']['settings']['strip_www'])) { + $domain = str_replace('www.', '', $domain); + } + return $vars['element']['url'] ? l($domain, $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']); } /** - * Theme function for 'url' text field formatter. + * Formats a link using an alternate display URL for its link text. */ function theme_link_formatter_link_url($vars) { $link_options = $vars['element']; - unset($link_options['element']['title']); - unset($link_options['element']['url']); + unset($link_options['title']); + unset($link_options['url']); return $vars['element']['url'] ? l($vars['element']['display_url'], $vars['element']['url'], $link_options) : ''; } /** - * Theme function for 'short' text field formatter. + * Formats a link using "Link" as the link text. */ function theme_link_formatter_link_short($vars) { $link_options = $vars['element']; - unset($link_options['element']['title']); - unset($link_options['element']['url']); + unset($link_options['title']); + unset($link_options['url']); return $vars['element']['url'] ? l(t('Link'), $vars['element']['url'], $link_options) : ''; } /** - * Theme function for 'label' text field formatter. + * Formats a link using the field's label as link text. */ function theme_link_formatter_link_label($vars) { $link_options = $vars['element']; - unset($link_options['element']['title']); - unset($link_options['element']['url']); + unset($link_options['title']); + unset($link_options['url']); return $vars['element']['url'] ? l($vars['field']['label'], $vars['element']['url'], $link_options) : ''; } /** - * Theme function for 'separate' text field formatter. + * Formats a link as separate title and URL elements. */ - function theme_link_formatter_link_separate($vars) { - $class = empty($vars['element']['attributes']['class']) ? '' : ' '. $vars['element']['attributes']['class']; + $class = empty($vars['element']['attributes']['class']) ? '' : ' ' . $vars['element']['attributes']['class']; unset($vars['element']['attributes']['class']); $link_options = $vars['element']; - unset($link_options['element']['title']); - unset($link_options['element']['url']); + unset($link_options['title']); + unset($link_options['url']); $title = empty($vars['element']['title']) ? '' : check_plain($vars['element']['title']); - - /** - * @TODO static html markup looks not very elegant to me (who takes it off?) - * needs smarter output solution and an optional title/url seperator (digidog) + + /** + * @TODO static html markup looks not very elegant + * needs smarter output solution and an optional title/url seperator */ + $url_parts = _link_parse_url($vars['element']['url']); $output = ''; - $output .= '<div class="link-item '. $class .'">'; + $output .= '<div class="link-item ' . $class . '">'; if (!empty($title)) { - $output .= '<div class="link-title">'. $title .'</div>'; + $output .= '<div class="link-title">' . $title . '</div>'; } - $output .= '<div class="link-url">'. l($vars['element']['url'], $vars['element']['url'], $link_options) .'</div>'; + $output .= '<div class="link-url">' . l($url_parts['url'], $vars['element']['url'], $link_options) . '</div>'; $output .= '</div>'; return $output; } - +/** + * Implements hook_token_list(). + */ function link_token_list($type = 'all') { if ($type === 'field' || $type === 'all') { $tokens = array(); - $tokens['link']['url'] = t("Link URL"); $tokens['link']['title'] = t("Link title"); $tokens['link']['view'] = t("Formatted html link"); - return $tokens; } } @@ -873,18 +1031,21 @@ function link_token_values($type, $object = NULL) { function link_views_api() { return array( 'api' => 2, - 'path' => drupal_get_path('module', 'link') .'/views', + 'path' => drupal_get_path('module', 'link') . '/views', ); } /** * Forms a valid URL if possible from an entered address. - * Trims whitespace and automatically adds an http:// to addresses without a protocol specified + * + * Trims whitespace and automatically adds an http:// to addresses without a + * protocol specified * * @param string $url - * @param string $protocol The protocol to be prepended to the url if one is not specified + * @param string $protocol + * The protocol to be prepended to the url if one is not specified */ -function link_cleanup_url($url, $protocol = "http") { +function link_cleanup_url($url, $protocol = 'http') { $url = trim($url); $type = link_validate_url($url); @@ -893,9 +1054,10 @@ function link_cleanup_url($url, $protocol = "http") { $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. - $domain_match = preg_match('/^(([a-z0-9]([a-z0-9\-_]*\.)+)('. LINK_DOMAINS .'|[a-z]{2}))/i', $url); + $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; + $url = $protocol . "://" . $url; } } } @@ -904,16 +1066,20 @@ function link_cleanup_url($url, $protocol = "http") { } /** - * A lenient verification for URLs. Accepts all URLs following RFC 1738 standard - * for URL formation and all email addresses following the RFC 2368 standard for - * mailto address formation. + * Validates 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 - * @return mixed Returns boolean FALSE if the URL is not valid. On success, returns an object with - * the following attributes: protocol, hostname, ip, and port. + * + * @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) { - $LINK_ICHARS_DOMAIN = (string) html_entity_decode(implode("", array( // @TODO completing letters ... + // @TODO Complete letters. + $LINK_ICHARS_DOMAIN = (string) html_entity_decode(implode("", array( "æ", // æ "Æ", // Æ "À", // À @@ -948,8 +1114,8 @@ function link_validate_url($text) { "Ö", // Ö "Ô", // Ô "ô", // ô - "Õ", // Õ - "õ", // õ + "Õ", // Õ + "õ", // õ "Œ", // Å’ "œ", // Å“ "ü", // ü @@ -959,7 +1125,7 @@ function link_validate_url($text) { "Û", // Û "û", // û "Ÿ", // Ÿ - "ÿ", // ÿ + "ÿ", // ÿ "Ñ", // Ñ "ñ", // ñ "þ", // þ @@ -973,36 +1139,37 @@ function link_validate_url($text) { "ß", // ß )), 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 - $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}))?)'; + $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}))?)'; $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 .'?'; + $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 ."_\-+\[\]\.]+)$/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. - $end = $directories .'?'. $query .'?'. $anchor .'?'.'$/i'; + $end = $directories . '?' . $query . '?' . $anchor . '?' . '$/i'; - $message_id = '[^@].*@'. $domain; + $message_id = '[^@].*@' . $domain; $newsgroup_name = '(?:[0-9a-z+-]*\.)*[0-9a-z+-]*'; - $news_pattern = '/^news:('. $newsgroup_name .'|'. $message_id .')$/i'; + $news_pattern = '/^news:(' . $newsgroup_name . '|' . $message_id . ')$/i'; - $user = '[a-zA-Z0-9'. $LINK_ICHARS .'_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\'\[\]]+'; - $email_pattern = '/^mailto:'. $user .'@'.'(?:'. $domain .'|'. $ipv4 .'|'. $ipv6 .'|localhost)'. $query .'?$/'; + $user = '[a-zA-Z0-9' . $LINK_ICHARS . '_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\'\[\]]+'; + $email_pattern = '/^mailto:' . $user . '@'.'(?:' . $domain . '|' . $ipv4 . '|' . $ipv6 . '|localhost)' . $query . '?$/'; if (strpos($text, '<front>') === 0) { return LINK_FRONT; @@ -1026,13 +1193,25 @@ function link_validate_url($text) { return FALSE; } +/** + * Returns the list of allowed domains, including domains added by admins via variable_set/$config. + */ +function _link_domains() { + $link_extra_domains = variable_get('link_extra_domains', array()); + return empty($link_extra_domains) ? LINK_DOMAINS : LINK_DOMAINS . '|' . implode('|', $link_extra_domains); +} + /** * Implements hook_migrate_field_alter(). */ function link_content_migrate_field_alter(&$field_value, $instance_value) { if ($field_value['type'] == 'link') { - // need to change the type: + // Adjust the field type. $field_value['type'] = 'link_field'; + // Remove settings that are now on the instance. + foreach (array('attributes', 'display', 'url', 'title', 'title_value', 'enable_tokens', 'validate_url') as $setting) { + unset($field_value['settings'][$setting]); + } } } @@ -1042,9 +1221,23 @@ function link_content_migrate_field_alter(&$field_value, $instance_value) { * Widget type also changed to link_field. */ function link_content_migrate_instance_alter(&$instance_value, $field_value) { - if ($instance_value['widget']['module'] == 'link' - && $instance_value['widget']['type'] == 'link') { - $instance_value['widget']['type'] = 'link_field'; + if ($field_value['type'] == 'link') { + // Grab settings that were previously on the field. + foreach (array('attributes', 'display', 'url', 'title', 'title_value', 'enable_tokens', 'validate_url') as $setting) { + if (isset($field_value['settings'][$setting])) { + $instance_value['settings'][$setting] = $field_value['settings'][$setting]; + } + } + // Adjust widget type. + if ($instance_value['widget']['type'] == 'link') { + $instance_value['widget']['type'] = 'link_field'; + } + // Adjust formatter types. + foreach ($instance_value['display'] as $context => $settings) { + if (in_array($settings['type'], array('default', 'title_plain', 'url', 'plain', 'short', 'label', 'separate'))) { + $instance_value['display'][$context]['type'] = 'link_' . $settings['type']; + } + } } } @@ -1057,6 +1250,7 @@ function link_field_settings_form() { /** * Additional callback to adapt the property info of link fields. + * * @see entity_metadata_field_entity_property_info(). */ function link_field_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) { @@ -1067,7 +1261,7 @@ function link_field_property_info_callback(&$info, $entity_type, $field, $instan $property['setter callback'] = 'entity_metadata_field_verbatim_set'; // Auto-create the field item as soon as a property is set. - $property['auto creation'] = 'link_field_item_create'; + $property['auto creation'] = 'link_field_item_create'; $property['property info'] = link_field_item_property_info(); $property['property info']['url']['required'] = !$instance['settings']['url']; @@ -1075,7 +1269,6 @@ function link_field_property_info_callback(&$info, $entity_type, $field, $instan if ($instance['settings']['title'] == 'none') { unset($property['property info']['title']); } - unset($property['query callback']); } @@ -1104,3 +1297,24 @@ function link_field_item_property_info() { ); return $properties; } + +/** + * Implements hook_field_update_instance(). + */ +function link_field_update_instance($instance, $prior_instance) { + if (function_exists('i18n_string_update') && $prior_instance['settings']['title_value'] != $instance['settings']['title_value']) { + $i18n_string_name = "field:{$instance['field_name']}:{$instance['bundle']}:title_value"; + i18n_string_update($i18n_string_name, $instance['settings']['title_value']); + } +} + +/** + * Implements hook_i18n_string_list_TEXTGROUP_alter(). + */ +function link_i18n_string_list_field_alter(&$strings, $type = NULL, $object = NULL) { + if ($type == 'field_instance' && $object && $object['widget']['type'] == 'link_field') { + if (isset($object['settings']['title_value'])) { + $strings['field'][$object['field_name']][$object['bundle']]['title_value']['string'] = $object['settings']['title_value']; + } + } +} diff --git a/sites/all/modules/link/tests/link.attribute.test b/sites/all/modules/link/tests/link.attribute.test index 398713a04e640c42fe167c5881ab26ccfb31c230..97ac2a478fc77a6a9aecc08a4606f65c1c925884 100644 --- a/sites/all/modules/link/tests/link.attribute.test +++ b/sites/all/modules/link/tests/link.attribute.test @@ -6,10 +6,9 @@ */ class LinkAttributeCrudTest extends DrupalWebTestCase { - private $zebra; - public $permissions = array( + protected $permissions = array( 'access content', 'administer content types', 'administer nodes', @@ -29,15 +28,14 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { } function setup() { + parent::setup('field_ui', 'link'); $this->zebra = 0; - parent::setup('field_ui', 'link'); // was 'views' - //$this->loginWithPermissions($this->permissions); // Create and login user. - $account = $this->drupalCreateUser(array('administer content types')); - $this->drupalLogin($account); + $this->web_user = $this->drupalCreateUser(array('administer content types')); + $this->drupalLogin($this->web_user); } - function createLink($url, $title, $attributes = array()) { + protected function createLink($url, $title, $attributes = array()) { return array( 'url' => $url, 'title' => $title, @@ -45,7 +43,7 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { ); } - private function assertLinkOnNode($field_name, $link_value, $message = '', $group = 'Other') { + protected function assertLinkOnNode($field_name, $link_value, $message = '', $group = 'Other') { $this->zebra++; $zebra_string = ($this->zebra % 2 == 0) ? 'even' : 'odd'; $cssFieldLocator = 'field-'. str_replace('_', '-', $field_name); @@ -59,9 +57,6 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { * that the node is being displayed. */ function testBasic() { - /*$this->acquireContentTypes(1); - variable_set('node_options_'. $this->content_types[0]->name, array('status', 'promote'));*/ - $content_type_friendly = $this->randomName(20); $content_type_machine = strtolower($this->randomName(10)); $title = $this->randomName(20); @@ -113,8 +108,8 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { // Now that we have a new content type, create a user that has privileges // on the content type. $permissions = array_merge($this->permissions, array($permission)); - $account = $this->drupalCreateUser($permissions); - $this->drupalLogin($account); + $this->web_user = $this->drupalCreateUser($permissions); + $this->drupalLogin($this->web_user); // Go to page. $this->drupalGet('node/add/'. $content_type_machine); @@ -129,33 +124,9 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { $this->drupalPost(NULL, $edit, t('Save')); $this->assertText(t('@content_type_friendly @title has been created', array('@content_type_friendly' => $content_type_friendly, '@title' => $title))); - /*$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. - ); - - $field = $this->createField($field_settings, 0); - //$this->pass('<pre>'. print_r($field, TRUE) .'</pre>'); - $field_db_info = content_database_info($field);*/ - - //$this->acquireNodes(2); - /*$node = $this->drupalCreateNode(array('type' => $content_type_machine, - 'promote' => 1)); - $test_nid = $node->nid;*/ - - //$node = node_load($this->nodes[0]->nid); - //$node->promote = 1; // We want this to show on front page for the teaser test. - /*$this->assert('debug', print_r($node, TRUE), 'Debug'); - $node->{$single_field_name}['und'][0] = $this->createLink('http://www.example.com', 'Test Link'); - node_save($node); - $this->assert('debug', print_r($node, TRUE), 'Debug');*/ - - //$this->drupalGet('node/'. $test_nid .'/edit'); $this->drupalGet('node/add/'. $content_type_machine); - // lets add a node: + // Create a node: $edit = array( 'title' => $title, 'field_' . $single_field_name_machine . '[und][0][url]' => 'http://www.example.com/', @@ -167,25 +138,11 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { $this->assertText(t('@content_type_friendly @title has been created', array('@content_type_friendly' => $content_type_friendly, '@title' => $title))); $this->assertText('Display'); - //$this->assertText('http://www.example.com/'); $this->assertLinkByHref('http://www.example.com'); } - private function createNodeType($content_type_machine, $content_type_friendly) { - $this->drupalGet('admin/structure/types'); - - // Create the content type. - $this->clickLink(t('Add content type')); - - $edit = array ( - 'name' => $content_type_friendly, - 'type' => $content_type_machine, - ); - $this->drupalPost(NULL, $edit, t('Save and add fields')); - $this->assertText(t('The content type @name has been added.', array('@name' => $content_type_friendly))); - } - - private function createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine) { + protected function createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine) { + $this->drupalGet('admin/structure/types/manage/' . $content_type_machine . '/fields'); $edit = array ( 'fields[_add_new_field][label]' => $single_field_name_friendly, 'fields[_add_new_field][field_name]' => $single_field_name_machine, @@ -209,7 +166,7 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { $this->assertTrue($type_exists, 'The new content type has been created in the database.'); } - function createNodeTypeUser($content_type_machine) { + protected function createNodeTypeUser($content_type_machine) { $permission = 'create ' . $content_type_machine . ' content'; $permission_edit = 'edit ' . $content_type_machine . ' content'; // Reset the permissions cache. @@ -218,12 +175,11 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { // Now that we have a new content type, create a user that has privileges // on the content type. $permissions = array_merge($this->permissions, array($permission)); - $account = $this->drupalCreateUser($permissions); - $this->drupalLogin($account); + $this->web_user = $this->drupalCreateUser($permissions); + $this->drupalLogin($this->web_user); } - function createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $title, $url, $node_title = '') { - // Go to page. + protected function createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $title, $url, $node_title = '') { $this->drupalGet('node/add/'. $content_type_machine); if (!$node_title) { @@ -239,48 +195,22 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { $edit['field_' . $single_field_name_machine . '[und][0][title]'] = $title; } - // Now we can fill in the second item in the multivalue field and save. $this->drupalPost(NULL, $edit, t('Save')); $this->assertText(t('@content_type_friendly @title has been created', array('@content_type_friendly' => $content_type_friendly, '@title' => $node_title))); } + /** + * Test the link_plain formatter and it's output. + */ function testFormatterPlain() { $content_type_friendly = $this->randomName(20); $content_type_machine = strtolower($this->randomName(10)); - $this->createNodeType($content_type_machine, $content_type_friendly); - - // Now add a singleton field. - $single_field_name_friendly = $this->randomName(20); - $single_field_name_machine = strtolower($this->randomName(10)); - //$single_field_name = 'field_'. $single_field_name_machine; - $this->createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine); - - // Okay, now we want to make sure this display is changed: - $this->drupalGet('admin/structure/types/manage/'. $content_type_machine .'/display'); - $edit = array( - 'fields[field_'. $single_field_name_machine .'][label]' => 'above', - 'fields[field_'. $single_field_name_machine .'][type]' => 'link_plain', - ); - $this->drupalPost(NULL, $edit, t('Save')); - - $this->createNodeTypeUser($content_type_machine); - - $link_text = 'Display'; - $link_url = 'http://www.example.com/'; - $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - - $this->assertText($link_url); - $this->assertNoText($link_text); - $this->assertNoLinkByHref($link_url); - } - - function testFormatterPlainWithQuerystring() { - $content_type_friendly = $this->randomName(20); - $content_type_machine = strtolower($this->randomName(10)); - - $this->createNodeType($content_type_machine, $content_type_friendly); + $this->drupalCreateContentType(array( + 'type' => $content_type_machine, + 'name' => $content_type_friendly, + )); // Now add a singleton field. $single_field_name_friendly = $this->randomName(20); @@ -297,82 +227,41 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { $this->drupalPost(NULL, $edit, t('Save')); $this->createNodeTypeUser($content_type_machine); - - $link_text = 'Display'; - $link_url = 'http://www.example.com/?q=test'; - $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - - $this->assertText($link_url); - $this->assertNoText($link_text); - $this->assertNoLinkByHref($link_url); - } - - function testFormatterPlainWithFragment() { - $content_type_friendly = $this->randomName(20); - $content_type_machine = strtolower($this->randomName(10)); - - $this->createNodeType($content_type_machine, $content_type_friendly); - - // Now add a singleton field. - $single_field_name_friendly = $this->randomName(20); - $single_field_name_machine = strtolower($this->randomName(10)); - //$single_field_name = 'field_'. $single_field_name_machine; - $this->createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine); - - // Okay, now we want to make sure this display is changed: - $this->drupalGet('admin/structure/types/manage/'. $content_type_machine .'/display'); - $edit = array( - 'fields[field_'. $single_field_name_machine .'][label]' => 'above', - 'fields[field_'. $single_field_name_machine .'][type]' => 'link_plain', + + $link_tests = array( + 'plain' => array( + 'text' => 'Display', + 'url' => 'http://www.example.com/', + ), + 'query' => array( + 'text' => 'Display', + 'url' => 'http://www.example.com/?q=test', + ), + 'fragment' => array( + 'text' => 'Display', + 'url' => 'http://www.example.com/#test', + ), ); - $this->drupalPost(NULL, $edit, t('Save')); - - $this->createNodeTypeUser($content_type_machine); - - $link_text = 'Display'; - $link_url = 'http://www.example.com/#test'; - $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - $this->assertText($link_url); - $this->assertNoText($link_text); - $this->assertNoLinkByHref($link_url); + foreach ($link_tests as $key => $link_test) { + $link_text = $link_test['text']; + $link_url = $link_test['url']; + $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); + + $this->assertText($link_url); + $this->assertNoText($link_text); + $this->assertNoLinkByHref($link_url); + } } function testFormatterURL() { $content_type_friendly = $this->randomName(20); $content_type_machine = strtolower($this->randomName(10)); - $this->createNodeType($content_type_machine, $content_type_friendly); - - // Now add a singleton field. - $single_field_name_friendly = $this->randomName(20); - $single_field_name_machine = strtolower($this->randomName(10)); - //$single_field_name = 'field_'. $single_field_name_machine; - $this->createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine); - - // Okay, now we want to make sure this display is changed: - $this->drupalGet('admin/structure/types/manage/'. $content_type_machine .'/display'); - $edit = array( - 'fields[field_'. $single_field_name_machine .'][label]' => 'above', - 'fields[field_'. $single_field_name_machine .'][type]' => 'link_url', - ); - $this->drupalPost(NULL, $edit, t('Save')); - - $this->createNodeTypeUser($content_type_machine); - - $link_text = 'Display'; - $link_url = 'http://www.example.com/'; - $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - - $this->assertNoText($link_text); - $this->assertLinkByHref($link_url); - } - - function testFormatterURLWithQuerystring() { - $content_type_friendly = $this->randomName(20); - $content_type_machine = strtolower($this->randomName(10)); - - $this->createNodeType($content_type_machine, $content_type_friendly); + $this->drupalCreateContentType(array( + 'type' => $content_type_machine, + 'name' => $content_type_friendly, + )); // Now add a singleton field. $single_field_name_friendly = $this->randomName(20); @@ -389,81 +278,40 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { $this->drupalPost(NULL, $edit, t('Save')); $this->createNodeTypeUser($content_type_machine); - - $link_text = 'Display'; - $link_url = 'http://www.example.com/?q=test'; - $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - - $this->assertNoText($link_text); - $this->assertLinkByHref($link_url); - } - - function testFormatterURLWithAnchor() { - $content_type_friendly = $this->randomName(20); - $content_type_machine = strtolower($this->randomName(10)); - - $this->createNodeType($content_type_machine, $content_type_friendly); - - // Now add a singleton field. - $single_field_name_friendly = $this->randomName(20); - $single_field_name_machine = strtolower($this->randomName(10)); - //$single_field_name = 'field_'. $single_field_name_machine; - $this->createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine); - - // Okay, now we want to make sure this display is changed: - $this->drupalGet('admin/structure/types/manage/'. $content_type_machine .'/display'); - $edit = array( - 'fields[field_'. $single_field_name_machine .'][label]' => 'above', - 'fields[field_'. $single_field_name_machine .'][type]' => 'link_url', + + $link_tests = array( + 'plain' => array( + 'text' => 'Display', + 'url' => 'http://www.example.com/', + ), + 'query' => array( + 'text' => 'Display', + 'url' => 'http://www.example.com/?q=test', + ), + 'fragment' => array( + 'text' => 'Display', + 'url' => 'http://www.example.com/#test', + ), ); - $this->drupalPost(NULL, $edit, t('Save')); - - $this->createNodeTypeUser($content_type_machine); - $link_text = 'Display'; - $link_url = 'http://www.example.com/#test'; - $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - - $this->assertNoText($link_text); - $this->assertLinkByHref($link_url); + foreach ($link_tests as $key => $link_test) { + $link_text = $link_test['text']; + $link_url = $link_test['url']; + $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); + + $this->assertNoText($link_text); + $this->assertLinkByHref($link_url); + } } function testFormatterShort() { $content_type_friendly = $this->randomName(20); $content_type_machine = strtolower($this->randomName(10)); - $this->createNodeType($content_type_machine, $content_type_friendly); - - // Now add a singleton field. - $single_field_name_friendly = $this->randomName(20); - $single_field_name_machine = strtolower($this->randomName(10)); - //$single_field_name = 'field_'. $single_field_name_machine; - $this->createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine); - - // Okay, now we want to make sure this display is changed: - $this->drupalGet('admin/structure/types/manage/'. $content_type_machine .'/display'); - $edit = array( - 'fields[field_'. $single_field_name_machine .'][label]' => 'above', - 'fields[field_'. $single_field_name_machine .'][type]' => 'link_short', - ); - $this->drupalPost(NULL, $edit, t('Save')); - - $this->createNodeTypeUser($content_type_machine); - - $link_text = 'Display'; - $link_url = 'http://www.example.com/'; - $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - - $this->assertText('Link'); - $this->assertNoText($link_text); - $this->assertLinkByHref($link_url); - } - - function testFormatterShortWithQuerystring() { - $content_type_friendly = $this->randomName(20); - $content_type_machine = strtolower($this->randomName(10)); - - $this->createNodeType($content_type_machine, $content_type_friendly); + $this->drupalCreateContentType(array( + 'type' => $content_type_machine, + 'name' => $content_type_friendly, + )); // Now add a singleton field. $single_field_name_friendly = $this->randomName(20); @@ -481,82 +329,40 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { $this->createNodeTypeUser($content_type_machine); - $link_text = 'Display'; - $link_url = 'http://www.example.com/?q=test'; - $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - - $this->assertText('Link'); - $this->assertNoText($link_text); - $this->assertLinkByHref($link_url); - } - - function testFormatterShortWithFragment() { - $content_type_friendly = $this->randomName(20); - $content_type_machine = strtolower($this->randomName(10)); - - $this->createNodeType($content_type_machine, $content_type_friendly); - - // Now add a singleton field. - $single_field_name_friendly = $this->randomName(20); - $single_field_name_machine = strtolower($this->randomName(10)); - //$single_field_name = 'field_'. $single_field_name_machine; - $this->createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine); - - // Okay, now we want to make sure this display is changed: - $this->drupalGet('admin/structure/types/manage/'. $content_type_machine .'/display'); - $edit = array( - 'fields[field_'. $single_field_name_machine .'][label]' => 'above', - 'fields[field_'. $single_field_name_machine .'][type]' => 'link_short', + $link_tests = array( + 'plain' => array( + 'text' => 'Display', + 'url' => 'http://www.example.com/', + ), + 'query' => array( + 'text' => 'Display', + 'url' => 'http://www.example.com/?q=test', + ), + 'fragment' => array( + 'text' => 'Display', + 'url' => 'http://www.example.com/#test', + ), ); - $this->drupalPost(NULL, $edit, t('Save')); - - $this->createNodeTypeUser($content_type_machine); - $link_text = 'Display'; - $link_url = 'http://www.example.com/#test'; - $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - - $this->assertText('Link'); - $this->assertNoText($link_text); - $this->assertLinkByHref($link_url); + foreach ($link_tests as $key => $link_test) { + $link_text = $link_test['text']; + $link_url = $link_test['url']; + $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); + + $this->assertText('Link'); + $this->assertNoText($link_text); + $this->assertLinkByHref($link_url); + } } function testFormatterLabel() { $content_type_friendly = $this->randomName(20); $content_type_machine = strtolower($this->randomName(10)); - $this->createNodeType($content_type_machine, $content_type_friendly); - - // Now add a singleton field. - $single_field_name_friendly = $this->randomName(20); - $single_field_name_machine = strtolower($this->randomName(10)); - //$single_field_name = 'field_'. $single_field_name_machine; - $this->createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine); - - // Okay, now we want to make sure this display is changed: - $this->drupalGet('admin/structure/types/manage/'. $content_type_machine .'/display'); - $edit = array( - 'fields[field_'. $single_field_name_machine .'][label]' => 'above', - 'fields[field_'. $single_field_name_machine .'][type]' => 'link_label', - ); - $this->drupalPost(NULL, $edit, t('Save')); - - $this->createNodeTypeUser($content_type_machine); - - $link_text = 'Display'; - $link_url = 'http://www.example.com/'; - $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - - $this->assertNoText($link_text); - $this->assertText($single_field_name_friendly); - $this->assertLinkByHref($link_url); - } - - function testFormatterLabelWithQuerystring() { - $content_type_friendly = $this->randomName(20); - $content_type_machine = strtolower($this->randomName(10)); - - $this->createNodeType($content_type_machine, $content_type_friendly); + $this->drupalCreateContentType(array( + 'type' => $content_type_machine, + 'name' => $content_type_friendly, + )); // Now add a singleton field. $single_field_name_friendly = $this->randomName(20); @@ -574,82 +380,40 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { $this->createNodeTypeUser($content_type_machine); - $link_text = 'Display'; - $link_url = 'http://www.example.com/?q=test'; - $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - - $this->assertNoText($link_text); - $this->assertText($single_field_name_friendly); - $this->assertLinkByHref($link_url); - } - - function testFormatterLabelWithFragment() { - $content_type_friendly = $this->randomName(20); - $content_type_machine = strtolower($this->randomName(10)); - - $this->createNodeType($content_type_machine, $content_type_friendly); - - // Now add a singleton field. - $single_field_name_friendly = $this->randomName(20); - $single_field_name_machine = strtolower($this->randomName(10)); - //$single_field_name = 'field_'. $single_field_name_machine; - $this->createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine); - - // Okay, now we want to make sure this display is changed: - $this->drupalGet('admin/structure/types/manage/'. $content_type_machine .'/display'); - $edit = array( - 'fields[field_'. $single_field_name_machine .'][label]' => 'above', - 'fields[field_'. $single_field_name_machine .'][type]' => 'link_label', + $link_tests = array( + 'plain' => array( + 'text' => 'Display', + 'url' => 'http://www.example.com/', + ), + 'query' => array( + 'text' => 'Display', + 'url' => 'http://www.example.com/?q=test', + ), + 'fragment' => array( + 'text' => 'Display', + 'url' => 'http://www.example.com/#test', + ), ); - $this->drupalPost(NULL, $edit, t('Save')); - $this->createNodeTypeUser($content_type_machine); - - $link_text = 'Display'; - $link_url = 'http://www.example.com/#test'; - $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - - $this->assertNoText($link_text); - $this->assertText($single_field_name_friendly); - $this->assertLinkByHref($link_url); + foreach ($link_tests as $key => $link_test) { + $link_text = $link_test['text']; + $link_url = $link_test['url']; + $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); + + $this->assertNoText($link_text); + $this->assertText($single_field_name_friendly); + $this->assertLinkByHref($link_url); + } } function testFormatterSeparate() { $content_type_friendly = $this->randomName(20); $content_type_machine = strtolower($this->randomName(10)); - $this->createNodeType($content_type_machine, $content_type_friendly); - - // Now add a singleton field. - $single_field_name_friendly = $this->randomName(20); - $single_field_name_machine = strtolower($this->randomName(10)); - //$single_field_name = 'field_'. $single_field_name_machine; - $this->createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine); - - // Okay, now we want to make sure this display is changed: - $this->drupalGet('admin/structure/types/manage/'. $content_type_machine .'/display'); - $edit = array( - 'fields[field_'. $single_field_name_machine .'][label]' => 'above', - 'fields[field_'. $single_field_name_machine .'][type]' => 'link_separate', - ); - $this->drupalPost(NULL, $edit, t('Save')); - - $this->createNodeTypeUser($content_type_machine); - - $link_text = $this->randomName(20); - $link_url = 'http://www.example.com/'; - $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - - $this->assertText($link_text); - $this->assertLink($link_url); - $this->assertLinkByHref($link_url); - } - - function testFormatterSeparateWithQuerystring() { - $content_type_friendly = $this->randomName(20); - $content_type_machine = strtolower($this->randomName(10)); - - $this->createNodeType($content_type_machine, $content_type_friendly); + $this->drupalCreateContentType(array( + 'type' => $content_type_machine, + 'name' => $content_type_friendly, + )); // Now add a singleton field. $single_field_name_friendly = $this->randomName(20); @@ -667,51 +431,41 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { $this->createNodeTypeUser($content_type_machine); - $link_text = $this->randomName(20); - $link_url = 'http://www.example.com/?q=test'; - $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - - $this->assertText($link_text); - $this->assertLink('http://www.example.com/'); - $this->assertLinkByHref($link_url); - } - - function testFormatterSeparateWithFragment() { - $content_type_friendly = $this->randomName(20); - $content_type_machine = strtolower($this->randomName(10)); - - $this->createNodeType($content_type_machine, $content_type_friendly); - - // Now add a singleton field. - $single_field_name_friendly = $this->randomName(20); - $single_field_name_machine = strtolower($this->randomName(10)); - //$single_field_name = 'field_'. $single_field_name_machine; - $this->createSimpleLinkField($single_field_name_machine, $single_field_name_friendly, $content_type_machine); - - // Okay, now we want to make sure this display is changed: - $this->drupalGet('admin/structure/types/manage/'. $content_type_machine .'/display'); - $edit = array( - 'fields[field_'. $single_field_name_machine .'][label]' => 'above', - 'fields[field_'. $single_field_name_machine .'][type]' => 'link_separate', + $plain_url = 'http://www.example.com/'; + $link_tests = array( + 'plain' => array( + 'text' => $this->randomName(20), + 'url' => $plain_url, + ), + 'query' => array( + 'text' => $this->randomName(20), + 'url' => $plain_url . '?q=test', + ), + 'fragment' => array( + 'text' => $this->randomName(20), + 'url' => $plain_url . '#test', + ), ); - $this->drupalPost(NULL, $edit, t('Save')); - $this->createNodeTypeUser($content_type_machine); - - $link_text = $this->randomName(20); - $link_url = 'http://www.example.com/#test'; - $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); - - $this->assertText($link_text); - $this->assertLink('http://www.example.com/'); - $this->assertLinkByHref($link_url); + foreach ($link_tests as $key => $link_test) { + $link_text = $link_test['text']; + $link_url = $link_test['url']; + $this->createNodeForTesting($content_type_machine, $content_type_friendly, $single_field_name_machine, $link_text, $link_url); + + $this->assertText($link_text); + $this->assertLink($plain_url); + $this->assertLinkByHref($link_url); + } } function testFormatterPlainTitle() { $content_type_friendly = $this->randomName(20); $content_type_machine = strtolower($this->randomName(10)); - $this->createNodeType($content_type_machine, $content_type_friendly); + $this->drupalCreateContentType(array( + 'type' => $content_type_machine, + 'name' => $content_type_friendly, + )); // Now add a singleton field. $single_field_name_friendly = $this->randomName(20); @@ -737,45 +491,4 @@ class LinkAttributeCrudTest extends DrupalWebTestCase { $this->assertNoText($link_url); $this->assertNoLinkByHref($link_url); } - - /** - * This test sees that we can create a link field with a defined class, and make sure - * that class displays properly when the link is displayed. - */ - /*function testLinkWithClassOnField() { - $this->acquireContentTypes(1); - $field_settings = array( - 'type' => 'link', - 'widget_type' => 'link', - 'type_name' => $this->content_types[0]->name, - 'attributes' => array( - 'class' => 'test-class', - 'target' => 'default', - 'rel' => FALSE, - ), - ); - - $field = $this->createField($field_settings, 0); - //$this->pass('<pre>'. print_r($field, TRUE) .'</pre>'); - $field_db_info = content_database_info($field); - - $this->acquireNodes(2); - - $node = node_load($this->nodes[0]->nid); - $node->promote = 1; // We want this to show on front page for the teaser test. - $node->{$field['field_name']}[0] = $this->createLink('http://www.example.com', 'Test Link'); - node_save($node); - - // Does this display on the node page? - $this->drupalGet('node/'. $this->nodes[0]->nid); - //$this->outputScreenContents('Link field with class', 'link_'); - $this->assertLinkOnNode($field['field_name'], l('Test Link', 'http://www.example.com', array('attributes' => array('class' => 'test-class')))); - - // Does this display on the front page? - $this->drupalGet('<front>'); - // reset the zebra! - $this->zebra = 0; - $this->assertLinkOnNode($field['field_name'], l('Test Link', 'http://www.example.com', array('attributes' => array('class' => 'test-class')))); - }*/ - } diff --git a/sites/all/modules/link/tests/link.crud.test b/sites/all/modules/link/tests/link.crud.test index cdd7fc5aec4fb7ab27f96a8de19d5ec104cd97cf..54889fd298b504c8203e9de346a8196262df3f7e 100644 --- a/sites/all/modules/link/tests/link.crud.test +++ b/sites/all/modules/link/tests/link.crud.test @@ -16,8 +16,7 @@ class LinkContentCrudTest extends DrupalWebTestCase { } function setUp() { - parent::setUp('field_ui', 'link'); // was views? - //$this->loginWithPermissions(); + parent::setUp('field_ui', 'link'); } /** @@ -30,15 +29,14 @@ class LinkContentCrudTest extends DrupalWebTestCase { $title = $this->randomName(20); // Create and login user. - $account = $this->drupalCreateUser(array('administer content types')); - $this->drupalLogin($account); + $this->web_user = $this->drupalCreateUser(array('administer content types')); + $this->drupalLogin($this->web_user); $this->drupalGet('admin/structure/types'); // Create the content type. $this->clickLink(t('Add content type')); - $edit = array ( 'name' => $content_type_friendly, 'type' => $content_type_machine, @@ -55,7 +53,6 @@ class LinkContentCrudTest extends DrupalWebTestCase { 'fields[_add_new_field][field_name]' => $single_field_name_machine, 'fields[_add_new_field][type]' => 'link_field', 'fields[_add_new_field][widget_type]' => 'link_field', - ); $this->drupalPost(NULL, $edit, t('Save')); diff --git a/sites/all/modules/link/tests/link.crud_browser.test b/sites/all/modules/link/tests/link.crud_browser.test index 7b01da0ff75e2e2c1d8ba06a0fc5f416dbcf6491..95406018089a663e5584f8a93e6358e7caf7d0c9 100644 --- a/sites/all/modules/link/tests/link.crud_browser.test +++ b/sites/all/modules/link/tests/link.crud_browser.test @@ -42,13 +42,15 @@ class LinkUITest extends DrupalWebTestcase { */ function testLinkCreate() { //libxml_use_internal_errors(true); - $account = $this->drupalCreateUser(array('administer content types', - 'administer nodes', - 'administer filters', - 'access content', - 'create page content', - 'access administration pages')); - $this->drupalLogin($account); + $this->web_user = $this->drupalCreateUser(array( + 'administer content types', + 'administer nodes', + 'administer filters', + 'access content', + 'create page content', + 'access administration pages' + )); + $this->drupalLogin($this->web_user); // create field $name = strtolower($this->randomName()); @@ -125,7 +127,7 @@ class LinkUITest extends DrupalWebTestcase { $input_test_cases[] = $test_case; foreach ($input_test_cases as $input) { - $this->drupalLogin($account); + $this->drupalLogin($this->web_user); $this->drupalGet('node/add/page'); $edit = array( @@ -135,7 +137,7 @@ class LinkUITest extends DrupalWebTestcase { ); $this->drupalPost(NULL, $edit, t('Save')); if ($input['type'] == self::LINK_INPUT_TYPE_BAD_URL) { - $this->assertRaw(t('Not a valid URL.'), 'Not a valid URL: ' . $input['href']); + $this->assertRaw(t('The value provided for %field is not a valid URL.', array('%field' => $name)), 'Not a valid URL: ' . $input['href']); continue; } else { @@ -167,122 +169,13 @@ class LinkUITest extends DrupalWebTestcase { //libxml_use_internal_errors(FALSE); } - /** - * Creates a link field for the "page" type and creates a page with a link. - * Just like the above test, only here we're turning off the validation on the field. - */ - /*function testLinkCreate_NoValidation() { - //libxml_use_internal_errors(true); - $account = $this->drupalCreateUser(array('administer content types', 'access content', 'create page content')); - $this->drupalLogin($account); - - // create field - $name = strtolower($this->randomName()); - $edit = array( - 'fields[_add_new_field][label]' => $name, - 'fields[_add_new_field][field_name]' => $name, - 'fields[_add_new_field][type]' => 'link_field', - 'fields[_add_new_field][widget_type]' => 'link_field', - ); - $this->drupalPost('admin/structure/types/manage/page/fields', $edit, t('Save')); - $this->drupalPost(NULL, array(), t('Save field settings')); - $this->drupalPost(NULL, array('validate_url' => FALSE), t('Save settings')); - - // Is field created? - $this->assertRaw(t('Saved %label configuration', array('%label' => $name)), 'Field added'); - _content_type_info(TRUE); - $fields = content_fields(); - $this->assertTRUE(0 === $fields['field_'. $name]['validate_url'], 'Make sure validation is off.'); - - // 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_test_cases = array( - array( - 'href' => 'http://example.com/' . $this->randomName(), - 'label' => $this->randomName(), - 'msg' => 'Link found', - 'type' => self::LINK_INPUT_TYPE_GOOD - ), - array( - 'href' => 'http://example.com/' . $this->randomName(), - 'label' => $this->randomName() . '<script>alert("hi");</script>', - 'msg' => 'js label', - 'type' => self::LINK_INPUT_TYPE_BAD_TITLE - ), - array( - 'href' => 'http://example.com/' . $this->randomName(), - 'label' => $this->randomName() . '<script src="http://devil.site.com"></script>', - 'msg' => 'js label', - 'type' => self::LINK_INPUT_TYPE_BAD_TITLE - ), - array( - 'href' => 'http://example.com/' . $this->randomName(), - 'label' => $this->randomName() . '" onmouseover="alert(\'hi\')', - 'msg' => 'js label', - 'type' => self::LINK_INPUT_TYPE_BAD_TITLE - ), - array( - 'href' => 'http://example.com/' . $this->randomName(), - 'label' => $this->randomName() . '\' onmouseover="alert(\'hi\')', - 'msg' => 'js label', - 'type' => self::LINK_INPUT_TYPE_BAD_TITLE - ), - array( - 'href' => 'javascript:alert("http://example.com/' . $this->randomName() . '")', - 'label' => $this->randomName(), - 'msg' => 'js url', - 'type' => self::LINK_INPUT_TYPE_BAD_URL - ), - ); - foreach ($input_test_cases as $input) { - $this->drupalLogin($account); - $this->drupalGet('node/add/page'); - - $edit = array( - 'title' => $input['label'], - $field_name . '[0][title]' => $input['label'], - $field_name . '[0][url]' => $input['href'], - ); - $this->drupalPost(NULL, $edit, t('Save')); - if ($input['type'] == self::LINK_INPUT_TYPE_BAD_URL) { - //$this->assertRaw(t('Not a valid URL.'), 'Not a valid URL: ' . $input['href']); - $this->assertNoRaw($input['href']); - $this->assertRaw(t('@type %title has been created.', array('@type' => 'Basic Page', '%title' => $edit['title'])), 'Page created: ' . $input['href']); - continue; - } - else { - $this->assertRaw(t('@type %title has been created.', array('@type' => 'Basic Page', '%title' => $edit['title'])), 'Page created: ' . $input['href']); - } - $url = $this->getUrl(); - - // change to anonym user - $this->drupalLogout(); - - $this->drupalGet($url); - //debug($this); - // If simpletest starts using something to override the error system, this will flag - // us and let us know it's broken. - $this->assertFalse(libxml_use_internal_errors(TRUE)); - $path = '//a[@href="'. $input['href'] .'" and text()="'. $input['label'] .'"]'; - //$this->pass(htmlentities($path)); - $elements = $this->xpath($path); - libxml_use_internal_errors(FALSE); - $this->assertIdentical(isset($elements[0]), $input['type'] == self::LINK_INPUT_TYPE_GOOD, $input['msg']); - } - //libxml_use_internal_errors(FALSE); - }*/ - /** * Testing that if you use <strong> in a static title for your link, that the * title actually displays <strong>. */ function testStaticLinkCreate() { - $account = $this->drupalCreateUser(array('administer content types', 'access content', 'create page content')); - $this->drupalLogin($account); + $this->web_user = $this->drupalCreateUser(array('administer content types', 'access content', 'create page content')); + $this->drupalLogin($this->web_user); // create field $name = strtolower($this->randomName()); @@ -330,8 +223,8 @@ class LinkUITest extends DrupalWebTestcase { * sure they are set to the expected results. */ function testCRUDCreateFieldDefaults() { - $account = $this->drupalCreateUser(array('administer content types', 'access content', 'create page content')); - $this->drupalLogin($account); + $this->web_user = $this->drupalCreateUser(array('administer content types', 'access content', 'create page content')); + $this->drupalLogin($this->web_user); // create field $name = strtolower($this->randomName()); diff --git a/sites/all/modules/link/tests/link.test b/sites/all/modules/link/tests/link.test index dfb4dda337f0162710949d454b3b0bd8db00bf67..538e3a6769859e7c7007421b5839a1225e338b0d 100644 --- a/sites/all/modules/link/tests/link.test +++ b/sites/all/modules/link/tests/link.test @@ -6,7 +6,7 @@ */ class LinkBaseTestClass extends DrupalWebTestCase { - public $permissions = array( + protected $permissions = array( 'access content', 'administer content types', 'administer nodes', @@ -17,21 +17,18 @@ class LinkBaseTestClass extends DrupalWebTestCase { 'create page content', ); - public $account; - - function setUp($modules = array()) { - if ($modules) { - parent::setUp($modules); - } - else { - parent::setUp('field_ui', 'link'); - } - $this->account = $this->drupalCreateUser($this->permissions); - $this->drupalLogin($this->account); + 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); } - function createLinkField($node_type = 'page', - $settings = array()) { + protected function createLinkField($node_type = 'page', $settings = array()) { $name = strtolower($this->randomName()); $edit = array( 'fields[_add_new_field][label]' => $name, diff --git a/sites/all/modules/link/tests/link.token.test b/sites/all/modules/link/tests/link.token.test index d23f46a78e54a675b4c5943e5831a12a97912551..de3ed3fef587b4fd77f7877e03032c5a593ca5e7 100644 --- a/sites/all/modules/link/tests/link.token.test +++ b/sites/all/modules/link/tests/link.token.test @@ -20,10 +20,7 @@ class LinkTokenTest extends LinkBaseTestClass { } function setUp($modules = array()) { - $modules[] = 'field_ui'; - $modules[] = 'link'; - $modules[] = 'token'; - parent::setUp($modules); + parent::setUp(array('token')); } /** @@ -31,9 +28,6 @@ class LinkTokenTest extends LinkBaseTestClass { * Creates a node with a token in the link title and checks the value. */ function testUserTokenLinkCreate() { - /*$account = $this->drupalCreateUser(array('administer content types', 'access content', 'create page content')); - $this->drupalLogin($account);*/ - // create field $settings = array( 'instance[settings][enable_tokens]' => 1, @@ -52,7 +46,7 @@ class LinkTokenTest extends LinkBaseTestClass { 'label' => $this->randomName(), ); - //$this->drupalLogin($account); + //$this->drupalLogin($this->web_user); $this->drupalGet('node/add/page'); $edit = array( @@ -92,7 +86,7 @@ class LinkTokenTest extends LinkBaseTestClass { 'href' => 'http://example.com/' . $this->randomName() ); - //$this->drupalLogin($account); + //$this->drupalLogin($this->web_user); $this->drupalGet('node/add/page'); $edit = array( @@ -135,7 +129,7 @@ class LinkTokenTest extends LinkBaseTestClass { 'href' => 'http://example.com/' . $this->randomName() ); - //$this->drupalLogin($account); + //$this->drupalLogin($this->web_user); $this->drupalGet('node/add/page'); $edit = array( @@ -280,8 +274,8 @@ class LinkTokenTest extends LinkBaseTestClass { * Trying to set the url to contain a token. */ function _testUserTokenLinkCreateInURL() { - $account = $this->drupalCreateUser(array('administer content types', 'access content', 'create page content')); - $this->drupalLogin($account); + $this->web_user = $this->drupalCreateUser(array('administer content types', 'access content', 'create page content')); + $this->drupalLogin($this->web_user); // create field $name = strtolower($this->randomName()); @@ -310,7 +304,7 @@ class LinkTokenTest extends LinkBaseTestClass { 'label' => $this->randomName(), ); - $this->drupalLogin($account); + $this->drupalLogin($this->web_user); $this->drupalGet('node/add/page'); $edit = array( @@ -333,8 +327,8 @@ class LinkTokenTest extends LinkBaseTestClass { * Trying to set the url to contain a token. */ function _testUserTokenLinkCreateInURL2() { - $account = $this->drupalCreateUser(array('administer content types', 'access content', 'create page content')); - $this->drupalLogin($account); + $this->web_user = $this->drupalCreateUser(array('administer content types', 'access content', 'create page content')); + $this->drupalLogin($this->web_user); // create field $name = strtolower($this->randomName()); @@ -363,7 +357,7 @@ class LinkTokenTest extends LinkBaseTestClass { 'label' => $this->randomName(), ); - $this->drupalLogin($account); + $this->drupalLogin($this->web_user); $this->drupalGet('node/add/page'); $edit = array( @@ -378,6 +372,6 @@ class LinkTokenTest extends LinkBaseTestClass { $this->drupalLogout(); $this->drupalGet($url); - $this->assertRaw(l($input['label'], $input['href'] .'/'. $account->uid)); + $this->assertRaw(l($input['label'], $input['href'] .'/'. $this->web_user->uid)); } } diff --git a/sites/all/modules/link/tests/link.validate.test b/sites/all/modules/link/tests/link.validate.test index affa7e94517f2d584b893d6d702083f8865f561f..0f721fbe181ed9c40118878e0d698d2b67ee2074 100644 --- a/sites/all/modules/link/tests/link.validate.test +++ b/sites/all/modules/link/tests/link.validate.test @@ -7,11 +7,7 @@ class LinkValidateTestCase extends LinkBaseTestClass { - function setUp($modules = array()) { - parent::setUp($modules); - } - - function createLink($url, $title, $attributes = array()) { + protected function createLink($url, $title, $attributes = array()) { return array( 'url' => $url, 'title' => $title, @@ -22,7 +18,7 @@ class LinkValidateTestCase extends LinkBaseTestClass { /** * Takes a url, and sees if it can validate that the url is valid. */ - public function link_test_validate_url($url) { + protected function link_test_validate_url($url) { $field_name = $this->createLinkField(); @@ -66,13 +62,13 @@ class LinkValidateTest extends LinkValidateTestCase { * Test if we're stopped from posting a bad url on default validation. */ function test_link_validate_bad_url_validate_default() { - $account = $this->drupalCreateUser(array('administer content types', + $this->web_user = $this->drupalCreateUser(array('administer content types', 'administer nodes', 'administer filters', 'access content', 'create page content', 'access administration pages')); - $this->drupalLogin($account); + $this->drupalLogin($this->web_user); // create field $name = strtolower($this->randomName()); @@ -104,20 +100,20 @@ class LinkValidateTest extends LinkValidateTestCase { ); $this->drupalPost(NULL, $edit, t('Save')); - $this->assertText(t('Not a valid URL.')); + $this->assertText(t('The value provided for @field is not a valid URL.', array('@field' => $name))); } /** * Test if we're stopped from posting a bad url with validation on. */ function test_link_validate_bad_url_validate_on() { - $account = $this->drupalCreateUser(array('administer content types', + $this->web_user = $this->drupalCreateUser(array('administer content types', 'administer nodes', 'administer filters', 'access content', 'create page content', 'access administration pages')); - $this->drupalLogin($account); + $this->drupalLogin($this->web_user); // create field $name = strtolower($this->randomName()); @@ -149,7 +145,7 @@ class LinkValidateTest extends LinkValidateTestCase { ); $this->drupalPost(NULL, $edit, t('Save')); - $this->assertText(t('Not a valid URL.')); + $this->assertText(t('The value provided for @field is not a valid URL.', array('@field' => $name))); } @@ -157,13 +153,13 @@ class LinkValidateTest extends LinkValidateTestCase { * Test if we can post a bad url if the validation is expressly turned off. */ function test_link_validate_bad_url_validate_off() { - $account = $this->drupalCreateUser(array('administer content types', + $this->web_user = $this->drupalCreateUser(array('administer content types', 'administer nodes', 'administer filters', 'access content', 'create page content', 'access administration pages')); - $this->drupalLogin($account); + $this->drupalLogin($this->web_user); // create field $name = strtolower($this->randomName()); @@ -199,8 +195,7 @@ class LinkValidateTest extends LinkValidateTestCase { ); $this->drupalPost(NULL, $edit, t('Save')); - $this->assertNoText(t('Not a valid URL.')); - + $this->assertNoText(t('The value provided for @field is not a valid URL.', array('@field' => $name))); } /** @@ -208,13 +203,13 @@ class LinkValidateTest extends LinkValidateTestCase { */ function x_test_link_validate_switching_between_validation_status() { $this->acquireContentTypes(1); - $account = $this->drupalCreateUser(array('administer content types', + $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->drupalLogin($account); + $this->drupalLogin($this->web_user); variable_set('node_options_'. $this->content_types[0]->name, array('status', 'promote')); $field_settings = array( 'type' => 'link', @@ -242,7 +237,7 @@ class LinkValidateTest extends LinkValidateTestCase { $this->drupalPost('node/'. $this->nodes[0]->nid .'/edit', $edit, t('Save')); //$this->pass($this->content); - $this->assertNoText(t('Not a valid URL.')); + $this->assertNoText(t('The value provided for %field is not a valid URL.', array('%field' => $name))); // Make sure we get a new version! $node = node_load($this->nodes[0]->nid, NULL, TRUE); @@ -361,11 +356,6 @@ class LinkValidateSpecificURL extends LinkValidateTestCase { */ class LinkValidateUrlLight extends DrupalWebTestCase { - //function setUp() { - // do we need to include something here? - //module_load_include('inc', 'link'); - //} - public static function getInfo() { return array( 'name' => 'Link Light Validation Tests', @@ -427,19 +417,18 @@ class LinkValidateUrlLight extends DrupalWebTestCase { $this->assertEqual(FALSE, $valid, 'newsgroup names can\'t contain underscores, so it should come back as invalid.'); } - function testValidateInternalLink() { - $valid = link_validate_url('node/5'); - $this->assertEqual(LINK_INTERNAL, $valid, 'Test normal internal link.'); - } - - function testValidateInternalLinkWithDot() { - $valid = link_validate_url('rss.xml'); - $this->assertEqual(LINK_INTERNAL, $valid, 'Test rss.xml internal link.'); - } - - function testValidateInternalLinkToFile() { - $valid = link_validate_url('files/test.jpg'); - $this->assertEqual(LINK_INTERNAL, $valid, 'Test files/test.jpg internal link.'); + function testValidateInternalLinks() { + $links = array( + 'node/5', + 'rss.xml', + 'files/test.jpg', + '/var/www/test', + ); + + foreach ($links as $link) { + $valid = link_validate_url($link); + $this->assertEqual(LINK_INTERNAL, $valid, 'Test ' . $link . ' internal link.'); + } } function testValidateExternalLinks() { @@ -481,15 +470,20 @@ class LinkValidateUrlLight extends DrupalWebTestCase { //$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() { $links = array( 'http://www.ex ample.com/', - '//www.example.com/', 'http://25.0.0/', // bad ip! '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. ); foreach ($links as $link) { @@ -497,5 +491,4 @@ class LinkValidateUrlLight extends DrupalWebTestCase { $this->assertEqual(FALSE, $valid, 'Testing that '. $link .' is not a valid link.'); } } - } diff --git a/sites/all/modules/link/views/link_views_handler_argument_target.inc b/sites/all/modules/link/views/link_views_handler_argument_target.inc index b0a2a3ee48118e7c18e049f6f7d14beccd9782fc..3a6fdf013b6fefe883402c3da1c159902326cb26 100644 --- a/sites/all/modules/link/views/link_views_handler_argument_target.inc +++ b/sites/all/modules/link/views/link_views_handler_argument_target.inc @@ -93,7 +93,7 @@ class link_views_handler_argument_target extends views_handler_argument { '#default_value' => $this->options['validate_type'], ); - $validate_types = array('none' => t('<Basic validation>')); + $validate_types = array('none' => t('- Basic validation -')); $plugins = views_fetch_plugin_data('argument validator'); foreach ($plugins as $id => $info) { $valid = TRUE; @@ -140,10 +140,10 @@ class link_views_handler_argument_target extends views_handler_argument { * * The argument sent may be found at $this->argument. */ - function query() { + 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))); + $this->query->add_where(0, $this->table_alias . '.' . $this->real_field . " LIKE '%%%s%'", serialize(array('target' => $this->argument))); } } diff --git a/sites/all/modules/link/views/link_views_handler_filter_protocol.inc b/sites/all/modules/link/views/link_views_handler_filter_protocol.inc index f43e345c4b2e5790b33e6cfc83fb5a92ce83cc70..9d194aa94bbc8f9eebf67b8b345bc753ec9a4571 100644 --- a/sites/all/modules/link/views/link_views_handler_filter_protocol.inc +++ b/sites/all/modules/link/views/link_views_handler_filter_protocol.inc @@ -80,27 +80,28 @@ class link_views_handler_filter_protocol extends views_handler_filter_string { $where_conditions = array(); foreach ($protocols as $protocol) { // Simple case, the URL begins with the specified protocol. - $condition = $field .' LIKE \''. $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. if ($protocol == 'http') { + $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. // 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. // 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]))' . '\''; } } $where_conditions[] = $condition; } - $this->query->add_where($this->options['group'], implode(' '. $this->operator .' ', $where_conditions)); + $this->query->add_where($this->options['group'], implode(' ' . $this->operator . ' ', $where_conditions)); } } diff --git a/sites/all/modules/metatag/CHANGELOG.txt b/sites/all/modules/metatag/CHANGELOG.txt new file mode 100644 index 0000000000000000000000000000000000000000..aeba62f52d1bbcf5a469cfb8d308b3c48b29ad3f --- /dev/null +++ b/sites/all/modules/metatag/CHANGELOG.txt @@ -0,0 +1,180 @@ +Metatag 7.x-1.0-beta5, 2013-03-23 +--------------------------------- +#1844638 by DamienMcKenna: Updated help messages around update 7004, when ran + via Drush it will no longer used Batch API. +#1844764 by Devin Carlson, DamienMcKenna: Fix arg placeholders in t() calls. +#1846516 by Staratel: Incorrect arguments for watchdog(). +#1846516 by DamienMcKenna: Further incorrect arguments for watchdog(). +#1844638 by DamienMcKenna: Correctly used drupal_is_cli() instead of just + php_sapi_name(). +#1846978 by edulterado: Corrected the theme function name used with the + Twitter Cards submodule. +#1307804 by juampy: Support for Select_or_Other for use with the OpenGraph + 'type' field. +#1854522 by DamienMcKenna: Redundant return statements in the MetaTag classes. +#1852600 by DamienMcKenna: Only use the first page argument in the Views and + Panels preprocessors if it is numerical. +#1850014 by plopesc: Not all contexts that may be shown on the admin page will + have a path condition defined. +#1846080 by DamienMcKenna: Only support entities that have the 'metatags' + option specifically enabled. +#1857116 by DamienMcKenna: Purge {metatag} records for a few known unsupported + entities that old versions would have saved. +#1857116 by DamienMcKenna: Don't purge 'file' {metatag} records until #1857334 + is decided. +#1857360 by DamienMcKenna: Purge {metatag} records for nodes, taxonomy terms + and users that were purged but where the APIs of older versions failed to + remove them. +#1857116 by DamienMcKenna: Purge {metatag} records for Profile2. +#1852600 by helmo: Typo in Views integration function. +#1852022 by DamienMcKenna: Don't export the {metatag_config}.cid field. +#1862570 by DamienMcKenna: Purge any empty values that may have been added by + very early releases. +#1862570 by DamienMcKenna: Follow-up to correctly handle the serialized empty + array. +#1864340 by cdoyle, DamienMcKenna: Incorrect output for certain Twitter Card + tags. +#1865170 by DamienMcKenna: Fix metatag_requirements() return array when the + Page Title module is also installed. +#1722564 by DamienMcKenna: Provide a hook_requirements() message and README.txt + note about a possible conflict with the Exclude Node Title module. +#1284756 by damiankloip, sylus, alanburke, lancee: Migrate module integration. +#1865228 by greggles, DamienMcKenna: Added the rel=author link meta tag. +#1866122 by DamienMcKenna: Added the twitter:site:id and twitter:creator:id + meta tags. +#1866980 by makangus: Corrected metatag_features_revert(). +#1862818 by DYdave, DamienMcKenna: Added documentation for + hook_metatag_config_default(). +#1778534 by DamienMcKenna: Added the original-source meta tag. +#1886170 by DamienMcKenna: Typo in the API docs regarding enabling metatag + support in custom entities. +#1871020 by DamienMcKenna: Compatibility problem with Workbench_Moderation. +#1773926 by Dave Reid: Fixed token validation fails on config edit if the + instance context is not an entity type. +#1814736 by plach, Dave Reid: metatag_page_build() did not check if the + global:frontpage metatag configuration is disabled. +#1871852: Fixed metatag_update_7005() did not check if the watchdog table + exists. +#1891082 by bago, Dave Reid: Fixed metatag_config_instance_label() failed to + recurse properly. +#1915284: Fixed metatag_html_head_alter() stopped removing duplicate tags too + soon. Fixed duplicate canonical links when global redirect is enabled. +#1845326 by DamienMcKenna, Peacog: Resolved language handling problems to + correctly identify the langcode to properly work with or without + Entity_Translation. +#1876042 by DamienMcKenna: Rename variables to use $entity_id instead of $id + and $entity_type instead of $type. +#1859136 by splatio, DamienMcKenna, multpix: Feeds integration - allow meta tag + fields to be the target for data imported using the Feeds module. +#1880302 by olli, DamienMcKenna: Resolve problems with Features integration. +#1923030 by krlucas, DamienMcKenna: Only run metatag_entity_update() on + supported entities. +#1844638 by DamienMcKenna, mikeytown2: Remove unnecessary duplicate {metatag} + records, fix language values for all entities. +#1935084 by DamienMcKenna: Remove unnecessary items from metatag_hook_info() + that was causing problems with PHP 5.4. +#1791720 by kbasarab: Added the news_keywords meta tag. +#1934492 by juampy, DamienMcKenna: Added a page for reverting meta tags for + specific entity or bundle. +#1386320: Note a known issue of using custom template files that do not output + the $page['content'] variable. +#1917902 by DamienMcKenna: Ensure strings returned from token replacement of + text fields ([node:summary]) is passed through the appropriate text filters. +#1919070 by DamienMcKenna: Fix any records that may have been corrupted by e.g. + #1871020. +#1861656 by DamienMcKenna, torrance123: Optionally load the global meta tags on + all pages, enabled by default. +#1871798 by mstrelan: Clear the Context plugin cache when metatag_context is + enabled so that the new plugin becomes available. +#1932192 by DamienMcKenna: Only run metatag_entity_view() once per page view. +#1900434 by Dustin Currie, j0rd, DamienMcKenna: Added several new OpenGraph meta + tags, including ones for videos, location and contact information. +#1883118 by DamienMcKenna: Improve the help message on Metatag:Context's Path + field as neither relative nor absolute URLs will work. +#1945114 by SergO, DamienMcKenna: A query from #1919070 was missing the + preproccess wrapper around the table name. +#1908586 by DamienMcKenna: Added a line to README.txt explaining how to + customize the tokens used to generate the meta tags. +#1350610 by DamienMcKenna: metatag_update_7001 needed to drop the primary key + before customizing it. +#1859136 by DamienMcKenna: Fixed scenarios when updating an entity there are two + copies of the data submitted, e.g. Feeds integration. +#1308790 by DamienMcKenna: Documented that [current-user] tokens should not be + used. +#1318294 by DamienMcKenna: Documented how to use Imagecache Token to resize + images that are being used as tokens for meta tags. +#1871534 by DamienMcKenna: Documented how some browser plugins can make the page + title appear to be wrapped with doublequotes though the output doesn't + actually show them. + + +Metatag 7.x-1.0-beta4, 2012-11-17 +--------------------------------- +#1842764 by DamienMcKenna: Work around problems in metatag_entity_load() + stemming from an outdated database schema, leave a message suggesting the + site admin run the database updates. +#1842868 by DamienMcKenna: Changed metatag_update_7003 to automatically assign + the correct language per entity, added update_7004 to fix records updated in + beta3, fixed the language selection for loading meta tags so sites without + translation functionality continue to work correctly. +#1842868 by DamienMcKenna: Changed update 7003 again so it *only* adds the new + field, changed update 7004 so it will update all records using Batch API. +#1843676 by DamienMcKenna: Changed the hook_requirements message to an INFO + message if Page_Title is also installed, will freak people out less. + + +Metatag 7.x-1.0-beta3, 2012-11-16 +--------------------------------- +#1688286 by colan, DamienMcKenna: Support for Entity Translation. +#1835030 by DamienMcKenna: Documentation and hook_requirements note re Drupal + core v7.17. +#1840402 by DamienMcKenna, paperdhc: Corrected use of array_pop(). +#1841404 by mh86: Don't attempt to load meta tags for unsupported entities, and + don't support configuration-only entities. +#1841564 by peximo: Correctly identify the content language being used on the + homepage. +#1841774 by DamienMcKenna: Provide a warning via hook_requirements if the Page + Title module is also enabled, due to the possibilities of complications and + unexpected results. +#1363476 by DamienMcKenna: Workaround to trigger metatag_entity_view() if the + current CTools (Panels, Panelizer, etc) page is an entity display page. +#1842052 by DamienMcKenna: Don't process unsupported entities being displayed + via Views. +#1664322 by nico059, kerasai, miechiel, idflood, DamienMcKenna, alexweber: + Twitter Cards meta tags. +#1842198 by DamienMcKenna: Move the 'advanced' fieldset under the others. +#1840236 by weri, Marty2081: Only revert the requested feature, not all + features. + + +Metatag 7.x-1.0-beta2, 2012-10-30 +--------------------------------- +#1817580 by DamienMcKenna: Removed code that was enabling debug mode on all + Contexts. +#1818240 by DamienMcKenna: Added $instance value to the drupal_alter() call in + metatag_metatags_view(). +#1817984 by DamienMcKenna, alexweber: Documented + hook_metatag_metatags_view_alter(). +#1818252 by DamienMcKenna: There was no caching on the front page's meta tags. +#1818516 by DamienMcKenna: Incorrect variable check in metatag_page_build(). +#1818762 by DamienMcKenna: Updated hook_hook_info(). +#1466292 by DamienMcKenna: Listed hooks in metatag.api.php and everywhere the + hooks are triggered there's a comment to say what the hook is. +#1818984 by DamienMcKenna: Add the $instance value to metatag_context's + triggering of hook_metatag_metatags_view. +#1819000 by DamienMcKenna: Don't load default meta tags if no active contexts + define meta tags. +#1819448 by DamienMcKenna: Error on admin page if any meta tags were disabled. +#1818958 by DamienMcKenna: The $cid_parts array should contain all relevant + entity variables. +#1820362 by DamienMcKenna: $cid_parts should use base_path() instead of '/'. +#1820374 by DamienMcKenna: Front page $cid_parts did not include the full URL. +#1822726 by DamienMcKenna: Ensure the CTools exportables system is loaded. +#1818300 by eugene.ilyin, DamienMcKenna: Improved Features integration. +#1151936 by DamienMcKenna, maximpodorov: Workaround to trigger + metatag_entity_view() if the current Views page is an entity display page. + + +Metatag 7.x-1.0-beta1, 2012-10-19 +--------------------------------- +First mostly-stable release. diff --git a/sites/all/modules/metatag/README.txt b/sites/all/modules/metatag/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..5e2f5a76f6554db5d4f6ca0cf72ab22552c8d99a --- /dev/null +++ b/sites/all/modules/metatag/README.txt @@ -0,0 +1,162 @@ +Metatag +------- +This module allows you to automatically provide structured metadata, aka "meta +tags", about your website and web pages. + +In the context of search engine optimization, providing an extensive set of +meta tags may help improve your site's & pages' ranking, thus may aid with +achieving a more prominent display of your content within search engine +results. Additionally, using meta tags can help control the summary content +that is used within social networks when visitors link to your site, +particularly the Open Graph submodule for use with Facebook (see below). + +This version of the module only works with Drupal 7.15 and newer. + + +Features +------------------------------------------------------------------------------ +The primary features include: + +* The current supported basic meta tags are ABSTRACT, DESCRIPTION, CANONICAL, + COPYRIGHT, GENERATOR, IMAGE_SRC, KEYWORDS, PUBLISHER, ROBOTS, SHORTLINK and + the page's TITLE tag. + +* Multi-lingual support using the Entity Translation module. + +* Per-path control over meta tags using the "Meta tags: Context" submodule + (requires the Context module). + +* The fifteen Dublin Core Basic Element Set 1.1 meta tags may be added by + enabling the "Meta tags: Dublin Core" submodule. + +* The Open Graph Protocol meta tags, as used by Facebook, may be added by + enabling the "Meta tags: Open Graph" submodule. + +* The Twitter Cards meta tags may be added by enabling the "Meta tags: Twitter + Cards" submodule. + +* An API allowing for additional meta tags to be added, beyond what is provided + by this module - see metatag.api.php for full details. + + +Configuration +------------------------------------------------------------------------------ + 1. On the People Permissions administration page ("Administer >> People + >> Permissions") you need to assign: + + - The "Administer meta tags" permission to the roles that are allowed to + access the meta tags admin pages to control the site defaults. + + - The "Edit meta tags" permission to the roles that are allowed to change + meta tags on each individual page (node, term, etc). + + 2. The main admininistrative page controls the site-wide defaults, both global + settings and defaults per entity (node, term, etc), in addition to those + assigned specifically for the front page: + admin/config/search/metatags + + 3. Each supported entity object (nodes, terms, users) will have a set of meta + tag fields available for customization on their respective edit page, these + will inherit their values from the defaults assigned in #2 above. Any + values that are not overridden per object will automatically update should + the defaults be updated. + + 4. As the meta tags are output using Tokens, it may be necessary to customize + the token display for the site's entities (content types, vocabularies, + etc). To do this go to e.g. admin/structure/types/manage/article/display, in + the "Custom Display Settings" section ensure that "Tokens" is checked (save + the form if necessary), then to customize the tokens go to: + admin/structure/types/manage/article/display/token + + +Fine Tuning +------------------------------------------------------------------------------ +* By default Metatag will load the global default values for all pages that do + not have meta tags assigned via the normal entity display or via Metatag + Context. This may be disabled by setting the variable 'metatag_load_all_pages' + to FALSE through one of the following methods: + * Use Drush to set the value: + drush vset metatag_load_all_pages FALSE + * Hardcode the value in the site's settings.php file: + $conf['metatag_load_all_pages'] = FALSE; + To re-enable this option simply set the value to TRUE. + + +Developers +------------------------------------------------------------------------------ +Full API documentation is available in metatag.api.php. + +To enable Metatag support in custom entities, add 'metatag' => TRUE to either +the entity or bundle definition in hook_entity_info(); see metatag.api.php for +further details and example code. + + +Troubleshooting / Known Issues +------------------------------------------------------------------------------ +* When using custom page template files, e.g. page--front.tpl.php, it is + important to ensure that the following code is present in the template file: + <?php render($page['content']); ?> + or + <?php render($page['content']['metatags']); ?> + Without one of these being present the meta tags will not be displayed. +* Versions of Drupal older than v7.17 were missing necessary functionality for + taxonomy term pages to work correctly. +* Using Metatag with values assigned for the page title and the Page Title + module simultaneously can cause conflicts and unexpected results. +* Using the Exclude Node Title module will cause the [node:title] token to be + empty on node pages, so using [current-page:title] will work around the + issue. Note: it isn't possible to "fix" this as it's a by-product of what + Exclude Node Title does - it removes the node title from display. +* When customizing the meta tags for user pages, it is strongly recommended to + not use the [current-user] tokens, these pertain to the person *viewing* the + page and not e.g. the person who authored a page. +* If images being displayed in image tags need to be resized to fit a specific + requirements, use the Imagecache Token module to customize the value. +* Certain browser plugins, e.g. on Chrome, can cause the page title so be + displayed with additional doublequotes, e.g. instead of: + <title>The page title | My cool site</title> + it will show: + <title>"The page title | My cool site"</title> + The solution is to remove the browser plugin - the page's actual output is not + affected, it is just a problem in the browser. + + +Related modules +------------------------------------------------------------------------------ +Some modules are available that extend Metatag with additional functionality: + +* Domain Meta Tags + http://drupal.org/project/domain_meta + Integrates with the Domain Access module, so each site of a multi-domain + install can separately control their meta tags. + +* Select or Other + http://drupal.org/project/select_or_other + Enhances the user experience of the metatag_opengraph submodule by allowing + the creation of custom Open Graph types. + +* Imagecache Token + http://drupal.org/project/imagecache_token + Provide tokens to load fields using an image style preset, for when meta tags + need to fix exact requirements. + + +Credits / Contact +------------------------------------------------------------------------------ +Currently maintained by Dave Reid [1] and Damien McKenna [2]. + +All initial development was sponsored by Acquia [3] and Palantir [4]; +continued development sponsored by Palantir and Mediacurrent [5]. + +The best way to contact the authors is to submit an issue, be it a support +request, a feature request or a bug report, in the project issue queue: + http://drupal.org/project/issues/metatag + + +References +------------------------------------------------------------------------------ +1: http://drupal.org/user/53892 +2: http://drupal.org/user/108450 +3: http://www.acquia.com/ +4: http://www.palantir.net/ +5: http://www.mediacurrent.com/ diff --git a/sites/all/modules/metatag/metatag.admin.inc b/sites/all/modules/metatag/metatag.admin.inc index d7873807b3ff465631179c80fbda711524c0104b..0b4278a4772bcd170f9acef4826dc14142848436 100644 --- a/sites/all/modules/metatag/metatag.admin.inc +++ b/sites/all/modules/metatag/metatag.admin.inc @@ -89,6 +89,10 @@ function metatag_config_overview() { // Add a summary of the configuration's defaults. $summary = array(); foreach ($config->config as $metatag => $data) { + // Skip meta tags that were disabled. + if (empty($metatags[$metatag])) { + continue; + } $summary[] = array( check_plain($metatags[$metatag]['label']) . ':', check_plain(metatag_get_value($metatag, $data, array('raw' => TRUE))), @@ -173,7 +177,6 @@ function metatag_config_overview() { ), '#rows' => $rows, '#empty' => t('No meta tag defaults available yet.'), - '#caption' => '<div class="js-show">' . t('To view a summary of the default meta tags and the inheritance, click on a meta tag type.') . '</div>', '#attributes' => array( 'class' => array('metatag-config-overview'), ), @@ -270,7 +273,14 @@ function metatag_config_edit_form($form, &$form_state, $config) { $contexts = explode(':', $config->instance); $options['context'] = $contexts[0]; if ($contexts[0] != 'global') { - $options['token types'] = array(token_get_entity_mapping('entity', $contexts[0])); + // The context part of the instance may not map to an entity type, so allow + // the token_get_entity_mapping() function to fallback to the provided type. + if ($token_type = token_get_entity_mapping('entity', $contexts[0], TRUE)) { + $options['token types'] = array($token_type); + } + else { + $options['token types'] = array($contexts[0]); + } } // Ensure that this configuration is properly compared to its parent 'default' @@ -382,3 +392,141 @@ function metatag_config_export_form($config) { ctools_include('export'); return drupal_get_form('ctools_export_form', ctools_export_crud_export('metatag_config', $config), t('Export')); } + +/** + * 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 + $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']; + } + } + } + + $form['update'] = array( + '#type' => 'checkboxes', + '#required' => TRUE, + '#title' => t('Select the entities to revert'), + '#options' => $options, + '#default_value' => array(), + '#description' => t('All meta tags will be removed for all content of the selected entities.'), + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Revert'), + ); + + return $form; +} + +/** + * Form submit handler for metatag reset bulk revert form. + * + * @see metatag_batch_revert_form() + * @see metatag_bulk_revert_batch_finished() + */ +function metatag_bulk_revert_form_submit($form, &$form_state) { + $batch = array( + 'title' => t('Bulk updating metatags'), + 'operations' => array(), + 'finished' => 'metatag_bulk_revert_batch_finished', + 'file' => drupal_get_path('module', 'metatag') . '/metatag.admin.inc', + ); + + // 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_set($batch); +} + +/** + * Batch callback: delete custom metatags for selected bundles. + */ +function metatag_bulk_revert_batch_operation($entity_type, $bundle, &$context) { + if (!isset($context['sandbox']['current'])) { + $context['sandbox']['count'] = 0; + $context['sandbox']['current'] = 0; + } + + // Query the selected entity table. + $entity_info = entity_get_info($entity_type); + $query = new EntityFieldQuery(); + $query->entityCondition('entity_type', $entity_type) + ->propertyCondition($entity_info['entity keys']['id'], $context['sandbox']['current'], '>') + ->propertyOrderBy($entity_info['entity keys']['id']); + if ($entity_type != 'user') { + /** + * Entities which do not define a bundle such as User fail returning no results. + * @see http://drupal.org/node/1054168#comment-5266208 + */ + $query->entityCondition('bundle', $bundle); + } + // Get the total amount of entities to process. + if (!isset($context['sandbox']['total'])) { + $context['sandbox']['total'] = $query->count()->execute(); + $query->count = FALSE; + + // If there are no bundles to revert, stop immediately. + if (!$context['sandbox']['total']) { + $context['finished'] = 1; + return; + } + } + + // Process 25 entities per iteration. + $query->range(0, 25); + $result = $query->execute(); + $ids = !empty($result[$entity_type]) ? array_keys($result[$entity_type]) : array(); + foreach ($ids as $id) { + $metatags = metatag_metatags_load($entity_type, $id); + if (!empty($metatags)) { + db_delete('metatag')->condition('entity_type', $entity_type) + ->condition('entity_id', $id) + ->execute(); + metatag_metatags_cache_clear($entity_type, $id); + $context['results'][] = t('Reverted metatags for @bundle with id @id.', array( + '@bundle' => $entity_type . ': ' . $bundle, + '@id' => $id, + )); + } + } + + $context['sandbox']['count'] += count($ids); + $context['sandbox']['current'] = max($ids); + + if ($context['sandbox']['count'] != $context['sandbox']['total']) { + $context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total']; + } +} + +/** + * Batch finished callback. + */ +function metatag_bulk_revert_batch_finished($success, $results, $operations) { + if ($success) { + if (!count($results)) { + drupal_set_message(t('No metatags were reverted.')); + } + else { + $message = theme('item_list', array('items' => $results)); + drupal_set_message($message); + } + } + else { + $error_operation = reset($operations); + drupal_set_message(t('An error occurred while processing @operation with arguments : @args', + array('@operation' => $error_operation[0], '@args' => print_r($error_operation[0], TRUE)))); + } +} diff --git a/sites/all/modules/metatag/metatag.admin.js b/sites/all/modules/metatag/metatag.admin.js index f278226f686bdce97874cec2d972ca200885b988..c213f721d0587385fa1a7cc3203ee05554ca46d5 100644 --- a/sites/all/modules/metatag/metatag.admin.js +++ b/sites/all/modules/metatag/metatag.admin.js @@ -5,8 +5,16 @@ Drupal.behaviors.metatagUIConfigListing = { // Hide elements to be visible if JavaScript is enabled. $('.js-show').show(); + // Make the leaf arrow clickable. + $('.metatag-config-label').hover(function(){ + $(this).css({'cursor':'pointer'}); + }) + .click(function(){ + $(this).find('a.toggle-details', context).trigger('click'); + }); + // Show or hide the summary - $('table.metatag-config-overview a.toggle-details', context).click(function() { + $('table.metatag-config-overview a.toggle-details', context).click(function(event) { $(this).parent('div').siblings('div.metatag-config-details').each(function() { if ($(this).hasClass('js-hide')) { $(this).slideDown('slow').removeClass('js-hide'); @@ -23,6 +31,10 @@ Drupal.behaviors.metatagUIConfigListing = { else { $(this).parent('div').removeClass('expanded').addClass('collapsed'); } + + // This event may be triggered by a parent element click - so we don't + // want the click to bubble up otherwise we get recursive click events. + event.stopPropagation(); }); } } diff --git a/sites/all/modules/metatag/metatag.api.php b/sites/all/modules/metatag/metatag.api.php index b1245a0355d86abd6b95987b8e96c16fe8400f33..b2d97ddf8df0e1300cd1297015b3e303b9d18cab 100644 --- a/sites/all/modules/metatag/metatag.api.php +++ b/sites/all/modules/metatag/metatag.api.php @@ -3,3 +3,217 @@ * @file * API documentation for the Metatag module. */ + +/** + * To enable Metatag support in custom entities, add 'metatags' => TRUE to the + * entity definition in hook_entity_info(), e.g.: + * + * /** + * * Implements hook_entity_info(). + * * + * * Taken from the Examples module. + * * / + * function entity_example_entity_info() { + * $info['entity_example_basic'] = array( + * 'label' => t('Example Basic Entity'), + * 'controller class' => 'EntityExampleBasicController', + * 'base table' => 'entity_example_basic', + * 'uri callback' => 'entity_example_basic_uri', + * 'fieldable' => TRUE, + * 'metatags' => TRUE, + * 'entity keys' => array( + * 'id' => 'basic_id' , // The 'id' (basic_id here) is the unique id. + * 'bundle' => 'bundle_type' // Bundle will be determined by the 'bundle_type' field + * ), + * 'bundle keys' => array( + * 'bundle' => 'bundle_type', + * ), + * 'static cache' => TRUE, + * 'bundles' => array( + * 'first_example_bundle' => array( + * 'label' => 'First example bundle', + * 'admin' => array( + * 'path' => 'admin/structure/entity_example_basic/manage', + * 'access arguments' => array('administer entity_example_basic entities'), + * ), + * ), + * ), + * 'view modes' => array( + * 'tweaky' => array( + * 'label' => t('Tweaky'), + * 'custom settings' => FALSE, + * ), + * ) + * ); + * + * return $info; + * } + * + * The definition of each bundle may be handled separately, thus support may be + * disabled for the entity as a whole but enabled for individual bundles. This + * is handled via the 'metatags' value on the bundle definition, e.g.: + * + * 'bundles' => array( + * 'first_example_bundle' => array( + * 'label' => 'First example bundle', + * 'metatags' => TRUE, + * 'admin' => array( + * 'path' => 'admin/structure/entity_example_basic/manage', + * 'access arguments' => array('administer entity_example_basic entities'), + * ), + * ), + * ), + */ + +/** + * Provides a default configuration for Metatag intances. + * + * This hook allows modules to provide their own Metatag instances which can + * either be used as-is or as a "starter" for users to build from. + * + * This hook should be placed in MODULENAME.metatag.inc and it will be auto- + * loaded. MODULENAME.metatag.inc *must* be in the same directory as the + * .module file which *must* also contain an implementation of + * hook_ctools_plugin_api, preferably with the same code as found in + * metatag_ctools_plugin_api(). + * + * The $config->disabled boolean attribute indicates whether the Metatag + * instance should be enabled (FALSE) or disabled (TRUE) by default. + * + * @return + * An associative array containing the structures of Metatag instances, as + * generated from the Export tab, keyed by the Metatag config name. + * + * @see metatag_metatag_config_default() + * @see metatag_ctools_plugin_api() + */ +function hook_metatag_config_default() { + $configs = array(); + + $config = new stdClass(); + $config->instance = 'config1'; + $config->api_version = 1; + $config->disabled = FALSE; + $config->config = array( + 'title' => array('value' => '[current-page:title] | [site:name]'), + 'generator' => array('value' => 'Drupal 7 (http://drupal.org)'), + 'canonical' => array('value' => '[current-page:url:absolute]'), + 'shortlink' => array('value' => '[current-page:url:unaliased]'), + ); + $configs[$config->instance] = $config; + + $config = new stdClass(); + $config->instance = 'config2'; + $config->api_version = 1; + $config->disabled = FALSE; + $config->config = array( + 'title' => array('value' => '[user:name] | [site:name]'), + ); + $configs[$config->instance] = $config; + + return $configs; +} + +/** + * + */ +function hook_metatag_config_default_alter(&$config) { +} + +/** + * + */ +function hook_metatag_config_delete($entity_type, $entity_ids) { +} + +/** + * + */ +function hook_metatag_config_insert($config) { +} + +/** + * + */ +function hook_metatag_config_instance_info() { + return array(); +} + +/** + * + */ +function hook_metatag_config_instance_info_alter(&$info) { +} + +/** + * + */ +function hook_metatag_config_load() { +} + +/** + * + */ +function hook_metatag_config_load_presave() { +} + +/** + * + */ +function hook_metatag_config_presave($config) { +} + +/** + * + */ +function hook_metatag_config_update($config) { +} + +/** + * + */ +function hook_metatag_info() { + return array(); +} + +/** + * + */ +function hook_metatag_info_alter(&$info) { +} + +/** + * + */ +function hook_metatag_load_entity_from_path_alter(&$path, $result) { +} + +/** + * Alter metatags before being cached. + * + * This hook is invoked prior to the meta tags for a given page are cached. + * + * @param array $output + * All of the meta tags to be output for this page in their raw format. This + * is a heavily nested array. + * @param string $instance + * An identifier for the current page's page type, typically a combination + * of the entity name and bundle name, e.g. "node:story". + */ +function hook_metatag_metatags_view_alter(&$output, $instance) { + if (isset($output['description']['#attached']['drupal_add_html_head'][0][0]['#value'])) { + $output['description']['#attached']['drupal_add_html_head'][0][0]['#value'] = 'O rly?'; + } +} + +/** + * + */ +function hook_metatag_page_cache_cid_parts_alter(&$cid_parts) { +} + +/** + * + */ +function hook_metatag_presave(&$metatags, $entity_type, $entity_id) { +} diff --git a/sites/all/modules/metatag/metatag.features.inc b/sites/all/modules/metatag/metatag.features.inc new file mode 100644 index 0000000000000000000000000000000000000000..5f8ab5440c3410294d91ba43cdb29159a7d2b07e --- /dev/null +++ b/sites/all/modules/metatag/metatag.features.inc @@ -0,0 +1,87 @@ +<?php +/** + * @file + * Features integration for the Metatag module. + */ + +/** + * Implements hook_features_export(). + */ +function metatag_features_export($data, &$export, $module_name = '', $type = 'metatag') { + $pipe = array(); + + foreach ($data as $name) { + if (metatag_config_load($name)) { + $export['features'][$type][$name] = $name; + } + } + + $export['dependencies']['metatag'] = 'metatag'; + + return $pipe; +} + +/** + * Implements hook_features_export_render(). + */ +function metatag_features_export_render($module_name, $data, $export = NULL) { + $code = array(); + $code[] = ' $config = array();'; + $code[] = ''; + + foreach ($data as $key => $name) { + if (is_object($name)) { + $name = $name->instance; + } + if ($config = metatag_config_load($name)) { + $export = new stdClass(); + $export->instance = $config->instance; + $export->config = $config->config; + $export = features_var_export($export, ' '); + $key = features_var_export($name); + $code[] = " // Exported Metatag config instance: {$name}."; + $code[] = " \$config[{$key}] = {$export};"; + $code[] = ""; + } + } + $code[] = ' return $config;'; + $code = implode("\n", $code); + return array('metatag_export_default' => $code); +} + +/** + * Implements hook_features_revert(). + */ +function metatag_features_revert($module) { + if ($feature_conf = features_get_default('metatag', $module)) { + foreach (array_keys($feature_conf) as $config) { + if ($conf = metatag_config_load($config)) { + db_delete('metatag_config')->condition('instance', $config)->execute(); + } + unset($feature_conf[$config]['cid']); + $object = new stdClass(); + $object->cid = NULL; + $object->instance = $config; + $object->config = $feature_conf[$config]['config']; + metatag_config_save($object); + } + } +} + +/** + * Implements hook_features_export_options(). + */ +function metatag_features_export_options() { + $instances = metatag_config_instance_info(); + foreach ($instances as $key => $instance) { + $options[$key] = $key; + }; + return $options; +} + +/** + * Implements hook_features_rebuild(). + */ +function metatag_features_rebuild($module) { + metatag_features_revert($module); +} diff --git a/sites/all/modules/metatag/metatag.feeds.inc b/sites/all/modules/metatag/metatag.feeds.inc new file mode 100644 index 0000000000000000000000000000000000000000..3ee0acfa010fe5b7a7fa0d795c1dc1b78b16bc72 --- /dev/null +++ b/sites/all/modules/metatag/metatag.feeds.inc @@ -0,0 +1,37 @@ +<?php +/** + * @file + * Feeds mapping implementation for the Metatag module. + */ + +/** + * Implements hook_feeds_processor_targets_alter(). + */ +function metatag_feeds_processor_targets_alter(&$targets, $entity_type, $bundle) { + if (metatag_entity_supports_metatags($entity_type)) { + $info = metatag_get_info(); + foreach ($info['tags'] as $name => $tag) { + $targets['meta_' . $name] = array( + 'name' => 'Meta tag: ' . check_plain($tag['label']), + 'callback' => 'metatag_feeds_set_target', + 'description' => $tag['description'], + ); + } + } +} + +/** + * Callback function to set value of a metatag tag. + */ +function metatag_feeds_set_target($source, $entity, $target, $value) { + // Don't do anything if we weren't given any data. + if (empty($value)) { + return; + } + + // Strip the prefix that was added above. + $name = str_replace('meta_', '', $target); + + // Assign the value. + $entity->metatags[$name]['value'] = $value; +} diff --git a/sites/all/modules/metatag/metatag.inc b/sites/all/modules/metatag/metatag.inc index f079d2971e999cb78a3efc71a0a99daccf712970..d2f9aac94799508a2dfd5d1f1f128dc363f9d139 100644 --- a/sites/all/modules/metatag/metatag.inc +++ b/sites/all/modules/metatag/metatag.inc @@ -65,7 +65,6 @@ class DrupalDefaultMetaTag implements DrupalMetaTagInterface { return array( '#attached' => array('drupal_add_html_head' => array(array($element, $element['#id']))), ); - return $element; } } @@ -88,7 +87,7 @@ class DrupalTextMetaTag extends DrupalDefaultMetaTag { '#default_value' => isset($this->data['value']) ? $this->data['value'] : '', '#element_validate' => array('token_element_validate'), '#token_types' => $options['token types'], - '#maxlength' => 255, + '#maxlength' => 1024, ); return $form; @@ -98,7 +97,7 @@ class DrupalTextMetaTag extends DrupalDefaultMetaTag { $options += array( 'token data' => array(), 'clear' => TRUE, - 'sanitize' => FALSE, + 'sanitize' => TRUE, 'raw' => FALSE, ); @@ -142,7 +141,6 @@ class DrupalLinkMetaTag extends DrupalTextMetaTag { return array( '#attached' => array('drupal_add_html_head' => array(array($element, $element['#id']))), ); - return $element; } } diff --git a/sites/all/modules/metatag/metatag.info b/sites/all/modules/metatag/metatag.info index 0addb2e115d62b86a279c9850e6af4659a6a788d..4a61acd75db34ac577d8726a5c34d289eb67928f 100644 --- a/sites/all/modules/metatag/metatag.info +++ b/sites/all/modules/metatag/metatag.info @@ -2,16 +2,23 @@ name = Meta tags description = "Adds support and an API to implement meta tags." package = Meta tags core = 7.x -dependencies[] = token + +# This requires Drupal 7.15 or newer. +dependencies[] = system (>=7.15) + +# CTools and Token are also required. dependencies[] = ctools +dependencies[] = token + configure = admin/config/search/metatags files[] = metatag.inc +files[] = metatag.migrate.inc files[] = metatag.test -; Information added by drupal.org packaging script on 2012-07-13 -version = "7.x-1.0-alpha6+1-dev" +; Information added by drupal.org packaging script on 2013-03-24 +version = "7.x-1.0-beta5" core = "7.x" project = "metatag" -datestamp = "1342182783" +datestamp = "1364088611" diff --git a/sites/all/modules/metatag/metatag.install b/sites/all/modules/metatag/metatag.install index 47518f91205c34086fc9f3b5437dfa2740c35762..f1ee370dffb12affacce441e767938fbb3b5da69 100644 --- a/sites/all/modules/metatag/metatag.install +++ b/sites/all/modules/metatag/metatag.install @@ -32,6 +32,7 @@ function metatag_schema() { 'unsigned' => TRUE, 'not null' => TRUE, 'description' => 'The primary identifier for a metatag configuration set.', + 'no export' => TRUE, ), 'instance' => array( 'type' => 'varchar', @@ -78,8 +79,15 @@ function metatag_schema() { 'not null' => TRUE, 'serialize' => TRUE, ), + 'language' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The language of the tag.', + ), ), - 'primary key' => array('entity_type', 'entity_id'), + 'primary key' => array('entity_type', 'entity_id', 'language'), ); $schema['cache_metatag'] = drupal_get_schema_unprocessed('system', 'cache'); @@ -88,6 +96,103 @@ function metatag_schema() { return $schema; } +/** + * Implements hook_requirements(). + */ +function metatag_requirements($phase) { + $requirements = array(); + // Ensure translations don't break during installation. + $t = get_t(); + + if ($phase == 'runtime') { + // Work out the release of D7 that is currently running. + list($major, $minor) = explode('.', VERSION); + // Strip off any suffixes on the version string, e.g. "17-dev". + if (strpos('-', $minor)) { + list($minor, $suffix) = explode('-', $minor); + } + + // Releases of Drupal older than 7.15 did not have entity_language(), which + // is now required. + if ($minor < 15) { + $requirements['metatag'] = array( + 'severity' => REQUIREMENT_WARNING, + 'title' => 'Metatag', + 'value' => $t('Upgrade Drupal core to v7.15 or newer'), + 'description' => $t("This older version of Drupal core is missing functionality necessary for the module's multilingual support, it must be upgraded to at least version 7.15."), + ); + } + // Releases of Drupal older than 7.17 did not trigger hook_entity_view on + // term pages, so recommend updating. + elseif ($minor < 17) { + $requirements['metatag'] = array( + 'severity' => REQUIREMENT_WARNING, + 'title' => 'Metatag', + 'value' => $t('Upgrade Drupal core to v7.17 or newer'), + 'description' => $t('Your older version of Drupal core is missing functionality necessary for taxonomy term pages to work correctly, it is strongly recommended to upgrade to the latest release.'), + ); + } + // Everything's OK. + else { + $requirements['metatag'] = array( + 'severity' => REQUIREMENT_OK, + 'title' => 'Metatag', + 'value' => $t('Drupal core is compatible'), + 'description' => $t('Older versions of Drupal core were missing functionality necessary for taxonomy term pages to work correctly, but this version <em>will</em> work correctly.'), + ); + } + + // Add a note if Page Title is also installed. + if (module_exists('page_title')) { + $requirements['metatag_page_title'] = array( + 'severity' => REQUIREMENT_INFO, + 'title' => 'Metatag', + 'value' => $t('Possible conflicts with Page Title module'), + 'description' => $t('The Metatag module is able to customize page titles so running the Page Title module simultaneously can lead to complications.'), + ); + } + + // Add a note if Page Title is also installed. + if (module_exists('exclude_node_title')) { + $requirements['metatag_exclude_node_title'] = array( + 'severity' => REQUIREMENT_INFO, + 'title' => 'Metatag', + 'value' => $t('Possible conflicts with Exclude Node Title module'), + 'description' => $t('The Metatag module\'s default setitngs for content types (nodes) uses [node:title] for the page title. Unfortunately, Exclude Node Title hides this so the page title ends up blank. It is recommended to <a href="!config">change the "title" field\'s default value</a> to "[current-page:title]" instead of "[node:title]" for any content types affected by Exclude Node Title.', array('!config' => 'admin/config/search/metatags')), + ); + } + } + + return $requirements; +} + +/** + * Implements hook_install(). + */ +// function metatag_install() { +// } + +/** + * Implements hook_uninstall(). + */ +function metatag_uninstall() { + // This variable is created via hook_enable. + variable_del('metatag_schema_installed'); +} + +/** + * Implements hook_enable(). + */ +function metatag_enable() { + variable_set('metatag_schema_installed', TRUE); +} + +/** + * Implements hook_disable(). + */ +// function metatag_disable() { +// } + /** * Disable the deprecated metatag_ui module which has been merged into metatag. */ @@ -99,14 +204,528 @@ function metatag_update_7000() { } /** - * Fix the {metatag_config}.cid column cannot be NULL. + * Fix the "{metatag_config}.cid column cannot be NULL" error. */ function metatag_update_7001() { - $field = array( + $table_name = 'metatag_config'; + $field_name = 'cid'; + $field_spec = array( 'type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE, 'description' => 'The primary identifier for a metatag configuration set.', ); - db_change_field('metatag_config', 'cid', 'cid', $field); + $keys = array('primary key' => array($field_name)); + + // Before making any changes, drop the existing primary key. + db_drop_primary_key($table_name); + + // Rejig the field, and turn on the primary key again. + db_change_field($table_name, $field_name, $field_name, $field_spec, $keys); +} + +/** + * Disable the deprecated metatag_ui module which has been merged into metatag, + * again. + */ +function metatag_update_7002() { + if (module_exists('metatag_ui')) { + module_disable(array('metatag_ui'), FALSE); + drupal_uninstall_modules(array('metatag_ui'), FALSE); + drupal_set_message(t('The deprecated Metatag UI module has been disabled.')); + } +} + +/** + * Add the {metatag}.language field. + */ +function metatag_update_7003() { + // Set the target table and field name. + $table_name = 'metatag'; + $field_name = 'language'; + + // Don't add the new field if it already exists. + if (!db_field_exists($table_name, $field_name)) { + // Describe the new field. + $field_spec = array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The language of the tag', + ); + + // Add it and update the primary key. + db_add_field($table_name, $field_name, $field_spec); + db_drop_primary_key($table_name); + db_add_primary_key($table_name, array('entity_type', 'entity_id', 'language')); + } +} + +/** + * Replaced by updates 7009, 7010, 7011, 7012 and 7013. + */ +function metatag_update_7004() { + // Do nothing. +} + +/** + * Removing wrong metatag watchdog entries that break the admin/reports/dblog + * page. + */ +function metatag_update_7005() { + if (db_table_exists('watchdog')) { + db_delete('watchdog') + ->condition('type', 'metatag') + ->condition('variables', serialize('info')) + ->execute(); + } +} + +/** + * Remove {metatag} records that were added by old versions of the module for + * entities that don't actually support meta tags. A more complete version of + * this will be added later on after it's (hopefully) guaranteed that all + * modules have updated to the correct API usage. + */ +function metatag_update_7006() { + $entity_types = array( + // Core. + 'comment', + 'menu_link', + 'taxonomy_vocabulary', + // Some contrib entities. + 'mailchimp_list', + 'profile2', + 'profile2_type', + 'redirect', + 'rules_config', + 'wysiwyg_profile', + ); + foreach ($entity_types as $entity_type) { + $num_deleted = db_delete('metatag') + ->condition('entity_type', $entity_type) + ->execute(); + if ($num_deleted > 0) { + drupal_set_message(t('Removed @count meta tag record(s) for the @type entity type, it does not support meta tags.', array('@count' => $num_deleted, '@type' => $entity_type))); + } + } +} + +/** + * Remove {metatag} records for objects that have been deleted; older versions + * of Metatag may have failed to purge these. + */ +function metatag_update_7007() { + $result = db_query("DELETE m + FROM {metatag} m + LEFT OUTER JOIN {node} n + ON m.entity_id=n.nid + WHERE m.entity_type='node' + AND n.nid IS NULL"); + if ($result->rowCount() > 0) { + drupal_set_message(t('Removed @count meta tag record(s) for nodes that had been purged.', array('@count' => $result->rowCount()))); + } + + $result = db_query("DELETE m + FROM {metatag} m + LEFT OUTER JOIN {users} u + ON m.entity_id=u.uid + WHERE m.entity_type='user' + AND u.uid IS NULL"); + if ($result->rowCount() > 0) { + drupal_set_message(t('Removed @count meta tag record(s) for users that had been purged.', array('@count' => $result->rowCount()))); + } + + $result = db_query("DELETE m + FROM {metatag} m + LEFT OUTER JOIN {taxonomy_term_data} t + ON m.entity_id=t.tid + WHERE m.entity_type='taxonomy_term' + AND t.tid IS NULL"); + if ($result->rowCount() > 0) { + drupal_set_message(t('Removed @count meta tag record(s) for taxonomy terms that had been purged.', array('@count' => $result->rowCount()))); + } +} + +/** + * Remove any empty records that may be hanging around from old releases. + */ +function metatag_update_7008() { + $result = db_query("DELETE m FROM {metatag} m WHERE m.data IS NULL or m.data = '' OR m.data = :empty", array(':empty' => serialize(array()))); + if ($result->rowCount() > 0) { + drupal_set_message(t('Purged @count empty meta tag record(s).', array('@count' => $result->rowCount()))); + } +} + +/** + * Fix {metatag} records for taxonomy terms. + */ +function metatag_update_7009() { + // Remove duplicates. + _metatag_remove_dupes('taxonomy_term'); + + // The taxonomy term entity doesn't support a 'language' option, so reset it + // to LANGUAGE_NONE. + $result = db_query("UPDATE {metatag} SET language = :language WHERE entity_type='taxonomy_term'", array(':language' => LANGUAGE_NONE)); + if ($result->rowCount() > 0) { + drupal_set_message(t('Fixed language values for @count taxonomy terms.', array('@count' => $result->rowCount()))); + } +} + +/** + * Fix {metatag} records for users. + */ +function metatag_update_7010() { + // Remove duplicates. + _metatag_remove_dupes('user'); + + // Update User values. + $result = db_query("UPDATE {metatag} SET language = :language WHERE entity_type='user'", array(':language' => LANGUAGE_NONE)); + if ($result->rowCount() > 0) { + drupal_set_message(t('Fixed language values for @count user records.', array('@count' => $result->rowCount()))); + } +} + +/** + * Fix {metatag} records for nodes. + */ +function metatag_update_7011() { + // Only proceed if Entity_Translation is not enabled as it allows each node + // record to have multiple languages available. + if (module_exists('entity_translation')) { + drupal_set_message(t("Entity Translation is enabled, so node meta tags will not be updated, to avoid accidental dataloss.")); + return; + } + + // Remove duplicates. + _metatag_remove_dupes('node'); + + // Update Node values. + $result = db_query("UPDATE {metatag} AS m INNER JOIN {node} n ON m.entity_id=n.nid AND m.entity_type='node' SET m.language = n.language"); + if ($result->rowCount() > 0) { + drupal_set_message(t('Fixed language values for @count nodes.', array('@count' => $result->rowCount()))); + } +} + +/** + * Remove duplicate {metatag} records for non-core entities. + */ +function metatag_update_7012() { + if (module_exists('entity_translation')) { + drupal_set_message(t("Entity Translation is enabled, duplicate meta tags will not be removed for custom entities, to avoid accidental dataloss.")); + return; + } + + $records = db_select('metatag', 'm') + ->fields('m', array('entity_type')) + ->condition('m.entity_type', array('node', 'taxonomy_term', 'user'), 'NOT IN') + ->orderBy('m.entity_type', 'ASC') + ->orderBy('m.entity_id', 'ASC') + ->distinct() + ->execute(); + + $entity_types = array(); + foreach ($records as $record) { + $entity_types[] = $record->entity_type; + // Remove duplicates. + _metatag_remove_dupes($record->entity_type); + } + + if (empty($entity_types)) { + drupal_set_message(t('There were no other records to fix.')); + } +} + +/** + * Fix the {metatag} language value for all non-core entity records. This might + * take a while, depending on how much data needs to be converted. + */ +function metatag_update_7013(&$sandbox) { + if (module_exists('entity_translation')) { + drupal_set_message(t("Entity Translation is enabled, meta tags will not be updated for custom entities, to avoid accidental dataloss.")); + return; + } + + // Use the sandbox at your convenience to store the information needed + // to track progression between successive calls to the function. + if (!isset($sandbox['progress'])) { + // The count of records visited so far. + $sandbox['progress'] = 0; + + // Because the {metatag} table uses multiple primary keys, there's no easy + // way to do this, so we're going to cache all record keys and manually + // step through them. + $records = db_select('metatag', 'm') + ->fields('m', array('entity_type', 'entity_id')) + ->condition('m.entity_type', array('node', 'taxonomy_term', 'user'), 'NOT IN') + ->orderBy('m.entity_type', 'ASC') + ->orderBy('m.entity_id', 'ASC') + ->execute(); + $sandbox['records'] = array(); + foreach ($records as $record) { + $sandbox['records'][] = $record; + } + + // If there's no data, don't bother with the extra work. + if (empty($sandbox['records'])) { + watchdog('metatag', 'Update 7013: No meta tag records need updating.', array(), WATCHDOG_INFO); + if (drupal_is_cli()) { + drupal_set_message(t('Update 7013: No meta tag records need updating.')); + } + return t('No meta tag records need updating.'); + } + + // Total records that must be visited. + $sandbox['max'] = count($sandbox['records']); + + // A place to store messages during the run. + $sandbox['messages'] = array(); + + // An initial record of the number of records to be updated. + watchdog('metatag', 'Update 7013: !count records to update.', array('!count' => $sandbox['max']), WATCHDOG_INFO); + if (drupal_is_cli()) { + drupal_set_message(t('Update 7013: !count records to update.', array('!count' => $sandbox['max']))); + } + + // Last record processed. + $sandbox['current_record'] = -1; + } + + // Process records by groups of 10 (arbitrary value). + // When a group is processed, the batch update engine determines whether it + // should continue processing in the same request or provide progress + // feedback to the user and wait for the next request. + $limit = 10; + + // The for loop will run as normal when ran via update.php, but when ran via + // Drush it'll just run 'til it's finished. + $increment = 1; + if (drupal_is_cli()) { + $increment = 0; + } + + // Set default values. + for ($ctr = 0; $ctr < $limit; $ctr += $increment) { + $sandbox['current_record']++; + if (empty($sandbox['records'][$sandbox['current_record']])) { + break; + } + + // Shortcuts for later. + $entity_type = $sandbox['records'][$sandbox['current_record']]->entity_type; + $entity_id = $sandbox['records'][$sandbox['current_record']]->entity_id; + + // Load the entity. + $entities = entity_load($entity_type, array($entity_id)); + if (!empty($entities)) { + $entity = array_pop($entities); + + // Make sure that the entity has a language set. + if (!empty($entity)) { + // If there's a (non-empty) language value, use it. + $new_language = entity_language($entity_type, $entity); + if (empty($new_language)) { + $new_language = LANGUAGE_NONE; + } + // Update the 'language' value. + db_update('metatag') + ->fields(array('language' => $new_language)) + ->condition('entity_type', $entity_type) + ->condition('entity_id', $entity_id) + ->execute(); + } + } + + // Update our progress information. + $sandbox['progress']++; + } + + // Set the "finished" status, to tell batch engine whether this function + // needs to run again. If you set a float, this will indicate the progress of + // the batch so the progress bar will update. + $sandbox['#finished'] = ($sandbox['progress'] >= $sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']); + + if ($sandbox['#finished']) { + // Clear all caches so the fixed data will be reloaded. + cache_clear_all('*', 'cache_metatag', TRUE); + + // A final log of the number of records that were converted. + watchdog('metatag', 'Update 7013: !count records were updated in total.', array('!count' => $sandbox['progress']), WATCHDOG_INFO); + if (drupal_is_cli()) { + drupal_set_message(t('Update 7013: !count records were updated.', array('!count' => $sandbox['progress']))); + } + + // hook_update_N() may optionally return a string which will be displayed + // to the user. + return t('!count records were updated in total.', array('!count' => $sandbox['progress'])); + } +} + +/** + * Remove duplicate records for a given entity. + * + * It should be OK to run this without doing a separate batch process as there + * shouldn't be many records that have this problem. Hopefully. + * + * @param $entity_type + * The name of an entity type to check for. + */ +function _metatag_remove_dupes($entity_type) { + $purge_count = 0; + + // First step: fix the records. There should not be multiple records for the + // same entity_id with different languages. + $dupe_records = db_query("SELECT m.entity_id, count(m.language) AS the_count + FROM {metatag} m + WHERE + m.entity_type = :type + GROUP BY m.entity_id + HAVING the_count > 1", array(':type' => $entity_type)); + + if (!empty($dupe_records)) { + foreach ($dupe_records as $record) { + $entity_id = $record->entity_id; + $langs = db_query("SELECT m.entity_id, m.language, m.data FROM {metatag} m WHERE m.entity_type = :type AND m.entity_id = :id", array(':type' => $entity_type, ':id' => $entity_id))->fetchAll(); + + // Work out which language record to remove. Will need to store this as + // an array incase there are multiple records to purge. + $langs_to_remove = array(); + + // Check for duplicate records. + // Outer loop starts from the beginning. + for ($outer = 0; $outer < count($langs); $outer++) { + // This record may have been removed already. + if (isset($langs[$outer])) { + // Inner loop starts from the end. + for ($inner = count($langs) - 1; $inner > 0; $inner--) { + // Work out if the outer loop's data is the same as the inner + // loop's. + if (isset($langs[$inner]) && $langs[$outer]->data == $langs[$inner]->data) { + // Remove the second record. + $langs_to_remove[] = $langs[$inner]->language; + unset($langs[$inner]); + } + } + } + } + + // Only one record left. + if (count($langs) == 1) { + // This is how it should be, this record is fine. + } + // More than one record, work out which one to keep. + elseif (count($langs) > 1) { + // Work out the entity's language. + $entity = entity_load($entity_type, $entity_id); + $entity_language = entity_language($entity_type, $entity); + if (empty($language)) { + $entity_language = LANGUAGE_NONE; + } + + // Work out if the entity's language record exists. + $lang_pos = NULL; + foreach ($langs as $key => $record) { + if ($record->language == $entity_language) { + $lang_pos = $key; + break; + } + } + // If the language record exists, delete the others. + if (isset($lang_pos)) { + foreach ($langs as $key => $record) { + if ($record->language != $entity_language) { + $langs_to_remove[] = $record->language; + } + } + } + // Otherwise look for a record for the site's default language. + else { + foreach ($langs as $key => $record) { + if ($record->language == $GLOBALS['language']->language) { + $lang_pos = $key; + break; + } + } + if (isset($lang_pos)) { + foreach ($langs as $key => $record) { + if ($record->language != $GLOBALS['language']->language) { + $langs_to_remove[] = $record->language; + } + } + } + // Finally check for LANGUAGE_NONE. + else { + foreach ($langs as $key => $record) { + if ($record->language == LANGUAGE_NONE) { + $lang_pos = $key; + break; + } + } + if (isset($lang_pos)) { + foreach ($langs as $key => $record) { + if ($record->language != LANGUAGE_NONE) { + $langs_to_remove[] = $record->language; + } + } + } + } + } + } + + // Purge the redundant records. + if (!empty($langs_to_remove)) { + $purge_count += db_delete('metatag') + ->condition('entity_type', $entity_type) + ->condition('entity_id', $entity_id) + ->condition('language', $langs_to_remove) + ->execute(); + } + } + } + + if (empty($purge_count)) { + drupal_set_message(t('No duplicate :entity_type records were found (this is a good thing).', array(':entity_type' => $entity_type))); + watchdog('metatag', 'No duplicate :entity_type records were found (this is a good thing).', array(':entity_type' => $entity_type)); + } + else { + drupal_set_message(t('Purged :count duplicate :entity_type record(s).', array(':count' => $purge_count, ':entity_type' => $entity_type))); + watchdog('metatag', 'Purged :count duplicate :entity_type record(s).', array(':count' => $purge_count, ':entity_type' => $entity_type)); + return; + } +} + +/** + * Fix {metatag} records that may have been corrupted by #1871020. + */ +function metatag_update_7014() { + $records = db_query("SELECT * + FROM {metatag} m + WHERE + m.`data` LIKE :nolang + OR m.`data` LIKE :lang + OR m.`data` LIKE :und", + array( + ':nolang' => 'a:1:{s:0:"";a:%:{s:%;a:%:{%;}}}', + ':lang' => 'a:1:{s:2:"__";a:%:{s:%;a:%:{%;}}}', + ':und' => 'a:1:{s:3:"___";a:%:{s:%;a:%:{%;}}}', + )); + + // Nothing to fix. + if ($records->rowCount() == 0) { + drupal_set_message(t('No corrupt records to fix, this is good news :-)')); + } + + // Fix the faulty records. + else { + foreach ($records as $record) { + // Extract the data and get the first element of the array, this should be + // valid data. + $record->data = reset(unserialize($record->data)); + + // Update the record. + drupal_write_record('metatag', $record, array('entity_type', 'entity_id', 'language')); + } + drupal_set_message(t('Fixed @count corrupt meta tag record(s).', array('@count' => $records->rowCount()))); + } } diff --git a/sites/all/modules/metatag/metatag.metatag.inc b/sites/all/modules/metatag/metatag.metatag.inc index 51f0c99e5521d7ff4e32b388411b7fdc33ac18ea..bd892556305708268244a9df8a487924a3743555 100644 --- a/sites/all/modules/metatag/metatag.metatag.inc +++ b/sites/all/modules/metatag/metatag.metatag.inc @@ -13,6 +13,8 @@ function metatag_metatag_config_default() { $config->config = array( 'title' => array('value' => '[current-page:title] | [site:name]'), 'generator' => array('value' => 'Drupal 7 (http://drupal.org)'), + 'canonical' => array('value' => '[current-page:url:absolute]'), + 'shortlink' => array('value' => '[current-page:url:unaliased]'), ); $configs[$config->instance] = $config; @@ -23,6 +25,7 @@ function metatag_metatag_config_default() { $config->config = array( 'title' => array('value' => variable_get('site_slogan') ? '[site:name] | [site:slogan]' : '[site:name]'), 'canonical' => array('value' => '[site:url]'), + 'shortlink' => array('value' => '[site:url]'), ); $configs[$config->instance] = $config; @@ -97,13 +100,30 @@ function metatag_metatag_info() { $info['groups']['advanced'] = array( 'label' => t('Advanced'), 'form' => array( - '#weight' => 90, + '#weight' => 100, ), ); + $info['tags']['title'] = array( + 'label' => t('Page title'), + 'description' => t("The text to display in the title bar of a visitor's web browser when they view this page. This meta tag may also be used as the title of the page when a visitor bookmarks or favorites this page."), + 'class' => 'DrupalTitleMetaTag', + ); + $info['tags']['description'] = array( 'label' => t('Description'), - 'description' => t("A brief and concise summary of the page's content, preferrably 150 characters or less. The description meta tag may be used by search engines to display a snippet about the page in search results."), + 'description' => t("A brief and concise summary of the page's content, preferably 150 characters or less. The description meta tag may be used by search engines to display a snippet about the page in search results."), + 'class' => 'DrupalTextMetaTag', + 'form' => array( + '#type' => 'textarea', + '#rows' => 2, + '#wysiwyg' => FALSE, + ), + ); + + $info['tags']['abstract'] = array( + 'label' => t('Abstract'), + 'description' => t("A brief and concise summary of the page's content, preferably 150 characters or less. The abstract meta tag may be used by search engines for archiving purposes."), 'class' => 'DrupalTextMetaTag', 'form' => array( '#type' => 'textarea', @@ -111,16 +131,12 @@ function metatag_metatag_info() { '#wysiwyg' => FALSE, ), ); + $info['tags']['keywords'] = array( 'label' => t('Keywords'), - 'description' => t("A comma-separated list of keywords about the page. This meta tag is <em>not</em> supported by most search engines."), + 'description' => t("A comma-separated list of keywords about the page. This meta tag is <em>not</em> used by most search engines."), 'class' => 'DrupalTextMetaTag', ); - $info['tags']['title'] = array( - 'label' => t('Title'), - 'description' => t("The text to display in the title bar of a visitor's web browser when they view this page. This meta tag may also be used as the title of the page when a visitor bookmarks or favorites this page."), - 'class' => 'DrupalTitleMetaTag', - ); // More advanced meta tags. $info['tags']['robots'] = array( @@ -129,15 +145,26 @@ function metatag_metatag_info() { 'class' => 'DrupalListMetaTag', 'form' => array( '#options' => array( + 'index' => t('Allow search engines to index this page (assumed).'), + 'follow' => t('Allow search engines to follow links on this page (assumed).'), 'noindex' => t('Prevent search engines from indexing this page.'), 'nofollow' => t('Prevent search engines from following links on this page.'), 'noarchive' => t('Prevent a cached copy of this page from being available in the search results.'), 'nosnippet' => t('Prevents a description from appearing below the page in the search results, as well as prevents caching of the page.'), 'noodp' => t('Blocks the <a href="@odp-url">Open Directory Project</a> description of the page from being used in the description that appears below the page in the search results.', array('@odp-url' => 'http://www.dmoz.org/')), + 'noydir' => t('Prevents Yahoo! from listing this page in the <a href="@ydir">Yahoo! Directory</a>.', array('@ydir' => 'http://dir.yahoo.com/')), ), ), 'group' => 'advanced', ); + + $info['tags']['news_keywords'] = array( + 'label' => t('Google News Keywords'), + 'description' => t('A comma-separated list of keywords about the page. This meta tag is used as an indicator in <a href="@google_news">Google News</a>.', array('@google_news' => 'http://support.google.com/news/publisher/bin/answer.py?hl=en&answer=68297')), + 'class' => 'DrupalTextMetaTag', + 'group' => 'advanced', + ); + $info['tags']['generator'] = array( 'label' => t('Generator'), 'description' => t("Describes the name and version number of the software or publishing tool used to create the page."), @@ -146,6 +173,7 @@ function metatag_metatag_info() { 'context' => array('global'), 'group' => 'advanced', ); + $info['tags']['copyright'] = array( 'label' => t('Copyright'), 'description' => t("Details a copyright, trademark, patent, or other information that pertains to intellectual property about this page. Note that this will not automatically protect your site's content or your intellectual property."), @@ -153,19 +181,27 @@ function metatag_metatag_info() { 'group' => 'advanced', ); - // Link tags. + $info['tags']['image_src'] = array( + 'label' => t('Image'), + 'description' => t("An image associated with this page, for use as a thumbnail in social networks and other services."), + 'class' => 'DrupalLinkMetaTag', + 'group' => 'advanced', + ); + $info['tags']['canonical'] = array( 'label' => t('Canonical URL'), 'description' => t("Tells search engines where the preferred location or URL is for this page to help eliminate self-created duplicate content for search engines."), 'class' => 'DrupalLinkMetaTag', 'group' => 'advanced', ); + $info['tags']['shortlink'] = array( 'label' => t('Shortlink URL'), 'description' => '', 'class' => 'DrupalLinkMetaTag', 'group' => 'advanced', ); + $info['tags']['publisher'] = array( 'label' => t('Publisher URL'), 'description' => '', @@ -173,5 +209,20 @@ function metatag_metatag_info() { 'group' => 'advanced', ); + $info['tags']['author'] = array( + 'label' => t('Author URL'), + 'description' => "Used by some search engines to confirm authorship of the content on a page. Should be either the full URL for the author's Google+ profile page or a local page with information about the author.", + 'class' => 'DrupalLinkMetaTag', + 'group' => 'advanced', + ); + + $info['tags']['original-source'] = array( + 'label' => t('Original Source'), + 'description' => '', + 'class' => 'DrupalTextMetaTag', + 'group' => 'advanced', + 'description' => "Used to indicate the URL that broke the story, and can link to either an internal URL or an external source. If the full URL is not known it is acceptable to use a partial URL or just the domain name.", + ); + return $info; } diff --git a/sites/all/modules/metatag/metatag.migrate.inc b/sites/all/modules/metatag/metatag.migrate.inc new file mode 100644 index 0000000000000000000000000000000000000000..b189ef001f48f4803be31ef0200bca5d269aebf0 --- /dev/null +++ b/sites/all/modules/metatag/metatag.migrate.inc @@ -0,0 +1,53 @@ +<?php + +/** + * @file + * Metatag support for migrate. + */ + +/** + * Implements hook_migrate_api(). + */ +function metatag_migrate_api() { + return array('api' => 2); +} + +/** + * Metatags destination handler. + */ +class MigrateMetatagHandler extends MigrateDestinationHandler { + + public function __construct() { + $this->registerTypes(array('node', 'user', 'taxonomy_term')); + } + + /** + * Implements MigrateDestinationHandler::fields(). + */ + public function fields() { + $fields = array(); + $elements = metatag_get_info(); + + foreach ($elements['tags'] as $value) { + $metatag_field = 'metatag_' . $value['name']; + $fields[$metatag_field] = $value['description']; + } + + return $fields; + } + + /** + * Implements MigrateDestinationHandler::prepare(). + */ + public function prepare($entity, stdClass $row) { + $elements = metatag_get_info(); + + foreach ($elements['tags'] as $value) { + $metatag_field = 'metatag_' . $value['name']; + if (isset($entity->$metatag_field)) { + $entity->metatags[$value['name']]['value'] = $entity->$metatag_field; + unset($entity->$metatag_field); + } + } + } +} diff --git a/sites/all/modules/metatag/metatag.module b/sites/all/modules/metatag/metatag.module index 9899f5e4e96c08864f818981d2b99839cdc65d18..6f0a4933e2e78b9eac94cfd6318b91b6899d4a0b 100644 --- a/sites/all/modules/metatag/metatag.module +++ b/sites/all/modules/metatag/metatag.module @@ -2,9 +2,24 @@ /** * @todo Add revisionable support for metatag data. - * @todo Add multilingual support for metatag data - is this even needed? */ +/** + * Implements hook_help(). + */ +function metatag_help($path, $arg) { + if ($path == 'admin/config/search/metatags') { + return '<p>' . t('To view a summary of the default meta tags and the inheritance, click on a meta tag type.') . '</p>'; + } + elseif ($path == 'admin/help#metatag') { + return '<p>' . t('The Meta tags module provides a options to let each page have customized meta data added to the "meta" tags in the HEAD section of the document.') . '</p>'; + } + elseif ($path == 'admin/config/search/metatags/bulk-revert') { + return '<p>' . t('This form <strong>will wipe out</strong> all custom meta tags for the selected entities, reverting them to the default configuration assigned at the <a href="@url">Defaults tab</a>. For example, if the meta tags are changed for an article they will be removed if the "Node: Article" checkbox is selected.', array('@url' => url('admin/config/search/metatags'))) . '</p>'; + } + +} + /** * Implements hook_theme(). */ @@ -43,38 +58,34 @@ function metatag_ctools_plugin_api($owner, $api) { */ function metatag_hook_info() { $hooks = array( - 'metatag_info', - 'metatag_info_alter', + 'metatag_config_default', + 'metatag_config_default_alter', + 'metatag_config_delete', + 'metatag_config_insert', 'metatag_config_instance_info', 'metatag_config_instance_info_alter', 'metatag_config_load', - 'metatag_config_presave', - 'metatag_config_insert', + 'metatag_config_load_presave', 'metatag_config_update', - 'metatag_config_delete', - 'metatag_load', - 'metatag_insert', - 'metatag_update', - 'metatag_delete', - 'metatag_alter', - 'metatag_config_default', - 'metatag_config_default_alter', - 'metatag_api', + 'metatag_info', + 'metatag_info_alter', ); return array_fill_keys($hooks, array('group' => 'metatag')); } /** - * Implements hook_permisson(). + * Implements hook_permission(). */ function metatag_permission() { $permissions['administer meta tags'] = array( 'title' => t('Administer meta tags.'), 'restrict access' => TRUE, + 'description' => t('Control the main settings pages and modify per-object meta tags.'), ); $permissions['edit meta tags'] = array( - 'title' => t('Edit meta tags.'), + 'title' => t('Edit meta tags'), + 'description' => t('Modify meta tags on individual entity records (nodes, terms, users, etc).'), ); return $permissions; } @@ -158,6 +169,15 @@ function metatag_menu() { 'type' => MENU_LOCAL_TASK, 'weight' => 10, ); + $items['admin/config/search/metatags/bulk-revert'] = array( + 'title' => 'Bulk revert', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('metatag_bulk_revert_form'), + 'access arguments' => array('administer meta tags'), + 'type' => MENU_LOCAL_TASK, + 'weight' => 30, + 'file' => 'metatag.admin.inc', + ); return $items; } @@ -179,18 +199,18 @@ function metatag_flush_caches() { * The levels of defaults is arranged by splitting the $instance variable by * the colon character, and always using a 'global' instance at the end. */ -function metatag_config_load_with_defaults($instance) { +function metatag_config_load_with_defaults($instance, $include_global = TRUE) { $defaults = &drupal_static(__FUNCTION__, array()); - // Check to see if - if (!isset($defaults[$instance])) { - $cid = "config:{$instance}"; + // Statically cache defaults since they can include multiple levels. + $cid = "config:{$instance}" . ($include_global ? ':withglobal' : ':withoutglobal'); + if (!isset($defaults[$cid])) { if ($cache = cache_get($cid, 'cache_metatag')) { - $defaults[$instance] = $cache->data; + $defaults[$cid] = $cache->data; } else { - $defaults[$instance] = array(); - $instances = metatag_config_get_parent_instances($instance); + $defaults[$cid] = array(); + $instances = metatag_config_get_parent_instances($instance, $include_global); $configs = metatag_config_load_multiple($instances); foreach ($instances as $key) { // Ignore disabled configurations. @@ -199,14 +219,14 @@ function metatag_config_load_with_defaults($instance) { } // Add config to the defaults array. - $defaults[$instance] += $configs[$key]->config; + $defaults[$cid] += $configs[$key]->config; } - cache_set($cid, $defaults[$instance], 'cache_metatag'); + cache_set($cid, $defaults[$cid], 'cache_metatag'); } } - return $defaults[$instance]; + return $defaults[$cid]; } /** @@ -231,15 +251,22 @@ function metatag_config_load_multiple(array $instances) { function metatag_config_save($config) { $config->is_new = empty($config->cid); - // Allow modules to alter the configuration before it is saved. + // Allow modules to alter the configuration before it is saved using + // hook_metatag_config_presave(). module_invoke_all('metatag_config_presave', $config); if ($config->is_new) { drupal_write_record('metatag_config', $config); + + // Allow modules to act upon the record insertion using + // hook_metatag_config_insert(). module_invoke_all('metatag_config_insert', $config); } else { drupal_write_record('metatag_config', $config, array('cid')); + + // Allow modules to act upon the record update using + // hook_metatag_config_insert(). module_invoke_all('metatag_config_update', $config); } @@ -267,62 +294,134 @@ function metatag_config_delete($instance) { function metatag_config_cache_clear() { cache_clear_all('*', 'cache_metatag', TRUE); drupal_static_reset('metatag_config_load_with_defaults'); + drupal_static_reset('metatag_entity_has_metatags'); + drupal_static_reset('metatag_entity_supports_metatags'); ctools_include('export'); ctools_export_load_object_reset('metatag_config'); } -function metatag_metatags_load($type, $id) { - $metatags = metatag_metatags_load_multiple($type, array($id)); +/** + * Load an entity's tags. + * + * @param $entity_type + * The entity type to load + * @param $entity_id + * The ID of the entity to load + * @return + * An array of tag data keyed by language. + */ +function metatag_metatags_load($entity_type, $entity_id) { + $metatags = metatag_metatags_load_multiple($entity_type, array($entity_id)); return !empty($metatags) ? reset($metatags) : array(); } -function metatag_metatags_load_multiple($type, array $ids) { +/** + * Load tags for multiple entities. + * + * @param $entity_type + * The entity type to load + * @param $entity_ids + * The list of entity IDs + * @return + * An array of tag data, keyed by ID. + */ +function metatag_metatags_load_multiple($entity_type, array $entity_ids) { // Double check entity IDs are numeric thanks to Entity API module. - $ids = array_filter($ids, 'is_numeric'); - if (empty($ids)) { + $entity_ids = array_filter($entity_ids, 'is_numeric'); + if (empty($entity_ids)) { return array(); } // Also need to check if the metatag table exists since this condition could // fire before the table has been installed yet. - if (!db_table_exists('metatag')) { - return array(); + if (!variable_get('metatag_schema_installed', FALSE)) { + if (db_table_exists('metatag')) { + variable_set('metatag_schema_installed', TRUE); + } + else { + watchdog('metatag', 'The system tried to load metatag data before the schema was fully loaded.', array(), WATCHDOG_WARNING); + return array(); + } + } + + // Get all translations of tag data for this entity. + $result = db_query("SELECT entity_id, data, language FROM {metatag} WHERE (entity_type = :type) AND (entity_id IN (:ids))", array( + ':type' => $entity_type, + ':ids' => $entity_ids, + )); + + // Marshal it into an array keyed by entity ID. Each value is an array of + // translations keyed by language code. + $metatags = array(); + while ($record = $result->fetchObject()) { + $metatags[$record->entity_id][$record->language] = unserialize($record->data); } - $metatags = db_query("SELECT entity_id, data FROM {metatag} WHERE entity_type = :type AND entity_id IN (:ids)", array( - ':type' => $type, - ':ids' => $ids, - ))->fetchAllKeyed(); - $metatags = array_map('unserialize', $metatags); return $metatags; } -function metatag_metatags_save($type, $id, $metatags) { - // Check that $id is numeric because of Entity API and string IDs. - if (!is_numeric($id)) { +/** + * Save an entity's tags. + * + * @param $entity_type + * The entity type to load + * @param $entity_id + * The entity's ID + * @param $metatags + * All of the tag information + * @param $language + * The language of the translation set + */ +function metatag_metatags_save($entity_type, $entity_id, $metatags, $language) { + // If no language assigned, use the has-no-language language. + if (!$language) { + $language = LANGUAGE_NONE; + } + + // Check that $entity_id is numeric because of Entity API and string IDs. + if (!is_numeric($entity_id)) { return; } - // Allow other modules to alter the metatags prior to saving. + // Certain modules, e.g. Workbench Moderation, will cause the data to be in + // an unsupported format; the problem needs to be resolved elsewhere so this + // can only be considered a temporary fix. + // TODO: Solve the core problem, which will probably entail something + // similar to http://drupal.org/node/1876034. + if (isset($metatags[$language])) { + // There are certain occasions when the old data and the new data are + // *both* added to the $metatags array, in this case throw away the language + // data. + $lang_data = $metatags[$language]; + unset($metatags[$language]); + if (empty($metatags)) { + $metatags = $lang_data; + } + } + + // Allow other modules to alter the meta tags prior to saving using + // hook_metatag_presave(). foreach (module_implements('metatag_presave') as $module) { $function = "{$module}_metatag_presave"; - $function($metatags, $type, $id); + $function($metatags, $entity_type, $entity_id, $language); } if (empty($metatags)) { // If the data array is empty, there is no data to actually save, so // just delete the record from the database. db_delete('metatag') - ->condition('entity_type', $type) - ->condition('entity_id', $id) + ->condition('entity_type', $entity_type) + ->condition('entity_id', $entity_id) + ->condition('language', $language) ->execute(); } else { // Otherwise save the data for this entity. db_merge('metatag') ->key(array( - 'entity_type' => $type, - 'entity_id' => $id, + 'entity_type' => $entity_type, + 'entity_id' => $entity_id, + 'language' => $language, )) ->fields(array( 'data' => serialize($metatags), @@ -331,30 +430,61 @@ function metatag_metatags_save($type, $id, $metatags) { } // Clear cached data. - metatag_metatags_cache_clear($type, $id); + metatag_metatags_cache_clear($entity_type, $entity_id); } -function metatag_metatags_delete($type, $id) { - return metatag_metatags_delete_multiple($type, array($id)); +/** + * Delete an entity's tags. + * + * @param $entity_type + * The entity type + * @param $entity_id + * The entity's ID + * @param $langcode + * The language ID of the entry to delete. If left blank, all language + * entries for this entity will be deleted. + */ +function metatag_metatags_delete($entity_type, $entity_id, $langcode = NULL) { + return metatag_metatags_delete_multiple($entity_type, array($entity_id), $langcode); } -function metatag_metatags_delete_multiple($type, array $ids) { +/** + * Delete multiple entities' tags. + * + * @param $entity_type + * The entity type + * @param $entity_ids + * The list of IDs + * @param $langcode + * The language ID of the entities to delete. If left blank, all language + * entries for the enities will be deleted. + */ +function metatag_metatags_delete_multiple($entity_type, array $entity_ids, $langcode = NULL) { // Double check entity IDs are numeric thanks to Entity API module. - $ids = array_filter(array_keys($ids, 'is_numeric')); + $entity_ids = array_filter($entity_ids, 'is_numeric'); - if ($metatags = metatag_metatags_load_multiple($type, $ids)) { + if ($metatags = metatag_metatags_load_multiple($entity_type, $entity_ids)) { $transaction = db_transaction(); try { - // Let other modules know about the metatags being deleted. - module_invoke_all('metatag_metatags_delete', $type, $ids); + // Let other modules know about the records being deleted using + // hook_metatag_metatags_delete(). + module_invoke_all('metatag_metatags_delete', $entity_type, $entity_ids, $langcode); + + // Set the entity to delete. + $query = db_delete('metatag') + ->condition('entity_type', $entity_type) + ->condition('entity_id', $entity_ids, 'IN'); + + // Specify a language if there is one. + if ($langcode) { + $query->condition('language', $langcode); + } - db_delete('metatag') - ->condition('entity_type', $type) - ->condition('entity_id', $ids, 'IN') - ->execute(); + // Perform the deletion(s). + $query->execute(); // Clear cached data. - metatag_metatags_cache_clear($type, $ids); + metatag_metatags_cache_clear($entity_type, $entity_ids); } catch (Exception $e) { $transaction->rollback(); @@ -364,14 +494,14 @@ function metatag_metatags_delete_multiple($type, array $ids) { } } -function metatag_metatags_cache_clear($type, $id = NULL) { - if (empty($id)) { - cache_clear_all("output:$type", 'cache_metatag', TRUE); +function metatag_metatags_cache_clear($entity_type, $entity_id = NULL) { + if (empty($entity_id)) { + cache_clear_all("output:$entity_type", 'cache_metatag', TRUE); } else { - $ids = (array) $id; - foreach ($ids as $id) { - cache_clear_all("output:$type:$id", 'cache_metatag', TRUE); + $entity_ids = (array) $entity_id; + foreach ($entity_ids as $entity_id) { + cache_clear_all("output:$entity_type:$entity_id", 'cache_metatag', TRUE); } } } @@ -379,10 +509,23 @@ function metatag_metatags_cache_clear($type, $id = NULL) { /** * Implements hook_entity_load(). */ -function metatag_entity_load($entities, $type) { - $metatags = metatag_metatags_load_multiple($type, array_keys($entities)); - foreach ($entities as $id => $entity) { - $entities[$id]->metatags = isset($metatags[$id]) ? $metatags[$id] : array(); +function metatag_entity_load($entities, $entity_type) { + // Wrap this in a try-catch block to work around occasions when the schema + // hasn't been updated yet. + try { + if (metatag_entity_supports_metatags($entity_type)) { + $metatags = metatag_metatags_load_multiple($entity_type, array_keys($entities)); + foreach ($entities as $entity_id => $entity) { + $entities[$entity_id]->metatags = isset($metatags[$entity_id]) ? $metatags[$entity_id] : array(); + } + } + } + catch (Exception $e) { + watchdog('metatag', 'Error loading meta tag data, do the <a href="@update">database updates</a> need to be run? The error occurred when loading record(s) %ids for the %type entity type. The error message was: %error', array('@update' => base_path() . 'update.php', '%ids' => implode(', ', array_keys($entities)), '%type' => $entity_type, '%error' => $e->getMessage()), WATCHDOG_CRITICAL); + // Don't display the same message twice for Drush. + if (php_sapi_name() != 'cli') { + drupal_set_message(t('Error loading meta tag data, do the <a href="@update">database updates</a> need to be run?', array('@update' => base_path() . 'update.php')), 'error'); + } } } @@ -391,8 +534,12 @@ function metatag_entity_load($entities, $type) { */ function metatag_entity_insert($entity, $entity_type) { if (isset($entity->metatags)) { - list($id) = entity_extract_ids($entity_type, $entity); - metatag_metatags_save($entity_type, $id, $entity->metatags); + list($entity_id) = entity_extract_ids($entity_type, $entity); + + // Determine the entity's language. + $language = metatag_entity_get_language($entity_type, $entity); + + metatag_metatags_save($entity_type, $entity_id, $entity->metatags, $language); } } @@ -400,14 +547,38 @@ function metatag_entity_insert($entity, $entity_type) { * Implements hook_entity_update(). */ function metatag_entity_update($entity, $entity_type) { - list($id) = entity_extract_ids($entity_type, $entity); + if (!metatag_entity_supports_metatags($entity_type)) { + return; + } + + list($entity_id) = entity_extract_ids($entity_type, $entity); if (isset($entity->metatags)) { - metatag_metatags_save($entity_type, $id, $entity->metatags); + // Determine the entity's language. + $new_language = metatag_entity_get_language($entity_type, $entity); + + // Determine the language for this entity object. + if (isset($entity->original)) { + $old_language = metatag_entity_get_language($entity_type, $entity->original); + + // If the language has changed then remove the old one. When a new + // translation is being saved using Entity Translation both values will + // be the same, so this is safe to do. + if ($old_language != $new_language) { + db_delete('metatag') + ->condition('entity_type', $entity_type) + ->condition('entity_id', $entity_id) + ->condition('language', $old_language) + ->execute(); + } + } + + // Save the record. + metatag_metatags_save($entity_type, $entity_id, $entity->metatags, $new_language); } else { // Still ensure the meta tag output is cached. - metatag_metatags_cache_clear($entity_type, $id); + metatag_metatags_cache_clear($entity_type, $entity_id); } } @@ -415,8 +586,8 @@ function metatag_entity_update($entity, $entity_type) { * Implements hook_entity_delete(). */ function metatag_entity_delete($entity, $entity_type) { - list($id) = entity_extract_ids($entity_type, $entity); - metatag_metatags_delete($entity_type, $id); + list($entity_id) = entity_extract_ids($entity_type, $entity); + metatag_metatags_delete($entity_type, $entity_id); } /** @@ -427,52 +598,117 @@ function metatag_field_attach_delete_revision($entity_type, $entity) { } /** - * Implements hook_field_attach_view_alter(). + * Implements hook_taxonomy_term_view_alter(). */ -function metatag_field_attach_view_alter(&$output, $context) { - $entity_type = $context['entity_type']; - $entity = $context['entity']; - list($entity_id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entity); +function metatag_taxonomy_term_view_alter(&$build, &$entity_type) { + // This is only needed if hook_entity_view has not been added to core. + // @see http://drupal.org/node/1067120 + if (isset($build['#term']) && !function_exists('taxonomy_term_view_multiple')) { + $entity = taxonomy_term_load($build['#term']->tid); + metatag_entity_view($entity, $entity_type, 'full', NULL); + } +} + +/** + * Implements hook_entity_view(). + */ +function metatag_entity_view($entity, $entity_type, $view_mode, $langcode) { + // Only run this function once per page load. + static $i_will_say_this_only_once = FALSE; + + // Only proceed if this entity object is the page being viewed. + if (_metatag_entity_is_page($entity_type, $entity)) { + // Only run this function once per page load. + if ($i_will_say_this_only_once) { + return; + } + $i_will_say_this_only_once = TRUE; + + // If this entity object isn't allowed meta tags, skip it. + if (!metatag_entity_has_metatags($entity_type, $entity)) { + return; + } + + // Obbtain some details of the entity that are needed elsewhere. + list($entity_id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entity); + $instance = "{$entity_type}:{$bundle}"; - if (metatag_entity_supports_metatags($entity_type, $bundle) && $context['view_mode'] == 'full' && _metatag_entity_is_page($entity_type, $entity)) { + // Determine the language this entity actually uses. + $entity_language = metatag_entity_get_language($entity_type, $entity); + + // The requested language is different to the entity's language, look for + // a language elsewhere. + if ($entity_language != $langcode) { + // If no language was defined for the entity then use that for the + if ($entity_language == LANGUAGE_NONE) { + $langcode = LANGUAGE_NONE; + } + else { + $enabled_languages = field_content_languages(); + foreach (field_language($entity_type, $entity) as $field => $lang) { + // Only accept actual language values that are properly enabled. + if ($lang != LANGUAGE_NONE && in_array($lang, $enabled_languages)) { + $langcode = $lang; + } + } + } + } + + // All applicable pieces for this current page. $cid_parts = array( - //'view_mode' => $context['view_mode'], - 'langcode' => $context['language'], - 'url' => $GLOBALS['base_url'] . '/' . current_path(), + 'entity_type' => $entity_type, + 'bundle' => $bundle, + 'entity_id' => $entity_id, + 'view_mode' => $view_mode, + 'langcode' => $langcode, + 'url' => $GLOBALS['base_url'] . base_path() . current_path(), ); - $cid = "output:{$entity_type}:{$entity_id}:" . hash('sha256', serialize($cid_parts)); + + // Allow each page in a sequence to have different values. + if (isset($_GET['page'])) { + $cid_parts['page'] = $_GET['page']; + } + + // Allow other modules to alter the page parts using + // hook_metatag_page_cache_cid_parts_alter(). + drupal_alter('metatag_page_cache_cid_parts', $cid_parts); + + $cid = "output:{$entity_type}:{$entity_id}:{$langcode}:" . hash('sha256', serialize($cid_parts)); if ($cache = cache_get($cid, 'cache_metatag')) { - $output['metatags'] = $cache->data; + $output = $cache->data; } else { + // Separate the meta tags. $metatags = isset($entity->metatags) ? $entity->metatags : array(); - $instance = "{$entity_type}:{$bundle}"; - // Build options for meta tag rendering. The context variable already - // contains entity type, entity, view mode, language, etc. - $options = $context; + // Build options for meta tag rendering. + $options = array( + 'entity' => $entity, + 'entity_type' => $entity_type, + 'view_mode' => $view_mode, + ); // Ensure we actually pass a language object rather than language code. $languages = language_list(); - if (isset($context['language']) && isset($languages[$context['language']])) { - $options['language'] = $languages[$context['language']]; + if (isset($languages[$langcode])) { + $options['language'] = $languages[$langcode]; } - // Reload the entity object from cache as it may have been altered by Panels. + // Reload the entity object from cache as it may have been altered. $token_type = token_get_entity_mapping('entity', $entity_type); $entities = entity_load($entity_type, array($entity_id)); $options['token data'][$token_type] = $entities[$entity_id]; $options['entity'] = $entities[$entity_id]; // Render the metatags and save to the cache. - $output['metatags'] = metatag_metatags_view($instance, $metatags, $options); - cache_set($cid, $output['metatags'], 'cache_metatag'); + $output = metatag_metatags_view($instance, $metatags, $options); + cache_set($cid, $output, 'cache_metatag'); } - // We have to add a '#field_type' property otherwise - // rdf_field_attach_view_alter() freaks out. - $output['metatags']['#field_type'] = NULL; + // We need to register the term's metatags, so we can later fetch them. + // @see metatag_page_build(). + metatag_page_set_metatags($instance, $output); } } @@ -480,9 +716,10 @@ function metatag_field_attach_view_alter(&$output, $context) { * Build a renderable array of meta tag output. * * @param string $instance - * The configuration instance key of the metatags to use, e.g. "node:article". + * The configuration instance key of the meta tags to use, e.g. + * "node:article". * @param array $metatags - * An arary of metatag data. + * An array of meta tag data. * @param array $options * (optional) An array of options including the following keys and values: * - language: A language object. @@ -491,7 +728,6 @@ function metatag_field_attach_view_alter(&$output, $context) { */ function metatag_metatags_view($instance, array $metatags = array(), array $options = array()) { $output = array(); - $metatags += metatag_config_load_with_defaults($instance); // Convert language codes to a language object. if (isset($options['language']) && is_string($options['language'])) { @@ -499,30 +735,58 @@ function metatag_metatags_view($instance, array $metatags = array(), array $opti $options['language'] = isset($languages[$options['language']]) ? $languages[$options['language']] : NULL; } + // If there are any tags, determine the translation to display. + if (!empty($metatags)) { + // Get the display language; default to the entity's language. + if (isset($options['language']) && isset($options['language']->language) && isset($metatags[$options['language']->language])) { + $metatags = $metatags[$options['language']->language]; + } + // If no language requested, use the no-language value. + elseif (!empty($metatags[LANGUAGE_NONE])) { + $metatags = $metatags[LANGUAGE_NONE]; + } + else { + $metatags = array(); + } + } + + // Add any default tags to the mix. + $metatags += metatag_config_load_with_defaults($instance); + foreach ($metatags as $metatag => $data) { if ($metatag_instance = metatag_get_instance($metatag, $data)) { $output[$metatag] = $metatag_instance->getElement($options); } } - drupal_alter('metatag_metatags_view', $output); + // Allow the output meta tags to be modified using + // hook_metatag_metatags_view_alter(). + drupal_alter('metatag_metatags_view', $output, $instance); return $output; } function metatag_metatags_values($instance, array $metatags = array(), array $options = array()) { $values = array(); - $metatags += metatag_config_load_with_defaults($instance); - // Convert language codes to a language object. - if (isset($options['language']) && is_string($options['language'])) { - $languages = language_list(); - $options['language'] = isset($languages[$options['language']]) ? $languages[$options['language']] : NULL; + // Apply defaults to the data for each language. + foreach ($metatags as $language => $metatag) { + $metatags[$language] += metatag_config_load_with_defaults($instance); } - foreach ($metatags as $metatag => $data) { - if ($metatag_instance = metatag_get_instance($metatag, $data)) { - $values[$metatag] = $metatag_instance->getValue($options); + // Generate output only if we have a valid language. + $language = $options['language']; + if (isset($language) && is_string($language) && isset($metatags[$language])) { + + // Convert language codes to a language object. + $languages = language_list(); + $options['language'] = isset($languages[$language]) ? $languages[$language] : NULL; + + // Get output elements. + foreach ($metatags[$language] as $metatag => $data) { + if ($metatag_instance = metatag_get_instance($metatag, $data)) { + $values[$metatag] = $metatag_instance->getValue($options); + } } } @@ -537,7 +801,7 @@ function metatag_metatags_values($instance, array $metatags = array(), array $op * @param string $instance * The configuration instance key of the metatags to use, e.g. "node:article". * @param array $metatags - * An arary of metatag data. + * An array of metatag data. * @param array $options * (optional) An array of options including the following keys and values: * - token types: An array of token types to be passed to theme_token_tree(). @@ -557,11 +821,12 @@ function metatag_metatags_form(array &$form, $instance, array $metatags = array( $form['metatags'] = array( '#type' => 'fieldset', '#title' => t('Meta tags'), + '#multilingual' => TRUE, '#collapsible' => TRUE, '#collapsed' => TRUE, '#tree' => TRUE, '#access' => user_access('edit meta tags') || user_access('administer meta tags'), - '#weight' => 10, + '#weight' => 40, '#attributes' => array( 'class' => array('metatags-form'), ), @@ -628,7 +893,8 @@ function metatag_metatags_form(array &$form, $instance, array $metatags = array( $form['metatags']['tokens'] = array( '#theme' => 'token_tree', '#token_types' => $options['token types'], - '#weight' => 100, + '#weight' => 999, + '#dialog' => TRUE, ); // Add a submit handler to compare the submitted values against the deafult @@ -657,7 +923,7 @@ function metatag_field_extra_fields() { $extra[$entity_type][$bundle]['form']['metatags'] = array( 'label' => t('Meta tags'), 'description' => t('Meta tag module form elements.'), - 'weight' => 10, + 'weight' => 40, ); } } @@ -665,55 +931,96 @@ function metatag_field_extra_fields() { 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 isset($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. + */ function metatag_entity_supports_metatags($entity_type = NULL, $bundle = NULL) { - $types = &drupal_static(__FUNCTION__); + $entity_types = &drupal_static(__FUNCTION__); - if (!isset($types)) { - $types = array(); + if (!isset($entity_types)) { + $entity_types = array(); foreach (entity_get_info() as $entity_type_key => $entity_info) { - if (!isset($entity_info['metatags'])) { - // By default allow entities that have fields and have paths. - $entity_info['metatags'] = !empty($entity_info['uri callback']) && !empty($entity_info['fieldable']); - } if (empty($entity_info['metatags'])) { - $types[$entity_type_key] = FALSE; + $entity_types[$entity_type_key] = FALSE; continue; } - $types[$entity_type_key] = array(); + $entity_types[$entity_type_key] = array(); foreach ($entity_info['bundles'] as $bundle_key => $bundle_info) { - $types[$entity_type_key][$bundle_key] = !isset($bundle_info['metatags']) || !empty($bundle_info['metatags']); + $entity_types[$entity_type_key][$bundle_key] = !isset($bundle_info['metatags']) || !empty($bundle_info['metatags']); } } } if (isset($entity_type) && isset($bundle)) { - return isset($types[$entity_type][$bundle]) ? $types[$entity_type][$bundle] : FALSE; + return isset($entity_types[$entity_type][$bundle]) ? $entity_types[$entity_type][$bundle] : FALSE; } elseif (isset($entity_type)) { - return isset($types[$entity_type]) ? ($types[$entity_type] !== FALSE) : FALSE; + return isset($entity_types[$entity_type]) ? ($entity_types[$entity_type] !== FALSE) : FALSE; } - return $types; + return $entity_types; } /** * Implements hook_entity_info_alter(). + * + * Enables Metatag support for the core entities. */ function metatag_entity_info_alter(&$info) { $defaults['node'] = array( 'path' => 'node/%node', + 'metatags' => TRUE, ); $defaults['taxonomy_term'] = array( 'path' => 'taxonomy/term/%taxonomy_term', + 'metatags' => TRUE, ); if (module_exists('forum') && ($vid = variable_get('forum_nav_vocabulary', 0)) && $vocabulary = taxonomy_vocabulary_load($vid)) { $defaults['taxonomy_term']['bundles'][$vocabulary->machine_name]['path'] = 'forum/%taxonomy_term'; } $defaults['user'] = array( 'path' => 'user/%user', - ); - $defaults['comment'] = array( - 'metatags' => FALSE, + 'metatags' => TRUE, ); foreach ($defaults as $key => $entity_defaults) { @@ -756,36 +1063,122 @@ function metatag_load_entity_from_path($path) { } } + // 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; } +/** + * Add meta tags to be added later with metatag_page_build(). + * + * @param string $instance + * The configuration instance key of the meta tags, e.g. "node:article". + * @param array $metatags + * An array of meta tags from metatag_metatags_view(). + */ +function metatag_page_set_metatags($instance, $metatags) { + $page_metatags = &drupal_static(__FUNCTION__, array()); + $page_metatags[$instance] = $metatags; +} + +/** + * Retrieve the array of met tags to be added with metatag_page_build(). + */ +function metatag_page_get_metatags() { + // @todo Add alter to this result? + return drupal_static('metatag_page_set_metatags', array()); +} + /** * Implements hook_page_build(). */ function metatag_page_build(&$page) { - // For some reason with Overlay enabled we get an empty $page, so just fail - // this case. - if (!isset($page['content'])) { - return; + // Ensure these arrays exist, otherwise several use cases will fail. + if (!isset($page['content']) || !is_array($page['content'])) { + $page['content'] = array(); } + if (!isset($page['content']['metatags']) || !is_array($page['content']['metatags'])) { + $page['content']['metatags'] = array(); + } + + // The front page has special consideration. + $instance = 'global:frontpage'; + if (drupal_is_front_page() && metatag_config_is_enabled($instance)) { + $instance = 'global:frontpage'; - // Load the metatags render array in before any page content so that more - // more specific meta tags in the page content can override these meta tags. - $page['content'] = array('metatags' => array()) + $page['content']; - if (drupal_is_front_page()) { - $page['content']['metatags']['global:frontpage'] = metatag_metatags_view('global:frontpage', array()); + // These two parts are sufficient given that the homepage is unique. + $cid_parts = array( + 'langcode' => $GLOBALS['language_content']->language, + 'url' => $GLOBALS['base_url'] . base_path() . '<front>', + ); + + // Allow each page in a sequence to have different values. + if (isset($_GET['page'])) { + $cid_parts['page'] = $_GET['page']; + } + + // Allow other modules to customize the data using + // hook_metatag_page_cache_cid_parts_alter(). + drupal_alter('metatag_page_cache_cid_parts', $cid_parts); + + $cid = "output:{$instance}:" . hash('sha256', serialize($cid_parts)); + + if ($cache = cache_get($cid, 'cache_metatag')) { + $metatags = $cache->data; + } + else { + $metatags = metatag_metatags_view($instance, array()); + cache_set($cid, $metatags, 'cache_metatag'); + } + + $page['content']['metatags'][$instance] = $metatags; + } + + // Load any meta tags assigned via metatag_page_set_metatags(). Note: this + // must include the necessary defaults. + else { + $page['content']['metatags'] += metatag_page_get_metatags(); } - elseif (!path_is_admin(current_path())) { - // Do not output the global metatags when on an administration path. - $page['content']['metatags']['global'] = metatag_metatags_view('global', array()); + + // If no meta tags were loaded, and this is not an admin path, at least load + // the global defaults. This may be disabled, see README.txt for details. + if (empty($page['content']['metatags']) && variable_get('metatag_load_all_pages', TRUE) && !path_is_admin(current_path())) { + $instance = 'global'; + + // These two parts are sufficient given that the homepage is unique. + $cid_parts = array( + 'langcode' => $GLOBALS['language_content']->language, + 'url' => $GLOBALS['base_url'] . $_SERVER['REQUEST_URI'], + ); + + // Allow each page in a sequence to have different values. + if (isset($_GET['page'])) { + $cid_parts['page'] = $_GET['page']; + } + + // Allow other modules to customize the data using + // hook_metatag_page_cache_cid_parts_alter(). + drupal_alter('metatag_page_cache_cid_parts', $cid_parts); + + $cid = "output:{$instance}:" . hash('sha256', serialize($cid_parts)); + + if ($cache = cache_get($cid, 'cache_metatag')) { + $metatags = $cache->data; + } + else { + $metatags = metatag_metatags_view($instance, array()); + cache_set($cid, $metatags, 'cache_metatag'); + } + $page['content']['metatags'][$instance] = $metatags; } } /** * Returns whether the current page is the page of the passed in entity. * - * @param $type + * @param $entity_type * The entity type; e.g. 'node' or 'user'. * @param $entity * The entity object. @@ -794,9 +1187,9 @@ function metatag_page_build(&$page) { * TRUE if the current page is the page of the specified entity, or FALSE * otherwise. */ -function _metatag_entity_is_page($type, $entity) { - $uri = entity_uri($type, $entity); - return (!empty($uri) && current_path() == $uri['path']); +function _metatag_entity_is_page($entity_type, $entity) { + $uri = entity_uri($entity_type, $entity); + return !empty($uri['path']) && current_path() == $uri['path']; } /** @@ -824,15 +1217,49 @@ function metatag_field_attach_delete_bundle($entity_type, $bundle) { * Implements hook_field_attach_form(). */ function metatag_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) { - list($entity_id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entity); - - if (!metatag_entity_supports_metatags($entity_type, $bundle)) { + 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}"; - $metatags = isset($entity->metatags) ? $entity->metatags : array(); + // Grab the meta tags for display in the form if there are any. + if (!empty($entity->metatags)) { + // Identify the language to use with this entity. + $entity_language = metatag_entity_get_language($entity_type, $entity); + + // If this is a new translation using Entity Translation, load the meta + // tags from the entity's original language. + if (module_exists('entity_translation') && empty($form['#entity_translation_source_form']) && ($handler = entity_translation_entity_form_get_handler($form, $form_state)) && isset($entity->metatags[$handler->getSourceLanguage()])) { + $metatags = $entity->metatags[$handler->getSourceLanguage()]; + } + // Determine from where we should get the tags. + elseif (isset($entity->metatags[$langcode])) { + // Set the tags to the translation set matching that of the form. + $metatags = $entity->metatags[$langcode]; + } + // There is no translation for this entity's tags in the current + // language. Instead, display tags in the language of the entity, the + // source language of translations. The will provide translators with the + // original text to translate. + elseif (isset($entity->metatags[$entity_language])) { + $metatags = $entity->metatags[$entity_language]; + } + // This is a preview so set the tags to the raw submission data. No + // language has been set. + else { + $metatags = $entity->metatags; + } + } + else { + $metatags = array(); + } + $options['token types'] = array(token_get_entity_mapping('entity', $entity_type)); $options['context'] = $entity_type; @@ -877,12 +1304,14 @@ function metatag_get_info($type = NULL, $name = NULL) { if (!isset($info)) { // hook_metatag_info() includes translated strings, so each language is cached // separately. - $cid = 'info:' . $GLOBALS['language']->language; + $cid = 'info:' . LANGUAGE_NONE; if ($cache = cache_get($cid, 'cache_metatag')) { $info = $cache->data; } else { + // Obtain all metatag specs defined in other modules using + // hook_metatag_info(). $info = module_invoke_all('metatag_info'); $info += array('tags' => array(), 'groups' => array()); @@ -895,8 +1324,10 @@ function metatag_get_info($type = NULL, $name = NULL) { ); } - // Let other modules alter the entity info and then cache it. + // Let other modules alter the entity info using + // hook_metatag_info_alter(). drupal_alter('metatag_info', $info); + cache_set($cid, $info, 'cache_metatag'); } } @@ -918,9 +1349,6 @@ function metatag_get_instance($metatag, array $data = array()) { $class = $info['class']; return new $class($info, $data); } - else { - trigger_error("Failed to load class {$info['class']} for metatag $metatag.", E_USER_ERROR); - } } /** @@ -1002,7 +1430,6 @@ function metatag_html_head_alter(&$elements) { foreach (array_keys($elements) as $key) { if (strpos($key, 'drupal_add_html_head_link:' . $name . ':') === 0) { unset($elements[$key]); - break; } } } @@ -1030,8 +1457,14 @@ function metatag_config_instance_info($instance = NULL) { $info = $cache->data; } else { + // Allow modules to act upon the record insertion using + // hook_metatag_config_instance_info(). $info = module_invoke_all('metatag_config_instance_info'); + + // Allow other modules to customize the data using + // hook_metatag_config_instance_info_alter(). drupal_alter('metatag_config_instance_info', $info); + cache_set($cid, $info, 'cache_metatag'); } } @@ -1101,12 +1534,20 @@ function metatag_config_instance_label($instance) { $labels = &drupal_static(__FUNCTION__, array()); if (!isset($labels[$instance])) { - $context = metatag_config_instance_info($instance); - $labels[$instance] = isset($context['label']) ? $context['label'] : t('Unknown'); - $parents = metatag_config_get_parent_instances($instance, FALSE); - array_shift($parents); - if (!empty($parents)) { - $labels[$instance] = metatag_config_instance_label(implode(':', $parents)) . ': ' . $labels[$instance]; + $instance_parts = explode(':', $instance); + $instance_part = array_pop($instance_parts); + if ($context = metatag_config_instance_info($instance)) { + $labels[$instance] = $context['label']; + } + else { + $labels[$instance] = t('Unknown (@instance)', array('@instance' => $instance_part)); + } + // Normally the following would use metatag_config_get_parent_instances() + // but since we already sliced the instance by separator and removed the + // last segment, putting the array back together gives us this instance's + // parent. + if (!empty($instance_parts)) { + $labels[$instance] = metatag_config_instance_label(implode(':', $instance_parts)) . ': ' . $labels[$instance]; } } @@ -1143,3 +1584,145 @@ function metatag_config_access($op, $config = NULL) { return FALSE; } + +/** + * Checks if a metatag configuration record is enabled. + * + * @param string $instance + * The configuration instance machine name. + * + * @return bool + * TRUE if the configuration is enabled, or FALSE otherwise. + */ +function metatag_config_is_enabled($instance, $include_defaults = FALSE, $include_global = TRUE) { + if ($include_defaults) { + return (bool) metatag_config_load_with_defaults($instance, $include_global); + } + else { + $config = metatag_config_load($instance); + return !empty($config) && empty($config->disabled); + } +} + +/** + * Wrapper around entity_language() to use LANGUAGE_NONE if the entity does not + * have a language assigned. + * + * @param $entity_type + * An entity type's machine name. + * @param $entity + * The entity to review; + * + * @return + * A string indicating the language code to be used. + */ +function metatag_entity_get_language($entity_type, $entity) { + // Determine the entity's language. + $langcode = entity_language($entity_type, $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. + // However, as the system default language can change, this could potentially + // cause data loss / confusion problems; as a result use the system "no + // language" value to avoid any potential problems. + if (empty($langcode)) { + $langcode = LANGUAGE_NONE; + } + + return $langcode; +} + +/** + * Implements of hook_features_api(). + */ +function metatag_features_api() { + $components = array( + 'metatag' => array( + 'name' => t('Meta tags'), + 'feature_source' => TRUE, + 'default_hook' => 'metatag_export_default', + 'default_file' => FEATURES_DEFAULTS_INCLUDED, + 'file' => drupal_get_path('module', 'metatag') . '/metatag.features.inc', + ), + ); + return $components; +} + +/** + * Implements hook_views_pre_render(). + */ +function metatag_views_post_render(&$view, &$output, &$cache) { + // 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') { + // Check if this is an entity display page, if so trigger + // hook_entity_view(). + foreach (entity_get_info() as $entity_name => $entity_type) { + // 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_type['path']) && ($display->display_options['path'] . $entity_name) == $entity_type['path']) { + // Only proceed if this entity type supports meta tags. + if (metatag_entity_supports_metatags($entity_name)) { + // 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_name, array($view->args[0])); + $entity = array_pop($entities); + metatag_entity_view($entity, $entity_name, 'full', NULL); + } + } + } + } + } +} + +/** + * Implements hook_ctools_render_alter(). + * + * Temporary solution to load meta tags on entity pages that are driven by + * CTools display handlers. + */ +function metatag_ctools_render_alter(&$info, $page, $context) { + // 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_name => $entity_type) { + // 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_type['path']) && $context['task']['admin path'] == $entity_type['path']) { + // Only proceed if this entity type supports meta tags. + if (metatag_entity_supports_metatags($entity_name)) { + // There must be at least one argument and the first argument must be + // numerical. + if (!empty($context['args']) && is_numeric($context['args'][0])) { + // Only the first argument is used. + $entities = entity_load($entity_name, array($context['args'][0])); + $entity = array_pop($entities); + metatag_entity_view($entity, $entity_name, 'full', NULL); + } + } + } + } + } +} + +/** + * 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) = entity_extract_ids($entity_type, $entity); + + // Delete the translation. + metatag_metatags_delete($entity_type, $entity_id, $langcode); +} diff --git a/sites/all/modules/metatag/metatag.test b/sites/all/modules/metatag/metatag.test index 2133504d79d65fbc857b56b17dee228de49548ac..ad5076eca968521647f9c0edd2c6fc325e7394e1 100644 --- a/sites/all/modules/metatag/metatag.test +++ b/sites/all/modules/metatag/metatag.test @@ -26,25 +26,27 @@ class MetaTagsUnitTest extends MetaTagsTestHelper { $defaults = metatag_config_load_with_defaults('test:foo'); $this->assertEqual($defaults, array( 'description' => array('value' => 'Test foo description'), + 'abstract' => array('value' => 'Test foo abstract'), 'title' => array('value' => 'Test altered title'), 'test:foo' => array('value' => 'foobar'), 'generator' => array('value' => 'Drupal 7 (http://drupal.org)'), + 'canonical' => array('value' => '[current-page:url:absolute]'), + 'shortlink' => array('value' => '[current-page:url:unaliased]'), )); } public function testEntitySupport() { - $test_cases[0] = array('type' => 'node', 'expected' => TRUE); $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' => 'invalid-entity', 'expected' => FALSE); foreach ($test_cases as $test_case) { $test_case += array('bundle' => NULL); - $this->assertMetatagEntitySupportsMetatags($test_case['type'], $test_case['bundle'], $test_case['expected']); + $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(); @@ -52,19 +54,43 @@ class MetaTagsUnitTest extends MetaTagsTestHelper { $test_cases[4]['expected'] = FALSE; foreach ($test_cases as $test_case) { $test_case += array('bundle' => NULL); - $this->assertMetatagEntitySupportsMetatags($test_case['type'], $test_case['bundle'], $test_case['expected']); + $this->assertMetatagEntityHasMetatags($test_case['type'], $test_case['bundle'], $test_case['expected']); } } - function assertMetatagEntitySupportsMetatags($type, $bundle, $expected) { + function assertMetatagEntityHasMetatags($entity_type, $bundle, $expected) { + $entity = entity_create_stub_entity($entity_type, array(0, NULL, $bundle)); return $this->assertEqual( - metatag_entity_supports_metatags($type, $bundle), + metatag_entity_has_metatags($entity_type, $entity), $expected, - t("metatag_entity_supports_metatags(:type, :bundle) was :expected", array( - ':type' => var_export($type, TRUE), - ':bundle' => var_export($bundle, TRUE), + 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); + } + } } diff --git a/sites/all/modules/metatag/metatag_context/README.txt b/sites/all/modules/metatag/metatag_context/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..3499605888a233f5906c94f976be17b3c33c5f13 --- /dev/null +++ b/sites/all/modules/metatag/metatag_context/README.txt @@ -0,0 +1,21 @@ +Metatag Context +--------------- +This module is provides a Metatag reaction for Context [1], thus allowing meta +tags to be assigned to specific paths and other conditions. + +Configuration can controlled via the normal Context UI module or the new admin +page available at: admin/config/search/metatags/context + + +Credits +------------------------------------------------------------------------------ +This module is based on the Context Metadata [2] module. The initial +development was by Marcin Pajdzik [3] (sponsored by Dennis Publishing [4]). + + +References +------------------------------------------------------------------------------ +1: http://drupal.org/project/context +2: http://drupal.org/project/context_metadata +3: http://drupal.org/user/160555 +4: http://www.dennis.co.uk/ diff --git a/sites/all/modules/metatag/metatag_context/metatag_context.admin.inc b/sites/all/modules/metatag/metatag_context/metatag_context.admin.inc new file mode 100644 index 0000000000000000000000000000000000000000..d776cb20682ac5db000ac75ade3863445592597c --- /dev/null +++ b/sites/all/modules/metatag/metatag_context/metatag_context.admin.inc @@ -0,0 +1,219 @@ +<?php +/** + * @file + * Admin settings page for Metatag Context. + */ + +/** + * Provides administration overview page for metatags by path settings. + */ +function metatag_context_context_overview() { + $contexts = context_enabled_contexts(TRUE); + $header = array(t('Name'), t('Paths'), t('Operations')); + $rows = array(); + + $caption = t('Values assigned here inherit from the <a href="@url" title="Edit the global default meta tags.">global defaults</a> and will override any other meta tags assigned elsewhere.', array('@url' => url('admin/config/search/metatags/config/global'))); + + foreach ($contexts as $name => $context) { + // Only show context items that are specifically selected to be "Shown on + // metatag admin page". + if (isset($context->reactions['metatag_context_reaction']['metatag_admin']) && $context->reactions['metatag_context_reaction']['metatag_admin']) { + $ops = array( + l('Edit', 'admin/config/search/metatags/context/' . $context->name, array('query' => array('destination' => 'admin/config/search/metatags/context'))), + l('Delete', 'admin/config/search/metatags/context/' . $context->name . '/delete', array('query' => array('destination' => 'admin/config/search/metatags/context'))), + ); + $rows[] = array( + $context->name, + isset($context->conditions['path']) ? implode(', ', $context->conditions['path']['values']) : t('No path condition.'), + implode(' | ', $ops), + ); + } + } + return theme('table', array('header' => $header, 'rows' => $rows, 'caption' => $caption)); +} + +function metatag_context_config_add_form($form, &$form_state) { + $form['name'] = array( + '#title' => 'Name', + '#type' => 'textfield', + '#default_value' => '', + '#description' => 'The unique ID for this metatag path context rule. This must contain only lower case letters, numbers and underscores.', + '#required' => 1, + '#maxlength' => 255, + '#element_validate' => array('metatag_context_edit_name_validate'), + ); + + $form['actions']['#type'] = 'actions'; + $form['actions']['save'] = array( + '#type' => 'submit', + '#value' => t('Add and configure'), + ); + $form['actions']['cancel'] = array( + '#type' => 'link', + '#title' => t('Cancel'), + '#href' => isset($_GET['destination']) ? $_GET['destination'] : 'admin/config/search/metatags/context', + ); + return $form; +} + +function metatag_context_edit_name_validate($element, &$form_state) { + // Check for string identifier sanity. + if (!preg_match('!^[a-z0-9_-]+$!', $element['#value'])) { + form_error($element, t('The name can only consist of lowercase letters, underscores, dashes, and numbers.')); + return; + } + + // Ensure the CTools exportables system is loaded. + ctools_include('export'); + + // Check for name collision. + if ($exists = ctools_export_crud_load('context', $element['#value'])) { + form_error($element, t('A context with this name already exists. Please choose another name or delete the existing item before creating a new one.')); + } +} + +function metatag_context_config_add_form_submit($form, &$form_state) { + $context = metatag_context_load_default_context(); + $context->name = $form_state['values']['name']; + context_save($context); + $form_state['redirect'] = 'admin/config/search/metatags/context/' . $context->name; +} + +function metatag_context_config_edit_form($form, &$form_state, $context) { + $form_state['metatag_context']['context'] = $context; + + // Empty form to start with. + $form = array(); + // Don't care about the instance name, the data is being managed by Context + // and not Metatag. + $instance = ""; + $options = array(); + + // Load the METATAG form. + metatag_metatags_form($form, $instance, $context->reactions['metatag_context_reaction']['metatags'], $options); + + $form['paths'] = array( + '#title' => 'Path', + '#description' => t('Set this metatag context when any of the paths above match the page path. Put each path on a separate line. You can use the <code>*</code> character (asterisk) as a wildcard and the <code>~</code> character (tilde) to exclude one or more paths. Use <code><front></code> for the site front page. Only local paths (e.g. "example/page") will work, do not use relative URLs ("/example/page") or absolute URLs ("http://example.com/example/page").'), + '#type' => 'textarea', + '#default_value' => isset($context->conditions['path']['values']) ? html_entity_decode(implode(' ', $context->conditions['path']['values'])) : '', + '#required' => 1, + '#weight' => -100, + ); + + // If other conditions are assigned, mention it. + $conditions = array_keys($context->conditions); + foreach ($conditions as $key => $condition) { + if ($condition == 'path') { + unset($conditions[$key]); + } + } + if (!empty($conditions)) { + $form['other_conditions'] = array( + '#prefix' => '<p><em>', + '#markup' => t('Other conditions have been assigned that must be controlled through the main Context settings page.'), + '#suffix' => '</em></p>', + '#weight' => -99.9, + ); + } + + $form['help'] = array( + '#prefix' => '<hr /><p><em>', + '#markup' => t('Values assigned here inherit from the <a href="@url" title="Edit the global default meta tags.">global defaults</a> and will override any other meta tags assigned elsewhere.', array('@url' => url('admin/config/search/metatags/config/global'))), + '#suffix' => '</em></p>', + '#weight' => -99, + ); + + // Show all tokens. + $form['metatags']['tokens']['#token_types'] = 'all'; + + $form['metatags']['#type'] = 'container'; + unset($form['metatags']['#collapsed']); + unset($form['metatags']['#collapsible']); + + $form['actions']['#type'] = 'actions'; + $form['actions']['save'] = array( + '#type' => 'submit', + '#value' => t('Save'), + ); + $form['actions']['cancel'] = array( + '#type' => 'submit', + '#value' => t('Cancel'), + '#submit' => array('metatag_context_config_edit_form_cancel_submit'), + '#limit_validation_errors' => array(), + ); + $form['#submit'][] = 'metatag_context_config_edit_form_submit'; + + return $form; +} + +function metatag_context_config_edit_form_cancel_submit($form, &$form_state) { + context_delete($form_state['metatag_context']['context']); + $form_state['redirect'] = 'admin/config/search/metatags/context'; +} + +function metatag_context_config_edit_form_submit($form, &$form_state) { + $context = $form_state['metatag_context']['context']; + $context->reactions['metatag_context_reaction']['metatags'] = array_merge($context->reactions['metatag_context_reaction']['metatags'], $form_state['values']['metatags']); + $paths = explode("\n", str_replace("\r", "", $form_state['values']['paths'])); + $paths = array_combine($paths, $paths); + $context->conditions['path']['values'] = $paths; + context_save($context); + $form_state['redirect'] = 'admin/config/search/metatags/context'; +} + +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.' + ); + + $form['actions']['#type'] = 'actions'; + $form['actions']['save'] = array( + '#type' => 'submit', + '#value' => t('Delete'), + ); + $form['actions']['cancel'] = array( + '#type' => 'link', + '#title' => t('Cancel'), + '#href' => isset($_GET['destination']) ? $_GET['destination'] : 'admin/config/search/metatags/context', + ); + $form['#submit'][] = 'metatag_context_delete_form_submit'; + + return $form; +} + +function metatag_context_delete_form_submit($form, &$form_state) { + context_delete($form_state['metatag_context']['context']); + $form_state['redirect'] = 'admin/config/search/metatags/context'; +} + +function metatag_context_load_default_context() { + $context = new stdClass(); + $context->disabled = FALSE; /* Edit this to true to make a default context disabled initially */ + $context->api_version = 3; + $context->name = 'default_metatag_context'; + $context->description = ''; + $context->tag = 'Metatag'; + $context->metatag = TRUE; + $context->conditions = array( + 'path' => array( + 'values' => array( + ), + ), + ); + $context->reactions = array( + 'metatag_context_reaction' => array( + 'metatags' => array(), + 'metatag_admin' => 1, + ), + ); + $context->condition_mode = 0; + $context->weight = 0; + + // Translatables + // Included for use with string extractors like potx. + t('Metatag'); + return $context; +} diff --git a/sites/all/modules/metatag/metatag_context/metatag_context.context.inc b/sites/all/modules/metatag/metatag_context/metatag_context.context.inc new file mode 100644 index 0000000000000000000000000000000000000000..70f67232abc208d4677682b4a09ea16794796fd1 --- /dev/null +++ b/sites/all/modules/metatag/metatag_context/metatag_context.context.inc @@ -0,0 +1,134 @@ +<?php +/** + * @file + * Context reaction for Metatag. + */ + +class metatag_context_reaction extends context_reaction { + function options_form($context) { + $form = array(); + + // Don't care about the instance name, the data is being managed by + // Context and not Metatag. + $instance = ""; + // Load the previously saved settings. + $data = $this->fetch_from_context($context); + if (!isset($data['metatags'])) { + $data['metatags'] = array(); + } + // No options currently available. + $options = array(); + + $form['help'] = array( + '#prefix' => '<p><em>', + '#markup' => t('Values assigned here inherit from the <a href="@url" title="Edit the global default meta tags.">global defaults</a> and will override any other meta tags assigned elsewhere.', array('@url' => url('admin/config/search/metatags/config/global'))), + '#suffix' => '</em></p>', + ); + + $form['basic_header'] = array( + '#prefix' => '<hr /><h3>', + '#markup' => t('Basic tags'), + '#suffix' => '</h3>', + ); + + // Load the basic Metatag form. + metatag_metatags_form($form, $instance, $data['metatags'], $options); + + // Stop the meta tag fields appearing within a fieldset. + $form['metatags']['#type'] = 'container'; + unset($form['metatags']['#collapsed']); + unset($form['metatags']['#collapsible']); + unset($form['#submit']); + + // Flatten the fieldsets because otherwise the Context module will not save + // them properly. + // TODO: Perhaps it can be done in a better way with #tree and #parents? + foreach (array('advanced', 'dublin-core', 'open-graph') as $fieldset) { + if (isset($form['metatags'][$fieldset])) { + $form['metatags'][$fieldset . '_heading'] = array( + '#prefix' => '<hr /><h3>', + '#markup' => $form['metatags'][$fieldset]['#title'], + '#suffix' => '</h3>', + ); + if (isset($form['metatags'][$fieldset]['#description'])) { + $form['metatags'][$fieldset . '_description'] = array( + '#prefix' => '<p>', + '#markup' => $form['metatags'][$fieldset]['#description'], + '#suffix' => '</p>', + ); + } + foreach ($form['metatags'][$fieldset] as $key => $value) { + if (substr($key, 0, 1) == '#') { + unset ($form['metatags'][$fieldset][$key]); + continue; + } + $form['metatags'][$key] = $value; + unset($form['metatags'][$key]['#parents']); + unset($form['metatags'][$fieldset][$key]); + } + unset($form['metatags'][$fieldset]); + } + } + + // Show all takens. + $form['metatags']['tokens']['#token_types'] = 'all'; + + $form['metatag_admin'] = array( + '#type' => 'checkbox', + '#title' => t('Show on metatag admin page.'), + '#weight' => -98, + '#default_value' => isset($data['metatag_admin']) ? $data['metatag_admin'] : '', + ); + + return $form; + } + + /** + * Output a list of active contexts. + */ + function execute() { + $output = &drupal_static('metatag_context'); + + if (!isset($output)) { + $metatags = array(); + $output = array(); + $contexts = context_active_contexts(); + $options = array(); + $instance_names = array(); + + foreach ($contexts as $context) { + if (!empty($context->reactions['metatag_context_reaction']['metatags'])) { + $metadata_array = $context->reactions['metatag_context_reaction']['metatags']; + foreach ($metadata_array as $key => $data) { + if (!empty($data['value'])) { + $metatags[$key] = $data;//t(check_plain($data['value'])); + } + } + + // Add this context to the list of instances. + $instance_names[] = $context->name; + } + } + + // Only proceed if metatags were assigned. + if (!empty($metatags)) { + $metatags += metatag_config_load_with_defaults(''); + + foreach ($metatags as $metatag => $data) { + if ($metatag_instance = metatag_get_instance($metatag, $data)) { + $output[$metatag] = $metatag_instance->getElement($options); + } + } + + // Compile the identifier for this combination based on the context + // names. + asort($instance_names); + $instance = 'context:' . implode(',', $instance_names); + + // Allow the output meta tags to be modified using + // hook_metatag_metatags_view_alter(). + drupal_alter('metatag_metatags_view', $output, $instance); + } + } + } +} diff --git a/sites/all/modules/metatag/metatag_context/metatag_context.info b/sites/all/modules/metatag/metatag_context/metatag_context.info new file mode 100644 index 0000000000000000000000000000000000000000..14d32b947fe9aa8c888beafeae26178297b007ea --- /dev/null +++ b/sites/all/modules/metatag/metatag_context/metatag_context.info @@ -0,0 +1,13 @@ +name = Meta tags: Context +description = "Assigned Meta tags using Context definitions, allowing them to be assigned by path and other criteria." +package = Meta tags +core = 7.x +dependencies[] = context +dependencies[] = metatag + +; Information added by drupal.org packaging script on 2013-03-24 +version = "7.x-1.0-beta5" +core = "7.x" +project = "metatag" +datestamp = "1364088611" + diff --git a/sites/all/modules/metatag/metatag_context/metatag_context.install b/sites/all/modules/metatag/metatag_context/metatag_context.install new file mode 100644 index 0000000000000000000000000000000000000000..78415ce60305a4e2101699c3e87e909c00d55725 --- /dev/null +++ b/sites/all/modules/metatag/metatag_context/metatag_context.install @@ -0,0 +1,13 @@ +<?php +/** + * @file + * Installation and update hooks for Metatag:Context. + */ + +/** + * Implements hook_enable(). + */ +function metatag_context_enable() { + // Clear the cache so Context and CTools know about this plugin. + cache_clear_all('plugins:context:plugins', 'cache'); +} diff --git a/sites/all/modules/metatag/metatag_context/metatag_context.module b/sites/all/modules/metatag/metatag_context/metatag_context.module new file mode 100644 index 0000000000000000000000000000000000000000..8be7312f5a59fee29df530ba0f7f293c75a3d786 --- /dev/null +++ b/sites/all/modules/metatag/metatag_context/metatag_context.module @@ -0,0 +1,105 @@ +<?php +/** + * @file + * Primary hook implementations for Metatag Context. + */ + +/** + * Implements hook_menu(). + */ +function metatag_context_menu() { + $items['admin/config/search/metatags/context'] = array( + 'title' => 'By path', + 'page callback' => 'metatag_context_context_overview', + 'access arguments' => array('administer meta tags'), + 'file' => 'metatag_context.admin.inc', + 'type' => MENU_LOCAL_TASK, + ); + $items['admin/config/search/metatags/context/add'] = array( + 'title' => 'Add a meta tag by path', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('metatag_context_config_add_form'), + 'access arguments' => array('administer meta tags'), + 'file' => 'metatag_context.admin.inc', + 'type' => MENU_LOCAL_ACTION, + ); + $items['admin/config/search/metatags/context/%context'] = array( + 'title' => 'Configure metatags by path', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('metatag_context_config_edit_form', 5), + 'access arguments' => array('administer meta tags'), + 'file' => 'metatag_context.admin.inc', + ); + $items['admin/config/search/metatags/context/%context/delete'] = array( + 'title' => 'Delete metatags by path', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('metatag_context_delete_form', 5), + 'access arguments' => array('administer meta tags'), + 'file' => 'metatag_context.admin.inc', + ); + + return $items; +} + +/** + * Implements hook_context_plugins(). + */ +function metatag_context_context_plugins() { + return array( + 'metatag_context_reaction' => array( + 'handler' => array( + 'path' => drupal_get_path('module', 'metatag_context'), + 'file' => 'metatag_context.context.inc', + 'class' => 'metatag_context_reaction', + 'parent' => 'context_reaction', + ), + ), + ); +} + +/** + * Implements hook_context_registry(). + */ +function metatag_context_context_registry() { + return array( + 'reactions' => array( + 'metatag_context_reaction' => array( + 'title' => t('Meta Data'), + 'description' => t('Control page meta tags using the Metatag module.'), + 'plugin' => 'metatag_context_reaction', + ), + ), + ); +} + +/** + * Implements hook_context_page_reaction(). + */ +function metatag_context_context_page_reaction() { + if ($plugin = context_get_plugin('reaction', 'metatag_context_reaction')) { + $plugin->execute(); + } +} + +/** + * Implements hook_page_build(). + */ +function metatag_context_page_build(&$page) { + // Load the meta tags that have been generated for this page. + $metatags = drupal_static('metatag_context', array()); + + if (!empty($metatags)) { + $page['content']['metatags']['global'] = $metatags; + } +} + +/** + * Implements hook_preprocess_html(). + */ +function metatag_context_preprocess_html(&$variables) { + $metadata = drupal_static('metatag_context'); + + if (isset($metadata['metadata_title'])) { + $variables['head_title'] = token_replace($metadata['metadata_title']); + } +} diff --git a/sites/all/modules/metatag/metatag_dc/README.txt b/sites/all/modules/metatag/metatag_dc/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..d95921887f4db8384f41bd3268fe1cf9380b242a --- /dev/null +++ b/sites/all/modules/metatag/metatag_dc/README.txt @@ -0,0 +1,36 @@ +Metatag: Dublin Core +-------------------- +This module adds the fifteen Dublin Core Metadata Element Set [1] to the +available meta tags, as defined by the Dublin Core Metadata Institute [2]. + +The following tags are provided: +* dcterms.contributor +* dcterms.coverage +* dcterms.creator +* dcterms.date +* dcterms.description +* dcterms.format +* dcterms.identifier +* dcterms.language +* dcterms.publisher +* dcterms.relation +* dcterms.rights +* dcterms.source +* dcterms.subject +* dcterms.title +* dcterms.type + + +Credits +------------------------------------------------------------------------------ +The initial development was by Marty2081 [3] (sponsored by Gemeentemuseum Den +Haag. [4]), with contributions by many in the community [5]. + + +References +------------------------------------------------------------------------------ +1: http://dublincore.org/documents/dces/ +2: http://www.dublincore.org/ +3: http://drupal.org/user/960720 +4: http://www.gemeentemuseum.nl/ +5: http://drupal.org/node/1491616 diff --git a/sites/all/modules/metatag/metatag_dc/metatag_dc.info b/sites/all/modules/metatag/metatag_dc/metatag_dc.info new file mode 100644 index 0000000000000000000000000000000000000000..7e05d4efc9c33d76fe51bc62e2a62207d63ce468 --- /dev/null +++ b/sites/all/modules/metatag/metatag_dc/metatag_dc.info @@ -0,0 +1,12 @@ +name = Meta tags: Dublin Core +description = Provides the fifteen <a href="http://dublincore.org/documents/dces/">Dublin Core Metadata Element Set 1.1</a> meta tags from the <a href="http://dublincore.org/">Dublin Core Metadata Institute</a>. +package = Meta tags +core = 7.x +dependencies[] = metatag + +; Information added by drupal.org packaging script on 2013-03-24 +version = "7.x-1.0-beta5" +core = "7.x" +project = "metatag" +datestamp = "1364088611" + diff --git a/sites/all/modules/metatag/metatag_dc/metatag_dc.metatag.inc b/sites/all/modules/metatag/metatag_dc/metatag_dc.metatag.inc new file mode 100644 index 0000000000000000000000000000000000000000..7f8094d157794ba486746d1d17267fa727a7c62d --- /dev/null +++ b/sites/all/modules/metatag/metatag_dc/metatag_dc.metatag.inc @@ -0,0 +1,235 @@ +<?php +/** + * @file + * Metatag integration for the metatag_dc module. + */ + +/** + * Implements hook_metatag_config_default_alter(). + */ +function metatag_dc_metatag_config_default_alter(array &$configs) { + foreach ($configs as &$config) { + switch ($config->instance) { + case 'global': + $config->config += array( + 'dcterms.title' => array('value' => '[current-page:title]'), + 'dcterms.type' => array('value' => 'Text'), + 'dcterms.format' => array('value' => 'text/html'), + ); + break; + + case 'global:frontpage': + $config->config += array( + 'dcterms.title' => array('value' => '[site:name]'), + 'dcterms.identifier' => array('value' => '[site:url]'), + ); + break; + + case 'node': + $config->config += array( + 'dcterms.title' => array('value' => '[node:title]'), + 'dcterms.date' => array('value' => '[node:created:custom:Y-m-d\TH:iP]'), + 'dcterms.identifier' => array('value' => '[current-page:url:absolute]'), + 'dcterms.language' => array('value' => '[node:language]'), + 'dcterms.creator' => array('value' => '[node:author]'), + ); + break; + + case 'taxonomy_term': + $config->config += array( + 'dcterms.title' => array('value' => '[term:name]'), + 'dcterms.identifier' => array('value' => '[current-page:url:absolute]'), + 'dcterms.description' => array('value' => '[term:description]'), + ); + break; + + case 'user': + $config->config += array( + 'dcterms.title' => array('value' => '[user:name]'), + 'dcterms.date' => array('value' => '[user:created:custom:Y-m-d\TH:iP]'), + 'dcterms.identifier' => array('value' => '[current-page:url:absolute]'), + 'dcterms.creator' => array('value' => '[user:name]'), + ); + break; + } + } +} + +/** + * Implements hook_metatag_info(). + * Dublin Core Elements taken from http://purl.org/dc/elements/1.1/. + */ +function metatag_dc_metatag_info() { + $info['groups']['dublin-core'] = array( + 'label' => t('Dublin Core'), + 'description' => t('The Dublin Core Metadata Element Set, aka "Dublin Core meta tags", are a set of internationally standardized metadata tags used to describe content to make identification and classification of content easier; the standards are controlled by the <a href="http://dublincore.org/">Dublin Core Metadata Initiative (DCMI)</a>.'), + 'form' => array( + '#weight' => 70, + ), + ); + $info['tags']['dcterms.title'] = array( + 'label' => t('Dublin Core Title'), + 'description' => t('The name given to the resource.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'dublin-core', + 'element' => array( + '#type' => 'term', + '#theme' => 'metatag_dc', + ), + ); + $info['tags']['dcterms.creator'] = array( + 'label' => t('Dublin Core Creator'), + 'description' => t('An entity primarily responsible for making the resource. Examples of a Creator include a person, an organization, or a service. Typically, the name of a Creator should be used to indicate the entity.'), + 'group' => 'dublin-core', + 'class' => 'DrupalTextMetaTag', + 'element' => array( + '#theme' => 'metatag_dc', + ), + ); + $info['tags']['dcterms.subject'] = array( + 'label' => t('Dublin Core Subject'), + 'description' => t('The topic of the resource. Typically, the subject will be represented using keywords, key phrases, or classification codes. Recommended best practice is to use a controlled vocabulary. To describe the spatial or temporal topic of the resource, use the Coverage element.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'dublin-core', + 'element' => array( + '#theme' => 'metatag_dc', + ), + ); + $info['tags']['dcterms.description'] = array( + 'label' => t('Dublin Core Description'), + 'description' => t('An account of the resource. Description may include but is not limited to: an abstract, a table of contents, a graphical representation, or a free-text account of the resource.'), + 'group' => 'dublin-core', + 'class' => 'DrupalTextMetaTag', + 'element' => array( + '#theme' => 'metatag_dc', + ), + ); + $info['tags']['dcterms.publisher'] = array( + 'label' => t('Dublin Core Publisher'), + 'description' => t('An entity responsible for making the resource available. Examples of a Publisher include a person, an organization, or a service. Typically, the name of a Publisher should be used to indicate the entity.'), + 'group' => 'dublin-core', + 'class' => 'DrupalTextMetaTag', + 'element' => array( + '#theme' => 'metatag_dc', + ), + ); + $info['tags']['dcterms.contributor'] = array( + 'label' => t('Dublin Core Contributor'), + 'description' => t('An entity responsible for making contributions to the resource. Examples of a Contributor include a person, an organization, or a service. Typically, the name of a Contributor should be used to indicate the entity.'), + 'group' => 'dublin-core', + 'class' => 'DrupalTextMetaTag', + 'element' => array( + '#theme' => 'metatag_dc', + ), + ); + $info['tags']['dcterms.date'] = array( + 'label' => t('Dublin Core Date'), + 'description' => t('A point or period of time associated with an event in the lifecycle of the resource. Date may be used to express temporal information at any level of granularity. Recommended best practice is to use an encoding scheme, such as the W3CDTF profile of ISO 8601 [W3CDTF].'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'dublin-core', + 'element' => array( + '#theme' => 'metatag_dc', + ), + ); + $info['tags']['dcterms.type'] = array( + 'label' => t('Dublin Core Type'), + 'description' => t('The nature or genre of the resource. Recommended best practice is to use a controlled vocabulary such as the DCMI Type Vocabulary [DCMITYPE]. To describe the file format, physical medium, or dimensions of the resource, use the Format element.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'dublin-core', + 'form' => array( + '#type' => 'select', + '#options' => _metatag_dc_dcmi_type_vocabulary_options(), + '#empty_option' => t('- None -'), + ), + 'element' => array( + '#theme' => 'metatag_dc', + ), + ); + $info['tags']['dcterms.format'] = array( + 'label' => t('Dublin Core Format'), + 'description' => t('The file format, physical medium, or dimensions of the resource. Examples of dimensions include size and duration. Recommended best practice is to use a controlled vocabulary such as the list of Internet Media Types [MIME].'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'dublin-core', + 'element' => array( + '#theme' => 'metatag_dc', + ), + ); + $info['tags']['dcterms.identifier'] = array( + 'label' => t('Dublin Core Identifier'), + 'description' => t('An unambiguous reference to the resource within a given context. Recommended best practice is to identify the resource by means of a string conforming to a formal identification system.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'dublin-core', + 'element' => array( + '#theme' => 'metatag_dc', + ), + ); + $info['tags']['dcterms.source'] = array( + 'label' => t('Dublin Core Source'), + 'description' => t('A related resource from which the described resource is derived. The described resource may be derived from the related resource in whole or in part. Recommended best practice is to identify the related resource by means of a string conforming to a formal identification system.'), + 'group' => 'dublin-core', + 'class' => 'DrupalTextMetaTag', + 'element' => array( + '#theme' => 'metatag_dc', + ), + ); + $info['tags']['dcterms.language'] = array( + 'label' => t('Dublin Core Language'), + 'description' => t('A language of the resource. Recommended best practice is to use a controlled vocabulary such as RFC 4646 [RFC4646].'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'dublin-core', + 'element' => array( + '#theme' => 'metatag_dc', + ), + ); + $info['tags']['dcterms.relation'] = array( + 'label' => t('Dublin Core Relation'), + 'description' => t('A related resource. Recommended best practice is to identify the related resource by means of a string conforming to a formal identification system.'), + 'group' => 'dublin-core', + 'class' => 'DrupalTextMetaTag', + 'element' => array( + '#theme' => 'metatag_dc', + ), + ); + $info['tags']['dcterms.coverage'] = array( + 'label' => t('Dublin Core Coverage'), + 'description' => t('The spatial or temporal topic of the resource, the spatial applicability of the resource, or the jurisdiction under which the resource is relevant. Spatial topic and spatial applicability may be a named place or a location specified by its geographic coordinates. Temporal topic may be a named period, date, or date range. A jurisdiction may be a named administrative entity or a geographic place to which the resource applies. Recommended best practice is to use a controlled vocabulary such as the Thesaurus of Geographic Names [TGN]. Where appropriate, named places or time periods can be used in preference to numeric identifiers such as sets of coordinates or date ranges.'), + 'group' => 'dublin-core', + 'class' => 'DrupalTextMetaTag', + 'element' => array( + '#theme' => 'metatag_dc', + ), + ); + $info['tags']['dcterms.rights'] = array( + 'label' => t('Dublin Core Rights'), + 'description' => t('Information about rights held in and over the resource. Typically, rights information includes a statement about various property rights associated with the resource, including intellectual property rights.'), + 'group' => 'dublin-core', + 'class' => 'DrupalTextMetaTag', + 'element' => array( + '#theme' => 'metatag_dc', + ), + ); + + return $info; +} + +/** + * Function that returns the DCMI type options. + * Types taken from http://dublincore.org/documents/dcmi-type-vocabulary/. + */ +function _metatag_dc_dcmi_type_vocabulary_options() { + $options = array( + 'Collection', + 'Dataset', + 'Event', + 'Image', + 'InteractiveResource', + 'MovingImage', + 'PhysicalObject', + 'Service', + 'Software', + 'Sound', + 'StillImage', + 'Text', + ); + return drupal_map_assoc($options); +} diff --git a/sites/all/modules/metatag/metatag_dc/metatag_dc.module b/sites/all/modules/metatag/metatag_dc/metatag_dc.module new file mode 100644 index 0000000000000000000000000000000000000000..6090aff71f7f662f750defa5f404d22b3fac1f24 --- /dev/null +++ b/sites/all/modules/metatag/metatag_dc/metatag_dc.module @@ -0,0 +1,39 @@ +<?php +/** + * @file + * Metatag integration for the metatag_dc module. + */ + +/** + * Implements hook_ctools_plugin_api(). + */ +function metatag_dc_ctools_plugin_api($owner, $api) { + if ($owner == 'metatag' && $api == 'metatag') { + return array('version' => 1); + } +} + +/** + * Implements hook_theme(). + */ +function metatag_dc_theme() { + $info['metatag_dc'] = array( + 'render element' => 'element', + ); + + return $info; +} + +/** + * Theme callback for a Dublin Core meta tag. + */ +function theme_metatag_dc($variables) { + $element = &$variables['element']; + element_set_attributes($element, array( + '#name' => 'property', + '#schema' => 'schema', + '#value' => 'content') + ); + unset($element['#value']); + return theme('html_tag', $variables); +} diff --git a/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.info b/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.info index 32df12e2ad61a7a9cc3bfdcc5ac15eb3d96e264f..6b32cfdfccd7c2bcb555691816c7e994944b891f 100644 --- a/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.info +++ b/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.info @@ -1,12 +1,12 @@ -name = Open Graph meta tags +name = Meta tags: Open Graph description = Provides support for open graph meta tags. package = Meta tags core = 7.x dependencies[] = metatag -; Information added by drupal.org packaging script on 2012-07-13 -version = "7.x-1.0-alpha6+1-dev" +; Information added by drupal.org packaging script on 2013-03-24 +version = "7.x-1.0-beta5" core = "7.x" project = "metatag" -datestamp = "1342182783" +datestamp = "1364088611" diff --git a/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.metatag.inc b/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.metatag.inc index a6d9dc7b0454cff7bb6b611bd8b7d5fcfaaa7870..f6c6a1b2a5bf93f7a352ef685a173a7f76995e9e 100644 --- a/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.metatag.inc +++ b/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.metatag.inc @@ -16,7 +16,7 @@ function metatag_opengraph_metatag_config_default_alter(array &$configs) { 'og:type' => array('value' => 'article'), 'og:title' => array('value' => '[current-page:title]'), 'og:site_name' => array('value' => '[site:name]'), - 'og:url' => array('value' => '[current-page:url]'), + 'og:url' => array('value' => '[current-page:url:absolute]'), ); break; case 'global:frontpage': @@ -60,6 +60,39 @@ function metatag_opengraph_metatag_config_default_alter(array &$configs) { function metatag_opengraph_metatag_info() { $info['groups']['open-graph'] = array( 'label' => t('Open Graph'), + 'form' => array( + '#weight' => 50, + ), + ); + + $info['tags']['fb:admins'] = array( + 'label' => t('Facebook Admins'), + 'description' => t('A comma-separated list of Facebook user IDs of people who are considered administrators or moderators of this page. Most sites will only need to assign this on the global settings page.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'open-graph', + 'element' => array( + '#theme' => 'metatag_opengraph', + ), + ); + $info['tags']['fb:app_id'] = array( + 'label' => t('Facebook Application ID'), + 'description' => t('A comma-separated list of Facebook Platform Application IDs applicable for this site. Most sites will only need to assign this on the global settings page.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'open-graph', + 'element' => array( + '#theme' => 'metatag_opengraph', + ), + ); + + $info['tags']['og:site_name'] = array( + 'label' => t('Open Graph site name'), + 'description' => t('A human-readable name for your site, e.g., <em>IMDb</em>.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'open-graph', + 'context' => array('global'), + 'element' => array( + '#theme' => 'metatag_opengraph', + ), ); $info['tags']['og:title'] = array( @@ -71,23 +104,41 @@ function metatag_opengraph_metatag_info() { '#theme' => 'metatag_opengraph', ), ); + + $info['tags']['og:description'] = array( + 'label' => t('Open Graph description'), + 'description' => t('A one to two sentence description of your page.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'open-graph', + 'element' => array( + '#theme' => 'metatag_opengraph', + ), + ); + $info['tags']['og:type'] = array( 'label' => t('Open Graph type'), 'description' => t('The type of your object, e.g., <em>movie</em>.'), 'class' => 'DrupalTextMetaTag', 'group' => 'open-graph', + 'element' => array( + '#theme' => 'metatag_opengraph', + ), 'form' => array( '#type' => 'select', '#options' => _metatag_opengraph_type_options(), '#empty_option' => t('- None -'), ), - 'element' => array( - '#theme' => 'metatag_opengraph', - ), ); - //if (module_exists('select_or_other')) { - // $info['tags']['og:type']['form']['#type'] = 'select_or_other'; - //} + + if (module_exists('select_or_other')) { + // Enhance the og:type field to support custom types. + $info['tags']['og:type']['form']['#type'] = 'select_or_other'; + $info['tags']['og:type']['form']['#other'] = t('Other (please type a value)'); + $info['tags']['og:type']['form']['#other_unknown_defaults'] = 'other'; + $info['tags']['og:type']['form']['#theme'] = 'select_or_other'; + $info['tags']['og:type']['form']['#element_validate'] = array('select_or_other_element_validate'); + } + $info['tags']['og:image'] = array( 'label' => t('Open Graph image'), 'description' => t('An image URL which should represent your object within the graph. The image must be at least 50px by 50px and have a maximum aspect ratio of 3:1. We support PNG, JPEG and GIF formats.'), @@ -97,6 +148,7 @@ function metatag_opengraph_metatag_info() { '#theme' => 'metatag_opengraph', ), ); + $info['tags']['og:url'] = array( 'label' => t('Open Graph URL'), 'description' => t('The canonical URL of your object that will be used as its permanent ID in the graph, e.g., <em>http://www.imdb.com/title/tt0117500/</em>.'), @@ -106,25 +158,153 @@ function metatag_opengraph_metatag_info() { '#theme' => 'metatag_opengraph', ), ); - $info['tags']['og:site_name'] = array( - 'label' => t('Open Graph site name'), - 'description' => t('A human-readable name for your site, e.g., <em>IMDb</em>.'), + + $info['tags']['og:latitude'] = array( + 'label' => t('Open Graph Latitude'), + 'description' => '', 'class' => 'DrupalTextMetaTag', 'group' => 'open-graph', - 'context' => array('global'), 'element' => array( '#theme' => 'metatag_opengraph', ), ); - $info['tags']['og:description'] = array( - 'label' => t('Open Graph description'), - 'description' => t('A one to two sentence description of your page.'), + $info['tags']['og:longitude'] = array( + 'label' => t('Open Graph Longitude'), + 'description' => '', + 'class' => 'DrupalTextMetaTag', + 'group' => 'open-graph', + 'element' => array( + '#theme' => 'metatag_opengraph', + ), + ); + + $info['tags']['og:street-address'] = array( + 'label' => t('Open Graph Street Address'), + 'description' => '', + 'class' => 'DrupalTextMetaTag', + 'group' => 'open-graph', + 'element' => array( + '#theme' => 'metatag_opengraph', + ), + ); + $info['tags']['og:locality'] = array( + 'label' => t('Open Graph Locality'), + 'description' => '', + 'class' => 'DrupalTextMetaTag', + 'group' => 'open-graph', + 'element' => array( + '#theme' => 'metatag_opengraph', + ), + ); + $info['tags']['og:region'] = array( + 'label' => t('Open Graph Region'), + 'description' => '', + 'class' => 'DrupalTextMetaTag', + 'group' => 'open-graph', + 'element' => array( + '#theme' => 'metatag_opengraph', + ), + ); + $info['tags']['og:postal-code'] = array( + 'label' => t('Open Graph Postal Code'), + 'description' => '', + 'class' => 'DrupalTextMetaTag', + 'group' => 'open-graph', + 'element' => array( + '#theme' => 'metatag_opengraph', + ), + ); + $info['tags']['og:country-name'] = array( + 'label' => t('Open Graph Country Name'), + 'description' => '', + 'class' => 'DrupalTextMetaTag', + 'group' => 'open-graph', + 'element' => array( + '#theme' => 'metatag_opengraph', + ), + ); + + $info['tags']['og:email'] = array( + 'label' => t('Open Graph Email'), + 'description' => '', + 'class' => 'DrupalTextMetaTag', + 'group' => 'open-graph', + 'element' => array( + '#theme' => 'metatag_opengraph', + ), + ); + $info['tags']['og:phone_number'] = array( + 'label' => t('Open Graph Phone Number'), + 'description' => '', + 'class' => 'DrupalTextMetaTag', + 'group' => 'open-graph', + 'element' => array( + '#theme' => 'metatag_opengraph', + ), + ); + $info['tags']['og:fax_number'] = array( + 'label' => t('Open Graph Fax Number'), + 'description' => '', + 'class' => 'DrupalTextMetaTag', 'group' => 'open-graph', + 'element' => array( + '#theme' => 'metatag_opengraph', + ), + ); + + $info['tags']['og:video'] = array( + 'label' => t('Open Graph Video (URL)'), + 'description' => t('A URL to a video file that complements this object.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'open-graph', + 'element' => array( + '#theme' => 'metatag_opengraph', + ), + ); + $info['tags']['og:video:secure_url'] = array( + 'label' => t('Open Graph Video Secure'), + 'description' => t('A URL to a video file that complements this object using the HTTPS protocol.'), 'class' => 'DrupalTextMetaTag', + 'group' => 'open-graph', 'element' => array( '#theme' => 'metatag_opengraph', ), ); + $info['tags']['og:video:width'] = array( + 'label' => t('Open Graph Video Width'), + 'description' => t('The width of the video.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'open-graph', + 'element' => array( + '#theme' => 'metatag_opengraph', + ), + ); + $info['tags']['og:video:height'] = array( + 'label' => t('Open Graph Video Height'), + 'description' => t('The height of the video.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'open-graph', + 'element' => array( + '#theme' => 'metatag_opengraph', + ), + ); + $info['tags']['og:video:type'] = array( + 'label' => t('Open Graph Video Type'), + 'description' => t('The type of the video file.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'open-graph', + 'element' => array( + '#theme' => 'metatag_opengraph', + ), + 'form' => array( + '#type' => 'select', + '#options' => array( + 'application/x-shockwave-flash' => 'Flash - playable directly from the feed', + 'text/html' => 'Separate HTML page', + ), + '#empty_option' => t('- None -'), + ), + ); return $info; } diff --git a/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.module b/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.module index d94e7d4551efc875270699c4c0483d5eeb5ced01..f4a191dcbfb2ecf4abfbf50092b38d1dd2ec17d5 100644 --- a/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.module +++ b/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.module @@ -74,4 +74,7 @@ og:audio:type og:upc og:isbn + +fb:admins +fb:app_id */ diff --git a/sites/all/modules/metatag/metatag_twitter_cards/README.txt b/sites/all/modules/metatag/metatag_twitter_cards/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..d6e6e8290d91c64fb8f5a8a611ed352a3c5970c7 --- /dev/null +++ b/sites/all/modules/metatag/metatag_twitter_cards/README.txt @@ -0,0 +1,44 @@ +Metatag: Twitter Cards +---------------------- +This module adds the fourteen basic Twitter Cards meta tags [1]. The following +tags are provided: + +* twitter:card +* twitter:site +* twitter:creator +* twitter:url +* twitter:title +* twitter:description +* twitter:image +* twitter:image:width +* twitter:image:height +* twitter:player +* twitter:player:width +* twitter:player:height +* twitter:player:stream +* twitter:player:stream:content_type + + +Usage +------------------------------------------------------------------------------ +The Twitter Cards meta tags are configured along with all other meta tags; +on-form help is provided to aid with configuring the meta tags. + +After enabling and configuring the meta tags it is important to first test [2] +the meta tags for compliance with Twitter's standards, and then apply [3] to +have your site's usage approved. + + +Credits +------------------------------------------------------------------------------ +The initial development was by nico059 [4] with contributions by many in the +community [5]. + + +References +------------------------------------------------------------------------------ +1: https://dev.twitter.com/docs/cards +2: https://dev.twitter.com/docs/cards/preview +3: http://drupal.org/user/960720 +4: http://www.gemeentemuseum.nl/ +5: http://drupal.org/node/1664322 diff --git a/sites/all/modules/metatag/metatag_twitter_cards/metatag_twitter_cards.info b/sites/all/modules/metatag/metatag_twitter_cards/metatag_twitter_cards.info new file mode 100644 index 0000000000000000000000000000000000000000..670094651c573d17fdd2898decbc7ba5991c18ff --- /dev/null +++ b/sites/all/modules/metatag/metatag_twitter_cards/metatag_twitter_cards.info @@ -0,0 +1,11 @@ +name = Meta tags: Twitter Cards +description = "Provides support for Twitter's Card meta tags. NOTE: Only use if the site supports SSL as all URLs *must* be secured via HTTPS." +package = Meta tags +core = 7.x +dependencies[] = metatag +; Information added by drupal.org packaging script on 2013-03-24 +version = "7.x-1.0-beta5" +core = "7.x" +project = "metatag" +datestamp = "1364088611" + diff --git a/sites/all/modules/metatag/metatag_twitter_cards/metatag_twitter_cards.metatag.inc b/sites/all/modules/metatag/metatag_twitter_cards/metatag_twitter_cards.metatag.inc new file mode 100644 index 0000000000000000000000000000000000000000..3d705a67141b7e4410897a00ef9bcb6e790e0ea2 --- /dev/null +++ b/sites/all/modules/metatag/metatag_twitter_cards/metatag_twitter_cards.metatag.inc @@ -0,0 +1,214 @@ +<?php +/** + * @file + * Metatag integration for the metatag Twitter Cards module. + */ + +/** + * Implements hook_metatag_config_default_alter(). + */ +function metatag_twitter_cards_metatag_config_default_alter(array &$configs) { + foreach ($configs as &$config) { + switch ($config->instance) { + case 'global': + $config->config += array( + 'twitter:card' => array('value' => 'summary'), + 'twitter:description' => array('value' => '[site:slogan]'), + 'twitter:title' => array('value' => '[site:name]'), + 'twitter:url' => array('value' => '[current-page:url:absolute]'), + ); + break; + + case 'global:frontpage': + $config->config += array( + 'twitter:description' => array('value' => ''), + ); + break; + + case 'node': + $config->config += array( + 'twitter:card' => array('value' => 'summary'), + 'twitter:description' => array('value' => '[node:summary]'), + 'twitter:title' => array('value' => '[node:title]'), + ); + break; + + case 'taxonomy_term': + $config->config += array( + 'twitter:card' => array('value' => 'summary'), + 'twitter:title'=> array('value' => '[term:name]'), + ); + break; + } + } +} + +/** + * Implements hook_metatag_info(). + */ +function metatag_twitter_cards_metatag_info() { + $info['groups']['twitter-cards'] = array( + 'label' => t('Twitter card'), + 'description' => t('A set of meta tags specially for controlling the summaries displayed when content is shared on <a href="!url">Twitter</a>. <strong>NOTE:</strong> Only use if the site supports SSL as all URLs <em>must</em> be secured via HTTPS.', array('!url' => 'http://twitter.com/')), + 'form' => array( + '#weight' => 60, + ), + ); + + $info['tags']['twitter:card'] = array( + 'label' => t('Twitter card type'), + 'description' => t('Notes: no other fields are required for a <em>Summary</em> card, a <em>Photo</em> card requires the \'image\' field, while a <em>Media player</em> card requires the \'title\', \'description\', \'media player URL\', \'media player width\', \'media player height\' and \'image\' fields.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'twitter-cards', + 'form' => array( + '#type' => 'select', + '#options' => array( + 'summary' => t('Summary (default)'), + 'photo' => t('Photo'), + 'player' => t('Media player'), + ), + '#empty_option' => t('- None -'), + ), + 'element' => array( + '#theme' => 'metatag_twitter_cards', + ), + ); + $info['tags']['twitter:site'] = array( + 'label' => t('Site\'s Twitter account'), + 'description' => t('The @username for the website, which will be displayed in the Card\'s footer; must include the @ symbol.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'twitter-cards', + 'context' => array('global'), + 'element' => array( + '#theme' => 'metatag_twitter_cards', + ), + ); + $info['tags']['twitter:site:id'] = array( + 'label' => t('Site\'s Twitter account ID'), + 'description' => t('The numerical Twitter account ID for the website, which will be displayed in the Card\'s footer.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'twitter-cards', + 'context' => array('global'), + 'element' => array( + '#theme' => 'metatag_twitter_cards', + ), + ); + $info['tags']['twitter:creator'] = array( + 'label' => t('Creator\'s Twitter account'), + 'description' => t('The @username for the content creator / author for this page, including the @ symbol.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'twitter-cards', + 'element' => array( + '#theme' => 'metatag_twitter_cards', + ), + ); + $info['tags']['twitter:creator:id'] = array( + 'label' => t('Creator\'s Twitter account ID'), + 'description' => t('The numerical Twitter account ID for the content creator / author for this page.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'twitter-cards', + 'element' => array( + '#theme' => 'metatag_twitter_cards', + ), + ); + $info['tags']['twitter:url'] = array( + 'label' => t('Page URL'), + 'description' => t('The permalink / canonical URL of the current page.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'twitter-cards', + 'element' => array( + '#theme' => 'metatag_twitter_cards', + ), + ); + $info['tags']['twitter:title'] = array( + 'label' => t('Title'), + 'description' => t('The page\'s title, which should be concise; it will be truncated at 70 characters by Twitter. This field is required unless this the \'type\' field is set to "photo".'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'twitter-cards', + 'element' => array( + '#theme' => 'metatag_twitter_cards', + ), + ); + $info['tags']['twitter:description'] = array( + 'label' => t('Description'), + 'description' => t('A description that concisely summarizes the content of the page, as appropriate for presentation within a Tweet. Do not re-use the title text as the description, or use this field to describe the general services provided by the website. The string will be truncated, by Twitter, at the word to 200 characters.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'twitter-cards', + 'element' => array( + '#theme' => 'metatag_twitter_cards', + ), + ); + $info['tags']['twitter:image'] = array( + 'label' => t('Image URL'), + 'description' => t('The URL to a unique image representing the content of the page. Do not use a generic image such as your website logo, author photo, or other image that spans multiple pages. Images larger than 120x120px will be resized and cropped square based on longest dimension. Images smaller than 60x60px will not be shown. If the \'type\' is set to <em>Photo</em> then the image must be at least 280x150px.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'twitter-cards', + 'element' => array( + '#theme' => 'metatag_twitter_cards', + ), + ); + $info['tags']['twitter:image:width'] = array( + 'label' => t('Image width'), + 'description' => t('The width of the image being linked to, in pixels.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'twitter-cards', + 'element' => array( + '#theme' => 'metatag_twitter_cards', + ), + ); + $info['tags']['twitter:image:height'] = array( + 'label' => t('Image height'), + 'description' => t('The height of the image being linked to, in pixels.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'twitter-cards', + 'element' => array( + '#theme' => 'metatag_twitter_cards', + ), + ); + $info['tags']['twitter:player'] = array( + 'label' => t('Media player URL'), + 'description' => t('The full URL for loading a media player. Required when using a <em>Media player</em> card.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'twitter-cards', + 'element' => array( + '#theme' => 'metatag_twitter_cards', + ), + ); + $info['tags']['twitter:player:width'] = array( + 'label' => t('Media player width'), + 'description' => t('The width of the media player iframe, in pixels. Required when using a <em>Media player</em> card.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'twitter-cards', + 'element' => array( + '#theme' => 'metatag_twitter_cards', + ), + ); + $info['tags']['twitter:player:height'] = array( + 'label' => t('Media player height'), + 'description' => t('The height of the media player iframe, in pixels. Required when using a <em>Media player</em> card.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'twitter-cards', + 'element' => array( + '#theme' => 'metatag_twitter_cards', + ), + ); + $info['tags']['twitter:player:stream'] = array( + 'label' => t('MP4 media stream URL'), + 'description' => t('The full URL for an MP4 video (h.264) or audio (AAC) stream, takes precidence over the other media player field.'), + 'class' => 'DrupalTextMetaTag', + 'group' => 'twitter-cards', + 'element' => array( + '#theme' => 'metatag_twitter_cards', + ), + ); + $info['tags']['twitter:player:stream:content_type'] = array( + 'label' => t('MP4 media stream MIME type'), + 'description' => t('The MIME type for the media contained in the stream URL, as defined by <a href="!url">RFC 4337</a>.', array('!url' => 'http://tools.ietf.org/rfc/rfc4337.txt')), + 'class' => 'DrupalTextMetaTag', + 'group' => 'twitter-cards', + 'element' => array( + '#theme' => 'metatag_twitter_cards', + ), + ); + return $info; +} diff --git a/sites/all/modules/metatag/metatag_twitter_cards/metatag_twitter_cards.module b/sites/all/modules/metatag/metatag_twitter_cards/metatag_twitter_cards.module new file mode 100644 index 0000000000000000000000000000000000000000..2f835c4f2b9c10ff5cf3098605548055179eff37 --- /dev/null +++ b/sites/all/modules/metatag/metatag_twitter_cards/metatag_twitter_cards.module @@ -0,0 +1,35 @@ +<?php +/** + * @file + * Primary hook implementations for Metatag: Twitter Cards. + */ + +/** + * Implements hook_ctools_plugin_api(). + */ +function metatag_twitter_cards_ctools_plugin_api($owner, $api) { + if ($owner == 'metatag' && $api == 'metatag') { + return array('version' => 1); + } +} + +/** + * Implements hook_theme(). + */ +function metatag_twitter_cards_theme() { + $info['metatag_twitter_cards'] = array( + 'render element' => 'element', + ); + + return $info; +} + +/** + * Theme callback for an twittercard meta tag. + */ +function theme_metatag_twitter_cards($variables) { + $element = &$variables['element']; + element_set_attributes($element, array('#name' => 'property', '#value' => 'content')); + unset($element['#value']); + return theme('html_tag', $variables); +} diff --git a/sites/all/modules/metatag/metatag_ui/metatag_ui.info b/sites/all/modules/metatag/metatag_ui/metatag_ui.info index 801f2c87e7b8b1485f64b21bd7ac899d4328d863..30fcb4bb214463398785feae58fdd6ccfb9b812c 100644 --- a/sites/all/modules/metatag/metatag_ui/metatag_ui.info +++ b/sites/all/modules/metatag/metatag_ui/metatag_ui.info @@ -1,14 +1,14 @@ name = Meta tag UI -description = User interface for the Meta tag API. +description = "DEPRECATED admin interface for the Meta tag API, this functionality has be merged into the main module." package = Meta tags core = 7.x dependencies[] = metatag dependencies[] = ctools hidden = TRUE -; Information added by drupal.org packaging script on 2012-07-13 -version = "7.x-1.0-alpha6+1-dev" +; Information added by drupal.org packaging script on 2013-03-24 +version = "7.x-1.0-beta5" core = "7.x" project = "metatag" -datestamp = "1342182783" +datestamp = "1364088611" diff --git a/sites/all/modules/metatag/metatag_ui/metatag_ui.module b/sites/all/modules/metatag/metatag_ui/metatag_ui.module new file mode 100644 index 0000000000000000000000000000000000000000..0942a6b3556b0ccd63d6bd99b76bc5f1d378c569 --- /dev/null +++ b/sites/all/modules/metatag/metatag_ui/metatag_ui.module @@ -0,0 +1,5 @@ +<?php +/** + * @file + * Empty file for the deprecated Metatag UI module. + */ diff --git a/sites/all/modules/metatag/tests/metatag_test.info b/sites/all/modules/metatag/tests/metatag_test.info index 451067fbaa435575422598278653467a376c0c06..57114e4d351c557009af0bb0dfee0dc1c7de8b27 100644 --- a/sites/all/modules/metatag/tests/metatag_test.info +++ b/sites/all/modules/metatag/tests/metatag_test.info @@ -4,9 +4,9 @@ core = 7.x dependencies[] = metatag hidden = TRUE -; Information added by drupal.org packaging script on 2012-07-13 -version = "7.x-1.0-alpha6+1-dev" +; Information added by drupal.org packaging script on 2013-03-24 +version = "7.x-1.0-beta5" core = "7.x" project = "metatag" -datestamp = "1342182783" +datestamp = "1364088611" diff --git a/sites/all/modules/metatag/tests/metatag_test.metatag.inc b/sites/all/modules/metatag/tests/metatag_test.metatag.inc index bed4abc69c946c2f5afa68dd075e0f10dd02e481..7525f5aa3475f0b4f2d0ecfdae129f3d2aaba243 100644 --- a/sites/all/modules/metatag/tests/metatag_test.metatag.inc +++ b/sites/all/modules/metatag/tests/metatag_test.metatag.inc @@ -21,6 +21,7 @@ function metatag_test_metatag_config_default() { $config->disabled = FALSE; $config->config = array( 'description' => array('value' => 'Test foo description'), + 'abstract' => array('value' => 'Test foo abstract'), 'title' => array('value' => 'Test title'), 'test:foo' => array('value' => 'foobar'), ); diff --git a/sites/all/modules/module_filter/CHANGELOG.txt b/sites/all/modules/module_filter/CHANGELOG.txt index 7de283d3e20d31ed115723a8a14d890076b88626..04f924766035e9c0ac83acf7e7c1b075b3c09af5 100644 --- a/sites/all/modules/module_filter/CHANGELOG.txt +++ b/sites/all/modules/module_filter/CHANGELOG.txt @@ -1,3 +1,21 @@ +Module Filter 7.x-2.x, 2013-01-04 +--------------------------------- +by greenSkin: Fixed issue relating to row coloring when enabling/disabling + modules via switch due to recent fix for jQuery Update module. + + +Module Filter 7.x-2.x, 2012-11-27 +--------------------------------- +by greenSkin: Added functionality to show recently enabled/disabled modules. +#1710230 by littlekoala, greenSkin: Fixed On | Off buttons does not change + state with jquery_update() module active. + + +Module Filter 7.x-2.x, 2012-11-26 +--------------------------------- +by greenSkin: Added description to filter textfield on permissions page. + + Module Filter 7.x-2.x, 2012-11-04 --------------------------------- by greenSkin: Added filter to user permissions page. diff --git a/sites/all/modules/module_filter/js/module_filter_tab.js b/sites/all/modules/module_filter/js/module_filter_tab.js index 2653f977d50f8bbcdfd03dad581e2da2081659e4..b14e84239027878c9822ca001c5e5ce8c5fc64b4 100644 --- a/sites/all/modules/module_filter/js/module_filter_tab.js +++ b/sites/all/modules/module_filter/js/module_filter_tab.js @@ -5,6 +5,40 @@ Drupal.ModuleFilter.tabs = {}; Drupal.ModuleFilter.enabling = {}; Drupal.ModuleFilter.disabling = {}; +Drupal.ModuleFilter.jQueryIsNewer = function() { + if (Drupal.ModuleFilter.jQueryNewer == undefined) { + var v1parts = $.fn.jquery.split('.'); + var v2parts = new Array('1', '4', '4'); + + for (var i = 0; i < v1parts.length; ++i) { + if (v2parts.length == i) { + Drupal.ModuleFilter.jQueryNewer = true; + return Drupal.ModuleFilter.jQueryNewer; + } + + if (v1parts[i] == v2parts[i]) { + continue; + } + else if (v1parts[i] > v2parts[i]) { + Drupal.ModuleFilter.jQueryNewer = true; + return Drupal.ModuleFilter.jQueryNewer; + } + else { + Drupal.ModuleFilter.jQueryNewer = false; + return Drupal.ModuleFilter.jQueryNewer; + } + } + + if (v1parts.length != v2parts.length) { + Drupal.ModuleFilter.jQueryNewer = false; + return Drupal.ModuleFilter.jQueryNewer; + } + + Drupal.ModuleFilter.jQueryNewer = false; + } + return Drupal.ModuleFilter.jQueryNewer; +}; + Drupal.behaviors.moduleFilterTabs = { attach: function(context) { if (Drupal.settings.moduleFilter.tabs) { @@ -39,6 +73,14 @@ Drupal.behaviors.moduleFilterTabs = { summary += '<span>' + Drupal.t('No modules added within the last week.') + '</span>'; } break; + case 'recent': + name = Drupal.t('Recent'); + title = Drupal.t('Modules enabled/disabled within the last week.'); + if (Drupal.settings.moduleFilter.enabledCounts['recent'].total == 0) { + tabClass += ' disabled'; + summary += '<span>' + Drupal.t('No modules were enabled or disabled within the last week.') + '</span>'; + } + break; default: var $row = $('#' + id + '-package'); name = $.trim($row.text()); @@ -90,6 +132,7 @@ Drupal.behaviors.moduleFilterTabs = { moduleFilter.element.bind('moduleFilter:start', function() { moduleFilter.tabResults = { 'all-tab': { items: {}, count: 0 }, + 'recent-tab': { items: {}, count: 0 }, 'new-tab': { items: {}, count: 0 } }; @@ -113,6 +156,11 @@ Drupal.behaviors.moduleFilterTabs = { // All tab moduleFilter.tabResults['all-tab'].count++; + // Recent tab + if (item.element.hasClass('recent-module')) { + moduleFilter.tabResults['recent-tab'].count++; + } + // New tab if (item.element.hasClass('new-module')) { moduleFilter.tabResults['new-tab'].count++; @@ -123,7 +171,7 @@ Drupal.behaviors.moduleFilterTabs = { } if (Drupal.ModuleFilter.activeTab != undefined && Drupal.ModuleFilter.activeTab.id != 'all-tab') { - if ((Drupal.ModuleFilter.activeTab.id == 'new-tab' && !item.element.hasClass('new-module')) || (Drupal.ModuleFilter.activeTab.id != 'new-tab' && id != Drupal.ModuleFilter.activeTab.id)) { + if ((Drupal.ModuleFilter.activeTab.id == 'recent-tab' && !item.element.hasClass('recent-module')) || (Drupal.ModuleFilter.activeTab.id == 'new-tab' && !item.element.hasClass('new-module')) || (Drupal.ModuleFilter.activeTab.id != 'recent-tab' && Drupal.ModuleFilter.activeTab.id != 'new-tab' && id != Drupal.ModuleFilter.activeTab.id)) { // The item is not in the active tab, so hide it. item.element.addClass('js-hide'); } @@ -183,13 +231,19 @@ Drupal.behaviors.moduleFilterTabs = { $('td.checkbox div.form-item').hide(); $('td.checkbox').each(function(i) { var $cell = $(this); + var $checkbox = $(':checkbox', $cell); var $switch = $('.toggle-enable', $cell); $switch.removeClass('js-hide').click(function() { if (!$(this).hasClass('disabled')) { - $(':checkbox', $cell).click().change(); + if (Drupal.ModuleFilter.jQueryIsNewer()) { + $checkbox.click(); + } + else { + $checkbox.click().change(); + } } }); - $(':checkbox', $cell).change(function() { + $checkbox.click(function() { if (!$switch.hasClass('disabled')) { $switch.toggleClass('off'); } diff --git a/sites/all/modules/module_filter/module_filter.admin.inc b/sites/all/modules/module_filter/module_filter.admin.inc index cd7d9e2da766576bd0bb7ccd60abcf2fe8073561..11a2f6bdd8c9a43923cf35d2d65829820842d827 100644 --- a/sites/all/modules/module_filter/module_filter.admin.inc +++ b/sites/all/modules/module_filter/module_filter.admin.inc @@ -70,6 +70,12 @@ function module_filter_settings() { '#description' => t('This is purely cosmetic (at least for now). Displays a ON/OFF switch rather than a checkbox to enable/disable modules.<br /><strong>Modules will not actually be enabled/disabled until the form is saved.</strong>'), '#default_value' => variable_get('module_filter_use_switch', 1), ); + $form['tabs']['module_filter_track_recent_modules'] = array( + '#type' => 'checkbox', + '#title' => t('Track recently enabled/disabled modules'), + '#description' => t('Adds a "Recent" tab that displays modules that have been enabled or disabled with the last week.'), + '#default_value' => variable_get('module_filter_track_recent_modules', 1), + ); $form['update'] = array( '#type' => 'fieldset', diff --git a/sites/all/modules/module_filter/module_filter.info b/sites/all/modules/module_filter/module_filter.info index f77610b22ff84bbeab914acfe0d66bbba76c540e..970265281a18ac8be5ff18278224bde0fd3a6e66 100644 --- a/sites/all/modules/module_filter/module_filter.info +++ b/sites/all/modules/module_filter/module_filter.info @@ -17,9 +17,9 @@ files[] = js/module_filter_tab.js configure = admin/config/user-interface/modulefilter -; Information added by drupal.org packaging script on 2012-11-05 +; Information added by drupal.org packaging script on 2013-01-05 version = "7.x-2.x-dev" core = "7.x" project = "module_filter" -datestamp = "1352121182" +datestamp = "1357349173" diff --git a/sites/all/modules/module_filter/module_filter.install b/sites/all/modules/module_filter/module_filter.install index ff37531d700bc9da57ca6ece0d9d59e0069b4ee7..6aa3dcf20107baad462eafc78226c7aec30d3e0d 100644 --- a/sites/all/modules/module_filter/module_filter.install +++ b/sites/all/modules/module_filter/module_filter.install @@ -16,6 +16,7 @@ function module_filter_uninstall() { variable_del('module_filter_dynamic_save_position'); variable_del('module_filter_use_url_fragment'); variable_del('module_filter_use_switch'); + variable_del('module_filter_track_recent_modules'); variable_del('module_filter_remember_update_state'); } diff --git a/sites/all/modules/module_filter/module_filter.module b/sites/all/modules/module_filter/module_filter.module index 05d8ab88771aea68b7b728858c4fa234f9b12132..39ca0e7fa122b9e7e5cb5740c8faa98858f4ed4c 100644 --- a/sites/all/modules/module_filter/module_filter.module +++ b/sites/all/modules/module_filter/module_filter.module @@ -83,6 +83,10 @@ function module_filter_form_system_modules_alter(&$form, &$form_state, $form_id) $form['#theme'] = 'module_filter_system_modules'; $form['#submit'][] = 'module_filter_system_modules_submit_redirect'; + + if (variable_get('module_filter_track_recent_modules', 1)) { + $form['#submit'][] = 'module_filter_system_modules_submit_recent'; + } } /** @@ -91,6 +95,7 @@ function module_filter_form_system_modules_alter(&$form, &$form_state, $form_id) function module_filter_form_user_admin_permissions_alter(&$form, &$form_state) { $form['module_filter'] = array( '#type' => 'module_filter', + '#description' => t('Filter list by module. Use the query operator "perm" to filter by permission, e.g., perm:access.'), '#attached' => array( 'js' => array( drupal_get_path('module', 'module_filter') . '/js/permissions.js', @@ -163,6 +168,7 @@ function form_process_module_filter($element, &$form_state) { 'dynamicPosition' => (!module_exists('page_actions')) ? variable_get('module_filter_dynamic_save_position', 1) : FALSE, 'useURLFragment' => variable_get('module_filter_use_url_fragment', 1), 'useSwitch' => variable_get('module_filter_use_switch', 1), + 'trackRecent' => variable_get('module_filter_track_recent_modules', 1), 'rememberUpdateState' => variable_get('module_filter_remember_update_state', 0), ) ), @@ -171,6 +177,9 @@ function form_process_module_filter($element, &$form_state) { ) ) ); + if (isset($element['#description'])) { + $element['name']['#description'] = $element['#description']; + } if (variable_get('module_filter_remember_update_state', 0)) { $element['name']['#attached']['js'][] = 'misc/jquery.cookie.js'; } @@ -193,6 +202,20 @@ function module_filter_system_modules_submit_redirect($form, &$form_state) { ); } +function module_filter_system_modules_submit_recent($form, &$form_state) { + $recent_modules = variable_get('module_filter_recent_modules', array()); + + foreach ($form_state['values']['modules'] as $package => $modules) { + foreach ($modules as $key => $module) { + if ($form['modules'][$package][$key]['enable']['#default_value'] != $module['enable']) { + $recent_modules[$key] = REQUEST_TIME; + } + } + } + + variable_set('module_filter_recent_modules', $recent_modules); +} + function module_filter_new_modules() { // Get current list of modules. $files = system_rebuild_module_data(); @@ -222,3 +245,7 @@ function module_filter_get_id($text) { $id = preg_replace('/([^a-z0-9]+)/', '-', $id); return trim($id, '-'); } + +function module_filter_recent_filter($var) { + return (!($var < REQUEST_TIME - 60*60*24*7)); +} diff --git a/sites/all/modules/module_filter/module_filter.theme.inc b/sites/all/modules/module_filter/module_filter.theme.inc index 22f764dc6df884a4953833832cf44819159a44ca..ac7e9d9960a4188df09fe6ff4ee1aeb7e87818ce 100644 --- a/sites/all/modules/module_filter/module_filter.theme.inc +++ b/sites/all/modules/module_filter/module_filter.theme.inc @@ -76,11 +76,22 @@ function theme_module_filter_system_modules_tabs($variables) { t('Version'), t('Description') ); - $package_ids = array('all', 'new'); - $enabled['all'] = $enabled['new'] = array(); + $package_ids = array('all'); + $enabled['all'] = array(); + + if (variable_get('module_filter_track_recent_modules', 1)) { + $recent_modules = array_filter(variable_get('module_filter_recent_modules', array()), 'module_filter_recent_filter'); + // Save the filtered results. + variable_get('module_filter_recent_modules', $recent_modules); + + $package_ids[] = 'recent'; + $enabled['recent'] = array(); + } // Determine what modules are new (within a week). $new_modules = module_filter_new_modules(); + $package_ids[] = 'new'; + $enabled['new'] = array(); $rows = array(); $flip = array('even' => 'odd', 'odd' => 'even'); @@ -99,6 +110,9 @@ function theme_module_filter_system_modules_tabs($variables) { $is_enabled = isset($module['enable']['#default_value']) ? $module['enable']['#default_value'] : ''; $enabled['all'][] = $enabled[$package_id][] = $is_enabled; + if (isset($recent_modules[$key])) { + $enabled['recent'][] = $is_enabled; + } if (isset($new_modules[$key])) { $enabled['new'][] = $is_enabled; } @@ -136,6 +150,9 @@ function theme_module_filter_system_modules_tabs($variables) { $row[] = array('data' => $description, 'class' => array('description')); $class = array(module_filter_get_id($package) . '-tab', 'module', $stripe); + if (isset($recent_modules[$key])) { + $class[] = 'recent-module'; + } if (isset($new_modules[$key])) { $class[] = 'new-module'; } diff --git a/sites/all/modules/options_element/options_element.inc b/sites/all/modules/options_element/options_element.inc index d6a90cc2e0c290ab96162a74c26edfacb2d03930..ed64743503c62a51bfe991da6e8cdd43452e09f0 100644 --- a/sites/all/modules/options_element/options_element.inc +++ b/sites/all/modules/options_element/options_element.inc @@ -133,13 +133,13 @@ function _form_options_expand($element) { '#rows' => 5, '#required' => isset($element['#required']) ? $element['#required'] : FALSE, '#description' => t('List options one option per line.'), - '#attributes' => $element['#disabled'] ? array('readonly' => 'readonly') : array(), + '#attributes' => $element['#options_readonly'] ? array('readonly' => 'readonly') : array(), '#wysiwyg' => FALSE, // Prevent CKeditor from trying to hijack. ); // If validation fails, reload the user's text even if it's not valid. - if (isset($element['#value']['text'])) { - $element['options_field']['#value'] = $element['#value']['text']; + if (isset($element['#value']['options_text'])) { + $element['options_field']['#value'] = $element['#value']['options_text']; } // Most of the time, we'll be converting the options array into the text. else { @@ -195,7 +195,8 @@ function _form_options_expand($element) { * @see form_options_validate() */ function _form_options_validate($element, &$form_state) { - // Convert text to an array of options. + // Even though we already have the converted options in #value['options'], run + // the conversion again to check for duplicates in the user-defined list. $duplicates = array(); $options = form_options_from_text($element['#value']['options_text'], $element['#key_type'], empty($element['#optgroups']), $duplicates); diff --git a/sites/all/modules/options_element/options_element.info b/sites/all/modules/options_element/options_element.info index d370e45105f5455b32b48763475e679a138cea55..9f566f94e64bfe51cc735166859659ee9373019d 100644 --- a/sites/all/modules/options_element/options_element.info +++ b/sites/all/modules/options_element/options_element.info @@ -2,9 +2,9 @@ name = Options element description = A custom form element for entering the options in select lists, radios, or checkboxes. core = 7.x -; Information added by drupal.org packaging script on 2012-03-17 -version = "7.x-1.7" +; Information added by drupal.org packaging script on 2012-09-13 +version = "7.x-1.8" core = "7.x" project = "options_element" -datestamp = "1332018945" +datestamp = "1347551745" diff --git a/sites/all/modules/options_element/options_element.js b/sites/all/modules/options_element/options_element.js index 0d71ae7fc76ea453e2cc77e560369cae7146195b..11ccb6a78820a14573537141870b2bc26e66e4e8 100644 --- a/sites/all/modules/options_element/options_element.js +++ b/sites/all/modules/options_element/options_element.js @@ -37,7 +37,8 @@ Drupal.optionsElement = function(element) { this.keyType = element.className.replace(/^.*?options-key-type-([a-z]+).*?$/, '$1'); this.customKeys = Boolean(element.className.match(/options-key-custom/)); this.identifier = this.manualOptionsElement.id + '-widget'; - this.enabled = $(this.manualOptionsElement).attr('readonly') == ''; + // jQuery 1.6 API change: http://api.jquery.com/prop/ + this.enabled = $.fn.prop ? !$(this.manualOptionsElement).prop('readonly') : !$(this.manualOptionsElement).attr('readonly'); this.defaultValuePattern = $(element).find('input.default-value-pattern').val(); if (this.defaultValuePattern) { diff --git a/sites/all/modules/options_element/options_element.module b/sites/all/modules/options_element/options_element.module index db007de545e2d5b186770925a770bb8bd0730cf7..d777164bca1495c07d6d4257065cc0343483fdba 100644 --- a/sites/all/modules/options_element/options_element.module +++ b/sites/all/modules/options_element/options_element.module @@ -12,7 +12,7 @@ * * The 'options' form element type is useful when collecting a series of * values in a list. The values within the list may optionally have unique - * keys, such as that in a array structure. In addition, a default choice + * keys, such as that in an array structure. In addition, a default choice * (or several default choices) may be selected by the user. * * @code @@ -89,13 +89,13 @@ function options_element_element_info() { '#optgroups' => TRUE, '#multiple' => FALSE, '#options' => array(), + '#options_readonly' => FALSE, '#key_type' => 'mixed', '#key_type_toggle' => NULL, '#key_type_toggled' => FALSE, '#default_value_allowed' => TRUE, '#default_value_pattern' => '', '#element_validate' => array('form_options_validate'), - '#disabled' => FALSE, ); return $type; diff --git a/sites/all/modules/token/tests/token_test.info b/sites/all/modules/token/tests/token_test.info index a344aa29bd899a1049cb917cd3805f1dea474581..141ed5cfcb339ccb1d25dc3fc7156229e8687eaa 100644 --- a/sites/all/modules/token/tests/token_test.info +++ b/sites/all/modules/token/tests/token_test.info @@ -5,9 +5,9 @@ core = 7.x files[] = token_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2012-09-24 -version = "7.x-1.4" +; Information added by drupal.org packaging script on 2013-02-24 +version = "7.x-1.5" core = "7.x" project = "token" -datestamp = "1348497279" +datestamp = "1361665026" diff --git a/sites/all/modules/token/token.drush.inc b/sites/all/modules/token/token.drush.inc new file mode 100644 index 0000000000000000000000000000000000000000..fc3235730d216f335682feef6749fbe1ef47d0de --- /dev/null +++ b/sites/all/modules/token/token.drush.inc @@ -0,0 +1,22 @@ +<?php + +/** + * @file + * Drush integration for the Token module. + */ + +/** + * Implements hook_drush_cache_clear(). + */ +function token_drush_cache_clear(&$types) { + if (function_exists('module_exists') && module_exists('token')) { + $types['token'] = 'drush_token_cache_clear_token_info'; + } +} + +/** + * Clear caches internal to Token module. + */ +function drush_token_cache_clear_token_info() { + token_clear_cache(); +} diff --git a/sites/all/modules/token/token.info b/sites/all/modules/token/token.info index 8d14814aca2c3bd773606c2d749be8d076bd8a2f..43fced193354ae6462affbd5fe79215aec4eee65 100644 --- a/sites/all/modules/token/token.info +++ b/sites/all/modules/token/token.info @@ -1,15 +1,11 @@ name = Token description = Provides a user interface for the Token API and some missing core tokens. core = 7.x -files[] = token.module -files[] = token.install -files[] = token.tokens.inc -files[] = token.pages.inc files[] = token.test -; Information added by drupal.org packaging script on 2012-09-24 -version = "7.x-1.4" +; Information added by drupal.org packaging script on 2013-02-24 +version = "7.x-1.5" core = "7.x" project = "token" -datestamp = "1348497279" +datestamp = "1361665026" diff --git a/sites/all/modules/token/token.js b/sites/all/modules/token/token.js index 88a75c1017e2e29cc8c0da42d73a989e43890f29..98d1ac3a5af68e585cdef59d75ed56bba34a694f 100644 --- a/sites/all/modules/token/token.js +++ b/sites/all/modules/token/token.js @@ -14,6 +14,12 @@ Drupal.behaviors.tokenDialog = { $('a.token-dialog', context).once('token-dialog').click(function() { var url = $(this).attr('href'); var dialog = $('<div style="display: none" class="loading">' + Drupal.t('Loading token browser...') + '</div>').appendTo('body'); + + // Emulate the AJAX data sent normally so that we get the same theme. + var data = {}; + data['ajax_page_state[theme]'] = Drupal.settings.ajaxPageState.theme; + data['ajax_page_state[theme_token]'] = Drupal.settings.ajaxPageState.theme_token; + dialog.dialog({ title: $(this).attr('title') || Drupal.t('Available tokens'), width: 700, @@ -24,7 +30,7 @@ Drupal.behaviors.tokenDialog = { // Load the token tree using AJAX. dialog.load( url, - {}, + data, function (responseText, textStatus, XMLHttpRequest) { dialog.removeClass('loading'); } diff --git a/sites/all/modules/token/token.module b/sites/all/modules/token/token.module index 7bba8a7427b71731961bbf3dd1a151af76781698..88bcc6093a476deda374beb4753021129b8d3acb 100644 --- a/sites/all/modules/token/token.module +++ b/sites/all/modules/token/token.module @@ -78,6 +78,7 @@ function token_menu() { 'access callback' => TRUE, 'type' => MENU_CALLBACK, 'file' => 'token.pages.inc', + 'theme callback' => 'ajax_base_page_theme', ); // Devel token pages. @@ -265,7 +266,7 @@ function token_form_block_admin_configure_alter(&$form, $form_state) { $form['settings']['title']['#description'] .= ' ' . t('This field supports tokens.'); // @todo Figure out why this token validation does not seem to be working here. $form['settings']['title']['#element_validate'][] = 'token_element_validate'; - $form['settings']['title']['#token_types'] = array(); + $form['settings']['title'] += array('#token_types' => array()); } /** @@ -377,23 +378,26 @@ function token_clear_cache() { * @see token_entity_info_alter() * @see http://drupal.org/node/737726 */ -function token_get_entity_mapping($value_type = 'token', $value = NULL) { +function token_get_entity_mapping($value_type = 'token', $value = NULL, $fallback = FALSE) { $mapping = &drupal_static(__FUNCTION__, array()); if (empty($mapping)) { foreach (entity_get_info() as $entity_type => $info) { $mapping[$entity_type] = !empty($info['token type']) ? $info['token type'] : $entity_type; } + // Allow modules to alter the mapping array. + drupal_alter('token_entity_mapping', $mapping); } if (!isset($value)) { - return $mapping; + return $value_type == 'token' ? array_flip($mapping) : $mapping; } elseif ($value_type == 'token') { - return array_search($value, $mapping); + $return = array_search($value, $mapping); + return $return !== FALSE ? $return : ($fallback ? $value : FALSE); } elseif ($value_type == 'entity') { - return isset($mapping[$value]) ? $mapping[$value] : FALSE; + return isset($mapping[$value]) ? $mapping[$value] : ($fallback ? $value : FALSE); } } @@ -739,24 +743,28 @@ function token_element_validate_token_context(&$element, &$form_state) { * Implements hook_form_FORM_ID_alter(). */ function token_form_field_ui_field_edit_form_alter(&$form, $form_state) { - if (!isset($form['instance'])) { + if (!isset($form['instance']) || !empty($form['#field']['locked'])) { return; } if (($form['#field']['type'] == 'file' || $form['#field']['type'] == 'image') && isset($form['instance']['settings']['file_directory']) && !module_exists('filefield_paths')) { // GAH! We can only support global tokens in the upload file directory path. $form['instance']['settings']['file_directory']['#element_validate'][] = 'token_element_validate'; - $form['instance']['settings']['file_directory']['#token_types'] = array(); - $form['instance']['settings']['token_tree'] = array( - '#theme' => 'token_tree', - '#token_types' => array(), - '#weight' => $form['instance']['settings']['file_directory']['#weight'] + 0.5, - ); + $form['instance']['settings']['file_directory'] += array('#token_types' => array()); $form['instance']['settings']['file_directory']['#description'] .= ' ' . t('This field supports tokens.'); } // Note that the description is tokenized via token_field_widget_form_alter(). $form['instance']['description']['#description'] .= '<br />' . t('This field supports tokens.'); + $form['instance']['description']['#element_validate'][] = 'token_element_validate'; + $form['instance']['description'] += array('#token_types' => array()); + + $form['instance']['settings']['token_tree'] = array( + '#theme' => 'token_tree', + '#token_types' => array(), + '#dialog' => TRUE, + '#weight' => $form['instance']['description']['#weight'] + 0.5, + ); } /** @@ -775,6 +783,7 @@ function token_form_system_actions_configure_alter(&$form, $form_state) { $form['token_tree'] = array( '#theme' => 'token_tree', '#token_types' => 'all', + '#dialog' => TRUE, '#weight' => 100, ); // @todo Add token validation to the action fields that can use tokens. @@ -836,10 +845,11 @@ function token_form_user_admin_settings_alter(&$form, &$form_state) { } // Add the token tree UI. - $form['token_tree'] = array( + $form['email']['token_tree'] = array( '#theme' => 'token_tree', '#token_types' => array('user'), '#show_restricted' => TRUE, + '#dialog' => TRUE, '#weight' => 90, ); } diff --git a/sites/all/modules/token/token.pages.inc b/sites/all/modules/token/token.pages.inc index 905943ca074a3392a8e4a5c1de9d5f79da80d821..2341a9f981c6dc6c1ff88dff2d626a4bef2a734f 100644 --- a/sites/all/modules/token/token.pages.inc +++ b/sites/all/modules/token/token.pages.inc @@ -57,7 +57,7 @@ function token_page_output_tree() { $options['dialog'] = FALSE; $output = theme('token_tree', $options); - print '<html><head><title></title>' . drupal_get_css() . drupal_get_js() . '</head>'; + print '<html><head>' . drupal_get_css() . drupal_get_js() . '</head>'; print '<body class="token-tree">' . $output . '</body></html>'; drupal_exit(); } @@ -231,7 +231,7 @@ function _token_clean_css_identifier($id) { /** * Menu callback; prints the available tokens and values for an object. */ -function token_devel_token_object($entity_type, $entity) { +function token_devel_token_object($entity_type, $entity, $token_type = NULL) { $header = array( t('Token'), t('Value'), @@ -243,7 +243,10 @@ function token_devel_token_object($entity_type, $entity) { 'values' => TRUE, 'data' => array($entity_type => $entity), ); - $tree = token_build_tree($entity_type, $options); + if (!isset($token_type)) { + $token_type = $entity_type; + } + $tree = token_build_tree($token_type, $options); foreach ($tree as $token => $token_info) { if (!empty($token_info['restricted'])) { continue; diff --git a/sites/all/modules/token/token.test b/sites/all/modules/token/token.test index b7e9582425595cee521d51ff5593161fd542817e..59fa6457b5e937186a520d7e26833675c9061def 100644 --- a/sites/all/modules/token/token.test +++ b/sites/all/modules/token/token.test @@ -676,10 +676,12 @@ class TokenEntityTestCase extends TokenTestHelper { $this->assertIdentical(token_get_entity_mapping('token', 'term'), 'taxonomy_term'); $this->assertIdentical(token_get_entity_mapping('token', 'vocabulary'), 'taxonomy_vocabulary'); $this->assertIdentical(token_get_entity_mapping('token', 'invalid'), FALSE); + $this->assertIdentical(token_get_entity_mapping('token', 'invalid', TRUE), 'invalid'); $this->assertIdentical(token_get_entity_mapping('entity', 'node'), 'node'); $this->assertIdentical(token_get_entity_mapping('entity', 'taxonomy_term'), 'term'); $this->assertIdentical(token_get_entity_mapping('entity', 'taxonomy_vocabulary'), 'vocabulary'); $this->assertIdentical(token_get_entity_mapping('entity', 'invalid'), FALSE); + $this->assertIdentical(token_get_entity_mapping('entity', 'invalid', TRUE), 'invalid'); // Test that when we send the mis-matched entity type into token_replace() // that we still get the tokens replaced. diff --git a/sites/all/modules/token/token.tokens.inc b/sites/all/modules/token/token.tokens.inc index 3dc2d3b4f210a70f5c9da7507bad25f11f743042..e0c0b5e91305c98e549b199573779bd777cbf8c5 100644 --- a/sites/all/modules/token/token.tokens.inc +++ b/sites/all/modules/token/token.tokens.inc @@ -80,7 +80,7 @@ function token_token_info_alter(&$info) { foreach ($date_format_types as $date_format_type => $date_format_type_info) { if (!isset($info['tokens']['date'][$date_format_type])) { $info['tokens']['date'][$date_format_type] = array( - 'name' => $date_format_type_info['title'], + 'name' => check_plain($date_format_type_info['title']), 'description' => t("A date in '@type' format. (%date)", array('@type' => $date_format_type, '%date' => format_date(REQUEST_TIME, $date_format_type))), 'module' => 'token', ); diff --git a/sites/all/modules/views/js/views-admin.js b/sites/all/modules/views/js/views-admin.js index e945429994ddcc852ebc0928bfa18e6a40203fbe..2b4ccf339411fadd6298c91a39ed14f71c86f607 100644 --- a/sites/all/modules/views/js/views-admin.js +++ b/sites/all/modules/views/js/views-admin.js @@ -255,10 +255,11 @@ Drupal.behaviors.viewsUiRenderAddViewButton.attach = function (context, settings // away from the item. We use mouseleave instead of mouseout because // the user is going to trigger mouseout when she moves from the trigger // link to the sub menu items. - // We use the live binder because the open class on this item will be + // + // We use the 'li.add' selector because the open class on this item will be // toggled on and off and we want the handler to take effect in the cases // that the class is present, but not when it isn't. - $('li.add', $menu).live('mouseleave', function (event) { + $menu.delegate('li.add', 'mouseleave', function (event) { var $this = $(this); var $trigger = $this.children('a[href="#"]'); if ($this.children('.action-list').is(':visible')) { diff --git a/sites/all/modules/views/modules/taxonomy/views_plugin_argument_validate_taxonomy_term.inc b/sites/all/modules/views/modules/taxonomy/views_plugin_argument_validate_taxonomy_term.inc index ba1a0e746875040013468803b4d2613708ac1903..435db0ddff84be02ffc55ddc6277a67cd5d369f7 100644 --- a/sites/all/modules/views/modules/taxonomy/views_plugin_argument_validate_taxonomy_term.inc +++ b/sites/all/modules/views/modules/taxonomy/views_plugin_argument_validate_taxonomy_term.inc @@ -96,7 +96,6 @@ class views_plugin_argument_validate_taxonomy_term extends views_plugin_argument $query = db_select('taxonomy_term_data', 'td'); $query->leftJoin('taxonomy_vocabulary', 'tv', 'td.vid = tv.vid'); $query->fields('td'); - $query->fields('tv', array('machine_name')); $query->condition('td.tid', $argument); $query->addTag('term_access'); $term = $query->execute()->fetchObject(); @@ -105,7 +104,7 @@ class views_plugin_argument_validate_taxonomy_term extends views_plugin_argument } $term = taxonomy_term_load($term->tid); $this->argument->validated_title = check_plain(entity_label('taxonomy_term', $term)); - return empty($vocabularies) || !empty($vocabularies[$term->machine_name]); + return empty($vocabularies) || !empty($vocabularies[$term->vocabulary_machine_name]); case 'tids': // An empty argument is not a term so doesn't pass. diff --git a/sites/all/modules/views/plugins/views_plugin_display_page.inc b/sites/all/modules/views/plugins/views_plugin_display_page.inc index 6ab95b710e906eab7895a6bcc5885fb4e4745cd9..7ca4bf7fe9057887750e1a09026c23833c751a8a 100644 --- a/sites/all/modules/views/plugins/views_plugin_display_page.inc +++ b/sites/all/modules/views/plugins/views_plugin_display_page.inc @@ -30,6 +30,7 @@ class views_plugin_display_page extends views_plugin_display { 'weight' => array('default' => 0), 'name' => array('default' => variable_get('menu_default_node_menu', 'navigation')), 'context' => array('default' => ''), + 'context_only_inline' => array('default' => FALSE), ), ); $options['tab_options'] = array( @@ -153,7 +154,7 @@ class views_plugin_display_page extends views_plugin_display { // Add context for contextual links. // @see menu_contextual_links() if (!empty($menu['context'])) { - $items[$path]['context'] = MENU_CONTEXT_INLINE; + $items[$path]['context'] = !empty($menu['context_only_inline']) ? MENU_CONTEXT_INLINE : (MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE); } // If this is a 'default' tab, check to see if we have to create teh @@ -386,12 +387,23 @@ class views_plugin_display_page extends views_plugin_display { ); $form['menu']['context'] = array( '#title' => t('Context'), - '#suffix' => '</div>', '#type' => 'checkbox', '#default_value' => !empty($menu['context']), '#description' => t('Displays the link in contextual links'), '#dependency' => array('radio:menu[type]' => array('tab')), ); + $form['menu']['context_only_inline'] = array( + '#title' => t('Hide menu tab'), + '#suffix' => '</div>', + '#type' => 'checkbox', + '#default_value' => !empty($menu['context_only_inline']), + '#description' => t('Only display menu item entry in contextual links. Menu tab should not be displayed.'), + '#dependency' => array( + 'radio:menu[type]' => array('tab'), + 'edit-menu-context' => array(1), + ), + '#dependency_count' => 2, + ); break; case 'tab_options': $form['#title'] .= t('Default tab options'); diff --git a/sites/all/modules/views/tests/views_test.info b/sites/all/modules/views/tests/views_test.info index 444ccc9c9db55ae4111330d8475f01e399a95d53..23bd884fdd4f6730c25dfa3d25debbf5aec037fa 100644 --- a/sites/all/modules/views/tests/views_test.info +++ b/sites/all/modules/views/tests/views_test.info @@ -5,9 +5,9 @@ core = 7.x dependencies[] = views hidden = TRUE -; Information added by drupal.org packaging script on 2013-03-20 -version = "7.x-3.6" +; Information added by drupal.org packaging script on 2013-04-09 +version = "7.x-3.7" core = "7.x" project = "views" -datestamp = "1363810217" +datestamp = "1365499236" diff --git a/sites/all/modules/views/views.api.php b/sites/all/modules/views/views.api.php index 33690e2fb705db1ce1b577549d80133fc1ca40f9..ba9b326f9601172fbbced7f6565be036ee675080 100644 --- a/sites/all/modules/views/views.api.php +++ b/sites/all/modules/views/views.api.php @@ -482,13 +482,15 @@ function hook_views_data_alter(&$data) { $data['users']['example_field'] = array( 'title' => t('Example field'), 'help' => t('Some example content that references a user'), - 'handler' => 'hook_handlers_field_example_field', + 'field' => array( + 'handler' => 'modulename_handler_field_example_field', + ), ); // This example changes the handler of the node title field. // In this handler you could do stuff, like preview of the node when clicking // the node title. - $data['node']['title']['handler'] = 'modulename_handlers_field_node_title'; + $data['node']['title']['field']['handler'] = 'modulename_handler_field_node_title'; // This example adds a relationship to table {foo}, so that 'foo' views can // add this table using a relationship. Because we don't want to write over diff --git a/sites/all/modules/views/views.info b/sites/all/modules/views/views.info index ceb1aeed14f64c7b14dcfa6c967a5a517a3f5233..50d814829ac0b5d2b6ff9ab96f6913835442b4b8 100644 --- a/sites/all/modules/views/views.info +++ b/sites/all/modules/views/views.info @@ -312,9 +312,9 @@ files[] = tests/views_cache.test files[] = tests/views_view.test files[] = tests/views_ui.test -; Information added by drupal.org packaging script on 2013-03-20 -version = "7.x-3.6" +; Information added by drupal.org packaging script on 2013-04-09 +version = "7.x-3.7" core = "7.x" project = "views" -datestamp = "1363810217" +datestamp = "1365499236" diff --git a/sites/all/modules/views/views_ui.info b/sites/all/modules/views/views_ui.info index b746aa43ae0729caf8672a4033f0e889ad5473e2..95ea014918c8f7f514b09984186194dac0d4185d 100644 --- a/sites/all/modules/views/views_ui.info +++ b/sites/all/modules/views/views_ui.info @@ -7,9 +7,9 @@ dependencies[] = views files[] = views_ui.module files[] = plugins/views_wizard/views_ui_base_views_wizard.class.php -; Information added by drupal.org packaging script on 2013-03-20 -version = "7.x-3.6" +; Information added by drupal.org packaging script on 2013-04-09 +version = "7.x-3.7" core = "7.x" project = "views" -datestamp = "1363810217" +datestamp = "1365499236"