format]) && !$reset) { return $shortcodes_enabled[$format->format]; } $shortcodes_enabled[$format->format] = array(); $shortcodes = shortcode_list_all($reset); $filters = filter_list_format($format->format); if (empty($filters['shortcode'])) { return array(); } foreach($filters['shortcode']->settings as $name => $enabled) { // Run through all shortcodes if ($enabled) { $shortcodes_enabled[$format->format][$name] = $shortcodes[$name]; } } return $shortcodes_enabled[$format->format]; } /** * Implementation of hook_filter_info(). */ function shortcode_filter_info() { $filters['shortcode'] = array( 'title' => t('Shortcodes'), 'description' => t('Provides WP like shortcodes to this text format.'), 'process callback' => '_shortcode_process', 'settings callback' => '_shortcode_settings_form', 'tips callback' => '_shortcode_filter_tips', ); $filters['shortcode_text_corrector'] = array( 'title' => t('Shortcodes - html corrector'), 'description' => t('Trying to correct the html around shortcodes. Enable only if you using wysiwyg editor.'), 'process callback' => '_shortcode_postprocess_text', ); return $filters; } /** * Filter tips callback */ function _shortcode_filter_tips($filter, $format, $long = FALSE) { $shortcodes = shortcode_list_all_enabled($format); $tips = array(); $args = func_get_args(); foreach($filter->settings as $name => $enabled) { // Run through all shortcodes if($enabled && !empty($shortcodes[$name]['tips callback']) && function_exists($shortcodes[$name]['tips callback'])) { $tips[] = call_user_func_array($shortcodes[$name]['tips callback'], array($format, $long)); } } return theme('item_list', array( 'title' => t('Shortcodes usage'), 'items' => $tips, 'type' => 'ol', ) ); } /** * Settings form */ function _shortcode_settings_form($form, &$form_state, $filter, $format, $defaults) { $settings = array(); $filter->settings += $defaults; $shortcodes = shortcode_list_all(); foreach ($shortcodes as $key => $shortcode) { $settings[$key] = array( '#type' => 'checkbox', '#title' => t('Enable %name shortcode', array('%name' => $shortcode['title'])), '#default_value' => array(), '#description' => 'Enable or disable this shortcode in this input format', ); if (!empty($filter->settings[$key])) { $settings[$key]['#default_value'] = $filter->settings[$key]; } elseif( !empty($defaults[$key])) { $settings[$key]['#default_value'] = $defaults[$key]; } } return $settings; } /** * Tags cache * @param $tags * * @access private */ function _shortcode_tags($tags = NULL) { $shortcodes = &drupal_static(__FUNCTION__, array()); if ($tags) { $shortcodes = $tags; return TRUE; } return $shortcodes; } /** * Process the shortcodes according to the text and the text format. */ function _shortcode_process($text, $filter) { // TODO: need cache for list_all for the given filter! $shortcodes = shortcode_list_all(); $shortcodes_enabled = array(); foreach($filter->settings as $name => $value) { // run through all shortcodes if($value && $shortcodes[$name]['process callback']) { $shortcodes_enabled[$name] = array( 'function' => $shortcodes[$name]['process callback'], ); } } if (empty($shortcodes_enabled)) { return $text; } // save the shortcodes _shortcode_tags($shortcodes_enabled); // improved version - recursive processing - embed tags within other tags is supported! $chunks = preg_split('!(\[.*?\])!', $text, -1, PREG_SPLIT_DELIM_CAPTURE ); //dpr($chunks); $heap = array(); $heap_index = array(); foreach ($chunks as $c) { if (!$c) { continue; } // shortcode or not if (($c[0] == '[') && (substr($c, -1, 1) == ']')) { // $c contains shortcode // self-closing tag or not $c = substr($c, 1, -1); //dpr('process: ' . $c); if (substr($c, -1, 1) == '/') { // process a self closing tag - it has / at the end! //dpr('self closing: ' . $c); /* * 0 - the full tag text? * 1/5 - An extra [ or ] to allow for escaping shortcodes with double [[]] * 2 - The shortcode name * 3 - The shortcode argument list * 4 - The content of a shortcode when it wraps some content. * */ $ts = explode(' ', trim($c)); $tag = array_shift($ts); $m = array( $c, '', $tag, implode(' ', $ts), NULL, '' ); array_unshift($heap_index, '_string_'); array_unshift($heap, _shortcode_process_tag($m)); } elseif ($c[0] == '/') { // closing tag - process the heap $closing_tag = substr($c, 1); //dpr('closing tag: ' . $closing_tag ); $process_heap = array(); $process_heap_index = array(); $found = FALSE; // get elements from heap and process do { $tag = array_shift($heap_index); $heap_text = array_shift($heap); if($closing_tag == $tag) { // process the whole tag $m = array( $tag . ' ' . $heap_text, '', $tag, $heap_text, implode('', $process_heap), '' ); $str = _shortcode_process_tag($m); array_unshift($heap_index, '_string_'); array_unshift($heap, $str); $found = TRUE; } else { array_unshift($process_heap, $heap_text); array_unshift($process_heap_index, $tag); } } while(!$found && $heap); if(!$found) { foreach($process_heap as $val) { array_unshift($heap, $val); } foreach($process_heap_index as $val) { array_unshift($heap_index, $val); } } } else { // starting tag. put to the heap //dpr('tag pattern: ' . $c); $ts = explode(' ', trim($c)); $tag = array_shift($ts); // dpr('start tag: ' . $tag); array_unshift($heap_index, $tag); array_unshift($heap, implode(' ', $ts)); } } else { // not found a pair? array_unshift($heap_index, '_string_'); array_unshift($heap, $c); } } return(implode('', array_reverse($heap))); } /* * Html corrector for wysiwyg editors * * Correcting p elements around the divs. No div are allowed in p so remove them. * */ function _shortcode_postprocess_text($text, $filter) { $patterns = array( '|#!#|is', '!
( |\s)*(<\/*div>)!is', '!
( |\s)*(