Index: teaserbytype.module =================================================================== --- teaserbytype.module (revision 22) +++ teaserbytype.module (working copy) @@ -27,6 +27,193 @@ /** + * Implementation of hook_menu() + */ +function teaserbytype_menu() { + + $items = array(); + + $items['admin/settings/teaserbytype'] = array( + 'title' => 'Teaser by type settings', + 'description' => 'Control the teaser by type options', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('teaserbytype_admin'), + 'access arguments' => array('administer teaser by type options'), + 'type' => MENU_NORMAL_ITEM, + ); + + return $items; +} + +/** + * Implementation of hook_admin() + */ +function teaserbytype_admin() { + $form = array(); + + $form['teaserbytype_options'] = array( + '#type' => 'textarea', + '#title' => t('Options'), + '#default_value' => variable_get('teaserbytype_options', "default|Default\n0|Unlimited\n200|200 characters\n300|300 characters\n400|400 characters\n600|600 characters\n800|800 characters\n1000|1000 characters\n1200|1200 characters\n1400|1400 characters\n1600|11600 characters\n1800|1800 characters\n2000|2000 characters"), + '#description' => t('A list of selectable options. One option per line. Key-value pairs may be entered seperated by pipes, such as "safe_key|Some readable option". Option groups for lists and menus may be specified with . <> can be used to insert items at the root of the menu after specifying a group.'), + '#required' => TRUE, + ); + + return system_settings_form($form); +} + + + +/** + * Utility function to split user-entered values from new-line seperated + * text into an array of options. + * + * @param $text + * Text to be converted into a select option array. + * @param $flat + * Optional. If specified, return the option array and exclude any optgroups. + */ +function _teaserbytype_select_options($text, $flat = FALSE) { + $options = array(); + $rows = array_filter(explode("\n", trim($text))); + $group = NULL; + foreach ($rows as $option) { + $option = trim($option); + /** + * If the Key of the option is within < >, treat as an optgroup + * + * + * creates an optgroup with the label "Group 1" + * + * <> + * Unsets the current group, allowing items to be inserted at the root element. + */ + + if (preg_match('/^\<([^>]*)\>$/', $option, $matches)) { + if (empty($matches[1])) { + unset($group); + } + elseif (!$flat) { + $group = _teaserbytype_filter_values($matches[1], NULL, NULL, FALSE); + } + } + elseif (preg_match('/^([^|]+)\|(.*)$/', $option, $matches)) { + $key = _teaserbytype_filter_values($matches[1], NULL, NULL, FALSE); + $value = _teaserbytype_filter_values($matches[2], NULL, NULL, FALSE); + isset($group) ? $options[$group][$key] = $value : $options[$key] = $value; + } + else { + $filtered_option = _teaserbytype_filter_values($option, NULL, NULL, FALSE); + isset($group) ? $options[$group][$filtered_option] = $filtered_option : $options[$filtered_option] = $filtered_option; + } + } + return $options; +} + + +function _teaserbytype_filter_values($string, $node = NULL, $submission = NULL, $strict = TRUE, $allow_anonymous = FALSE) { + global $user; + static $replacements; + + // Setup default token replacements. + if (!isset($replacements)) { + $replacements['unsafe'] = array(); + $replacements['safe']['%site'] = variable_get('site_name', 'drupal'); + $replacements['safe']['%date'] = format_date(time(), 'large'); + } + + // Node replacements. + if (isset($node) && !array_key_exists('%title', $replacements)) { + $replacements['safe']['%title'] = $node->title; + } + + // Submission replacements. + if (isset($submission) && !array_key_exists('%email_values', $replacements)) { + foreach ($submission as $cid => $value) { + $replacements['unsafe']['%cid[' . $cid . ']'] = $value; + } + } + + // Provide a list of candidates for token replacement. + // Note these tokens are not cached as they may change frequently. + $special_tokens = array( + 'safe' => array( + '%get' => $_GET, + ), + 'unsafe' => array( + '%cookie' => $_COOKIE, + '%session' => $_SESSION, + '%post' => $_POST, + '%request' => $_REQUEST, + ), + ); + + // User replacements. + if (!array_key_exists('%username', $replacements['unsafe'])) { + $replacements['unsafe']['%username'] = isset($user->name) ? $user->name : ''; + $replacements['unsafe']['%useremail'] = isset($user->mail) ? $user->mail : ''; + $replacements['unsafe']['%ip_address'] = ip_address(); + + // Doesn't really belong here with user things, but works. + $special_tokens['unsafe']['%server'] = $_SERVER; + } + + // User profile replacements. + if (!isset($replacements['unsafe']['%profile[uid]'])) { + if ($user->uid && module_exists('profile')) { + profile_load_profile($user); + } + $special_tokens['unsafe']['%profile'] = $user; + } + + foreach ($special_tokens as $safe_state => $tokens) { + foreach ($tokens as $token => $variable) { + if (strpos($string, $token) !== FALSE) { + foreach ($variable as $key => $value) { + // This special case for profile module dates. + if ($token == '%profile' && is_array($value) && isset($value['year'])) { + $replacement = format_date(strtotime($value['month'] .'/'. $value['day'] .'/'. $value['year']), 'custom', 'F j, Y', '0'); + } + else { + $replacement = (string) $value; + } + $replacements[$safe_state][$token .'['. $key .']'] = $replacement; + } + } + } + } + + // Make a copy of the replacements so we don't affect the static version. + $safe_replacements = $replacements['safe']; + + // Restrict replacements for anonymous users. Not all tokens can be used + // because they may expose session or other private data to other users when + // anonymous page caching is enabled. + if ($user->uid || $allow_anonymous) { + $safe_replacements += $replacements['unsafe']; + } + else { + foreach ($replacements['unsafe'] as $key => $value) { + $safe_replacements[$key] = ''; + } + } + + $find = array_keys($safe_replacements); + $replace = array_values($safe_replacements); + $string = str_replace($find, $replace, $string); + + // Clean up any unused tokens. + foreach ($special_tokens as $safe_state => $tokens) { + foreach (array_keys($tokens) as $token) { + $string = preg_replace('/\\'. $token .'\[\w+\]/', '', $string); + } + } + + return $strict ? filter_xss($string) : $string; +} + + +/** * Implementation of hook_help(). */ function teaserbytype_help($section='') { @@ -70,9 +257,8 @@ '#type' => 'select', '#title' => t('Length of trimmed posts'), '#default_value' => variable_get('teaser_length_'. $form['#node_type']->type, 'default'), - '#options' => array('default' => t('Default'), 0 => t('Unlimited'), 200 => t('200 characters'), 400 => t('400 characters'), 600 => t('600 characters'), - 800 => t('800 characters'), 1000 => t('1000 characters'), 1200 => t('1200 characters'), 1400 => t('1400 characters'), - 1600 => t('1600 characters'), 1800 => t('1800 characters'), 2000 => t('2000 characters')), + //'#options' => array('default' => t('Default'), 0 => t('Unlimited'), 200 => t('200 characters'), 400 => t('400 characters'), 600 => t('600 characters'), 800 => t('800 characters'), 1000 => t('1000 characters'), 1200 => t('1200 characters'), 1400 => t('1400 characters'), 1600 => t('1600 characters'), 1800 => t('1800 characters'), 2000 => t('2000 characters')), + '#options' => _teaserbytype_select_options(variable_get('teaserbytype_options', "default|Default\n0|Unlimited\n200|200 characters\n300|300 characters\n400|400 characters\n600|600 characters\n800|800 characters\n1000|1000 characters\n1200|1200 characters\n1400|1400 characters\n1600|11600 characters\n1800|1800 characters\n2000|2000 characters")), '#description' => t("The maximum number of characters used in the trimmed version of a post. Drupal will use this setting to determine at which offset long posts should be trimmed. The trimmed version of a post is typically used as a teaser when displaying the post on the main page, in XML feeds, etc. To disable teasers, set to 'Unlimited'. Note that this setting will only affect new or updated content and will not affect existing teasers.") .'
'. t("Set to 'Default' to use the value from the post settings page.", array('@post-settings-page' => url('admin/content/node-settings'))), ); // add a submit handler