Extension:SectionHide

From MediaWiki.org
Jump to navigation Jump to search
MediaWiki extensions manual
Crystal Clear action run.svg
SectionHide
Release status: beta
px
Implementation User interface, Skin
Description Adds a link alongside edit links to collapse and expand sections of the article
Author(s) Simon Oliver (Hoggle42talk)
Latest version 1.7 (2014-08-26)
MediaWiki 1.19+
PHP 5.5+
Database changes No
License GNU General Public Licence 2.0 or later
Download See the code section
Parameters
  • $wgSectionHideShowtop
  • $wgSectionHideUseImages
  • $wgSectionHideUseImagesOnly
  • $wgSectionHideHideImage
  • $wgSectionHideShowImage
  • $wgSectionHideb4title
  • $wgSectionHideopenbracket
  • $wgSectionHideclosebracket
Hooks used
ParserSectionCreate
ParserAfterParse
Translate the SectionHide extension if it is available at translatewiki.net
Check usage and version matrix.

The SectionHide extension adds a [hide] link wherever there is a section heading [edit] link. This link will hide all content between that section header and the next section header of the same level, changing itself to read [show] in the process. Clicking [show] will restore the section to view.

Example[edit]

Illustration showing how each hide link conceals the related block in a nested fashion while keeping the title visible.

SectionHide.jpg

Installation[edit]

  • Copy the code and place the file(s) in a directory called SectionHide in your extensions/ folder.
  • Add the following code at the bottom of your LocalSettings.php:
    require_once "$IP/extensions/SectionHide/SectionHide.php";
    
  • Yes Done – Navigate to Special:Version on your wiki to verify that the extension is successfully installed.

Code[edit]

SectionHide.php[edit]

<?php
/**
 * SectionHide extension - implements a hide/show link on sections on any ordinary page.
 * @version 1.7 - 2014/08/26
 * version 1.1 added a hide all/show all link for the entire article 
 * version 1.2 added an option on the hide all/show all link to show the initial text
 * version 1.3 added opt out error if installed alongside header tabs
 * version 1.4 added options for images as well as/instead of text
 * version 1.5 tweaks to improve appearance under vector skin, class change
 * version 1.6 options to show the link before the title and change the brackets
 * version 1.7 changed the closing tags to adhere to strict html
 *
 * @link https://www.mediawiki.org/wiki/Extension:SectionHide Documentation
 *
 * @file
 * @ingroup Extensions
 * @package MediaWiki
 * @author Simon Oliver
 * @copyright © 2013 Simon Oliver (Hoggle42)
 * @licence http://www.gnu.org/copyleft/gpl.html GNU General Public Licence 2.0 or later
 *
 * add the following line to localsettinge.php to use
 * require_once("$IP/extensions/SectionHide/SectionHide.php");
 * // Set this option to 1 to show the text before the first section when hiding all
 * // Set to X to show the top x-1 sections (use with caution - some browsers may complain)
 * // Set to -1 to disable
 * $wgSectionHideShowtop = 0; //default
 * // Set the first option to 1 to use images and add a relative path from the wiki root to the two images
 * // Set the second option to 1 to not include the text as well as the image
 * $wgSectionHideUseImages = 0; //default
 * $wgSectionHideUseImagesOnly = 0; //default
 * $wgSectionHideHideImage = ""; //default - set to e.g. "images/minus.gif" NB direction of slash
 * $wgSectionHideShowImage = ""; //default - e.g. "images/plus.gif"
 * // Use this option to show the link before the title (set to 1) 
 * $wgSectionHideb4title = 0; //default
 * // These options allow you to change or hide the brackets
 * $wgSectionHideopenbracket = "["; //default 
 * $wgSectionHideclosebracket = "]"; //default 
 */
 
if( !defined( 'MEDIAWIKI' ) ) {
	echo( "This file is an extension to the MediaWiki software and cannot be used standalone.\n" );
	die( 1 );
}
 
if( isset( $htUseHistory ) ) {
	echo( "This extension should not be used in conjunction with the Header Tabs extension.\n" );
	die( 1 );
}
 
