lib/TWiki/UI/View.pm
author Colas Nahaboo <colas@nahaboo.net>
Sat, 26 Jan 2008 15:50:53 +0100
changeset 0 414e01d06fd5
child 1 e2915a7cbdfa
permissions -rw-r--r--
RELEASE 4.2.0 freetown
colas@0
     1
# Module of TWiki Enterprise Collaboration Platform, http://TWiki.org/
colas@0
     2
#
colas@0
     3
# Copyright (C) 1999-2007 Peter Thoeny, peter@thoeny.org
colas@0
     4
# and TWiki Contributors. All Rights Reserved. TWiki Contributors
colas@0
     5
# are listed in the AUTHORS file in the root of this distribution.
colas@0
     6
# NOTE: Please extend that file, not this notice.
colas@0
     7
#
colas@0
     8
# Additional copyrights apply to some or all of the code in this
colas@0
     9
# file as follows:
colas@0
    10
# Based on parts of Ward Cunninghams original Wiki and JosWiki.
colas@0
    11
# Copyright (C) 1998 Markus Peter - SPiN GmbH (warpi@spin.de)
colas@0
    12
# Some changes by Dave Harris (drh@bhresearch.co.uk) incorporated
colas@0
    13
#
colas@0
    14
# This program is free software; you can redistribute it and/or
colas@0
    15
# modify it under the terms of the GNU General Public License
colas@0
    16
# as published by the Free Software Foundation; either version 2
colas@0
    17
# of the License, or (at your option) any later version. For
colas@0
    18
# more details read LICENSE in the root of this distribution.
colas@0
    19
#
colas@0
    20
# This program is distributed in the hope that it will be useful,
colas@0
    21
# but WITHOUT ANY WARRANTY; without even the implied warranty of
colas@0
    22
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
colas@0
    23
#
colas@0
    24
# As per the GPL, removal of this notice is prohibited.
colas@0
    25
colas@0
    26
=pod
colas@0
    27
colas@0
    28
---+ package TWiki::UI::View
colas@0
    29
colas@0
    30
UI delegate for view function
colas@0
    31
colas@0
    32
=cut
colas@0
    33
colas@0
    34
package TWiki::UI::View;
colas@0
    35
colas@0
    36
use strict;
colas@0
    37
use integer;
colas@0
    38
use Monitor;
colas@0
    39
colas@0
    40
use CGI::Carp qw( fatalsToBrowser );
colas@0
    41
use CGI qw( -any ); # pretty basic, this
colas@0
    42
colas@0
    43
require TWiki;
colas@0
    44
require TWiki::UI;
colas@0
    45
require TWiki::Sandbox;
colas@0
    46
require TWiki::OopsException;
colas@0
    47
colas@0
    48
=pod
colas@0
    49
colas@0
    50
---++ StaticMethod view( $session )
colas@0
    51
colas@0
    52
=view= command handler.
colas@0
    53
This method is designed to be
colas@0
    54
invoked via the =UI::run= method.
colas@0
    55
colas@0
    56
Generate a complete HTML page that represents the viewed topics.
colas@0
    57
The view is controlled by CGI parameters as follows:
colas@0
    58
colas@0
    59
| =rev= | topic revision to view |
colas@0
    60
| =section= | restrict view to a named section |
colas@0
    61
| =raw= | no format body text if set |
colas@0
    62
| =skin= | comma-separated list of skin(s) to use |
colas@0
    63
| =contenttype= | Allows you to specify an alternate content type |
colas@0
    64
colas@0
    65
=cut
colas@0
    66
colas@0
    67
