wiki-archive/twiki/lib/TWiki/UI/Edit.pm

379 lines
14 KiB
Perl

# Module of TWiki Enterprise Collaboration Platform, http://TWiki.org/
#
# Copyright (C) 1999-2007 Peter Thoeny, peter@thoeny.org
# and TWiki Contributors. All Rights Reserved. TWiki Contributors
# are listed in the AUTHORS file in the root of this distribution.
# NOTE: Please extend that file, not this notice.
#
# Additional copyrights apply to some or all of the code in this
# file as follows:
# Based on parts of Ward Cunninghams original Wiki and JosWiki.
# Copyright (C) 1998 Markus Peter - SPiN GmbH (warpi@spin.de)
# Some changes by Dave Harris (drh@bhresearch.co.uk) incorporated
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. For
# more details read LICENSE in the root of this distribution.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# As per the GPL, removal of this notice is prohibited.
=begin twiki
---+ package TWiki::UI::Edit
Edit command handler
=cut
package TWiki::UI::Edit;
use strict;
use Assert;
use TWiki;
use TWiki::Form;
use TWiki::Plugins;
use TWiki::Prefs;
use TWiki::Store;
use TWiki::UI;
use Error qw( :try );
use TWiki::OopsException;
use CGI qw( -any );
=pod
---++ StaticMethod edit( $session )
Edit command handler.
This method is designed to be
invoked via the =TWiki::UI::run= method.
Most parameters are in the CGI query:
| =cmd= | Undocumented save command, passed on to save script |
| =onlywikiname= | if defined, requires a wiki name for the topic name if this is a new topic |
| =onlynewtopic= | if defined, and the topic exists, then moans |
| =formtemplate= | name of the form for the topic; will replace existing form |
| =templatetopic= | name of the topic to copy if creating a new topic |
| =skin= | skin(s) to use |
| =topicparent= | what to put in the topic prent meta data |
| =text= | text that will replace the old topic text if a formtemplate is defined (what the heck is this for?) |
| =contenttype= | optional parameter that defines the application type to write into the CGI header. Defaults to text/html. |
| =action= | Optional. If supplied, use the edit${action} template instead of the standard edit template. An empty value means edit both form and text, "form" means edit form only, "text" means edit text only |
=cut
sub edit {
my $session = shift;
my ( $text, $tmpl ) = init_edit( $session, 'edit' );
finalize_edit ( $session, $text, $tmpl );
}
sub init_edit {
my ( $session, $templateName ) = @_;
my $query = $session->{cgiQuery};
my $webName = $session->{webName};
my $topic = $session->{topicName};
my $user = $session->{user};
# empty means edit both form and text, "form" means edit form only,
# "text" means edit text only
my $editaction = lc($query->param( 'action' )) || "";
my $saveCmd = $query->param( 'cmd' ) || '';
my $redirectTo = $query->param( 'redirectto' ) || '';
my $onlyWikiName = TWiki::isTrue( $query->param( 'onlywikiname' ));
my $onlyNewTopic = TWiki::isTrue( $query->param( 'onlynewtopic' ));
my $formTemplate = $query->param( 'formtemplate' ) || '';
my $templateTopic = $query->param( 'templatetopic' ) || '';
# apptype is undocumented legacy
my $cgiAppType = $query->param( 'contenttype' ) ||
$query->param( 'apptype' ) || 'text/html';
my $skin = $session->getSkin();
my $theParent = $query->param( 'topicparent' ) || '';
my $ptext = $query->param( 'text' );
my $store = $session->{store};
TWiki::UI::checkWebExists( $session, $webName, $topic, 'edit' );
TWiki::UI::checkMirror( $session, $webName, $topic );
my $tmpl = '';
my $text = '';
my $meta = '';
my $extra = '';
my $topicExists = $store->topicExists( $webName, $topic );
# If you want to edit, you have to be able to view and change.
TWiki::UI::checkAccess( $session, $webName, $topic, 'view', $user );
TWiki::UI::checkAccess( $session, $webName, $topic, 'change', $user );
# Check lease, unless we have been instructed to ignore it
# or if we are using the 10X's topic name for dynamic topic names
my $breakLock = $query->param( 'breaklock' ) || '';
unless( $breakLock || ($topic =~ /X{10}/ )) {
my $lease = $store->getLease( $webName, $topic );
if( $lease ) {
my $who = $lease->{user}->webDotWikiName();
if( $who ne $user->webDotWikiName() ) {
# redirect; we are trying to break someone else's lease
my( $future, $past );
my $why = $lease->{message};
my $def;
my $t = time();
if( $t > $lease->{expires} ) {
# The lease has expired, but see if we are still
# expected to issue a "less forceful' warning
if( $TWiki::cfg{LeaseLengthLessForceful} < 0 ||
$t < $lease->{expires} +
$TWiki::cfg{LeaseLengthLessForceful} ) {
$def = 'lease_old';
$past = TWiki::Time::formatDelta(
$t - $lease->{expires}, $session->{i18n} );
$future = '';
}
}
else {
# The lease is active
$def = 'lease_active';
$past = TWiki::Time::formatDelta(
$t - $lease->{taken}, $session->{i18n} );
$future = TWiki::Time::formatDelta(
$lease->{expires} - $t, $session->{i18n} );
}
if( $def ) {
# use a 'keep' redirect to ensure we pass parameter
# values in the query on to the oops script
throw TWiki::OopsException( 'leaseconflict',
def => $def,
web => $webName,
topic => $topic,
keep => 1,
params =>
[ $who, $past, $future, 'edit' ] );
}
}
}
}
# Prevent editing existing topic?
if( $onlyNewTopic && $topicExists ) {
# Topic exists and user requested oops if it exists
throw TWiki::OopsException( 'attention',
def => 'topic_exists',
web => $webName,
topic => $topic );
}
# prevent non-Wiki names?
if( ( $onlyWikiName )
&& ( ! $topicExists )
&& ( ! TWiki::isValidTopicName( $topic ) ) ) {
# do not allow non-wikinames
throw TWiki::OopsException( 'attention',
def => 'not_wikiword',
web => $webName,
topic => $topic,
params => [ $topic ] );
}
if( $topicExists ) {
( $meta, $text ) =
$store->readTopic( undef, $webName, $topic, undef );
}
if( $saveCmd && ! $session->{user}->isAdmin()) {
throw TWiki::OopsException( 'accessdenied', def=>'only_group',
web => $webName, topic => $topic,
params => $TWiki::cfg{UsersWebName}.
'.'.$TWiki::cfg{SuperAdminGroup} );
}
# Get edit template, standard or a different skin
my $template = $query->param( 'template' ) ||
$session->{prefs}->getPreferencesValue('EDIT_TEMPLATE') ||
$templateName;
$tmpl =
$session->{templates}->readTemplate( $template.$editaction, $skin );
if( !$tmpl && $template ne $templateName ) {
$tmpl = $session->{templates}->readTemplate( $templateName, $skin );
}
if( !$tmpl ) {
throw TWiki::OopsException( 'attention',
def => 'no_such_template',
web => $webName,
topic => $topic,
params => [ $template.$editaction,
'EDIT_TEMPLATE' ] );
}
my $templateWeb = $webName;
unless( $topicExists ) {
if( $templateTopic ) {
( $templateWeb, $templateTopic ) =
$session->normalizeWebTopicName( $templateWeb, $templateTopic );
unless( $store->topicExists( $templateWeb, $templateTopic )) {
throw TWiki::OopsException( 'accessdenied',
def => 'no_such_topic',
web => $templateWeb,
topic => $templateTopic,
params => [ 'templatetopic' ] );
}
( $meta, $text ) =
$store->readTopic( $session->{user}, $templateWeb,
$templateTopic, undef );
$templateTopic = $templateWeb.'.'.$templateTopic;
} else {
( $meta, $text ) = TWiki::UI::readTemplateTopic( $session, 'WebTopicEditTemplate' );
}
$extra = "(not exist)";
# If present, instantiate form
if( ! $formTemplate ) {
my $form = $meta->get( 'FORM' );
$formTemplate = $form->{name} if $form;
}
$text = $session->expandVariablesOnTopicCreation( $text, $user );
$tmpl =~ s/%NEWTOPIC%/1/;
} else {
$tmpl =~ s/%NEWTOPIC%//;
}
$tmpl =~ s/%TEMPLATETOPIC%/$templateTopic/;
$tmpl =~ s/%REDIRECTTO%/$redirectTo/;
# override with parameter if set
$text = $ptext if defined $ptext;
# Insert the rev number/date we are editing. This will be boolean false if
# this is a new topic.
if( $topicExists ) {
my ( $orgDate, $orgAuth, $orgRev ) = $meta->getRevisionInfo();
$tmpl =~ s/%ORIGINALREV%/${orgRev}_$orgDate/g;
} else {
$tmpl =~ s/%ORIGINALREV%/0/g;
}
# parent setting
if( $theParent eq 'none' ) {
$meta->remove( 'TOPICPARENT' );
} elsif( $theParent ) {
my $parentWeb;
($parentWeb, $theParent) =
$session->normalizeWebTopicName( $webName, $theParent );
if( $parentWeb ne $webName ) {
$theParent = $parentWeb.'.'.$theParent;
}
$meta->put( 'TOPICPARENT', { name => $theParent } );
} else {
$theParent = $meta->getParent();
}
$tmpl =~ s/%TOPICPARENT%/$theParent/;
if( $formTemplate ) {
$meta->remove( 'FORM' );
if( $formTemplate ne 'none' ) {
$meta->put( 'FORM', { name => $formTemplate } );
# Because the form has been expanded from a Template, we
# want to expand $percnt-style content right now
$meta->forEachSelectedValue(qr/FIELD/,
qr/value/,
sub {TWiki::expandStandardEscapes(@_)},
);
} else {
$meta->remove( 'FORM' );
}
$tmpl =~ s/%FORMTEMPLATE%/$formTemplate/go;
}
if( $saveCmd ) {
$text = $store->readTopicRaw( $session->{user}, $webName,
$topic, undef );
}
$session->{plugins}->beforeEditHandler(
$text, $topic, $webName, $meta ) unless( $saveCmd );
if( $TWiki::cfg{Log}{edit} ) {
# write log entry
$session->writeLog( 'edit', $webName.'.'.$topic, $extra );
}
$tmpl =~ s/\(edit\)/\(edit cmd=$saveCmd\)/go if $saveCmd;
$tmpl =~ s/%CMD%/$saveCmd/go;
$session->enterContext( 'can_render_meta', $meta );
$tmpl = $session->handleCommonTags( $tmpl, $webName, $topic );
$tmpl = $session->{renderer}->getRenderedVersion( $tmpl, $webName, $topic );
# Don't want to render form fields, so this after getRenderedVersion
my $formMeta = $meta->get( 'FORM' );
my $form = '';
my $formText = '';
$form = $formMeta->{name} if( $formMeta );
if( $form && !$saveCmd ) {
my $formDef = new TWiki::Form( $session, $templateWeb, $form );
unless( $formDef ) {
throw TWiki::OopsException( 'attention',
def => 'no_form_def',
web => $session->{webName},
topic => $session->{topicName},
params => [ $templateWeb, $form ] );
}
$formDef->getFieldValuesFromQuery( $session->{cgiQuery}, $meta );
# And render them for editing
# SMELL: these are both side-effecting functions, that will set
# default values for fields if they are not set in the meta.
# This behaviour really ought to be pulled out to a common place.
if ( $editaction eq 'text' ) {
$formText = $formDef->renderHidden( $meta );
} else {
$formText = $formDef->renderForEdit( $webName, $topic, $meta );
}
} elsif( !$saveCmd && $session->{prefs}->getWebPreferencesValue( 'WEBFORMS', $webName )) {
$formText = $session->{templates}->readTemplate( "addform", $skin );
$formText = $session->handleCommonTags( $formText, $webName, $topic );
}
$tmpl =~ s/%FORMFIELDS%/$formText/g;
$tmpl =~ s/%FORMTEMPLATE%//go; # Clear if not being used
return ( $text, $tmpl );
}
sub finalize_edit {
my ( $session, $text, $tmpl ) = @_;
my $query = $session->{cgiQuery};
my $webName = $session->{webName};
my $topic = $session->{topicName};
my $user = $session->{user};
# apptype is undocumented legacy
my $cgiAppType = $query->param( 'contenttype' ) ||
$query->param( 'apptype' ) || 'text/html';
$tmpl =~ s/%UNENCODED_TEXT%/$text/g;
$text = TWiki::entityEncode( $text );
$tmpl =~ s/%TEXT%/$text/g;
$session->{store}->setLease( $webName, $topic, $user, $TWiki::cfg{LeaseLength} );
$session->writeCompletePage( $tmpl, 'edit', $cgiAppType );
}
1;