$wgExtensionCredits['other'][] = array( 
	'name' => 'SectionHide', 
	'author' => 'Simon Oliver',
	'version' => '1.7',
	'url' => 'https://www.mediawiki.org/wiki/Extension:SectionHide',
	'descriptionmsg' => 'sectionhide-desc',
); 
 
$wgHooks['ParserAfterParse'][] = 'SectionHideHooks::onParserAfterParse';
$wgHooks['ParserSectionCreate'][] = 'SectionHideHooks::onParserSectionCreate';
 
$wgAutoloadClasses[ 'SectionHideHooks' ] = __DIR__ . '/SectionHideHooks.php';
$wgExtensionMessagesFiles[ 'SectionHide' ] = __DIR__ . '/SectionHide.i18n.php';
$wgExtensionMessagesFiles[ 'SectionHideAlias' ] = __DIR__ . '/SectionHide.alias.php';

SectionHideHooks.php[edit]

<?php
/**
 * Body file for extension SectionHide.
 *
 * @file
 * @ingroup Extensions
 */

# hooks class
class SectionHideHooks
        {
        public static function onParserSectionCreate( $parser, $section, &$sectionContent, $showEditLinks )
                {
                global $wgSectionHideUseImages, $wgSectionHideUseImagesOnly, $wgSectionHideHideImage, $wgSectionHideShowImage;
                global $wgSectionHideopenbracket, $wgSectionHideclosebracket, $wgSectionHideb4title;
                if ($showEditLinks && $section > 0)
                        {
                        $hidetext = wfMsg( 'sectionhide-hide' );
                        $showtext = wfMsg( 'sectionhide-show' );
                        $initialtext = $hidetext;
                        if ($wgSectionHideUseImages != 0)
                                {
                                if ($wgSectionHideUseImagesOnly == 0)
                                        {
                                        $initialtext = '<img src="'.$wgSectionHideHideImage.'">'.$hidetext;
                                        $hidetext = $wgSectionHideHideImage.':'.$hidetext;
                                        $showtext = $wgSectionHideShowImage.':'.$showtext;
                                        }
                                else
                                        {
                                        $initialtext = '<img src="'.$wgSectionHideHideImage.'">';
                                        $hidetext = $wgSectionHideHideImage.':';
                                        $showtext = $wgSectionHideShowImage.':';
                                        }      
                                }
			$divstart = '<div class="sectionblocks" id="sectionblock'.$section.'">';
                        $divend = '</div><!-- id="sectionblock'.$section.'" -->';
                        if($wgSectionHideb4title == 1)
                        	{
	                        $hidelink = '<span class="visibilitytoggle">'.$wgSectionHideopenbracket.'<a href="#" onclick="toggleSectionVisibility(this, '.$section.", '".$showtext."','".$hidetext."'".');">'.$initialtext.'</a>'.$wgSectionHideclosebracket.'</span>';
                        	$sectionContent = preg_replace( '/<h[2-6]>/', "$0$hidelink", $sectionContent);
                        	$sectionContent = preg_replace( '/<\/h[2-6]>/', "$0\n$divstart\n", $sectionContent);
                        	}
                        else 
                        	{
	                        $hidelink = '<span class="mw-editsection visibilitytoggle">'.$wgSectionHideopenbracket.'<a href="#" onclick="toggleSectionVisibility(this, '.$section.", '".$showtext."','".$hidetext."'".');">'.$initialtext.'</a>'.$wgSectionHideclosebracket.'</span>';
                        	$sectionContent = preg_replace( '/<\/h[2-6]>/', "$hidelink$0\n$divstart\n", $sectionContent);
                        	}
                        $sectionContent = $sectionContent."\n$divend\n";                               
                        }
                return true;
                }

        public static function onParserAfterParse( &$parser, &$text, &$sstate )
                {
                global $wgSectionHideShowtop;
                // need to nest sections by levels by moving around the closing tags
                $numberofmatches = preg_match_all('#<h[2-6].*<\/h[2-6]>\n<div class="sectionblocks"#', $text, $matches, PREG_OFFSET_CAPTURE);
                $closingdivmatches = preg_match_all('#<\/div><!-- id=#', $text, $divmatches, PREG_OFFSET_CAPTURE);

                if($numberofmatches == $closingdivmatches && $numberofmatches > 1)
                        {
                        if( $wgSectionHideShowtop > $numberofmatches) $wgSectionHideShowtop = $numberofmatches; // cannot exceed the number of matches
                        $headlevel = array();
                        $i = 0;
                        foreach($matches[0] as $match)
                                {
                                $headlevel[$i] = substr($match[0],2,1); // heading level always third character
                                $i++;
                                }
                        $headlevel[$i] = 1; // make sure it terminates correctly
                        for($i = 0 ; $i < $numberofmatches - 1 ; $i++)
                                {
                                // the rule is:
                                // for each heading if the next level is lower, move the closing div to after the closing div of that level and re-test
                                // length of closing divs is </div><!-- id="sectionblock2" --> = 32 + number of digits of id (which is $i+1)
                                $divlength = 33 + ($i>98 ? 2 : ($i>8 ? 1 : 0)); //NB this should cope with up to 999 heading sections. Articles with more than that deserve to be broken
                                $j = 1;
                                while(($i+$j) < $numberofmatches && $headlevel[$i+$j] > $headlevel[$i])
                                        {
                                        // insert a closing div before the next heading, or at the end if no more headings
                                        $text = insertAtLoc($text
                                                        , '</div><!-- id="sectionblock'.($i+1).'" -->'
                                                        , $divmatches[0][$i+$j][1] + $divlength + (($i+$j>98 && $i <= 8) ? 2 : ((($i+$j>98 && $i <= 98) || ($i+$j > 8 && $i <= 8)) ? 1 : 0))); // deal with situations where sublevel div is longer than parent level
                                        $text = removeAtLoc($text, $divlength, $divmatches[0][$i][1]);  // remove the closing div from its previous location
                                        // update the current positions
                                        $divmatches[0][$i][1] = $divmatches[0][$i+$j][1]; // is now where the sublevel div was
                                        $divmatches[0][$i+$j][1] = $divmatches[0][$i+$j][1]-$divlength; // moves by divlength towards the start
                                        $matches[0][$i+$j][1] = $matches[0][$i+$j][1]-$divlength; // moves by sublevel heading towards the start
                                        $j++;
                                        }
                                }
                        // new hide all link
                        if ( $wgSectionHideShowtop > 0 )
                                {
                                // insert a section zero opening div before the first section heading
                                $text = insertAtLoc($text, '<div class="sectionblocks" id="sectionblock0">', $matches[0][($wgSectionHideShowtop-1)][1]);
                                $text = '<p><span class="visibilitytoggle">[<a href="#" onclick="toggleSectionVisibility(this, 0,'."'"
                                        .wfMsg( 'sectionhide-showall' )."','".wfMsg( 'sectionhide-hideall' )."'".')">'.wfMsg( 'sectionhide-hideall' )
                                        .'</a>]</span></p>'
                                        .$text
                                        .'</div><!-- id="sectionblock0" -->';
                                }
                            elseif ( $wgSectionHideShowtop == 0 )
                                {
                                $text = '<p><span class="visibilitytoggle">[<a href="#" onclick="toggleSectionVisibility(this, 0,'."'"
                                        .wfMsg( 'sectionhide-showall' )."','".wfMsg( 'sectionhide-hideall' )."'".')">'.wfMsg( 'sectionhide-hideall' )
                                        .'</a>]</span></p><div class="sectionblocks" id="sectionblock0">'
                                        .$text
                                        .'</div><!-- id="sectionblock0" -->';
                                }
                        }
                return true;
                }      
        } # end of SectionHideHooks class

