Posted by gagarine on August 19, 2011 at 9:28am
2 followers
Jump to:
| Project: | Table of Contents |
| Version: | 7.x-1.x-dev |
| Component: | Code |
| Category: | bug report |
| Priority: | normal |
| Assigned: | Unassigned |
| Status: | active |
Issue Summary
Filters doesn't work mainly because the function are not in .module and not always include.
Comments
#1
Here my implementation. I use http://simplehtmldom.sourceforge.net/ instead of a regex. So you have to download it rename it "simple_html_dom.inc" and add in the module folder.
If you like this approach I can provide a patch.
<?php
/**
* Implementation of hook_filter_info().
*/
function tableofcontents_filter_info() {
module_load_include('admin.inc', 'tableofcontents');
$filters['filter_toc'] = array(
'title' => t('Table of contents'),
'description' => t('Inserts a table of contents in place of [toc ...] tags.'),
'process callback' => '_tableofcontents_process',
'prepare callback' => '_tableofcontents_prepare',
'settings callback' => '_tableofcontents_settings',
'cache' => TRUE,
'tips callback' => 'tableofcontents_filter_tips',
);
$filters['filter_toc_id'] = array(
'title' => t('Assign an ID to each anchors'),
'description' => t('Add an ID to all the anchors on the page. May be necessary in case the table of contents block is used.'),
'process callback' => 'tableofcontents_filter_toc_id_process',
//'settings callback' => '_tableofcontents_settings',
'cache' => TRUE,
'tips callback' => 'tableofcontents_filter_tips',
);
return $filters;
}
/**
* Filter toc_id
*
* This is not really a hook.
* The function name is manually specified via 'process callback' in filter_info
*
* Assign an ID to each anchors
*
* @see tableofcontents_filter_info
*
*/
function tableofcontents_filter_toc_id_process($text, $filter, $format, $langcode, $cache, $cache_id) {
if (empty($text)) {
return '';
}
module_load_include('inc', 'tableofcontents', 'simple_html_dom');
$html = str_get_html($text);
$tags = array('h2', 'h3','h4'); //TODO make a settings
foreach ($tags as $tag) {
$matchs = $html->find($tag);
foreach ($matchs as $match) {
if (!$match->hasAttribute("id"))
$match->id = drupal_html_id($match->innertext);
}
}
return $html->save();
}
?>
#2
gagarine,
As a side note, the D6 version had the filter definitions in the .module and the implementations in the .pages.inc. That way, the include file could be avoided on all the pages were no [toc] was required. Great optimization.
As for your implementation it won't be possible to auto-number the headers if you don't work on the in order. The first H2 should be 1., the first H3 after that should be 1.1, whereas the second H3 after the 3rd H2 would be 3.2.
Note also that one of the optimizations in my D6 version was to save the list of headers in this process so when I generate the table of contents I already have it (instead of having to re-parse the whole $text variable which if it's large will be very slow--i.e. think of pages of 100Kb.)
The D6 version includes quite many features that seem to be gone from the D7 version...
Thank you.
Alexis
#3
--snip--
#4
AlexisWilke I tried first to move the definition in the .module and make in include but the function fail in other part... I was faster for me to rewrite the filter instead of trying to debug the code. I move all in module because it was short and I want to avoid to load an other file (I use the other .inc but only for the toc block, and I forced the block to be cache by page)
I'm note sure is really useful to save the list of header because filter result are cached anyway. But we can store the result in a static variable so if the filter is called two time with the same text we return the static variable.
The big issue is than, we can't auto number the header like that and we can't create a multi level TOC. Perhaps with something like that to create a nested array of all the header and send this array to a theme function:
<?php$headersLevel = array('h1'=>1,'h2'=>2,'h3'=>3);
foreach($html->find('h1, h2,h3,h4') as $header){
if($headersLevel[$lastHeader->tag] == $headersLevel[$header->tag]){
}else if($headersLevel[$lastHeader->tag] > $headersLevel[$header-tag]){
}else{
}
$lastHeader = $header;
}
?>
I have to think about that.
#5
I was just saying... 8-)
That's most certainly a functionality that many users will not want to lose.
How to implement it is less a concern as long as the result is correct.
Note that someone even asked me to have a different counting system for each level (i.e. 1, 1a, 1b, 2, 2a, 2b, 2c, etc.) instead of just numbers or letters.
Thank you.
Alexis Wilke