sub view {
colas@0
    68
    my $session = shift;
colas@0
    69
colas@0
    70
    my $query = $session->{cgiQuery};
colas@0
    71
    my $webName = $session->{webName};
colas@0
    72
    my $topicName = $session->{topicName};
colas@0
    73
colas@0
    74
    my $raw = $query->param( 'raw' ) || '';
colas@0
    75
    my $contentType = $query->param( 'contenttype' );
colas@0
    76
colas@0
    77
    my $showRev = 1;
colas@0
    78
    my $logEntry = '';
colas@0
    79
    my $revdate = '';
colas@0
    80
    my $revuser = '';
colas@0
    81
    my $store = $session->{store};
colas@0
    82
    # is this view indexable by search engines? Default yes.
colas@0
    83
    my $indexableView = 1;
colas@0
    84
colas@0
    85
    TWiki::UI::checkWebExists( $session, $webName, $topicName, 'view' );
colas@0
    86
colas@0
    87
    my $skin = $session->getSkin();
colas@0
    88
colas@0
    89
    my $rev = $store->cleanUpRevID( $query->param( 'rev' ));
colas@0
    90
colas@0
    91
    my $topicExists =
colas@0
    92
      $store->topicExists( $webName, $topicName );
colas@0
    93
colas@0
    94
    # text and meta of the _latest_ rev of the topic
colas@0
    95
    my( $currText, $currMeta );
colas@0
    96
    # text and meta of the chosen rev of the topic
colas@0
    97
    my( $meta, $text );
colas@0
    98
    if( $topicExists ) {
colas@0
    99
        require TWiki::Time;
colas@0
   100
        ( $currMeta, $currText ) = $store->readTopic
colas@0
   101
          ( $session->{user}, $webName, $topicName, undef );
colas@0
   102
        TWiki::UI::checkAccess( $session, $webName, $topicName,
colas@0
   103
                                'VIEW', $session->{user}, $currText );
colas@0
   104
        ( $revdate, $revuser, $showRev ) = $currMeta->getRevisionInfo();
colas@0
   105
        $revdate = TWiki::Time::formatTime( $revdate );
colas@0
   106
colas@0
   107
        if ( !$rev || $rev > $showRev ) {
colas@0
   108
            $rev = $showRev;
colas@0
   109
        }
colas@0
   110
colas@0
   111
        if( $rev < $showRev ) {
colas@0
   112
            ( $meta, $text ) = $store->readTopic
colas@0
   113
              ( $session->{user}, $webName, $topicName, $rev );
colas@0
   114
colas@0
   115
            ( $revdate, $revuser ) = $meta->getRevisionInfo();
colas@0
   116
            $revdate = TWiki::Time::formatTime( $revdate );
colas@0
   117
            $logEntry .= 'r'.$rev;
colas@0
   118
        } else {
colas@0
   119
            # viewing the most recent rev
colas@0
   120
            ( $text, $meta ) = ( $currText, $currMeta );
colas@0
   121
        }
colas@0
   122
colas@0
   123
        # So we're reading an existing topic here.  It is about time
colas@0
   124
        # to apply the 'section' selection (and maybe others in the
colas@0
   125
        # future as well).  $text is cleared unless a named section
colas@0
   126
        # matching the 'section' URL parameter is found.
colas@0
   127
        if (my $section  =  $query->param('section')) {
colas@0
   128
            my ( $ntext, $sections ) = TWiki::parseSections( $text );
colas@0
   129
            $text = ''; # in the beginning, there was ... NO section
colas@0
   130
          FINDSECTION:
colas@0
   131
            for my $s (@$sections) {
colas@0
   132
                if ($s->{type} eq 'section'  &&  $s->{name} eq $section) {
colas@0
   133
                    $text = substr( $ntext, $s->{start}, $s->{end}-$s->{start} );
colas@0
   134
                    last FINDSECTION;
colas@0
   135
                }
colas@0
   136
            }
colas@0
   137
        }
colas@0
   138
colas@0
   139
    } else { # Topic does not exist yet
colas@0
   140
        $indexableView = 0;
colas@0
   141
        $session->enterContext( 'new_topic' );
colas@0
   142
        $rev = 1;
colas@0
   143
        if( TWiki::isValidTopicName( $topicName )) {
colas@0
   144
            ( $currMeta, $currText ) =
colas@0
   145
              TWiki::UI::readTemplateTopic( $session, 'WebTopicViewTemplate' );
colas@0
   146
        } else {
colas@0
   147
            ( $currMeta, $currText ) =
colas@0
   148
              TWiki::UI::readTemplateTopic( $session, 'WebTopicNonWikiTemplate' );
colas@0
   149
        }
colas@0
   150
        ( $text, $meta ) = ( $currText, $currMeta );
colas@0
   151
        $logEntry .= ' (not exist)';
colas@0
   152
    }
colas@0
   153
colas@0
   154
    if( $raw ) {
colas@0
   155
        $indexableView = 0;
colas@0
   156
        $logEntry .= ' raw='.$raw;
colas@0
   157
        if( $raw eq 'debug' || $raw eq 'all' ) {
colas@0
   158
            $text = $store->getDebugText( $meta, $text );
colas@0
   159
        }
colas@0
   160
    }
colas@0
   161
colas@0
   162
    if( $TWiki::cfg{Log}{view} ) {
colas@0
   163
        $session->writeLog( 'view', $webName.'.'.$topicName, $logEntry );
colas@0
   164
    }
colas@0
   165
colas@0
   166
    my( $mirrorSiteName, $mirrorViewURL, $mirrorLink, $mirrorNote ) =
colas@0
   167
      $session->readOnlyMirrorWeb( $webName );
colas@0
   168
colas@0
   169
    # Note; must enter all contexts before the template is read, as
colas@0
   170
    # TMPL:P is expanded on the fly in the template reader. :-(
colas@0
   171
    my( $revTitle, $revArg ) = ( '', '' );
colas@0
   172
    if( $mirrorSiteName ) {
colas@0
   173
        $session->enterContext( 'inactive' );
colas@0
   174
        unless( $topicExists ) {
colas@0
   175
            $text = '';
colas@0
   176
        }
colas@0
   177
    } elsif( $rev < $showRev ) {
colas@0
   178
        $session->enterContext( 'inactive' );
colas@0
   179
        # disable edit of previous revisions
colas@0
   180
        $revTitle = '(r'.$rev.')';
colas@0
   181
        $revArg = '&rev='.$rev;
colas@0
   182
    }
colas@0
   183
colas@0
   184
    my $template = $query->param( 'template' ) ||
colas@0
   185
      $session->{prefs}->getPreferencesValue( 'VIEW_TEMPLATE' ) ||
colas@0
   186
        'view';
colas@0
   187
colas@0
   188
    # Item4756: Always use default view template for raw=debug and raw=all
colas@0
   189
    if( $raw eq 'debug' || $raw eq 'all' ) {
colas@0
   190
        $template = 'view';
colas@0
   191
    }
colas@0
   192
colas@0
   193
    my $tmpl = $session->templates->readTemplate( $template, $skin );
colas@0
   194
    if( !$tmpl && $template ne 'view' ) {
colas@0
   195
        $tmpl = $session->templates->readTemplate( 'view', $skin );
colas@0
   196
    }
colas@0
   197
colas@0
   198
    if( !$tmpl ) {
colas@0
   199
        throw TWiki::OopsException( 'attention',
colas@0
   200
                                    def => 'no_such_template',
colas@0
   201
                                    web => $webName,
colas@0
   202
                                    topic => $topicName,
colas@0
   203
                                    params => [ $template, 'VIEW_TEMPLATE' ] );
colas@0
   204
    }
colas@0
   205
colas@0
   206
    $tmpl =~ s/%REVINFO%/%REVINFO%$mirrorNote/go;
colas@0
   207
    $tmpl =~ s/%REVTITLE%/$revTitle/g;
colas@0
   208
    $tmpl =~ s/%REVARG%/$revArg/g;
colas@0
   209
colas@0
   210
    if( $indexableView &&
colas@0
   211
          $TWiki::cfg{AntiSpam}{RobotsAreWelcome} &&
colas@0
   212
            !$query->param() ) {
colas@0
   213
        # it's an indexable view type, there are no parameters
colas@0
   214
        # on the url, and robots are welcome. Remove the NOINDEX meta tag
colas@0
   215
        $tmpl =~ s/<meta name="robots"[^>]*>//goi;
colas@0
   216
    }
colas@0
   217
colas@0
   218
    # Show revisions around the one being displayed
colas@0
   219
    # we start at $showRev then possibly jump near $rev if too distant
colas@0
   220
    my $revsToShow = $TWiki::cfg{NumberOfRevisions} + 1;
colas@0
   221
    $revsToShow = $showRev if $showRev < $revsToShow;
colas@0
   222
    my $doingRev = $showRev;
colas@0
   223
    my $revs = '';
colas@0
   224
    while( $revsToShow > 0 ) {
colas@0
   225
        $revsToShow--;
colas@0
   226
        if( $doingRev == $rev) {
colas@0
   227
            $revs .= 'r'.$rev;
colas@0
   228
        } else {
colas@0
   229
            $revs .= CGI::a({
colas@0
   230
                             href=>$session->getScriptUrl( 0,
colas@0
   231
                                                           'view',
colas@0
   232
                                                           $webName,
colas@0
   233
                                                           $topicName,
colas@0
   234
                                                           rev => $doingRev ),
colas@0
   235
                             rel => 'nofollow'
colas@0
   236
                            },
colas@0
   237
                            "r$doingRev" );
colas@0
   238
        }
colas@0
   239
        if ( $doingRev - $rev >= $TWiki::cfg{NumberOfRevisions} ) {
colas@0
   240
            # we started too far away, need to jump closer to $rev
colas@0
   241
            use integer;
colas@0
   242
            $doingRev = $rev + $revsToShow / 2;
colas@0
   243
            $doingRev = $revsToShow if $revsToShow > $doingRev;
colas@0
   244
            $revs .= ' | ';
colas@0
   245
            next;
colas@0
   246
        }
colas@0
   247
        if( $revsToShow ) {
colas@0
   248
            $revs .= '&nbsp;' . CGI::a
colas@0
   249
              ( { href=>$session->getScriptUrl(
colas@0
   250
                  0, 'rdiff', $webName, $topicName,
colas@0
   251
                  rev1 => $doingRev,
colas@0
   252
                  rev2 => $doingRev-1 ),
colas@0
   253
                  rel => 'nofollow' },
colas@0
   254
                '&lt;' ) . '&nbsp;';
colas@0
   255
        }
colas@0
   256
        $doingRev--;
colas@0
   257
    }
colas@0
   258
colas@0
   259
    my $ri = $session->renderer->renderRevisionInfo( $webName,
colas@0
   260
                                                     $topicName,
colas@0
   261
                                                     $meta );
colas@0
   262
    $tmpl =~ s/%REVINFO%/$ri/go;
colas@0
   263
    $tmpl =~ s/%REVISIONS%/$revs/go;
colas@0
   264
colas@0
   265
    ## SMELL: This is also used in TWiki::_TOC. Could insert a tag in
colas@0
   266
    ## TOC and remove all those here, finding the parameters only once
colas@0
   267
    my @qparams = ();
colas@0
   268
    foreach my $name ( $query->param ) {
colas@0
   269
      next if ($name eq 'keywords');
colas@0
   270
      next if ($name eq 'topic');
colas@0
   271
      push @qparams, $name => $query->param($name);
colas@0
   272
    }
colas@0
   273
    $tmpl =~ s/%QUERYPARAMSTRING%/TWiki::_make_params(1,@qparams)/geo;
colas@0
   274
colas@0
   275
    # extract header and footer from the template, if there is a
colas@0
   276
    # %TEXT% tag marking the split point. The topic text is inserted
colas@0
   277
    # in place of the %TEXT% tag. The text before this tag is inserted
colas@0
   278
    # as header, the text after is inserted as footer. If there is a
colas@0
   279
    # %STARTTEXT% tag present, the header text between %STARTTEXT% and
colas@0
   280
    # %TEXT is rendered together, as is the footer text between %TEXT%
colas@0
   281
    # and %ENDTEXT%, if present. This allows correct handling of TWiki
colas@0
   282
    # markup in header or footer if those do require examination of the
colas@0
   283
    # topic text to work correctly (e.g., %TOC%).
colas@0
   284
    # Note: This feature is experimental and may be replaced by an
colas@0
   285
    # alternative solution not requiring additional tags.
colas@0
   286
    my( $start, $end );
colas@0
   287
    if( $tmpl =~ m/^(.*)%TEXT%(.*)$/s ) {
colas@0
   288
        my @starts = split( /%STARTTEXT%/, $1 );
colas@0
   289
        if ( $#starts > 0 ) {
colas@0
   290
            # we know that there is something before %STARTTEXT%
colas@0
   291
            $start = $starts[0];
colas@0
   292
            $text = $starts[1] . $text;
colas@0
   293
        } else {
colas@0
   294
            $start = $1;
colas@0
   295
        }
colas@0
   296
        my @ends = split( /%ENDTEXT%/, $2 );
colas@0
   297
        if ( $#ends > 0 ) {
colas@0
   298
            # we know that there is something after %ENDTEXT%
colas@0
   299
            $text .= $ends[0];
colas@0
   300
            $end = $ends[1];
colas@0
   301
        } else {
colas@0
   302
            $end = $2;
colas@0
   303
        }
colas@0
   304
    } else {
colas@0
   305
        my @starts = split( /%STARTTEXT%/, $tmpl );
colas@0
   306
        if ( $#starts > 0 ) {
colas@0
   307
            # we know that there is something before %STARTTEXT%
colas@0
   308
            $start = $starts[0];
colas@0
   309
            $text = $starts[1];
colas@0
   310
        } else {
colas@0
   311
            $start = $tmpl;
colas@0
   312
            $text = '';
colas@0
   313
        }
colas@0
   314
        $end = '';
colas@0
   315
    }
colas@0
   316
colas@0
   317
    # If minimalist is set, images and anchors will be stripped from text
colas@0
   318
    my $minimalist = 0;
colas@0
   319
    if( $contentType ) {
colas@0
   320
        $minimalist = ( $skin =~ /\brss/ );
colas@0
   321
    } elsif( $skin =~ /\brss/ ) {
colas@0
   322
        $contentType = 'text/xml';
colas@0
   323
        $minimalist = 1;
colas@0
   324
    } elsif( $skin =~ /\bxml/ ) {
colas@0
   325
        $contentType = 'text/xml';
colas@0
   326
        $minimalist = 1;
colas@0
   327
    } elsif( $raw eq 'text' || $raw eq 'all' ) {
colas@0
   328
        $contentType = 'text/plain';
colas@0
   329
    } else {
colas@0
   330
        $contentType = 'text/html'
colas@0
   331
    }
colas@0
   332
    $session->{SESSION_TAGS}{MAXREV} = $showRev;
colas@0
   333
    $session->{SESSION_TAGS}{CURRREV} = $rev;
colas@0
   334
colas@0
   335
    # Set page generation mode to RSS if using an RSS skin
colas@0
   336
    $session->enterContext( 'rss' ) if $skin =~ /\brss/;
colas@0
   337
colas@0
   338
    # Set the meta-object that contains the rendering info
colas@0
   339
    # SMELL: hack to get around not having a proper topic object model
colas@0
   340
    $session->enterContext( 'can_render_meta', $meta );
colas@0
   341
colas@0
   342
    my $page;
colas@0
   343
    # Legacy: If the _only_ skin is 'text' it is used like this:
colas@0
   344
    # http://.../view/Codev/MyTopic?skin=text&contenttype=text/plain&raw=on
colas@0
   345
    # which shows the topic as plain text; useful for those who want
colas@0
   346
    # to download plain text for the topic. So when the skin is 'text'
colas@0
   347
    # we do _not_ want to create a textarea.
colas@0
   348
    # raw=on&skin=text is deprecated; use raw=text instead.
colas@0
   349
    Monitor::MARK('Ready to render');
colas@0
   350
    if( $raw eq 'text' || $raw eq 'all' || ( $raw && $skin eq 'text' )) {
colas@0
   351
        # use raw text
colas@0
   352
        $page = $text;
colas@0
   353
    } else {
colas@0
   354
        my @args = ( $session, $webName, $topicName, $meta, $minimalist );
colas@0
   355
colas@0
   356
        $session->enterContext( 'header_text' );
colas@0
   357
        $page = _prepare($start, @args);
colas@0
   358
        $session->leaveContext( 'header_text' );
colas@0
   359
        Monitor::MARK('Rendered header');
colas@0
   360
colas@0
   361
        if( $raw ) {
colas@0
   362
            if ($text) {
colas@0
   363
                my $p = $session->{prefs};
colas@0
   364
                $page .=
colas@0
   365
                  CGI::textarea(
colas@0
   366
                      -readonly => 'readonly',
colas@0
   367
                      -rows => $p->getPreferencesValue('EDITBOXHEIGHT'),
colas@0
   368
                      -cols => $p->getPreferencesValue('EDITBOXWIDTH'),
colas@0
   369
                      -style => $p->getPreferencesValue('EDITBOXSTYLE'),
colas@0
   370
                      -class => 'twikiTextarea twikiTextareaRawView',
colas@0
   371
                      -id => 'topic',
colas@0
   372
                      -default => $text
colas@0
   373
                     );
colas@0
   374
            }
colas@0
   375
        } else {
colas@0
   376
            $session->enterContext( 'body_text' );
colas@0
   377
            $page .= _prepare($text, @args);
colas@0
   378
            $session->leaveContext( 'body_text' );
colas@0
   379
        }
colas@0
   380
colas@0
   381
        Monitor::MARK('Rendered body');
colas@0
   382
        $session->enterContext( 'footer_text' );
colas@0
   383
        $page .= _prepare($end, @args);
colas@0
   384
        $session->leaveContext( 'footer_text' );
colas@0
   385
        Monitor::MARK('Rendered footer');
colas@0
   386
    }
colas@0
   387
    # Output has to be done in one go, because if we generate the header and
colas@0
   388
    # then redirect because of some later constraint, some browsers fall over
colas@0
   389
    $session->writeCompletePage( $page, 'view', $contentType );
colas@0
   390
    Monitor::MARK('Wrote HTML');
colas@0
   391
}
colas@0
   392
