Compare commits

..

2 Commits
mojo ... master

Author SHA1 Message Date
52f9d258d4 Update master README to contain info on the different branches
Signed-off-by: peteyboy <peteyboy@noreply.localhost>
2022-04-20 07:16:43 +00:00
a38abfdf7e Updated master branch to contain the CGI::Tiny version with mocked
PATH_INFO, but supporting a 'neutral' environment (not SDF), and playing
out of /cgi-bin.
2022-04-20 06:20:34 +00:00
2 changed files with 169 additions and 101 deletions

View File

@ -2,11 +2,14 @@
one-page app that turns wordle shares into standard file format used in the Conway's Game of Life community and sets it up for running in embedded Lifeviewer on the same page
## Project Branches
There are 3 branches to this project. There is just one CGI file and a version of the README.md in each one. Use the one that works for you. If at SDF, use the sdf branch. Otherwise master or mojo branches will work:
* *sdf* is the original branch, a CGI set up to run on SDF.org. I had to abandon using Mojolicious Lite, but found a nice way to do it using CGI:Tiny and the Mojo Templates. This version also uses a path_info URL parameter to manage routing, and mocks path_info, since is doesn't work on SDF's nginx web server setup.
* *master* is a branch made to be mostly the same as the SDF branch, using CGI::Tiny, but with more normal paths and not trying to pick out a separate user-level perl library. It still uses the path_info parameter, which actuall works pretty well for the simple case, I put it to, at least.
* *mojo* is a rewrite of the CGI part of the script using Mojolicious Lite and expecting that path_info is honored for CGI scripts. It uses routing based off the path_info (it's like *magic*) instead of a url parameter. I got this to run very nice with a handmade Mojolicous Daemon web server script, using the Mojolicous CGI plugin. It would have been so much less sweat to do this from the get-go, but I learned a lot banging my head against the wall, I guess.
## Dependencies
This version was rewritten to what I meant to do in the first place, use Mojolicious Lite and take advantage of PATH_INFO routing, so it is simpler. You should be able to run it in any environment where CGI is properly passed the PATH_INFO (SDF has not configured its nginx setup to do so).
This single CGI file is expecting to be run out of the /cgi-bin directory. to change the location, you should update $ORIGIN_PAGE to what you expect. You also may need to change the path to /lv-plugin.js in the <script> tags of the html templates in the __DATA__ section.
This app is not at all packaged. You'll have to make sure all the perl modules you need are installed. If you are on a community unix server with just user rights (like, say SDF), you may need to ask an admin to install for you, or alternately, you can make a personal perl library in your user space.
Here's what I used to [install the perl module I needed with cpanm](https://stackoverflow.com/questions/2980297/how-can-i-use-cpan-as-a-non-root-user). It's just:
@ -18,7 +21,13 @@ Here's what I used to [install the perl module I needed with cpanm](https://stac
then for each package, you can run *cpanminus*:
$> cpanm Mojolicious::Lite
$> cpanm CGI::Tiny
...
$> cpanm Mojo::Template
...
$> cpnam Mojo::Loader
...
$> cpanm Readonly
...
Also, the Life Viewer is a super-cool javascript plugin app from the people at [Conwaylife.com](https://conwaylife.com) that is expected to be in the same directory as the CGI file. It is available with instructions [here](https://conwaylife.com/wiki/Tutorials/LifeViewer_JavaScript_plug-in)

View File

@ -2,90 +2,122 @@
use strict;
use warnings;
use utf8;
use Mojolicious::Lite -signatures;
#add my user perl library path, this is particular to each person's setup
#use lib qw( /usr/pkg/lib/perl5/5.24.0 /meta/p/peteyboy/perl5/lib/perl5 );
#make request for all of the following
use CGI::Tiny;
use Mojo::Template;
use Mojo::Loader 'data_section';
use Readonly;
#use List::Util qw(first);
use URI;
use experimental 'smartmatch';
#===
# MAKE SURE ORIGIN PAGE MATCHES FILENAME (especially if you are testing changes)!
#
#===
my $ORIGIN_PAGE = "/cgi-bin/wordle-life.cgi";
my $defaulttext = "Paste your wordle share here (replace this text)";
my $PATH_PARAM = "PATH_INFO";
my $WORDLE_INPUT_PARAM = "wordle-result";
my $SHARENOTE_PARAM = "share-note";
my $ORIGIN_PAGE = "/cgi-bin/wordle-life.cgi";
my $defaulttext = "Paste your wordle share here (replace this text)";
my $PATH_PARAM = "path_info";
my $WORDLE_INPUT_PARAM = "wordle-result";
my $SHARENOTE_PARAM = "share-note";
my $wordle;
my $rle = "";
my $isshare = 0; #flag for sharing, default false
my $sharenote = ""; #a note someone can tack onto their share
cgi {
get '/' => sub ($cgi) {
#error handler from the CGI:Tiny cookbook
my $cgi = $_;
$cgi->set_error_handler(sub {
my ($cgi, $error, $rendered) = @_;
warn $error;
unless ($rendered) {
if ($cgi->response_status_code == 413) {
$cgi->render(json => {error => 'Request body limit exceeded'});
}elsif ($cgi->response_status_code == 400) {
$cgi->render(json => {error => 'Bad request'});
} else {
$cgi->render(json => {error => 'Internal server error'});
}
}
});
#if there are sharing query parameters put them in $wordle and activate share page logic
if (first { $_ eq $WORDLE_INPUT_PARAM } @{$cgi->req->query_params} ){
$wordle = $cgi->param($WORDLE_INPUT_PARAM);
$isshare = 1; #true
$sharenote = $cgi->param($SHARENOTE_PARAM);
}else{
my $wordle;
my $method = $cgi->method;
my $rle = "";
my $isshare = 0; #flag for sharing, default false
my $sharenote = ""; #a note someone can tack onto their share
my $navigate = 0; #flag to go to different page, default false
#Simple switch: make a one page app, and always go back to the same page.
#GET or HEAD loads the page blank, POST runs the Life file maker
if ($method eq 'HEAD') {
$wordle = $defaulttext;
}
my $template_name = 'index';
$cgi->stash( WORDLE_INPUT_PARAM => $WORDLE_INPUT_PARAM, ORIGIN_PAGE => $ORIGIN_PAGE, defaulttext => $defaulttext, wordle => $wordle, rle => $rle, isshare => $isshare, sharenote => $sharenote );
$cgi->render(template => $template_name);
};
get '/*path_info' => sub ($cgi) {
my $template_name = $cgi->stash("path_info");
#TODO: Check against a list of valid templates!
if ( $template_name ~~ ['about'] ) {
$cgi->stash( ORIGIN_PAGE => $ORIGIN_PAGE); #this isn't general purpose, what template needs what variables?
}else{ #TODO: fix so as not to need copied code from the else below?
$template_name = 'index';
$cgi->stash( WORDLE_INPUT_PARAM => $WORDLE_INPUT_PARAM, ORIGIN_PAGE => $ORIGIN_PAGE, defaulttext => $defaulttext, wordle => $wordle, rle => $rle, isshare => $isshare, sharenote => $sharenote );
}
$cgi->render(template => $template_name);
};
post '/' => sub($cgi) {
#textarea name, pull the value from POST and reload into textarea
$wordle = $cgi->param($WORDLE_INPUT_PARAM);
#if input textarea not changed from default text, do nothing
unless ($wordle =~ /\Q$defaulttext\E/ or $wordle eq '') {
#if they put in something that looks like an RLE, pass output through, for share or to just use the life viewer
if ($wordle =~ /^#[Cc] /){
} elsif ($method eq 'GET') {
#Check path_info to see if we are navigating (ignoring sharing query params if there?)
#if (first { $_ eq $PATH_PARAM } @{$cgi->query_param_names} ){
if ( $PATH_PARAM ~~ @{$cgi->query_param_names} ){
$navigate = mock_path_info($cgi->query_param($PATH_PARAM)); #true and populated
}
#if there are sharing query parameters put them in $wordle and activate share page logic
elsif ($WORDLE_INPUT_PARAM ~~ @{$cgi->query_param_names} ){
$wordle = $cgi->query_param($WORDLE_INPUT_PARAM);
$isshare = 1; #true
$sharenote = $cgi->query_param($SHARENOTE_PARAM);
}else{
$wordle = $defaulttext;
}
} elsif ($method eq 'POST') {
#textarea name, pull the value from POST and reload into textarea
$wordle = $cgi->body_param($WORDLE_INPUT_PARAM);
#if input textarea not changed from default text, do nothing
unless ($wordle =~ /\Q$defaulttext\E/ or $wordle eq '') {
#[Cc] /){ #cheat, if they put in something that looks like an RLE, pass output through
if ($wordle =~ /^#[Cc] /){
$rle = $wordle;
#if none of that, do the thing: generate RLE
}else{
}else{
$rle = generate_rle($wordle);
}
}
}
}
#$output = $mt->render($template, { WORDLE_INPUT_PARAM => $WORDLE_INPUT_PARAM, ORIGIN_PAGE => $ORIGIN_PAGE, defaulttext => $defaulttext, wordle => $wordle, rle => $rle, sharenote => $sharenote });
} else { #some other request? PUT, DELETE?
$cgi->set_response_status(405)->render;
exit;
}
#die "Invalid wordle parameter" unless length $wordle;
#Load template from DATA section and output
#my $mt = Mojo::Template->new(auto_escape => 1, vars => 1);
my $mt = Mojo::Template->new(vars => 1);
my $template;
my $output;
$cgi->stash( WORDLE_INPUT_PARAM => $WORDLE_INPUT_PARAM, ORIGIN_PAGE => $ORIGIN_PAGE, defaulttext => $defaulttext, wordle => $wordle, rle => $rle, isshare => $isshare, sharenote => $sharenote );
$cgi->render(template =>'index');
};
app->start;
sub template_from_path_info {
my $template='';
my $path = shift;
if ($path =~ /^\//){
$template = $path;
$template =~ s/^.//;
$template =~ tr/\//./;
}
return $template;
}
#TODO: this is ugly; clean up
if ($method eq 'GET' && $navigate ) {
my $template_name = template_from_path_info ($navigate);
#TODO: Check against a list of valid templates!
#if ( grep ( /^$template_name$/, ('index.html.ep', 'about.html.ep') )) {
if ( $template_name ~~ ['index.html.ep', 'about.html.ep'] ) {
$template = data_section __PACKAGE__, $template_name;
$output = $mt->render($template, { ORIGIN_PAGE => $ORIGIN_PAGE }); #this isn't general purpose, what template needs what variables?
}else{ #TODO: fix so as not to need copied code from the else below?
$template = data_section __PACKAGE__, 'index.html.ep';
$output = $mt->render($template, { WORDLE_INPUT_PARAM => $WORDLE_INPUT_PARAM, ORIGIN_PAGE => $ORIGIN_PAGE, defaulttext => $defaulttext, wordle => $wordle, rle => $rle, isshare => $isshare, sharenote => $sharenote });
}
}else{
$template = data_section __PACKAGE__, 'index.html.ep';
$output = $mt->render($template, { WORDLE_INPUT_PARAM => $WORDLE_INPUT_PARAM, ORIGIN_PAGE => $ORIGIN_PAGE, defaulttext => $defaulttext, wordle => $wordle, rle => $rle, isshare => $isshare, sharenote => $sharenote });
}
$cgi->render(html => $output );
};
#Try to Generate the RLE. Makes a lot of assumptions
sub generate_rle {
sub generate_rle (\$) {
my $headline = "";
my $rleline = "";
@ -118,6 +150,7 @@ sub generate_rle {
$myline =~ s/:green_square:/o/g;
$myline =~ s/:blue_square:/o/g; #HC equiv yellow
$myline =~ s/:orange_square:/o/g; #HC equiv green
$myline =~ s/:red_square:/o/g; #wordum red fails
$myline =~ s/:[a-z_]*square:/b/g;
@ -126,6 +159,7 @@ sub generate_rle {
$myline =~ s/\N{U+1f7e9}/o/g; #green hit
$myline =~ s/\N{U+1F7E6}/o/g; #blue
$myline =~ s/\N{U+1F7E7}/o/g; #orange
$myline =~ s/\N{U+1F7E5}/o/g; #red
$myline =~ s/\N{U+2B1B}/b/g; #black
@ -138,6 +172,7 @@ sub generate_rle {
$myline =~ s/green_square/o/g;
$myline =~ s/blue_square/o/g;
$myline =~ s/orange_square/o/g;
$myline =~ s/red_square/o/g; #wordum red fails
@ -153,6 +188,29 @@ sub generate_rle {
return "$headline\n$row_prefix$col_prefix$rowcount\n$rleline\n$lifeviewer_settings";
}
#Because SDF's nginx config is broken, we have to use query params to *navigate*
sub mock_path_info {
my $uri = URI->new (shift);
return $uri->path;
}
sub template_from_path_info {
my $template='';
my $path = shift;
if ($path =~ /^\//){
$template = $path;
$template =~ s/^.//;
$template =~ tr/\//./;
$template = $template . ".html.ep";
}
return $template;
}
__DATA__
@@ index.html.ep
@ -163,27 +221,24 @@ __DATA__
<link rel="stylesheet" href="https://unpkg.com/some-nice-basic-css/global.css" />
<style>
.
lv-rle{
min-height:200px;
.lv-rle{
min-height:140px;
}
</style>
<meta name="LifeViewer" content="viewer textarea"> <!--required tag-->
<script src="/lv-plugin.js"></script>
</head>
<body>
<p align= left> <a href="/">back to Site Home</a>
</p>
<body><h1>Wordle->Life</h1>
<h1>Wordle->Life</h1>
<p align= right> <a href="<%=$ORIGIN_PAGE%>/about">about</a></p>
<p align= right> <a href="<%=$ORIGIN_PAGE%>?path_info=/about">about</a>
% if ($isshare) {
<span>
<h3> Hey, someone wants to share their Wordle->Life score with you!</h3>
%if ($sharenote){
<p>They said: <em><%= $sharenote =%></em></p>
<p>They said:<em><%= $sharenote %></em></p>
%};
<p><strong>Skip to step 2 of the instructions below and see the shared wordle-life happen!</strong></p>
</span>
@ -220,15 +275,16 @@ lv-rle{
<!--viewer container-->
<div class="viewer" class="flow">
<label for="wordle">
<p>If the conversion is successful, you should see your Wordle converted to a Run Length Encoded Conway Life file (RLE) in this box:</p>
<p>If the conversion is successful, you should see your Wordle converted to a Run Length Encoded Conway Life file in this box:</p>
</label>
<label for="wordle">Converted RLE:</label>
<textarea id="life" name="life-file" rows="12" cols="50" style="vertical-align: middle" class="lv-rle" class="flow">
<textarea id="life" name="life-file" rows="9" cols="50" style="vertical-align: middle" class="lv-rle" class="flow">
<%== $rle %></textarea>
<p>If the RLE is correct, it should be loaded in the Life Viewer, and so you are ready to go!</p>
<ul>
<li><p>For best results with the Life Viewer, you may adjust the default Zoom and the playback speed, then press the Play button:</p>
<canvas height=400 width=450></canvas></li>
<canvas height=400 width=400></canvas></li>
<li><p>Press "[Pause]" when it seems stable. </p>
</li>
</ul>
@ -248,15 +304,16 @@ lv-rle{
% if ($rle ne ""){
<span>
<p><strong> Share your wordle-life!</strong></p>
<p> You can copy/paste the RLE into your favorite social media post or email</p>
<p><em>OR</em></p>
<p> Click this button to go to a share page where you can copy/paste the url to someone</p>
<form id="life-share" name="life-share" action="<%== $ORIGIN_PAGE %>" method="GET" class="flow">
<p><strong> Share your wordle-life!</strong>
<p> You can copy/paste the RLE into your favorite social media post or email
<br><em>OR</em><br>
Click this button to go to a share page where you can copy/paste the URL to someone:</p>
<textarea id="share-rle" name="<%= $WORDLE_INPUT_PARAM %>" rows="9" cols="50" style="display:none;">
<%=$rle %></textarea>
<input id="share-note" type="text" name="share-note" placeholder="Include a note, if you'd like" style="vertical-align: bottom">
<%=$rle %>
</textarea>
<input type="submit" value="Go to sharing page">
<input id="share-note" type="text" name="share-note" placeholder="Add a note if you'd like" style="vertical-align: bottom">
</form>
% };
</span>
@ -305,39 +362,41 @@ GRID
<p>
</body></html>
@@ about.html.ep
<html>
<head>
@@ about.html.ep
<html>
<head>
<title>About Wordle-Life</title>
<link rel="stylesheet" href="https://unpkg.com/some-nice-basic-css/global.css" />
<!-- <link rel="stylesheet" href="https://peteyboy.freeshell.org/air.css"> -->
</head>
<body><h1>Wordle->Life</h1>
% my $home_path= "$ORIGIN_PAGE";
% my $current_path = "$home_path?path_info=/about";
<p>
<p> This is an about page. </p>
<p> Click <a href=<%== $ORIGIN_PAGE %>>here</a> to return to Wordle-Life page </p>
<p> Click <a href="/">here</a> to go to website Home</p>
<p> Click <a href=<%== $home_path %> >here</a> to go Home </p>
<p> Click <a href=<%== $current_path %> >here</a> to stay put </p>
<p>
<hr 100%>
<em>Disclaimer: I made this! People and/or corporations may own their marks or copyrights, etc, on words mentioned on these pages I don't claim to</em>
<em>Disclaimer: I made this! People and/or corporations may own their marks or copyrights, etc, on words mentioned on this page I don't claim to</em>
<p>
Thanks to <a href="https://github.com/hankchizljaw/some-nice-basic-css"> hankchizljaw</a> for making his basic css publicly available
<p> Made in February 2022, revised April 2022 for this site, using Perl Mojolicious
<p> Made in February 2022
<p>
<center>
Hosted by Bisect Hosting
<p><a href="https://www.bisecthosting.com"><img src=https://www.bisecthosting.com/images/logos/logo.svg alt="bisecthosting.com" height= 10% width=auto></a>
</center>
</body></html>
</body></html>