theme('item_list', $sitemaps)));
break;
case 'admin/settings/xmlsitemap':
break;
case 'admin/settings/xmlsitemap/rebuild':
$output .= '
' . t("This action rebuilds your site's XML sitemap and regenerates the cached files, and may be a lengthy process. If you just installed XML sitemap, this can be helpful to import all your site's content into the sitemap. Otherwise, this should only be used in emergencies.") . '
';
}
// Use a static variable because this code may be called more than once.
static $checked = FALSE;
if (!$checked && arg(0) == 'admin' && strpos($path, 'xmlsitemap') !== FALSE) {
$checked = TRUE;
module_load_include('inc', 'xmlsitemap');
xmlsitemap_check_status();
if ($blurb = _xmlsitemap_get_blurb()) {
$output .= '';
}
}
return $output;
}
/**
* Implementation of hook_perm().
*/
function xmlsitemap_perm() {
return array('administer xmlsitemap');
}
/**
* Implementation of hook_menu().
*/
function xmlsitemap_menu() {
$items['admin/settings/xmlsitemap'] = array(
'title' => 'XML sitemap',
'description' => 'Configure the XML sitemap.',
'page callback' => 'drupal_get_form',
'page arguments' => array('xmlsitemap_settings_form'),
'access arguments' => array('administer xmlsitemap'),
'file' => 'xmlsitemap.admin.inc',
);
$items['admin/settings/xmlsitemap/settings'] = array(
'title' => 'Settings',
'access arguments' => array('administer xmlsitemap'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'file' => 'xmlsitemap.admin.inc',
'weight' => -10,
);
$items['admin/settings/xmlsitemap/rebuild'] = array(
'title' => 'Rebuild',
'description' => 'Rebuild the site map.',
'page callback' => 'drupal_get_form',
'page arguments' => array('xmlsitemap_rebuild_confirm'),
'access arguments' => array('administer xmlsitemap'),
'type' => MENU_LOCAL_TASK,
'file' => 'xmlsitemap.admin.inc',
'weight' => 10,
);
$items['sitemap.xml'] = array(
'page callback' => 'xmlsitemap_output_chunk',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
'file' => 'xmlsitemap.pages.inc',
);
$chunks = xmlsitemap_get_chunk_count();
if ($chunks > 1) {
for ($i = 1; $i <= $chunks; $i++) {
$items['sitemap-' . $i . '.xml'] = array(
'page callback' => 'xmlsitemap_output_chunk',
'page arguments' => array((string) $i),
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
'file' => 'xmlsitemap.pages.inc',
);
}
}
$items['sitemap.xsl'] = array(
'page callback' => 'xmlsitemap_output_xsl',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
'file' => 'xmlsitemap.pages.inc',
);
return $items;
}
/**
* Implementation of hook_cron().
*/
function xmlsitemap_cron() {
if (!xmlsitemap_var('regenerate_needed')
|| (REQUEST_TIME - xmlsitemap_var('generated_last')) < xmlsitemap_var('minimum_lifetime')) {
return;
}
module_load_include('inc', 'xmlsitemap');
xmlsitemap_regenerate();
}
/**
* Implementation of hook_xmlsitemap_links().
*/
function xmlsitemap_xmlsitemap_links() {
$links = array();
// Frontpage link.
$links[] = array(
'type' => 'frontpage',
'id' => 0,
'loc' => '',
);
return $links;
}
/**
* Implementation of hook_xmlsitemap_link_alter().
*/
function xmlsitemap_xmlsitemap_link_alter(&$link) {
// Alter the frontpage priority.
if ($link['type'] == 'frontpage' || $link['loc'] == '' || $link['loc'] == drupal_get_normal_path(variable_get('site_frontpage', 'node'))) {
$link['priority'] = xmlsitemap_var('frontpage_priority');
$link['changefreq'] = xmlsitemap_var('frontpage_changefreq');
}
}
/**
* Implementation of hook_xmlsitemap_links_clear().
*/
function xmlsitemap_xmlsitemap_links_clear() {
db_query("DELETE FROM {xmlsitemap} WHERE type = 'frontpage'");
}
/**
* Implementation of hook_robotstxt().
*/
function xmlsitemap_robotstxt() {
module_load_include('inc', 'xmlsitemap');
$sitemaps = xmlsitemap_get_sitemaps();
foreach ($sitemaps as $index => $sitemap) {
$sitemaps[$index] = 'Sitemap: ' . $sitemap;
}
return $sitemaps;
}
/**
* Get an array of the current site's sitemaps.
*
* @param $links
* A boolean if TRUE, the array elements will be HTML links.
* @return
* An array of sitemaps.
*/
function xmlsitemap_get_sitemaps($links = FALSE) {
static $sitemaps = array();
if (!$sitemaps) {
$url_options = xmlsitemap_get_url_options();
$languages = language_list();
$sitemap_languages = xmlsitemap_var('languages');
natsort($sitemap_languages);
foreach ($sitemap_languages as $language) {
$url_options['language'] = $languages[$language];
$sitemap = url('sitemap.xml', $url_options);
$sitemaps[$language] = $links ? l($sitemap, $sitemap) : $sitemap;
}
}
return $sitemaps;
}
/**
* Return a list of commonly used parameters for url() used by XML sitemap.
*
* @see url()
*/
function xmlsitemap_get_url_options($options = array()) {
return $options + array(
'absolute' => TRUE,
'base_url' => xmlsitemap_var('base_url'),
);
}
/**
* Determine the frequency of updates to a link.
*
* @param $interval
* An interval value in seconds.
* @return
* A string representing the update frequency according to the sitemaps.org
* protocol.
*/
function xmlsitemap_get_changefreq($interval) {
if ($interval <= 0 || !is_numeric($interval)) {
return FALSE;
}
foreach (xmlsitemap_get_changefreq_options() as $value => $frequency) {
if ($interval <= $value) {
return $frequency;
}
}
return 'never';
}
/**
* Get the current number of sitemap chunks.
*/
function xmlsitemap_get_chunk_count($reset = FALSE) {
static $chunks;
if (!isset($chunks) || $reset) {
$count = max(xmlsitemap_get_link_count($reset), 1);
$chunks = ceil($count / xmlsitemap_get_chunk_size($reset));
}
return $chunks;
}
/**
* Get the current number of sitemap links.
*/
function xmlsitemap_get_link_count($reset = FALSE) {
static $count;
if (!isset($count) || $reset) {
$count = db_result(db_query("SELECT COUNT(id) FROM {xmlsitemap} WHERE access = 1 AND status = 1"));
}
return $count;
}
/**
* Get the sitemap chunk size.
*
* This function is useful with the chunk size is set to automatic as it will
* calculate the appropriate value. Use this function instead of @code
* xmlsitemap_var('chunk_size') @endcode when the actual value is needed.
*
* @param $reset
* A boolean to reset the saved, static result. Defaults to FALSE.
* @return
* An integer with the number of links in each sitemap page.
*/
function xmlsitemap_get_chunk_size($reset = FALSE) {
static $size;
if (!isset($size) || $reset) {
$size = xmlsitemap_var('chunk_size');
if ($size === 'auto') {
$count = max(xmlsitemap_get_link_count($reset), 1); // Prevent divide by zero.
$size = min(ceil($count / 10000) * 5000, XMLSITEMAP_MAX_SITEMAP_LINKS);
}
}
return $size;
}
/**
* Recalculate the changefreq of a sitemap link.
*
* @param $link
* A sitemap link array.
*/
function xmlsitemap_recalculate_changefreq(&$link) {
$link['changefreq'] = round((($link['changefreq'] * $link['changecount']) + (REQUEST_TIME - $link['lastmod'])) / ($link['changecount'] + 1));
$link['changecount']++;
$link['lastmod'] = REQUEST_TIME;
}
/**
* Calculates the average interval between UNIX timestamps.
*
* @param $timestamps
* An array of UNIX timestamp integers.
* @return
* An integer of the average interval.
*/
function xmlsitemap_calculate_changefreq($timestamps) {
sort($timestamps);
$count = count($timestamps) - 1;
$diff = 0;
for ($i = 0; $i < $count; $i++) {
$diff += $timestamps[$i + 1] - $timestamps[$i];
}
return $count > 0 ? round($diff / $count) : 0;
}
/**
* Given an {xmlsitemap} field, return the correct %-placeholder.
*
* @param $field
* The {xmlsitemap} schema field name.
* @return
* The placeholder string to embed in a query for that type.
*
* @see db_type_placeholder()
*/
function _xmlsitemap_get_placeholder($field) {
static $schema;
if (!isset($schema)) {
$schema = drupal_get_schema('xmlsitemap');
}
return db_type_placeholder($schema['fields'][$field]['type']);
}
/**
* Check if there is a visible sitemap link given a certain set of conditions.
*
* @param $conditions
* An array of values to match keyed by field.
* @param $flag
* An optional boolean that if TRUE, will set the regenerate needed flag if
* there is a match. Defaults to FALSE.
* @return
* TRUE if there is a visible link, or FALSE otherwise.
*/
function _xmlsitemap_check_changed_links(array $conditions = array(), array $updates = array(), $flag = FALSE) {
// If we are changing status or access, check for negative current values.
$conditions['status'] = (!empty($updates['status']) && empty($condition['status'])) ? 0 : 1;
$conditions['access'] = (!empty($updates['access']) && empty($condition['access'])) ? 0 : 1;
$args = _xmlsitemap_build_conditions($conditions);
$sql = "SELECT 1 FROM {xmlsitemap} WHERE ". implode(' AND ', $conditions);
$changed = db_result(db_query_range($sql, $args, 0, 1));
if ($changed && $flag) {
variable_set('xmlsitemap_regenerate_needed', TRUE);
}
return $changed;
}
/**
* Check if there is sitemap link is changed from the existing data.
*
* @param $link
* An array of the sitemap link.
* @param $original_link
* An optional array of the existing data. This should only contain the
* fields necessary for comparison. If not provided the existing data will be
* loaded from the database.
* @param $flag
* An optional boolean that if TRUE, will set the regenerate needed flag if
* there is a match. Defaults to FALSE.
* @return
* TRUE if the link is changed, or FALSE otherwise.
*/
function _xmlsitemap_check_changed_link(array $link, $original_link = NULL, $flag = FALSE) {
$changed = FALSE;
if ($original_link === NULL) {
// Load only the fields necessary for data to be changed in the sitemap.
$original_link = db_fetch_array(db_query_range("SELECT loc, access, status, lastmod, priority, changefreq, changecount, language FROM {xmlsitemap} WHERE type = '%s' AND id = %d", $link['type'], $link['id'], 0, 1));
}
if (!$original_link) {
if ($link['access'] && $link['status']) {
// Adding a new visible link.
$changed = TRUE;
}
}
else {
if (!($original_link['access'] && $original_link['status']) && $link['access'] && $link['status']) {
// Changing a non-visible link to a visible link.
$changed = TRUE;
}
elseif ($original_link['access'] && $original_link['status'] && array_diff_assoc($original_link, $link)) {
// Changing a visible link
$changed = TRUE;
}
}
if ($changed && $flag) {
variable_set('xmlsitemap_regenerate_needed', TRUE);
}
return $changed;
}
/**
* Condition builder for queries.
*
* @todo (Drupal7) Replace with DBTNG
*/
function _xmlsitemap_build_conditions(array &$conditions = array(), array &$args = array(), $operator = '=', $null_operator = 'IS') {
foreach ($conditions as $field => $value) {
if (is_int($field)) {
continue;
}
elseif ($value === NULL) {
$conditions[$field] = "$field $null_operator NULL";
}
else {
$placeholder = _xmlsitemap_get_placeholder($field);
$conditions[$field] = "$field $operator $placeholder";
$args[] = $value;
}
}
return $args;
}
/**
* Load a specific sitemap link.
*
* @param $conditions
* An array of values to match keyed by field.
* @return
* An array representing the first sitemap link matching the conditions found.
*/
function xmlsitemap_load_link(array $conditions) {
$args = _xmlsitemap_build_conditions($conditions);
$link = db_fetch_array(db_query_range("SELECT * FROM {xmlsitemap} WHERE ". implode(' AND ', $conditions), $args, 0, 1));
// Allow other modules to respond after loading the link.
//module_invoke_all('xmlsitemap_load_link', $link, $conditions, $args);
return $link;
}
/**
* Saves or updates a sitemap link.
*
* @param $link
* An array with a sitemap link.
*/
function xmlsitemap_save_link(array $link) {
$link += array(
'access' => 1,
'status' => 1,
'status_override' => 0,
'lastmod' => 0,
'priority' => 0.5,
'priority_override' => 0,
'changefreq' => 0,
'changecount' => 0,
'language' => '',
);
// Allow other modules to alter the link before saving.
drupal_alter('xmlsitemap_link', $link);
// Temporary validation checks.
// @todo Remove in final?
if ($link['priority'] < 0 || $link['priority'] > 1) {
trigger_error(t('Invalid sitemap link priority %priority.
@link', array('%priority' => $link['priority'], '@link' => var_export($link, TRUE))), E_USER_ERROR);
}
if ($link['changecount'] < 0) {
trigger_error(t('Negative changecount value. Please report this to @516928.
@link', array('@516928' => 'http://drupal.org/node/516928', '@link' => var_export($link, TRUE))), E_USER_ERROR);
$link['changecount'] = 0;
}
$existing = db_fetch_array(db_query_range("SELECT loc, access, status, lastmod, priority, changefreq, changecount, language FROM {xmlsitemap} WHERE type = '%s' AND id = %d", $link['type'], $link['id'], 0, 1));
// Check if this is a changed link and set the regenerate flag if necessary.
if (!variable_get('xmlsitemap_regenerate_needed', FALSE)) {
_xmlsitemap_check_changed_link($link, $existing, TRUE);
}
if ($existing) {
xmlsitemap_write_record('xmlsitemap', $link, array('type', 'id'));
}
else {
xmlsitemap_write_record('xmlsitemap', $link);
}
// Allow other modules to respond after saving the link.
//module_invoke_all('xmlsitemap_save_link', $link);
return $link;
}
/**
* Perform a mass update of sitemap data.
*
* If visible links are updated, this will automatically set the regenerate
* needed flag to TRUE.
*
* @param $updates
* An array of values to update fields to, keyed by field name.
* @param $conditions
* An array of values to match keyed by field.
* @return
* The number of links that were updated.
*/
function xmlsitemap_update_links($updates = array(), $conditions = array()) {
// If we are going to modify a visible sitemap link, we will need to set
// the regenerate needed flag.
if (!variable_get('xmlsitemap_regenerate_needed', FALSE)) {
_xmlsitemap_check_changed_links($conditions, $updates, TRUE);
}
// Process updates.
$args = array();
_xmlsitemap_build_conditions($updates, $args, '=', '=');
_xmlsitemap_build_conditions($conditions, $args);
$sql = "UPDATE {xmlsitemap} SET " . implode(', ', $updates) . " WHERE " . implode(' AND ', $conditions);
db_query($sql, $args);
return db_affected_rows();
}
/**
* Delete one or more sitemap links.
*
* If a visible sitemap link was deleted, this will automatically set the
* regenerate needed flag.
*
* @param $conditions
* An array of values to match keyed by field.
* @return
* The number of links that were deleted.
*/
function xmlsitemap_delete_link(array $conditions) {
if (!variable_get('xmlsitemap_regenerate_needed', TRUE)) {
_xmlsitemap_check_changed_links($conditions, array(), TRUE);
}
$args = _xmlsitemap_build_conditions($conditions);
db_query("DELETE FROM {xmlsitemap} WHERE " . implode(' AND ', $conditions), $args);
// Allow other modules to respond after deleting the link.
//module_invoke_all('xmlsitemap_delete_link', $conditions, $args);
return db_affected_rows();
}
/**
* Delete all cached sitemap XML files.
*
* @param $rmdir
* A boolean that if TRUE the directory will be removed after emptying.
* @param $path
* An optional directory path, defaults to the xmlsiteamp cache directory.
*/
function xmlsitemap_clear_cache($rmdir = FALSE, $path = NULL) {
if (!isset($path)) {
$path = file_create_path(xmlsitemap_var('path'));
}
file_scan_directory($path, '.*', array('.', '..', 'CVS', '.svn'), 'file_delete', TRUE);
if ($rmdir) {
rmdir($path);
}
}
/**
* Get the filename of a specific sitemap page chunk.
*
* @param $chunk
* An integer representing the integer of the sitemap page chunk.
* @param $language
* A language object, defaults to the default language.
* @return
* A file path to the expected chunk file.
*
* @todo Move to xmlsitemap.inc
*/
function xmlsitemap_get_chunk_file($chunk = 0, $language, $compressed = FALSE) {
return file_create_path(xmlsitemap_var('path')) .'/xmlsitemap-' . $language->language . '-' . $chunk . ($compressed ? '.gz' : '.xml');
}
/**
* Implementation of hook_form_alter().
*
* Set the regeneration needed flag if settings are changed.
*/
function xmlsitemap_form_alter(&$form, $form_state, $form_id) {
$forms = array(
'locale_languages_overview_form', // Language settings
'xmlsitemap_settings_form', // XML sitemap settings
);
if (in_array($form_id, $forms)) {
array_unshift($form['#submit'], 'xmlsitemap_form_submit_flag_regenerate');
}
}
/**
* Submit handler; Set the regenerate needed flag if variables have changed.
*
* This function needs to be called before system_settings_form_submit() or any
* calls to variable_set().
*/
function xmlsitemap_form_submit_flag_regenerate($form, $form_state) {
foreach ($form_state['values'] as $variable => $value) {
$stored_value = variable_get($variable, 'not_a_variable');
if (is_array($value) && isset($form_state['values']['array_filter'])) {
$value = array_keys(array_filter($value));
}
if ($stored_value != 'not_a_variable' && $stored_value != $value) {
variable_set('xmlsitemap_regenerate_needed', TRUE);
return;
}
}
}
/**
* Implementation of hook_form_FORM_ID_alter().
*
* Add vertical tabs to the XML sitemap settings.
*/
function xmlsitemap_form_xmlsitemap_settings_form_alter(&$form, $form_state) {
if (module_exists('vertical_tabs') && function_exists('vertical_tabs_add_vertical_tabs')) {
vertical_tabs_add_vertical_tabs($form);
}
}
/**
* Internal default variables for xmlsitemap_var().
*/
function xmlsitemap_variables() {
return array(
'xmlsitemap_rebuild_needed' => FALSE,
'xmlsitemap_regenerate_needed' => FALSE,
'xmlsitemap_generated_last' => 0,
'xmlsitemap_minimum_lifetime' => 0,
'xmlsitemap_xsl' => TRUE,
'xmlsitemap_languages' => array(language_default('language')),
'xmlsitemap_chunk_size' => 'auto',
'xmlsitemap_batch_limit' => 100,
'xmlsitemap_path' => 'xmlsitemap',
'xmlsitemap_base_url' => $GLOBALS['base_url'],
'xmlsitemap_developer_mode' => FALSE,
'xmlsitemap_frontpage_priority' => '1.0',
'xmlsitemap_frontpage_changefreq' => XMLSITEMAP_FREQUENCY_DAILY,
// Removed variables are set to NULL so they can still be deleted.
'xmlsitemap_gz' => FALSE,
'xmlsitemap_regenerate_last' => NULL,
'xmlsitemap_custom_links' => NULL,
'xmlsitemap_priority_default' => NULL,
);
}
/**
* Internal implementation of variable_get().
*/
function xmlsitemap_var($name, $default = NULL) {
static $defaults = NULL;
if (!isset($defaults)) {
$defaults = xmlsitemap_variables();
}
$name = 'xmlsitemap_'. $name;
// @todo Remove when stable.
if (!isset($defaults[$name])) {
trigger_error(t('Default variable for %variable not found.', array('%variable' => $name)));
}
return variable_get($name, isset($default) || !isset($defaults[$name]) ? $default : $defaults[$name]);
}
/**
* Set the current user stored in $GLOBALS['user'].
*
* @todo Remove when http://drupal.org/node/287292 is fixed.
*/
function xmlsitemap_switch_user($new_user = NULL) {
global $user;
static $user_original;
if (!isset($new_user)) {
if (isset($user_original)) {
// Restore the original user.
$user = $user_original;
$user_original = NULL;
session_save_session(TRUE);
}
else {
return FALSE;
}
}
elseif (is_numeric($new_user) && $user->uid != $new_user) {
// Get the full user object.
if (!$new_user) {
$new_user = drupal_anonymous_user();
}
elseif (!$new_user = user_load(array('uid' => $new_user))) {
return FALSE;
}
// Backup the original user object.
if (!isset($user_original)) {
$user_original = $user;
session_save_session(FALSE);
}
$user = $new_user;
}
elseif (is_object($new_user) && $user->uid != $new_user->uid) {
// Backup the original user object.
if (!isset($user_original)) {
$user_original = $user;
session_save_session(FALSE);
}
$user = $new_user;
}
else {
return FALSE;
}
return $user;
}
/**
* Restore the user that was originally loaded.
*
* @return
* Current user.
*/
function xmlsitemap_restore_user() {
return xmlsitemap_switch_user();
}
/**
* Special implementation of drupal_write_record() to allow NULL values.
*
* @todo Remove when http://drupal.org/node/227677 is fixed.
*/
function xmlsitemap_write_record($table, &$object, $update = array()) {
// Standardize $update to an array.
if (is_string($update)) {
$update = array($update);
}
$schema = drupal_get_schema($table);
if (empty($schema)) {
return FALSE;
}
// Convert to an object if needed.
if (is_array($object)) {
$object = (object) $object;
$array = TRUE;
}
else {
$array = FALSE;
}
$fields = $defs = $values = $serials = $placeholders = array();
$object_fields = get_object_vars($object);
// Go through our schema, build SQL, and when inserting, fill in defaults for
// fields that are not set.
foreach ($schema['fields'] as $field => $info) {
// Special case -- skip serial types if we are updating.
if ($info['type'] == 'serial') {
if (empty($update)) {
// Track serial fields so we can helpfully populate them after the query.
$serials[] = $field;
}
continue;
}
// For inserts, populate defaults from Schema if not already provided
if (!isset($object->$field) && !count($update) && isset($info['default']) && !array_key_exists($field, $object_fields)) {
$object->$field = $info['default'];
}
// Build arrays for the fields, placeholders, and values in our query.
if (isset($object->$field) || (array_key_exists($field, $object_fields) && empty($info['not null']))) {
$fields[] = $field;
if (isset($object->$field)) {
$placeholders[] = db_type_placeholder($info['type']);
$values[] = empty($info['serialize']) ? $object->$field : serialize($object->$field);
}
else {
$placeholders[] = '%s';
$values[] = 'NULL';
}
}
}
// Build the SQL.
$query = '';
if (!count($update)) {
$query = "INSERT INTO {". $table ."} (". implode(', ', $fields) .') VALUES ('. implode(', ', $placeholders) .')';
$return = SAVED_NEW;
}
else {
$query = '';
foreach ($fields as $id => $field) {
if ($query) {
$query .= ', ';
}
$query .= $field .' = '. $placeholders[$id];
}
foreach ($update as $key) {
$conditions[] = "$key = ". db_type_placeholder($schema['fields'][$key]['type']);
$values[] = $object->$key;
}
$query = "UPDATE {". $table ."} SET $query WHERE ". implode(' AND ', $conditions);
$return = SAVED_UPDATED;
}
// Execute the SQL.
if (db_query($query, $values)) {
if ($serials) {
// Get last insert ids and fill them in.
foreach ($serials as $field) {
$object->$field = db_last_insert_id($table, $field);
}
}
}
else {
$return = FALSE;
}
// If we began with an array, convert back so we don't surprise the caller.
if ($array) {
$object = (array) $object;
}
return $return;
}
function xmlsitemap_process_form_link_options($form, &$form_state) {
$link = &$form_state['values']['xmlsitemap'];
$fields = array('status' => 1, 'priority' => 0.5);
foreach ($fields as $field => $default) {
if ($link[$field] === 'default') {
$link[$field] = isset($link[$field . '_default']) ? $link[$field . '_default'] : $default;
$link[$field . '_override'] = 0;
}
else {
$link[$field . '_override'] = 1;
}
}
}
/**
* @todo Document this function.
*/
function xmlsitemap_get_changefreq_options() {
return array(
XMLSITEMAP_FREQUENCY_ALWAYS => 'always',
XMLSITEMAP_FREQUENCY_HOURLY => 'hourly',
XMLSITEMAP_FREQUENCY_DAILY => 'daily',
XMLSITEMAP_FREQUENCY_WEEKLY => 'weekly',
XMLSITEMAP_FREQUENCY_MONTHLY => 'monthly',
XMLSITEMAP_FREQUENCY_YEARLY => 'yearly',
);
}