/[drupal]/contributions/modules/quotes/quotes.module
ViewVC logotype

Contents of /contributions/modules/quotes/quotes.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.23 - (show annotations) (download) (as text)
Sun Oct 18 15:31:46 2009 UTC (5 weeks, 5 days ago) by nancyw
Branch: MAIN
CVS Tags: HEAD
Changes since 1.22: +13 -11 lines
File MIME type: text/x-php
Coder finds
1 <?php
2 // $Id: quotes.module,v 1.22 2009/10/11 01:01:25 nancyw Exp $
3
4 /**
5 * @file
6 * The quotes module allows users to maintain a list of quotes that
7 * can be displayed in any number of administrator-defined quote
8 * blocks.
9 *
10 * @copyright Copyright (c) 2003-2007 Jim Riggs. All rights reserved.
11 * @author Jim Riggs <drupal at jim and lissa dot com>
12 */
13
14 define('QUOTES_VERSION', '$Id: quotes.module,v 1.22 2009/10/11 01:01:25 nancyw Exp $');
15 define('BIOS_PATH', 'admin/settings/quotes/bios');
16
17 //********************************************************************
18 //* Drupal Hooks
19 //********************************************************************/
20
21 /**
22 * Implements hook_perm().
23 */
24 function quotes_permission() {
25 return array(
26 'access quotes' => array(
27 'title' => t('Access Quotes'),
28 'description' => t('Allow quotes display.'),
29 ),
30 'administer quotes' => array(
31 'title' => t('Administer Quotes'),
32 'description' => t('Perform administration tasks for quotes module.'),
33 ),
34 // @TODO: Don't need this any more.
35 'create quotes' => array(
36 'title' => t('Create Quotes'),
37 'description' => t('Allow the creation of quotes.'),
38 ),
39 'import quotes' => array(
40 'title' => t('Import quotes'),
41 'description' => t('Allow quotes to be imported to the quotes database.'),
42 ),
43 // @TODO: Don't need this any more.
44 'edit own quotes' => array(
45 'title' => t('Edit own quotes'),
46 'description' => t('Allow editing of own quotes.'),
47 ),
48 'promote quotes to block' => array(
49 'title' => t('Promote quotes to block'),
50 'description' => t('Allow the promotion of quotes to a block.'),
51 ),
52 'view my quotes' => array(
53 'title' => t('View my quotes'),
54 'description' => t('Allow other to view my quotes.'),
55 ),
56 );
57 }
58
59 /**
60 * Implements hook_access().
61 */
62 function quotes_node_access($op, $node, $account) {
63 $type = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type);
64 // We only care about "quotes" nodes.
65 if ($type != 'quotes') {
66 return NODE_ACCESS_IGNORE;
67 }
68
69 switch ($op) {
70 case 'create':
71 if (user_access('create quotes', $account) || user_access('import quotes', $account) || user_access('edit own quotes', $account)) {
72 return NODE_ACCESS_ALLOW;
73 }
74 break;
75
76 case 'update':
77 case 'delete':
78 if (user_access('edit own quotes', $account) && ($account->uid == $node->uid)) {
79 return NODE_ACCESS_ALLOW;
80 }
81 if (user_access('administer quotes', $account)) {
82 return NODE_ACCESS_ALLOW;
83 }
84 break;
85
86 case 'view':
87 if (user_access('access quotes', $account)) {
88 return NODE_ACCESS_ALLOW;
89 }
90 break;
91 }
92 return NODE_ACCESS_IGNORE;
93 }
94
95 function _quotes_access_myquotes() {
96 global $user;
97 // This path is only allowed for authenticated users looking at their own posts.
98 return $user->uid && variable_get('quotes_show_myquotes', TRUE);
99 }
100
101 /**
102 * Implements hook_menu().
103 */
104 function quotes_menu() {
105 global $user;
106 $items = array();
107
108 // User account tab.
109 $items['user/%user/quotes'] = array(
110 'title' => 'My Quotes',
111 'page callback' => 'quotes_user_page',
112 'page arguments' => array(1),
113 // 'file' => 'quotes.user.inc',
114 'access callback' => '_quotes_recentquotes_access',
115 'type' => MENU_LOCAL_TASK,
116 'weight' => 2,
117 );
118
119 // Menu callbacks.
120 $items['quotes/feed'] = array(
121 'title' => 'RSS feed',
122 'access arguments' => array('access quotes'),
123 'page callback' => '_quotes_feed_last',
124 'type' => MENU_CALLBACK,
125 );
126
127 $items['quotes/autocomplete/author'] = array(
128 'title' => 'Autocomplete Author Field',
129 'access arguments' => array('access quotes'),
130 'page callback' => '_quotes_autocomplete_author',
131 'type' => MENU_CALLBACK,
132 );
133
134 $items['quotes/autocomplete/citation'] = array(
135 'title' => 'Autocomplete Citation Field',
136 'access arguments' => array('access quotes'),
137 'page callback' => '_quotes_autocomplete_citation',
138 'type' => MENU_CALLBACK,
139 );
140
141 // My quotes menu entry.
142 $items['quotes/myquotes'] = array(
143 'title' => 'My quotes',
144 'page callback' => 'quotes_myquotes',
145 'access callback' => '_quotes_myquotes_access',
146 'type' => MENU_NORMAL_ITEM,
147 );
148
149 // Primary menu entry.
150 $items['quotes'] = array(
151 'title' => 'Quotes',
152 'access arguments' => array('access quotes'),
153 'page callback' => 'quotes_page',
154 'type' => MENU_NORMAL_ITEM,
155 );
156
157 $items['quotes/%'] = array(
158 'title' => 'Quotes page',
159 'page callback' => 'quotes_page',
160 'page arguments' => array(1),
161 'access arguments' => array('access quotes'),
162 'type' => MENU_DEFAULT_LOCAL_TASK,
163 'weight' => -5,
164 ); /* */
165
166 // Add new quote.
167 $items['quotes/add'] = array(
168 'title' => 'Add a new quote',
169 'page callback' => '_quotes_add',
170 'access arguments' => array('create quotes'),
171 'type' => MENU_LOCAL_TASK,
172 );
173
174 $items['quotes/author'] = array(
175 'title' => 'By author',
176 'access arguments' => array('access quotes'),
177 'page callback' => 'quotes_page',
178 'page arguments' => array(1, 2),
179 'type' => MENU_LOCAL_TASK,
180 ); /* */
181
182 // Admin settings for the site.
183 $items['quotes/settings'] = array(
184 'title' => 'Settings',
185 'description' => 'Configure Quotes module options and blocks.',
186 'page callback' => '_quotes_settings',
187 'access arguments' => array('administer quotes'),
188 'type' => MENU_LOCAL_TASK,
189 );
190
191 // Admin menu.
192 $items['admin/settings/quotes'] = array(
193 'title' => 'Quotes',
194 'description' => 'Configure Quotes module options and blocks.',
195 'access arguments' => array('administer quotes'),
196 'page callback' => 'quotes_admin_settings_page',
197 'type' => MENU_NORMAL_ITEM,
198 // 'file' => 'quotes.admin.inc',
199 );
200
201 $items['admin/settings/quotes/general'] = array(
202 'title' => 'General',
203 'page callback' => 'drupal_get_form',
204 'page arguments' => array('quotes_admin_settings'),
205 'access arguments' => array('administer quotes'),
206 'type' => MENU_DEFAULT_LOCAL_TASK,
207 'weight' => -1,
208 // 'file' => 'quotes.admin.inc',
209 );
210
211 $items['admin/settings/quotes/blocks'] = array(
212 'title' => 'Configure blocks',
213 'page callback' => 'drupal_get_form',
214 'access arguments' => array('administer quotes'),
215 'page arguments' => array('quotes_blocks_settings'),
216 'type' => MENU_LOCAL_TASK,
217 // 'file' => 'quotes.admin.inc',
218 );
219
220 $items['admin/settings/quotes/export'] = array(
221 'title' => 'Export',
222 'page callback' => 'drupal_get_form',
223 'access arguments' => array('administer quotes'),
224 'page arguments' => array('quotes_export'),
225 'type' => MENU_LOCAL_TASK,
226 'weight' => 1,
227 // 'file' => 'quotes.admin.inc',
228 );
229
230 $items[BIOS_PATH] = array(
231 'title' => 'Bios',
232 'page callback' => 'drupal_get_form',
233 'page arguments' => array('quotes_bios'),
234 'access arguments' => array('administer quotes'),
235 'type' => MENU_LOCAL_TASK,
236 // 'file' => 'quotes.admin.inc',
237 );
238
239 $items['admin/settings/quotes/delete'] = array(
240 'title' => 'Delete quote block',
241 'page callback' => 'drupal_get_form',
242 'page arguments' => array('_quotes_block_delete'),
243 'access arguments' => array('administer quotes'),
244 'type' => MENU_CALLBACK,
245 // 'file' => 'quotes.admin.inc',
246 );
247
248 return $items;
249 }
250
251 /**
252 * Implements hook_menu_alter().
253 */
254 function quotes_menu_alter(&$callbacks) {
255 // Get what the admin wants to call it.
256 // Note: menu_get_item('quotes') returns the original, not the overridden.
257 $menu_title = db_query("SELECT link_title FROM {menu_links} WHERE router_path = :path", array(':path' => 'quotes'))->fetchField();
258 $callbacks['quotes']['title'] = $menu_title;
259 $callbacks['quotes/%user_uid_optional']['title'] =t('User ') . $menu_title;
260 $callbacks['user/%user/quotes']['title'] =t('My ') . $menu_title;
261 }
262
263 /**
264 * Implements hook_init().
265 */
266 function quotes_init() {
267 drupal_add_css(drupal_get_path('module', 'quotes') . '/quotes.css');
268 }
269
270 /**
271 * Implements hook_help().
272 */
273 function quotes_help($path, $args = NULL) {
274 switch ($path) {
275 case 'node/add#quotes':
276 return t('A quote is a famous, infamous, humorous, witty, or otherwise noteworthy quotation or fortune file entry. Quotes can be entered one at a time or mass imported in either tab-separated text or fortune file format.');
277
278 case 'node/add/quotes':
279 return t('Use the form below to enter a single quote.') . (user_access('import quotes') ? ' ' . t('Multiple quotes can also be !mass imported in either tab-separted text or fortune file format.', array('!mass imported' => l(t('mass imported'), 'node/add/quotes/import'))) : '');
280
281 case 'node/add/quotes/import':
282 $output = t('<p>Use the form below to mass import quotes in either tab-separated text or fortune file format.
283 Many quotes will be imported in this one step by creating an individual node for each imported quote.
284 Expand the %Notes section below for more information.</p>', array('%Notes' => t('Notes')));
285
286 $output .= theme('fieldset', array(
287 '#title' => t('Notes for importing'),
288 '#collapsible' => TRUE,
289 '#collapsed' => TRUE,
290 '#value' => t('<ul>
291 <li>Tab-separated quotes should appear one quote per line in the format
292 <em><span style="color: blue;">quote</span><span style="color: red;">&lt;tab&gt;</span><span style="color:
293 green;">author</span></em>. The author and citation are optional; however, the tab is still required.
294 To import quotes, authors, or citations with more than one line, escape the embedded newlines with a backslash.
295 Examples:
296
297 <pre style="font-size: .75em;">
298 <span style="color: blue;">Single-line quote.</span><em style="color: red;">&lt;tab&gt;</em><span style="color: green;">Author</span>
299 <span style="color: blue;">Quote without author.</span><em style="color: red;">&lt;tab&gt;</em>
300 <span style="color: blue;">Multi-line quote: line 1...\
301 ...line 2.</span><em style="color: red;">&lt;tab&gt;</em><span style="color: green;">Author line 1\
302 Author line 2</span>
303 <span style="color: blue;">Another quote.<em style="color: red;">&lt;tab&gt;</em><span style="color: green;">Another Author</span>
304 </pre></li>
305 <li>Fortune files do not explicitly provide an author or attribution
306 for each quote/fortune. This import will extract an author when
307 there is a line of the form <em>-- Author</em> with any amount of
308 leading whitespace. Examples:
309
310 <pre style="font-size: .75em;">
311 <span style="color: blue;">A fortune without an author.</span>
312 <em style="color: red;">%</em>
313 <span style="color: blue;">Fortune with author.</span>
314 --<span style="color: green;">Author</span>
315 <em style="color: red;">%</em>
316 <span style="color: blue;">Multi-line fortune: line 1...
317 ...line 2.</span>
318 -- <span style="color: green;">Author line 1
319 Author line 2</span>
320 </pre></li>
321 <li>Any settings used in the form below (comment, moderation, sticky,
322 input format, categories, etc.) will be applied to all imported
323 quotes.</li>
324 <li>The title entered below will be applied to all quotes. You can use
325 the variable <em>%id</em> in the title which will be replaced by
326 the newly-created quote\'s node ID.</li>
327 <li>Fortune files and tab-separated text data can contain many
328 quotes. To avoid timeout errors while importing large amounts of
329 data, consider importing in smaller chunks with no more than 1000
330 quotes at a time.</li>
331 <li>If the path module is enabled, you cannot create a path alias
332 while importing, as the import will attempt to use the same path
333 for every quote.</li>
334 </ul>
335 '))
336 );
337
338 return $output;
339
340 case 'admin/settings/quotes':
341 $text = t('This page displays the status of, and settings for, the quotes module. The permissions for this module are found <a href="!url">here</a>.', array('!url' => url('admin/user/permissions')));
342 $text .= '<p>Version: ' . QUOTES_VERSION . '</p>';
343 return $text;
344
345 case 'admin/settings/quotes/blocks':
346 return t('You can define any number of blocks that will each display a randomly-selected quote,
347 the most-recent quotes, or unpublished quotes. The quotes displayed
348 in each block can be restricted to certain node IDs, roles, users, or
349 categories. Each block has a name that is used for identification on
350 on the !block administration page and as the default title when the
351 block is displayed.',
352 array('!block administration page' => l(t('block administration page'), 'admin/build/block'))
353 );
354
355 case 'admin/settings/quotes/add':
356 return t('Use the form below to define a new quote block.');
357
358 case 'admin/help#quotes':
359 return t('The quotes module allows users to maintain a list of quotations that
360 they find notable, humorous, famous, infamous, or otherwise worthy of
361 sharing with website visitors. The quotes can be displayed in any
362 number of administrator-defined blocks. These blocks will display
363 quotes based on the restrictions of each block. Blocks can be
364 configured to restrict to certain nodes, roles, users, or categories.');
365
366 case 'user/%/quotes':
367 case 'quotes/author/%':
368 case 'quotes/author':
369 case 'quotes/%':
370 case 'quotes':
371 $links = array();
372 if (user_access('create quotes')) {
373 $links['add'] = array(
374 'title' => t('Add another'),
375 'href' => 'node/add/quotes',
376 );
377 }
378 if (user_access('administer quotes')) {
379 $links['admin'] = array(
380 'title' => t('Settings'),
381 'href' => 'admin/settings/quotes',
382 'query' => drupal_get_destination(),
383 );
384 }
385 return theme('links', $links);
386 }
387 }
388
389 /**
390 * Implements hook_node_info().
391 */
392 function quotes_node_info() {
393 return array(
394 'quotes' => array(
395 'name' => t('Quotes'),
396 'base' => 'quotes',
397 'description' => t('A quote is a famous, infamous, humorous, witty, or otherwise noteworthy quotation or fortune file entry. Users can maintain personal lists of quotations and display quotes in one or more blocks. Quotes can be entered one at a time or mass imported in either tab-separated text or fortune file format.')
398 )
399 );
400 }
401
402 /**
403 * Implements hook_load().
404 */
405 function quotes_load($node) {
406 $obj = db_fetch_object(db_query("SELECT a.aid AS quotes_aid, a.name AS quotes_author, a.bio AS quotes_bio, q.citation AS quotes_citation, q.promote AS quotes_promote FROM {quotes} q JOIN {quotes_authors} a USING (aid) WHERE q.vid = %d", $node->vid));
407 return $obj;
408 }
409
410 /**
411 * Implements hook_insert().
412 * This inserts the quote-specific information into the quotes
413 * tables and also handles the %id variable in the node title.
414 */
415 function quotes_insert($node) {
416 // Is it a single node or an import?
417 switch ($node->quotes_format) {
418 case 'text':
419 case 'fortune':
420 $quotes_found = _quotes_parse_import($node, FALSE);
421 foreach ($quotes_found as $count => $quote) {
422 $teaser = node_teaser($quote->body, isset($quote->format) ? $quote->format : NULL);
423 if ($count == 0) {
424 // We'll set the base node correctly from the first quote.
425 $first = clone($node);
426 // unset($node);
427 $node->quotes_format = $first->quotes_format = 'single';
428 $node->body = $first->body = $quote->body;
429 $node->teaser = $first->teaser = $teaser;
430 $node->quotes_author = $first->quotes_author = $quote->quotes_author;
431 $node->quotes_citation = $first->quotes_citation = $quote->quotes_citation;
432 // Write back the updated info.
433 node_save($node);
434
435 // When we end this function, the corrected information will be saved for us.
436 continue;
437 }
438 else {
439 // It's not the first one, so we will make a copy and update the information.
440 // Then we'll save the temporary node.
441 $temp = clone($first);
442 $temp->quotes_format = 'single';
443 // Make node_save create a new nid.
444 unset($temp->nid, $temp->vid, $temp->created);
445 $temp->body = $quote->body;
446 $temp->teaser = $teaser;
447 $temp->quotes_author = $quote->quotes_author;
448 $temp->quotes_citation = $quote->quotes_citation;
449
450 // Okay, let's get it saved.
451 node_save($temp);
452 }
453 } // End foreach.
454
455 // Note that we don't "break" so the first quote falls into saving the extra stuff.
456 drupal_set_message(t('!count quotes imported.', array('!count' => $count + 1)));
457
458 case 'single':
459 // Save the author, citation, and promote values into our table.
460 $aid = _quotes_handle_author($node->quotes_author);
461
462 db_query("INSERT INTO {quotes} (nid, vid, aid, citation, promote) VALUES (%d, %d, '%d', '%s', %d)", $node->nid, $node->vid, $aid, $node->quotes_citation, $node->quotes_promote);
463
464 // replace %id variable in title
465 if (strpos($node->title, '%id') !== FALSE) {
466 $node->title = str_replace('%id', $node->nid, $node->title);
467
468 db_query("UPDATE {node} SET title = '%s' WHERE vid = %d", $node->title, $node->vid);
469 db_query("UPDATE {node_revision} SET title = '%s' WHERE vid = %d", $node->title, $node->vid);
470 }
471 } // End switch.
472 }
473
474 /**
475 * Helper function to fetch or insert an author.
476 *
477 * @param $author
478 * The text of the author's name.
479 *
480 * @return
481 * The aid for the author in the quotes_authors table.
482 */
483 function _quotes_handle_author($author) {
484 $aid = db_result(db_query("SELECT qa.aid FROM {quotes_authors} qa WHERE qa.name='%s'", $author));
485 if (empty($aid)) {
486 $result = db_query("INSERT INTO {quotes_authors} (name) VALUES ('%s')", $author);
487 if ($result === FALSE) {
488 drupal_set_message(t('Quotes: insert author failed.'), 'error');
489 }
490 // $aid = db_result(db_query("SELECT aid FROM {quotes_authors} qa WHERE qa.name='%s'", $author));
491 $aid = db_last_insert_id('quotes_authors', 'aid');
492 if ($aid === FALSE) {
493 drupal_set_message(t('Quotes: get aid failed.'), 'error');
494 }
495 }
496 return $aid;
497 }
498
499 /**
500 * Implements hook_update().
501 */
502 function quotes_update($node) {
503 $aid = _quotes_handle_author($node->quotes_author);
504
505 if ($node->revision) {
506 quotes_insert($node);
507 }
508 else {
509 db_query("UPDATE {quotes} SET aid='%d', citation='%s', promote=%d WHERE nid=%d AND vid=%d", $aid, $node->quotes_citation, $node->quotes_promote, $node->nid, $node->vid);
510 }
511 }
512
513 /**
514 * Implements hook_delete().
515 */
516 function quotes_delete($node) {
517 $aid = _quotes_handle_author($node->quotes_author);
518
519 // If this is the last quote using this author, then delete the author too.
520 $aid_count = db_result(db_query("SELECT COUNT(q.nid) FROM {quotes} q WHERE q.aid='%d'", $aid));
521 if ($aid_count == 1) {
522 db_query("DELETE FROM {quotes_authors} WHERE aid = %d", $aid);
523 }
524
525 db_query("DELETE FROM {quotes} WHERE nid = %d", $node->nid);
526 }
527
528 /**
529 * Implements hook_view().
530 */
531 function quotes_view($node, $teaser = FALSE, $page = FALSE, $links = FALSE, $max_length = 0) {
532 global $user;
533
534 if ($page) {
535 // Breadcrumb navigation.
536 $breadcrumb = array();
537 $breadcrumb[] = l('Home', variable_get('site_frontpage', 'node'));
538 // Get the menu title.
539 $menu_info = menu_get_item('quotes');
540 $menu_title = $menu_info['title'];
541 $breadcrumb[] = l($menu_title, 'quotes');
542 $username = $node->uid ? $node->name : variable_get('anonymous', t('Anonymous'));
543 $breadcrumb[] = l(t("!name's !menu", array('!menu' => $menu_title, '!name' => $username)), "quotes/$node->uid");
544 drupal_set_breadcrumb($breadcrumb);
545 }
546
547 // Prepare the node content.
548 if (!isset($node->quotes_format)) {
549 $node = node_prepare($node);
550 $node->content['body'] = array(
551 '#value' => theme('quotes_quote', $node, $teaser, FALSE, $max_length),
552 '#weight' => 0
553 );
554 }
555 else {
556 $quotes = array();
557
558 foreach (_quotes_parse_import($node) as $quote) {
559 $quote->body = check_markup($quote->body, $node->format, FALSE);
560 $quotes[] = theme('quotes_quote', $quote);
561 }
562
563 $node->content['body'] = array(
564 '#value' => theme('item_list', $quotes, t('!count quotes will be imported:', array('!count' => count($quotes)))),
565 '#weight' => 0
566 );
567 }
568
569 return $node;
570 }
571
572 /**
573 * Implements hook_link().
574 */
575 function quotes_link($type, $node = NULL, $teaser = FALSE) {
576 global $user;
577 $links = array();
578
579 if (($type == 'node') && ($node->type == 'quotes') && !((arg(0) == 'quotes') && (arg(1) == $node->uid))) {
580 $name = ($node->uid ? $node->name : variable_get('anonymous', t('Anonymous')));
581
582 if (variable_get('quotes_showlink', TRUE)) {
583 if ($node->uid != $user->uid) {
584 // Get the menu title.
585 $menu_info = menu_get_item('quotes');
586 $menu_title = $menu_info['title'];
587 $links['quotes_usernames_quotes'] = array(
588 'title' => t("!name's !menu", array('!menu' => $menu_title, '!name' => $name)),
589 'href' => "quotes/$node->uid",
590 'attributes' => array('title' => t("View !name's !menu.", array('!menu' => $menu_title, '!name' => $name))),
591 );
592 }
593 }
594
595 if (variable_get('quotes_edit_link', TRUE)) {
596 if ((user_access('edit own quotes') && $node->uid == $user->uid) ||
597 user_access('administer quotes')) {
598 $links['quotes_edit_link'] = array(
599 'title' => t('Edit !menu', array('!menu' => drupal_strtolower($menu_title))),
600 'href' => 'node/' . $node->nid . '/edit',
601 'attributes' => array('title' => t('Edit this !menu', array('!menu' => drupal_strtolower($menu_title)))),
602 );
603 }
604 }
605 }
606
607 return $links;
608 }
609
610 /**
611 * Implements hook_block().
612 */
613 function quotes_block_list() {
614 // List of blocks
615 $blocks = array();
616 $result = db_query('SELECT bid, name, block_type FROM {quotes_blocks}');
617
618 while ($block = db_fetch_object($result)) {
619 $blocks[$block->bid] = array(
620 'info' => t('Quotes') . ': ' . $block->name,
621 'cache' => $block->block_type == 0 ? BLOCK_NO_CACHE : BLOCK_CACHE_PER_ROLE,
622 );
623 }
624
625 return $blocks;
626 }
627
628 function quotes_block_mb_blocked() {
629 return 'quotes';
630 }
631
632 /**
633 * Quotes block configuration.
634 */
635 function _quotes_block_configure($delta = 0, $edit = array()) {
636 global $_quotes_subs;
637 drupal_add_js(drupal_get_path('module', 'quotes') . '/quotes.js');
638
639 $block = db_fetch_object(db_query("SELECT qb.* FROM {quotes_blocks} qb WHERE bid = %d", $delta));
640 $any_filters = $block->nid_filter . $block->aid_filter . $block->rid_filter . $block->uid_filter . $block->tid_filter;
641 $block->aid_filter = explode(',', $block->aid_filter);
642 $block->rid_filter = explode(',', $block->rid_filter);
643 $block->uid_filter = explode(',', $block->uid_filter);
644 $block->tid_filter = explode(',', $block->tid_filter);
645
646 // Get authors.
647 $authors = quotes_get_authors();
648
649 // Get roles.
650 $roles = user_roles(FALSE, 'create quotes');
651
652 foreach (user_roles(FALSE, 'import quotes') as $rid => $role) {
653 $roles[$rid] = $role;
654 }
655
656 foreach (user_roles(FALSE, 'edit own quotes') as $rid => $role) {
657 $roles[$rid] = $role;
658 }
659
660 // Get users.
661 $users = array();
662
663 if ($roles[DRUPAL_ANONYMOUS_RID]) {
664 $users[0] = variable_get('anonymous', t('Anonymous'));
665 }
666
667 $result = db_query("SELECT DISTINCT u.uid, u.name FROM {users} u LEFT JOIN {users_roles} ur ON ur.uid = u.uid WHERE u.uid = 1 OR ur.rid IN (" . implode(',', (count($roles) ? array_keys($roles) : array(0))) . ")");
668
669 while ($row = db_fetch_object($result)) {
670 $users[$row->uid] = ($row->uid ? $row->name : variable_get('anonymous', t('Anonymous')));
671 }
672
673 $form = array();
674
675 if ($delta) {
676 $form['bid'] = array(
677 '#type' => 'value',
678 '#value' => $delta
679 );
680 }
681
682 $form['quotes'] = array(
683 '#type' => 'fieldset',
684 '#title' => 'Quotes specific settings',
685 '#collapsible' => TRUE,
686 '#collapsed' => FALSE,
687 );
688
689 $form['quotes']['block_type'] = array(
690 '#type' => 'radios',
691 '#title' => t('Type'),
692 '#required' => TRUE,
693 '#default_value' => (($block->block_type == 1) ? 1 : 0),
694 // Note the order of these options is important!
695 '#options' => array(t('Random'), t('Most recent'), t('Unpublished')),
696 '#description' => t('"Random" will choose a published quote at random. "Most recent" will display the most recently updated quotes. "Unpublished" will display quotes that are marked as not published (awaiting approval).'),
697 '#prefix' => '<div class="quotes-radios">',
698 '#suffix' => '</div>'
699 );
700
701 $sub_str = implode(', ', array_keys($_quotes_subs));
702 $form['quotes']['name'] = array(
703 '#type' => 'textfield',
704 '#title' => t('Name'),
705 '#required' => TRUE,
706 '#default_value' => $block->name,
707 '#description' => t("Enter a unique name for this block. This will identify the block on the !block administration page and be used for the default block title. The name may include any of: '%subs'.", array('!block administration page' => l(t('block administration page'), 'admin/build/block'), '%subs' => $sub_str)),
708 );
709
710 $form['quotes']['show_titles'] = array(
711 '#type' => 'checkbox',
712 '#title' => t('Show titles'),
713 '#required' => FALSE,
714 '#default_value' => $block->show_titles,
715 '#description' => t('If this option is selected, the titles of the quotes in this block will be shown.'),
716 );
717
718 $form['quotes']['show_citation'] = array(
719 '#type' => 'checkbox',
720 '#title' => t('Show citation?'),
721 '#default_value' => $block->show_citation,
722 '#description' => t('If this option is selected, the citation for the quote will be included in blocks.')
723 );
724
725 $form['quotes']['max_length'] = array(
726 '#type' => 'select',
727 '#title' => t('Maximum quote length'),
728 '#options' => drupal_map_assoc(array(0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 128, 192, 256, 320, 384, 448, 512)),
729 '#default_value' => $block->max_length,
730 '#description' => t('This will limit the length of a quote shown in the block. A value of zero (0) means no limit.')
731 );
732
733 $form['quotes']['block_count'] = array(
734 '#type' => 'select',
735 '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 40, 50, 75, 100)),
736 '#title' => t('Number of quotes in this block'),
737 '#default_value' => $block->count,
738 '#description' => t('This sets the maximum number of quotes that will be displayed in this block. Note that "Random" and timed ("Update every") blocks may contain only one quote.'),
739 );
740
741 $form['quotes']['view_text'] = array(
742 '#type' => 'textfield',
743 '#title' => t('Block "View" link text'),
744 '#maxlength' => 64,
745 '#default_value' => $block->view_text,
746 '#description' => t('The text of the link to view the quote from a block. Leaving this field blank disables the link.')
747 );
748
749 $form['quotes']['view_weight'] = array(
750 '#type' => 'weight',
751 '#title' => t('"View" link weight'),
752 '#delta' => 1,
753 '#default_value' => $block->view_weight,
754 '#description' => t('A positive value sets the link after the body; a negative value before.')
755 );
756
757 $form['quotes']['block_more'] = array(
758 '#type' => 'textfield',
759 '#title' => t('"More" link text'),
760 '#maxlength' => 64,
761 '#default_value' => $block->more_text,
762 '#description' => t('This sets the text that will display on the "more" link at the bottom of the block. Leave it blank for no link. This is only available for a "Random" block.'),
763 '#prefix' => '<div class="quotes_block_more">',
764 // '#suffix' => '</div>',
765 );
766
767 $frequency = drupal_map_assoc(range(0, 100, 10));
768 $form['quotes']['rand_freq'] = array(
769 '#type' => 'select',
770 '#title' => t('How often block will show'),
771 '#options' => $frequency,
772 '#default_value' => isset($block->rand_freq) ? $block->rand_freq : 100,
773 '#description' => t('This sets the frequency with which the block will be shown. 100% is all the time; 0% is none of the time.'),
774 // '#prefix' => '<div class="quotes_block_more">',
775 '#suffix' => '</div>',
776 );
777
778 $form['quotes']['filters'] = array(
779 '#type' => 'fieldset',
780 '#title' => 'Quotes Filters',
781 '#collapsible' => TRUE,
782 '#collapsed' => empty($any_filters),
783 );
784
785 $form['quotes']['filters']['nid_filter'] = array(
786 '#type' => 'textarea',
787 '#title' => t('Node filter'),
788 '#rows' => 2,
789 '#default_value' => $block->nid_filter,
790 '#description' => t('To restrict this block to display only certain quotes based on node IDs, enter the IDs here separated by commas, spaces, or returns.'),
791 );
792
793 if (count($authors)) {
794 $form['quotes']['filters']['aid_filter'] = array(
795 '#type' => 'select',
796 '#title' => t('Author filter'),
797 '#multiple' => TRUE,
798 '#default_value' => $block->aid_filter,
799 '#options' => $authors,
800 '#description' => t('To restrict this block to display only quotes from certain authors, select the authors here.'),
801 );
802 }
803 else {
804 $form['quotes']['filters']['aid_filter'] = array(
805 '#type' => 'item',
806 '#title' => t('Author filter'),
807 '#description' => t('There are no authors. To filter by authors, there must be at least one quote with an attributed author')
808 );
809 }
810
811 if (count($roles)) {
812 $form['quotes']['filters']['rid_filter'] = array(
813 '#type' => 'select',
814 '#title' => t('Role filter'),
815 '#multiple' => TRUE,
816 '#default_value' => $block->rid_filter,
817 '#options' => $roles,
818 '#description' => t('To restrict this block to display only quotes submitted by users in specific roles, select the roles here.'),
819 );
820 }
821 else {
822 $form['quotes']['filters']['rid_filter'] = array(
823 '#type' => 'item',
824 '#title' => t('Role filter'),
825 '#description' => t('There are no roles configured with the %create quotes, %import quotes, or %edit own quotes permissions, so no roles are available. To filter by role, please assign this permission to at least one role on the !access control page.', array('%create quotes' => t('create quotes'), '%import quotes' => t('import quotes'), '%edit own quotes' => t('edit own quotes'), '!access control page' => l(t('administer permissions page'), 'admin/user/permissions'))),
826 );
827 }
828
829 $form['quotes']['filters']['uid_filter'] = array(
830 '#type' => 'select',
831 '#title' => t('User filter'),
832 '#multiple' => TRUE,
833 '#default_value' => $block->uid_filter,
834 '#options' => $users,
835 '#description' => t('To restrict this block to display only quotes submitted by specific users, select the users here.'),
836 );
837
838 if (drupal_function_exists('taxonomy_form_all')) {
839 $form['quotes']['filters']['tid_filter'] = array(
840 '#type' => 'select',
841 '#title' => t('Category filter'),
842 '#multiple' => TRUE,
843 '#default_value' => $block->tid_filter,
844 '#options' => taxonomy_form_all(TRUE),
845 '#description' => t('To restrict this block to display only quotes in specific categories, select the categories here.'),
846 );
847 }
848
849 $form['quotes']['cron'] = array(
850 '#type' => 'fieldset',
851 '#title' => t('Update options'),
852 '#description' => t('Note: Timed blocks will contain only one quote.'),
853 '#collapsible' => TRUE,
854 '#collapsed' => FALSE,
855 );
856
857 $form['quotes']['cron']['cron_interval'] = array(
858 '#type' => 'textfield',
859 '#size' => 4,
860 '#maxlength' => 3,
861 '#default_value' => ($block->cron_interval ? $block->cron_interval : ''),
862 '#field_prefix' => t('Update every'),
863 '#prefix' => '<div class="container-inline">',
864 );
865
866 $form['quotes']['cron']['cron_step'] = array(
867 '#type' => 'select',
868 '#default_value' => $block->cron_step,
869 '#options' => array(
870 1 => t('seconds'),
871 60 => t('minutes'),
872 3600 => t('hours'),
873 86400 => t('days'),
874 604800 => t('weeks'),
875 ),
876 '#suffix' => '</div>'
877 );
878
879 $form['quotes']['cron']['description'] = array(
880 '#type' => 'item',
881 '#description' => t('If set, the quote displayed in this block will get updated based on the interval specified (requires cron if page cache is enabled). Leave this value blank to have the quote updated every time the block is viewed.'),
882 '#prefix' => '<div style="display: block;">',
883 '#suffix' => '</div>',
884 );
885
886 return $form;
887 }
888
889 /**
890 * Quotes block configuration save.
891 */
892 function _quotes_block_configure_save($delta, $edit) {
893 $vals = array(
894 $edit['name'],
895 $edit['block_type'],
896 preg_replace('<[,\s]+>', ',', trim($edit['nid_filter'])),
897 implode(',', (array) $edit['aid_filter']),
898 implode(',', (array) $edit['rid_filter']),
899 implode(',', (array) $edit['uid_filter']),
900 implode(',', (array) $edit['tid_filter']),
901 ($edit['cron_interval'] ? $edit['cron_interval'] : 0),
902 $edit['cron_step'],
903 // Make sure random and timed blocks have a count of 1.
904 ($edit['block_type'] == 0 || !empty($edit['cron_interval'])) ? 1 : $edit['block_count'],
905 $edit['show_titles'],
906 $edit['show_citation'],
907 // We only save the "more" text for random blocks.
908 $edit['block_type'] == 0 ? $edit['block_more'] : NULL,
909 $edit['view_text'],
910 $edit['view_weight'],
911 $edit['max_length'],
912 // Frequency is only for random blocks.
913 ($edit['block_type'] == 0 ? $edit['rand_freq'] : 100),
914 $delta
915 );
916 db_query("UPDATE {quotes_blocks} SET name = '%s', block_type = %d, nid_filter = '%s', aid_filter = '%s', rid_filter = '%s',
917 uid_filter = '%s', tid_filter = '%s', cron_interval = %d, cron_step = %d,
918 count = %d, show_titles = %d, show_citation = %d, more_text = '%s',
919 view_text = '%s', view_weight = %d, max_length = %d, rand_freq = %d WHERE bid = %d", $vals);
920
921 }
922
923 /**
924 * Implements hook_form_alter().
925 */
926 function quotes_form_alter(&$form, &$form_state, $form_id) {
927 if ($form_id == 'block_admin_configure' && $form['module']['#value'] == 'quotes') {
928 $form['#validate'][] = '_quotes_block_configuration_validate';
929 }
930 }
931
932 /**
933 * Validates that changes made on the block configuration screen are valid.
934 *
935 * @param $form_id
936 * The string specifying the form ID of the form that was submitted.
937 * @param $form_values
938 * The array specifying the form values.
939 */
940 function _quotes_block_configuration_validate($form, &$form_state) {
941 if (trim($form_state['values']['nid_filter'])
942 && !preg_match('<^(\d+[,\s]*)+$>', trim($form_state['values']['nid_filter']))) {
943 form_set_error('nid_filter', t('Please enter valid node IDs.'));
944 }
945
946 $interval = $form_state['values']['cron_interval'];
947 if (($interval != '') && (!preg_match('<^\d+$>', $interval) || ($interval < 1) || ($interval > 999))) {
948 form_set_error('cron_interval', t('The update interval must be between 1 and 999.'));
949 }
950 }
951
952 /**
953 * Implements hook_cron().
954 */
955 function quotes_cron() {
956 $result = db_query("SELECT qb.* FROM {quotes_blocks} qb INNER JOIN {blocks} b ON b.module = 'quotes' WHERE b.status = 1 AND qb.cron_interval > 0 AND (qb.vid = 0 OR (qb.cron_last + (qb.cron_step * qb.cron_interval)) < %d)", REQUEST_TIME);
957
958 for ($updated = FALSE; $block = db_fetch_array($result); $updated = TRUE) {
959 $quotes = quotes_get_quote($block, TRUE, 1);
960 db_query('UPDATE {quotes_blocks} SET vid = %d, cron_last = %d WHERE bid = %d', $quotes[0], REQUEST_TIME, $block['bid']);
961 }
962
963 if ($updated) {
964 cache_clear_all();
965 }
966 }
967
968 /**
969 * Implements hook_theme().
970 */
971 function quotes_theme() {
972 return array(
973 'quotes_user_form' => array(
974 'arguments' => array('form' => NULL),
975 ),
976 'quotes_quote' => array(
977 'arguments' => array('node' => NULL, 'teaser' => FALSE),
978 ),
979 'quotes_page' => array(
980 'arguments' => array('uid' => NULL),
981 ),
982 'quotes_blocks_settings' => array(
983 'arguments' => array('form' => NULL),
984 ),
985 );
986 }
987
988 //********************************************************************
989 //* Themeable Functions
990 //********************************************************************
991
992 /**
993 * Themeable function that displays a single quote and optional author.
994 *
995 * @param $node
996 * The node object containing the quote body and author.
997 * @param $teaser
998 * Boolean to indicate whether to use the teaser or the body.
999 * @param $bio
1000 * Boolean to indicate whether to use the author's biography.
1001 * @param $max_length
1002 * Integer to limit the quote length in a block.
1003 *
1004 * @return
1005 * An HTML-formatted quote.
1006 *
1007 * @ingroup themeable
1008 */
1009 function theme_quotes_quote($node, $teaser = FALSE, $bio = TRUE, $max_length = 0) {
1010 global $user;
1011
1012 if ($max_length) {
1013 // We want to limit the text.
1014 $text = node_teaser($node->body, $node->format, $size = $max_length);
1015 if (drupal_strlen($text) < drupal_strlen($node->body)) {
1016 $text .= '&hellip;' . l(t('(more)'), drupal_get_path_alias('node/' . $node->nid));
1017 }
1018 }
1019 else {
1020 $text = $teaser ? $node->teaser : $node->body;
1021 }
1022 $body = check_markup($text, $node->format, FALSE);
1023 $leader = variable_get('quotes_leader', '&mdash;') . ' ';
1024 // $show_bio = (arg(1) != 'author') && variable_get('quotes_author_bio', FALSE);
1025 if (arg(1) == 'author') {
1026 $show_bio = FALSE;
1027 }
1028 else {
1029 $show_bio = variable_get('quotes_author_bio', FALSE);
1030 }
1031
1032 if (variable_get('quotes_author_link', FALSE)) {
1033 $author = $node->quotes_author ? $leader . l($node->quotes_author, 'quotes/author/' . $node->quotes_author, array('title' => t('View all quotes by this author'))) : '';
1034 $author = decode_entities($author);
1035 }
1036 else {
1037 $author = ($node->quotes_author ? check_markup($leader . $node->quotes_author, $node->format, FALSE) : '');
1038 }
1039
1040 switch ($show_bio) {
1041 case 1:
1042 $bio = $node->quotes_bio ? ('<div class="quotes-bio">' . check_markup($node->quotes_bio, $node->format, FALSE) . '</div>') : '';
1043 break;
1044 case 2:
1045 $menu_info = menu_get_item('quotes');
1046 $menu_title = drupal_strtolower($menu_info['title']);
1047 $bio = $node->quotes_author ? ('<div class="quotes-bio-link">' . l(t('See biography and !menu', array('!menu' => $menu_title)), 'quotes/author/' . $node->quotes_author, array('title' => t('View all quotes by this author'))) . '</div>') : '';
1048 break;
1049 default:
1050 $bio = NULL;
1051 }
1052 $citation = ($node->quotes_citation ? check_markup("<cite>" . $node->quotes_citation . "</cite>", $node->format, FALSE) : '');
1053
1054 return '<div class="quotes-quote">' . $body . '</div>'
1055 . ($author ? '<div class="quotes-author">' . $author . '</div>' : '')
1056 . $bio
1057 . ($citation ? '<div class="quotes-citation">' . $citation . '</div>' : '')
1058 . '<div class="clear-block"></div>';
1059 }
1060
1061 /**
1062 * Themeable function that displays a page of quotes that may be
1063 * restricted to a certain user.
1064 *
1065 * @param $account
1066 * The user object for the user whose quotes should be displayed (loaded by menu system).
1067 *
1068 * @return
1069 * An HTML-formatted list of quotes.
1070 *
1071 * @ingroup themeable
1072 */
1073 function theme_quotes_page($account) {
1074 $limit = variable_get('quotes_per_page', 10);
1075
1076 if (isset($account)) {
1077 $user = user_load($account->uid);
1078 $name = isset($user->realname) ? $user->realname : $user->name;
1079 $menu_info = menu_get_item('quotes');
1080 $menu_title = $menu_info['title'];
1081 drupal_set_title(t("!name's !menu", array('!menu' => $menu_title, '!name' => $name)));
1082
1083 $url = url("quotes/$user->uid/feed");
1084 //$result = pager_query(db_rewrite_sql("SELECT n.nid FROM {node} n INNER JOIN {node_revisions} nr USING (vid) WHERE n.status=1 AND n.type='quotes' AND n.uid=%d ORDER BY n.sticky DESC, n.created DESC"), $limit, 0, NULL, $user->uid);
1085 $query = db_select('node', 'n');
1086 $nr_alais = $query->join('node.revision', 'nr', 'nr.vid = n.vid');
1087 $query
1088 -> fields('n', array('nid'))
1089 -> condition("n.status", '1')
1090 -> condition("n.type", 'quotes')
1091 -> condition("n.uid", $user->uid)
1092 -> Orderby("n.sticky", 'DESC')
1093 -> Orderby("n.created", 'DESC')
1094 -> range('0', $limit);
1095 $query->addTag('node_access');
1096 $result = $query->execute();
1097 }
1098 else {
1099 $url = url('quotes/feed');
1100 //$result = pager_query(db_rewrite_sql("SELECT n.nid FROM {node} n INNER JOIN {node_revisions} nr USING (vid) WHERE n.status = 1 AND n.type = 'quotes' ORDER BY n.sticky DESC, n.created DESC"), $limit, 0);
1101 $query = db_select('node', 'n');
1102 $nr_alais = $query->join('node.revision', 'nr', 'nr.vid = n.vid');
1103 $query
1104 -> fields('n', array('nid'))
1105 -> condition("n.status", '1')
1106 -> condition("n.type", 'quotes')
1107 -> condition("n.uid", $user->uid)
1108 -> Orderby("n.sticky", 'DESC')
1109 -> Orderby("n.created", 'DESC')
1110 ->range('1', $limit);
1111 $query->addTag('node_access');
1112 $result = $query->execute();
1113 }
1114
1115 $output = '<div class="quotes">';
1116 $count = 0;
1117
1118 while ($nid = db_result($result)) {
1119