spsp.cgi (7974B) - raw
1 #!/usr/local/bin/perl -T 2 3 # "Simplified Perl Status Poster" 4 # 5 # MIT License 6 # 7 # Copyright (c) 2023 Samir Parikh 8 # 9 # Permission is hereby granted, free of charge, to any person obtaining a copy 10 # of this software and associated documentation files (the "Software"), to deal 11 # in the Software without restriction, including without limitation the rights 12 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 # copies of the Software, and to permit persons to whom the Software is 14 # furnished to do so, subject to the following conditions: 15 # 16 # The above copyright notice and this permission notice shall be included in all 17 # copies or substantial portions of the Software. 18 # 19 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 # SOFTWARE. 26 27 use warnings; 28 use strict; 29 use lib "/home/compiler/perl5/lib/perl5"; 30 use JSON; 31 use POSIX qw( strftime ); 32 use Data::Dumper; 33 use CGI; 34 use Text::Markdown; 35 use File::Spec::Functions; 36 use feature qw( signatures ); 37 no warnings qw( experimental::signatures ); 38 39 # declare constants 40 ### path to spsp directory 41 my $SPSP_PATH = "/home/compiler/programs/spsp"; 42 43 ### file to store json-formatted post history in reverse chronological order 44 my $POST_DATA_FILENAME = "$SPSP_PATH/html/post_data"; 45 46 ### temporary file used to create updated post_data json file 47 my $POST_DATA_TEMP_FILENAME = "$SPSP_PATH/html/post_data_temp.$$"; 48 49 ### HTML file for all fo our posts 50 my $POSTS_HTML_FILENAME = "$SPSP_PATH/html/posts.html"; 51 my $POSTS_REL_PATH_FILENAME = "/posts.html"; 52 53 ### temporary HTML file we use to update posts.html based on data from post_data 54 my $POSTS_TEMP_HTML_FILENAME = "$SPSP_PATH/html/posts_temp.html.$$"; 55 56 ### path for new HTML file for our most recent post 57 my $NEW_POST_HTML_PATH = "$SPSP_PATH/html/"; 58 59 ### length of random ID assigned to each post 60 my $ID_LENGTH = 4; 61 62 ### hash key constants 63 my $ID = "id"; 64 my $TITLE = "title"; 65 my $STATUS = "status"; 66 my $TIMESTAMP = "timestamp"; 67 68 ### HTML constants 69 my $HTML_TITLE = "SPSP"; 70 my $APPLICATION = "Simple Perl Status Poster"; 71 my $AUTHOR = "Samir Parikh"; 72 my $HEADING = "Samir's Updates"; 73 74 my $cgi = CGI->new; 75 my $markdown_to_html = Text::Markdown->new; 76 77 sub generate_post_id ( $id_length ) 78 { 79 my @characters = (0 .. 9, "a" .. "z"); 80 return join '', map $characters[ rand @characters ], 1 .. $id_length; 81 } 82 83 ### check to see if post_data file exists. If not, create it 84 unless ( -e $POST_DATA_FILENAME ) 85 { 86 open my $post_data_filehandle, '>>', $POST_DATA_FILENAME 87 or die "Cannot open $POST_DATA_FILENAME for appending: $!\n"; 88 89 close $post_data_filehandle or die "Error closing $POST_DATA_FILENAME: $!\n"; 90 } 91 92 ### open post data file for reading 93 open my $post_data_filehandle, '<', $POST_DATA_FILENAME 94 or die "Cannot open $POST_DATA_FILENAME for reading: $!\n"; 95 96 ### read all current posts into @posts 97 my $json_posts = JSON->new; 98 my @posts = do { 99 local $/; 100 @{ $json_posts->decode( <$post_data_filehandle> ) }; 101 }; 102 103 ### close post data file 104 close $post_data_filehandle or die "Cannot close $POST_DATA_FILENAME: $!\n"; 105 106 ### assemble the new post into a hash 107 my $new_post = { 108 $ID => generate_post_id( $ID_LENGTH ), 109 $TITLE => scalar $cgi->param( $TITLE ), 110 $STATUS => scalar $cgi->param( $STATUS ), 111 $TIMESTAMP => strftime "%a %d %b %Y %H:%M %Z", localtime, 112 }; 113 114 ### prepend new post to the array of posts 115 unshift @posts => $new_post; 116 117 # write updated posts to temporary file 118 ### open temporary file for writing 119 open my $temp_filehandle, '>', $POST_DATA_TEMP_FILENAME 120 or die "Cannot open $POST_DATA_TEMP_FILENAME for writing: $!\n"; 121 122 my $posts = $json_posts->pretty->encode( \@posts ); 123 print $temp_filehandle $posts; 124 125 ### close the temporary file 126 close $temp_filehandle or die "Error closing $POST_DATA_TEMP_FILENAME: $!\n"; 127 128 ### delete original post data file 129 unlink $POST_DATA_FILENAME 130 or die "Cannot delete original $POST_DATA_FILENAME: $!\n"; 131 132 ### Rename temporary file to replace original post data file 133 rename $POST_DATA_TEMP_FILENAME, $POST_DATA_FILENAME 134 or die "Cannot rename $POST_DATA_TEMP_FILENAME to $POST_DATA_FILENAME: $!\n"; 135 136 ### recreate new posts.html file by first opening and creating temporary file 137 open $temp_filehandle, '>', $POSTS_TEMP_HTML_FILENAME 138 or die "Cannot open $POSTS_TEMP_HTML_FILENAME for writing: $!\n"; 139 140 ### change default output filehandle 141 select $temp_filehandle; 142 143 print <<"EOF"; 144 <!DOCTYPE html> 145 <html lang="en"> 146 147 <head> 148 <title>$HTML_TITLE</title> 149 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" > 150 <meta name="viewport" content="width=device-width" > 151 <meta name="author" content="$AUTHOR" > 152 <link rel="icon" type="image/png" href="/assets/favicon.png" > 153 <link href="styles/style.css" rel="stylesheet" type="text/css" > 154 </head> 155 156 <body> 157 <h2>$APPLICATION</h2> 158 <a href="$POSTS_REL_PATH_FILENAME"><h1>$HEADING</h1></a> 159 <hr> 160 EOF 161 162 foreach my $post ( @posts ) 163 { 164 print "<p><strong><a href=\"$post->{ $ID }.html\">$post->{ $TITLE }</a></strong>\n" 165 unless ( $post->{ $TITLE } eq '' ); 166 my $html = $markdown_to_html->markdown( $post->{ $STATUS } ); 167 print $html, "\n"; 168 print "<p><a href=\"$post->{ $ID }.html\">$post->{ $TIMESTAMP }</a>\n"; 169 print "<hr>\n"; 170 } 171 172 print "</body>\n"; 173 print "</html>\n"; 174 175 ### change back to default output filehandle 176 select STDOUT; 177 178 ### close the temporary file 179 close $temp_filehandle or die "Error closing $POSTS_TEMP_HTML_FILENAME: $!\n"; 180 181 ### delete original posts.html file 182 unlink $POSTS_HTML_FILENAME 183 or die "Cannot delete original $POSTS_HTML_FILENAME: $!\n"; 184 185 ### rename temporary posts html file to replace original post data file 186 rename $POSTS_TEMP_HTML_FILENAME, $POSTS_HTML_FILENAME 187 or die "Cannot rename $POSTS_TEMP_HTML_FILENAME to $POSTS_HTML_FILENAME: $!\n"; 188 189 ### open filehandle to create HTML file for new post 190 my $new_post_html = catfile( $NEW_POST_HTML_PATH, "$new_post->{ $ID }.html" ); 191 open my $new_post_filehandle, '>', $new_post_html 192 or die "Cannot open $new_post_html for writing: $!\n"; 193 194 ### change default output filehandle 195 select $new_post_filehandle; 196 197 print <<"EOF"; 198 <!DOCTYPE html> 199 <html lang="en"> 200 201 <head> 202 <title>$HTML_TITLE</title> 203 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" > 204 <meta name="viewport" content="width=device-width" > 205 <meta name="author" content="$AUTHOR" > 206 <link rel="icon" type="image/png" href="/assets/favicon.png" > 207 <link href="styles/style.css" rel="stylesheet" type="text/css" > 208 </head> 209 210 <body> 211 <h2>$APPLICATION</h2> 212 <a href="$POSTS_REL_PATH_FILENAME"><h1>$HEADING</h1></a> 213 <hr> 214 EOF 215 216 print "<p><strong><a href=\"$new_post->{ $ID }.html\">$new_post->{ $TITLE }</a></strong>\n" 217 unless ( $new_post->{ $TITLE } eq '' ); 218 my $html = $markdown_to_html->markdown( $new_post->{ $STATUS } ); 219 print $html, "\n"; 220 print "<p><a href=\"$new_post->{ $ID }.html\">$new_post->{ $TIMESTAMP }</a>\n"; 221 print "<hr>\n"; 222 223 print "</body>\n"; 224 print "</html>\n"; 225 226 ### change back to default output filehandle 227 select STDOUT; 228 229 ### close the temporary file 230 close $new_post_filehandle or die "Error closing $new_post_html: $!\n"; 231 232 print $cgi->redirect( $POSTS_REL_PATH_FILENAME ); 233 234 exit 0;