http://www.perlmonks.org?node_id=180189
Category: Web Stuff
Author/Contact Info alfimp - alfimp.freeshell.org
Description: For letting people write in the margins of your pages. Doubles as a simplistic Wiki.
UPDATE (030811): OK, this needs a whole lot of work, which maybe one day I'll get round to. What's here is really pretty bad. But hey, it was my first sizable perl script, ok? And I still think there's a good idea there somewhere. The basic idea of Marge is to make web pages annotatable - that is, to let readers stick anonymous little notes in the margin of a page with their comments, corrections etc. which will then be displayed alongside the page. This is meant to provide a nice and simple way of getting discussion and feedback going, and just to make pages a bit more interestinga nd less static. And since the notes themselves can be annotated, you've got yourself a simple threaded forum as well.

That's the fundamentals, though it's kind of developed into a basic Wiki system while I wasn't watching... whoever you nominate as "gods" (which can be everyone, or just the webmaster, or a group of trusted people, or whatever) can create new web-pages, edit pre-existing pages and their notes, and delete notes. Which makes it a nice simple system for collaborative webby stuff, as well as the thing with the margins. What more can you want?

If you want to see it in action, the site of the Alfimp Collective has recently been put entirely on marge, so feel free to play around with it there.

This is the first script of this complexity that I've written, so it's not particualarly beautifully coded and there might even be the odd bug in there I haven't found, though it seems to all work fine. Also, I wasn't really worrying too much about security issues when writing this - in particular, since I don't know any other way to let the script create new files, you have to make the whole directory new web-pages will go in world read-writable, and each page you want to be able to edit world read-writable. This is probably terribly insecure, and if anyone knows how to fix it please /msg.

Detailed instructions for setting up marge follow.

Marge consists of six .pl files, which are posted in full after these instructions. Except for input.pl, which is just a little script for processing passed variables, each has a ***CONFIGURATION*** section near the top with a few variables you should set. Most of these can be left as their defaults, but you should change the $god variable in each - this is effectively the password to godly powers, and you should give a cookie reading "user=whatever you set $god to" to those you wish to have those powers. If you set $god='', then everyone will be a god, making your site effectively a Wiki. Unless you set $nologin to 1 in margemain.pl, non-gods will get a link to a login page at the top of each screen, which you should make and which should give the cookie.

Once that's done, using marge is pretty simple. Just stick the .pl files in one directory on your site, chmod 755 them, set the directory to be world read-writable (yes, I know), and run margificator.pl having given yourself a god cookie as above, or having set $god=''. This will let you create your pages - read this page for full instructions - that's a page you might want to copy and paste and make into your own instructions.marge page if you're intending to let other people be gods. You can copy and paste existing HTML if you want, and click the convert HTML button to change <p> and <br> tags into marge equivalents. The files margificator.pl create will all be "something.marge". To link to it use [something] (see below).