colas@0
   393
sub _prepare {
colas@0
   394
    my( $text, $session, $webName, $topicName, $meta, $minimalist) = @_;
colas@0
   395
colas@0
   396
    $text = $session->handleCommonTags( $text, $webName, $topicName, $meta );
colas@0
   397
    $text = $session->renderer->getRenderedVersion( $text, $webName, $topicName );
colas@0
   398
    $text =~ s/( ?) *<\/?(nop|noautolink)\/?>\n?/$1/gois;
colas@0
   399
colas@0
   400
    if( $minimalist ) {
colas@0
   401
        $text =~ s/<img [^>]*>//gi;  # remove image tags
colas@0
   402
        $text =~ s/<a [^>]*>//gi;    # remove anchor tags
colas@0
   403
        $text =~ s/<\/a>//gi;        # remove anchor tags
colas@0
   404
    }
colas@0
   405
colas@0
   406
    return $text;
colas@0
   407
}
colas@0
   408
colas@0
   409
=pod
colas@0
   410
colas@0
   411
---++ StaticMethod viewfile( $session, $web, $topic, $query )
colas@0
   412
colas@0
   413
=viewfile= command handler.
colas@0
   414
This method is designed to be
colas@0
   415
invoked via the =UI::run= method.
colas@0
   416
