lib/TWiki/Plugins/TwistyPlugin.pm
changeset 0 414e01d06fd5
child 1 e2915a7cbdfa
equal deleted inserted replaced
-1:000000000000 0:414e01d06fd5
       
     1 # Plugin for TWiki Collaboration Platform, http://TWiki.org/
       
     2 #
       
     3 # Copyright (C) 2005 Rafael Alvarez, soronthar@sourceforge.net
       
     4 #
       
     5 # This program is free software; you can redistribute it and/or
       
     6 # modify it under the terms of the GNU General Public License
       
     7 # as published by the Free Software Foundation; either version 2
       
     8 # of the License, or (at your option) any later version.
       
     9 #
       
    10 # This program is distributed in the hope that it will be useful,
       
    11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    13 # GNU General Public License for more details, published at
       
    14 # http://www.gnu.org/copyleft/gpl.html
       
    15 #
       
    16 
       
    17 =pod
       
    18 
       
    19 ---+ package TwistyPlugin
       
    20 
       
    21 Convenience plugin for TWiki:Plugins.TwistyContrib.
       
    22 It has two major features:
       
    23    * When active, the Twisty javascript library is included in every topic.
       
    24    * Provides a convenience syntax to define twisty areas.
       
    25 
       
    26 =cut
       
    27 
       
    28 package TWiki::Plugins::TwistyPlugin;
       
    29 
       
    30 use TWiki::Func;
       
    31 use CGI::Cookie;
       
    32 use strict;
       
    33 
       
    34 use vars
       
    35   qw( $VERSION $RELEASE $pluginName $debug @modes $doneHeader $twistyCount
       
    36   $prefMode $prefShowLink $prefHideLink $prefRemember
       
    37   $defaultMode $defaultShowLink $defaultHideLink $defaultRemember );
       
    38 
       
    39 # This should always be $Rev: 15653 (19 Nov 2007) $ so that TWiki can determine the checked-in
       
    40 # status of the plugin. It is used by the build automation tools, so
       
    41 # you should leave it alone.
       
    42 $VERSION = '$Rev: 15653 (19 Nov 2007) $';
       
    43 
       
    44 # This is a free-form string you can use to "name" your own plugin version.
       
    45 # It is *not* used by the build automation tools, but is reported as part
       
    46 # of the version number in PLUGINDESCRIPTIONS.
       
    47 $RELEASE = '1.4.9';
       
    48 
       
    49 $pluginName = 'TwistyPlugin';
       
    50 
       
    51 my $TWISTYPLUGIN_COOKIE_PREFIX  = "TwistyContrib_";
       
    52 my $TWISTYPLUGIN_CONTENT_HIDDEN = 0;
       
    53 my $TWISTYPLUGIN_CONTENT_SHOWN  = 1;
       
    54 
       
    55 #there is no need to document this.
       
    56 sub initPlugin {
       
    57     my ( $topic, $web, $user, $installWeb ) = @_;
       
    58 
       
    59     # check for Plugins.pm versions
       
    60     if ( $TWiki::Plugins::VERSION < 1.1 ) {
       
    61         TWiki::Func::writeWarning(
       
    62             "Version mismatch between $pluginName and Plugins.pm");
       
    63         return 0;
       
    64     }
       
    65 
       
    66     _setDefaults();
       
    67 
       
    68     $debug = TWiki::Func::getPluginPreferencesFlag("DEBUG");
       
    69 
       
    70     $doneHeader  = 0;
       
    71     $twistyCount = 0;
       
    72 
       
    73     $prefMode = TWiki::Func::getPreferencesValue('TWISTYMODE')
       
    74       || TWiki::Func::getPluginPreferencesValue('TWISTYMODE')
       
    75       || $defaultMode;
       
    76     $prefShowLink = TWiki::Func::getPreferencesValue('TWISTYSHOWLINK')
       
    77       || TWiki::Func::getPluginPreferencesValue('TWISTYSHOWLINK')
       
    78       || $defaultShowLink;
       
    79     $prefHideLink = TWiki::Func::getPreferencesValue('TWISTYHIDELINK')
       
    80       || TWiki::Func::getPluginPreferencesValue('TWISTYHIDELINK')
       
    81       || $defaultHideLink;
       
    82     $prefRemember = TWiki::Func::getPreferencesValue('TWISTYREMEMBER')
       
    83       || TWiki::Func::getPluginPreferencesValue('TWISTYREMEMBER')
       
    84       || $defaultRemember;
       
    85 
       
    86     TWiki::Func::registerTagHandler( 'TWISTYSHOW',      \&_TWISTYSHOW );
       
    87     TWiki::Func::registerTagHandler( 'TWISTYHIDE',      \&_TWISTYHIDE );
       
    88     TWiki::Func::registerTagHandler( 'TWISTYBUTTON',    \&_TWISTYBUTTON );
       
    89     TWiki::Func::registerTagHandler( 'TWISTY',          \&_TWISTY );
       
    90     TWiki::Func::registerTagHandler( 'ENDTWISTY',       \&_ENDTWISTYTOGGLE );
       
    91     TWiki::Func::registerTagHandler( 'TWISTYTOGGLE',    \&_TWISTYTOGGLE );
       
    92     TWiki::Func::registerTagHandler( 'ENDTWISTYTOGGLE', \&_ENDTWISTYTOGGLE );
       
    93 
       
    94     return 1;
       
    95 }
       
    96 
       
    97 sub _setDefaults {
       
    98     $defaultMode     = 'span';
       
    99     $defaultShowLink = '';
       
   100     $defaultHideLink = '';
       
   101     $defaultRemember =
       
   102       '';    # do not default to 'off' or all cookies will be cleared!
       
   103 }
       
   104 
       
   105 sub _addHeader {
       
   106     return if $doneHeader;
       
   107     $doneHeader = 1;
       
   108 
       
   109     my $header .= <<'EOF';
       
   110 <style type="text/css" media="all">
       
   111 @import url("%PUBURL%/%TWIKIWEB%/TwistyContrib/twist.css");
       
   112 </style>
       
   113 <script type='text/javascript' src='%PUBURL%/%TWIKIWEB%/BehaviourContrib/behaviour.compressed.js'></script>
       
   114 <script type="text/javascript" src="%PUBURL%/%TWIKIWEB%/TWikiJavascripts/twikilib.js"></script>
       
   115 <script type="text/javascript" src="%PUBURL%/%TWIKIWEB%/TWikiJavascripts/twikiPref.js"></script>
       
   116 <script type="text/javascript" src="%PUBURL%/%TWIKIWEB%/TWikiJavascripts/twikiCSS.js"></script>
       
   117 <script type="text/javascript" src="%PUBURL%/%TWIKIWEB%/TwistyContrib/twist.compressed.js"></script>
       
   118 <script type="text/javascript">
       
   119 // <![CDATA[
       
   120 var styleText = '<style type="text/css" media="all">.twikiMakeVisible{display:inline;}.twikiMakeVisibleInline{display:inline;}.twikiMakeVisibleBlock{display:block;}.twikiMakeHidden{display:none;}</style>';
       
   121 document.write(styleText);
       
   122 // ]]>
       
   123 </script>
       
   124 EOF
       
   125 
       
   126     TWiki::Func::addToHEAD( 'TWISTYPLUGIN_TWISTY', $header );
       
   127 }
       
   128 
       
   129 sub _TWISTYSHOW {
       
   130     my ( $session, $params, $theTopic, $theWeb ) = @_;
       
   131     my $mode = $params->{'mode'} || $prefMode;
       
   132 
       
   133     my $btn = _twistyBtn( 'show', @_ );
       
   134     return _decodeFormatTokens( _wrapInButtonHtml( $btn, $mode ) );
       
   135 }
       
   136 
       
   137 sub _TWISTYHIDE {
       
   138     my ( $session, $params, $theTopic, $theWeb ) = @_;
       
   139     my $mode = $params->{'mode'} || $prefMode;
       
   140 
       
   141     my $btn = _twistyBtn( 'hide', @_ );
       
   142     return _decodeFormatTokens( _wrapInButtonHtml( $btn, $mode ) );
       
   143 }
       
   144 
       
   145 sub _TWISTYBUTTON {
       
   146     my ( $session, $params, $theTopic, $theWeb ) = @_;
       
   147     my $mode = $params->{'mode'} || $prefMode;
       
   148 
       
   149     my $btnShow = _twistyBtn( 'show', @_ );
       
   150     my $btnHide = _twistyBtn( 'hide', @_ );
       
   151     my $prefix = $params->{'prefix'} || '';
       
   152     my $suffix = $params->{'suffix'} || '';
       
   153     my $btn = $prefix . ' ' . $btnShow . $btnHide . ' ' . $suffix;
       
   154     return _decodeFormatTokens( _wrapInButtonHtml( $btn, $mode ) );
       
   155 }
       
   156 
       
   157 sub _TWISTY {
       
   158     my ( $session, $params, $theTopic, $theWeb ) = @_;
       
   159 
       
   160     _addHeader();
       
   161     $twistyCount++;
       
   162     my $id = $params->{'id'};
       
   163     if ( !defined $id || $id eq '' ) {
       
   164         $params->{'id'} = 'twistyId' . $theWeb . $theTopic . $twistyCount;
       
   165     }
       
   166     return _TWISTYBUTTON(@_) . ' ' . _TWISTYTOGGLE(@_);
       
   167 }
       
   168 
       
   169 sub _TWISTYTOGGLE {
       
   170     my ( $session, $params, $theTopic, $theWeb ) = @_;
       
   171     my $id = $params->{'id'};
       
   172     if ( !defined $id || $id eq '' ) {
       
   173         return '';
       
   174     }
       
   175     my $idTag = $id . 'toggle';
       
   176     my $mode  = 'div';            #$params->{'mode'} || $prefMode;
       
   177     unshift @modes, $mode;
       
   178 
       
   179     my $isTrigger = 0;
       
   180     my $cookieState = _readCookie( $session, $idTag );
       
   181     my @propList =
       
   182       _createHtmlProperties( undef, $idTag, $mode, $params, $isTrigger,
       
   183         $cookieState );
       
   184     my $props = @propList ? " " . join( " ", @propList ) : '';
       
   185     my $modeTag = '<' . $mode . $props . '>';
       
   186     return _decodeFormatTokens( _wrapInContentHtmlOpen() . $modeTag );
       
   187 }
       
   188 
       
   189 sub _ENDTWISTYTOGGLE {
       
   190     my ( $session, $params, $theTopic, $theWeb ) = @_;
       
   191     my $mode = shift @modes;
       
   192     my $modeTag = ($mode) ? '</' . $mode . '>' : '';
       
   193     return $modeTag . _wrapInContentHtmlClose();
       
   194 }
       
   195 
       
   196 sub _twistyBtn {
       
   197     my ( $twistyControlState, $session, $params, $theTopic, $theWeb ) = @_;
       
   198 
       
   199     _addHeader();
       
   200 
       
   201     # not used yet:
       
   202     #my $triangle_right = '&#9658;';
       
   203     #my $triangle_down = '&#9660;';
       
   204 
       
   205     my $id = $params->{'id'};
       
   206     if ( !defined $id || $id eq '' ) {
       
   207         return '';
       
   208     }
       
   209     my $idTag = $id . $twistyControlState if ($twistyControlState) || '';
       
   210 
       
   211     my $defaultLink =
       
   212       ( $twistyControlState eq 'show' ) ? $prefShowLink : $prefHideLink;
       
   213 
       
   214     # link="" takes precedence over showlink="" and hidelink=""
       
   215     my $link = $params->{'link'};
       
   216 
       
   217     if ( !defined $link ) {
       
   218 
       
   219         # if 'link' is not set, try 'showlink' / 'hidelink'
       
   220         $link = $params->{ $twistyControlState . 'link' };
       
   221     }
       
   222     if ( !defined $link ) {
       
   223         $link = $defaultLink || '';
       
   224     }
       
   225 
       
   226     my $img = $params->{ $twistyControlState . 'img' }
       
   227       || $params->{'img'}
       
   228       || '';
       
   229     my $imgright = $params->{ $twistyControlState . 'imgright' }
       
   230       || $params->{'imgright'}
       
   231       || '';
       
   232     my $imgleft = $params->{ $twistyControlState . 'imgleft' }
       
   233       || $params->{'imgleft'}
       
   234       || '';
       
   235     $img      =~ s/['\"]//go;
       
   236     $imgright =~ s/['\"]//go;
       
   237     $imgleft  =~ s/['\"]//go;
       
   238     my $imgTag =
       
   239       ( $img ne '' ) ? '<img src="' . $img . '" border="0" alt="" />' : '';
       
   240     my $imgRightTag =
       
   241       ( $imgright ne '' )
       
   242       ? '<img src="' . $imgright . '" border="0" alt="" />'
       
   243       : '';
       
   244     my $imgLeftTag =
       
   245       ( $imgleft ne '' )
       
   246       ? '<img src="' . $imgleft . '" border="0" alt="" />'
       
   247       : '';
       
   248     my $imgLinkTag =
       
   249         '<a href="#">'
       
   250       . $imgLeftTag
       
   251       . '<span class="twikiLinkLabel twikiUnvisited">'
       
   252       . $link
       
   253       . '</span>'
       
   254       . $imgTag
       
   255       . $imgRightTag . '</a>' . ' ';
       
   256 
       
   257     my $isTrigger = 1;
       
   258     my $props     = '';
       
   259 
       
   260     if ( $idTag && $params ) {
       
   261         my $cookieState = _readCookie( $session, $idTag );
       
   262         my @propList =
       
   263           _createHtmlProperties( $twistyControlState, $idTag, undef, $params,
       
   264             $isTrigger, $cookieState );
       
   265         $props = @propList ? " " . join( " ", @propList ) : '';
       
   266     }
       
   267     my $triggerTag = '<span' . $props . '>' . $imgLinkTag . '</span>' . ' ';
       
   268     return $triggerTag;
       
   269 }
       
   270 
       
   271 sub _createHtmlProperties {
       
   272     my ( $twistyControlState, $idTag, $mode, $params, $isTrigger, $cookie ) =
       
   273       @_;
       
   274     my $class      = $params->{'class'}      || '';
       
   275     my $firststart = $params->{'firststart'} || '';
       
   276     my $firstStartHidden;
       
   277     $firstStartHidden = 1 if ( $firststart eq 'hide' );
       
   278     my $firstStartShown;
       
   279     $firstStartShown = 1 if ( $firststart eq 'show' );
       
   280     my $cookieShow;
       
   281     $cookieShow = 1 if defined $cookie && $cookie == 1;
       
   282     my $cookieHide;
       
   283     $cookieHide = 1 if defined $cookie && $cookie == 0;
       
   284     my $start = $params->{start} || '';
       
   285     my $startHidden;
       
   286     $startHidden = 1 if ( $start eq 'hide' );
       
   287     my $startShown;
       
   288     $startShown = 1 if ( $start eq 'show' );
       
   289     my $remember = $params->{'remember'} || $prefRemember;
       
   290     my $noscript = $params->{'noscript'} || '';
       
   291     my $noscriptHide;
       
   292     $noscriptHide = 1 if ( $noscript eq 'hide' );
       
   293     $mode ||= $prefMode;
       
   294 
       
   295     my @classList = ();
       
   296     push( @classList, $class ) if $class && !$isTrigger;
       
   297     push( @classList, 'twistyRememberSetting' ) if ( $remember eq 'on' );
       
   298     push( @classList, 'twistyForgetSetting' )   if ( $remember eq 'off' );
       
   299     push( @classList, 'twistyStartHide' )      if $startHidden;
       
   300     push( @classList, 'twistyStartShow' )      if $startShown;
       
   301     push( @classList, 'twistyFirstStartHide' ) if $firstStartHidden;
       
   302     push( @classList, 'twistyFirstStartShow' ) if $firstStartShown;
       
   303 
       
   304     # Mimic the rules in twist.js, function _update()
       
   305     my $state = '';
       
   306     $state = $TWISTYPLUGIN_CONTENT_HIDDEN if $firstStartHidden;
       
   307     $state = $TWISTYPLUGIN_CONTENT_SHOWN  if $firstStartShown;
       
   308 
       
   309     # cookie setting may override  firstStartHidden and firstStartShown
       
   310     $state = $TWISTYPLUGIN_CONTENT_HIDDEN if $cookieHide;
       
   311     $state = $TWISTYPLUGIN_CONTENT_SHOWN  if $cookieShow;
       
   312 
       
   313     # startHidden and startShown may override cookie
       
   314     $state = $TWISTYPLUGIN_CONTENT_HIDDEN if $startHidden;
       
   315     $state = $TWISTYPLUGIN_CONTENT_SHOWN  if $startShown;
       
   316 
       
   317     # assume trigger should be hidden
       
   318     # unless explicitly said otherwise
       
   319     my $shouldHideTrigger = 1;
       
   320     if ($isTrigger) {
       
   321         push( @classList, 'twistyTrigger twikiUnvisited' );
       
   322 
       
   323         if (   $state eq $TWISTYPLUGIN_CONTENT_SHOWN
       
   324             && $twistyControlState eq 'hide' )
       
   325         {
       
   326             $shouldHideTrigger = 0;
       
   327         }
       
   328         if (   $state eq $TWISTYPLUGIN_CONTENT_HIDDEN
       
   329             && $twistyControlState eq 'show' )
       
   330         {
       
   331             $shouldHideTrigger = 0;
       
   332         }
       
   333         push( @classList, 'twistyHidden' ) if $shouldHideTrigger;
       
   334     }
       
   335 
       
   336     # assume content should be hidden
       
   337     # unless explicitly said otherwise
       
   338     my $shouldHideContent = 1;
       
   339     if ( !$isTrigger ) {
       
   340         push( @classList, 'twistyContent' );
       
   341 
       
   342         if ( $state eq $TWISTYPLUGIN_CONTENT_SHOWN ) {
       
   343             $shouldHideContent = 0;
       
   344         }
       
   345         push( @classList, 'twikiMakeHidden' ) if $shouldHideContent;
       
   346     }
       
   347 
       
   348     # deprecated
       
   349     # should be done by twiki template scripts instead
       
   350     if ( !$isTrigger && $noscriptHide ) {
       
   351         if ( $mode eq 'div' ) {
       
   352             push( @classList, 'twikiMakeVisibleBlock' );
       
   353         }
       
   354         else {
       
   355             push( @classList, 'twikiMakeVisibleInline' );
       
   356         }
       
   357     }
       
   358 
       
   359     # let javascript know we have set the state already
       
   360     push( @classList, 'twistyInited' . $state );
       
   361 
       
   362     my @propList = ();
       
   363     push( @propList, 'id="' . $idTag . '"' );
       
   364     my $classListString = join( " ", @classList );
       
   365     push( @propList, 'class="' . $classListString . '"' );
       
   366     return @propList;
       
   367 }
       
   368 
       
   369 =pod
       
   370 
       
   371 Reads a setting from the TWIKIPREF cookie.
       
   372 Returns:
       
   373    * 1 if the cookie has been set (meaning: show content)
       
   374    * 0 if the cookie is '0' (meaning: hide content)
       
   375    * undef if no cookie has been set
       
   376 
       
   377 =cut
       
   378 
       
   379 sub _readCookie {
       
   380     my ( $session, $idTag ) = @_;
       
   381 
       
   382     return '' if !$idTag;
       
   383 
       
   384     # which state do we use?
       
   385     my $cgi    = new CGI;
       
   386     my $cookie = $cgi->cookie('TWIKIPREF');
       
   387     my $tag    = $idTag;
       
   388     $tag =~ s/^(.*)(hide|show|toggle)$/$1/go;
       
   389     my $key = $TWISTYPLUGIN_COOKIE_PREFIX . $tag;
       
   390 
       
   391     return unless ( defined($key) && defined($cookie) );
       
   392 
       
   393     my $value = '';
       
   394     if ( $cookie =~ m/\b$key\=(.+?)\b/gi ) {
       
   395         $value = $1;
       
   396     }
       
   397 
       
   398     return if $value eq '';
       
   399     return ( $value eq '1' ) ? 1 : 0;
       
   400 }
       
   401 
       
   402 sub _wrapInButtonHtml {
       
   403     my ( $text, $mode ) = @_;
       
   404     return _wrapInContainerHideIfNoJavascripOpen($mode) . "\n" . $text
       
   405       . _wrapInContainerDivIfNoJavascripClose($mode);
       
   406 }
       
   407 
       
   408 sub _wrapInContentHtmlOpen {
       
   409     return '<div style="display:inline;" class="twistyPlugin">';
       
   410 }
       
   411 
       
   412 sub _wrapInContentHtmlClose {
       
   413     return '</div><!--/twistyPlugin-->';
       
   414 }
       
   415 
       
   416 sub _wrapInContainerHideIfNoJavascripOpen {
       
   417     my ($mode) = shift;
       
   418     return '<' . $mode . ' class="twistyPlugin twikiMakeVisibleInline">';
       
   419 }
       
   420 
       
   421 sub _wrapInContainerDivIfNoJavascripClose {
       
   422     my ($mode) = shift;
       
   423     return '</' . $mode . '><!--/twistyPlugin twikiMakeVisibleInline-->';
       
   424 }
       
   425 
       
   426 sub _decodeFormatTokens {
       
   427     my $text = shift;
       
   428     return
       
   429       defined(&TWiki::Func::decodeFormatTokens)
       
   430       ? TWiki::Func::decodeFormatTokens($text)
       
   431       : _expandStandardEscapes($text);
       
   432 }
       
   433 
       
   434 =pod
       
   435 
       
   436 For TWiki versions that do not implement TWiki::Func::decodeFormatTokens.
       
   437 
       
   438 =cut
       
   439 
       
   440 sub _expandStandardEscapes {
       
   441     my $text = shift;
       
   442     $text =~ s/\$n\(\)/\n/gos;    # expand '$n()' to new line
       
   443     my $alpha = TWiki::Func::getRegularExpression('mixedAlpha');
       
   444     $text =~ s/\$n([^$alpha]|$)/\n$1/gos;    # expand '$n' to new line
       
   445     $text =~ s/\$nop(\(\))?//gos;      # remove filler, useful for nested search
       
   446     $text =~ s/\$quot(\(\))?/\"/gos;   # expand double quote
       
   447     $text =~ s/\$percnt(\(\))?/\%/gos; # expand percent
       
   448     $text =~ s/\$dollar(\(\))?/\$/gos; # expand dollar
       
   449     return $text;
       
   450 }
       
   451 
       
   452 1;