George S. Baugh 5 anni fa
parent
commit
3c9b628698

+ 25 - 1
lib/Trog/Data/DUMMY.pm

@@ -9,6 +9,7 @@ use feature qw{signatures};
 use Carp qw{confess};
 use JSON::MaybeXS;
 use File::Slurper;
+use File::Copy;
 
 =head1 WARNING
 
@@ -92,6 +93,8 @@ sub _write($data) {
 sub get ($self, %request) {
 
     my $example_posts = _read();
+    $request{acls} //= [];
+    $request{tags} //=[];
 
     my @filtered = @$example_posts;
 
@@ -104,7 +107,7 @@ sub get ($self, %request) {
     @filtered = grep { $_->{data} =~ m/\Q$request{like}\E/i } @filtered if $request{like};
 
     # Finally, paginate
-    my $offset = int($request{limit});
+    my $offset = int($request{limit} // 25);
     $offset = @filtered < $offset ? @filtered : $offset;
     my $pages = int(scalar(@filtered) / ($offset || 1) );
 
@@ -168,12 +171,33 @@ sub add ($self, @posts) {
             $post->{version}  = $existing_post->{version};
             $post->{version}++;
         }
+
+        $post = _process($post);
         push @$example_posts, $post;
     }
     _write($example_posts);
     return 0;
 }
 
+# Not actually a subprocess, kek
+sub _process ($post) {
+
+    $post->{href}    = _handle_upload($post->{file}, $post->{id})         if $post->{file};
+    $post->{preview} = _handle_upload($post->{preview_file}, $post->{id}) if $post->{preview_file};
+    delete $post->{app};
+    delete $post->{file};
+    delete $post->{preview_file};
+
+    return $post;
+}
+
+sub _handle_upload ($file, $uuid) {
+    my $f = $file->{tempname};
+    my $newname = "$uuid.$file->{filename}";
+    File::Copy::move($f, "www/assets/$newname");
+    return "/assets/$newname";
+}
+
 sub delete($self, @posts) {
     my $example_posts = _read();
     foreach my $update (@posts) {

+ 37 - 52
lib/Trog/Routes/HTML.pm

@@ -170,14 +170,14 @@ if ($theme_dir) {
     }
 }
 
-sub robots ($query, $input, $render_cb) {
+sub robots ($query, $render_cb) {
     my $processor = Text::Xslate->new(
         path   => $template_dir,
     );
     return [200, ["Content-type:text/plain\n"],[$processor->render('robots.tx', { domain => $query->{domain} })]];
 }
 
-sub index ($query, $input, $render_cb, $content = '', $i_styles = []) {
+sub index ($query,$render_cb, $content = '', $i_styles = []) {
     $query->{theme_dir}  = $theme_dir || '';
 
     my $processor = Text::Xslate->new(
@@ -230,7 +230,7 @@ Implements the 4XX status codes.  Override templates named the same for theming
 
 =cut
 
-sub _generic_route ($rname, $code, $title, $query,$input,$render_cb) {
+sub _generic_route ($rname, $code, $title, $query, $render_cb) {
     $query->{code} = $code;
 
     my $processor = Text::Xslate->new(
@@ -244,7 +244,7 @@ sub _generic_route ($rname, $code, $title, $query,$input,$render_cb) {
         user     => $query->{user},
         styles   => $styles,
     });
-    return Trog::Routes::HTML::index($query, $input, $render_cb, $content, $styles);
+    return Trog::Routes::HTML::index($query, $render_cb, $content, $styles);
 }
 
 sub notfound (@args) {
@@ -271,7 +271,7 @@ One time setup page; should only display to the first user to visit the site whi
 
 =cut
 
-sub setup ($query, $input, $render_cb) {
+sub setup ($query, $render_cb) {
     File::Touch::touch("$ENV{HOME}/.tcms/setup");
     return $render_cb->('notconfigured.tx', {
         title => 'tCMS Requires Setup to Continue...',
@@ -285,33 +285,30 @@ Sets the user cookie if the provided user exists, or sets up the user as an admi
 
 =cut
 
-sub login ($query, $input, $render_cb) {
+sub login ($query, $render_cb) {
 
     # Redirect if we actually have a logged in user.
     # Note to future me -- this user value is overwritten explicitly in server.psgi.
     # If that ever changes, you will die
     $query->{to} //= $query->{route};
     if ($query->{user}) {
-        return $routes{$query->{to}}{callback}->($query,$input,$render_cb);
+        return $routes{$query->{to}}{callback}->($query,$render_cb);
     }
 
-    #Set the cookiez and issue a 302 back to ourselves if we have good creds
-    my $postdata = _input2postdata($input);
-
     #Check and see if we have no users.  If so we will just accept whatever creds are passed.
     my $hasusers = -f "$ENV{HOME}/.tcms/has_users";
     my $btnmsg = $hasusers ? "Log In" : "Register";
 
     my @headers;
-    if ($postdata->{username} && $postdata->{password}) {
+    if ($query->{username} && $query->{password}) {
         if (!$hasusers) {
             # Make the first user
-            Trog::Auth::useradd($postdata->{username}, $postdata->{password}, ['admin'] );
+            Trog::Auth::useradd($query->{username}, $query->{password}, ['admin'] );
             File::Touch::touch("$ENV{HOME}/.tcms/has_users");
         }
 
         $query->{failed} = 1;
-        my $cookie = Trog::Auth::mksession($postdata->{username}, $postdata->{password});
+        my $cookie = Trog::Auth::mksession($query->{username}, $query->{password});
         if ($cookie) {
             # TODO secure / sameSite cookie to kill csrf, maybe do rememberme with Expires=~0
             @headers = (
@@ -338,12 +335,12 @@ Renders the configuration page, or redirects you back to the login page.
 
 =cut
 
-sub config ($query, $input, $render_cb) {
+sub config ($query, $render_cb) {
     if (!$query->{user}) {
-        return login($query,$input,$render_cb);
+        return login($query,$render_cb);
     }
     #NOTE: we are relying on this to skip the ACL check with 'admin', this may not be viable in future?
-    return forbidden($query, $input, $render_cb) unless grep { $_ eq 'admin' } @{$query->{acls}};
+    return forbidden($query, $render_cb) unless grep { $_ eq 'admin' } @{$query->{acls}};
 
     my $css   = _build_themed_styles('config.css');
     my $js    = _build_themed_scripts('post.js');
@@ -386,10 +383,9 @@ Implements /config/save route.  Saves what little configuration we actually use
 
 =cut
 
-sub config_save ($query, $input, $render_cb) {
-    my $postdata = _input2postdata($input);
-    $conf->param( 'general.theme',      $postdata->{theme} )      if defined $postdata->{theme};
-    $conf->param( 'general.data_model', $postdata->{data_model} ) if $postdata->{data_model};
+sub config_save ($query, $render_cb) {
+    $conf->param( 'general.theme',      $query->{theme} )      if defined $query->{theme};
+    $conf->param( 'general.data_model', $query->{data_model} ) if $query->{data_model};
 
     $query->{failure} = 1;
     $query->{message} = "Failed to save configuration!";
@@ -398,12 +394,12 @@ sub config_save ($query, $input, $render_cb) {
         $query->{message} = "Configuration updated succesfully.";
     }
     # TODO we need to soft-restart the server at this point.  Maybe we can just hot-load config on each page when we get to have static renders?  Probably not worth the perf hit for paywall users.
-    return config($query, $input, $render_cb);
+    return config($query, $render_cb);
 }
 
 # TODO actually do stuff
-sub profile ($query, $input, $render_cb) {
-    return config($query, $input, $render_cb);
+sub profile ($query, $render_cb) {
+    return config($query, $render_cb);
 }
 
 =head2 themeclone
@@ -412,9 +408,8 @@ Clone a theme by copying a directory.
 
 =cut
 
-sub themeclone ($query, $input, $render_cb) {
-    my $postdata = _input2postdata($input);
-    my ($theme, $newtheme) = ($postdata->{theme},$postdata->{newtheme});
+sub themeclone ($query, $render_cb) {
+    my ($theme, $newtheme) = ($query->{theme},$query->{newtheme});
 
     my $themedir = 'www/themes';
 
@@ -425,7 +420,7 @@ sub themeclone ($query, $input, $render_cb) {
         $query->{failure} = 0;
         $query->{message} = "Successfully cloned theme '$theme' as '$newtheme'.";
     }
-    return config($query, $input, $render_cb);
+    return config($query, $render_cb);
 }
 
 =head2 post
@@ -434,11 +429,11 @@ Display the route for making new posts.
 
 =cut
 
-sub post ($query, $input, $render_cb) {
+sub post ($query, $render_cb) {
     if (!$query->{user}) {
-        return login($query,$input,$render_cb);
+        return login($query, $render_cb);
     }
-    return forbidden($query, $input, $render_cb) unless grep { $_ eq 'admin' } @{$query->{acls}};
+    return forbidden($query, $render_cb) unless grep { $_ eq 'admin' } @{$query->{acls}};
 
     my $tags  = _coerce_array($query->{tag});
     my ($pages,$posts) = _post_helper($query, $tags, $query->{acls});
@@ -469,20 +464,18 @@ sub post ($query, $input, $render_cb) {
     });
 }
 
-sub post_save ($query, $input, $render_cb) {
+sub post_save ($query, $render_cb) {
     state $data = Trog::Data->new($conf);
-    my $postdata = _input2postdata($input);
-    $data->add($postdata);
+    $data->add($query);
     $query->{failure} = 0;
-    return post($query, $input, $render_cb);
+    return post($query, $render_cb);
 }
 
-sub post_delete ($query, $input, $render_cb) {
+sub post_delete ($query, $render_cb) {
     state $data = Trog::Data->new($conf);
-    my $postdata = _input2postdata($input);
-    $query->{failure} = $data->delete($postdata);
-    $query->{to} = $postdata->{to};
-    return post($query, $input, $render_cb);
+    $query->{failure} = $data->delete($query);
+    $query->{to} = $query->{to};
+    return post($query, $render_cb);
 }
 
 =head2 posts
@@ -491,14 +484,14 @@ Display multi or single posts, supports RSS and pagination.
 
 =cut
 
-sub posts ($query, $input, $render_cb) {
+sub posts ($query, $render_cb) {
     my $tags = _coerce_array($query->{tag});
 
     #TODO If we have a direct ID query, we should show unlisted videos as well as public ones IFF they have a valid campaign ID attached to query
     push(@{$query->{acls}}, 'public');
     my ($pages,$posts) = _post_helper($query, $tags, $query->{acls});
 
-    return notfound($query,$input,$render_cb) unless @$posts;
+    return notfound($query, $render_cb) unless @$posts;
 
     my $fmt = $query->{format} || '';
     return _rss($query,$posts) if $fmt eq 'rss';
@@ -535,7 +528,7 @@ sub posts ($query, $input, $render_cb) {
         about_header => $header,
         about_footer => $footer,
     });
-    return Trog::Routes::HTML::index($query, $input, $render_cb, $content, $styles);
+    return Trog::Routes::HTML::index($query, $render_cb, $content, $styles);
 }
 
 sub _post_helper ($query, $tags, $acls) {
@@ -564,7 +557,7 @@ Passing compressed=1 will gzip the output.
 
 =cut
 
-sub sitemap ($query, $input, $render_cb) {
+sub sitemap ($query, $render_cb) {
 
     my (@to_map, $is_index, $route_type);
     my $warning = '';
@@ -663,7 +656,7 @@ sub sitemap ($query, $input, $render_cb) {
         route      => $query->{route},
     });
 
-    return Trog::Routes::HTML::index($query,$input,$render_cb,$content,$styles);
+    return Trog::Routes::HTML::index($query, $render_cb,$content,$styles);
 }
 
 sub _rss ($query,$posts) {
@@ -705,14 +698,6 @@ sub _rss ($query,$posts) {
     return [200, ["Content-type: application/rss+xml\n"], [$rss->as_string]];
 }
 
-sub _input2postdata ($input) {
-    #Set the cookiez and issue a 302 back to ourselves if we have good creds
-    my ($slurpee,$postdata) = ('',{});
-    while (<$input>) { $slurpee .= $_ }
-    $postdata = URL::Encode::url_params_mixed($slurpee) if $slurpee;
-    return $postdata;
-}
-
 # Deal with Params which may or may not be arrays
 sub _coerce_array ($param) {
     my $p = $param || [];

+ 16 - 1
www/server.psgi

@@ -6,6 +6,7 @@ use feature qw{signatures};
 
 use Date::Format qw{strftime};
 
+use HTTP::Body   ();
 use URL::Encode  ();
 use Text::Xslate ();
 use Plack::MIME  ();
@@ -119,7 +120,21 @@ my $app = sub {
 
     @{$query}{keys(%{$routes{$path}{'data'}})} = values(%{$routes{$path}{'data'}}) if ref $routes{$path}{'data'} eq 'HASH' && %{$routes{$path}{'data'}};
 
-    my $output =  $routes{$path}{callback}->($query,$env->{'psgi.input'}, \&_render);
+    #Actually parse the POSTDATA and dump it into the QUERY object if this is a POST
+    if ($env->{REQUEST_METHOD} eq 'POST') {
+        #TODO don't slurp
+        my $slurpee = '';
+        my $input = $env->{'psgi.input'};
+        while (<$input>) { $slurpee .= $_ }
+
+        my $body = HTTP::Body->new( $env->{CONTENT_TYPE}, $env->{CONTENT_LENGTH} );
+        $body->add($slurpee);
+
+        @$query{keys(%{$body->param})}  = values(%{$body->param});
+        @$query{keys(%{$body->upload})} = values(%{$body->upload});
+    }
+
+    my $output =  $routes{$path}{callback}->($query, \&_render);
     return $output;
 };
 

+ 2 - 2
www/templates/blog.tx

@@ -1,6 +1,6 @@
-<form class="Submissions" action="/post/save" method="POST">
+<form class="Submissions" action="/post/save" method="POST" enctype="multipart/form-data">
     Title *<br /><input required class="cooltext" type="text" name="title" placeholder="Iowa Man Destroys Moon" value="<: $post.title :>" />
-    Preview Image<br /><input type="file" class="cooltext" name="preview" placeholder="PROMO.JPG" value="<: $post.preview :>" />
+    : include "preview.tx";
     : include "acls.tx";
     : include "tags.tx";
     Content<br /><textarea required class="cooltext" name="comment" placeholder="Potzrebie"><: $post.data :></textarea>

+ 6 - 3
www/templates/file.tx

@@ -1,9 +1,12 @@
-<form class="Submissions" action="/post/save" method="POST">
+<form class="Submissions" action="/post/save" method="POST" enctype="multipart/form-data">
     Title *<br /><input required class="cooltext" type="text" name="title" placeholder="Iowa Man Destroys Moon" value="<: $post.title :>" />
-    File *<br /><input required class="cooltext" type="file" name="file" value="<: $post.href :>" />
+    File *<br /><input <: $post.href ? '' : 'required' :> class="cooltext" type="file" name="file" />
+    : if ( $post.href ) {
+    <input type="hidden" name="href" value="<: $post.href :>" />
+    : }
     <!-- Need to validate file or add an upload link that kicks off WS streaming of the file at some point? TAB -->
     <br /> TODO: Add "alternative" links, which scrape the appropriate icon for the alt link from the favicon<br />
-    Preview Image<br /><input type="file" class="cooltext" name="preview" placeholder="PROMO.JPG" value="<: $post.preview :>" />
+    : include "preview.tx";
     : include "acls.tx";
     : include "tags.tx";
     Comments<br /><textarea class="cooltext" name="comment" placeholder="Potzrebie"><: $post.data :></textarea>

+ 2 - 2
www/templates/microblog.tx

@@ -1,7 +1,7 @@
-<form class="Submissions" action="/post/save" method="POST">
+<form class="Submissions" action="/post/save" method="POST" enctype="multipart/form-data">
     Title *<br /><input required class="cooltext" type="text" name="title" placeholder="Iowa Man Destroys Moon" value="<: $post.title :>" />
     URL *<br /><input required class="cooltext" type="href" name="URL" placeholder="https://oneweirdtrick.scam" value="<: $post.href :>" />
-    Image<br /><input class="cooltext" type="url" name="preview" placeholder="https://gifdump.tld/Advice_Dog.jpg" value="<: $post.preview :>" />
+    : include "preview.tx";
     Audio<br /><input class="cooltext" type="url" name="audio_href" placeholder="https://soundclod.com/static.mp3" value="<: $post.audio_href :>" />
     Video<br /><input class="cooltext" type="url" name="video_href" placeholder="https://youvimeo.tv/infomercial.mp4" value="<: $post.video_href :>" />
     : include "acls.tx";

+ 4 - 0
www/templates/preview.tx

@@ -0,0 +1,4 @@
+Preview Image<br /><input type="file" class="cooltext" name="preview_file" placeholder="PROMO.JPG" />
+: if ( $post.preview ) {
+<input type="hidden" name="preview" value="<: $post.preview :>" />
+: }

+ 5 - 2
www/templates/profile.tx

@@ -1,7 +1,10 @@
-<form class="Submissions" action="/profile" method="POST">
+<form class="Submissions" action="/profile" method="POST" enctype="multipart/form-data">
     Username *<br /><input required class="cooltext" type="text" name="title" placeholder="AzureDiamond" value="<: $post.user :>" />
     Password *<br /><input required class="cooltext" type="password" name="password" placeholder="hunter2" />
-    Avatar *<br /><input class="cooltext" type="file" name="file" value="<: $post.preview :>" />
+    Avatar *<br /><input class="cooltext" type="file" name="preview_file" />
+    : if ( $post.preview ) {
+    <input type="hidden" name="preview" value="<: $post.preview :>" />
+    : }
     Wallpaper<br /><input type="file" class="cooltext" name="wallpaper" placeholder="PROMO.JPG" value="<: $post.background_image :>" />
     Title  <br /><input class="cooltext" type="text" name="title" value="<: $post.title :>" />
     : include "acls.tx";

+ 2 - 2
www/templates/series.tx

@@ -1,7 +1,7 @@
-<form class="Submissions" action="/post/save" method="POST">
+<form class="Submissions" action="/post/save" method="POST" enctype="multipart/form-data">
     Title *<br /><input required class="cooltext" type="text" name="title" placeholder="Iowa Man Destroys Moon" value="<: $post.title :>" />
     ACL name *<br /><input required class="cooltext" type="text" name="series" <: $post.aclname :> />
-    Preview Image<br /><input type="file" class="cooltext" name="preview" placeholder="PROMO.JPG" value="<: $post.preview :>" />
+    : include "preview.tx";
     Visibility<br />
     <select class="cooltext" name="visibility">
         : for $post_visibilities -> $visibility {