inclure blog url in sms notification
[vozmob:vojo.git] / vojo.module
1 <?php
2
3 // Optimization: maintain a single-query-duration cache of published story counts per group
4 $_vojo_group_published_story_counts = array();
5
6 /**
7  * Load up helpers
8  */
9 function vojo_init(){
10   // load custom blocks
11   module_load_include('inc', 'vojo', 'includes/vojo.blocks');  
12   // load costom voip-drupal scripts
13   module_load_include('inc', 'vojo', 'vojo.script');
14   drupal_add_css(drupal_get_path('module', 'vojo') .'/css/vojo-site-header.css');
15 }
16
17 /**
18  * Implementation of hook_menu.
19  */
20 function vojo_menu() {
21   $items = array();
22   $items['nolink'] = array(
23     'page callback'    => 'vojo_nolink',
24     'access callback'  => TRUE,
25     'type'             => MENU_CALLBACK,
26   );
27   $items['story'] = array(
28      'title' => 'Create story',
29      'page callback' => 'vojo_create_story',
30      'access arguments' => array('access content'),
31    );
32    $items['story/%ctools_js/go'] = array(
33      'page callback' => 'vojo_create_story_ajax_callback',
34      'page arguments' => array(1),
35      'access arguments' => array('access content'),
36    );   
37   return $items;
38 }
39
40 function vojo_create_story() {
41   // Load the modal library and add the modal javascript.
42   ctools_include('modal');
43   ctools_modal_add_js();
44
45   $output = l('Load modal content', 'story/nojs/go', array(
46     'attributes' => array('class' => 'ctools-use-modal')));
47
48   return $output;
49 }
50
51 function vojo_create_story_ajax_callback($js = FALSE) {
52   global $user; 
53   
54   $links = array();
55   foreach ($user->og_groups as $group) {
56     $group_nid = $group['nid'];
57     $links[] = l($group['title'], 'node/add/blog', array('attributes' => array('title' => t('Add a new story in this group.')),'query' => "gids[]=$group_nid"));
58   }
59   
60   $title = t('Create story');
61   $intro = "<p>".t("Select the group you'd like to post your story to:")."</p>";
62   $output = $intro.theme('item_list', $links);
63   
64   if ($js) {
65     ctools_include('ajax');
66     ctools_include('modal');
67     ctools_modal_render($title, $output);
68     // above command will exit().
69   }
70   else {
71     drupal_set_title($title);
72     return $output;
73   }
74     
75 }
76
77 /**
78  * Implementation of hook_menu_alter.
79  */
80 function vojo_menu_alter(&$items) {
81   // Unset default blog module's blog path (we want to use views instead). 
82   unset($items['blog/%user_uid_optional']);
83   $items['blog/%user_uid_optional']['page callback'] = FALSE;
84 }
85
86 /**
87  * Page callback
88 */
89 function vojo_nolink() {
90   $output = 'This is a dummy page used for nolink menu items. You should not be able to see this page';
91   return t($output);
92 }
93
94 /**
95  * Implementation of hook_link_alter.
96  */
97 function vojo_link_alter(&$links, $node) {
98   // Change user's blog link to so it points to custom view (instead of core blog module's path)
99   if ($links['blog_usernames_blog']) {
100     $links['blog_usernames_blog']['href'] = 'blogs/'. $node->name;
101   }
102 }
103 /**
104  * Implementation of hook_profile_alter. 
105  */
106 function vojo_profile_alter(&$account) {
107   // Override path to user's blog, /blogs points to a view instead. 
108   $account->content['summary']['blog']['#value'] = l(t('View recent blog entries'), 'blogs/'. $account->name, array('attributes' => array('title' => 'Read latest blog entries.'))); 
109   // Unset groups since we want to add them in a separate block/pane. 
110   unset($account->content['summary']['groups']);
111 }
112
113 /**
114  * Helper function to find out how many stories are published within a group.  This is written
115  * to be called by themes and custom panels/blocks if they need to embed the count somewhere.  This
116  * manages a qingle-query-duration cache for you.
117  */
118 function vojo_group_published_story_count($group_id) {
119     global $_vojo_group_published_story_counts;
120     $story_count = 0;
121     if(array_key_exists($group_id,$_vojo_group_published_story_counts)){
122         $story_count = $_vojo_group_published_story_counts[$group_id];
123     } else {
124         $results = db_query("SELECT count(n.nid) as story_count 
125                          FROM {og_ancestry} as oga, {node} as n
126                          WHERE oga.nid=n.nid AND oga.group_nid=$group_id AND n.status=1", $group_id);
127         while ($object = db_fetch_object($results)) {
128             $story_count = $object->story_count;
129         }
130         $_vojo_group_published_story_counts[$group_id] = $story_count;
131     }
132     return $story_count;
133 }
134
135 /**
136  * Use this to get the path to an image inside the "img" dir of this module
137  */
138 function _vojo_image_path($imgfile){
139     return theme_image(drupal_get_path('module','vojo')."/img/".$imgfile);
140 }
141
142 /**
143  * Return the VoipVoice id (name) for a ISO standard language code.  All calls to change
144  * the language during a voice call should query this function to return the VoipVoice
145  * for a language.
146  */
147 function vojo_voice_id_for_language( $language=null ) {
148   $voice = 'vojo-en';  // default to english
149   switch($language) {
150     case 'en':
151     case 'en-us':
152       $voice = 'vojo-en';
153       break;
154     case 'es':
155       $voice = 'vojo-es';
156       break;
157     case 'pt':
158     case 'pt-br':
159       $voice = 'vojo-pt';
160       break;
161   }
162   return $voice;
163 }
164
165 /**
166  * Implementation of hook_voipscript_load_script().
167  */
168 function vojo_voipscript_load_script($script_name, $params = NULL) {
169     if ($script_name == 'vojo_group') {
170         module_load_include('inc', 'vojo', 'vojo.script');
171         $node = node_load($params['nid']);
172         return _vojo_group_script($node);
173     }
174     elseif ($script_name == 'vojo_simple_group') {
175         module_load_include('inc', 'vojo', 'vojo.script');
176         $node = node_load($params['nid']);
177         return _vojo_simple_group_script($node);
178     }
179     elseif ($script_name == 'vojo_record_blog') {
180         module_load_include('inc', 'vojo', 'vojo.script');
181         $node = node_load($params['nid']);
182         return _vojo_record_blog_script($node, $params['simple']);
183     }
184     elseif ($script_name == 'vojo_save_blog') {
185         module_load_include('inc', 'vojo', 'vojo.script');
186         return _vojo_save_blog_script($params['nid'], $params['fid'], $params['uid']);
187     }
188     elseif ($script_name == 'vojo_login') {
189         module_load_include('inc', 'vojo', 'vojo.script');
190         return _vojo_login_script($params['number']);
191     }
192     elseif ($script_name == 'vojo_welcome') {
193         module_load_include('inc', 'vojo', 'vojo.script');
194         return _vojo_welcome_script($params['number']);
195     }
196     elseif ($script_name == 'vojo_redirect_to_group') {
197         module_load_include('inc', 'vojo', 'vojo.script');
198         return _vojo_redirect_to_group_script($params['number']);
199     }
200     elseif ($script_name == 'vojo_pick_create_or_anon') {
201         module_load_include('inc', 'vojo', 'vojo.script');
202         return _vojo_pick_create_or_anon_script($params['number'], $params['nid']);
203     }
204     elseif ($script_name == 'vojo_create_user') {
205         module_load_include('inc', 'vojo', 'vojo.script');
206         return _vojo_create_user_script($params['number'], $params['nid'], $params['simple']);
207     }
208     elseif ($script_name == 'vojo_pick_language') {
209         module_load_include('inc', 'vojo', 'vojo.script');
210         return _vojo_pick_language_script();
211     }
212     elseif ($script_name == 'vojo_change_language') {
213         module_load_include('inc', 'vojo', 'vojo.script');
214         return _vojo_change_language_script($params['language']);
215     }
216     elseif ($script_name == 'vojo_play_extension') {
217         module_load_include('inc', 'vojo', 'vojo.script');
218         return _vojo_get_play_extension_script($params['eid']);
219     }
220     elseif ($script_name == 'vojo_default_incoming_text') {
221         module_load_include('inc', 'vojo', 'vojo.script');
222         return _vojo_default_incoming_text();
223     }
224     elseif ($script_name == 'vojo_update_username') {
225         module_load_include('inc', 'vojo', 'vojo.script');
226         return _vojo_update_username($params['number'], $params['text']);
227     }
228     elseif ($script_name == 'vojo_hear_group_stories') {
229         module_load_include('inc', 'vojo', 'vojo.script');
230         return _vojo_hear_group_stories_script($params['nid']);
231     }
232     elseif ($script_name == 'vojo_route_inboud_text') {
233         module_load_include('inc', 'vojo', 'vojo.script');
234         return _vojo_route_inbound_text($params['number'], $params['text']);
235     }
236
237 }
238
239 /**
240  * Implementation of hook_voipscript_get_script_names().
241  */
242 function vojo_voipscript_get_script_names() {
243   return array(
244     'vojo_group',
245     'vojo_simple_group',
246     'vojo_record_blog',
247     'vojo_save_blog',
248     'vojo_login',
249     'vojo_welcome',
250     'vojo_redirect_to_group',
251     'vojo_pick_create_or_anon',
252     'vojo_create_user',
253     'vojo_pick_language',
254     'vojo_change_language',
255     'vojo_play_extension',
256     'vojo_default_incoming_text',
257     'vojo_update_username',
258     'vojo_hear_group_stories',
259     'vojo_route_inboud_text'
260   );
261 }
262
263 /**
264  * Implementation of hook_ctools_plugin_dierctory() to let the system know
265  * we implement task and task_handler plugins.
266  */
267 function vojo_ctools_plugin_directory($module, $plugin) {
268   return 'plugins/' . $plugin;
269 }
270
271 /** --------- OPENLAYERS ------------------------------------------------------ **/
272
273 /**
274  * Define custom styles for rendering the markers on a map.
275  * Implements hook_openlayers_styles().
276  *   http://drupal.org/node/620602
277  */
278 function vojo_openlayers_styles() {
279   $styles = array();
280   
281   $style = new stdClass();
282   $style->api_version = 1;
283   $style->name = 'vojo_default';
284   $style->title = t('Vojo Default style');
285   $style->description = t('Basic default style for the vojo website.');
286   $style->data = array(
287     'pointRadius' => 6,
288     'fillColor' => '#ffffff',
289     'strokeColor' => '#890053',
290     'strokeWidth' => 4,
291     'fillOpacity' => 0.9,
292     'strokeOpacity' => 1
293   );
294   $styles[ $style->name ] = $style;
295   
296   $style = new stdClass();
297   $style->api_version = 1;
298   $style->name = 'vojo_selected';
299   $style->title = t('Vojo Selected style');
300   $style->description = t('Basic selected style for the vojo website.');
301   $style->data = array(
302     'pointRadius' => 10,
303     'fillColor' => '#ffffff',
304     'strokeColor' => '#890053',
305     'strokeWidth' => 8,
306     'fillOpacity' => 1.0,
307     'strokeOpacity' => 1.0
308   );
309   $styles[ $style->name ] = $style;
310   
311   $style = new stdClass();
312   $style->api_version = 1;
313   $style->name = 'vojo_temporary';
314   $style->title = t('Vojo Temporary style');
315   $style->description = t('Basic temporary style for the vojo website.');
316   $style->data = array(
317     'pointRadius' => 8,
318     'fillColor' => '#ffffff',
319     'strokeColor' => '#890053',
320     'strokeWidth' => 6,
321     'fillOpacity' => 0.5,
322     'strokeOpacity' => 0.5
323   );
324   $styles[ $style->name ] = $style;
325   
326   return $styles;
327 }
328
329 /**
330  * Tell ctools that we have custom marker styles.
331  * Implements hook_ctools_plugin_api().
332  *   http://drupal.org/node/620602
333  */
334 function vojo_ctools_plugin_api($module, $api) {
335   if ($module == "openlayers") {
336     switch ($api) {
337       case 'openlayers_styles':
338         return array('version' => 1);
339
340     }
341   }
342 }
343
344 /**
345  * Get the url to an audio file stored with this model.
346  */
347 function _vojo_get_audio_url($filename){
348   global $base_url;
349   return $base_url."/".drupal_get_path('module','vojo')."/audio/".$filename;
350 }
351
352 /**
353  * Tweak the blog edit form and setup some callbacks for later customizations
354  */
355 function vojo_form_blog_node_form_alter($form, &$form_state) {
356   // Hide location field to prevent users from editing it directly. 
357   $form['field_location_string']['#theme'] = FALSE;
358
359   $form['#after_build'][] = 'vojo_form_blog_node_form_after_build';
360   // Add submit handler. 
361   $form['#submit'][] = 'vojo_form_blog_node_form_submit';
362   
363   // Adjust weight of submit button
364   $form['buttons']['#type'] = 'markup';
365   $form['buttons']['#weight'] = 15;
366   // Hide preview button
367   unset($form['buttons']['preview']);
368   // Hide sticky check box
369   unset($form['options']['sticky']);
370 }
371
372 /* After build function for blog node form */
373 function vojo_form_blog_node_form_after_build($form, &$form_state) {
374   global $user;
375   // Move attachments outside vertical tabs
376   $form['attachments']['#group'] = '';
377   // Make attachments fieldset expanded and not collapsible. 
378   $form['attachments']['#collapsed '] = 0;
379   $form['attachments']['#collapsible'] = 0;
380
381   // Hide fields from regular users (authenticaged and bloggers).
382   if (user_access('administer nodes') == FALSE) {
383     $form['path']['#access'] = FALSE;               // only admins can set custom paths for blog posts
384     $form['comment_settings']['#access'] = FALSE;   // turn off blog post comments
385     // Hide field_image from everyone except admins, since all image uploading
386     // should normally be done through file attachments (to be handled by media mover).
387     $form['field_image']['#access'] = FALSE;
388
389     // Hide the audience select area if we are creating a new node.
390     if (!empty($_GET['gids'][0]) && empty($form['nid']['#value'])) {
391       // For some reason using '#access' => false causes any values to be ereased on save.
392       $form['og_nodeapi']['visible']['og_groups']['#prefix'] = '<div style="display: none">';
393       $form['og_nodeapi']['visible']['og_groups']['#suffix'] = '</div>';
394       // Unset read only version of audience field.
395       unset($form['og_nodeapi']['visible']['og_groups_visible']);
396     }
397   }
398     
399   // Make publishing options always expanded
400   $form['options']['#collapsible'] = FALSE;
401   $form['options']['#collapsed'] = FALSE;
402   $form['options']['promote']['#title'] = t("Yes, feature this story!");
403   // Change title from Publishing options to Promote story if not admin
404   if (user_access('administer nodes') == FALSE && user_access('override blog published option') == FALSE) {
405     $form['options']['#title'] = t("Promote story");
406   }
407
408   // pre-fill the WKT search address with the existing location string
409   $form['field_map']['openlayers_geocoder_query']['#value'] = $form['#node']->field_location_string[0]['value'];
410
411   // force hiding of voipnode extension settings (see http://drupal.org/node/1549288)
412   unset($form['group_voipnode_settings']);
413
414   return $form;
415 }
416
417 /* Submit handler for blog node form */
418 function vojo_form_blog_node_form_submit($form, &$form_state) {
419   // Grab user entered value from openlayers_geocoder field and save to $form['field_location_string']
420   $form_state['values']['field_location_string'][0]['value'] = $form['field_map']['openlayers_geocoder_query']['#value'];
421 }
422
423 /**
424  * Find the node matching the specified phone number, checking in all voipnumber fields.
425  * Returns false if no match is found.  If multiple matches are found, this return the first
426  * one it finds.
427  */
428 function _vojo_voipnumber_api_nid_from_number($number) {
429   // look in all the voipnumber fields
430   foreach( content_fields() as $field){
431     if($field['type']=='voipnumberfield'){
432       // determine what table to search in 
433       $table_name= "content_type_".$field['type_name'];
434       $col_name = $field['field_name']."_vnid";
435       $sql = "
436         SELECT num.nid
437         FROM {".$table_name."} AS g
438         LEFT JOIN {voipnumber} AS num
439         ON g.".$col_name."=num.vnid
440         LEFT JOIN {voipnumber_dialing_codes} AS code
441         ON num.country_name = code.country
442         WHERE CONCAT('+', code.code, num.phone_number) = %s";
443       $result = db_query($sql, $number);
444       while ($noderow = db_fetch_object($result)) {
445         return $noderow->nid;
446       }
447     }
448   }
449   return false;
450 }
451
452
453 /**
454  * Find the extension number for a specific node id.
455  */
456 function _vojo_voipextension_api_extension_for_node($nid) {
457   return db_result(db_query('SELECT eid FROM {voipextension} WHERE module_id = %d', $nid));
458 }
459
460 /**
461  * Helper function to format phone numbers. 
462  */
463 function vojo_format_phone_number($number) {
464   $parts = array();
465   if ($number{0} == '+') {
466     $number = substr($number, 1);
467     if (is_numeric($number)) {
468       if (strlen($number) == 19) {
469         $formatted_number = '+'. substr($number, 0, 2) .' '. substr($number, 2, 2) .' '. substr($number, 4, 3) .'-'. substr($number, 7);
470       }
471       // If 12 digits format as Mexican number: +xx xx xxx-xxxxx.
472       else if (strlen($number) == 11 && substr($number, 0, 1) == '1') {
473         // 10 digit with US country code: +1 (xxx) xxx-xxxx.
474         $formatted_number = '+1 ('. substr($number, 1, 3) .') '. substr($number, 4, 3) .'-'. substr($number, 7);
475       }
476       else if ((strlen($number) == 12 || strlen($number) == 13) && substr($number, 0, 2) == '55') {
477         // Brazillian +55 aa nnnn nnnn or +55 aa nnnn nnnnn
478         $parts[] = '+55';
479         $parts[] = substr($number, 2, 2);
480         $parts[] = substr($number, 4, 4);
481         $parts[] = substr($number, 8);
482         $formatted_number = implode(' ', $parts);
483       }
484       // If 10 digits format as US number: (xxx) xxx-xxxx.
485       else if (strlen($number) == 10) {
486         $formatted_number = '('. substr($number, 0, 3) .') '. substr($number, 3, 3) .'-'. substr($number, 6);
487       } else {
488         watchdog('vojo', "Unrecognized phone number format: $number");
489       }
490     }
491     else {
492       $formatted_number = $number;
493     }
494   } else {
495     // Local number
496     $formatted_number = $number;
497   }
498   return $formatted_number;
499 }
500
501 /** THEME FUNCTIONS **/
502
503 function vojo_theme() {
504   $items['vojo_site_header'] = array( 
505     'arguments' => array('items' => NULL),
506     'template' => 'templates/vojo-site-header',
507   );
508   $items['dropdown_links'] = array( 
509     'arguments' => array('links' => NULL, 'attributes' => NULL),
510   );
511   return $items;
512 }
513
514 function vojo_preprocess_vojo_site_header(&$vars) {
515   global $user;
516   
517   $vars['base_path'] = base_path();
518   
519   // Generate user links menu
520   $menu_tree = menu_tree_page_data('secondary-links');
521   $vars['user_links'] = '<div id="vojo-user-menu" class="vojo-header-menu">';
522   if ($vars['logged_in'] == 1) {
523     // The parent menu item showing the user name is hard coded for easier
524     // rendering of the user name. Couldn't get it to work as a menu callback. 
525     $vars['user_links'] .= '<ul id="vojo-user-links" class="vojo-header-links"><li class="menu-parent">';
526     $vars['user_links'] .= '<span>'. $user->name .'</span>';
527     $vars['user_links'] .= theme('dropdown_links', $menu_tree, array('class' => 'submenu'));
528     $vars['user_links'] .= '</li></ul>';
529   }
530    else {
531     $vars['user_links'] .= l(t('Login'), 'user/login', array('attributes' => array('id' => 'vojo-login-link'))) .' ';
532     if (variable_get('user_register', '0')) {
533       // Include signup link if users are allowed to register online
534       $vars['user_links'] .= ' | '. l(t('Signup'), 'user/register', array('attributes' => array('id' => 'vojo-login-link')));
535     }
536   }
537   $vars['user_links'] .= '</div>';
538   
539   $vars['primary_links'] = theme_get_setting('toggle_primary_links') ? menu_primary_links() : array();
540   
541   // Generate language links menu
542   $q = $_GET['q']; 
543   $translations = translation_path_get_translations($q); 
544   $languages = language_list('enabled'); 
545   $output  = '<div id="vojo-language-menu" class="vojo-header-menu">';
546   $output .= '<ul id="vojo-language-links" class="vojo-header-links"><li class="menu-parent">';
547   $output .= '<span>'.t('Languages').'</span>';
548   $output .= '<ul class="submenu">';
549   // figure out the paths (copied from the switcher block view in locale.module)
550   $path = drupal_is_front_page() ? '<front>' : $_GET['q'];
551   $links = array();
552   foreach ($languages[1] as $language) {
553     $links[$language->language] = array(
554       'href'       => $path,
555       'title'      => $language->native,
556       'language'   => $language,
557       'attributes' => array('class' => 'language-link'),
558     );
559   }
560   drupal_alter('translation_link', $links, $path);
561   $output .= theme('links', $links, array());
562   $output .= '</li></ul>';   
563   $output .= '</div>';
564   $vars['language_links'] = $output;
565   
566   // Get voip call in number, set at admin/voip/call/settings
567   $vars['vojo_callin_number'] = vojo_format_phone_number(variable_get('voipcall_cid_number',''));
568   
569   // Load language swicher block
570   $lang_block = module_invoke('locale', 'block', 'view', 0);
571   $vars['language_switcher'] = '<div class="block-locale">'. $lang_block['content'] .'</div>';
572 }
573
574 function vojo_preprocess_page(&$vars) {
575   // Provide persistant vojo site-wide header (for global operations like login)
576   $vars['vojo_site_header'] = theme('vojo_site_header');
577 }
578
579 /** 
580  * Provides our own version of theme_links() with markup for a dropdown menu. 
581  */
582 function theme_dropdown_links($links, $attributes = array('class' => 'links')) {
583   global $language;
584   $output = '';
585   if (count($links) > 0) {
586     $output = '<ul'. drupal_attributes($attributes) .'>';
587
588     $num_links = count($links);
589     $i = 1;
590     foreach ($links as $menu_item) {
591       $link = $menu_item['link'];
592       // Do not show disabled menu items and submenu
593       if ($link['hidden'] != 1) {
594         $class = 'menu-' . $menu_item['link']['mlid'];
595         // Add first, last and active classes to the list of links to help out themers.
596         if ($i == 1) {
597           $class .= ' first';
598         }
599         if ($menu_item['below']) {
600           $class .= ' menu-parent';
601         }
602         if ($i == $num_links) {
603           $class .= ' last';
604         }
605
606         $class .= ' menu-item-' . $i;
607
608         if ($link['in_active_trail'] == TRUE) {
609           $class .= ' active-trail';
610         }
611         // Add classes based on path name
612         $path_alias = drupal_get_path_alias($link['href']); 
613         $class_from_path = str_replace(array('/', ' '), '-', $path_alias);
614         $class .= ' menu-' . $class_from_path;
615
616         if (isset($link['href']) && ($link['href'] == $_GET['q'] || ($link['href'] == '<front>' && drupal_is_front_page()))
617             && (empty($link['language']) || $link['language']->language == $language->language)) {
618           $class .= ' active';
619         }
620         $output .= '<li'. drupal_attributes(array('class' => $class)) .'>';
621
622         if (isset($link['href']) && $link['href'] != 'nolink' && $link['href'] != 'vojo_user_name') {
623           // Pass in $link as $options, they share the same keys.
624           $output .= l($link['title'], $link['href'], $link);
625         }
626         else if (!empty($link['title'])) {
627           // Some links are actually not links, but we wrap these in <span> for adding title and class attributes
628           if (empty($link['html'])) {
629             $link['title'] = check_plain($link['title']);
630           }
631           $span_attributes = '';
632           if (isset($link['localized_options']['attributes'])) {
633             $span_attributes = drupal_attributes($link['localized_options']['attributes']);
634           }
635           $output .= '<span'. $span_attributes .'>'. $link['title'] .'</span>';
636         }
637         $i++;
638
639         // Display expanded submenu items, so we can style it as a dropdown menu
640         if (!empty($menu_item['below']) && $menu_item['link']['expanded'] != 0) {
641           // reset counter 
642           $i_sub = 0;
643           $sublinks_count = count($menu_item['below']);
644
645           $output .= '<ul class="submenu' . $submenu_class . '">';
646
647           foreach ($menu_item['below'] as $child_item) {
648             $child_link =  $child_item['link'];
649             $class = 'menu-' . $child_item['link']['mlid'];
650
651             $options = array();
652             if ($child_item['link']['localized_options']['fragment']) { 
653               $options = array(
654                 'fragment' => $child_item['link']['localized_options']['fragment'],
655               );
656             } 
657             // Only show menu items not set to hidden in UI
658             if ($child_link['hidden'] != 1) {
659               $output .= '<li'. drupal_attributes(array ('class' => $class)) .'>';
660               $output .= l($child_link['title'], $child_link['href'], $options);
661               $output .= '</li>';
662             }
663             $i_sub++;
664           }
665           $output .= '</ul>';
666           // End submenu
667         }
668       }
669       $output .= '</li>';
670     }
671
672   $output .= '</ul>';
673   }
674   return $output;
675 }
676
677 /** 
678  * Return true if the node has audio attached
679  */
680 function vojo_node_has_audio($node){
681   return vojo_node_has_file_with_extension($node,'mp3');
682 }
683
684 /** 
685  * Return true if the node has images attached
686  */
687 function vojo_node_has_image($node){
688   return $node->field_image[0]!=null;
689 }
690
691 /** 
692  * Return true if the node has video attached
693  */
694 function vojo_node_has_video($node){
695   return vojo_node_has_file_with_extension($node,'m4v');
696 }
697
698 /** 
699  * Return true if the node has a mediamover completed file with this extension
700  */
701 function vojo_node_has_file_with_extension($node,$ext){
702   if( property_exists($node, 'media_mover') ) {
703     foreach($node->media_mover as $mm_config){
704       foreach($mm_config as $mm_item){
705         $pathinfo = pathinfo($mm_item['complete_file']);
706         if(array_key_exists('extension',$pathinfo)){
707           if($pathinfo['extension']==$ext){
708             return true;
709           }
710         }
711       }
712     }
713   }
714   return false;
715 }