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

Contents of /contributions/modules/annotate/annotate.module

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


Revision 1.13 - (show annotations) (download) (as text)
Fri Sep 18 16:10:48 2009 UTC (2 months, 1 week ago) by clemenstolboom
Branch: MAIN
CVS Tags: HEAD
Changes since 1.12: +167 -75 lines
File MIME type: text/x-php
Bug #222556 - clemenstolboom: Multiple annotations per uid + nid
1 <?php
2
3 // $Id: annotate.module,v 1.12 2009/09/04 14:55:43 clemenstolboom Exp $
4
5 /**
6 * @file
7 * Lets users add private annotations to nodes.
8 * Currently only allows 1 annotation per node.
9 * Variables are now saved to the variable database.
10 * Adds a text field when a node is displayed
11 * so that authenticated users may make notes.
12 */
13
14 define( ANNOTATE_CREATE_ANNOTATION, 'create annotation');
15 define( ANNOTATE_READ_PUBLISHED_ANNOTATION, 'read annotation');
16
17 define( ANNOTATE_ENUM_VISIBILITY_PRIVATE, 0);
18 define( ANNOTATE_ENUM_VISIBILITY_EDITOR, 1);
19 define( ANNOTATE_ENUM_VISIBILITY_OTHERS, 2);
20 define( ANNOTATE_ENUM_VISIBILITY_COLLABORATORS, 3);
21
22 /**
23 * Implementation of hook_menu()
24 */
25 function annotate_menu() {
26 $items = array();
27
28 $items[ 'admin/settings/annotate'] = array(
29 'title' => 'Annotation settings',
30 'description' => 'Change how annotations behave.',
31 'page callback' => 'drupal_get_form',
32 'page arguments' => array('annotate_admin_settings'),
33 'access arguments' => array( 'administer site configuration'),
34 );
35
36 $items[ 'node/%/annotations'] = array(
37 'title' => 'Annotations',
38 'page callback' => 'annotate_list_by_node',
39 'page arguments' => array(1),
40 'access callback' => 'annotate_node_has_annotations_access',
41 'access arguments' => array(1),
42 'type' => MENU_LOCAL_TASK,
43 );
44
45 $items['user/%/annotations'] = array(
46 'title' => 'Annotations',
47 'page callback' => 'annotate_list_by_user',
48 'page arguments' => array(1),
49 'access callback' => 'annotate_user_has_annotations_access',
50 'access arguments' => array(1),
51 'type' => MENU_LOCAL_TASK,
52 );
53 return $items;
54 }
55
56 function annotate_node_has_annotations_access($nid) {
57 return is_numeric( $nid)
58 && user_access( ANNOTATE_READ_PUBLISHED_ANNOTATION)
59 && db_result(_annotate_list_by_node($nid))
60 ;
61 }
62
63 function annotate_user_has_annotations_access($uid) {
64 global $user;
65
66 return is_numeric($uid)
67 && ($user->uid == $uid || user_access( ANNOTATE_READ_PUBLISHED_ANNOTATION))
68 && db_result(_annotate_list_by_user($uid))
69 ;
70 }
71
72 /**
73 * Implementation of hook_perm()
74 */
75 function annotate_perm() {
76 return array( ANNOTATE_CREATE_ANNOTATION, ANNOTATE_READ_PUBLISHED_ANNOTATION);
77 }
78
79 /**
80 * Define the settings form.
81 */
82 function annotate_admin_settings() {
83 $form['annotate_show_expanded'] = array(
84 '#type' => 'radios',
85 '#title' => t('Show the annotation form expanded'),
86 '#options' => array( '1' => t('Yes'), '0' => t('No')),
87 '#default_value' => variable_get('annotate_show_expanded', '0'),
88 '#description' => t('The annotation is not collapsed by default'),
89 );
90
91 $form['annotate_nodetypes'] = array(
92 '#type' => 'checkboxes',
93 '#title' => t('Users may annotate these node types'),
94 '#options' => node_get_types('names'),
95 '#default_value' => variable_get('annotate_nodetypes', array('story')),
96 '#description' => t('A text field will be available on these node types to make user-specific notes.'),
97 );
98
99 $form['multi']= array(
100 '#type' => 'fieldset',
101 '#title' => t('Multiple Annotations'),
102 );
103 // TODO: validate with annotate_nodetypes
104 $form['multi']['annotate_show_multi_expanded'] = array(
105 '#type' => 'radios',
106 '#title' => t('Show the multiple annotation fieldset expanded'),
107 '#options' => array( '1' => t('Yes'), '0' => t('No')),
108 '#default_value' => variable_get('annotate_show_multi_expanded', '0'),
109 '#description' => t('The annotation is not collapsed by default'),
110 );
111
112 $form['multi']['annotate_nodetypes_multiple'] = array(
113 '#type' => 'checkboxes',
114 '#title' => t('Multiple annotations for these node types'),
115 '#options' => node_get_types('names'),
116 '#default_value' => variable_get('annotate_nodetypes_multiple', array()),
117 '#description' => t('Multiple annotations per users are allowed for these node types.'),
118 );
119
120 // FAPI 2 magic: make arrays auto compact
121 $form['array_filter'] = array(
122 '#type' => 'hidden'
123 );
124
125
126 return system_settings_form($form);
127 }
128
129 function annotate_admin_settings_validate($form, $form_values) {
130 $types = array_values( $form_values['values']['annotate_nodetypes']);
131 $multi = array_values( $form_values['values']['annotate_nodetypes_multiple']);
132 $diff = array_diff( $multi, $types, array(0));
133 if (count( $diff)) {
134 form_set_error('annotate_nodetypes_multiple', t("Mismatch between 'node types' and 'multiple'"));
135 }
136 }
137
138 /**
139 * Implementation of hook_nodeapi().
140 *
141 * We only intercept the view $op
142 *
143 * @param object $node
144 * @param string $op
145 * @param boolean $teaser
146 * @param boolean $page
147 */
148 function annotate_nodeapi(&$node, $op, $teaser, $page) {
149 switch ($op) {
150 case 'view':
151 $node->content['debug']= array(
152 '#content' => "hoi",
153 '#weight' => 1,
154 );
155
156 global $user;
157 if ($teaser || $user->uid == 0) {
158 // no annotations for teaser or anonymous users
159 break;
160 }
161
162
163 // No form when user may not create an annotation
164 if (!user_access(ANNOTATE_CREATE_ANNOTATION)) {
165 break;
166 }
167 // only show for allowed node types
168 $types_to_annotate = variable_get('annotate_nodetypes', array('story'));
169 if (!in_array($node->type, $types_to_annotate)) {
170 break;
171 }
172
173 if ($node->printing===TRUE) {
174 // We put all annotation into the printer friendly page
175 $node->content['annotate_print'] = array(
176 '#value' => annotate_list_by_node( $node->nid),
177 '#weight' => 10,
178 );
179 }
180 else {
181 drupal_add_css( drupal_get_path('module', 'annotate') .'/css/annotate.css');
182 $node->notes=_annotate_get_notes($node, $user);
183 $node->content['annotate_print'] = array(
184 '#value' => _annotate_forms( $node),
185 '#weight' => 10,
186 );
187 }
188 break;
189
190 case 'delete':
191 _annotate_node_delete($node);
192 break;
193
194 default:
195 break;
196 }
197 }
198
199 function _annotate_forms( $node) {
200 global $user;
201 $forms="";
202 foreach($node->notes as $index => $note) {
203 if (!isset($note->uid) || $note->uid==$user->uid) {
204 $forms .= drupal_get_form('annotate_private_entry_form_' . $note->timestamp, $note);
205 }
206 else {
207 $form=array();
208 $form['annotate'] = array(
209 '#type' => 'fieldset',
210 '#title' => theme('annotate_fieldset_title', $note),
211 '#collapsible' => TRUE,
212 '#collapsed' => !$note->note && !variable_get('annotate_show_expanded', '0'),
213 );
214 _annotate_prepare_view_item( $note);
215 $form['annotate']['note'] = array(
216 '#type' => 'markup',
217 '#value' => theme_annotate_node_item($note),
218 );
219 $forms .= drupal_render($form);
220 }
221 }
222
223 $multiple = in_array($node->type, variable_get('annotate_nodetypes_multiple', array()));
224 if ($multiple && count($node->notes)>1) {
225 // Make fieldset displaying count of visible notes.
226 $form=array();
227 $form['annotates_private'] = array(
228 '#type' => 'fieldset',
229 '#title' => t('Annotations') . ' : ' . (count($node->notes) - 1),
230 '#collapsible' => TRUE,
231 '#collapsed' => !variable_get('annotate_show_multi_expanded', '0'),
232 );
233 $form['annotates_private']['forms'] = array(
234 '#type' => 'markup',
235 '#value' => $forms,
236 );
237 $forms = drupal_render($form);
238 }
239
240 return $forms;
241 }
242
243 function _annotate_get_notes($node, $user) {
244 $multiple = in_array($node->type, variable_get('annotate_nodetypes_multiple', array()));
245
246 $notes= array();
247 // Existing annotations
248 $result = _annotate_list_by_node( $node->nid);
249 //$result = _annotate_list_by_node_by_user( $node->nid, $user->uid);
250 while ($note = db_fetch_object($result)) {
251 $notes[]=$note;
252 if (!$multiple) {
253 break;
254 }
255 }
256 // Add new in non or multiple
257 if (!count($notes) || $multiple) {
258 // New annotation
259 $note= (object)array();
260 $note->nid=$node->nid;
261 $notes[]=$note;
262 }
263 return $notes;
264 }
265 /**
266 * Implementation of hook_user()
267 *
268 * We need to delete the user annotation when a user is deleted.
269 *
270 * @param string $op
271 * @param array $edit
272 * @param object $account
273 * @param string $category
274 */
275 function annotate_user($op, &$edit, &$account, $category = NULL) {
276 switch($op) {
277 case 'delete':
278 _annotate_user_delete( $account);
279 break;
280
281 default:
282 break;
283 }
284 }
285
286 /*
287 * Implementation of hook_forms
288 *
289 * By adding an id next to the form builder we use have multiple forms.
290 *
291 * Thanks to http://www.computerminds.co.uk/drupal-6-multiple-instances-same-form-one-page
292 */
293 function annotate_forms($form_id) {
294 $forms = array();
295 if (strpos($form_id, 'annotate_private_entry_form_') === 0) {
296 $forms[$form_id] = array(
297 'callback' => 'annotate_private_entry_form',
298 );
299 }
300 return $forms;
301 }
302
303 /**
304 * Define the form for entering an annotation.
305 *
306 * The fieldset collapse if no annotation is made and allowed
307 */
308 function annotate_private_entry_form($form_state, $note) {
309 $form['annotate'] = array(
310 '#type' => 'fieldset',
311 '#title' => theme('annotate_fieldset_title', $note),
312 '#collapsible' => TRUE,
313 '#collapsed' => !$note->note && !variable_get('annotate_show_expanded', '0'),
314 );
315
316 $form['annotate']['timestamp'] = array(
317 '#type' => 'value',
318 '#value' => $note->timestamp
319 );
320
321 $form['annotate']['uid'] = array(
322 '#type' => 'value',
323 '#value' => $note->uid
324 );
325
326 $form['annotate']['nid'] = array(
327 '#type' => 'value',
328 '#value' => $note->nid
329 );
330
331 $form['annotate']['note_filter']['note'] = array(
332 '#type' => 'textarea',
333 '#title' => t('Notes'),
334 '#default_value' => $note->note,
335 '#description' => t('Make your personal annotations about this content here. When marked private only you (and the site administrator) will be able to see them.'),
336 );
337
338 $form['annotate']['note_filter']['format']= filter_form( $note->note_format);
339
340 $options = _annotate_get_visibility_list();
341
342 $form['annotate']['visibility'] = array(
343 '#type' => 'radios',
344 '#title' => t('Visibility'),
345 '#default_value' => isset($note->visibility) ? $note->visibility : 0,
346 '#options' => $options,
347 '#description' => t('Set the visibility of this annotation'),
348 );
349
350 $form['annotate']['submit'] = array(
351 '#type' => 'submit',
352 '#value' => t('Update')
353 );
354
355 // Make sure the correct submit handle is called.
356 $form['#submit'] = array(
357 'annotate_private_entry_form_submit',
358 );
359
360 return $form;
361 }
362
363 function _annotate_get_visibility_list() {
364 return array(
365 ANNOTATE_ENUM_VISIBILITY_PRIVATE => t('Private'),
366 ANNOTATE_ENUM_VISIBILITY_EDITOR => t('Node editor'),
367 ANNOTATE_ENUM_VISIBILITY_COLLABORATORS => t('Node annotators'),
368 ANNOTATE_ENUM_VISIBILITY_OTHERS => t('Others'),
369 );
370 }
371
372 /**
373 * implementation of hook_submit
374 */
375 function annotate_private_entry_form_submit($form, &$form_state) {
376 global $user;
377 $nid = $form_state['values']['nid'];
378 $note_format = trim( $form_state['values']['format']);
379 $note = trim( $form_state['values']['note']);
380 $visibility = $form_state['values']['visibility'];
381 $timestamp = $form_state['values']['timestamp'];
382
383 db_query("DELETE FROM {annotations} WHERE uid = %d AND nid = %d AND timestamp = %d", $user->uid, $nid, $timestamp);
384
385 if (isset($note) && strlen($note)) {
386 db_query("INSERT INTO {annotations} (uid, nid, note_format, note, visibility, timestamp) VALUES (%d, %d, %d, '%s', %d, %d)"
387 , $user->uid, $nid, $note_format, $note, $visibility, time()
388 );
389
390 drupal_set_message(t('Your annotation was saved.'));
391 }
392 else {
393 drupal_set_message(t('Your annotation is deleted.'));
394 }
395 }
396
397 function annotate_theme() {
398 return array(
399 'annotate_user_item' => array(
400 'annotation' => NULL,
401 ),
402 'annotate_node_item' => array(
403 'annotation' => NULL,
404 ),
405 'annotate_fieldset_title' => array(
406 'annotation' => NULL,
407 ),
408 );
409 }
410
411 function theme_annotate_user_item( $annotation) {
412 return '<div class="annotation">'
413 . t('node') .': '.'<span class="annotation-title">'. l( $annotation->title , 'node/'. $annotation->nid ) .'</span>'
414 . t('visibility') .': '.'<span class="annotation-visibility">'. $annotation->visibility .'</span>'
415 . t('written on') .': '.'<span class="annotation-timestamp">'. $annotation->formatted_timestamp .'</span>'
416 .'<span class="annotation-note">'. $annotation->note .'</span>'
417 .'</div>';
418 }
419
420 function theme_annotate_node_item( $annotation) {
421 return '<div class="annotation">'
422 . t('author') .': '.'<span class="annotation-user">'
423 . (user_access( 'access_user_profiles') ? l($annotation->name, 'user/'. $annotation->uid ) : $annotation->name)
424 . '</span>'
425 . t('visibility') .': '.'<span class="annotation-visibility">'. $annotation->visibility .'</span>'
426 . t('written on') .': '.'<span class="annotation-timestamp">'. $annotation->formatted_timestamp .'</span>'
427 .'<span class="annotation-note">'. $annotation->note .'</span>'
428 .'</div>';
429 }
430
431 function theme_annotate_fieldset_title( $annotation) {
432 return t('Annotation')
433 . ($annotation->note
434 ? ' ' . t('by')
435 . ': ' . $annotation->name
436 . " : '" . check_plain(substr($annotation->note,0, 30)) . "'"
437 : ': - ' . t('New note') . ' -');
438 }
439
440 /* ========== PRIVATE FUNCTIONS ========== */
441
442 function _annotate_list_by_sql() {
443 $sql="SELECT n.nid, n.title, u.name, u.uid, a.visibility, a.note_format, a.note, a.timestamp"
444 ." FROM {annotations} a "
445 ." INNER JOIN {node} n ON a.nid= n.nid"
446 ." INNER JOIN {users} u ON a.uid=u.uid"
447 ;
448 return $sql;
449 }
450
451 /**
452 * Get annotations by user
453 *
454 * Retrieves all annotations visible for the current user
455 * @param int $uid
456 * @return db_resultset
457 */
458 function _annotate_list_by_user($account_uid) {
459 global $user;
460
461 $sql = _annotate_list_by_sql() ." WHERE a.uid = %d";
462
463 if ($user->uid==$account_uid || $user->uid==1) {
464 // own account : $account_uid == $user-> uid ==> show all
465 // user-1 ==> show all
466 $filter = "";
467 }
468 else {
469 $filter = " a.visibility= " . ANNOTATE_ENUM_VISIBILITY_OTHERS
470 . " OR (n.uid=%d AND a.visibility= " . ANNOTATE_ENUM_VISIBILITY_EDITOR . ")";
471
472 // AND-wrap
473 $filter = " AND (". $filter .")";
474 }
475 $sql .= $filter;
476 $sql .= ' ORDER BY timestamp';
477
478 $sql = db_rewrite_sql( $sql);
479 $result = db_query( $sql, $account_uid, $user->uid);
480
481 return $result;
482 }
483
484 function _annotate_list_by_node($nid) {
485 global $user;
486 $uid= $user->uid;
487
488 $sql = _annotate_list_by_sql() ." WHERE a.nid = %d";
489
490 if ($uid==1) {
491 $filter="";
492 }
493 else {
494 $filter=
495 " a.uid = %d"
496 ." OR (a.visibility = " . ANNOTATE_ENUM_VISIBILITY_EDITOR . " AND %d = n.uid)"
497 ." OR a.visibility = " . ANNOTATE_ENUM_VISIBILITY_OTHERS;
498
499 $is_collaborator = _annotate_exists_by_node_by_user($nid, $uid);
500
501 if ( $is_collaborator) {
502 $filter .= " OR a.visibility = " . ANNOTATE_ENUM_VISIBILITY_COLLABORATORS;
503 }
504 // AND-wrap
505 $filter = " AND (". $filter .")";
506 }
507 $sql .= $filter;
508 $sql .= ' ORDER BY timestamp';
509 $sql = db_rewrite_sql( $sql);
510 $result = db_query( $sql , $nid, $uid, $uid);
511
512 return $result;
513 }
514
515 function annotate_list_by_user($uid) {
516 $result= _annotate_list_by_user( $uid);
517
518 return _annotate_make_list($result);
519 }
520
521 function annotate_list_by_node($nid) {
522 $result= _annotate_list_by_node( $nid);
523
524 return _annotate_make_list($result);
525 }
526
527 function _annotate_list_by_node_by_user( $nid, $uid) {
528 $sql = "SELECT * FROM {annotations} WHERE nid = %d AND uid = %d";
529 $result = db_query( $sql, $nid, $uid);
530 return $result;
531 }
532
533 function _annotate_make_list($result) {
534 drupal_add_css( drupal_get_path('module', 'annotate') .'/css/annotate.css');
535
536 $items= array();
537
538 if ($result) {
539 while ($annotation = db_fetch_object( $result )) {
540 _annotate_prepare_view_item( $annotation);
541
542 $items[] = theme( 'annotate_user_item', $annotation);
543 }
544 }
545
546 return theme( 'item_list', $items, t('Annotations'), 'ul');
547 }
548
549 /**
550 * prepare a annotation for viewing
551 *
552 * @param object $annotation
553 */
554 function _annotate_prepare_view_item( $annotation) {
555 $annotation->title = check_plain( $annotation->title);
556 $annotation->visibility = _annotate_visibility_to_text($annotation->visibility);
557 $annotation->note = check_markup( $annotation->note, $annotation->note_format);
558 $annotation->formatted_timestamp= format_date($annotation->timestamp);
559 }
560
561 function _annotate_visibility_to_text( $visibility) {
562 static $list;
563
564 if (!$list) {
565 $list= _annotate_get_visibility_list();
566 }
567
568 return $list[$visibility];
569 }
570
571 /**
572 * Delete annotations associated with a node
573 *
574 * @param object $node
575 */
576 function _annotate_node_delete( &$node) {
577 $nid = $node->nid;
578 if (_annotate_exists_by_node( $nid)) {
579 db_query("DELETE FROM {annotations} WHERE nid = %d", $nid);
580 drupal_set_message(t('Annotations deleted for node %nid.', array( '%nid' => $nid)));
581 }
582 }
583
584 /**
585 * checks for the existance of node related annotations
586 *
587 * @param int $nid
588 * @return boolean
589 */
590 function _annotate_exists_by_node( $nid) {
591 $result= db_query( ( "SELECT COUNT(*) AS num FROM {annotations} WHERE nid=%d"), $nid);
592 $row = db_fetch_array( $result);
593 return $row['num'] > 0;
594 }
595
596 /**
597 * Delete annotations associated with a user
598 *
599 * @param object $account
600 */
601 function _annotate_user_delete( &$account) {
602 $uid = $account->uid;
603 if (_annotate_exists_by_user( $uid)) {
604 db_query("DELETE FROM {annotations} WHERE uid = %d", $uid);
605 drupal_set_message(t('Annotations deleted for user %uid.', array( '%uid' => $uid)));
606 }
607 }
608
609 /**
610 * checks for the existance of user related annotations
611 *
612 * @param int $uid
613 * @return boolean
614 */
615 function _annotate_exists_by_user( $uid) {
616 $result= db_query( ( "SELECT COUNT(*) AS num FROM {annotations} WHERE uid=%d"), $uid);
617 $row = db_fetch_array( $result);
618 return $row['num'] > 0;
619 }
620
621 function _annotate_exists_by_node_by_user( $nid, $uid) {
622 $result= db_query( ( "SELECT COUNT(*) AS num FROM {annotations} WHERE nid = %d AND uid=%d"), $nid, $uid);
623 $row = db_fetch_array( $result);
624 return $row['num'] > 0;
625 }

  ViewVC Help
Powered by ViewVC 1.1.2