5 * "Full quote buster" feature extension for Quote plus module.
7 * Checks if given quotes need to be considered full quotes
8 * depending on defined criteria and, if necessary, shortens them
9 * and adds a link to retrieve the entire quote length.
13 * Default configuration values.
15 define('QUOTE_PLUS_FQB_THRESHOLD', 750);
16 define('QUOTE_PLUS_FQB_MAXLENGTH', 500);
18 define('QUOTE_PLUS_FQB_TOGGLE_LINK_NONE', 0);
19 define('QUOTE_PLUS_FQB_TOGGLE_LINK_ADD', 1);
20 define('QUOTE_PLUS_FQB_TOGGLE_LINK_PLACEHOLDER', 2);
23 * Implements hook_comment().
25 function quote_plus_fqb_comment(&$a1, $op) {
26 // Initialize js flags.
27 static
$js_added = FALSE
;
29 // Prepare static request path and query details cache.
30 static
$is_node_view, $is_preview, $fq;
32 $fq = is_array($_GET['fq']) ?
array_flip($_GET['fq']) : array();
35 // We will only work on node views.
36 if (!isset($is_node_view)) {
37 $is_node_view = preg_match('#^node/[0-9]+(/view)?$#', $_GET['q']) != 0;
38 $is_preview = !$is_node_view && preg_match('#^comment/(quote|reply)/[0-9]+(/[0-9]+)?$#', $_GET['q']) != 0;
40 if ($is_node_view || $is_preview) {
41 while (preg_match('/<!--fqb-start=([0-9]+)\.([0-9]+)-->(.*?)<!--fqb-end-->/s', $a1->comment
, $found)) {
42 if (isset($fq[$found[1]])) {
43 // Replace short quotes, if requested.
44 // 1. Retrieve pre-filtered long quote.
46 $longquote = _quote_plus_fqb_load($found[1], $found[2], $a1->cid
, TRUE
, TRUE
, FALSE
, QUOTE_PLUS_FQB_TOGGLE_LINK_ADD
);
47 // 2. Add toggle link.
48 $longquote .
= ' ' .
_quote_plus_fqb_toggle_link($found[1], $a1->cid
, FALSE
);
50 $a1->comment
= str_replace($found[0], $longquote, $a1->comment
);
54 // Otherwise, remove comment tags.
55 $a1->comment
= str_replace($found[0], $found[3], $a1->comment
);
59 // Replace any <!--fql-toggle--> link placeholder with a toggle link.
60 while (preg_match('/<!--fql-toggle=([0-9]+)-->/', $a1->comment
, $found)) {
61 $a1->comment
= str_replace(
63 ' ' .
_quote_plus_fqb_toggle_link($found[1], $a1->cid
, TRUE
),
71 if ($add_js && !$js_added) {
72 drupal_add_js(drupal_get_path('module', 'quote_plus_fqb') .
'/quote_plus_fqb.js');
73 drupal_add_js(array('quotePlusFqb' => array('callbackPath' => 'js/quote_plus_fqb/ajax')), 'setting');
79 * Implements hook_cron().
81 function quote_plus_fqb_cron() {
82 // Remove shortened quotes after one week.
83 // That should be long enough to cover looooong browser sessions.
84 // (Otherwise the Ajax retrieval of a quote could fail just in case
85 // a user falls asleep while reading a comment page...
87 db_query("DELETE FROM {quote_plus_fqb} where created < %d", time() - 7 * 24 * 60 * 60);
91 * Implements hook_disable().
93 function quote_plus_fqb_disable() {
94 // If fqb submodule is being disabled, there may still be
95 // shortened quotes in the filter format cache. They would
96 // still be displayed without a chance to switch to full quotes.
97 cache_clear_all('*', 'cache_filter', TRUE
);
101 * Implements hook_enable().
103 function quote_plus_fqb_enable() {
104 // If fqb submodule was not disabled, there may still be
105 // unshortened quotes in the filter format cache.
106 // fqb module would be useless for up to one day.
107 cache_clear_all('*', 'cache_filter', TRUE
);
111 * Implements hook_js().
113 function quote_plus_fqb_js() {
116 'callback' => '_quote_plus_fqb_ajax',
117 'includes' => array('unicode'),
123 * Implements hook_menu().
125 function quote_plus_fqb_menu() {
127 'js/quote_plus_fqb/ajax' => array(
128 'title' => 'JS fullquote toggle',
129 'page callback' => '_quote_plus_fqb_ajax',
130 'type' => MENU_CALLBACK
,
136 * Retrieve quotes from the DB and serve them via JSON output.
138 * Checks user access, evaluates POST data and conditionally returns
139 * a shortened or a full length quote.
141 function _quote_plus_fqb_ajax() {
142 if (user_access('access comments')) {
143 // Evaluate POST params.
144 if (is_numeric($qid = $_POST['qid']) && is_numeric($full = $_POST['full']) && is_numeric($cid = $_POST['cid']) && is_numeric($sid = $_POST['sid'])) {
145 // Retrieve the short or full quote.
146 $quote = _quote_plus_fqb_load($qid, $sid, $cid, $full == 1, TRUE
, FALSE
, QUOTE_PLUS_FQB_TOGGLE_LINK_ADD
);
147 drupal_json(array('content' => $quote));
153 * Process one text block out of one quote level.
156 * Text block without sub quotes. Not yet filtered,
157 * may contain HTML etc.
159 * Current filter format (settings related).
162 * The processed block content.
164 function _quote_plus_fqb_process($block, $format) {
165 // Remove the filter indicator we have set to force refiltering,
166 // in case e.g. the HTML filter did not do that yet.
167 // @see quote_plus_fqb_comment()
168 $block = preg_replace('/<!--fqb-->$/', '', $block);
170 // Configuration data.
171 static
$threshold = array();
172 static
$maxlength = array();
173 if (!isset($threshold[$format])) {
174 $threshold[$format] = variable_get(
175 "quote_plus_fqb_threshold_$format",
176 QUOTE_PLUS_FQB_THRESHOLD
178 $maxlength[$format] = variable_get(
179 "quote_plus_fqb_maxlength_$format",
180 QUOTE_PLUS_FQB_MAXLENGTH
184 // Shortened quotes and length determination will base on plain text.
185 $plain_text = decode_entities(strip_tags($block));
186 $total_length = drupal_strlen($plain_text);
188 // Find out if the block is a candidate and, if so, process it.
189 if ($total_length > $threshold[$format]) {
192 'sid' => rand(100000, 999999),
193 'full' => check_markup($block, $format),
194 'short' => truncate_utf8($plain_text, $maxlength[$format], TRUE
, TRUE
),
195 'length' => $total_length,
198 drupal_write_record('quote_plus_fqb', $data);
200 // Only shorten if DB operation was successful.
202 $block = _quote_plus_fqb_load($data['qid'], $data['sid'], NULL
, FALSE
, TRUE
, TRUE
, QUOTE_PLUS_FQB_TOGGLE_LINK_PLACEHOLDER
);
209 * Load a formatted quote from the database.
214 * The additional "secure" ID.
216 * ID of the comment the quote belongs to.
218 * If TRUE, load the full quote. Otherwise the shortened version.
220 * If TRUE, return the quote embedded in a shortquote/fullquote container
221 * consisting of a div with properly named classes and ID and comment
222 * tags indicating a quote sections start, end and a placeholder for a
225 * Whether to enclose the container in <!--fqb-start--><!--fqb-end-->
226 * delimiters (needed for conditional replacement of the pre-filtered
227 * cache data) in hook_comment.
228 * @param $toggle_link
229 * Whether to add a toggle link or a placeholder or none of these.
230 * See QUOTE_PLUS_FQB_TOGGLE_LINK_ constant definitions.
232 * @return string|bool
233 * The quote related to $qid or FALSE, if loading failed.
235 function _quote_plus_fqb_load($qid, $sid, $cid, $full = TRUE
, $container = FALSE
, $delimiters = FALSE
, $toggle_link = QUOTE_PLUS_FQB_TOGGLE_LINK_NONE
) {
236 if (is_numeric($qid) && is_numeric($sid)) {
237 if ($res = db_query('SELECT * FROM {quote_plus_fqb} WHERE qid = %d AND sid = %d', $qid, $sid)) {
238 $data = db_fetch_array($res);
240 $ret = '<div class="quote-plus-fqb quote-plus-fqb-' .
($full ?
'full' : 'short')
241 .
'quote" id="quote-plus-fqb-' .
$data['qid'].
'"'
244 $ret .
= ' title="' .
t(
245 'This quote block has been shortened from a total length of appr. !length characters for better readability.',
246 array('!length' => $data['length'])
249 $ret .
= '><span class="quote-plus-fqb-sid" id="quote-plus-fqb-sid-' .
$data['sid'] .
'"></span>' .
$data[$full ?
'full' : 'short'];
250 if ($toggle_link == QUOTE_PLUS_FQB_TOGGLE_LINK_ADD
) {
251 $ret .
= ' ' .
_quote_plus_fqb_toggle_link($data['qid'], $cid, !$full);
253 elseif ($toggle_link == QUOTE_PLUS_FQB_TOGGLE_LINK_PLACEHOLDER
) {
254 $ret .
= '<!--fql-toggle=' .
$data['qid'] .
'--></div>';
257 $ret = '<!--fqb-start=' .
$data['qid'] .
'.' .
$data['sid'] .
'-->' .
$ret .
'<!--fqb-end-->';
261 $ret = $data[$full ?
'full' : 'short'];
270 * Generate a toggle link.
273 * Internal quote ID (for link CSS id).
275 * Comment ID (for direct redirection in non-JS calls).
277 * If true, link to a full quote. Otherwise to a short quote.
280 * An HTML link snippet.
283 * Add theming support?
285 function _quote_plus_fqb_toggle_link($qid, $cid, $full) {
287 if (!isset($query)) {
288 $query = is_array($_GET) ?
$_GET : array();
291 $fq = is_array($_GET['fq']) ?
$_GET['fq'] : array();
295 '(' .
t('Show full quote') .
')',
298 'attributes' => array(
299 'class' => 'quote-plus-fqb-toggle',
300 'id' => 'quote-plus-fqb-toggle-' .
$qid,
302 'query' => array_merge($query, array('fq' => array_merge((array)$_GET['fq'], array($qid)))),
308 '(' .
t('Hide full quote') .
')',
311 'attributes' => array(
312 'class' => 'quote-plus-fqb-toggle',
313 'id' => 'quote-plus-fqb-toggle-' .
$qid,
315 'query' => array_merge($query, array('fq' => array_diff((array)$_GET['fq'], array($qid))))
322 * Validation callback for fqb specific filter settings.
324 * @see quote_plus.module
325 * @see quote_plus_form_alter()
327 function _quote_plus_fqb_settings_validate($form, &$form_state) {
328 if (!is_numeric($form_state['values']['quote_plus_fqb_threshold'])) {
329 form_set_error('quote_plus_fqb_threshold', t('The long quote threshold must be a positive integer.'));
332 !is_numeric($form_state['values']['quote_plus_fqb_maxlength'])
334 $form_state['values']['quote_plus_fqb_maxlength'] >= $form_state['values']['quote_plus_fqb_threshold']
336 form_set_error('quote_plus_fqb_maxlength', t('The maximum short quote length must be a positive integer and smaller than the threshold.'));