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

Contents of /contributions/modules/nodereference_asmselect/nodereference_asmselect.module

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


Revision 1.3 - (show annotations) (download) (as text)
Tue Oct 28 13:16:37 2008 UTC (12 months, 3 weeks ago) by electricmonk
Branch: MAIN
CVS Tags: DRUPAL-5--1-0-RC3, DRUPAL-5--1-0-RC2, HEAD
Changes since 1.2: +321 -7 lines
File MIME type: text/x-php
Added two features:
1) Creation of new nodes from the parent node form (opens in a new window). Solution is currently a bit lame and only suitable for backoffice usage.

2) Filtering of the referenenceable nodes by vocabularies
1 <?php
2 // $Id$
3
4 /**
5 * @file
6 * Integrates the ASM Select JQuery plugin as an additional NodeReference widget.
7 *
8 */
9 define('ASM_SELECT_WIDGET_ID', 'asm_select_widget_id');
10 define('ASM_SELECT_NODE_TYPE', 'asm_select_node_type');
11 define('ASM_SELECT_FILTER_PATH', 'nodereference_asmselect_options');
12 define('ASM_SELECT_MODULE_PATH', drupal_get_path('module', 'nodereference_asmselect'));
13
14 /**
15 * Implementation of hook_menu().
16 */
17 function nodereference_asmselect_menu($may_cache)
18 {
19 $items = array();
20
21 if ($may_cache)
22 {
23 $items[] = array(
24 'path' => ASM_SELECT_FILTER_PATH,
25 'callback' => 'nodereference_asmselect_options',
26 'type' => MENU_CALLBACK,
27 'access' => user_access('access content')
28 );
29 }
30
31 return $items;
32 }
33
34 function nodereference_asmselect_options()
35 {
36 $filters = explode(',', check_plain($_POST['filters']));
37
38 $filters_array = array();
39
40 foreach ($filters as $key => $filter)
41 {
42 $properties = explode('=', $filter);
43 $filters_array[$key]['type'] = $properties[0];
44 $filters_array[$key]['value'] = $properties[1];
45 }
46
47 $field_name = $json_array['field_name'];
48
49 $field = content_fields(check_plain($_POST['field_name']));
50
51 $nodes = _nodereference_asmselect_get_filtered_options($field, $filters_array);
52
53 $output = '';
54
55 foreach ($nodes as $value => $title)
56 {
57 $output .= '<option value="' . $value . '">' . $title . '</option>';
58 }
59
60 echo $output;
61 }
62
63 function _nodereference_asmselect_add_filters_js($id, $field_name)
64 {
65 drupal_add_js(ASM_SELECT_MODULE_PATH . '/nodereference_asmselect.js');
66 $js = "$(document).ready(function()
67 {
68 NodeReferenceAsmSelect.settings.filterPath = '" . base_path() . ASM_SELECT_FILTER_PATH . "';
69
70 NodeReferenceAsmSelect.fn.termIdFiltersChange('$id', '$field_name');
71
72 // simulate change so that the initial filter settings would be taken into account
73 $('#$id-filters select.asmselect-filter:first').change();
74
75 });";
76
77 drupal_add_js($js, 'inline');
78 }
79
80 /**
81 * Implementation of hook_widget_info().
82 */
83 function nodereference_asmselect_widget_info() {
84 return array(
85 'nodereference_asm_select' => array(
86 'label' => t('ASM Select List'),
87 'field types' => array('nodereference'),
88 )
89 );
90 }
91
92 function _nodereference_asmselect_add_vocabulary_filters(&$node, $field)
93 {
94 $id = form_clean_id($field['field_name']);
95
96 $taxonomy_field = array(
97 '#type' => 'fieldset',
98 '#title' => t('Filters'),
99 '#collapsible' => TRUE,
100 '#attributes' => array('id' => $id . '-filters'),
101 '#collapsed' => FALSE
102 );
103
104 $vocabs = taxonomy_get_vocabularies($node->type);
105
106 $filters = array();
107
108 foreach ($vocabs as $vocab)
109 {
110 // should I add a filter for this vocabulary?
111 if (in_array($vocab->vid, $field['widget']['vocabulary_filters']))
112 {
113 $terms = taxonomy_node_get_terms_by_vocabulary($node->nid, $vocab->vid);
114 $filter = taxonomy_form($vocab->vid, $node->taxonomy);
115 $filter['#id'] = $id . '-filter-vocab-' . $vocab->vid;
116 $filter['#attributes']['class'] = 'asmselect-filter';
117 $filter['#default_value'] = $terms ? array_keys($terms) : null;
118
119 $filters[$vocab->name] = $filter;
120 }
121 }
122
123 _nodereference_asmselect_add_filters_js($id, $field['field_name']);
124
125 if (count($filters))
126 {
127 return array_merge($taxonomy_field, $filters);
128 }
129 }
130
131 /**
132 * @param $field
133 * @param $filters array of arrays ['type', 'value']
134 */
135 function _nodereference_asmselect_get_filtered_options($field, $filters)
136 {
137 if (module_exists('views') && isset($field['advanced_view'])) {
138 $view = null;
139
140 if ($field['advanced_view'] != '--') {
141 $view = views_get_view($field['advanced_view']);
142 }
143
144 // if view does not exist, create one on the fly
145 if (!$view)
146 {
147 $view = views_create_view('candidate_nodes', '');
148
149 foreach ($field['referenceable_types'] as $related_type) {
150 if ($related_type) {
151 views_view_add_filter($view, 'node', 'type', '=', $related_type, null);
152 }
153 }
154
155 }
156
157 // arguments for the view
158 $view_args = array();
159 if (isset($field['advanced_view_args'])) {
160 // TODO: Support Tokens using token.module ?
161 $view_args = array_map(trim, explode(',', $field['advanced_view_args']));
162 }
163
164 if (isset($filters) && is_array($filters) && count($filters) > 0) {
165 foreach ($filters as $filter)
166 {
167 switch ($filter['type'])
168 {
169 case 'tid';
170 views_view_add_filter($view, 'term_node', 'tid', '=', array($filter['value']), 20);
171 break;
172
173 //TODO: other types?
174 }
175 }
176 }
177
178 // we do need title field, so add it if not present (unlikely, but...)
179 $has_title = array_reduce($view->field, create_function('$a, $b', 'return ($b["field"] == "title") || $a;'), false);
180 if (!$has_title) {
181 views_view_add_field($view, 'node', 'title', '');
182 }
183 views_load_cache();
184 views_sanitize_view($view);
185
186 // make sure the fields get included in the query
187 $view->page = true;
188 $view->page_type = 'list';
189
190 // make sure the query is not cached
191 unset($view->query); // Views 1.5-
192 $view->is_cacheable = FALSE; // Views 1.6+
193
194 $view_result = views_build_view('result', $view, $view_args);
195 $result = $view_result['result'];
196 }
197
198 $rows = array();
199
200 while ($node = db_fetch_object($result)) {
201 if ($return_full_nodes) {
202 $rows[$node->nid] = $node;
203 }
204 else {
205 $rows[$node->nid] = $node->node_title;
206 }
207 }
208
209 return $rows;
210 }
211
212 /**
213 * Implementation of hook_widget().
214 */
215 function nodereference_asmselect_widget($op, &$node, $field, &$items) {
216 if ($field['widget']['type'] == 'nodereference_asm_select') {
217 // call node reference module, pass a dummy field so that a regular select field is returned
218 $nodereference_field = $field;
219 $nodereference_field['widget']['type'] = 'nodereference_select';
220
221 $ret = nodereference_widget($op, $node, $nodereference_field, $items);
222
223 if ($op == 'form' && !isset($node->cck_dummy_node_form)) {
224 // set new id
225 $id = form_clean_id('edit-' . $field['field_name'] . '-nids');
226
227 $ret[$field['field_name']]['nids']['#id'] = $id;
228
229 // remove the <none> option
230 $options = &$ret[$field['field_name']]['nids']['#options'];
231
232 if (array_key_exists(0, $options))
233 {
234 unset($options[0]);
235 }
236
237 $path = drupal_get_path('module', 'nodereference_asmselect');
238
239 drupal_add_js($path . '/asmselect/jquery.asmselect.js');
240 drupal_add_css($path . '/asmselect/jquery.asmselect.css');
241
242 if ($field['widget']['sortable']) {
243 // add list sorting
244 jquery_ui_add(array('ui.draggable', 'ui.droppable', 'ui.sortable'));
245 }
246
247 // add sortable multiple select widget
248 $js = "
249 $(document).ready(function() {
250 $('#" . $id . "').asmSelect({
251 addItemTarget: '" . (isset($field['widget']['add_item_target']) ? $field['widget']['add_item_target'] : 'bottom') . "',
252 sortable: " . (isset($field['widget']['sortable']) ? $field['widget']['sortable'] : 'false') . ",
253 animate: " . (isset($field['widget']['animate']) ? $field['widget']['animate'] : 'true') . ",
254 highlight: " . (isset($field['widget']['highlight']) ? $field['widget']['highlight'] : 'true') .",
255 removeLabel: '" . t('Remove') . "',
256 highlightAddedLabel: '" . t('Added ') . "',
257 highlightRemovedLabel: '" . t('Removed ') . "'
258 });
259 });";
260
261 drupal_add_js($js, 'inline');
262
263 // add vocabulary filters?
264 if ( isset($field['widget']['vocabulary_filters'])) {
265
266 $ret[$field['field_name']]['filters'] = _nodereference_asmselect_add_vocabulary_filters($node, $field);
267 }
268
269 // add the "new node" buttons
270 if ($field['widget']['create_new'])
271 {
272 // add "create new" button for each referencable type
273 $create_new_fieldset = array(
274 '#type' => 'fieldset',
275 '#collapsible' => TRUE,
276 '#collapsed' => FALSE
277 );
278
279 foreach ($field['referenceable_types'] as $type => $is_referenceable) {
280 if ($is_referenceable) {
281 $create_new_fieldset[$type] = array(
282 '#value' => theme('asm_select_create_new_button', $type, $id)
283 );
284 }
285 }
286
287 $ret[$field['field_name']]['create_new'] = $create_new_fieldset;
288 }
289 }
290 }
291
292 return $ret;
293 }
294
295 /**
296 * Implementation of hook_widget_settings()
297 */
298 function nodereference_asmselect_widget_settings($op, $widget) {
299 switch ($op) {
300 case 'form':
301 $form = array();
302
303 // add some settings for this field (these are all ASM Select settings)
304 $form['asm_settings'] = array (
305 '#type' => 'fieldset',
306 '#title' => t('ASM Select Settings')
307 );
308
309 $form['asm_settings']['add_item_target'] = array (
310 '#type' => 'select',
311 '#title' => t('Add Item Target'),
312 '#description' => t('Where to place new selected items that are added to the list.'),
313 '#options' => array(
314 'bottom' => t('Bottom'),
315 'top' => t('Top'),
316 ),
317 '#default_value' => isset($widget['add_item_target']) ? $widget['add_item_target'] : 'bottom',
318 '#required' => TRUE,
319 );
320
321 $form['asm_settings']['sortable'] = array (
322 '#type' => 'checkbox',
323 '#title' => 'Sortable',
324 '#description' => t('May the user drag and drop to sort the list of elements they have selected?'),
325 '#default_value' => isset($widget['sortable']) ? $widget['sortable'] : false
326 );
327
328 $form['asm_settings']['animate'] = array (
329 '#type' => 'checkbox',
330 '#title' => 'Animate',
331 '#description' => t('Animate the adding or removing of items from the list?'),
332 '#default_value' => isset($widget['animate']) ? $widget['animate'] : true
333 );
334
335 $form['asm_settings']['highlight'] = array (
336 '#type' => 'checkbox',
337 '#title' => 'Highlight',
338 '#description' => t('Show a quick highlight of what item was added or removed?'),
339 '#default_value' => isset($widget['highlight']) ? $widget['highlight'] : true
340 );
341
342 $form['asm_settings']['create_new'] = array (
343 '#type' => 'checkbox',
344 '#title' => 'Allow Create',
345 '#description' => t('Add a link to create a new node of the referenced type (will open in a new window)?'),
346 '#default_value' => isset($widget['create_new']) ? $widget['create_new'] : false
347 );
348
349 $vocabularies = taxonomy_get_vocabularies();
350
351 $options = array();
352
353 foreach ($vocabularies as $vocabulary)
354 {
355 $options[$vocabulary->vid] = $vocabulary->name;
356 }
357
358 $form['asm_settings']['vocabulary_filters'] = array (
359 '#type' => 'checkboxes',
360 '#title' => t('Vocabulary Filters'),
361 '#description' => t('Which vocabularies to use in order to filter this select?'),
362 '#options' => $options,
363 '#default_value' => isset($widget['vocabulary_filters']) ? $widget['vocabulary_filters'] : null
364 );
365
366 return $form;
367
368 case 'save':
369 return array('add_item_target', 'sortable', 'animate', 'highlight', 'create_new', 'vocabulary_filters');
370
371 case 'callbacks':
372 return array(
373 'default value' => CONTENT_CALLBACK_NONE,
374 );
375 }
376 }
377 /**
378 * Implementation of hook_form_alter()
379 */
380 function nodereference_asmselect_form_alter($form_id, &$form) {
381 // should I work on this form?
382 if (strpos($form_id, 'node_form') !== false && isset($_GET['asmSelect'])) {
383 $_SESSION[ASM_SELECT_WIDGET_ID] = $_GET['asmSelect'];
384 $form['#submit']['nodereference_asmselect_node_create_callback'] = array();
385 }
386 }
387
388 /**
389 * Called after a node form has been submitted to add JS that injects the node back
390 * into our select widget
391 */
392 function nodereference_asmselect_node_create_callback($form_id, $form_values) {
393 $_SESSION[ASM_SELECT_NODE_TYPE] = $form_values['type'];
394 }
395
396 /**
397 * Implementation of hook_nodeapi
398 */
399 function nodereference_asmselect_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
400 // check if this is the first node view after user has created it from an ASM select
401 // link
402 if ( $op == 'view'
403 && isset($_SESSION[ASM_SELECT_NODE_TYPE])
404 && $node->type == $_SESSION[ASM_SELECT_NODE_TYPE]) {
405
406 $select_id = $_SESSION[ASM_SELECT_WIDGET_ID];
407
408 $title = drupal_urlencode($node->title);
409
410 drupal_add_js("$(document).ready(function() {
411 var asmSelect = window.opener.$('#$select_id');
412 var select = asmSelect.get(0);
413
414 var opt = document.createElement('option');
415 opt.value = $node->nid;
416 opt.selected = 'selected';
417 opt.appendChild(document.createTextNode('$title'));
418
419 select.options[select.length] = opt;
420
421 asmSelect.change();
422
423 window.opener.focus();
424 window.close();
425 });", "inline");
426
427 // remove variable from session
428 session_unregister(ASM_SELECT_NODE_TYPE);
429 }
430 }
431
432 /**
433 * @param $type the node type to create a button for
434 */
435 function theme_asm_select_create_new_button($type, $selectId) {
436 $node_names = node_get_types('names');
437
438 $path = url("node/add/$type") . "&asmSelect=" . $selectId;
439
440 return '<a href="#" onclick="window.open(\'' . $path . '\'); return false">' . t('Create new !type', array('!type' => $node_names[$type])) . '</a>';
441 }

  ViewVC Help
Powered by ViewVC 1.1.2