]> sipb.mit.edu Git - ikiwiki.git/commitdiff
Merge branch 'tova'
authorJoey Hess <joey@kodama.kitenet.net>
Wed, 2 Jul 2008 20:38:13 +0000 (16:38 -0400)
committerJoey Hess <joey@kodama.kitenet.net>
Wed, 2 Jul 2008 20:38:13 +0000 (16:38 -0400)
17 files changed:
IkiWiki.pm
IkiWiki/CGI.pm
IkiWiki/Plugin/attachment.pm [new file with mode: 0644]
IkiWiki/Plugin/editdiff.pm
IkiWiki/Plugin/lockedit.pm
IkiWiki/Plugin/toggle.pm
IkiWiki/Render.pm
debian/changelog
doc/plugins/attachment.mdwn [new file with mode: 0644]
doc/plugins/contrib/attach/discussion.mdwn [new file with mode: 0644]
doc/plugins/toggle.mdwn
doc/soc.mdwn
doc/todo/attachments.mdwn [new file with mode: 0644]
doc/todo/attachments_plugin.mdwn [deleted file]
doc/todo/fileupload.mdwn
doc/todo/toggle_initial_state.mdwn
templates/editpage.tmpl

index 7d5668d74d6899f08019b964517223b693606db5..d9b3dcdb473df66c4505a1b47e429febac493b63 100644 (file)
@@ -88,6 +88,7 @@ sub defaultconfig () { #{{{
        account_creation_password => "",
        prefix_directives => 0,
        hardlink => 0,
+       cgi_disable_uploads => 1,
 } #}}}
 
 sub checkconfig () { #{{{
index 015c9ae01fbde4f3be3a85bf28f4a429fba54881..07e92322f4cbf94b9af112890077bd093f94b89d 100644 (file)
@@ -281,7 +281,6 @@ sub cgi_editpage ($$) { #{{{
        eval q{use CGI::FormBuilder};
        error($@) if $@;
        my $form = CGI::FormBuilder->new(
-               title => "editpage",
                fields => \@fields,
                charset => "utf-8",
                method => 'POST',
@@ -304,7 +303,7 @@ sub cgi_editpage ($$) { #{{{
        
        # This untaint is safe because titlepage removes any problematic
        # characters.
-       my ($page)=$form->field('page');
+       my $page=$form->field('page');
        $page=titlepage(possibly_foolish_untaint($page));
        if (! defined $page || ! length $page ||
            file_pruned($page, $config{srcdir}) || $page=~/^\//) {
@@ -667,10 +666,11 @@ sub cgi (;$$) { #{{{
        my $q=shift;
        my $session=shift;
 
+       eval q{use CGI};
+       error($@) if $@;
+       $CGI::DISABLE_UPLOADS=$config{cgi_disable_uploads};
+
        if (! $q) {
-               eval q{use CGI};
-               error($@) if $@;
-       
                binmode(STDIN);
                $q=CGI->new;
                binmode(STDIN, ":utf8");
diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm
new file mode 100644 (file)
index 0000000..a5c42d6
--- /dev/null
@@ -0,0 +1,346 @@
+#!/usr/bin/perl
+package IkiWiki::Plugin::attachment;
+
+use warnings;
+use strict;
+use IkiWiki 2.00;
+
+sub import { #{{{
+       hook(type => "checkconfig", id => "attachment", call => \&checkconfig);
+       hook(type => "formbuilder_setup", id => "attachment", call => \&formbuilder_setup);
+       hook(type => "formbuilder", id => "attachment", call => \&formbuilder);
+} # }}}
+
+sub checkconfig () { #{{{
+       $config{cgi_disable_uploads}=0;
+} #}}}
+
+sub formbuilder_setup (@) { #{{{
+       my %params=@_;
+       my $form=$params{form};
+       my $q=$params{cgi};
+
+       if ($form->field("do") eq "edit") {
+               $form->field(name => 'attachment', type => 'file');
+               # These buttons are not put in the usual place, so
+               # are not added to the normal formbuilder button list.
+               $form->tmpl_param("field-upload" => '<input name="_submit" type="submit" value="Upload Attachment" />');
+               $form->tmpl_param("field-link" => '<input name="_submit" type="submit" value="Insert Links" />');
+
+               # Add the javascript from the toggle plugin;
+               # the attachments interface uses it to toggle visibility.
+               require IkiWiki::Plugin::toggle;
+               $form->tmpl_param("javascript" => $IkiWiki::Plugin::toggle::javascript);
+               # Start with the attachments interface toggled invisible,
+               # but if it was used, keep it open.
+               if ($form->submitted ne "Upload Attachment" &&
+                   ! length $q->param("attachment_select")) {
+                       $form->tmpl_param("attachments-class" => "toggleable");
+               }
+               else {
+                       $form->tmpl_param("attachments-class" => "toggleable-open");
+               }
+       }
+       elsif ($form->title eq "preferences") {
+               my $session=$params{session};
+               my $user_name=$session->param("name");
+
+               $form->field(name => "allowed_attachments", size => 50,
+                       fieldset => "admin",
+                       comment => "(".htmllink("", "", "ikiwiki/PageSpec", noimageinline => 1).")");
+               if (! IkiWiki::is_admin($user_name)) {
+                       $form->field(name => "allowed_attachments", type => "hidden");
+               }
+                if (! $form->submitted) {
+                       $form->field(name => "allowed_attachments", force => 1,
+                               value => IkiWiki::userinfo_get($user_name, "allowed_attachments"));
+                }
+               if ($form->submitted && $form->submitted eq 'Save Preferences') {
+                       if (defined $form->field("allowed_attachments")) {
+                               IkiWiki::userinfo_set($user_name, "allowed_attachments",
+                               $form->field("allowed_attachments")) ||
+                                       error("failed to set allowed_attachments");
+                       }
+               }
+       }
+} #}}}
+
+sub formbuilder (@) { #{{{
+       my %params=@_;
+       my $form=$params{form};
+       my $q=$params{cgi};
+
+       return if $form->field("do") ne "edit";
+
+       my $filename=$q->param('attachment');
+       if (defined $filename && length $filename &&
+            ($form->submitted eq "Upload Attachment" || $form->submitted eq "Save Page")) {
+               my $session=$params{session};
+               
+               # This is an (apparently undocumented) way to get the name
+               # of the temp file that CGI writes the upload to.
+               my $tempfile=$q->tmpFileName($filename);
+               
+               $filename=IkiWiki::titlepage(
+                       IkiWiki::possibly_foolish_untaint(
+                               attachment_location($form->field('page')).
+                               IkiWiki::basename($filename)));
+               if (IkiWiki::file_pruned($filename, $config{srcdir})) {
+                       error(gettext("bad attachment filename"));
+               }
+               
+               # Check that the user is allowed to edit a page with the
+               # name of the attachment.
+               IkiWiki::check_canedit($filename, $q, $session, 1);
+               
+               # Use a special pagespec to test that the attachment is valid.
+               my $allowed=1;
+               foreach my $admin (@{$config{adminuser}}) {
+                       my $allowed_attachments=IkiWiki::userinfo_get($admin, "allowed_attachments");
+                       if (defined $allowed_attachments &&
+                           length $allowed_attachments) {
+                               $allowed=pagespec_match($filename,
+                                       $allowed_attachments,
+                                       file => $tempfile,
+                                       user => $session->param("name"),
+                                       ip => $ENV{REMOTE_ADDR},
+                               );
+                               last if $allowed;
+                       }
+               }
+               if (! $allowed) {
+                       error(gettext("attachment rejected")." ($allowed)");
+               }
+
+               # Needed for fast_file_copy and for rendering below.
+               require IkiWiki::Render;
+
+               # Move the attachment into place.
+               # Try to use a fast rename; fall back to copying.
+               IkiWiki::prep_writefile($filename, $config{srcdir});
+               unlink($config{srcdir}."/".$filename);
+               if (rename($tempfile, $config{srcdir}."/".$filename)) {
+                       # The temp file has tight permissions; loosen up.
+                       chmod(0666 & ~umask, $config{srcdir}."/".$filename);
+               }
+               else {
+                       my $fh=$q->upload('attachment');
+                       if (! defined $fh || ! ref $fh) {
+                               error("failed to get filehandle");
+                       }
+                       binmode($fh);
+                       writefile($filename, $config{srcdir}, undef, 1, sub {
+                               IkiWiki::fast_file_copy($tempfile, $filename, $fh, @_);
+                       });
+               }
+
+               # Check the attachment in and trigger a wiki refresh.
+               if ($config{rcs}) {
+                       IkiWiki::rcs_add($filename);
+                       IkiWiki::disable_commit_hook();
+                       IkiWiki::rcs_commit($filename, gettext("attachment upload"),
+                               IkiWiki::rcs_prepedit($filename),
+                               $session->param("name"), $ENV{REMOTE_ADDR});
+                       IkiWiki::enable_commit_hook();
+                       IkiWiki::rcs_update();
+               }
+               IkiWiki::refresh();
+               IkiWiki::saveindex();
+       }
+       elsif ($form->submitted eq "Insert Links") {
+               my $add="";
+               foreach my $f ($q->param("attachment_select")) {
+                       $add.="[[$f]]\n";
+               }
+               $form->field(name => 'editcontent',
+                       value => $form->field('editcontent')."\n\n".$add,
+                       force => 1) if length $add;
+       }
+       
+       # Generate the attachment list only after having added any new
+       # attachments.
+       $form->tmpl_param("attachment_list" => [attachment_list($form->field('page'))]);
+} # }}}
+
+sub attachment_location ($) {
+       my $page=shift;
+       
+       # Put the attachment in a subdir of the page it's attached
+       # to, unless that page is an "index" page.
+       $page=~s/(^|\/)index//;
+       $page.="/" if length $page;
+       
+       return $page;
+}
+
+sub attachment_list ($) {
+       my $page=shift;
+       my $loc=attachment_location($page);
+
+       my @ret;
+       foreach my $f (values %pagesources) {
+               if (! defined IkiWiki::pagetype($f) &&
+                   $f=~m/^\Q$loc\E[^\/]+$/ &&
+                   -e "$config{srcdir}/$f") {
+                       push @ret, {
+                               "field-select" => '<input type="checkbox" name="attachment_select" value="'.$f.'" />',
+                               link => htmllink($page, $page, $f, noimageinline => 1),
+                               size => humansize((stat(_))[7]),
+                               mtime => displaytime($IkiWiki::pagemtime{$f}),
+                               mtime_raw => $IkiWiki::pagemtime{$f},
+                       };
+               }
+       }
+
+       # Sort newer attachments to the top of the list, so a newly-added
+       # attachment appears just before the form used to add it.
+       return sort { $b->{mtime_raw} <=> $a->{mtime_raw} || $a->{link} cmp $b->{link} } @ret;
+}
+
+my %units=(            # size in bytes
+       B               => 1,
+       byte            => 1,
+       KB              => 2 ** 10,
+       kilobyte        => 2 ** 10,
+       K               => 2 ** 10,
+       KB              => 2 ** 10,
+       kilobyte        => 2 ** 10,
+       M               => 2 ** 20,
+       MB              => 2 ** 20,
+       megabyte        => 2 ** 20,
+       G               => 2 ** 30,
+       GB              => 2 ** 30,
+       gigabyte        => 2 ** 30,
+       T               => 2 ** 40,
+       TB              => 2 ** 40,
+       terabyte        => 2 ** 40,
+       P               => 2 ** 50,
+       PB              => 2 ** 50,
+       petabyte        => 2 ** 50,
+       E               => 2 ** 60,
+       EB              => 2 ** 60,
+       exabyte         => 2 ** 60,
+       Z               => 2 ** 70,
+       ZB              => 2 ** 70,
+       zettabyte       => 2 ** 70,
+       Y               => 2 ** 80,
+       YB              => 2 ** 80,
+       yottabyte       => 2 ** 80,
+       # ikiwiki, if you find you need larger data quantities, either modify
+       # yourself to add them, or travel back in time to 2008 and kill me.
+       #   -- Joey
+);
+
+sub parsesize ($) { #{{{
+       my $size=shift;
+
+       no warnings;
+       my $base=$size+0; # force to number
+       use warnings;
+       foreach my $unit (sort keys %units) {
+               if ($size=~/[0-9\s]\Q$unit\E$/i) {
+                       return $base * $units{$unit};
+               }
+       }
+       return $base;
+} #}}}
+
+sub humansize ($) { #{{{
+       my $size=shift;
+
+       foreach my $unit (reverse sort { $units{$a} <=> $units{$b} || $b cmp $a } keys %units) {
+               if ($size / $units{$unit} > 0.25) {
+                       return (int($size / $units{$unit} * 10)/10).$unit;
+               }
+       }
+       return $size; # near zero, or negative
+} #}}}
+
+package IkiWiki::PageSpec;
+
+sub match_maxsize ($$;@) { #{{{
+       shift;
+       my $maxsize=eval{IkiWiki::Plugin::attachment::parsesize(shift)};
+       if ($@) {
+               return IkiWiki::FailReason->new("unable to parse maxsize (or number too large)");
+       }
+
+       my %params=@_;
+       if (! exists $params{file}) {
+               return IkiWiki::FailReason->new("no file specified");
+       }
+
+       if (-s $params{file} > $maxsize) {
+               return IkiWiki::FailReason->new("file too large (".(-s $params{file})." >  $maxsize)");
+       }
+       else {
+               return IkiWiki::SuccessReason->new("file not too large");
+       }
+} #}}}
+
+sub match_minsize ($$;@) { #{{{
+       shift;
+       my $minsize=eval{IkiWiki::Plugin::attachment::parsesize(shift)};
+       if ($@) {
+               return IkiWiki::FailReason->new("unable to parse minsize (or number too large)");
+       }
+
+       my %params=@_;
+       if (! exists $params{file}) {
+               return IkiWiki::FailReason->new("no file specified");
+       }
+
+       if (-s $params{file} < $minsize) {
+               return IkiWiki::FailReason->new("file too small");
+       }
+       else {
+               return IkiWiki::SuccessReason->new("file not too small");
+       }
+} #}}}
+
+sub match_ispage ($$;@) { #{{{
+       my $filename=shift;
+
+       if (defined IkiWiki::pagetype($filename)) {
+               return IkiWiki::SuccessReason->new("file is a wiki page");
+       }
+       else {
+               return IkiWiki::FailReason->new("file is not a wiki page");
+       }
+} #}}}
+
+sub match_user ($$;@) { #{{{
+       shift;
+       my $user=shift;
+       my %params=@_;
+       
+       if (! exists $params{user}) {
+               return IkiWiki::FailReason->new("no user specified");
+       }
+
+       if (defined $params{user} && lc $params{user} eq lc $user) {
+               return IkiWiki::SuccessReason->new("user is $user");
+       }
+       else {
+               return IkiWiki::FailReason->new("user is $params{user}, not $user");
+       }
+} #}}}
+
+sub match_ip ($$;@) { #{{{
+       shift;
+       my $ip=shift;
+       my %params=@_;
+       
+       if (! exists $params{ip}) {
+               return IkiWiki::FailReason->new("no IP specified");
+       }
+
+       if (defined $params{ip} && lc $params{ip} eq lc $ip) {
+               return IkiWiki::SuccessReason->new("IP is $ip");
+       }
+       else {
+               return IkiWiki::FailReason->new("IP is $params{ip}, not $ip");
+       }
+} #}}}
+
+1
index 2a70ca0b81a49c3113e6074bcb797d94e41e133e..b8ecaa3d747c356e1690c197592eaecfd44c1456 100644 (file)
@@ -48,8 +48,7 @@ sub formbuilder_setup { #{{{
        my $form=$params{form};
        my $page=$form->field("page");
 
-       return if $form->title ne "editpage"
-                 || $form->field("do") ne "edit";
+       return if $form->field("do") ne "edit";
 
        $page = IkiWiki::titlepage(IkiWiki::possibly_foolish_untaint($page));
        return unless exists $pagesources{$page};
index 010705c49a4fc460976b3239227c972d05d6df76..5ffb4e6f9edd254691f4fb5b246e3afb2aeea147 100644 (file)
@@ -40,11 +40,11 @@ sub formbuilder_setup (@) { #{{{
        my %params=@_;
        
        my $form=$params{form};
-       my $session=$params{session};
-       my $cgi=$params{cgi};
-       my $user_name=$session->param("name");
-
        if ($form->title eq "preferences") {
+               my $session=$params{session};
+               my $cgi=$params{cgi};
+               my $user_name=$session->param("name");
+
                $form->field(name => "locked_pages", size => 50,
                        fieldset => "admin",
                        comment => "(".htmllink("", "", "ikiwiki/PageSpec", noimageinline => 1).")");
index 8089443e02b9296d37d393b83ba6456a7c0a4e45..284eb8249df031e77f2e1ff1796d19624bbae1cf 100644 (file)
@@ -9,7 +9,7 @@ use IkiWiki 2.00;
 # of css to hide toggleables, to avoid any flashing on page load. The css
 # is only emitted after the javascript tests that it's going to be able to
 # show the toggleables.
-my $javascript=<<'EOF';
+our $javascript=<<'EOF';
 <script type="text/javascript">
 <!--
 if (document.getElementById && document.getElementsByTagName && document.createTextNode) {
@@ -21,7 +21,8 @@ function inittoggle() {
        var as = getElementsByClass('toggle');
        for (var i = 0; i < as.length; i++) {
                var id = as[i].href.match(/#(\w.+)/)[1];
-               document.getElementById(id).style.display="none";
+               if (document.getElementById(id).className == "toggleable")
+                       document.getElementById(id).style.display="none";
                as[i].onclick = function() {
                        toggle(this);
                        return false;
@@ -80,17 +81,11 @@ sub preprocess_toggle (@) { #{{{
        my %params=(id => "default", text => "more", @_);
 
        my $id=genid($params{page}, $params{id});
-       if (! $params{preview}) {
-               return "<a class=\"toggle\" href=\"#$id\">$params{text}</a>";
-       }
-       else {
-               return "$params{text} ".
-                       gettext("(not toggleable in preview mode)");
-       }
+       return "<a class=\"toggle\" href=\"#$id\">$params{text}</a>";
 } # }}}
 
 sub preprocess_toggleable (@) { #{{{
-       my %params=(id => "default", text => "", @_);
+       my %params=(id => "default", text => "", open => "no", @_);
 
        # Preprocess the text to expand any preprocessor directives
        # embedded inside it.
@@ -98,23 +93,24 @@ sub preprocess_toggleable (@) { #{{{
                IkiWiki::filter($params{page}, $params{destpage}, $params{text}));
        
        my $id=genid($params{page}, $params{id});
+       my $class=(lc($params{open}) ne "yes") ? "toggleable" : "toggleable-open";
 
        # Should really be a postprocessor directive, oh well. Work around
        # markdown's dislike of markdown inside a <div> with various funky
        # whitespace.
        my ($indent)=$params{text}=~/( +)$/;
        $indent="" unless defined $indent;
-       return "<div class=\"toggleable\" id=\"$id\"></div>\n\n$params{text}\n$indent<div class=\"toggleableend\"></div>";
+       return "<div class=\"$class\" id=\"$id\"></div>\n\n$params{text}\n$indent<div class=\"toggleableend\"></div>";
 } # }}}
 
 sub format (@) { #{{{
         my %params=@_;
 
-       if ($params{content}=~s!(<div class="toggleable" id="[^"]+">)</div>!$1!g) {
+       if ($params{content}=~s!(<div class="toggleable(?:-open)?" id="[^"]+">)</div>!$1!g) {
                $params{content}=~s/<div class="toggleableend">//g;
-               if (! ($params{content}=~s!^<\/body>!$javascript</body>!m)) {
+               if (! ($params{content}=~s!^<body>!<body>$javascript!m)) {
                        # no </body> tag, probably in preview mode
-                       $params{content}.=$javascript;
+                       $params{content}=$javascript.$params{content};
                }
        }
        return $params{content};
index 272eb239a08a4ea2d2bb2683c01a3bee6b1bfa03..5184be2df0129bf9837aaabd91133dd1b1e3ba96 100644 (file)
@@ -180,6 +180,30 @@ sub scan ($) { #{{{
        }
 } #}}}
 
+sub fast_file_copy (@) { #{{{
+       my $srcfile=shift;
+       my $destfile=shift;
+       my $srcfd=shift;
+       my $destfd=shift;
+       my $cleanup=shift;
+
+       my $blksize = 16384;
+       my ($len, $buf, $written);
+       while ($len = sysread $srcfd, $buf, $blksize) {
+               if (! defined $len) {
+                       next if $! =~ /^Interrupted/;
+                       error("failed to read $srcfile: $!", $cleanup);
+               }
+               my $offset = 0;
+               while ($len) {
+                       defined($written = syswrite $destfd, $buf, $len, $offset)
+                               or error("failed to write $destfile: $!", $cleanup);
+                       $len -= $written;
+                       $offset += $written;
+               }
+       }
+}
+
 sub render ($) { #{{{
        my $file=shift;
        
@@ -215,24 +239,7 @@ sub render ($) { #{{{
                
                my $srcfd=readfile($srcfile, 1, 1);
                writefile($file, $config{destdir}, undef, 1, sub {
-                       my $destfd=shift;
-                       my $cleanup=shift;
-
-                       my $blksize = 16384;
-                       my ($len, $buf, $written);
-                       while ($len = sysread $srcfd, $buf, $blksize) {
-                               if (! defined $len) {
-                                       next if $! =~ /^Interrupted/;
-                                       error("failed to read $srcfile: $!", $cleanup);
-                               }
-                               my $offset = 0;
-                               while ($len) {
-                                       defined($written = syswrite $destfd, $buf, $len, $offset)
-                                               or error("failed to write $file: $!", $cleanup);
-                                       $len -= $written;
-                                       $offset += $written;
-                               }
-                       }
+                       fast_file_copy($srcfile, $file, $srcfd, @_);
                });
        }
 } #}}}
index 9153254b4765eaf2aa04aa518c9867cad2122365..e6ffa17de51322f7988f748d6c7e4cf4fd34007b 100644 (file)
@@ -1,3 +1,16 @@
+ikiwiki (2.52) UNRELEASED; urgency=low
+
+  * attachment: New plugin for uploading and managing attachments.
+    (Sponsored by The TOVA Company.)
+  * If attachments are not enabled, configure CGI.pm to disable file
+    uploads by default. (An anti-DOS measure.)
+  * toggle: Add support for toggles that are open by default.
+  * toggle: Fix to work in preview mode.
+  * toggle: Add javascript to top of page, not to end. This avoids flicker
+    since closed toggles will not be displayed as the page is loading.
+
+ -- Joey Hess <joeyh@debian.org>  Mon, 30 Jun 2008 19:56:28 -0400
+
 ikiwiki (2.51) unstable; urgency=low
 
   * Improve toplevel parentlink to link directly to index.html when usedirs is
diff --git a/doc/plugins/attachment.mdwn b/doc/plugins/attachment.mdwn
new file mode 100644 (file)
index 0000000..184f5b5
--- /dev/null
@@ -0,0 +1,64 @@
+[[template id=plugin name=conditional core=1 author="[[Joey]]"]]
+[[tag type/useful]]
+
+This plugin allows files to be uploaded to the wiki over the web.
+
+For each page `foo`, files in the subdirectory `foo/` are treated as
+attachments of that page. Attachments can be uploaded and managed as
+part of the interface for editing a page.
+
+Warning: Do not enable this plugin on publically editable wikis, unless you
+take care to lock down the types and sizes of files that can be uploaded.
+Bear in mind that if you let anyone upload a particular kind of file
+("*.mp3" files, say), then someone can abuse your wiki in at least three ways:
+
+1. By uploading many mp3 files, wasting your disk space.
+2. By uploading mp3 files that attempt to exploit security holes
+   in web browsers or other players.
+3. By uploading files that claim to be mp3 files, but are really some 
+   other kind of file. Some web browsers may display a `foo.mp3` that
+   contains html as a web page; including running any malicious javascript
+   embedded in that page.
+
+To provide a way to combat these abuses, the wiki admin can specify a
+[[ikiwiki/PageSpec]] on their preferences page, to control what types of
+attachments can be uploaded, and by whom. The regular [[ikiwiki/PageSpec]]
+syntax is expanded with additional tests.
+
+For example, to limit arbitrary files to 50 kilobytes, but allow
+larger mp3 files to be uploaded by joey, a test like this could be
+used:
+  
+       (user(joey) and *.mp3 and maxsize(15mb)) or (!ispage() and maxsize(50kb))
+
+The following additional tests are available:
+
+* maxsize(size)
+
+  Tests whether the attachment is no larger than the specified size.
+  The size defaults to being in bytes, but "kb", "mb", "gb" etc can be
+  used to specify the units.
+  
+* minsize(size)
+
+  Tests whether the attachment is no smaller than the specified size.
+
+* ispage()
+
+  Tests whether the attachment will be treated by ikiwiki as a wiki page.
+  (Ie, if it has an extension of ".mdwn", or of any other enabled page
+  format).
+
+  So, if you don't want to allow wiki pages to be uploaded as attachments,
+  use `!ispage()` ; if you only want to allow wiki pages to be uploaded
+  as attachments, use `ispage()`.
+
+* user(username)
+
+  Tests whether the attachment is being uploaded by a user with the
+  specified username. If openid is enabled, an openid can also be put here.
+
+* ip(address)
+
+  Tests whether the attacment is being uploaded from the specified IP
+  address.
diff --git a/doc/plugins/contrib/attach/discussion.mdwn b/doc/plugins/contrib/attach/discussion.mdwn
new file mode 100644 (file)
index 0000000..803b7dc
--- /dev/null
@@ -0,0 +1,18 @@
+I found this posted to todo list, moved here: --[[Joey]]
+
+> First pass at an attachments plugin. See [[plugins/contrib/attach]] for
+> details/docs. Here's the [diff](http://pastebin.com/f4d889b65), and
+> here's some [technical notes](http://pastebin.com/f584b9d9d). There are
+> still various things I want to fix and tweak, but it works reasonably for
+> me as is.
+
+I guess I missed this when the plugin page was posted last September, and
+since the [[soc]] stuff wasn't updated, I didn't realize this was Ben's soc
+work. Which is more or less why I didn't look at it.
+
+This plugin would need quite a lot of work to finish up, I do think it was
+taking the right approach, sorry I never followed up on it.
+
+In the meantime, I've written an attachment plugin that does most of the
+same stuff, and behaves closer to how I originally sketched [[todo/fileupload]]
+as working.
index cb76d0b7b396b5797d0557b580076c6b0dda42c9..b33575824e88f083bbec295b6b939f780c63cd14 100644 (file)
@@ -28,3 +28,6 @@ each other, but can be located anywhere on the page. There can also be
 mutiple toggles that all toggle a single togglable.
 
 The id has a default value of "default", so can be omitted in simple cases.
+
+If you'd like a toggleable to be displayed by default, and toggle to
+hidden, then pass a parameter "open=true" when setting up the toggleable.
index c762d2e43baec690d1ea1eb1fcedfbad465c0b28..fffb5bed4749f7420ddfae2d11d4fe5399b8bc4b 100644 (file)
@@ -11,7 +11,7 @@ accepted, and the following projects were worked on:
   (See [[todo/latex]])
 * Implement File Upload Functionality and Image Gallery Creation  
   by Ben Coffey  
-  (See [[todo/fileupload/soc-proposal]])
+  (See [[todo/fileupload/soc-proposal]] and [[plugins/contrib/attach]])
 * Wiki WYSIWYG Editor  
   by [[TaylorKillian]]  
   (See [[todo/wikiwyg]])
diff --git a/doc/todo/attachments.mdwn b/doc/todo/attachments.mdwn
new file mode 100644 (file)
index 0000000..08052f3
--- /dev/null
@@ -0,0 +1,16 @@
+Stuff the [[plugins/attachment]] plugin is currently missing, that might be
+nice to add:
+
+* `mimetype()` pagespecs. (Using a mime type sniffer.)
+* Virus scanning.
+* Add a progress bar for attachment uploads (needs AJAX stuff..)
+* Maybe optimise the "Insert Links" button with javascript, so, if
+  javascript is available, the link is inserted at the current cursor
+  position in the page edit form, without actually reposting the form.
+  (Falling back to the current reposting of the form if javascript is not
+  available of course.)
+* Set `$CGI::POST_MAX` to some sane value (ie, larger than the largest
+  configured `maxsize()` in the pagespec, or if none is configured,
+  something reasonable. Just as a belt-and-suspenders DOS prevention.
+* Only allow attachments to be added to a given list of pages.
+  Maybe a pagespec like `parent(patches/*)`
diff --git a/doc/todo/attachments_plugin.mdwn b/doc/todo/attachments_plugin.mdwn
deleted file mode 100644 (file)
index 3b050b4..0000000
+++ /dev/null
@@ -1 +0,0 @@
-First pass at an attachments plugin. See [[plugins/contrib/attach]] for details/docs. Here's the [diff](http://pastebin.com/f4d889b65), and here's some [technical notes](http://pastebin.com/f584b9d9d). There are still various things I want to fix and tweak, but it works reasonably for me as is.
\ No newline at end of file
index 1962d6b400cc6bf2270032ba90b5c31194c23867..9a9106229d4f2f0943c8067cd556cbe49e9f3b34 100644 (file)
@@ -60,4 +60,4 @@ pagespec lock like the above prevents an edit or upload from happening,
 ikiwiki could display a reasonable message to the user, indicating what
 they've done wrong.)
 
-[[tag soc]]
+[[tag soc done]]
index f54d33c0498030130d0c7e5c2bd0eb6517e8614a..cbbf7e6fdff77be6c2c51784ca08d5bd0ae4303c 100644 (file)
@@ -2,3 +2,5 @@ It would be nice if one could set the initial state of the toggleable area.
 --[[[rdennis]]
 
 [[tag plugins/toggle]]
+
+[[done]]
index b0bb0ecb99ceb52b4b7214f214aa5660a64dae53..f8eda1b47c9a0aadee0d505e0c915815cbf80401 100644 (file)
@@ -1,3 +1,4 @@
+<TMPL_VAR JAVASCRIPT>
 <TMPL_IF NAME="PAGE_CONFLICT">
 <p>
 <b>Your changes conflict with other changes made to the page.</b>
@@ -60,6 +61,20 @@ Optional comment about this change:<br />
 </TMPL_IF>
 <TMPL_VAR FORM-SUBMIT>
 <TMPL_VAR HELPONFORMATTINGLINK>
+<a class="toggle" href="#attachments">Attachments</a>
+<TMPL_IF NAME="FIELD-ATTACHMENT">
+<div class="<TMPL_VAR ATTACHMENTS-CLASS>" id="attachments">
+<table>
+<tr><td colspan="5"><TMPL_VAR FIELD-ATTACHMENT><TMPL_VAR FIELD-UPLOAD></td></tr>
+<TMPL_LOOP NAME="ATTACHMENT_LIST">
+<tr><td><TMPL_VAR FIELD-SELECT><TMPL_VAR LINK></td><td><TMPL_VAR SIZE></td><td><TMPL_VAR MTIME></td></tr>
+</TMPL_LOOP>
+<TMPL_IF NAME="ATTACHMENT_LIST">
+<tr><td colspan="2"><TMPL_VAR FIELD-LINK><TMPL_VAR FIELD-DELETE><TMPL_VAR FIELD-RENAME></td></tr>
+</TMPL_IF>
+</table>
+</div>
+</TMPL_IF>
 <TMPL_VAR FORM-END>
 
 <TMPL_IF NAME="PAGE_PREVIEW">