| 1 |
<?php
|
| 2 |
// $Id
|
| 3 |
|
| 4 |
/**
|
| 5 |
* @file
|
| 6 |
* Defines an API for annotation fields which annotate particular field
|
| 7 |
* types on an arbitrary node
|
| 8 |
*
|
| 9 |
* In order to make a single content type able to modify all kinds of types
|
| 10 |
* we have a different annotation module:
|
| 11 |
*
|
| 12 |
* annotation module would provide here
|
| 13 |
* 1. fields nid, vid, fieldname, annotation_type, start, end, morehooks
|
| 14 |
* 2. particular annotation fields may just annotate a subset of
|
| 15 |
* available fieldtypes, widget types,
|
| 16 |
*
|
| 17 |
* Then video annotation just provides:
|
| 18 |
* 1. how to FILL the start/end/morehooks fields
|
| 19 |
* 2. ui widget (maybe even a modify on the three fields on the form)
|
| 20 |
inputs: which fields should/can we annotate?
|
| 21 |
* 3. validate morehooks field
|
| 22 |
* 4. define field type that we annotate
|
| 23 |
* 4.1 response hook that a node/field is annotatable with this widget
|
| 24 |
* 5. provide output version of annotated node (?or just js)
|
| 25 |
* 6. what to display in view-mode
|
| 26 |
* 7. Javascript Functions:
|
| 27 |
* init(contentfield,start,end,morehooks)--shows annotation
|
| 28 |
* prep_edit(contentfield,start-field,end-field,morehooks-field)
|
| 29 |
* 8. what an aggregate view of annotations on a single node should
|
| 30 |
* look like.
|
| 31 |
* Policies:
|
| 32 |
* just one type
|
| 33 |
* wherever 'focus' is?
|
| 34 |
* fieldname per type
|
| 35 |
* fieldtype priority (video,audio,image,text)
|
| 36 |
*
|
| 37 |
* sub-module hooks:
|
| 38 |
* - annotationfield_view
|
| 39 |
* - annotationfield_form
|
| 40 |
* - annotationfield_form_submit
|
| 41 |
* - annotationfield_annotatable_fields
|
| 42 |
* -
|
| 43 |
* -
|
| 44 |
* Any way to leverage reference node cck type?
|
| 45 |
* Well, what if there's more than one on the type, and
|
| 46 |
* even then, we'll probably have to do something significantly
|
| 47 |
* different when displaying the node.
|
| 48 |
*
|
| 49 |
how to annotate:
|
| 50 |
'click annotate'
|
| 51 |
on field
|
| 52 |
benefit of widget knowing what to work on/pay attention
|
| 53 |
a page reload for starting to annotate?
|
| 54 |
on node
|
| 55 |
when does widget get dipatched?
|
| 56 |
annotation framework helps?
|
| 57 |
|
| 58 |
|
| 59 |
So one order:
|
| 60 |
1. annotation module looks at each field
|
| 61 |
and determines which annotator will be used for each field
|
| 62 |
2. load the annotators
|
| 63 |
2.1 annotators load js with a hook to be called when 'annotate' is clicked
|
| 64 |
|
| 65 |
3. -annotate click links created
|
| 66 |
4.
|
| 67 |
|
| 68 |
|
| 69 |
*/
|
| 70 |
|
| 71 |
|
| 72 |
|
| 73 |
/**
|
| 74 |
* Implementation of hook_menu().
|
| 75 |
*/
|
| 76 |
function annotationfield_menu($may_cache) {
|
| 77 |
/* adds tab-style menu items to annotatable nodes for each
|
| 78 |
* content-type that
|
| 79 |
*/
|
| 80 |
$items = array();
|
| 81 |
if (!$may_cache) {
|
| 82 |
if (arg(0) == 'node' && is_numeric(arg(1))) {
|
| 83 |
$node = node_load(arg(1));
|
| 84 |
foreach (annotationfield_annotatable($node) as $annotator_cname => $annotator_ctype) {
|
| 85 |
$items[] = array('path' => 'node/' . arg(1) .'/annotate/'. $annotator_cname,
|
| 86 |
'title' => t('Add @ctype', array('@ctype' => $annotator_ctype['name'])),
|
| 87 |
'callback' => 'annotationfield_node_annotate',
|
| 88 |
'access' => user_access('access content') && user_access('create '. $annotator_cname .' content'),
|
| 89 |
'weight' => 5,
|
| 90 |
'type' => MENU_LOCAL_TASK, // :MENU_CALLBACK
|
| 91 |
);
|
| 92 |
}
|
| 93 |
}
|
| 94 |
}
|
| 95 |
return $items;
|
| 96 |
}
|
| 97 |
|
| 98 |
|
| 99 |
|
| 100 |
/**
|
| 101 |
* Implementation of hook_field_info().
|
| 102 |
*/
|
| 103 |
function annotationfield_field_info() {
|
| 104 |
return array(
|
| 105 |
'annotation_field' => array('label' => 'Annotation on a Node Field'),
|
| 106 |
);
|
| 107 |
}
|
| 108 |
|
| 109 |
/**
|
| 110 |
* Implementation of hook_field_settings().
|
| 111 |
*/
|
| 112 |
function annotationfield_field_settings($op, $field) {
|
| 113 |
switch ($op) {
|
| 114 |
case 'form':
|
| 115 |
///additions to the field configuration on a particular type
|
| 116 |
$form['referenceable_types'] = array(
|
| 117 |
'#type' => 'checkboxes',
|
| 118 |
'#title' => t('Content types that can be referenced'),
|
| 119 |
'#multiple' => TRUE,
|
| 120 |
'#default_value' => isset($field['referenceable_types']) ? $field['referenceable_types'] : array(),
|
| 121 |
'#options' => node_get_types('names'),
|
| 122 |
);
|
| 123 |
|
| 124 |
$annotators = module_invoke_all('annotationfield_annotator');
|
| 125 |
$form['annotator_types'] = array(
|
| 126 |
'#type' => 'checkboxes',
|
| 127 |
'#title' => t('Annotator types'),
|
| 128 |
'#multiple' => TRUE,
|
| 129 |
'#default_value' => isset($field['annotator_types']) ? $field['annotator_types'] : array_keys($annotators),
|
| 130 |
'#options' => $annotators,
|
| 131 |
);
|
| 132 |
$form['annotatee_fields'] = array('#tree' => TRUE,
|
| 133 |
'#type' =>'fieldset',
|
| 134 |
'#title'=>'For each content type, which fields',
|
| 135 |
);
|
| 136 |
$ref_types = (isset($field['referenceable_types'])) ?
|
| 137 |
$field['referenceable_types'] : array_keys(node_get_types('names'));
|
| 138 |
|
| 139 |
foreach ($ref_types as $nodetype) {
|
| 140 |
if (!$nodetype) continue;/// when type is not chosen it's set to int(0)
|
| 141 |
|
| 142 |
$node_fields = annotationfield_annotatable_fields($field, $nodetype);
|
| 143 |
$fieldnames = array_values($node_fields['fields']);
|
| 144 |
if (!empty($fieldnames)) {
|
| 145 |
$form['annotatee_fields'][$nodetype] = array(
|
| 146 |
'#collapsible'=>TRUE,
|
| 147 |
'#collapsed'=>TRUE,
|
| 148 |
'#type' => 'checkboxes',
|
| 149 |
'#multiple' => TRUE,
|
| 150 |
'#title' => t('Fields for @nodetype',array('@nodetype' => $nodetype)),
|
| 151 |
'#default_value' => isset($field['annotatee_fields'][$nodetype]) ? $field['annotatee_fields'][$nodetype] : $fieldnames,
|
| 152 |
'#options' => array_combine($fieldnames,$fieldnames),
|
| 153 |
);
|
| 154 |
}
|
| 155 |
}
|
| 156 |
|
| 157 |
return $form;
|
| 158 |
case 'save':
|
| 159 |
///returns a list of additional options on a field (processed by $op=form)
|
| 160 |
$settings = array('referenceable_types','annotator_types','annotatee_fields');
|
| 161 |
return $settings;
|
| 162 |
case 'database columns':
|
| 163 |
//assume nid, vid (version id), and fieldname
|
| 164 |
$columns = array(
|
| 165 |
'nid' => array('type' => 'int', 'not null' => TRUE, 'default' => '0'),
|
| 166 |
'vid' => array('type' => 'int', 'not null' => TRUE, 'default' => '0'),
|
| 167 |
/*based off of content_install */
|
| 168 |
'fieldname' => array('type' => 'varchar', 'length' => 32, 'not null' => FALSE, 'default' => NULL),
|
| 169 |
'fielditem' => array('type' => 'int', 'not null' => FALSE, 'default' => '0'),
|
| 170 |
/*call this 'annotator' instead? */
|
| 171 |
'annotator' => array('type' => 'varchar', 'length' => 127, 'not null' => FALSE, 'default' => NULL),
|
| 172 |
/* range1 is either start or x or similar,
|
| 173 |
range2 is end,y,etc
|
| 174 |
morehooks is a json/php object with with any more annotation info
|
| 175 |
*/
|
| 176 |
'range1' => array('type' => 'float', 'not null' => FALSE, 'default' => NULL),
|
| 177 |
'range2' => array('type' => 'float', 'not null' => FALSE, 'default' => NULL),
|
| 178 |
'morehooks' => array('type' => 'mediumtext', 'not null' => FALSE, 'default' => NULL),
|
| 179 |
);
|
| 180 |
return $columns;
|
| 181 |
|
| 182 |
case 'filters':
|
| 183 |
///array for views to filter based on a function here
|
| 184 |
return;
|
| 185 |
}
|
| 186 |
}
|
| 187 |
|
| 188 |
/**
|
| 189 |
* Implementation of hook_field_formatter_info().
|
| 190 |
* - options available for the nodereference field display in
|
| 191 |
* a View
|
| 192 |
*/
|
| 193 |
function annotationfield_field_formatter_info() {
|
| 194 |
return array(
|
| 195 |
'default' => array(
|
| 196 |
'label' => 'Annotated Field',
|
| 197 |
'field types' => array('annotation_field'),
|
| 198 |
),
|
| 199 |
'fragment' => array(
|
| 200 |
'label' => 'Annotated Fragment',
|
| 201 |
'field types' => array('annotation_field'),
|
| 202 |
),
|
| 203 |
'full' => array(
|
| 204 |
'label' => 'Annotated Node (all fields)',
|
| 205 |
'field types' => array('annotation_field'),
|
| 206 |
),
|
| 207 |
'noderef_link' => array(
|
| 208 |
'label' => 'Link to annotated node',
|
| 209 |
'field types' => array('annotation_field', 'nodereference'),
|
| 210 |
),
|
| 211 |
'noderef_nolink' => array(
|
| 212 |
'label' => "Annotated node's title (no link)",
|
| 213 |
'field types' => array('annotation_field', 'nodereference'),
|
| 214 |
),
|
| 215 |
);
|
| 216 |
}
|
| 217 |
|
| 218 |
|
| 219 |
/**
|
| 220 |
* Implementation of hook_field_formatter().
|
| 221 |
*/
|
| 222 |
function annotationfield_field_formatter($field, $item, $formatter, $node) {
|
| 223 |
/* returns text to display when this field is included in a view
|
| 224 |
@param formatter: the key chosen in field_formatter_info
|
| 225 |
|
| 226 |
*/
|
| 227 |
$annotatee_node = node_load($item['nid']);
|
| 228 |
|
| 229 |
///must precede annotator JS
|
| 230 |
drupal_add_js(drupal_get_path('module','annotationfield').'/js/annotator.js');
|
| 231 |
$data = annotationfield_invoke_data('view', $annotatee_node, $field, $item);
|
| 232 |
if ($item['annotator']) {
|
| 233 |
//just allows drupal_add_js(), etc.
|
| 234 |
$discard = module_invoke($item['annotator'], 'annotationfield_view', $item, $annotatee_node);
|
| 235 |
}
|
| 236 |
///if we haven't specified the fieldname, then we can't just show the field!
|
| 237 |
if (!$item['fieldname'] && $formatter==='default') {
|
| 238 |
$formatter = 'noderef_link';
|
| 239 |
}
|
| 240 |
$html_class_selector = annotationfield_onpage_counter();
|
| 241 |
$annotatee_class = "annotationfield-annotatee-".$html_class_selector;
|
| 242 |
|
| 243 |
switch($formatter) {
|
| 244 |
case 'default':
|
| 245 |
case 'fragment':/*TODO:NOT IMPLEMENTED*/
|
| 246 |
$annotatee_field = annotationfield_annotatee_field_content($annotatee_node, $item['fieldname'], $item['fielditem']);
|
| 247 |
theme('annotationfield_annotatee_js',$html_class_selector, 'view', $annotatee_class, $data, null, $item['annotator']);
|
| 248 |
$rv = theme('annotationfield_annotatee',$annotatee_class, $annotatee_field);
|
| 249 |
break;
|
| 250 |
case 'full':
|
| 251 |
///cck findable with .field-field-{fieldname}
|
| 252 |
$annotatee_node = node_view($annotatee_node, FALSE, TRUE, FALSE);
|
| 253 |
theme('annotationfield_annotatee_js',$html_class_selector, 'view', $annotatee_class, $data, null, $item['annotator']);
|
| 254 |
$rv = theme('annotationfield_annotatee',$annotatee_class, $annotatee_node);
|
| 255 |
break;
|
| 256 |
case 'noderef_link':
|
| 257 |
$rv = l($annotatee_node->title,'node/'.$annotatee_node->nid);
|
| 258 |
break;
|
| 259 |
}
|
| 260 |
|
| 261 |
return $rv;
|
| 262 |
///1. include the noderef page
|
| 263 |
///2. send JS hook to annotator for decoration
|
| 264 |
/// ALT 1: send this to an annotator php method?
|
| 265 |
/// This would mean we have to do 'display' twice, however
|
| 266 |
/// ALT 2: defer to theme() method.
|
| 267 |
}
|
| 268 |
|
| 269 |
/**
|
| 270 |
* Implementation of hook_widget_info().
|
| 271 |
*/
|
| 272 |
function annotationfield_widget_info() {
|
| 273 |
/*
|
| 274 |
Options available when configuring the field in content-types
|
| 275 |
*/
|
| 276 |
return array(
|
| 277 |
'annotation_field' => array(
|
| 278 |
'label' => 'Annotation on a Node Field',
|
| 279 |
'field types' => array('annotation_field'),
|
| 280 |
),
|
| 281 |
);
|
| 282 |
}
|
| 283 |
|
| 284 |
/**
|
| 285 |
* Implementation of hook_widget().
|
| 286 |
*/
|
| 287 |
function annotationfield_widget($op, &$node, $field, &$items) {
|
| 288 |
switch ($op) {
|
| 289 |
case 'prepare form values':
|
| 290 |
/* What presumptions/help should we have here
|
| 291 |
* - if there's a specific node, then prepare that
|
| 292 |
* also--only include annotators that can help with that node
|
| 293 |
* ^^^^--this might be presumptuous, but we can weaken this
|
| 294 |
* assumption later
|
| 295 |
* - if there's only one annotator/field possible
|
| 296 |
* then *default* to that. (later someone could manually
|
| 297 |
* switch with the UI
|
| 298 |
* - only include annotators that can help with a
|
| 299 |
*/
|
| 300 |
//$items_transposed = content_transpose_array_rows_cols($items);
|
| 301 |
|
| 302 |
///nodereference combines the 'nids' into one field
|
| 303 |
///as a multiselect. We would never do it that way
|
| 304 |
///even if we did have multiple values, because
|
| 305 |
/// other data is per-node.
|
| 306 |
//$items['default nids'] = $items_transposed['nid'];
|
| 307 |
if ($dest_node = $node->annotationhook['node']) {
|
| 308 |
///It can get easy to get bogged down in the possibilities
|
| 309 |
///for multi-field issues. Probably should ignore
|
| 310 |
///for now, but consider an 'annotation' type that
|
| 311 |
///compares to nodes side by side. then how would this work?
|
| 312 |
///We could defer to client-side setting/fixing
|
| 313 |
/// but then, we have to at least avoid clobbering
|
| 314 |
/// old values here.
|
| 315 |
$new_item = array();
|
| 316 |
$new_item['nid'] = $dest_node->nid;
|
| 317 |
$new_item['vid'] = $dest_node->vid;
|
| 318 |
//fieldname, type, (range1, range2, morehooks)
|
| 319 |
$node->annotationhook['annotatables'] = $annotatables = annotationfield_annotatable_fields($field, $dest_node->type, $dest_node);
|
| 320 |
switch(count($annotatables['annotators'])) {
|
| 321 |
case 1:
|
| 322 |
$new_item['annotator'] = current($annotatables['annotators']);
|
| 323 |
$new_item['fieldname'] = current($annotatables['fields']);
|
| 324 |
///TODO:what about more than one annotatable field rather than one annotator
|
| 325 |
break;
|
| 326 |
///TODO:case 0
|
| 327 |
///TODO:case many
|
| 328 |
}
|
| 329 |
//$new_item['annotator'] = 'videoannotation'; ///GENERALIZE
|
| 330 |
//$new_item['fieldname'] = "field_video";//'mediaref'; ///GENERALIZE
|
| 331 |
$items[] = $new_item;
|
| 332 |
|
| 333 |
}
|
| 334 |
break;
|
| 335 |
|
| 336 |
case 'form':
|
| 337 |
$form = array();
|
| 338 |
$form[$field['field_name']]['#tree'] = TRUE;
|
| 339 |
|
| 340 |
/*
|
| 341 |
* TODO: array_merge, and call fields based on field type
|
| 342 |
*/
|
| 343 |
//var_dump($items);
|
| 344 |
$mode = ($node->nid) ? 'edit':'create';
|
| 345 |
drupal_add_js(drupal_get_path('module','annotationfield').'/js/annotator.js');
|
| 346 |
|
| 347 |
///add form keys for fields included in form to fill out
|
| 348 |
///this will probably have some real meat for annotations
|
| 349 |
//var_dump($node->annotationhook['node'],$node);
|
| 350 |
foreach ($items as $i => $item) {
|
| 351 |
if (!is_int($i)) {
|
| 352 |
continue;
|
| 353 |
}
|
| 354 |
|
| 355 |
$html_class_selector = annotationfield_onpage_counter();
|
| 356 |
|
| 357 |
$form[$field['field_name']][$i] = array(
|
| 358 |
'#type' => 'fieldset',
|
| 359 |
'#attributes' => array('class' => 'annotationfield annotationfield-'.$html_class_selector),
|
| 360 |
'#tree' => TRUE
|
| 361 |
);
|
| 362 |
|
| 363 |
if (is_numeric($item['nid'])) {
|
| 364 |
$annotatee_node = node_load($item['nid']);//, $rid, TRUE);
|
| 365 |
$annotatables = ($node->annotationhook['annotatables']) ? $node->annotationhook['annotatables'] : annotationfield_annotatable_fields($field, $annotatee_node->type, $annotatee_node);
|
| 366 |
|
| 367 |
|
| 368 |
if (!$item['vid']) {
|
| 369 |
$item['vid'] = $annotatee_node->vid;
|
| 370 |
}
|
| 371 |
//signal for annotationfield_nodeapi(); still necessary?
|
| 372 |
$annotatee_node->annotatee = array();
|
| 373 |
|
| 374 |
$form[$field['field_name']][$i]['annotatee'] = array(
|
| 375 |
'#type'=> 'markup',
|
| 376 |
'#prefix' => '<div class="annotatee-'.$html_class_selector.'">',
|
| 377 |
'#suffix' => '</div>',
|
| 378 |
'#value' => annotationfield_annotatee_annotatables($annotatee_node, $annotatables['fields']),
|
| 379 |
|
| 380 |
//'#attributes' => array('class' => 'annotatee-'.$html_class_selector ),//doesn't seem to work
|
| 381 |
|
| 382 |
);
|
| 383 |
//should theme() be here or elsewhere? should we pass the node content?
|
| 384 |
//theme fieldtype+annotator+'_widget'
|
| 385 |
$data = null; //mode=create means no data
|
| 386 |
if ($mode === 'edit') {
|
| 387 |
$data = annotationfield_invoke_data($mode, $annotatee_node, $field, $item);
|
| 388 |
}
|
| 389 |
theme('annotationfield_annotatee_js',$html_class_selector, $mode, 'annotatee-'.$html_class_selector, $data);
|
| 390 |
} /** NOTE EITHER-OR ^-v **/
|
| 391 |
else {
|
| 392 |
///no annotatee
|
| 393 |
theme('annotationfield_annotatee_js',$html_class_selector, $mode);
|
| 394 |
}
|
| 395 |
|
| 396 |
$form[$field['field_name']][$i]['annotator_fields'] = array('#prefix' => '<div class="annotationfield-available-annotators">',
|
| 397 |
'#suffix' => '</div>');
|
| 398 |
|
| 399 |
foreach( $annotatables['annotators'] as $ator) {
|
| 400 |
///note:this hierarchy is currently a contract with the $ator in hook_annotationfield_form_submit
|
| 401 |
/// if we just gave them their fields, then we wouldn't have to do this
|
| 402 |
$form[$field['field_name']][$i]['annotator_fields'][$ator] = module_invoke($ator, 'annotationfield_form', $item);
|
| 403 |
}
|
| 404 |
|
| 405 |
if (!$item['annotator'] && count($annotatables['annotators']) == 1) {
|
| 406 |
$item['annotator'] = $annotatables['annotators'][0];
|
| 407 |
}
|
| 408 |
|
| 409 |
$form[$field['field_name']][$i]['nid'] = array(
|
| 410 |
'#type' => 'hidden',
|
| 411 |
'#default_value' => $item['nid'],
|
| 412 |
'#attributes' => array('class' => 'annotationfield-nid'),
|
| 413 |
);
|
| 414 |
//TODO:eventually, we'll have to figure out how to communicate a new version
|
| 415 |
//and what to do.
|
| 416 |
$form[$field['field_name']][$i]['vid'] = array(
|
| 417 |
'#type' => 'hidden',
|
| 418 |
'#default_value' => $item['vid'],
|
| 419 |
'#attributes' => array('class' => 'annotationfield-vid'),
|
| 420 |
);
|
| 421 |
$form[$field['field_name']][$i]['fieldname'] = array(
|
| 422 |
'#type' => 'hidden',
|
| 423 |
'#default_value' => $item['fieldname'],
|
| 424 |
'#attributes' => array('class' => 'annotationfield-fieldname'),
|
| 425 |
);
|
| 426 |
//for multiple, if any
|
| 427 |
$form[$field['field_name']][$i]['fielditem'] = array(
|
| 428 |
'#type' => 'hidden',
|
| 429 |
'#default_value' => $item['fielditem'],
|
| 430 |
'#attributes' => array('class' => 'annotationfield-fielditem'),
|
| 431 |
);
|
| 432 |
$form[$field['field_name']][$i]['annotator'] = array(
|
| 433 |
'#type' => 'hidden',
|
| 434 |
'#default_value' => $item['annotator'],
|
| 435 |
'#attributes' => array('class' => 'annotationfield-annotator'),
|
| 436 |
);
|
| 437 |
}
|
| 438 |
//if no items set...weirdly expressed, because there may be non-numerical item values
|
| 439 |
if (!isset($items[0])
|
| 440 |
///BUGFIX:is_array($node) indicates default_value widget for field
|
| 441 |
///there should never be a default value for an annotation field (right now, anyway)
|
| 442 |
&& !is_array($node)
|
| 443 |
) {
|
| 444 |
$options = _nodereference_potential_references($field, TRUE);
|
| 445 |
|
| 446 |
foreach ($options as $key => $value) {
|
| 447 |
$options[$key] = _nodereference_item($field, $value);
|
| 448 |
}
|
| 449 |
|
| 450 |
if (!$field['required']) {
|
| 451 |
//$options = array(0 => t('<none>')) + $options;
|
| 452 |
}
|
| 453 |
$form[$field['field_name']]['nids'] = array(
|
| 454 |
'#type' => 'select',
|
| 455 |
'#title' => t($field['widget']['label']),
|
| 456 |
'#default_value' => $items['default nid'],
|
| 457 |
'#size' => 0,
|
| 458 |
'#options' => $options,
|
| 459 |
'#required' => $field['required'],
|
| 460 |
'#description' => t($field['widget']['description']),
|
| 461 |
);
|
| 462 |
}
|
| 463 |
return $form;
|
| 464 |
case 'validate':
|
| 465 |
//foreach $items...
|
| 466 |
//TODO:make sure nid exists
|
| 467 |
//TODO:make sure each item annotator is a proper annotator
|
| 468 |
break;
|
| 469 |
case 'process form values':
|
| 470 |
/// for each item send it to the right annotator
|
| 471 |
foreach ($items as $i => $item) {
|
| 472 |
if ($item['annotator']) {
|
| 473 |
$ator_vals = module_invoke($item['annotator'],'annotationfield_form_submit',
|
| 474 |
$op, $node, $field, $item);//notice singular
|
| 475 |
//var_dump($ator_vals);
|
| 476 |
if (isset($ator_vals['morehooks'])) {
|
| 477 |
$ator_vals['morehooks'] = serialize($ator_vals['morehooks']);
|
| 478 |
}
|
| 479 |
$items[$i] = array_merge($item,$ator_vals);
|
| 480 |
/*OLD WAY
|
| 481 |
$annotator = $item['annotator'] . '_annotationfield_form_submit';
|
| 482 |
if (function_exists($annotator)) {
|
| 483 |
$items[$i] = array_merge($item,$annotator($op, $node, $field, $item));//notice singular
|
| 484 |
}*/
|
| 485 |
}
|
| 486 |
else {
|
| 487 |
///TODO:no annotator assigned. do we clean up here? maybe nothing is necessary
|
| 488 |
}
|
| 489 |
}
|
| 490 |
|
| 491 |
///? for multiple option
|
| 492 |
if (!empty($items['nids'])) {
|
| 493 |
$new_item['nid'] = $items['nids'];
|
| 494 |
//$new_item['vid'] = $items['vid'];
|
| 495 |
$items[] = $new_item;
|
| 496 |
unset($items['nids']);
|
| 497 |
}
|
| 498 |
break;
|
| 499 |
}
|
| 500 |
}
|
| 501 |
|
| 502 |
|
| 503 |
|
| 504 |
/**
|
| 505 |
* Implementation of hook_nodeapi().
|
| 506 |
*/
|
| 507 |
function annotationfield_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
|
| 508 |
/*
|
| 509 |
This hook, though promising is pretty useless. Since CCK decorates the node content
|
| 510 |
too late (during op==alter), we can't really update it any further. Also, many types
|
| 511 |
(like the video module), configure hook_view() so even the body field is lost.
|
| 512 |
Therefore, perhaps annotation on nodes should be restricted to hooks with a View(module)
|
| 513 |
rather than the node_view (pretty annoying). OR we let the annotator do what it can for particular
|
| 514 |
views. we could in theory set a hook here, which the annotator module could simply see, and then
|
| 515 |
only perform its logic when we're actually annotating the view.
|
| 516 |
*/
|
| 517 |
static $annotatees = array();
|
| 518 |
static $info = false;
|
| 519 |
//var_dump('OP',$op,$node);
|
| 520 |
|
| 521 |
/* note: eventually, we'll want to add $op == 'edit' case too, for self-annotating nodes (e.g. footnotes)
|
| 522 |
*/
|
| 523 |
if (isset($node->annotatee) && $op == 'view') {
|
| 524 |
if (!$info) { $info = _content_type_info(); }
|
| 525 |
|
| 526 |
//var_dump(annotationfield_annotator_content_types(), $info["fields"]);
|
| 527 |
//var_dump(content_fields(NULL, 'ann'));
|
| 528 |
//var_dump($node->type,$info['content types']['media']['fields']/*$node,content_fields(NULL, $node->type)*/);
|
| 529 |
/* For CCK
|
| 530 |
$info['content types'][$node->type]['fields'][$field_name]['type'] == 'video_cck'
|
| 531 |
*/
|
| 532 |
//var_dump($node->content);
|
| 533 |
// $node->content['']['#value'] = '<div class="hi">hi'. $node->content['body']['#value'] .'hi</div>';
|
| 534 |
|
| 535 |
//$node->content['body']['#value'] = '<div class="hi">altered body: '. $node->content['body']['#value'] .' END</div>';
|
| 536 |
//var_dump($node);
|
| 537 |
//If annotators/annotatees are delegated for this specific type/content:
|
| 538 |
|
| 539 |
if (true) {
|
| 540 |
//GENERALIZE
|
| 541 |
//drupal_add_js("\$(document).ready(function(){\$('//.field-field-mediaref').append('<button>blah</button>')});",'inline');
|
| 542 |
//var_dump($info['content types'][$node->type]['fields']);
|
| 543 |
foreach ($info['content types'][$node->type]['fields']
|
| 544 |
as $field_name => $f_info) {
|
| 545 |
//$field_name = 'field_mediaref';
|
| 546 |
$annotatees[$field_name] = $info['content types'][$node->type]['fields'][$field_name]['type'];
|
| 547 |
drupal_add_js('AnnotationController.annotatees["'. $node->nid .'"] = ' . drupal_to_js($annotatees),'inline');
|
| 548 |
}
|
| 549 |
}
|
| 550 |
|
| 551 |
}
|
| 552 |
}
|
| 553 |
|
| 554 |
function annotationfield_invoke_data($mode, &$annotatee_node, $field, &$item) {
|
| 555 |
if ($item['annotator']) {
|
| 556 |
$item['morehooks'] = unserialize($item['morehooks']);
|
| 557 |
return module_invoke($item['annotator'], 'annotationfield_data', $mode, $annotatee_node, $field, $item);
|
| 558 |
}
|
| 559 |
return NULL;
|
| 560 |
}
|
| 561 |
|
| 562 |
///@return node content for the fields in @param fieldname_array
|
| 563 |
function annotationfield_annotatee_annotatables($node, $fieldname_array) {
|
| 564 |
$rv = '';
|
| 565 |
foreach($fieldname_array as $field) {
|
| 566 |
$rv .= annotationfield_annotatee_field_content($node, $field);
|
| 567 |
}
|
| 568 |
return $rv;
|
| 569 |
}
|
| 570 |
|
| 571 |
///@return the content of a node for a particular field--shouldn't this be easy!
|
| 572 |
function annotationfield_annotatee_field_content($node, $fieldname, $fielditem=NULL) {
|
| 573 |
$annotatee_field = content_fields($fieldname, $node->type);
|
| 574 |
//$annotatee_field = content_fields('field_video', $annotatee_node->type);///GENERALIZE-done
|
| 575 |
///Using CCK
|
| 576 |
if ($annotatee_field) {
|
| 577 |
$annotatee_field_value = $node->$fieldname;
|
| 578 |
$delta = (is_null($fielditem))?0:$fielditem;
|
| 579 |
$items[0]['view'] = content_format($annotatee_field, $annotatee_field_value[$delta], 'default', $node);
|
| 580 |
$annotatee_output = theme('annotatee_field', $node, $annotatee_field, $items, 0, 1);
|
| 581 |
//return content_format(content_fields('field_video', $annotatee_node->type), $annotatee_node->field_video[$delta], 'default', $annotatee_node);
|
| 582 |
return $annotatee_output;
|
| 583 |
}
|
| 584 |
///No CCK
|
| 585 |
else {///stolen from node_view() code
|
| 586 |
//OLD:node_view($annotatee_node, FALSE, TRUE, FALSE),
|
| 587 |
//TODO:need to set page==true for video module, e.g. to completely trigger
|
| 588 |
node_build_content($node, FALSE, FALSE);
|
| 589 |
node_invoke_nodeapi($node, 'alter', FALSE, FALSE);
|
| 590 |
$items[0]['view'] = $node->content[$fieldname]['#value'];
|
| 591 |
$fake_field['field_name'] = $fieldname;
|
| 592 |
$fake_field['widget']['label'] = $fieldname;
|
| 593 |
$annotatee_output = theme('annotatee_field', $node, $fake_field, $items, 0, 1);
|
| 594 |
return $annotatee_output;
|
| 595 |
//return $annotatee_node->content['body']['#value'];
|
| 596 |
}
|
| 597 |
}
|
| 598 |
|
| 599 |
function theme_annotatee_field(&$node, &$field, &$items, $teaser, $page) {
|
| 600 |
///watch out! non-CCK fields flow here through a hack, too
|
| 601 |
///our contract assumes $field['fieldname'], $field['widget']['label'] and $items[i]['view'] only
|
| 602 |
return theme('field', $node, $field, $items, 0, 1);
|
| 603 |
}
|
| 604 |
|
| 605 |
/*
|
| 606 |
* @return array('annotators'=>[list of ators for node],'fields'=>[list of annable field names])
|
| 607 |
* @param $node, provided when deciding what to do for a particular node
|
| 608 |
*/
|
| 609 |
function annotationfield_annotatable_fields($field, $nodetype, $node=NULL) {
|
| 610 |
/*TODO: This needs some love. we shouldn't make sub modules do all the work
|
| 611 |
instead we should cache results of module_invoke_all('annotationfield_provides'
|
| 612 |
then
|
| 613 |
*/
|
| 614 |
$cck_info = content_types($nodetype);
|
| 615 |
$ftype_names = array();
|
| 616 |
$rv = array('fields'=>array(),'annotators'=>array());
|
| 617 |
if (is_array($cck_info['fields'])) {
|
| 618 |
foreach($cck_info['fields'] as $fname => $f) {
|
| 619 |
$ftype_names[$f['type']][] = $fname;
|
| 620 |
}
|
| 621 |
}
|
| 622 |
$annotator_opinion = array();
|
| 623 |
|
| 624 |
///it won't be set if the field isn't configured yet
|
| 625 |
$annotator_types = (isset($field['annotator_types'])) ?
|
| 626 |
$field['annotator_types'] : array_keys(module_invoke_all('annotationfield_annotator'));
|
| 627 |
|
| 628 |
foreach($annotator_types as $ator) {//instead of module_invoke_all, just the enabled ones
|
| 629 |
if ($ator) {///$ator===0 when it's not selected
|
| 630 |
$annotator_opinion = array_merge($annotator_opinion, module_invoke($ator, 'annotationfield_annotatable_fields', $nodetype, $ftype_names, $node));
|
| 631 |
}
|
| 632 |
}
|
| 633 |
$field_annotator_list = array();
|
| 634 |
|
| 635 |
|
| 636 |
if ($node && $field["annotatee_fields"][$nodetype]) {
|
| 637 |
///filter out the fields that admin has UNchecked
|
| 638 |
$filterfields = array_filter($field["annotatee_fields"][$nodetype]);
|
| 639 |
}
|
| 640 |
|
| 641 |
foreach ($annotator_opinion as $ator_name => $afields) {
|
| 642 |
///intersect with filter list
|
| 643 |
if (is_array($filterfields)) {
|
| 644 |
$afields = array_intersect($afields, $filterfields);
|
| 645 |
}
|
| 646 |
if (count($afields)) {
|
| 647 |
$rv['annotators'][] = $ator_name;
|
| 648 |
$rv['fields'] = array_merge($rv['fields'], $afields);
|
| 649 |
}
|
| 650 |
}
|
| 651 |
return $rv;
|
| 652 |
}
|
| 653 |
|
| 654 |
function annotationfield_formfields($type) {
|
| 655 |
// Create a dummy node and form and call hook_form_alter()
|
| 656 |
// to produce an array of fields and weights added to the node by all modules.
|
| 657 |
$dummy_node = new stdClass();
|
| 658 |
$dummy_node->type = $type;
|
| 659 |
|
| 660 |
// Some modules (userreview...) "hide" their node forms, resulting in no field
|
| 661 |
// being listed. We set a special flag to inform them this form is special.
|
| 662 |
$dummy_node->cck_dummy_node_form = TRUE;
|
| 663 |
|
| 664 |
$dummy_form_id = $type .'_node_form';
|
| 665 |
$dummy_form = node_form($dummy_node);
|
| 666 |
foreach (module_implements('form_alter') as $module) {
|
| 667 |
$function = $module .'_form_alter';
|
| 668 |
$function($dummy_form_id, $dummy_form);
|
| 669 |
}
|
| 670 |
return $dummy_form;
|
| 671 |
}
|
| 672 |
|
| 673 |
/*
|
| 674 |
* @return Array of content types that can annotate this node
|
| 675 |
*/
|
| 676 |
function annotationfield_annotatable($node) {
|
| 677 |
$ator_ctypes = annotationfield_annotator_content_types();
|
| 678 |
$rv = array();
|
| 679 |
foreach($ator_ctypes as $cname => $annfields) {
|
| 680 |
$first_field = current($annfields);
|
| 681 |
if ($first_field["referenceable_types"][$node->type]) {
|
| 682 |
$rv[$cname] = content_types($cname);
|
| 683 |
}
|
| 684 |
}
|
| 685 |
return $rv;
|
| 686 |
}
|
| 687 |
|
| 688 |
/// @return Array of content types that have annotation fields
|
| 689 |
function annotationfield_annotator_content_types() {
|
| 690 |
static $content_types = false;
|
| 691 |
if ($content_types) {
|
| 692 |
return $content_types;
|
| 693 |
}
|
| 694 |
$content_types = array();
|
| 695 |
|
| 696 |
$ann_fields = annotationfield_annotator_fields();
|
| 697 |
foreach (content_types() as $cname => $ctype) {
|
| 698 |
$c_ann_fields = array_intersect_key($ann_fields, $ctype['fields']);
|
| 699 |
if (count($c_ann_fields)) {
|
| 700 |
$content_types[$cname] = $c_ann_fields;
|
| 701 |
}
|
| 702 |
}
|
| 703 |
return $content_types;
|
| 704 |
}
|
| 705 |
/// @return all annotationfields in the system
|
| 706 |
function annotationfield_annotator_fields() {
|
| 707 |
static $ann_fields = false;
|
| 708 |
if ($ann_fields) {
|
| 709 |
return $ann_fields;
|
| 710 |
}
|
| 711 |
|
| 712 |
$ann_fields = array();
|
| 713 |
foreach ( content_fields() as $fname => $field) {
|
| 714 |
if ($field['type'] == 'annotation_field') {
|
| 715 |
$ann_fields[$fname] = $field;
|
| 716 |
}
|
| 717 |
}
|
| 718 |
return $ann_fields;
|
| 719 |
}
|
| 720 |
|
| 721 |
function annotationfield_onpage_counter() {
|
| 722 |
static $html_class_selector = 0;
|
| 723 |
return ++$html_class_selector;
|
| 724 |
}
|
| 725 |
|
| 726 |
function theme_annotationfield_annotatee($annotatee_class, $annotatee_content) {
|
| 727 |
return '<div class="annotationfield-annotatee-annotated '. check_plain($annotatee_class).'">'. $annotatee_content .'</div>';
|
| 728 |
}
|
| 729 |
|
| 730 |
function theme_annotationfield_annotatee_js($onpage_id, $mode, $annotatee_selector=NULL, $data=NULL, $options=NULL, $ator=NULL) {
|
| 731 |
$js = sprintf('jQuery(function($){AnnotationController.initField("%s", %d', $mode, $onpage_id);
|
| 732 |
if ($annotatee_selector) {
|
| 733 |
$js .= ',".'. $annotatee_selector .'"';
|
| 734 |
if ($data) {
|
| 735 |
$js .= ',' . drupal_to_js($data);
|
| 736 |
$js .= ',' . drupal_to_js($options);
|
| 737 |
if ($ator) {
|
| 738 |
$js .= ',' . drupal_to_js($ator);
|
| 739 |
}
|
| 740 |
}
|
| 741 |
}
|
| 742 |
$js .= ');});';
|
| 743 |
drupal_add_js($js,'inline');
|
| 744 |
}
|
| 745 |
|
| 746 |
function annotationfield_node_annotate() {
|
| 747 |
/* So far this function only knows that it's annotating a particular
|
| 748 |
node with a certain type. We don't know what the annotation
|
| 749 |
field is here.
|
| 750 |
*/
|
| 751 |
|
| 752 |
global $user;
|
| 753 |
$nid = arg(1);
|
| 754 |
//$rid = arg(3);
|
| 755 |
$annotation_type = arg(3);
|
| 756 |
|
| 757 |
$field_annotatee = arg(4); //should be UNREQUIRED
|
| 758 |
$output = '';
|
| 759 |
|
| 760 |
if (is_numeric($nid)) {
|
| 761 |
$node = node_load($nid);//, $rid, TRUE);
|
| 762 |
$node->annotatee = array(); //signal for annotationfield_nodeapi()
|
| 763 |
}
|
| 764 |
$annotator_node = array('uid' => $user->uid,
|
| 765 |
'name' => $user->name,
|
| 766 |
'type' => $annotation_type,
|
| 767 |
'annotationhook' => array('node' => $node,
|
| 768 |
),
|
| 769 |
);
|
| 770 |
$output .= drupal_get_form($annotation_type .'_node_form', $annotator_node);
|
| 771 |
|
| 772 |
return $output;
|
| 773 |
}
|