diff --git a/modules/data.eval.inc b/modules/data.eval.inc index bbeddfa..7d5a0d1 100644 --- a/modules/data.eval.inc +++ b/modules/data.eval.inc @@ -190,42 +190,89 @@ function rules_action_data_convert($arguments, RulesPlugin $element, $state) { $value_info = $element->getArgumentInfo('value'); $from_type = $value_info['type']; $target_type = $arguments['type']; - - // First apply the rounding behavior if given. - if (isset($arguments['rounding_behavior'])) { - switch ($arguments['rounding_behavior']) { - case 'up': - $arguments['value'] = ceil($arguments['value']); - break; - case 'down': - $arguments['value'] = floor($arguments['value']); - break; - default: - case 'round': - $arguments['value'] = round($arguments['value']); - break; - } + if ($from_type == 'date' && !empty($arguments['date_format'])) { + $result = rules_action_data_convert_date2string($arguments); } + else { + // First apply the rounding behavior if given. + if (isset($arguments['rounding_behavior'])) { + switch ($arguments['rounding_behavior']) { + case 'up': + $arguments['value'] = ceil($arguments['value']); + break; + case 'down': + $arguments['value'] = floor($arguments['value']); + break; + default: + case 'round': + $arguments['value'] = round($arguments['value']); + break; + } + } - switch ($target_type) { - case 'decimal': - $result = floatval($arguments['value']); - break; - case 'integer': - $result = intval($arguments['value']); - break; - case 'text': - $result = strval($arguments['value']); - break; + switch ($target_type) { + case 'decimal': + $result = floatval($arguments['value']); + break; + case 'integer': + $result = intval($arguments['value']); + break; + case 'text': + $result = strval($arguments['value']); + break; + } } return array('conversion_result' => $result); } /** + * Converts a date to a string, using format and timezone. + * Code inspired of Views module's date field renderer. + * + * @see views_handler_field_date::render() + */ +function rules_action_data_convert_date2string($arguments) { + $value = $arguments['value']; + $format = $arguments['date_format']; + if (in_array($format, _rules_action_date_to_string_custom_formats(TRUE))) { + $custom_format = $arguments['custom_date_format']; + } + + if ($value) { + $timezone = !empty($arguments['timezone']) ? $arguments['timezone'] : NULL; + $time_diff = REQUEST_TIME - $value; // will be positive for a datetime in the past (ago), and negative for a datetime in the future (hence) + switch ($format) { + case 'raw time ago': + return format_interval($time_diff, is_numeric($custom_format) ? $custom_format : 2); + case 'time ago': + return t('%time ago', array('%time' => format_interval($time_diff, is_numeric($custom_format) ? $custom_format : 2))); + case 'raw time hence': + return format_interval(-$time_diff, is_numeric($custom_format) ? $custom_format : 2); + case 'time hence': + return t('%time hence', array('%time' => format_interval(-$time_diff, is_numeric($custom_format) ? $custom_format : 2))); + case 'raw time span': + return ($time_diff < 0 ? '-' : '') . format_interval(abs($time_diff), is_numeric($custom_format) ? $custom_format : 2); + case 'inverse time span': + return ($time_diff > 0 ? '-' : '') . format_interval(abs($time_diff), is_numeric($custom_format) ? $custom_format : 2); + case 'time span': + return t(($time_diff < 0 ? '%time hence' : '%time ago'), array('%time' => format_interval(abs($time_diff), is_numeric($custom_format) ? $custom_format : 2))); + case 'custom': + if ($custom_format == 'r') { + return format_date($value, $format, $custom_format, $timezone, 'en'); + } + return format_date($value, $format, $custom_format, $timezone); + default: + return format_date($value, $format, '', $timezone); + } + } +} + +/** * Info alteration callback for variable add action. */ function rules_action_data_convert_info_alter(&$element_info, RulesAbstractPlugin $element) { + module_load_include('inc', 'rules', 'modules/data.rules'); if (isset($element->settings['type']) && $type = $element->settings['type']) { $element_info['provides']['conversion_result']['type'] = $type; @@ -235,6 +282,62 @@ function rules_action_data_convert_info_alter(&$element_info, RulesAbstractPlugi unset($element_info['parameter']['rounding_behavior']); } + if (array_key_exists('value:select', $element->settings)) { + $wrapper = $element->applyDataSelector($element->settings['value:select']); + if (!empty($wrapper)) { + $info = $wrapper->info(); + if ($info['type'] == 'date' && $type == 'text') { + $element_info['parameter']['date_format'] = array( + 'type' => 'token', + 'label' => t('Date format'), + 'description' => t('The date format the conversion should use.'), + 'options list' => 'rules_action_date_to_string_format_options', + 'restriction' => 'input', + 'optional' => TRUE, + 'default value' => '', + ); + $custom_formats = _rules_action_date_to_string_custom_formats(TRUE); + if (!array_key_exists('date_format', $element->settings)) { + $element->settings['date_format'] = ''; + } + if (in_array($element->settings['date_format'], $custom_formats)) { + $element_info['parameter']['custom_date_format'] = array( + 'type' => 'text', + 'label' => t('Custom date format'), + 'description' => $element->settings['date_format'] == 'custom' ? t('See the PHP manual for date formats.', array('@url' => 'http://php.net/manual/function.date.php')) : t('Enter the number of different time units to display, which defaults to 2.'), + 'default value' => '', + 'optional' => $element->settings['date_format'] != 'custom', + ); + } + else { + unset($element_info['parameter']['custom_date_format']); + } + // Only system date formats and 'custom' can have a timezone. + $timezoneable_formats = _rules_action_date_to_string_system_formats(TRUE); + $timezoneable_formats[] = 'custom'; + if (in_array($element->settings['date_format'], $timezoneable_formats)) { + $element_info['parameter']['timezone'] = array( + 'type' => 'token', + 'label' => t('Timezone'), + 'description' => t('Timezone to be used for date output.'), + 'options list' => 'rules_action_date_to_string_timezone_options', + 'restriction' => 'input', + 'optional' => TRUE, + 'default value' => '', + ); + } + else { + unset($element_info['parameter']['timezone']); + } + } + else { + foreach (array('date_format', 'custom_date_format', 'timezone') as $key) { + unset($element_info['parameter'][$key]); + } + } + } + } + // Configure compatible source-types: switch ($type) { case 'integer': diff --git a/modules/data.rules.inc b/modules/data.rules.inc index e78420b..9e12b96 100644 --- a/modules/data.rules.inc +++ b/modules/data.rules.inc @@ -228,7 +228,7 @@ function rules_data_action_info() { 'base' => 'rules_action_data_convert', 'named parameter' => TRUE, 'callbacks' => array( - 'form_alter' => 'rules_action_type_form_alter', + 'form_alter' => 'rules_action_data_convert_form_alter', ), ); return $return; @@ -257,6 +257,67 @@ function rules_action_data_convert_rounding_behavior_options(RulesPlugin $elemen } /** + * Date to string conversion action: Options list callback for date format. + * Code inspired of Views module's date field options. + * + * @see views_handler_field_date::options_form() + */ +function rules_action_date_to_string_format_options(RulesPlugin $element = NULL, $param_name = '') { + $date_formats = array( + '' => t('Linux timestamp'), + ); + $date_formats += _rules_action_date_to_string_system_formats(); + $date_formats += _rules_action_date_to_string_custom_formats(); + return $date_formats; +} + +/** + * Returns system date formats. + * + * @see rules_action_date_to_string_format_options() + */ +function _rules_action_date_to_string_system_formats($keys = FALSE) { + $result = array(); + $date_types = system_get_date_types(); + foreach ($date_types as $key => $value) { + $result[$value['type']] = t('@date_format format', array('@date_format' => $value['title'])) . ': ' . format_date(REQUEST_TIME, $value['type']); + } + if ($keys) { + $result = array_keys($result); + } + return $result; +} + +/** + * Returns date formats that accept a custom parameter. + * + * @see rules_action_date_to_string_format_options() + */ +function _rules_action_date_to_string_custom_formats($keys = FALSE) { + $result = array( + 'custom' => t('Custom'), + 'raw time ago' => t('Time ago'), + 'time ago' => t('Time ago (with "ago" appended)'), + 'raw time hence' => t('Time hence'), + 'time hence' => t('Time hence (with "hence" appended)'), + 'raw time span' => t('Time span (future dates have "-" prepended)'), + 'inverse time span' => t('Time span (past dates have "-" prepended)'), + 'time span' => t('Time span (with "ago/hence" appended)'), + ); + if ($keys) { + $result = array_keys($result); + } + return $result; +} + +/** + * Date to string conversion action: Options list callback for date timezone. + */ +function rules_action_date_to_string_timezone_options(RulesPlugin $element = NULL, $param_name = '') { + return array('' => t('- Default site/user timezone -')) + system_time_zones(FALSE); +} + +/** * Customize access check for data set action. */ function rules_action_data_set_access(RulesAbstractPlugin $element) { @@ -365,7 +426,6 @@ function rules_data_list_form_alter(&$form, &$form_state, $options, RulesAbstrac } } - /** * Form alter callback for actions relying on the entity type or the data type. */ @@ -407,6 +467,100 @@ function rules_action_type_form_alter(&$form, &$form_state, $options, RulesAbstr } /** + * Form alter callback for data convert action. + */ +function rules_action_data_convert_form_alter(&$form, &$form_state, $options, RulesAbstractPlugin $element) { + if (empty($element->settings['type'])) { + $step = 1; + } + elseif (empty($element->settings['value:select'])) { + $step = 2; + } + else { + $step = 3; + } + + $form['reload'] = array( + '#weight' => 5, + '#type' => 'submit', + '#name' => 'reload', + '#value' => $step != 3 ? t('Continue') : t('Reload form'), + '#limit_validation_errors' => array(array('parameter', 'type')), + '#submit' => array('rules_action_type_form_submit_rebuild'), + '#ajax' => rules_ui_form_default_ajax(), + ); + // Use ajax and trigger as the reload button. + $form['parameter']['type']['settings']['type']['#ajax'] = $form['reload']['#ajax'] + array( + 'event' => 'change', + 'trigger_as' => array('name' => 'reload'), + ); + // Use ajax on date_format button too, if available. + if (!empty($form['parameter']['date_format'])) { + $form['parameter']['date_format']['settings']['date_format']['#ajax'] = $form['reload']['#ajax'] + array( + 'event' => 'change', + 'trigger_as' => array('name' => 'reload'), + ); + } + + if ($step != 3) { + // In two first steps, disable submit button and "provides variables" section. + unset($form['submit']); + unset($form['provides']); + // Also disable #ajax as they have troubles with lazy-loaded JS. + // @todo: Re-enable once JS lazy-loading is fixed in core. + unset($form['parameter']['type']['settings']['type']['#ajax']); + unset($form['reload']['#ajax']); + } + + switch ($step) { + case 1: + // In the first step show only the type select. + $keys = array('type'); + $remove = array_diff(element_children($form['parameter']), $keys); + foreach ($remove as $key) { + unset($form['parameter'][$key]); + } + break; + case 2: + // In the second step, show only type select and value field. + $keys = array('type', 'value'); + $remove = array_diff(element_children($form['parameter']), $keys); + foreach ($remove as $key) { + unset($form['parameter'][$key]); + } + break; + case 3: + // Get the user to reload the form to load date-specific fields if needed, + // as we have no way to auto-refresh when a value to convert is set. + if (array_key_exists('value:select', $element->settings)) { + $wrapper = $element->applyDataSelector($element->settings['value:select']); + if (!empty($wrapper)) { + $info = $wrapper->info(); + if ($element->settings['type'] == 'text' && $info['type'] != 'date') { + $form['parameter']['value']['#suffix'] = '
' . t('If converting a date to text, please reload the form to display date-specific settings.') . '
'; + } + else { + $form['parameter']['value']['#suffix'] = '
' . t('If not converting a date to text, please reload the form to hide date-specific settings.') . '
'; + } + } + } + // Make sure the date-specific elements stay in a constant order. + $date_elements = array( + 'date_format' => 2, + 'custom_date_format' => 4, + 'timezone' => 6, + ); + foreach ($date_elements as $date_element => $weight) { + if (!empty($form['parameter'][$date_element])) { + $form['parameter'][$date_element]['#weight'] = $weight; + } + } + break; + default: break; // Did something go wrong? + } +} + +/** * FAPI submit callback for reloading the type form for entities or data types. */ function rules_action_type_form_submit_rebuild($form, &$form_state) {