Command handler for viewfile. View a file in the browser.
colas@0
   417
Some parameters are passed in CGI query:
colas@0
   418
| =filename= | Attachment to view |
colas@0
   419
| =rev= | Revision to view |
colas@0
   420
colas@0
   421
=cut
colas@0
   422
colas@0
   423
sub viewfile {
colas@0
   424
    my $session = shift;
colas@0
   425
colas@0
   426
    my $query = $session->{cgiQuery};
colas@0
   427
colas@0
   428
    my $topic = $session->{topicName};
colas@0
   429
    my $webName = $session->{webName};
colas@0
   430
colas@0
   431
    my @path = split( '/', $query->path_info() );
colas@0
   432
    shift( @path )unless $path[0];
colas@0
   433
    my $fileName;
colas@0
   434
    if( defined( $query->param( 'filename' ))) {
colas@0
   435
        $fileName = $query->param( 'filename' );
colas@0
   436
    } else {
colas@0
   437
        $fileName = pop( @path );
colas@0
   438
    }
colas@0
   439
    if (!$fileName) {
colas@0
   440
        throw TWiki::OopsException( 'attention',
colas@0
   441
                                    def => 'no_such_attachment',
colas@0
   442
                                    web => 'Unknown',
colas@0
   443
                                    topic => 'Unknown',
colas@0
   444
                                    params => [ 'viewfile', '?' ] );
colas@0
   445
    }
colas@0
   446
colas@0
   447
    $fileName = TWiki::Sandbox::sanitizeAttachmentName( $fileName );
colas@0
   448
colas@0
   449
    my $rev = $session->{store}->cleanUpRevID( $query->param( 'rev' ) );
colas@0
   450
    unless( $fileName && $session->{store}->attachmentExists(
colas@0
   451
        $webName, $topic, $fileName )) {
colas@0
   452
        throw TWiki::OopsException( 'attention',
colas@0
   453
                                    def => 'no_such_attachment',
colas@0
   454
                                    web => $webName,
colas@0
   455
                                    topic => $topic,
colas@0
   456
                                    params => [ 'viewfile', $fileName||'?' ] );
colas@0
   457
    }
colas@0
   458
    my $fileContent = $session->{store}->readAttachment(
colas@0
   459
        $session->{user}, $webName, $topic, $fileName, $rev );
colas@0
   460
colas@0
   461
    my $type = _suffixToMimeType( $session, $fileName );
colas@0
   462
    my $length =  length( $fileContent );
colas@0
   463
    my $dispo = 'inline;filename='.$fileName;
colas@0
   464
colas@0
   465
    print <<HERE;
colas@0
   466
Content-type: $type
colas@0
   467
Content-length: $length
colas@0
   468
Content-Disposition: $dispo
colas@0
   469
colas@0
   470
$fileContent
colas@0
   471
HERE
colas@0
   472
}
colas@0
   473
colas@0
   474
sub _suffixToMimeType {
colas@0
   475
    my( $session, $theFilename ) = @_;
colas@0
   476
colas@0
   477
    my $mimeType = 'text/plain';
colas@0
   478
    if( $theFilename =~ /\.([^.]+)$/ ) {
colas@0
   479
        my $suffix = $1;
colas@0
   480
        my @types = grep{ s/^\s*([^\s]+).*?\s$suffix\s.*$/$1/i }
colas@0
   481
          map{ $_.' ' }
colas@0
   482
            split( /[\n\r]/,
colas@0
   483
                   TWiki::readFile( $TWiki::cfg{MimeTypesFileName} ) );
colas@0
   484
        $mimeType = $types[0] if( @types );
colas@0
   485
    }
colas@0
   486
    return $mimeType;
colas@0
   487
}
colas@0
   488
colas@0
   489
1;