Although when creating or editing pages you can use any HTML you want, marge also has some limited formatting abilities. These also work on notes, where HTML is by default disallowed. Here's the stuff -

  • \n starts a new line
  • \p starts a new paragraph (two new-lines)
  • *This comes out bold*
  • +This in italics+
  • [link] to a page called link (with file name link.marge)
  • [link|pipe link] to a page called link that appears as pipe link
  • {www.perlmonks.org} is a link to that page (note - no http://)
  • {www.everything2.com|E2} a pipe-link.
  • *> gives two non-breaking spaces - use to indent

That's about all you need to know, it's all quite simple really. If anyone actually does use this, please do tell me via alfimp@freeshell.org, or /msg alfimp.

Here's the code - the scripts are separated by ----------'s, and in order they are -

  • margemain.pl - prints out a page
  • margeannotate.pl - provides a form for annotation
  • margedonote.pl - file manipulation
  • margeedit.pl - a form for editing
  • margificator.pl - a form for page creation

    
    
    --------------------------------------------------------
    
    
    #! /usr/pkg/bin/perl -wi
    use strict; 
    #margemain.pl - outputs an html file for an annotated page
    #    Outputs a table with three columns - the margin, a load of a's an
    +d >'s,
    #                        and the page proper
    #    Works line by line from the .marge file passed to it as "pageroot
    +".
    
    #***CONFIGURATION***
    my $god = "god"; #give a "user=$god" cookie for super-special powers 
            #(deletion, creation, editing)
    
    my $defaultformatting = "bgcolor='#202000' text='#3080FF' link='#80202
    +0' vlink='#FF1010'"; 
        #set default colours here (used if the page hasn't set any). 
        #Could also use to insert other HTML just after the body tag, 
        #if you so wish.
    
    my $nologin = 0; #set this to 1 if you don't want a link to a login.ma
    +rge page
    
    use CGI qw/:standard/;
    
    my $pageroot = param(pageroot); #the root page name, 
                      #so $pageroot.marge exists
    
    my $pagepath = param('pagepath'); #empty for root page, or something l
    +ike -4-6
            #for an annotation (-4-6 would be an annotation on the sixth 
            #line of a note to the 4th line of the $pageroot page).
    
    my $message = param('message'); #pass message=text to have it printed 
    +at the top.
    
    my $user = cookie(-name=>'user');
    my $cloaked = cookie(-name=>'cloaked');
    
     print "Content-type: text/html\n\n";
    
    #***A simple-minded per-page hit counter*** 
    if ($cloaked==0){
    my $count;
    open(COUNTIN, "$pageroot.count") or $count=0;
    flock(COUNTIN,2);
    seek(COUNTIN,0,0);
    $count=<COUNTIN> + 1; 
    close(COUNTIN);
    open(COUNTOUT, ">$pageroot.count");
    flock(COUNTOUT,2);
    seek(COUNTOUT,0,0);
    print COUNTOUT "$count";
    close(COUNTOUT);
    }
    
    #***Reading .marge file***
    my @page;
     open(MARGE, "$pageroot.marge") or die("Can't open $pageroot.marge");
     flock(MARGE,2);
     seek(MARGE,0,0);
     @page = <MARGE>;
    close(MARGE);
    
    #***Basic processing of and reading off variables from .marge file
    foreach (@page[ 0 .. $#page]) { s/\\n|\\p//g } #make disappear the \n&
    +\p
    
    my $lines = $page[0]; # first line should give the number of lines of 
    +the rest
    my $notewidth = $page[1]; #then width of notes in characters
    my $longname = $page[2]; #page title
    my $formatting = $page[3]; #stuff in body tag
    my $mainwidth = $page[4]; #main page width
    my $note = 0;
    my($pagestart,$pagelength,$notes,@afline,@ano,@apline,@anotes);
     for (my $i=0;$i<=$lines;$i++)
     {my $line = $page[$i];
    
     if ("$line" eq "***$pageroot$pagepath***\n") 
               #make sure parent always comes before child in .marge file
        { $pagestart = $i+3; #not the *** line nor the ano line nor the no
    +tes
          $pagelength = $page[$i+1];
          $notes = $page[$i+2];
          $afline[0] = $notes; #afline is line in file annotation starts o
    +n (from 0)
          $ano[0] = $notes; #ano is number of lines in note, given as firs
    +t line
          $apline[0] = $notes; #apline is line in page annotation starts o
    +n (from 1)
          $anotes[0] = $notes; #no of annotations the annotation has
          $i+=2;
        } 
     if ("$line" =~ /\*\*\*$pageroot$pagepath-(\d+)\*\*\*\n/)
        { $note +=1;
          $afline[$note] = $i+3; #not the *** line nor the ano line nor th
    +e notes
          $ano[$note] = $page[$i+1];
          $apline[$note] = $1;
          $anotes[$note] = $page[$i+2];
          $i+=2;
        }
     }
    
    #***print header & heading***
    
    print "<html><head><title>marge - $pageroot$pagepath</title></head>";
    
    if ($formatting =="") {$formatting=$defaultformatting;}
    print "<body $formatting>";
    
    my $upnote;
    if ($pagepath == ""){
        print "<center><h3>$longname</h3><small>";}
    else {
        print "<center><h3>An annotation to $longname</h3><small>";
        $upnote = $pagepath;
        while (chop($upnote) ne "-"){;}
        print "<a href = \"margemain.pl?pageroot=$pageroot&pagepath=$upnot
    +e\">back up</a>";}
    
    if ("$user" eq "$god") {
           #print "<br><a href=\"margemain.pl?pageroot=gods\">godly instru
    +ctions</a>";
        print "<br><a href = \"margeedit.pl?pageroot=$pageroot&pagepath=$p
    +agepath&mainwidth=$mainwidth&notewidth=$notewidth\">edit</a>";
        print "<br><a href= \"margificator.pl\">create</a>";
        }
    else {
        #If you're going to have multiple gods, you might want to make a 
        #login page which passes a cookie "user=$god". Else set $nologin=1
    +.
        if ($nologin==0) {print "<br><a href = \"margemain.pl?pageroot=log
    +in\">login</a>";}
    }
    
    if ($message ne '') {print "<br>$message";}
    
    if ("$user" eq "$god" && $notes==0 && "$pagepath" ne "") {
        print "<br><a href = \"margedonote.pl?pageroot=$pageroot&pagepath=
    +$pagepath&mode=delete\">delete</a>(!)";
        }
    print "</small></center><br>";
    
    print "<table cellpadding=5><tr>";
    
     print "<td valign=\"top\" align = \"right\" nowrap>"; #the margin
    
    
     #***Sorting the margin***
    my @start;
    $start[0] = $notes; #line to start printing from (from 1) 
     
    #starts with everything central, and goes down expanding each up and d
    +own
    #until all are blocked.
    
    my @end;
    $end[0] = $notes;
    my $up=0;
    my $blocked=0;
    for ($note=1;$note<=$notes;$note++) 
        {$start[$note]=$apline[$note]; $end[$note]=$apline[$note];}
    while ($blocked != 2)
        {$blocked++;
    for ($note=1;$note<=$notes;$note++)
    {#print "$note: start=$start[$note], end=$end[$note], apl=$apline[$not
    +e]";
    
    #Some rather messy conditionals - they work, though.
    if ($up==0) {if (($end[$note]-$apline[$note])<=($apline[$note]-$start[
    +$note]) 
        && ($end[$note]-$start[$note]+1)<$ano[$note] && (($note != $notes 
        && $end[$note]<($start[$note+1]-2)) || ($note == $notes 
        && $end[$note]<($pagelength)))) 
            {$end[$note]++;$blocked=0;}}
     
    else {if (($end[$note]-$apline[$note])>=($apline[$note]-$start[$note])
    + 
        && ($end[$note]-$start[$note]+1)<$ano[$note] && (($note != 1 
        && $start[$note]>($end[$note-1]+2)) 
        || ($note == 1 && $start[$note]>1))) 
            {$start[$note]--;$blocked=0;}
       }
    }
     $up = 1-$up; 
    } 
    
    #***Printing the margin***
    
    $note=1;
    my $nobreak;
    my $filelineno;
    for (my $i=1;(($i<=$pagelength) && ($note <= $notes)); $i++) 
                # $i goes down the main page itself, line by line
    {if ($nobreak==0) {print "&nbsp\;<small>";}
    $nobreak=0;
    if ($i == $start[$note])
     {
     $filelineno = $afline[$note]; #filelineno is line of file we're on 
    
      if ($anotes[$note] == 0) {print "<i>";} #if unannotated, print in it
    +alics
    #  print "<a href=\"margemain.pl?pageroot=$pageroot&pagepath=$pagepath
    +-$apline[$note]&user=$user\">";  
    }
    
     if ($i >= $start[$note] && $i < $end[$note])
    {my $line = $page[$filelineno++]; 
        if ("$line" =~ /\*\\\s*$/) { #end a line with *\ to prevent breaki
    +ng
                #(This is a leftover from a previous approach and
                #shouldn't be needed any more, but is left in just in
                #case).
             $nobreak=1;
            $line =~ s/\*\\\s*/ /;
            }
        print "$line";
        }
    
    if ($i == $end[$note])
    {#end of note
       my $line = $page[$filelineno++];
         if (($end[$note]-$start[$note]+1)<$ano[$note]) {
        #chop $line; chop $line; chop $line;
         print "$line...";
         }
         else {print "$line";}
         if ($anotes[$note]==0) {print "</i>";}
         #print "</a>";
         $note++;
    }
    
    if ($nobreak==0) {print "</small><br>";}
    }
    
    print "</td>";
    
    #***print the "a"'s (mid page) and***
    #***printing the main page (RHS)***
    #The mid page gets printed directly, and simulatenously the RHS gets p
    +ut into
    #$maintext, which is printed afterwards.
    
    print "<td valign=\"top\" nowrap>";
    my $maintext = '';   
    $filelineno = $pagestart;
    my $line = $page[$filelineno++];
    my $i=1;
    
    while ($line !~ /^\*\*\*/)
    {
    #Provision for simple includes - one-line only, only one per line
        #Syntax - e.g. #i/marge/poll.dat#/i
        if ("$line" =~ /\#i(.+)\#\/i/) {open(INCLUDE, "$1");
                        my $include =<INCLUDE>;
                        $line =~ s/\#i.+\#\/i/$include/;
                        close(INCLUDE);
                    }
    my $test=0;
    for (my $j=1;$j<=$notes;$j++) {if ($i == $apline[$j]) {
        #A note is centred on this line 
        $test=1;}}
    
    if ($test == 0) {
    if ($nobreak==0) {print "&nbsp;<small><small><a href=\"margeannotate.p
    +l?line=$i&pageroot=$pageroot&pagepath=$pagepath&width=$notewidth\">a<
    +/a></small></small><br>";}
    $nobreak=0;
    if ($line =~ /\*\\\s*$/) {$nobreak=1; $line =~ s/\*\\\s*/ /;}
        #Again, this whole $nobreak and *\ stuff should be ignored
    
    $maintext = $maintext."$line";
    if ($nobreak==0) {$maintext=$maintext."<br>";}
    }
    
     else {
    if ($nobreak==0) {print "&nbsp;<a href=\"margemain.pl?pageroot=$pagero
    +ot&pagepath=$pagepath-$i\">></a><br>";}
    $nobreak=0;
    if ($line =~ /\*\\\s*$/) {$nobreak=1; $line =~ s/\*\\\s*/ /;}
    $maintext = $maintext."$line";
    if ($nobreak==0) {$maintext=$maintext."<br>";}
    }
    $line = $page[$filelineno++];
     if ($nobreak==0) {$i++;}
    }
    
    print "</td>";
    print "<td valign=\"top\" nowrap>$maintext</td>";
    print "</table></body></html>"; 
    
    
    ---------------------------------------------------------
    
    
    #! /usr/pkg/bin/perl -w
    #margeannotate.pl - outputs a form for annotation 
    
    use strict;
    
    #***CONFIGURE***
    my $defaultformatting = "bgcolor='#202000' text='#3080FF' link='#80202
    +0' vlink='#FF1010'"; 
    
    use CGI qw/:standard/;
    
    my $pageroot = param('pageroot'); #the root page name, 
    
                      #so $pageroot.marge exists
    my $pagepath = param('pagepath'); #empty for root page, or something l
    +ike -4-6
    my $user = cookie(-name=>'user');
    my $noteline = param('line');
    my $width = param('width');
    
     print "Content-type: text/html\n\n";
    print "<html><head><title>Annotating $pageroot$pagepath around line $n
    +oteline</title></head>";
    print "<body $defaultformatting>";
    print "<center><h2>Annotating</h2>";
    print "<p><small>(Annotating the following piece of text - the line in
    + italics
    is the particular line your annotation will be centred on)</small></ce
    +nter><p>";
    open(MARGE, "$pageroot.marge");
     flock(MARGE,2);
     seek(MARGE,0,0);
    
    my $lines = <MARGE>;
    my ($pagestart, $pagelength);
     for (my $i=1;$i<=$lines;$i++)
     {my $line = <MARGE>;
    
     if ("$line" eq "***$pageroot$pagepath***\n") 
               #make sure parent always comes before child in .marge file
        { $pagestart = $i+3; #not the *** line nor the ano line nor the no
    +tes
          $pagelength = <MARGE>;
          <MARGE>;
          $line = <MARGE>;
          $i+=3;
      }
    
    #Prints some context lines from the annotated text around the line ann
    +otated
    my $diminish=0;
    if ("$line" =~ /\*\\\s*$/) {$diminish=1; $line =~ s/\*\\\s*/ /;} 
    if ($pagestart !=0 && $i+1-$pagestart >= $noteline-2)
    { $line =~ s/\\n|\\p//g;
    if ($i+1-$pagestart == $noteline) {print "<i>$line</i><br>";}
    else {print "$line<br>";}}
    if ($pagestart !=0 && ($i+1-$pagestart >= $noteline+2 || $i >= $pagest
    +art+$pagelength-1)) {last;}
    if ($diminish == 1) {$i--;}
    }
    
    close(MARGE);
    
    print <<EOF;
    <center><p><small>(Type your annotation into the box below, then click
    + the Annotate! button)</small>
    
    <p><form action=\"margedonote.pl\" method=\"POST\">
    <textarea name=\"text\" rows=7 cols=$width></textarea>
    <input type=\"submit\" value=\"Annotate!\">
    <input type=\"hidden\" name=\"pageroot\" value=\"$pageroot\">
    <input type=\"hidden\" name=\"pagepath\" value=\"$pagepath\">
    <input type=\"hidden\" name=\"mode\" value=\"add\">
    <input type=\"hidden\" name=\"line\" value=\"$noteline\">
    <input type=\"hidden\" name=\"width\" value=$width>
    </form>
    
    </center></body></html>
    EOF
     
    
     
    
    ---------------------------------------------------------
    
    #! /usr/pkg/bin/perl -w
    #margedonote.pl - messes with the .marge file for the 
    #                  addition and deletion of notes and for editing stuf
    +f
    
    use strict;
    
    #***CONFIGURATION***
    my $god = "god"; #As in margemain.pl
    
    use CGI qw/:standard/;
    
    my $pageroot = param('pageroot'); #the root page name, 
                      #so $pageroot.marge exists
    my $pagepath = param('pagepath'); #empty for root page, or something l
    +ike -4-6
    my $noteline = param('line');
    my $user;
    
    my $user = cookie(-name=>'user');
    my $mode = param('mode');
    if ("$mode" ne "delete" && "$mode" ne "edit" && "$mode" ne "create") {
    +$mode = "add";}
    my $text = param('text');
    my $width = param('width');
    
    if ($mode eq "add") {$text =~ s/</&lt;/g; $text =~ s/>/&gt;/g;}
        #Disallow all HTML in notes
    
    if ($mode eq "create" && param('convhtml') eq "yes") { 
        $text =~ s/\<br\>/\\n/g;
        $text =~ s/\<p\>/\\p/g;
        }
    
    $text =~ s/[\n\r]//g; #Kills all new line characters
    $text =~ s/\s+/ /g; #Cuts down extended white space
    
    #$text =~ s/\+\\\s*\n/ /g;     # +\ stops wrapping at all 
    
    $text =~ s/\*\>/&nbsp;&nbsp;/g;  #*> becomes 2 spaces
    $text =~ s/\t/\&nbsp\;\&nbsp\;\&nbsp\;\&nbsp\;\&nbsp\;/g; #tabs become
    + 5 spaces
    $text =~ s/\*([^\*]*)\*/<b>$1<\/b>/g; #*x* bold
    $text =~ s/\+([^\+]*)\+/<i>$1<\/i>/g; #+x+ italics
    $text =~ s/\[([^\s\|]*)\]/<a href="margemain.pl\?pageroot=$1">$1<\/a>/
    +g; #[x]
    $text =~ s/\[([^\s\|]*)\|([^\n\|]*)\]/<a href="margemain.pl\?pageroot=
    +$1">$2<\/a>/g; #[x|y]
    $text =~ s/\{([^\s\|]*)\}/<a href="http\:\/\/$1">$1<\/a>/g; #{x}
    $text =~ s/\{([^\s\|]*)\|([^\n\|]*)\}/<a href="http\:\/\/$1">$2<\/a>/g
    +; #{x|y}
    
    #***do custom wrapping***
    my $remaining=$width;
    my $intag=0;
    my $lastspace=-1;
    for (my $pos=0;$pos<=length($text);$pos++) {
        my $char = substr($text, $pos, 1);
        if ($char eq "<") {$intag=1;}
        if ($intag==0) {$remaining--;} 
        if ($char eq ">") {$intag=0;}
        if ($char eq " " && $intag==0) {$lastspace=$pos;}
        if ($remaining==0) {
            if ($lastspace==-1) {$lastspace=$pos;}
            $text = substr($text, 0, $lastspace+1)."\n".substr($text,$last
    +space+1);
            $pos=$lastspace+2;
            $lastspace=-1;
            $remaining=$width;
            }
        if ($char eq "\\") {
            my $nextchar = substr($text,$pos+1,1);
            if (($nextchar eq "n" && $remaining != $width-1) || ($nextchar
    + eq "p" && $remaining==$width-1)) {
                $text = substr($text, 0, $pos)."\n".substr($text,$pos); 
                $pos+=2;
                $remaining=$width;
                }
            if ($nextchar eq "p" && $remaining!=$width-1) {
                $text = substr($text, 0, $pos)."\n\n".substr($text,$pos); 
                $pos+=3;
                $remaining=$width;
                }
    
            }
        }
    #$text = $text."\n";
    
    
    my $textlines = $text =~ tr/\n/\n/;
    $textlines++;
    # print "Content-type: text/html\n\n";
    
    
    open(MARGE, "$pageroot.marge") or die("Can't open $pageroot.marge for 
    +reading");
    flock(MARGE,2);
    seek(MARGE,0,0);
    my @page = <MARGE>;
    close(MARGE);
    
    #***Annotation***
    my $line;
    
    if ($mode eq "add")
    {my $max=0;
    foreach $line (@page) {
         chomp($line);
         if ($line =~ /\*\*\*$pageroot$pagepath-(\d+)\*\*\*/)
         {if ($1<$noteline && $1>$max) {$max=$1;}
      }    
     }
     
     
     open(MARGE, ">$pageroot.marge") or die("Can't open $pageroot.marge fo
    +r writing");
     flock(MARGE,2);
     seek(MARGE,0,0);
     
    my $count = 0;
    my $watch=0;
    my $location;
     foreach $line (@page) {
         $count++;
         chomp($line);
         if ($watch==1 && $line =~ /\*\*\*$pageroot$pagepath-(\d+)\*\*\*/)
         {$location = $count-1; $watch=0;}
         if (($max != 0 && $line eq "***$pageroot$pagepath-$max***") || ($
    +max == 0 && $line eq "***$pageroot$pagepath***"))
         {$watch=1;}
         if ($line eq "***end***" && $location == 0) {$location = $count-1
    +;}
     }
     
    my $firstline=1;
    my $weirdify=0;
    $count=0;
     foreach $line (@page) {
         $count++;
         chomp($line);
         if ($firstline == 1) {$line=($line+4+$textlines); $firstline=0;}
         if ($weirdify == 1) {$line++; $weirdify = 0;}
         if ($weirdify == 2) {$weirdify = 1;}
         if ($line eq "***$pageroot$pagepath***") {$weirdify = 2;} 
         print MARGE "$line\n";
         if ($count==$location)
         {print MARGE "***$pageroot$pagepath-$noteline***";
          print MARGE "\n$textlines";
          print MARGE "\n0";
          print MARGE "\n$text\n";}
     }
     close(MARGE);
     
    
    # print "click <a href=\"margemain.pl?pageroot=$pageroot&pagepath=$pag
    +epath&user=$user\">here</a>";
     print "Location: margemain.pl?pageroot=$pageroot&pagepath=$pagepath&m
    +essage=Your annotation has been successfully made. Huzzah, eh?\n\n";
    }
    
    #***Note deletion***
        #(Can only prune from the outside in - 
        #i.e., only unannotated annotations may be deleted.
    
    if ($mode eq "delete" && $user eq "$god"){
     open(MARGE, ">$pageroot.marge") or die("Can't open $pageroot.marge fo
    +r writing");
     flock(MARGE,2);
     seek(MARGE,0,0);
    
        my $weirdify =0;
        my $writeoff =0;
        my $upnote = $pagepath;
        my $line;
        while (chop($upnote) ne "-") {;}
        foreach $line (@page){
        chomp($line);
        if ($weirdify == 1) {$line--; $weirdify = 0;}
        if ($weirdify == 2) {$weirdify--;}
        if ($line eq "***$pageroot$upnote***") {$weirdify = 2;}
        if ($writeoff == 1 && "$line" =~ /^\*\*\*/) {$writeoff = 0;}
        if ($line eq "***$pageroot$pagepath***") {$writeoff = 1;}
        if ($writeoff == 0) {print MARGE "$line\n";}
        }
     close(MARGE);
    #    print "click <a href=\"margemain.pl?pageroot=$pageroot&pagepath=$
    +upnote&user=$user\">here</a>";
     print "Location: margemain.pl?pageroot=$pageroot&pagepath=$upnote&mes
    +sage=You just lopped off a bit of this site. Hope you know what you'r
    +e doing, and mean well.\n\n";
    }
    
    #***Editing a page or note***
    
    if ($mode eq "edit" && $user eq "$god") {
        
        open(MARGE, ">$pageroot.marge") or die("Can't open $pageroot.marge
    + for writing");
         flock(MARGE,2);
        seek(MARGE,0,0);
        
        my $countdown = -1;
        my $writeoff=0;
        my $line;
        foreach $line (@page){
            chomp($line);
            if ($countdown == 2) {$line = $textlines;}
            if ($countdown == 0) {$writeoff=1; print MARGE "$text\n";}
            if ($countdown >= 0) {$countdown--;}
            if ($line eq "***$pageroot$pagepath***") {$countdown=2;}
            if ($writeoff==1 && "$line" =~ /^\*\*\*/) {$writeoff=0;}
            if ($writeoff==0) {print MARGE "$line\n";}
            }
        close(MARGE);
         print "Location: margemain.pl?pageroot=$pageroot&pagepath=$pagepa
    +th&message=I am not what I used to be\n\n";
        }
    
    #***Creating a new page***
    if ($mode eq "create" && "$user" eq "$god") {
    my $filelines=$textlines+11; #9 needed for stuff, and a couple extra j
    +ust in case
    my $filename = param('createfilename');
    my $notewidth = param('createnotewidth');
    my $title = param('createtitle');
    my $formatting = param('createformatting');
    open(MARGE, ">$filename.marge") or die("Can't open $pageroot.marge for
    + writing");
    print MARGE "$filelines\n";
    print MARGE "$notewidth\n";
    print MARGE "$title\n";
    print MARGE "$formatting\n";
    print MARGE "$width\n";
    print MARGE "***$filename***\n";
    print MARGE "$textlines\n";
    print MARGE "0\n";
    print MARGE "$text\n";
    print MARGE "***end***\n";
    close(MARGE);
    # print "Content-type: text/html\n\n";
    #print "<html><body>The margification was a success.</body</html>";
    print "Location: margemain.pl?pageroot=$filename&message=You made this
    +\n\n"; 
    }
    
    
    
    
    ---------------------------------------------------------
    
    
    #! /usr/pkg/bin/perl -w
    #margeedit.pl - provides a form for editing a page/note
    
    use strict;
    
    #***CONFIGURATION***
    my $god = "god"; #As in margemain.pl
    
    #textarea dimensions
    my $width=60;
    my $height=40;
    
    use CGI qw/:standard/;
    
    my $pageroot = param('pageroot'); 
    my $pagepath = param('pagepath');
    
    my $user = cookie(-name=>'user');
    print "Content-type: text/html\n\n";
    if ("$user" ne "$god") {print "Sorry, only gods have editorial powers.
    +\n";}
    else {
    my $physwidth;
    if ($pagepath eq "") {$physwidth = param('mainwidth');}
    else {$physwidth = param('notewidth');}
    
    print <<EOF;
    <html><head><title>marge - editing $pageroot$pagepath</title></head>
    <body><center><h3>Editing $pageroot$pagepath</h3></center>
    Annotations will remain on the same physical lines they did before the
    + edit,
    so any significant editing could make things go poo-poo (as they say).
    + 
    <b>You have been warned</b>.
    <form action='margedonote.pl' method='POST'>
    EOF
    
    open(MARGE, "$pageroot.marge") or die("Couldn't open $pageroot.marge, 
    +which is bad.");
    flock(MARGE,2);
    seek(MARGE,0,0);
    my @page = <MARGE>;
    close(MARGE);
    
    #$countdown=0;
    #foreach $line (@page) {
    #    chomp($line);
    #    if ($countdown==1) {$countdown=0; $height=$line*2;}
    #    if ("$line" eq "***$pageroot$pagepath***") {$countdown=1;}
    #}
    
    print "<textarea name=text rows=$height cols=$width >";
    
    
    my $active=0;
    my $countdown=-1;
    my $text = '';
    my $line;
    foreach $line (@page) {
        chomp($line);
        if ("$line" =~ /^\*\*\*/) {$active=0;}
        if ($countdown>=0) {$countdown--; if ($countdown==0) {$active=1;}}
        if ($active==1) {$text = $text."$line\n";}
        if ("$line" eq "***$pageroot$pagepath***") {$countdown=3;}
         }    
    
    #Custom line breaks, in accordance with \n and \p escapes in file.
    $text =~ s/[\n\r]//g; 
    $text =~ s/\\p/\n\n\\p/g;
    $text =~ s/\\n/\n\\n/g;
    
    print <<EOF;
    $text
    </textarea>
    <input type='submit' value='Edit'>
    <input type='hidden' name='pageroot' value='$pageroot'>
    <input type='hidden' name='pagepath' value='$pagepath'>
    <input type='hidden' name='mode' value="edit">
    <input type='hidden' name='user' value='$user'>
    <input type='hidden' name='width' value=$physwidth>
    </form></body></html>
    EOF
    }
    
    
    ---------------------------------------------------------
    
    
    #! /usr/pkg/bin/perl -w
    #margificator.pl - Outputs a form for page creation
    use strict;
    
    #***CONFIGURATION***
    my $god = "god"; #As in margemain.pl
    
    #textarea size - purely aesthetic
    my $height=40;
    my $width=60;
    
    use CGI qw/:standard/;
    
    
    my $user = cookie(-name=>'user');
    print "Content-type: text/html\n\n";
    if ($user ne "$god") {
        print "Sorry, but mortals such as yourself may not be trusted with
    + the secrets of margification.";}
    else { 
    print <<EOF;
    <html><head><title>margificator</title></head>
    <body bgcolor='#202000' text='#3080FF' link='#802020' vlink='#FF1010'>
    +<center><h3>Margificator V1.0</h3></center>
    Please read the <a href='margemain.pl?pageroot=instructions'>instructi
    +ons</a> before using this.
    <p><form action='margedonote.pl' method='POST'>
    <p>Page title - <input type=text name='createtitle' size=30 maxlength=
    +150>
    <p>Filename - <input type=text name='createfilename' size=16 maxlength
    +=16> .marge
    <p>Page width - <input type=text name='width' size=3 maxlength=3 value
    +=80>
    <p>Note width - <input type=text name='createnotewidth' size=3 maxleng
    +th=3 value=40>
    <p>Formatting - &lt;BODY <input type=text name='createformatting'size=
    +30 maxlength=2048>&gt;
    <p>Page text - <textarea name='text' rows=$height cols=$width></textar
    +ea>
    <input type='hidden' name='mode' value='create'>
    <p>Convert HTML?<input type='checkbox' name='convhtml' value='yes'>
    <input type='submit' name='Margify!'>
    </form>
    EOF
    }
    
    
    ---------------------------------------------------------
    
      History of this node :
    • 7.7.02 (approx) - put it up
    • 11.7.02 - made a few changes - fixed a stupid bug which added a line at the end of annotations, made it all "use CGI" instead of hand rolled parameter processing.
    • 12.7.02 - now uses CGI module for cookies, too.
    • 9.8.02 - fixed a heinously stupid bug in create section of margedonote. Looked into Javascript - don't think it's possible.
      Plans :
    • Do all the stuff thraxil said to do below
    • Let the annotator select some text to underline along with the note. Intend to do with a textarea and OnSelect, as soon as I get round to relearning Javascript. One sleepless night, perhaps.
  • Replies are listed 'Best First'.
    Re: Marge - the Interactive Marginalia Processor
    by thraxil (Prior) on Jul 08, 2002 at 21:33 UTC

      a few things:

      1. use CGI; this is of supreme importance.
      2. use strict; and -w
      3. taint checking. using -T would inform you that you are doing some very dangerous things using code like open(MARGE,">$pageroot.marge") where $pageroot just comes straight in from a parameter without any kind of scrubbing.
      4. the code would be much more readable if the HTML were seperated out via some kind of templating system like HTML::Template.
      5. code like if("$link" eq "some string") is bad. perl will treat $link as a string automatically; quoting it serves no purpose.

      anders pearson