Files
oddmuse/modules/atom.pl
Alex Schroeder c6e490dcb5 Remove $NewText and $NewComment.
$NewText is replaced by a call to NewText() which you can overwrite. Or
you can provide translations for the texts used by NewText(). Basically,
the new text explains that the page in question does not exist and
invites you edit it.
2015-07-03 13:01:32 +02:00

252 lines
9.5 KiB
Perl

# Copyright (C) 2004, 2006, 2008, 2014 Alex Schroeder <alex@gnu.org>
#
# 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 3 of the License, or
# (at your option) any later version.
#
# 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. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
use strict;
use XML::Atom::Entry;
use XML::Atom::Link;
use XML::Atom::Person;
AddModuleDescription('atom.pl', 'Atom Extension');
our ($q, %Page, %Action, $CommentsPrefix, $ScriptName, $SiteName, $MaxPost, $UseDiff, $DeletedPage, @MyInitVariables, @MyMacros, $FS, $BannedContent, $RssStyleSheet, $RssRights, $RssLicense, $RssImageUrl, $RssExclude, $RCName, @UploadTypes, $UploadAllowed, $UsePathInfo, $SiteDescription, $LastUpdate, $InterWikiMoniker);
push(@MyInitVariables, \&AtomInit);
sub AtomInit {
SetParam('action', 'atom') if $q->path_info =~ m|/atom\b|;
}
$Action{atom} = \&DoAtom;
sub DoAtom {
my $id = shift;
if ($q->request_method eq 'POST') {
DoAtomSave('POST');
} elsif (GetParam('info', 0) or $id eq 'info') {
DoAtomIntrospection();
} elsif (GetParam('wiki', 0)) {
if ($q->request_method eq 'PUT') {
DoAtomSave('PUT', $id);
} elsif ($q->request_method eq 'DELETE') {
DoAtomDelete($id);
} else {
DoAtomGet($id);
}
} else {
SetParam($id, 1); # /atom/full should work, too
print GetHttpHeader('application/atom+xml');
print GetRcAtom();
}
}
# from http://www.ietf.org/internet-drafts/draft-ietf-atompub-protocol-10.txt
sub DoAtomIntrospection {
print GetHttpHeader('application/atomserv+xml');
my @types = ('entry', );
push(@types, @UploadTypes) if $UploadAllowed;
my $upload = '<accept>' . join(', ', @types) . '</accept>';
print <<EOT;
<?xml version="1.0" encoding='UTF-8'?>
<service xmlns="http://purl.org/atom/app#">
<workspace title="Wiki" >
<collection title="$SiteName" href="$ScriptName/atom/wiki">
$upload
</collection>
</workspace>
</service>
EOT
}
sub AtomTag {
my ($tag, $value) = @_;
return '' unless $value;
return "<$tag>$value</$tag>\n";
}
# based on GetRcRss
sub GetRcAtom {
my $url = QuoteHtml($ScriptName) . ($UsePathInfo ? "/" : "?");
my $diffPrefix = QuoteHtml($ScriptName) . "?action=browse;diff=1;id=";
my $historyPrefix = QuoteHtml($ScriptName) . "?action=history;id=";
my $limit = GetParam("rsslimit", 15); # Only take the first 15 entries
my $count = 0;
my $feed = qq{<?xml version="1.0" encoding="UTF-8"?>\n};
if ($RssStyleSheet =~ /\.(xslt?|xml)$/) {
$feed .= qq{<?xml-stylesheet type="text/xml" href="$RssStyleSheet" ?>\n};
} elsif ($RssStyleSheet) {
$feed .= qq{<?xml-stylesheet type="text/css" href="$RssStyleSheet" ?>\n};
}
$feed .= qq{<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:wiki="http://purl.org/rss/1.0/modules/wiki/"
xmlns:cc="http://backend.userland.com/creativeCommonsRssModule">\n};
$feed .= AtomTag('title', QuoteHtml($SiteName) . ': ' . GetParam('title', QuoteHtml($RCName)));
$feed .= qq{<link href="} . $url . UrlEncode($RCName) . qq{"/>\n};
$feed .= AtomTag('subtitle', QuoteHtml($SiteDescription));
$feed .= AtomTag('updated', TimeToW3($LastUpdate));
$feed .= qq{<generator uri="http://www.oddmuse.org/">Oddmuse</generator>\n};
$feed .= AtomTag('rights', $RssRights) if $RssRights;
$feed .= join('', map { AtomTag('<cc:license>', QuoteHtml($_)) }
(ref $RssLicense eq 'ARRAY' ? @$RssLicense : $RssLicense));
$feed .= AtomTag('wiki:interwiki', $InterWikiMoniker) if $InterWikiMoniker;
$feed .= AtomTag('logo', $RssImageUrl) if $RssImageUrl;
my %excluded = ();
if (GetParam("exclude", 1)) {
foreach (split(/\n/, GetPageContent($RssExclude))) {
if (/^ ([^ ]+)[ \t]*$/) { # only read lines with one word after one space
$excluded{$1} = 1;
}
}
}
# Now call GetRc with some blocks of code as parameters:
ProcessRcLines(sub {}, sub {
my ($pagename, $timestamp, $host, $username, $summary, $minor, $revision, $languages, $cluster) = @_;
return if $excluded{$pagename} or ($limit ne 'all' and $count++ >= $limit);
my $name = NormalToFree($pagename);
$username = QuoteHtml($username);
$username = $host unless $username;
$feed .= "\n<entry>\n";
$feed .= AtomTag('title', QuoteHtml($name));
$feed .= qq{<link rel="alternate" href="}
. (GetParam('all', $cluster)
? QuoteHtml($ScriptName) . "?" . GetPageParameters('browse', $pagename, $revision, $cluster)
: $url . UrlEncode($pagename)) . qq{"/>\n};
$feed .= AtomLink("$ScriptName/atom/wiki/$pagename");
$feed .= AtomTag('summary', QuoteHtml($summary));
$feed .= qq{<content type="xhtml">\n<div xmlns="http://www.w3.org/1999/xhtml">\n}
. PageHtml($pagename, 50*1024,$q->div(T('This page is too big to send over RSS.')))
. qq{\n</div>\n</content>\n} if GetParam('full', 0);
$feed .= AtomTag('published', TimeToW3($timestamp));
$feed .= qq{<link rel="replies" href="} . $url . $CommentsPrefix . UrlEncode($pagename) . qq{"/>\n}
if $CommentsPrefix and $pagename !~ /^$CommentsPrefix/;
$feed .= AtomTag('author', substr(AtomTag('name', $username), 0, -1)); # strip one newline
$feed .= AtomTag('wiki:username', $username);
$feed .= AtomTag('wiki:status', 1 == $revision ? 'new' : 'updated');
$feed .= AtomTag('wiki:importance', $minor ? 'minor' : 'major');
$feed .= AtomTag('wiki:version', $revision);
$feed .= AtomTag('wiki:history', $historyPrefix . UrlEncode($pagename));
$feed .= AtomTag('wiki:diff', $diffPrefix . UrlEncode($pagename))
if $UseDiff and GetParam('diffrclink', 1);
$feed .= "</entry>\n";
});
$feed .= "</feed>\n";
return $feed;
}
# Based on DoPost
sub DoAtomSave {
my ($type, $oldid) = @_;
my $entry = AtomEntry();
my $title = $entry->title();
my $author = $entry->author();
SetParam('username', $author->name) if $author; # Used in Save()
my $id = FreeToNormal($title);
UserCanEditOrDie($id);
$oldid = $id unless $oldid;
ValidIdOrDie($oldid);
my $summary = $entry->summary();
# Lock before getting old page to prevent races
RequestLockOrError(); # fatal
OpenPage($oldid);
my $old = $Page{text};
# FIXME: Assuming XML Type content, because that's what
# XML::Atom::Client does. Sent mail to the maintainers, asking for
# clarification.
$_ = $entry->content()->{elem}->getChildrenByTagName('div')->[0]->textContent;
foreach my $macro (@MyMacros) {
&$macro;
}
my $string = $_;
# Massage the string
$string =~ s/\r//g;
$string .= "\n" if ($string !~ /\n$/);
$string =~ s/$FS//g;
# Banned Content
if (not UserIsEditor()) {
my $rule = BannedContent($string) || BannedContent($summary);
ReportError(T('Edit Denied'), '403 FORBIDDEN', undef,
$q->p(T('The page contains banned text.')),
$q->p(T('Contact the wiki administrator for more information.')),
$q->p($rule . ' ' . Ts('See %s for more information.', GetPageLink($BannedContent))))
if $rule;
}
my $oldrev = $Page{revision};
if ($old eq $string and $oldid eq $id) {
ReportError(T('No changes to be saved.'), '200 OK'); # an update without consequence
} elsif ($oldrev == 0 and $string eq "\n") {
ReportError(T('No changes to be saved.'), '400 BAD REQUEST'); # don't fake page creation because of webdav
} else {
# My providing a different title, the entry is automatically renamed
if ($oldrev > 0 and $oldid ne $id) {
Save($oldid, $DeletedPage, Ts('Renamed to %s', NormalToFree($id)));
OpenPage($id);
}
# Now save the new page
Save($id, $string, $summary);
ReleaseLock();
# Do we reply 200 or 201 depending on the request, or depending on
# the action taken?
my $url = "$ScriptName/atom/wiki/$id";
if ($type eq 'POST') { # instead of $oldrev == 0
print $q->header(-status=>'201 CREATED', -location=>$url);
} else {
print $q->header(-status=>'200 OK');
}
$entry->title(NormalToFree($id));
$entry->add_link(AtomLink($url));
print '<?xml version="1.0" encoding="utf-8"?>', "\n";
print $entry->{elem}->toString;
}
}
sub DoAtomGet {
print $q->header(-status=>'304 NOT MODIFIED') and return if FileFresh(); # return value is ignored
print GetHttpHeader('application/atomserv+xml');
print '<?xml version="1.0" encoding="utf-8"?>', "\n";
my $id = GetId();
OpenPage($id);
my $entry = XML::Atom::Entry->new;
my $person = XML::Atom::Person->new;
$person->name($Page{username});
$entry->author($person) if $Page{username};
$entry->title(NormalToFree($id));
$entry->summary($Page{summary});
$entry->content($Page{text});
$entry->add_link(AtomLink("$ScriptName/atom/wiki/$id"));
print $entry->{elem}->toString;
}
sub AtomEntry {
my $data = $q->param('POSTDATA');
if (not $data) {
# CGI provides POSTDATA for POST requests, not for PUT requests.
# The following code is based on the CGI->init code.
my $content_length = defined($ENV{'CONTENT_LENGTH'}) ? $ENV{'CONTENT_LENGTH'} : 0;
if ($content_length > 0 and $content_length < $MaxPost) {
$q->read_from_client(\$data, $content_length, 0);
}
}
my $entry = XML::Atom::Entry->new(\$data);
return $entry;
}
sub AtomLink {
my $url = shift;
my $link = XML::Atom::Link->new;
$link->href($url);
$link->rel('edit');
return $link;
}