Skip to content

Commit

Permalink
Merge remote branch 'remotes/petdance/master'
Browse files Browse the repository at this point in the history
Conflicts:
	crank
  • Loading branch information
shlomif committed Feb 13, 2010
2 parents 1054f29 + 8b6be7d commit 87b70b8
Show file tree
Hide file tree
Showing 26 changed files with 2,304 additions and 154 deletions.
3 changes: 3 additions & 0 deletions .gitignore
@@ -0,0 +1,3 @@
*~
build/
.DS_Store
25 changes: 25 additions & 0 deletions INSTALL
@@ -0,0 +1,25 @@
These are short installation instructions. They contain what needs to be done
in order to install and play around with the Perl101.org codebase.

- Install a webserver (apache, lighttpd, etc.).
- Create the "build" folder for the "crank" script.
- Make sure that the "href" path defined in "section.tt"'s javascript matches
where your CGI folder will be. It assumes it well be in "/cgi-bin".
- Make sure that the path for the captcha pictures hardcoded in
"tt/show_captcha.tt" is good for you.
- If you need to adjust the path in "show_captcha.tt", recompile the templates
using the following command:
$ jemplate --compile tt/show_captcha.tt tt/show_result.tt > static/js/jmpls.js
(you must have Jemplate installed)
- Run the crank script.
- Copy the built HTML files in your "build" folder into the document root of
your Perl101.
- Copy the "static" folder to the same document root.
- Copy over the formmail.pl and auth.pl into the appropriate CGI folder
(usually this would be "cgi-bin")
- Make sure the paths for $data_folder in both files reflects where you want
to keep the captchas. I would recommend writing a full path.
- Change the $recipient to your email address in "formmail.pl".
(you can comment off the entire Email::Stuff part to avoid emails entirely)

Written by Sawyer X (2009)
27 changes: 13 additions & 14 deletions Makefile
Expand Up @@ -2,24 +2,23 @@
crank \ crank \
clean clean


BUILD=build
SOURCE=s

default: crank default: crank


