Index: includes/date_formats.inc =================================================================== RCS file: includes/date_formats.inc diff -N includes/date_formats.inc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ includes/date_formats.inc 15 Sep 2009 08:10:17 -0000 @@ -0,0 +1,197 @@ + 'short', + 'format' => 'Y-m-d H:i', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'short', + 'format' => 'm/d/Y - H:i', + 'locales' => array('en-us',), + ); + $formats[] = array( + 'type' => 'short', + 'format' => 'd/m/Y - H:i', + 'locales' => array('en-gb', 'en-hk', 'en-ie', 'el-gr', 'es-es', 'fr-be', 'fr-fr', 'fr-lu', 'it-it', 'nl-be', 'pt-pt',), + ); + $formats[] = array( + 'type' => 'short', + 'format' => 'Y/m/d - H:i', + 'locales' => array('en-ca', 'fr-ca', 'no-no', 'sv-se',), + ); + $formats[] = array( + 'type' => 'short', + 'format' => 'd.m.Y - H:i', + 'locales' => array('de-ch', 'de-de', 'de-lu', 'fi-fi', 'fr-ch', 'is-is', 'pl-pl', 'ro-ro', 'ru-ru',), + ); + $formats[] = array( + 'type' => 'short', + 'format' => 'm/d/Y - g:ia', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'short', + 'format' => 'd/m/Y - g:ia', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'short', + 'format' => 'Y/m/d - g:ia', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'short', + 'format' => 'M j Y - H:i', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'short', + 'format' => 'j M Y - H:i', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'short', + 'format' => 'Y M j - H:i', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'short', + 'format' => 'M j Y - g:ia', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'short', + 'format' => 'j M Y - g:ia', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'short', + 'format' => 'Y M j - g:ia', + 'locales' => array(), + ); + + // Medium date formats. + $formats[] = array( + 'type' => 'medium', + 'format' => 'D, Y-m-d H:i', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'medium', + 'format' => 'D, m/d/Y - H:i', + 'locales' => array('en-us',), + ); + $formats[] = array( + 'type' => 'medium', + 'format' => 'D, d/m/Y - H:i', + 'locales' => array('en-gb', 'en-hk', 'en-ie', 'el-gr', 'es-es', 'fr-be', 'fr-fr', 'fr-lu', 'it-it', 'nl-be', 'pt-pt',), + ); + $formats[] = array( + 'type' => 'medium', + 'format' => 'D, Y/m/d - H:i', + 'locales' => array('en-ca', 'fr-ca', 'no-no', 'sv-se',), + ); + $formats[] = array( + 'type' => 'medium', + 'format' => 'F j, Y - H:i', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'medium', + 'format' => 'j F, Y - H:i', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'medium', + 'format' => 'Y, F j - H:i', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'medium', + 'format' => 'D, m/d/Y - g:ia', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'medium', + 'format' => 'D, d/m/Y - g:ia', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'medium', + 'format' => 'D, Y/m/d - g:ia', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'medium', + 'format' => 'F j, Y - g:ia', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'medium', + 'format' => 'j F Y - g:ia', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'medium', + 'format' => 'Y, F j - g:ia', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'medium', + 'format' => 'j. F Y - G:i', + 'locales' => array(), + ); + + // Long date formats. + $formats[] = array( + 'type' => 'long', + 'format' => 'l, F j, Y - H:i', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'long', + 'format' => 'l, j F, Y - H:i', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'long', + 'format' => 'l, Y, F j - H:i', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'long', + 'format' => 'l, F j, Y - g:ia', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'long', + 'format' => 'l, j F Y - g:ia', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'long', + 'format' => 'l, Y, F j - g:ia', + 'locales' => array(), + ); + $formats[] = array( + 'type' => 'long', + 'format' => 'l, j. F Y - G:i', + 'locales' => array(), + ); + + return $formats; +} Index: includes/locale.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/locale.inc,v retrieving revision 1.226 diff -u -p -r1.226 locale.inc --- includes/locale.inc 22 Aug 2009 14:34:17 -0000 1.226 +++ includes/locale.inc 15 Sep 2009 08:10:20 -0000 @@ -2726,3 +2726,217 @@ function country_get_list() { return $countries; } +/** + * Display edit date format links for each language. + */ +function locale_date_format_language_overview_page() { + $header = array( + t('Language'), + array('data' => t('Operations'), 'colspan' => '2'), + ); + + // Get list of languages. + $languages = locale_language_list('native'); + + foreach ($languages as $langcode => $info) { + $row = array(); + $row[] = $languages[$langcode]; + $row[] = l(t('edit'), 'admin/config/regional/date-time/locale/' . $langcode . '/edit'); + $row[] = l(t('reset'), 'admin/config/regional/date-time/locale/' . $langcode . '/reset'); + $rows[] = $row; + } + + return theme('table', $header, $rows); +} + +/** + * Provide date localization configuration options to users. + */ +function locale_date_format_form($form_state, $langcode) { + $form = array(); + + $languages = locale_language_list('native'); + $language_name = $languages[$langcode]; + + // Display the current language name. + $form['language'] = array( + '#type' => 'item', + '#title' => t('Language'), + '#markup' => check_plain($language_name), + '#weight' => -10, + ); + $form['langcode'] = array( + '#type' => 'value', + '#value' => $langcode, + ); + + // Get list of date format types. + $types = system_get_date_types(); + + // Get list of available formats. + $formats = system_get_date_formats(); + $choices = array(); + foreach ($formats as $type => $list) { + foreach ($list as $f => $format) { + $choices[$f] = format_date(REQUEST_TIME, 'custom', $f); + } + } + + // Get configured formats for each language. + $locale_formats = system_date_format_locale($langcode); + // Display a form field for each format type. + foreach ($types as $type => $type_info) { + if (!empty($locale_formats) && in_array($type, array_keys($locale_formats))) { + $default = $locale_formats[$type]; + } + else { + $default = variable_get('date_format_' . $type, array_shift(array_keys($formats))); + } + + // Show date format select list. + $form['date_formats']['date_format_' . $type] = array( + '#type' => 'select', + '#title' => check_plain($type_info['title']), + '#attributes' => array('class' => array('date-format')), + '#default_value' => (isset($choices[$default]) ? $default : 'custom'), + '#options' => $choices, + ); + } + + $form['buttons']['submit'] = array( + '#type' => 'submit', + '#value' => t('Save configuration'), + ); + + return $form; +} + +/** + * Submit handler for configuring localized date formats on the locale_date_format_form. + */ +function locale_date_format_form_submit($form, &$form_state) { + $langcode = $form_state['values']['langcode']; + + // Get list of date format types. + $types = system_get_date_types(); + foreach ($types as $type => $type_info) { + $format = $form_state['values']['date_format_' . $type]; + if ($format == 'custom') { + $format = $form_state['values']['date_format_' . $type . '_custom']; + } + locale_date_format_save($langcode, $type, $format); + } + drupal_set_message(t('Configuration saved.')); + $form_state['redirect'] = 'admin/config/regional/date-time/locale'; +} + +/** + * Save locale specific date formats to the database. + * + * @param $langcode + * Language code, can be 2 characters, e.g. 'en' or 5 characters, e.g. + * 'en-CA'. + * @param $type + * Date format type, e.g. 'short', 'medium'. + * @param $format + * The date format string. + */ +function locale_date_format_save($langcode, $type, $format) { + $locale_format = array(); + $locale_format['language'] = $langcode; + $locale_format['type'] = $type; + $locale_format['format'] = $format; + + $is_existing = (bool) db_query_range('SELECT 1 FROM {date_format_locale} WHERE language = :langcode AND type = :type', array(':langcode' => $langcode, ':type' => $type), 0, 1)->fetchField(); + if ($is_existing) { + $keys = array('type', 'language'); + drupal_write_record('date_format_locale', $locale_format, $keys); + } + else { + drupal_write_record('date_format_locale', $locale_format); + } +} + +/** + * Reset locale specific date formats to the global defaults. + * + * @param $langcode + * Language code, e.g. 'en'. + */ +function locale_date_format_reset_form(&$form_state, $langcode) { + $form['langcode'] = array('#type' => 'value', '#value' => $langcode); + $languages = language_list(); + return confirm_form($form, + t('Are you sure you want to reset the date formats for %language to the global defaults?', array('%language' => $languages[$langcode]->name)), + 'admin/config/regional/date-time/locale', + t('Resetting will remove all localized date formats for this language. This action cannot be undone.'), + t('Reset'), t('Cancel')); +} + +/** + * Reset date formats for a specific language to global defaults. + */ +function locale_date_format_reset_form_submit($form, &$form_state) { + db_delete('date_format_locale') + ->condition('language', $form_state['values']['langcode']) + ->execute(); + $form_state['redirect'] = 'admin/config/regional/date-time/locale'; +} + +/** + * Select locale date format details from database. + * + * @param $languages + * An array of language codes. + * @return + * An array of date formats. + */ +function locale_get_localized_date_format($languages) { + $formats = array(); + + // Get list of different format types. + $format_types = system_get_date_types(); + $short_default = variable_get('date_format_short', 'm/d/Y - H:i'); + + // Loop through each language until we find one with some date formats + // configured. + foreach ($languages as $language) { + $date_formats = system_date_format_locale($language); + if (!empty($date_formats)) { + // We have locale-specific date formats, so check for their types. If + // we're missing a type, use the default setting instead. + foreach ($format_types as $type => $type_info) { + // If format exists for this language, use it. + if (!empty($date_formats[$type])) { + $formats['date_format_' . $type] = $date_formats[$type]; + } + // Otherwise get default variable setting. If this is not set, default + // to the short format. + else { + $formats['date_format_' . $type] = variable_get('date_format_' . $type, $short_default); + } + } + + // Return on the first match. + return $formats; + } + } + + // No locale specific formats found, so use defaults. + $system_types = array('short', 'medium', 'long'); + // Handle system types separately as they have defaults if no variable exists. + $formats['date_format_short'] = $short_default; + $formats['date_format_medium'] = variable_get('date_format_medium', 'D, m/d/Y - H:i'); + $formats['date_format_long'] = variable_get('date_format_long', 'l, F j, Y - H:i'); + + // For non-system types, get the default setting, otherwise use the short + // format. + foreach ($format_types as $type => $type_info) { + if (!in_array($type, $system_types)) { + $formats['date_format_' . $type] = variable_get('date_format_' . $type, $short_default); + } + } + + return $formats; +} + Index: modules/locale/locale.module =================================================================== RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v retrieving revision 1.258 diff -u -p -r1.258 locale.module --- modules/locale/locale.module 31 Aug 2009 17:06:09 -0000 1.258 +++ modules/locale/locale.module 15 Sep 2009 08:10:21 -0000 @@ -184,10 +184,67 @@ function locale_menu() { 'file path' => 'includes', ); + // Localize date formats. + $items['admin/config/regional/date-time/locale'] = array( + 'title' => 'Localize', + 'description' => 'Configure date formats for each locale', + 'page callback' => 'locale_date_format_language_overview_page', + 'access arguments' => array('administer site configuration'), + 'type' => MENU_LOCAL_TASK, + 'weight' => -8, + 'file' => 'locale.inc', + 'file path' => 'includes', + ); + $items['admin/config/regional/date-time/locale/%/edit'] = array( + 'title' => 'Localize date formats', + 'description' => 'Configure date formats for each locale', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('locale_date_format_form', 5), + 'access arguments' => array('administer site configuration'), + 'type' => MENU_CALLBACK, + 'file' => 'locale.inc', + 'file path' => 'includes', + ); + $items['admin/config/regional/date-time/locale/%/reset'] = array( + 'title' => 'Reset date formats', + 'description' => 'Reset localized date formats to global defaults', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('locale_date_format_reset_form', 5), + 'access arguments' => array('administer site configuration'), + 'type' => MENU_CALLBACK, + 'file' => 'locale.inc', + 'file path' => 'includes', + ); + return $items; } /** + * Implements hook_init(). + * + * Initialize date formats according to the user's current locale. + */ +function locale_init() { + include_once DRUPAL_ROOT . '/includes/locale.inc'; + + global $conf; + global $language; + + // Don't do this on the general date and time formats settings page, as we + // want to display the defaults, not the ones specific to the language we're + // currently browsing the site in. + if (!drupal_match_path($_GET['q'], 'admin/config/regional/date-time/formats')) { + $languages = array($language->language); + + // Setup appropriate date formats for this locale. + $formats = locale_get_localized_date_format($languages); + foreach ($formats as $format_type => $format) { + $conf[$format_type] = $format; + } + } +} + +/** * Wrapper function to be able to set callbacks in locale.inc */ function locale_inc_callback() { @@ -340,6 +397,9 @@ function locale_theme() { 'locale_translation_filters' => array( 'arguments' => array('form' => array()), ), + 'locale_date_format_form' => array( + 'arguments' => array('form' => array()), + ), ); } @@ -681,3 +741,30 @@ function theme_locale_translation_filter $output .= '
' . drupal_render($form['buttons']) . '
'; return $output; } + +/** + * Theme locale date format form. + * + * @ingroup themeable + */ +function theme_locale_date_format_form($form) { + $header = array( + t('Date type'), + t('Format'), + ); + + foreach (element_children($form['date_formats']) as $key) { + $row = array(); + $row[] = $form['date_formats'][$key]['#title']; + unset($form['date_formats'][$key]['#title']); + $row[] = array('data' => drupal_render($form['date_formats'][$key])); + $rows[] = $row; + } + + $output = drupal_render($form['language']); + $output .= theme('table', $header, $rows); + $output .= drupal_render_children($form); + + return $output; +} + Index: modules/locale/locale.test =================================================================== RCS file: /cvs/drupal/drupal/modules/locale/locale.test,v retrieving revision 1.40 diff -u -p -r1.40 locale.test --- modules/locale/locale.test 28 Aug 2009 14:40:12 -0000 1.40 +++ modules/locale/locale.test 15 Sep 2009 08:10:25 -0000 @@ -1601,3 +1601,73 @@ class UILanguageNegotiationTest extends $this->assertText($test['expect'], $test['message']); } } + +/** + * Functional tests for localizing date formats. + */ +class LocalizeDateFormatsFunctionalTest extends DrupalWebTestCase { + + public static function getInfo() { + return array( + 'name' => 'Localize date formats', + 'description' => 'Tests for the localization of date formats.', + 'group' => 'Locale', + ); + } + + function setUp() { + parent::setUp('locale'); + + // Create and login user. + $admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer languages', 'access administration pages', 'create article content')); + $this->drupalLogin($admin_user); + } + + /** + * Functional tests for localizing date formats. + */ + function testLocalizeDateFormats() { + // Add language. + $edit = array( + 'langcode' => 'fr', + ); + $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); + + // Set language negotiation. + $edit = array( + 'language_negotiation' => LANGUAGE_NEGOTIATION_PATH_DEFAULT, + ); + $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); + + // Configure date formats. + $this->drupalGet('admin/config/regional/date-time/locale'); + $this->assertText('Français', 'Configured languages appear.'); + $edit = array( + 'date_format_long' => 'd.m.Y - H:i', + 'date_format_medium' => 'd.m.Y - H:i', + 'date_format_short' => 'd.m.Y - H:i', + ); + $this->drupalPost('admin/config/regional/date-time/locale/fr/edit', $edit, t('Save configuration')); + $this->assertText(t('Configuration saved.'), 'French date formats updated.'); + $edit = array( + 'date_format_long' => 'j M Y - g:ia', + 'date_format_medium' => 'j M Y - g:ia', + 'date_format_short' => 'j M Y - g:ia', + ); + $this->drupalPost('admin/config/regional/date-time/locale/en/edit', $edit, t('Save configuration')); + $this->assertText(t('Configuration saved.'), 'English date formats updated.'); + + // Create node content. + $node = $this->drupalCreateNode(array('type' => 'article')); + + // Configure format for the node posted date changes with the language. + $this->drupalGet('node/' . $node->nid); + $english_date = format_date(REQUEST_TIME, 'custom', 'j M Y'); + $this->assertText($english_date, t('English date format appears')); + $this->drupalGet('fr/node/' . $node->nid); + $french_date = format_date(REQUEST_TIME, 'custom', 'd.m.Y'); + $this->assertText($french_date, t('French date format appears')); + } +} + + Index: modules/system/system.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.admin.inc,v retrieving revision 1.201 diff -u -p -r1.201 system.admin.inc --- modules/system/system.admin.inc 11 Sep 2009 04:09:26 -0000 1.201 +++ modules/system/system.admin.inc 15 Sep 2009 08:10:26 -0000 @@ -1589,16 +1589,13 @@ function system_rss_feeds_settings() { } /** - * Form builder; Configure the site date and time settings. + * Form builder; Configure the site regional settings. * * @ingroup forms * @see system_settings_form() * @see system_regional_settings_submit() */ function system_regional_settings() { - drupal_add_js(drupal_get_path('module', 'system') . '/system.js'); - drupal_add_js(array('dateTime' => array('lookup' => url('admin/config/regional/settings/lookup'))), 'setting'); - include_once DRUPAL_ROOT . '/includes/locale.inc'; $countries = country_get_list(); // Add a 'No default country' option to the start of the list. @@ -1607,31 +1604,6 @@ function system_regional_settings() { // Date settings: $zones = system_time_zones(); - // Date settings: possible date formats - $date_short = array('Y-m-d H:i', 'm/d/Y - H:i', 'd/m/Y - H:i', 'Y/m/d - H:i', - 'd.m.Y - H:i', 'm/d/Y - g:ia', 'd/m/Y - g:ia', 'Y/m/d - g:ia', - 'M j Y - H:i', 'j M Y - H:i', 'Y M j - H:i', - 'M j Y - g:ia', 'j M Y - g:ia', 'Y M j - g:ia'); - $date_medium = array('D, Y-m-d H:i', 'D, m/d/Y - H:i', 'D, d/m/Y - H:i', - 'D, Y/m/d - H:i', 'F j, Y - H:i', 'j F, Y - H:i', 'Y, F j - H:i', - 'D, m/d/Y - g:ia', 'D, d/m/Y - g:ia', 'D, Y/m/d - g:ia', - 'F j, Y - g:ia', 'j F Y - g:ia', 'Y, F j - g:ia', 'j. F Y - G:i'); - $date_long = array('l, F j, Y - H:i', 'l, j F, Y - H:i', 'l, Y, F j - H:i', - 'l, F j, Y - g:ia', 'l, j F Y - g:ia', 'l, Y, F j - g:ia', 'l, j. F Y - G:i'); - - // Date settings: construct choices for user - foreach ($date_short as $f) { - $date_short_choices[$f] = format_date(REQUEST_TIME, 'custom', $f); - } - foreach ($date_medium as $f) { - $date_medium_choices[$f] = format_date(REQUEST_TIME, 'custom', $f); - } - foreach ($date_long as $f) { - $date_long_choices[$f] = format_date(REQUEST_TIME, 'custom', $f); - } - - $date_long_choices['custom'] = $date_medium_choices['custom'] = $date_short_choices['custom'] = t('Custom format'); - $form['locale'] = array( '#type' => 'fieldset', '#title' => t('Locale'), @@ -1696,97 +1668,243 @@ function system_regional_settings() { '#description' => t('Only applied if users may set their own time zone.') ); - $form['date_formats'] = array( - '#type' => 'fieldset', - '#title' => t('Date formats'), - ); + $form = system_settings_form($form, FALSE); + // We will call system_settings_form_submit() manually, so remove it for now. + unset($form['#submit']); + return $form; +} - $date_format_short = variable_get('date_format_short', $date_short[1]); - $form['date_formats']['date_format_short'] = array( - '#prefix' => '
', - '#suffix' => '
', - '#type' => 'select', - '#title' => t('Short date format'), - '#attributes' => array('class' => array('date-format')), - '#default_value' => (isset($date_short_choices[$date_format_short]) ? $date_format_short : 'custom'), - '#options' => $date_short_choices, +/** + * Process system_regional_settings form submissions. + */ +function system_regional_settings_submit($form, &$form_state) { + if ($form_state['values']['date_format_short'] == 'custom') { + $form_state['values']['date_format_short'] = $form_state['values']['date_format_short_custom']; + } + if ($form_state['values']['date_format_medium'] == 'custom') { + $form_state['values']['date_format_medium'] = $form_state['values']['date_format_medium_custom']; + } + if ($form_state['values']['date_format_long'] == 'custom') { + $form_state['values']['date_format_long'] = $form_state['values']['date_format_long_custom']; + } + return system_settings_form_submit($form, $form_state); +} + +/** + * Form builder; Configure the site date and time settings. + * + * @ingroup forms + * @see system_settings_form() + * @see system_date_time_settings_submit() + */ +function system_date_time_settings() { + // Get list of all available date types. + $format_types = system_get_date_types('', TRUE); + + // Get list of all available date formats. + $all_formats = array(); + $date_formats = system_get_date_formats('', TRUE); // Call this to rebuild the list, and to have default list. + foreach ($date_formats as $type => $format_info) { + $all_formats = array_merge($all_formats, $format_info); + } + $custom_formats = system_get_date_formats('custom'); + if (!empty($format_types)) { + foreach ($format_types as $type => $type_info) { + // If a system type, only show the available formats for that type and + // custom ones. + if ($type_info['locked'] == 1) { + $formats = system_get_date_formats($type); + if (empty($formats)) { + $formats = $all_formats; + } + elseif (!empty($custom_formats)) { + $formats = array_merge($formats, $custom_formats); + } + } + // If a user configured type, show all available date formats. + else { + $formats = $all_formats; + } + + $choices = array(); + foreach ($formats as $f => $format) { + $choices[$f] = format_date(REQUEST_TIME, 'custom', $f); + } + $default = variable_get('date_format_' . $type, array_shift(array_keys($formats))); + + // Get date type info for this date type. + $type_info = system_get_date_types($type); + + // Show date format select list. + $form['formats']['date_format_' . $type] = array( + '#type' => 'select', + '#title' => check_plain($type_info['title']), + '#attributes' => array('class' => array('date-format')), + '#default_value' => (isset($choices[$default]) ? $default : 'custom'), + '#options' => $choices, + ); + + // If this isn't a system provided type, allow the user to remove it from + // the system. + if ($type_info['locked'] == 0) { + $form['delete']['date_format_' . $type . '_delete'] = array( + '#markup' => l(t('delete'), 'admin/config/regional/date-time/types/delete/' . $type), + ); + } + } + } + + // Display a message if no date types configured. + $form['#empty_text'] = t('No date types available. Add date type.', array('@link' => url('admin/config/regional/date-time/types/add'))); + + $form = system_settings_form($form, FALSE); + // We will call system_settings_form_submit() manually, so remove it for now. + unset($form['#submit']); + unset($form['#theme']); + return $form; +} + +/** + * Process system_date_time_settings form submissions. + */ +function system_date_time_settings_submit($form, &$form_state) { + return system_settings_form_submit($form, $form_state); +} + +/** + * Theme function for date settings form. + * + * @form + * An associative array containing the structure of the form. + * @ingroup themeable + */ +function theme_system_date_time_settings($form) { + $header = array( + t('Date type'), + t('Format'), + t('Operations'), ); - $default_short_custom = variable_get('date_format_short_custom', (isset($date_short_choices[$date_format_short]) ? $date_format_short : '')); - $form['date_formats']['date_format_short_custom'] = array( - '#prefix' => '
', - '#suffix' => '
', + foreach (element_children($form['formats']) as $key) { + $delete_key = $key . '_delete'; + $row = array(); + $row[] = $form['formats'][$key]['#title']; + unset($form['formats'][$key]['#title']); + $row[] = array('data' => drupal_render($form['formats'][$key])); + $row[] = array('data' => drupal_render($form['delete'][$delete_key])); + $rows[] = $row; + } + + $output = theme('table', $header, $rows); + $output .= drupal_render_children($form); + + return $output; +} + + +/** + * Add new date type. + * + * @ingroup forms + * @ingroup system_add_date_format_type_form_validate() + * @ingroup system_add_date_format_type_form_submit() + */ +function system_add_date_format_type_form() { + $form['date_type'] = array( + '#title' => t('Date type'), '#type' => 'textfield', - '#title' => t('Custom short date format'), - '#attributes' => array('class' => array('custom-format')), - '#default_value' => $default_short_custom, - '#description' => t('A user-defined short date format. See the PHP manual for available options. This format is currently set to display as %date.', array('@url' => 'http://php.net/manual/function.date.php', '%date' => format_date(REQUEST_TIME, 'custom', $default_short_custom))), + '#required' => TRUE, + '#field_suffix' => '  ', ); - - $date_format_medium = variable_get('date_format_medium', $date_medium[1]); - $form['date_formats']['date_format_medium'] = array( - '#prefix' => '
', - '#suffix' => '
', - '#type' => 'select', - '#title' => t('Medium date format'), - '#attributes' => array('class' => array('date-format')), - '#default_value' => (isset($date_medium_choices[$date_format_medium]) ? $date_format_medium : 'custom'), - '#options' => $date_medium_choices, + $js_settings = array( + 'type' => 'setting', + 'data' => array( + 'machineReadableValue' => array( + 'date-type' => array( + 'text' => t('Machine name'), + 'target' => 'machine-name', + 'searchPattern' => '[^a-z0-9]+', + 'replaceToken' => '_', + ), + ), + ), ); - - $default_medium_custom = variable_get('date_format_medium_custom', (isset($date_medium_choices[$date_format_medium]) ? $date_format_medium : '')); - $form['date_formats']['date_format_medium_custom'] = array( - '#prefix' => '
', - '#suffix' => '
', + $form['machine_name'] = array( + '#title' => t('Machine readable name'), + '#description' => t('The unique machine readable name for this date type, can only contain lowercase letters, numbers and underscores.'), '#type' => 'textfield', - '#title' => t('Custom medium date format'), - '#attributes' => array('class' => array('custom-format')), - '#default_value' => $default_medium_custom, - '#description' => t('A user-defined medium date format. See the PHP manual for available options. This format is currently set to display as %date.', array('@url' => 'http://php.net/manual/function.date.php', '%date' => format_date(REQUEST_TIME, 'custom', $default_medium_custom))), + '#required' => TRUE, + '#attached' => array( + 'js' => array(drupal_get_path('module', 'system') . '/system.js', $js_settings), + ), ); - $date_format_long = variable_get('date_format_long', $date_long[0]); - $form['date_formats']['date_format_long'] = array( - '#prefix' => '
', - '#suffix' => '
', + // Get list of all available date formats. + $formats = array(); + $date_formats = system_get_date_formats('', TRUE); // Call this to rebuild the list, and to have default list. + foreach ($date_formats as $type => $format_info) { + $formats = array_merge($formats, $format_info); + } + $custom_formats = system_get_date_formats('custom'); + if (!empty($custom_formats)) { + $formats = array_merge($formats, $custom_formats); + } + $choices = array(); + foreach ($formats as $f => $format) { + $choices[$f] = format_date(REQUEST_TIME, 'custom', $f); + } + // Show date format select list. + $form['date_format'] = array( '#type' => 'select', - '#title' => t('Long date format'), + '#title' => t('Date format'), '#attributes' => array('class' => array('date-format')), - '#default_value' => (isset($date_long_choices[$date_format_long]) ? $date_format_long : 'custom'), - '#options' => $date_long_choices, + '#options' => $choices, + '#required' => TRUE, ); - $default_long_custom = variable_get('date_format_long_custom', (isset($date_long_choices[$date_format_long]) ? $date_format_long : '')); - $form['date_formats']['date_format_long_custom'] = array( - '#prefix' => '
', - '#suffix' => '
', - '#type' => 'textfield', - '#title' => t('Custom long date format'), - '#attributes' => array('class' => array('custom-format')), - '#default_value' => $default_long_custom, - '#description' => t('A user-defined long date format. See the PHP manual for available options. This format is currently set to display as %date.', array('@url' => 'http://php.net/manual/function.date.php', '%date' => format_date(REQUEST_TIME, 'custom', $default_long_custom))), + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Add date type'), ); - $form = system_settings_form($form, FALSE); - // We will call system_settings_form_submit() manually, so remove it for now. - unset($form['#submit']); + $form['#validate'][] = 'system_add_date_format_type_form_validate'; + $form['#submit'][] = 'system_add_date_format_type_form_submit'; + return $form; } /** - * Process system_regional_settings form submissions. + * Validate system_add_date_format_type form submissions. */ -function system_regional_settings_submit($form, &$form_state) { - if ($form_state['values']['date_format_short'] == 'custom') { - $form_state['values']['date_format_short'] = $form_state['values']['date_format_short_custom']; - } - if ($form_state['values']['date_format_medium'] == 'custom') { - $form_state['values']['date_format_medium'] = $form_state['values']['date_format_medium_custom']; - } - if ($form_state['values']['date_format_long'] == 'custom') { - $form_state['values']['date_format_long'] = $form_state['values']['date_format_long_custom']; +function system_add_date_format_type_form_validate($form, &$form_state) { + if (!empty($form_state['values']['machine_name']) && !empty($form_state['values']['date_type'])) { + if (!preg_match("/^[a-zA-Z0-9_]+$/", trim($form_state['values']['machine_name']))) { + form_set_error('machine_name', t('The date type must contain only alphanumeric characters and underscores.')); + } + $types = system_get_date_types(); + if (in_array(trim($form_state['values']['machine_name']), array_keys($types))) { + form_set_error('machine_name', t('This date type already exists. Please enter a unique type.')); + } } - return system_settings_form_submit($form, $form_state); +} + +/** + * Process system_add_date_format_type form submissions. + */ +function system_add_date_format_type_form_submit($form, &$form_state) { + include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'system') . '/' . 'system.date.inc'; + $machine_name = trim($form_state['values']['machine_name']); + + $format_type = array(); + $format_type['title'] = trim($form_state['values']['date_type']); + $format_type['type'] = $machine_name; + $format_type['locked'] = 0; + $format_type['is_new'] = 1; + system_date_format_type_save($format_type); + variable_set('date_format_' . $machine_name, $form_state['values']['date_format']); + + drupal_set_message(t('New date type added successfully.')); + $form_state['redirect'] = 'admin/config/regional/date-time'; } /** @@ -2300,3 +2418,185 @@ function theme_system_themes_form($form) $output .= drupal_render_children($form); return $output; } + +/** + * Menu callback; present a form for deleting a date format. + */ +function system_date_delete_format_form(&$form_state, $dfid) { + $form = array(); + $form['dfid'] = array( + '#type' => 'value', + '#value' => $dfid, + ); + $format = system_get_date_format($dfid); + + $output = confirm_form($form, + t('Are you sure you want to remove the format %format?', array('%format' => format_date(REQUEST_TIME, 'custom', $format->format))), + 'admin/config/regional/date-time/formats', + t('This action cannot be undone.'), + t('Remove'), t('Cancel'), + 'confirm' + ); + + return $output; +} + +/** + * Delete a configured date format. + */ +function system_date_delete_format_form_submit($form, &$form_state) { + include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'system') . '/' . 'system.date.inc'; + if ($form_state['values']['confirm']) { + $format = system_get_date_format($form_state['values']['dfid']); + system_date_format_delete($form_state['values']['dfid']); + drupal_set_message(t('Removed date format %format.', array('%format' => format_date(REQUEST_TIME, 'custom', $format->format)))); + $form_state['redirect'] = 'admin/config/regional/date-time/formats'; + } +} + +/** + * Menu callback; present a form for deleting a date type. + */ +function system_delete_date_format_type_form(&$form_state, $format_type) { + $form = array(); + $form['format_type'] = array( + '#type' => 'value', + '#value' => $format_type, + ); + $type_info = system_get_date_types($format_type); + + $output = confirm_form($form, + t('Are you sure you want to remove the date type %type?', array('%type' => $type_info['title'])), + 'admin/config/regional/date-time', + t('This action cannot be undone.'), + t('Remove'), t('Cancel'), + 'confirm' + ); + + return $output; +} + +/** + * Delete a configured date type. + */ +function system_delete_date_format_type_form_submit($form, &$form_state) { + include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'system') . '/' . 'system.date.inc'; + if ($form_state['values']['confirm']) { + $type_info = system_get_date_types($form_state['values']['format_type']); + system_date_format_type_delete($form_state['values']['format_type']); + drupal_set_message(t('Removed date type %type.', array('%type' => $type_info['title']))); + $form_state['redirect'] = 'admin/config/regional/date-time'; + } +} + + +/** + * Displays the date format strings overview page. + */ +function system_date_time_formats() { + $header = array(t('Format'), array('data' => t('Operations'), 'colspan' => '2')); + $rows = array(); + + $formats = system_get_date_formats('custom', TRUE); + if (!empty($formats)) { + foreach ($formats as $format) { + $row = array(); + $row[] = array('data' => format_date(REQUEST_TIME, 'custom', $format['format'])); + $row[] = array('data' => l(t('edit'), 'admin/config/regional/date-time/formats/edit/' . $format['dfid'])); + $row[] = array('data' => l(t('delete'), 'admin/config/regional/date-time/formats/delete/' . $format['dfid'])); + $rows[] = $row; + } + } + + if (empty($rows)) { + $rows[] = array(array('data' => t('No custom date formats available. Add date format.', array('@link' => url('admin/config/regional/date-time/formats/add'))), 'colspan' => '5', 'class' => array('message'))); + } + + $build['date_formats_table'] = array( + '#theme' => 'table', + '#header' => $header, + '#rows' => $rows + ); + + return $build; +} + +/** + * Allow users to add additional date formats. + */ +function system_configure_date_formats_form($edit, $dfid = 0) { + $js_settings = array( + 'type' => 'setting', + 'data' => array( + 'dateTime' => array( + 'date-format' => array( + 'text' => t('Displayed as'), + 'lookup' => url('admin/config/regional/date-time/formats/lookup'), + ), + ), + ), + ); + + if ($dfid) { + $form['dfid'] = array( + '#type' => 'value', + '#value' => $dfid, + ); + $format = system_get_date_format($dfid); + } + + $now = ($dfid ? t('Displayed as %date', array('%date' => format_date(REQUEST_TIME, 'custom', $format->format))) : ''); + + $form['date_format'] = array( + '#type' => 'textfield', + '#title' => t('Format string'), + '#description' => t('A user-defined date format. See the PHP manual for available options.', array('@url' => 'http://php.net/manual/function.date.php')), + '#default_value' => ($dfid ? $format->format : ''), + '#field_suffix' => ' ' . $now . '', + '#attached' => array( + 'js' => array(drupal_get_path('module', 'system') . '/system.js', $js_settings), + ), + ); + + $form['update'] = array( + '#type' => 'submit', + '#value' => ($dfid ? t('Save format') : t('Add format')), + ); + + $form['#validate'][] = 'system_add_date_formats_form_validate'; + $form['#submit'][] = 'system_add_date_formats_form_submit'; + + return $form; +} + +/** + * Validate new date format string submission. + */ +function system_add_date_formats_form_validate($form, &$form_state) { + $formats = system_get_date_formats('custom'); + $format = trim($form_state['values']['date_format']); + if (!empty($formats) && in_array($format, array_keys($formats)) && (!isset($form_state['values']['dfid']) || $form_state['values']['dfid'] != $formats[$format]['dfid'])) { + form_set_error('date_format', t('This format already exists. Please enter a unique format string.')); + } +} + +/** + * Process new date format string submission. + */ +function system_add_date_formats_form_submit($form, &$form_state) { + $format = array(); + $format['format'] = trim($form_state['values']['date_format']); + $format['type'] = 'custom'; + $format['locked'] = 0; + if (!empty($form_state['values']['dfid'])) { + system_date_format_save($format, $form_state['values']['dfid']); + drupal_set_message(t('Custom date format updated.')); + } + else { + $format['is_new'] = 1; + system_date_format_save($format); + drupal_set_message(t('Custom date format added.')); + } + + $form_state['redirect'] = 'admin/config/regional/date-time/formats'; +} Index: modules/system/system.date.inc =================================================================== RCS file: modules/system/system.date.inc diff -N modules/system/system.date.inc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/system/system.date.inc 15 Sep 2009 08:10:26 -0000 @@ -0,0 +1,238 @@ + $type_title) { + $type = array(); + $type['module'] = $module; + $type['type'] = $module_type; + $type['title'] = $type_title; + $type['locked'] = 1; + $type['is_new'] = TRUE; // Will be over-ridden later if in the db. + $types[$module_type] = $type; + } + } + + // Get custom formats added to the database by the end user. + $result = db_query('SELECT dft.type, dft.title, dft.locked FROM {date_format_types} dft ORDER BY dft.title'); + foreach ($result as $record) { + if (!in_array($record->type, $types)) { + $type = array(); + $type['is_new'] = FALSE; + $type['module'] = ''; + $type['type'] = $record->type; + $type['title'] = $record->title; + $type['locked'] = $record->locked; + $types[$record->type] = $type; + } + else { + $type = array(); + $type['is_new'] = FALSE; // Over-riding previous setting. + $types[$record->type] = array_merge($types[$record->type], $type); + } + } + + // Allow other modules to modify these date types. + drupal_alter('date_format_types', $types); + + return $types; +} + +/** + * Builds and returns the list of available date formats. + * + * @return + * Array of date formats. + */ +function _system_date_formats_build() { + $date_formats = array(); + + // First handle hook_date_format_types(). + $types = _system_date_format_types_build(); + foreach ($types as $type => $info) { + system_date_format_type_save($info); + } + + // Get formats supplied by various contrib modules. + $module_formats = module_invoke_all('date_formats'); + + foreach ($module_formats as $module_format) { + $module_format['locked'] = 1; // System types are locked. + // If no date type is specified, assign 'custom'. + if (!isset($module_format['type'])) { + $module_format['type'] = 'custom'; + } + if (!in_array($module_format['type'], array_keys($types))) { + continue; + } + if (!isset($date_formats[$module_format['type']])) { + $date_formats[$module_format['type']] = array(); + } + + // If another module already set this format, merge in the new settings. + if (isset($date_formats[$module_format['type']][$module_format['format']])) { + $date_formats[$module_format['type']][$module_format['format']] = array_merge_recursive($date_formats[$module_format['type']][$module_format['format']], $format); + } + else { + // This setting will be overridden later if it already exists in the db. + $module_format['is_new'] = TRUE; + $date_formats[$module_format['type']][$module_format['format']] = $module_format; + } + } + + // Get custom formats added to the database by the end user. + $result = db_query('SELECT df.dfid, df.format, df.type, df.locked, dfl.language FROM {date_formats} df LEFT JOIN {date_format_types} dft ON df.type = dft.type LEFT JOIN {date_format_locale} dfl ON df.format = dfl.format AND df.type = dfl.type ORDER BY df.type, df.format'); + foreach ($result as $record) { + // If this date type isn't set, initialise the array. + if (!isset($date_formats[$record->type])) { + $date_formats[$record->type] = array(); + } + // If this format not already present, add it to the array. + if (!isset($date_formats[$record->type][$record->format])) { + // We don't set 'is_new' as it is already in the db. + $format = array(); + $format['module'] = ''; + $format['dfid'] = $record->dfid; + $format['format'] = $record->format; + $format['type'] = $record->type; + $format['locked'] = $record->locked; + $format['locales'] = array($record->language); + $date_formats[$record->type][$record->format] = $format; + } + // Format already present, so merge in settings. + else { + $format = array(); + $format['is_new'] = FALSE; // It's in the db, so override this setting. + $format['dfid'] = $record->dfid; + $format['format'] = $record->format; + $format['type'] = $record->type; + $format['locked'] = $record->locked; + if (!empty($record->language)) { + $format['locales'] = array_merge($date_formats[$record->type][$record->format]['locales'], array($record->language)); + } + $date_formats[$record->type][$record->format] = array_merge($date_formats[$record->type][$record->format], $format); + } + } + + // Allow other modules to modify these formats. + drupal_alter('date_formats', $date_formats); + + return $date_formats; +} + +/** + * Save a date type to the database. + * + * @param $date_format_type + * An array of attributes for a date type. + */ +function system_date_format_type_save($date_format_type) { + $type = array(); + $type['type'] = $date_format_type['type']; + $type['title'] = $date_format_type['title']; + $type['locked'] = $date_format_type['locked']; + + // Update date_format table. + if (isset($date_format_type['is_new']) && !empty($date_format_type['is_new'])) { + drupal_write_record('date_format_types', $type); + } + else { + drupal_write_record('date_format_types', $type, 'type'); + } +} + +/** + * Delete a date type from the database. + * + * @param $date_format_type + * The date type name. + */ +function system_date_format_type_delete($date_format_type) { + db_delete('date_formats') + ->condition('type', $date_format_type) + ->execute(); + db_delete('date_format_types') + ->condition('type', $date_format_type) + ->execute(); + db_delete('date_format_locale') + ->condition('type', $date_format_type) + ->execute(); +} + +/** + * Save a date format to the database. + * + * @param $date_format + * An array of attributes for a date format. + * @param $dfid + * If set, replace an existing date format with a new string using this + * identifier. + */ +function system_date_format_save($date_format, $dfid = 0) { + $format = array(); + $format['dfid'] = $dfid; + $format['type'] = $date_format['type']; + $format['format'] = $date_format['format']; + $format['locked'] = $date_format['locked']; + + // Update date_format table. + if (isset($date_format['is_new']) && !empty($date_format['is_new'])) { + drupal_write_record('date_formats', $format); + } + else { + $keys = ($dfid ? array('dfid') : array('format', 'type')); + drupal_write_record('date_formats', $format, $keys); + } + + $languages = language_list('enabled'); + $languages = $languages[1]; + + $locale_format = array(); + $locale_format['type'] = $date_format['type']; + $locale_format['format'] = $date_format['format']; + + // Check if the suggested language codes are configured and enabled. + if (!empty($date_format['locales'])) { + foreach ($date_format['locales'] as $langcode) { + // Only proceed if language is enabled. + if (in_array($langcode, $languages)) { + $is_existing = (bool) db_query_range('SELECT 1 FROM {date_format_locale} WHERE type = :type AND language = :language', array(':type' => $date_format['type'], ':language' => $langcode), 0, 1)->fetchField(); + if (!$is_existing) { + $locale_format['language'] = $langcode; + drupal_write_record('date_format_locale', $locale_format); + } + } + } + } +} + +/** + * Delete a date format from the database. + * + * @param $date_format_id + * The date format string identifier. + */ +function system_date_format_delete($dfid) { + db_delete('date_formats') + ->condition('dfid', $dfid) + ->execute(); +} + Index: modules/system/system.install =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.install,v retrieving revision 1.385 diff -u -p -r1.385 system.install --- modules/system/system.install 10 Sep 2009 06:38:20 -0000 1.385 +++ modules/system/system.install 15 Sep 2009 08:10:28 -0000 @@ -691,6 +691,90 @@ function system_schema() { $schema['cache_registry'] = $schema['cache']; $schema['cache_registry']['description'] = 'Cache table for the code registry system to remember what code files need to be loaded on any given page.'; + $schema['date_format_types'] = array( + 'description' => 'For storing configured date format types.', + 'fields' => array( + 'type' => array( + 'description' => 'The date format type, e.g. medium.', + 'type' => 'varchar', + 'length' => 200, + 'not null' => TRUE, + ), + 'title' => array( + 'description' => 'The human readable name of the format type.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + ), + 'locked' => array( + 'description' => 'Whether or not this is a system provided format.', + 'type' => 'int', + 'size' => 'tiny', + 'default' => 0, + 'not null' => TRUE, + ), + ), + 'primary key' => array('type'), + ); + + $schema['date_formats'] = array( + 'description' => 'For storing configured date formats.', + 'fields' => array( + 'dfid' => array( + 'description' => 'The date format identifier.', + 'type' => 'serial', + 'not null' => TRUE, + 'unsigned' => TRUE, + ), + 'format' => array( + 'description' => 'The date format string.', + 'type' => 'varchar', + 'length' => 100, + 'not null' => TRUE, + ), + 'type' => array( + 'description' => 'The date format type, e.g. medium.', + 'type' => 'varchar', + 'length' => 200, + 'not null' => TRUE, + ), + 'locked' => array( + 'description' => 'Whether or not this format can be modified.', + 'type' => 'int', + 'size' => 'tiny', + 'default' => 0, + 'not null' => TRUE, + ), + ), + 'primary key' => array('dfid'), + 'unique keys' => array('formats' => array('format', 'type')), + ); + + $schema['date_format_locale'] = array( + 'description' => 'For storing configured date formats for each locale.', + 'fields' => array( + 'format' => array( + 'description' => 'The date format string.', + 'type' => 'varchar', + 'length' => 100, + 'not null' => TRUE, + ), + 'type' => array( + 'description' => 'The date format type, e.g. medium.', + 'type' => 'varchar', + 'length' => 200, + 'not null' => TRUE, + ), + 'language' => array( + 'description' => 'A {languages}.language for this format to be used with.', + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + ), + ), + 'primary key' => array('type', 'language'), + ); + $schema['file'] = array( 'description' => 'Stores information for uploaded files.', 'fields' => array( @@ -2472,6 +2556,103 @@ function system_update_7037() { } /** + * Create new date format tables. + */ +function system_update_7038() { + $ret = array(); + + $schema['date_format_types'] = array( + 'description' => 'For storing configured date format types.', + 'fields' => array( + 'type' => array( + 'description' => 'The date format type, e.g. medium.', + 'type' => 'varchar', + 'length' => 200, + 'not null' => TRUE, + ), + 'title' => array( + 'description' => 'The human readable name of the format type.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + ), + 'locked' => array( + 'description' => 'Whether or not this is a system provided format.', + 'type' => 'int', + 'size' => 'tiny', + 'default' => 0, + 'not null' => TRUE, + ), + ), + 'primary key' => array('type'), + ); + + $schema['date_formats'] = array( + 'description' => 'For storing configured date formats.', + 'fields' => array( + 'dfid' => array( + 'description' => 'The date format identifier.', + 'type' => 'serial', + 'not null' => TRUE, + 'unsigned' => TRUE, + ), + 'format' => array( + 'description' => 'The date format string.', + 'type' => 'varchar', + 'length' => 100, + 'not null' => TRUE, + ), + 'type' => array( + 'description' => 'The date format type, e.g. medium.', + 'type' => 'varchar', + 'length' => 200, + 'not null' => TRUE, + ), + 'locked' => array( + 'description' => 'Whether or not this format can be modified.', + 'type' => 'int', + 'size' => 'tiny', + 'default' => 0, + 'not null' => TRUE, + ), + ), + 'primary key' => array('dfid'), + 'unique keys' => array('formats' => array('format', 'type')), + ); + + $schema['date_format_locale'] = array( + 'description' => 'For storing configured date formats for each locale.', + 'fields' => array( + 'format' => array( + 'description' => 'The date format string.', + 'type' => 'varchar', + 'length' => 100, + 'not null' => TRUE, + ), + 'type' => array( + 'description' => 'The date format type, e.g. medium.', + 'type' => 'varchar', + 'length' => 200, + 'not null' => TRUE, + ), + 'language' => array( + 'description' => 'A {languages}.language for this format to be used with.', + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + ), + ), + 'primary key' => array('type', 'language'), + ); + + db_create_table($ret, 'date_format_types', $schema['date_format_types']); + db_create_table($ret, 'date_formats', $schema['date_formats']); + db_create_table($ret, 'date_format_locale', $schema['date_format_locale']); + + return $ret; +} + +/** * @} End of "defgroup updates-6.x-to-7.x" * The next series of updates should start at 8000. */ Index: modules/system/system.js =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.js,v retrieving revision 1.35 diff -u -p -r1.35 system.js --- modules/system/system.js 9 Sep 2009 21:53:15 -0000 1.35 +++ modules/system/system.js 15 Sep 2009 08:10:28 -0000 @@ -96,23 +96,21 @@ Drupal.behaviors.copyFieldValue = { * Show/hide custom format sections on the regional settings page. */ Drupal.behaviors.dateTime = { - attach: function (context, settings) { - // Show/hide custom format depending on the select's value. - $('select.date-format', context).once('date-time').change(function () { - $(this).parents('div.date-container').children('div.custom-container')[$(this).val() == 'custom' ? 'show' : 'hide'](); - }); + attach: function (context) { + for (var value in Drupal.settings.dateTime) { + var settings = Drupal.settings.dateTime[value]; + var source = '#edit-' + value; + var suffix = source + '-suffix'; - // Attach keyup handler to custom format inputs. - $('input.custom-format', context).once('date-time').keyup(function () { - var input = $(this); - var url = settings.dateTime.lookup + (settings.dateTime.lookup.match(/\?q=/) ? '&format=' : '?format=') + encodeURIComponent(input.val()); - $.getJSON(url, function (data) { - $('div.description span', input.parent()).html(data); + // Attach keyup handler to custom format inputs. + $('input' + source, context).once('date-time').keyup(function () { + var input = $(this); + var url = settings.lookup + (settings.lookup.match(/\?q=/) ? '&format=' : '?format=') + encodeURIComponent(input.val()); + $.getJSON(url, function (data) { + $(suffix).empty().append(' ' + settings.text + ': ' + data + ''); + }); }); - }); - - // Trigger the event handler to show the form input if necessary. - $('select.date-format', context).trigger('change'); + } } }; Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.789 diff -u -p -r1.789 system.module --- modules/system/system.module 11 Sep 2009 02:14:20 -0000 1.789 +++ modules/system/system.module 15 Sep 2009 08:10:33 -0000 @@ -101,7 +101,7 @@ function system_help($path, $arg) { $output .= '
  • ' . t('support for enabling and disabling themes, which determine the design and presentation of your site. Drupal comes packaged with several core themes and additional contributed themes are available at the Drupal.org theme page.', array('@themes' => url('admin/appearance'), '@drupal-themes' => 'http://drupal.org/project/themes')) . '
  • '; $output .= '
  • ' . t('a robust caching system that allows the efficient re-use of previously-constructed web pages and web page components. Drupal stores the pages requested by anonymous users in a compressed format; depending on your site configuration and the amount of your web traffic tied to anonymous visitors, Drupal\'s caching system may significantly increase the speed of your site.', array('@cache-settings' => url('admin/config/development/performance'))) . '
  • '; $output .= '
  • ' . t('a set of routine administrative operations that rely on a correctly-configured cron maintenance task to run automatically. A number of other modules, including the feed aggregator, and search also rely on cron maintenance tasks. For more information, see the online handbook entry for configuring cron jobs.', array('@cron' => url('admin/reports/status'), '@handbook' => 'http://drupal.org/cron')) . '
  • '; - $output .= '
  • ' . t('basic configuration options for your site, including date and time settings, file system settings, clean URL support, site name and other information, and a maintenance mode for taking your site temporarily offline.', array('@regional-settings' => url('admin/config/regional/settings'), '@file-system' => url('admin/config/media/file-system'), '@clean-url' => url('admin/config/search/clean-urls'), '@site-info' => url('admin/config/system/site-information'), '@maintenance-mode' => url('admin/config/development/maintenance'))) . '
  • '; + $output .= '
  • ' . t('basic configuration options for your site, including date and time settings, file system settings, clean URL support, site name and other information, and a maintenance mode for taking your site temporarily offline.', array('@date-time-settings' => url('admin/config/regional/date-time'), '@file-system' => url('admin/config/media/file-system'), '@clean-url' => url('admin/config/search/clean-urls'), '@site-info' => url('admin/config/system/site-information'), '@maintenance-mode' => url('admin/config/development/maintenance'))) . '
  • '; $output .= '

    ' . t('For more information, see the online handbook entry for System module.', array('@system' => 'http://drupal.org/handbook/modules/system/')) . '

    '; return $output; case 'admin/by-module': @@ -204,6 +204,10 @@ function system_theme() { 'system_run_cron_image' => array( 'arguments' => array('image_path' => NULL), ), + 'system_date_time_settings' => array( + 'arguments' => array('form' => NULL), + 'file' => 'system.admin.inc', + ), )); } @@ -773,6 +777,8 @@ function system_menu() { 'access arguments' => array('administer site configuration'), 'file' => 'system.admin.inc', ); + + // Regional and date settings. $items['admin/config/regional'] = array( 'title' => 'Regional and language', 'description' => 'Regional settings, localization and translation.', @@ -784,20 +790,96 @@ function system_menu() { ); $items['admin/config/regional/settings'] = array( 'title' => 'Regional settings', - 'description' => "Settings for how Drupal displays date and time, as well as the system's default time zone.", + 'description' => "Settings for the site's default time zone and country settings.", 'page callback' => 'drupal_get_form', 'page arguments' => array('system_regional_settings'), 'access arguments' => array('administer site configuration'), 'weight' => -10, 'file' => 'system.admin.inc', ); - $items['admin/config/regional/settings/lookup'] = array( + $items['admin/config/regional/date-time'] = array( + 'title' => 'Date and time', + 'description' => 'Configure display formats for date and time.', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('system_date_time_settings'), + 'access arguments' => array('administer site configuration'), + 'weight' => -9, + 'file' => 'system.admin.inc', + ); + $items['admin/config/regional/date-time/types'] = array( + 'title' => 'Types', + 'description' => 'Configure display formats for date and time.', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('system_date_time_settings'), + 'access arguments' => array('administer site configuration'), + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -10, + 'file' => 'system.admin.inc', + ); + $items['admin/config/regional/date-time/types/add'] = array( + 'title' => 'Add date type', + 'description' => 'Add new date type.', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('system_add_date_format_type_form'), + 'access arguments' => array('administer site configuration'), + 'type' => MENU_LOCAL_ACTION, + 'weight' => -10, + 'file' => 'system.admin.inc', + ); + $items['admin/config/regional/date-time/types/delete/%'] = array( + 'title' => 'Delete date type', + 'description' => 'Allow users to delete a configured date type.', + 'type' => MENU_CALLBACK, + 'page callback' => 'drupal_get_form', + 'page arguments' => array('system_delete_date_format_type_form', 6), + 'access arguments' => array('administer site configuration'), + 'file' => 'system.admin.inc', + ); + $items['admin/config/regional/date-time/formats'] = array( + 'title' => 'Formats', + 'description' => 'Configure display format strings for date and time.', + 'page callback' => 'system_date_time_formats', + 'access arguments' => array('administer site configuration'), + 'type' => MENU_LOCAL_TASK, + 'weight' => -9, + 'file' => 'system.admin.inc', + ); + $items['admin/config/regional/date-time/formats/add'] = array( + 'title' => 'Add format', + 'description' => 'Allow users to add additional date formats.', + 'type' => MENU_LOCAL_ACTION, + 'page callback' => 'drupal_get_form', + 'page arguments' => array('system_configure_date_formats_form'), + 'access arguments' => array('administer site configuration'), + 'weight' => -10, + 'file' => 'system.admin.inc', + ); + $items['admin/config/regional/date-time/formats/edit/%'] = array( + 'title' => 'Edit date format', + 'description' => 'Allow users to edit a configured date format.', + 'type' => MENU_CALLBACK, + 'page callback' => 'drupal_get_form', + 'page arguments' => array('system_configure_date_formats_form', 6), + 'access arguments' => array('administer site configuration'), + 'file' => 'system.admin.inc', + ); + $items['admin/config/regional/date-time/formats/delete/%'] = array( + 'title' => 'Delete date format', + 'description' => 'Allow users to delete a configured date format.', + 'type' => MENU_CALLBACK, + 'page callback' => 'drupal_get_form', + 'page arguments' => array('system_date_delete_format_form', 6), + 'access arguments' => array('administer site configuration'), + 'file' => 'system.admin.inc', + ); + $items['admin/config/regional/date-time/formats/lookup'] = array( 'title' => 'Date and time lookup', 'type' => MENU_CALLBACK, 'page callback' => 'system_date_time_lookup', 'access arguments' => array('administer site configuration'), 'file' => 'system.admin.inc', ); + $items['admin/config/search'] = array( 'title' => 'Search and metadata', 'description' => 'Local site search, metadata and SEO.', @@ -2391,6 +2473,9 @@ function system_cron() { foreach ($cache_tables as $table) { cache_clear_all(NULL, $table); } + + // Rebuild list of date formats. + system_date_formats_rebuild(); // Reset expired items in the default queue implementation table. If that's // not used, this will simply be a no-op. @@ -3158,3 +3243,141 @@ function theme_system_run_cron_image($im return ''; } +/** + * Get the list of available date types and attributes. + * + * @param $type + * The date type, e.g. 'short', 'medium', 'long', 'custom'. If empty, then + * all attributes for that type will be returned. + * @param $reset + * Whether or not to reset this function's internal cache (defaults to FALSE). + * @return + * Array of date types. + */ +function system_get_date_types($type = NULL, $reset = FALSE) { + static $_date_format_types; + + if ($reset || !isset($_date_format_types)) { + include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'system') . '/' . 'system.date.inc'; + $_date_format_types = _system_date_format_types_build(); + } + + return $type ? (isset($_date_format_types[$type]) ? $_date_format_types[$type] : FALSE) : $_date_format_types; +} + +/** + * Implements hook_date_format_types(). + */ +function system_date_format_types() { + return array( + 'long' => t('Long'), + 'medium' => t('Medium'), + 'short' => t('Short'), + ); +} + +/** + * Implements hook_date_formats(). + * + * @return + * An array of date formats with attributes 'type' (short, medium or long), + * 'format' (the format string) and 'locales'. The 'locales' attribute is an + * array of locales, which can include both 2 character language codes like + * 'en', 'fr', but also 5 character language codes like 'en-gb' and 'en-us'. + */ +function system_date_formats() { + include_once DRUPAL_ROOT . '/includes/date_formats.inc'; + return system_default_date_formats(); +} + +/** + * Get the list of date formats for a particular format length. + * + * @param $type + * The date type: 'short', 'medium', 'long', 'custom'. If empty, then all + * available types will be returned. + * @param $reset + * Whether or not to reset this function's internal cache (defaults to FALSE). + * @return + * Array of date formats. + */ +function system_get_date_formats($type = NULL, $reset = FALSE) { + static $_date_formats; + + if ($reset || !isset($_date_formats)) { + include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'system') . '/' . 'system.date.inc'; + $_date_formats = _system_date_formats_build(); + } + + return $type ? (isset($_date_formats[$type]) ? $_date_formats[$type] : FALSE) : $_date_formats; +} + +/** + * Get the format details for a particular id. + * + * @param $dfid + * Identifier of a date format string. + * @return + * Array of date format details. + */ +function system_get_date_format($dfid) { + $format = db_query('SELECT df.dfid, df.format, df.type, df.locked FROM {date_formats} df WHERE df.dfid = :dfid', array(':dfid' => $dfid))->fetch(); + return $format; +} + +/** + * Resets the database cache of date formats, and saves all new date formats to + * the database. + */ +function system_date_formats_rebuild() { + include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'system') . '/' . 'system.date.inc'; + $date_formats = system_get_date_formats(NULL, TRUE); + + foreach ($date_formats as $format_type => $formats) { + foreach ($formats as $format => $info) { + system_date_format_save($info); + } + } + + // Rebuild configured date formats locale list. + system_date_format_locale(NULL, NULL, TRUE); + + _system_date_formats_build(); +} + +/** + * Get the appropriate date format for a type and locale. + * + * @param $langcode + * Language code for the current locale. This can be a 2 character language + * code like 'en', 'fr', or a longer 5 character code like 'en-gb'. + * @param $type + * Date type: short, medium, long, custom. + * @param $reset + * Whether or not to reset this function's internal cache (defaults to FALSE). + * @return + * The format string, or NULL if no matching format found. + */ +function system_date_format_locale($langcode = NULL, $type = NULL, $reset = FALSE) { + static $formats; + + if ($reset || empty($formats)) { + $formats = array(); + $result = db_query("SELECT format, type, language FROM {date_format_locale}"); + foreach ($result as $record) { + if (!isset($formats[$record->language])) { + $formats[$record->language] = array(); + } + $formats[$record->language][$record->type] = $record->format; + } + } + + if ($type && $langcode && !empty($formats[$langcode][$type])) { + return $formats[$langcode][$type]; + } + elseif ($langcode && !empty($formats[$langcode])) { + return $formats[$langcode]; + } + + return FALSE; +} Index: modules/system/system.test =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.test,v retrieving revision 1.75 diff -u -p -r1.75 system.test --- modules/system/system.test 1 Sep 2009 16:50:12 -0000 1.75 +++ modules/system/system.test 15 Sep 2009 08:10:33 -0000 @@ -727,6 +727,15 @@ class DateTimeFunctionalTest extends Dru ); } + function setUp() { + parent::setUp(); + + // Create admin user and log in admin user. + $this->admin_user = $this->drupalCreateUser(array('administer site configuration')); + $this->drupalLogin($this->admin_user); + } + + /** * Test time zones and DST handling. */ @@ -757,6 +766,77 @@ class DateTimeFunctionalTest extends Dru $this->drupalGet("node/$node2->nid"); $this->assertText('2007-08-01 00:00:00 -0700', t('Date should be three hours ahead, with GMT offset of -7 hours.')); } + + /** + * Test date type configuration. + */ + function testDateTypeConfiguration() { + // Confirm system date types appear. + $this->drupalGet('admin/config/regional/date-time'); + $this->assertText(t('Medium'), 'System date types appear in date type list.'); + $this->assertNoRaw('href="/admin/config/regional/date-time/types/delete/medium"', 'No delete link appear for system date types.'); + + // Add custom date type. + $this->clickLink(t('Add date type')); + $date_type = $this->randomName(8); + $machine_name = 'machine_' . $date_type; + $date_format = 'd.m.Y - H:i'; + $edit = array( + 'date_type' => $date_type, + 'machine_name' => $machine_name, + 'date_format' => $date_format, + ); + $this->drupalPost('admin/config/regional/date-time/types/add', $edit, t('Add date type')); + $this->assertEqual($this->getUrl(), url('admin/config/regional/date-time', array('absolute' => TRUE)), t('Correct page redirection.')); + $this->assertText(t('New date type added successfully.'), 'Date type added confirmation message appears.'); + $this->assertText($date_type, 'Custom date type appears in the date type list.'); + $this->assertText(t('delete'), 'Delete link for custom date type appears.'); + + // Delete custom date type. + $this->clickLink(t('delete')); + $this->drupalPost('admin/config/regional/date-time/types/delete/' . $machine_name, array(), t('Remove')); + $this->assertEqual($this->getUrl(), url('admin/config/regional/date-time', array('absolute' => TRUE)), t('Correct page redirection.')); + $this->assertText(t('Removed date type ' . $date_type), 'Custom date type removed.'); + } + + /** + * Test date format configuration. + */ + function testDateFormatConfiguration() { + // Confirm 'no custom date formats available' message appears. + $this->drupalGet('admin/config/regional/date-time/formats'); + $this->assertText(t('No custom date formats available.'), 'No custom date formats message appears.'); + + // Add custom date format. + $this->clickLink(t('Add format')); + $edit = array( + 'date_format' => 'Y', + ); + $this->drupalPost('admin/config/regional/date-time/formats/add', $edit, t('Add format')); + $this->assertEqual($this->getUrl(), url('admin/config/regional/date-time/formats', array('absolute' => TRUE)), t('Correct page redirection.')); + $this->assertNoText(t('No custom date formats available.'), 'No custom date formats message does not appear.'); + $this->assertText(t('Custom date format added.'), 'Custom date format added.'); + + // Ensure custom date format appears in date type configuration options. + $this->drupalGet('admin/config/regional/date-time'); + $this->assertRaw('