function insertAtLoc($subject, $toinsert, $atloc)
        {
        return substr($subject,0,$atloc).$toinsert.substr($subject,$atloc);
        }
 
function removeAtLoc($subject, $remlength, $atloc)
        {
        return substr($subject,0,$atloc).substr($subject,$atloc+$remlength);
        }

SectionHide.i18n.php[edit]

<?php
/**
 * Internationalisation file for extension SectionHide (system messages).
 *
 * @file
 * @ingroup Extensions
 */

$messages = array();

/** English
 * @author Hoggle42
 */
$messages['en'] = array(
	'sectionhide' => 'Section Hide',
	'sectionhide-desc' => 'Provides a hide/show link on section headings',
	'sectionhide-hide' => 'hide',
	'sectionhide-show' => 'show',
	'sectionhide-hideall' => 'hide all',
	'sectionhide-showall' => 'show all',
);

/** German (Deutsch)
 * @author Kghbln
 */
$messages['de'] = array(
	'sectionhide' => 'Section Hide',
	'sectionhide-desc' => 'Stellt einen Link zum Ein- und Ausblenden von Abschnittsüberschriften bereit',
	'sectionhide-hide' => 'ausblenden',
	'sectionhide-show' => 'einblenden',
	'sectionhide-hideall' => 'alle ausblenden',
	'sectionhide-showall' => 'alle einblenden',
);

