1# See bottom of file for license and copyright information
2
3package Foswiki::UI::Preview;
4
5use strict;
6use warnings;
7use Error qw( :try );
8
9use Foswiki                ();
10use Foswiki::UI::Save      ();
11use Foswiki::OopsException ();
12
13use Assert;
14
15BEGIN {
16    if ( $Foswiki::cfg{UseLocale} ) {
17        require locale;
18        import locale();
19    }
20}
21
22sub preview {
23    my $session = shift;
24
25    my $query = $session->{request};
26    my $web   = $session->{webName};
27    my $topic = $session->{topicName};
28    my $user  = $session->{user};
29
30    if ( $session->{invalidTopic} ) {
31        throw Foswiki::OopsException(
32            'accessdenied',
33            status => 404,
34            def    => 'invalid_topic_name',
35            web    => $web,
36            topic  => $topic,
37            params => [ $session->{invalidTopic} ]
38        );
39    }
40
41    # SMELL: it's probably not good to do this here, because a preview may
42    # give enough time for a new topic with the same name to be created.
43    # It would be better to do it only on an actual save.
44
45    $topic = Foswiki::UI::Save::expandAUTOINC( $session, $web, $topic );
46
47    my $topicObject = Foswiki::Meta->new( $session, $web, $topic );
48
49    my ( $saveOpts, $merged ) =
50      Foswiki::UI::Save::buildNewTopic( $session, $topicObject, 'preview' );
51
52    # Note: param(formtemplate) has already been decoded by buildNewTopic
53    # so the $meta entry reflects if it was used.
54    # get form fields to pass on
55    my $formFields = '';
56    my $form = $topicObject->get('FORM') || '';
57    my $formName;
58    if ($form) {
59        $formName = $form->{name};    # used later on as well
60        require Foswiki::Form;
61        my $formDef = new Foswiki::Form( $session, $web, $formName );
62        unless ($formDef) {
63            throw Foswiki::OopsException(
64                'attention',
65                def    => 'no_form_def',
66                web    => $session->{webName},
67                topic  => $session->{topicName},
68                params => [ $web, $formName ]
69            );
70        }
71        $formFields = $formDef->renderHidden( $topicObject, 0 );
72    }
73
74    my $text = $topicObject->text() || '';
75    $session->{plugins}
76      ->dispatch( 'afterEditHandler', $text, $topic, $web, $topicObject );
77
78    # Load the template for the view
79    my $content  = $text;
80    my $template = $session->{prefs}->getPreference('VIEW_TEMPLATE');
81    if ($template) {
82        my $vt = $session->templates->readTemplate( $template, no_oops => 1 );
83        if ($vt) {
84
85            # We can't just use a VIEW_TEMPLATE directly because it
86            # describes an entire HTML page. But the bit we
87            # need is defined by the %TMPL:DEF{"content"}% within it, so
88            # we can just pull it out and instantiate that small bit.
89
90            $content = $session->templates->expandTemplate('content');
91            $content =~ s/%TEXT%/$text/g;
92        }
93    }
94
95    my $tmpl = $session->templates->readTemplate('preview');
96
97    if ( $saveOpts->{minor} ) {
98        $tmpl =~ s/%DONTNOTIFYCHECKBOX%/checked="checked"/g;
99    }
100    else {
101        $tmpl =~ s/%DONTNOTIFYCHECKBOX%//g;
102    }
103    if ( $saveOpts->{forcenewrevision} ) {
104        $tmpl =~ s/%FORCENEWREVISIONCHECKBOX%/checked="checked"/g;
105    }
106    else {
107        $tmpl =~ s/%FORCENEWREVISIONCHECKBOX%//g;
108    }
109    my $saveCmd = Foswiki::entityEncode( $query->param('cmd') || '' );
110    $tmpl =~ s/%CMD%/$saveCmd/g;
111
112    my $redirectTo = Foswiki::entityEncode( $query->param('redirectto') || '' );
113    $tmpl =~ s/%REDIRECTTO%/$redirectTo/g;
114
115    $formName ||= '';
116    $tmpl =~ s/%FORMTEMPLATE%/$formName/g;
117
118    my $parent = $topicObject->get('TOPICPARENT');
119    $parent = $parent->{name} if ($parent);
120    $parent ||= '';
121    $tmpl =~ s/%TOPICPARENT%/$parent/g;
122
123    my $displayText = $content;
124    $displayText = $topicObject->expandMacros($displayText);
125    $displayText = $topicObject->renderTML($displayText);
126
127    # Disable links and inputs in the text
128    # SMELL: This will break on <a name="blah />
129    # XXX - Use a real HTML parser like HTML::Parser
130    $displayText =~ s#(<a\s[^>]*>)(.*?)(</a>)#_disableLink($1, $2, $3)#gies
131      ;    # Disables base relative links
132    $displayText =~ s#(<a\s[^>]*>)(.*?)(</a>)#_reTargetLink($1, $2, $3)#gies
133      ;    # Retargets remaining links
134    $displayText =~ s/<(input|button|textarea) /<$1 disabled="disabled" /gis;
135    $displayText =~ s(</?form(|\s.*?)>)()gis;
136    $displayText =~ s/(<[^>]*\bon[A-Za-z]+=)('[^']*'|"[^"]*")/$1''/gis;
137
138    # let templates know the context so they can act on it
139    $session->enterContext( 'preview', 1 );
140
141    # note: preventing linkage in rendered form can only happen in templates
142    # see formtables.tmpl
143
144    my $originalrev =
145      Foswiki::entityEncode( $query->param('originalrev') || '' )
146      ;    # rev edit started on
147
148    #ASSERT($originalrev ne '%ORIGINALREV%') if DEBUG;
149    $tmpl =~ s/%ORIGINALREV%/$originalrev/g;
150
151    my $templatetopic =
152      Foswiki::entityEncode( $query->param('templatetopic') || '' );
153
154    #ASSERT($templatetopic ne '%TEMPLATETOPIC%') if DEBUG;
155    $tmpl =~ s/%TEMPLATETOPIC%/$templatetopic/g;
156
157    #this one's worrying, its special, and not set much at all
158    #$tmpl =~ s/%SETTINGSTOPIC%/$settingstopic/g;
159    my $newtopic = Foswiki::entityEncode( $query->param('newtopic') || '' );
160
161    #ASSERT($newtopic ne '%NEWTOPIC%') if DEBUG;
162    $tmpl =~ s/%NEWTOPIC%/$newtopic/g;
163
164# CAUTION: Once expandMacros executes, any template tokens that are expanded
165# inside a %ENCODE will be corrupted.  So do token substitution before this point.
166    $tmpl = $topicObject->expandMacros($tmpl);
167    $tmpl = $topicObject->renderTML($tmpl);
168    $tmpl =~ s/%TEXT%/$displayText/g;
169
170    # write the hidden form fields
171    $tmpl =~ s/%FORMFIELDS%/$formFields/g;
172
173    # SMELL: this should be done using CGI::hidden
174    $text = Foswiki::entityEncode( $text, "\n" );
175
176    $tmpl =~ s/%HIDDENTEXT%/$text/g;
177
178    $tmpl =~ s/<\/?(nop|noautolink)\/?>//gis;
179
180###
181    $session->writeCompletePage($tmpl);
182}
183
184sub _reTargetLink {
185    my ( $one, $two, $three ) = @_;
186
187    unless ( $one =~ m/foswikiEmulatedLink/ ) {
188        if ( $one =~ m/\btarget=/i ) {
189            $one =~
190s/\btarget=(?:(?: \'[^\']*\' | \"[^\"]*\" | [^\'\"\s]+ )+)(.*?>)/target="_blank"$1/xi;
191        }
192        else {
193            $one =~ s/\bhref=/target="_blank" href=/;
194        }
195    }
196    return $one . $two . $three;
197}
198
199sub _disableLink {
200    my ( $one, $two, $three ) = @_;
201
202    if ( $one =~ m/\bhref=['"][#?]/i ) {    #Anchors or relative links
203        $one   = "<span class=\"foswikiEmulatedLink\">";
204        $three = "</span>";
205    }
206    return $one . $two . $three;
207}
208
2091;
210__END__
211Foswiki - The Free and Open Source Wiki, http://foswiki.org/
212
213Copyright (C) 2008-2010 Foswiki Contributors. Foswiki Contributors
214are listed in the AUTHORS file in the root of this distribution.
215NOTE: Please extend that file, not this notice.
216
217Additional copyrights apply to some or all of the code in this
218file as follows:
219
220Copyright (C) 1999-2007 Peter Thoeny, peter@thoeny.org
221and TWiki Contributors. All Rights Reserved. TWiki Contributors
222are listed in the AUTHORS file in the root of this distribution.
223
224This program is free software; you can redistribute it and/or
225modify it under the terms of the GNU General Public License
226as published by the Free Software Foundation; either version 2
227of the License, or (at your option) any later version. For
228more details read LICENSE in the root of this distribution.
229
230This program is distributed in the hope that it will be useful,
231but WITHOUT ANY WARRANTY; without even the implied warranty of
232MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
233
234As per the GPL, removal of this notice is prohibited.
235