crank: crank: clean
rm -fr 101/*.html mkdir -p $(BUILD)/ || true > /dev/null 2>&1
mkdir 101/ || true > /dev/null 2>&1 perl crank --podpath=$(SOURCE) --buildpath=$(BUILD)
perl crank 101.pod cp -R static/* $(BUILD)/
rsync -azu --delete \
--exclude=.svn --exclude='*~' \
static/ 101/static/
rsync -azu --delete \
--exclude=.svn --exclude='*~' \
s/ 101/s/
cp s/*.ico 101/


clean: clean:
rm -fr 101/ rm -fr $(BUILD)

test: crank
prove t/html.t


# This is only useful for Andy # This is only useful for Andy
rsync: rsync:
rsync -azu -e ssh --delete \ rsync -azu -e ssh --delete --verbose \
101/ petdance@midhae.pair.com:~/p/ $(BUILD)/ andy@huggy.petdance.com:/srv/p101
59 changes: 59 additions & 0 deletions README
@@ -0,0 +1,59 @@
These should be instructions for how the crank operation works,
what the directory structure is and how to generate perl101.org.

If anything is unclear, feel free to email me at xsawyerx@cpan.org or send
a patch for the documentation.

Perl101.org code has several components:
* The (source) POD files under the "s" folder:
The POD files are the core content of perl101.org. It's written in
POD form. You can read the documentation of how to write POD on perldoc,
using either the command "perldoc perlpod" or googling for "perlpod".
These POD files are renderes to HTML by the generator script documented
below.

* The static JS, images and design under the "static" folder:
Some things are static, such as images, the CSS layout, javascript
functions and the sorts. You can find those in the "static" folder.
There is basic CSS for handheld, print and web.
Currently the only image is the Perl101 logo.
The Javascript files are the Jemplate engine (Jemplate can be found on
CPAN) and a compiled Template Toolkit template file to Javascript.

* The Template::Toolkit templates under the "tt" folder:
The generation engine uses the HTMLified POD content in templates in order
to render the complete website. These are the templates in the "tt" folder.
There is one single template called "show.tt" which is not directly used.
It is compiled to javascript using "jemplate" command line tool and then
put in the JS folder in "static".

Each section in Perl101.org is rendered independently using the
"section.tt" template. If you want to control each section, that's the
template you want to check out.

It's simple HTML with Template::Toolkit tags. Template::Toolkit can be
found on CPAN.

* All the generated files under the "build" folder:
Once the generation script generated the files, it saves them in the
"build" folder. The folder doesn't exist by default (Perl101.org is on
Github.com and Git cannot monitor empty folders so you'll need to create
this folder yourself. The generation script will alert you if it is
missing.

This folder can be seen as the "htdocs" or "httpdocs" people are used
to.

* The CGI scripts used for mailing and image creation, in the main folder:
Right now there are only two scripts:
- "auth.pl": this script is not production-ready yet. It's creates
captchas for the feedback form. It will try to create them in the
default 'captcha' folder. You'll probably have to create it.
- "formmail.pl": this script sends the form submitted (via AJAX)
and returns an answer or error.

* The generator script in the main folder:
The generator script goes over all the POD files in "s", renders them using
the templates in folder "tt" and then puts them as static files in the
"build" folder. You can specify a different "build" folder or it will
by default look for a folder named "build".
91 changes: 91 additions & 0 deletions auth.pl
@@ -0,0 +1,91 @@
#!/usr/bin/perl

use strict;
use warnings;

use File::stat;
use File::Spec;
use File::Slurp;
use Digest::MD5 'md5_hex';
use CGI::Simple;
use Authen::Captcha;
use GD::SecurityImage;

sub create_formula {
my @numbers = ( 0 .. 20 );
my %operators = (
'+' => sub { $_[0] + $_[1] },
'-' => sub { $_[0] - $_[1] },
'*' => sub { $_[0] * $_[1] },
'/' => sub { $_[0] * $_[1] },
);
my ( $num1, $op, $num2, $accepted );

while ( ! $accepted ) {
$num1 = $numbers[ rand scalar @numbers ];
$num2 = $numbers[ rand scalar @numbers ];
$op = ( keys %operators )[ rand scalar keys %operators ];

# avoiding edge cases in division
if ( $op eq '/' ) {
if ( $num2 == 0 ) {
# avoiding division by zero
next;
} elsif ( $num1 % $num2 != 0 ) {
# check if easily divisable
next;
}
}

$accepted++;
}

return [ "$num1 $op $num2", $operators{$op}->( $num1, $num2 ) ];
}

sub create_image {
my $text = shift;

# Create a normal image
my $image = GD::SecurityImage->new(
width => 100,
height => 50,
lines => 3,
#gd_font => 'giant',
rndmax => 3,
);

$image->random($text);
$image->create(normal => 'rect');

return $image;
}

my $cgi = CGI::Simple->new();
my $data_folder = 'captchas';
my ( $formula, $result ) = @{ create_formula() };
my $md5sum = md5_hex($result);
my $image = create_image($formula);
my $really_delete = 1; # set to 0 or '' for testing purposes
my $captcha_timeout = 10; # allow them to exit for 1 minute

-d $data_folder || die "Cannot find data folder ($data_folder)";

my ( $image_data, $mime_type, $random_number ) = $image->out;
my $image_filename = File::Spec->catfile( $data_folder, "$md5sum.png" );

# write the new file
write_file( $image_filename, { binmode => ':raw' }, $image_data );

# delete older files
my $captcha_regex = qr/^\w+\.png$/;

foreach my $file ( read_dir($data_folder) ) {
$file =~ $captcha_regex || next;
my $filename = File::Spec->catfile( $data_folder, $file );

if ( stat($filename)->ctime() < ( time() - $captcha_timeout ) ) {
$really_delete && unlink $filename;
}
}

54 changes: 41 additions & 13 deletions crank
Expand Up @@ -3,20 +3,42 @@
use strict; use strict;
use warnings; use warnings;


BEGIN { use Carp::Always;
eval 'use Carp::Always'; use Getopt::Long;
} use File::Slurp;
use Pod::Simple 3.13;


use Template (); use Template ();
use Template::Constants qw( :debug :chomp ); use Template::Constants qw( :debug :chomp );


my $podpath = 's';
my $buildpath = 'build';

GetOptions(
'podpath:s' => \$podpath,
'buildpath:s' => \$buildpath,
) or exit;

if ( ! -d $buildpath || ! -w $buildpath ) {
die "Buildpath ($buildpath) doesn't exist or is not writable";
}

my %defaults = ( my %defaults = (
<<<<<<< HEAD
INCLUDE_PATH => [ qw( tt ) ], INCLUDE_PATH => [ qw( tt ) ],
OUTPUT_PATH => $ENV{'PERL101_OUT_PATH'} || '/srv/p101/', OUTPUT_PATH => $ENV{'PERL101_OUT_PATH'} || '/srv/p101/',
DEBUG => DEBUG_UNDEF, DEBUG => DEBUG_UNDEF,
TRIM => CHOMP_ALL, TRIM => CHOMP_ALL,
PRE_CHOMP => 1, PRE_CHOMP => 1,
POST_CHOMP => 1, POST_CHOMP => 1,
=======
INCLUDE_PATH => [ qw( tt ) ],
OUTPUT_PATH => $buildpath,
DEBUG => DEBUG_UNDEF,
TRIM => CHOMP_ALL,
PRE_CHOMP => 1,
POST_CHOMP => 1,
>>>>>>> remotes/petdance/master
); );
my $tt = Template->new( \%defaults ); my $tt = Template->new( \%defaults );
Expand All @@ -25,26 +47,26 @@ my $vars = {};
my @podfiles; my @podfiles;
my @sidelinks; my @sidelinks;
for ( get_sections( 's/' ) ) { for ( get_sections( $podpath ) ) {
my ($sectionfile, $sectiontext) = @{$_}; my ($sectionfile, $sectiontext) = @{$_};
my $podfile = "s/$sectionfile.pod"; my $podfile = "$podpath/$sectionfile.pod";
my $htmlfile = "$sectionfile.html"; my $htmlfile = "$sectionfile.html";
push( @sidelinks, { push( @sidelinks, {
filename => $htmlfile, filename => $htmlfile,
text => $sectiontext, text => $sectiontext,
} ); } );
push( @podfiles, { push( @podfiles, {
section => $sectiontext, section => $sectiontext,
podfile => $podfile, podfile => $podfile,
htmlfile => $htmlfile, htmlfile => $htmlfile,
} ); } );
} }
for my $vars ( @podfiles ) { for my $vars ( @podfiles ) {
$vars->{content} = pod2html( $vars->{podfile} ); $vars->{content} = pod2html( $vars->{podfile} );
$vars->{sidelinks} = \@sidelinks; $vars->{sidelinks} = \@sidelinks;
$tt->process( 'section.tt', $vars, $vars->{htmlfile} ) || die $tt->error; $tt->process( 'page.ttml', $vars, $vars->{htmlfile} ) || die $tt->error;
} }
sub get_sections { sub get_sections {
Expand Down Expand Up @@ -87,13 +109,16 @@ sub pod2html {
$parser->html_header_after_title( '' ); $parser->html_header_after_title( '' );
$parser->html_footer( '' ); $parser->html_footer( '' );
# Manually adjust the stuff we passed thru earlier
my $podtext = read_file( $podfile );
$podtext =~ s{P<(.+?)>}{L<$1|http://perldoc.perl.org/$1.html>}g;
$podtext =~ s{M<(.+?)>}{L<$1|http://search.cpan.org/perldoc?$1>}g;
$parser->complain_stderr( 1 ); $parser->complain_stderr( 1 );
$parser->output_string( \$html ); $parser->output_string( \$html );
$parser->parse_file( $podfile ); $parser->parse_string_document( $podtext );
# Manually adjust the stuff we passed thru earlier
$html =~ s{P<(.+?)>}{<a href="http://perldoc.perl.org/$1.html">$1</a>}g;
$html =~ s{M<(.+?)>}{<a href="http://search.cpan.org/perldoc?$1">$1</a>}g;
return $html; return $html;
} }
Expand All @@ -120,6 +145,9 @@ sub new {
$tagmap->{"/$code"} = ">"; $tagmap->{"/$code"} = ">";
} }
$tagmap->{'VerbatimFormatted'} =
qq{\n<pre class="prettyprint lang-perl">\n};
return $self; return $self;
} }
Expand Down
48 changes: 48 additions & 0 deletions formmail.pl
@@ -0,0 +1,48 @@
#!/usr/bin/perl

use strict;
use warnings;

use CGI; # or CGI::Simple ?
use JSON::XS;
use Email::Stuff;

sub error {
my $error_msg = shift;

# we don't really need those since we're updating the page with Jemplate
#my $return_link_url = $cgi->param('return_link_url') || q{};
#my $return_link_title = $cgi->param('return_link_title') || q{};

print encode_json { error => $error_msg };

exit 0;
}

my $cgi = CGI->new();

print $cgi->header( -charset => 'UTF-8' );

my $subject = $cgi->param('subject') || q{};
my $name = $cgi->param('realname') || q{};
my $email = $cgi->param('email') || q{};
my $text = $cgi->param('text') || q{};
my $recipient = q{andy@petdance.com}; # this shouldn't be in the form
my $from = qq{$name <$email>};

if ( !$name || !$text ) {
# a name and text are essential
error('Missing name or text');
}

# this should work but it didn't for me
# maybe it was my sendmail definitions
# but i didn't have the time to debug it
Email::Stuff->from($from)
->to($recipient)
->text_body($text)
->subject($subject)
->send;

print encode_json { success => 'imminent' };

0 comments on commit 87b70b8

Please sign in to comment.