/** French
 * @author Arcane21
 */
$messages['fr'] = array(
	'sectionhide' => 'Section Cacher',
	'sectionhide-desc' => 'Fournit un lien de cacher/montrer sur les en-têtes de section',
	'sectionhide-hide' => 'Cacher',
	'sectionhide-show' => 'Montrer',
	'sectionhide-hideall' => 'toute cacher',
	'sectionhide-showall' => 'toute montrer',
);

/** Spanish
 * @author Arcane21
 */
$messages['es'] = array(
	'sectionhide' => 'Sección Ocultar',
	'sectionhide-desc' => 'Proporciona un enlace mostrar/ocultar en las partidas de la sección',
	'sectionhide-hide' => 'Ocultar',
	'sectionhide-show' => 'Mostrar',
	'sectionhide-hideall' => 'todos ocultar',
	'sectionhide-showall' => 'todos mostrar',
);

SectionHide.alias.php[edit]

<?php
/**
 * Internationalisation file for extension SectionHide (aliases).
 *
 * @file
 * @ingroup Extensions
 */

$aliases = array();

/** English
 * @author Simon Oliver (Hoggle42)
 */
$aliases['en'] = array(
	'SectionHide' => array( 'SectionHide' ),
);

Mediawiki:Common.js[edit]

Add this here, or in individual skins

function toggleSectionVisibility(fieldObj, id, txt1, txt2)
  {
  var e = document.getElementById('sectionblock'+id);
  if(txt1.search(/gif:/i) >= 0) txt1 = '<img src="'+txt1.replace(':', '">');
  if(txt2.search(/gif:/i) >= 0) txt2 = '<img src="'+txt2.replace(':', '">');
  if(fieldObj.innerHTML == txt1)
    {
    fieldObj.innerHTML = txt2;
    e.style.display = 'block';
    }
  else
    {
    fieldObj.innerHTML = txt1;
    e.style.display = 'none';
    }
  }

Versions[edit]

Version 1.1

Added a hide/show all link at the top

Version 1.2

Added an option to keep the content before the first X-1 headings visible when using the hide all link. NB set to 1 to hide everything from the first heading to the end.

Note Note: Using anything other than 1 means that divs are overlapping instead of nested, which is a standards violation in xhtml [1]. Mediawiki is still using HTML, however, so overlapping is tolerated and most browsers deal with it. Some may have issues. By the same token, any divs in the actual text that cross over headings will potentially cause a problem, which is why every closing tag added has an id attribute to match it with the opening tag.

Version 1.3

added an opt-out error if installed alongside header tabs

Version 1.4

Added an option to use images (NB gifs only but change the javascript if you want to use pngs or jpegs) instead of/alongside words - as per requested feature

Allow hideall to be disabled with setting less than zero - as requested

Version 1.5

Resolved issues with display under Vector skin, resulting from renaming editsection class to mw-editsection in 1.22. Validated in 1.23.2

Version 1.6

Added an option on request to show the link before the title (on purpose this time) and two more to change or leave out the brackets

Version 1.7

Tweaked the closing div tag handling to avoid parser issues

Author Notes[edit]

Several similar extensions were created in the past, but they are not maintained and didn't do quite what I needed.