George S. Baugh 5 rokov pred
rodič
commit
83dc8fa21d
100 zmenil súbory, kde vykonal 813 pridanie a 1448 odobranie
  1. 0 436
      Indexer.pm
  2. 6 2
      Makefile
  3. 90 0
      lib/Trog/Auth.pm
  4. 4 4
      lib/Trog/Config.pm
  5. 2 2
      lib/Trog/Data.pm
  6. 135 1
      lib/Trog/Data/DUMMY.pm
  7. 218 42
      lib/Trog/Routes/HTML.pm
  8. 0 1
      microblog/.gitignore
  9. 15 0
      schema/auth.schema
  10. 0 1
      sys/admin/config/.gitignore
  11. 0 27
      sys/admin/config/main.json.example
  12. 0 14
      sys/admin/config/users.inc
  13. 0 12
      sys/admin/config/users.json.example
  14. 0 83
      sys/admin/index.php
  15. 0 100
      sys/blogroll.inc
  16. 0 1
      sys/fileshare/include/blacklist.json
  17. 0 12
      sys/fileshare/include/forbidden.inc
  18. 0 5
      sys/fileshare/include/notfound
  19. 0 20
      sys/fileshare/sanitize.inc
  20. 0 17
      sys/fileshare/showaudio.inc
  21. 0 36
      sys/fileshare/showcode.inc
  22. 0 15
      sys/fileshare/showdoc.inc
  23. 0 143
      sys/fileshare/showfiles.inc
  24. 0 15
      sys/fileshare/showimg.inc
  25. 0 23
      sys/fileshare/showpost.inc
  26. 0 19
      sys/fileshare/showvideo.inc
  27. 0 161
      sys/microblog.inc
  28. 0 71
      sys/rss/blog.php
  29. 0 78
      sys/rss/microblog.php
  30. BIN
      www/assets/audio/test.mp3
  31. BIN
      www/assets/video/test.ogv
  32. BIN
      www/img/sys/rummy.jpg
  33. 8 0
      www/scripts/post.js
  34. 26 5
      www/server.psgi
  35. 57 0
      www/styles/config.css
  36. 1 12
      www/styles/login.css
  37. 53 0
      www/styles/post.css
  38. 2 2
      www/styles/screen.css
  39. 4 0
      www/styles/structure.css
  40. 6 0
      www/templates/blog.tx
  41. 35 21
      www/templates/config.tx
  42. 9 0
      www/templates/file.tx
  43. 4 4
      www/templates/header.tx
  44. 15 7
      www/templates/index.tx
  45. 50 0
      www/templates/jsalert.tx
  46. 2 35
      www/templates/login.tx
  47. 18 0
      www/templates/mbengine.tx
  48. 10 0
      www/templates/microblog.tx
  49. 1 1
      www/templates/notconfigured.tx
  50. 2 18
      www/templates/post.tx
  51. 21 2
      www/templates/posts.tx
  52. 10 0
      www/templates/profile.tx
  53. 9 0
      www/templates/sysbar.tx
  54. BIN
      www/themes/teodesian.net/img/links/LR.png
  55. BIN
      www/themes/teodesian.net/img/links/alp.png
  56. BIN
      www/themes/teodesian.net/img/links/anarchast.png
  57. BIN
      www/themes/teodesian.net/img/links/antiWarRadio.png
  58. BIN
      www/themes/teodesian.net/img/links/atland.png
  59. BIN
      www/themes/teodesian.net/img/links/avtm.png
  60. BIN
      www/themes/teodesian.net/img/links/awc.png
  61. BIN
      www/themes/teodesian.net/img/links/axiom.png
  62. BIN
      www/themes/teodesian.net/img/links/bash.png
  63. BIN
      www/themes/teodesian.net/img/links/bfp.png
  64. BIN
      www/themes/teodesian.net/img/links/bq.png
  65. BIN
      www/themes/teodesian.net/img/links/charleyjones.png
  66. BIN
      www/themes/teodesian.net/img/links/cheeses.png
  67. BIN
      www/themes/teodesian.net/img/links/civ.png
  68. BIN
      www/themes/teodesian.net/img/links/corbett.png
  69. BIN
      www/themes/teodesian.net/img/links/cryptogon.png
  70. BIN
      www/themes/teodesian.net/img/links/dailybell.png
  71. BIN
      www/themes/teodesian.net/img/links/dr.png
  72. BIN
      www/themes/teodesian.net/img/links/dudeenough.png
  73. BIN
      www/themes/teodesian.net/img/links/e2.png
  74. BIN
      www/themes/teodesian.net/img/links/econned.png
  75. BIN
      www/themes/teodesian.net/img/links/ehancock.png
  76. BIN
      www/themes/teodesian.net/img/links/epj.png
  77. BIN
      www/themes/teodesian.net/img/links/feens.png
  78. BIN
      www/themes/teodesian.net/img/links/ff.png
  79. BIN
      www/themes/teodesian.net/img/links/fourmilab.png
  80. BIN
      www/themes/teodesian.net/img/links/haskell.png
  81. BIN
      www/themes/teodesian.net/img/links/hn.png
  82. BIN
      www/themes/teodesian.net/img/links/infowars.png
  83. BIN
      www/themes/teodesian.net/img/links/insideauto.png
  84. BIN
      www/themes/teodesian.net/img/links/intelnews.png
  85. BIN
      www/themes/teodesian.net/img/links/kittyfeet.png
  86. BIN
      www/themes/teodesian.net/img/links/lrcpod.png
  87. BIN
      www/themes/teodesian.net/img/links/lrn.png
  88. BIN
      www/themes/teodesian.net/img/links/maddox.png
  89. BIN
      www/themes/teodesian.net/img/links/mcninja.png
  90. BIN
      www/themes/teodesian.net/img/links/mises.png
  91. BIN
      www/themes/teodesian.net/img/links/mogambo.png
  92. BIN
      www/themes/teodesian.net/img/links/nuk.png
  93. BIN
      www/themes/teodesian.net/img/links/octave.png
  94. BIN
      www/themes/teodesian.net/img/links/pennyarcade.png
  95. BIN
      www/themes/teodesian.net/img/links/pimp.png
  96. BIN
      www/themes/teodesian.net/img/links/ritholtz.png
  97. BIN
      www/themes/teodesian.net/img/links/robandelliot.png
  98. BIN
      www/themes/teodesian.net/img/links/rumblo.png
  99. BIN
      www/themes/teodesian.net/img/links/scarygoround.png
  100. BIN
      www/themes/teodesian.net/img/links/schiffradio.png

+ 0 - 436
Indexer.pm

@@ -1,436 +0,0 @@
-# ABSTRACT: Define what data is to be uploaded to elasticsearch, and handle it's uploading
-# PODNAME: App::Prove::Elasticsearch::Indexer
-
-package App::Prove::Elasticsearch::Indexer;
-
-use strict;
-use warnings;
-
-use App::Prove::Elasticsearch::Utils();
-
-use Search::Elasticsearch();
-use JSON::MaybeXS();
-use File::Temp();
-use File::Slurper();
-use List::Util 1.33;
-
-=head1 SYNOPSIS
-
-    App::Prove::Elasticsearch::Indexer::check_index({ 'server.host' => 'zippy.test', 'server.port' => 9600 });
-
-=head1 VARIABLES
-
-=head2 index (STRING)
-
-The name of the elasticsearch index used.
-If you are subclassing this, be aware that the Searcher plugin will rely on this.
-
-=cut
-
-our $index = 'testsuite';
-
-sub index {
-    return $index;
-}
-
-=head2 max_query_size
-
-Number of items returned by queries.
-Defaults to 1000.
-
-=cut
-
-our $max_query_size = 1000;
-our $e;
-our $bulk_helper;
-our $idx;
-our %stashed;
-
-=head1 SUBROUTINES
-
-=head2 check_index
-
-Returns 1 if the index needed to be created, 0 if it's already OK.
-Dies if the server cannot be reached, or the index creation fails.
-
-=cut
-
-sub check_index {
-    my $conf = shift;
-
-    my $port = $conf->{'server.port'} ? ':' . $conf->{'server.port'} : '';
-    die "server must be specified" unless $conf->{'server.host'};
-    die("port must be specified")  unless $port;
-    my $serveraddress = "$conf->{'server.host'}$port";
-    $e //= Search::Elasticsearch->new( nodes => $serveraddress, );
-
-    #XXX for debugging
-    #$e->indices->delete( index => $index );
-
-    if ( !$e->indices->exists( index => $index ) ) {
-        $e->indices->create(
-            index => $index,
-            body  => {
-                index => {
-                    similarity => {
-                        default => {
-                            type => "classic"
-                        }
-                    }
-                },
-                analysis => {
-                    analyzer => {
-                        default => {
-                            type      => "custom",
-                            tokenizer => "whitespace",
-                            filter    => [
-                                'lowercase', 'std_english_stop', 'custom_stop'
-                            ]
-                        }
-                    },
-                    filter => {
-                        std_english_stop => {
-                            type      => "stop",
-                            stopwords => "_english_"
-                        },
-                        custom_stop => {
-                            type      => "stop",
-                            stopwords => [ "test", "ok", "not" ]
-                        }
-                    }
-                },
-                mappings => {
-                    testsuite => {
-                        properties => {
-                            id       => { type => "integer" },
-                            elapsed  => { type => "integer" },
-                            occurred => {
-                                type   => "date",
-                                format => "yyyy-MM-dd HH:mm:ss"
-                            },
-                            executor => {
-                                type        => "text",
-                                analyzer    => "default",
-                                fielddata   => "true",
-                                term_vector => "yes",
-                                similarity  => "classic",
-                                fields      => {
-                                    keyword => { type => "keyword" }
-                                }
-                            },
-                            status => {
-                                type        => "text",
-                                analyzer    => "default",
-                                fielddata   => "true",
-                                term_vector => "yes",
-                                similarity  => "classic",
-                                fields      => {
-                                    keyword => { type => "keyword" }
-                                }
-                            },
-                            version => {
-                                type        => "text",
-                                analyzer    => "default",
-                                fielddata   => "true",
-                                term_vector => "yes",
-                                similarity  => "classic",
-                                fields      => {
-                                    keyword => { type => "keyword" }
-                                }
-                            },
-                            test_version => {
-                                type        => "text",
-                                analyzer    => "default",
-                                fielddata   => "true",
-                                term_vector => "yes",
-                                similarity  => "classic",
-                                fields      => {
-                                    keyword => { type => "keyword" }
-                                }
-                            },
-                            platform => {
-                                type        => "text",
-                                analyzer    => "default",
-                                fielddata   => "true",
-                                term_vector => "yes",
-                                similarity  => "classic",
-                                fields      => {
-                                    keyword => { type => "keyword" }
-                                }
-                            },
-                            path => {
-                                type        => "text",
-                                analyzer    => "default",
-                                fielddata   => "true",
-                                term_vector => "yes",
-                                similarity  => "classic",
-                                fields      => {
-                                    keyword => { type => "keyword" }
-                                }
-                            },
-                            defect => {
-                                type        => "text",
-                                analyzer    => "default",
-                                fielddata   => "true",
-                                term_vector => "yes",
-                                similarity  => "classic",
-                                fields      => {
-                                    keyword => { type => "keyword" }
-                                }
-                            },
-                            steps_planned => { type => "integer" },
-                            body          => {
-                                type        => "text",
-                                analyzer    => "default",
-                                fielddata   => "true",
-                                term_vector => "yes",
-                                similarity  => "classic",
-                            },
-                            name => {
-                                type        => "text",
-                                analyzer    => "default",
-                                fielddata   => "true",
-                                term_vector => "yes",
-                                similarity  => "classic",
-                                fields      => {
-                                    keyword => { type => "keyword" }
-                                }
-                            },
-                            steps => {
-                                properties => {
-                                    number  => { type => "integer" },
-                                    text    => { type => "text" },
-                                    status  => { type => "text" },
-                                    elapsed => { type => "integer" },
-                                }
-                            },
-                        }
-                    }
-                }
-            }
-        );
-        return 1;
-    }
-    return 0;
-}
-
-=head2 index_results
-
-Index a test result (see L<App::Prove::Elasticsearch::Parser> for the input).
-
-=cut
-
-sub index_results {
-    my ($result) = @_;
-
-    die("check_index must be run first") unless $e;
-
-    $idx //= App::Prove::Elasticsearch::Utils::get_last_index( $e, $index );
-    $idx++;
-
-    eval {
-        $e->index(
-            index => $index,
-            id    => $idx,
-            type  => $index,
-            body  => $result,
-        );
-    };
-    if ($@) {
-        if ( ( ref $@ ) eq "Search::Elasticsearch::Error::NoNodes" ) {
-            print "Failed to index due to no nodes online - continuing: $@\n";
-            _stash_result_for_retry($result);
-            return;
-        }
-    }
-
-    my $doc_exists =
-      $e->exists( index => $index, type => 'testsuite', id => $idx );
-    if ( !defined($doc_exists) || !int($doc_exists) ) {
-        die
-"Failed to Index $result->{'name'}, could find no record with ID $idx\n";
-    }
-    print "Successfully Indexed test: $result->{'name'} with result ID $idx\n";
-
-    #Now that we've proven the server is up, let's re-upload if we need to.
-    _resubmit_stashed_documents();
-}
-
-sub _stash_result_for_retry {
-    my ($result) = @_;
-    my $encoder  = JSON::MaybeXS->new( utf8 => 1 );
-    my $dump     = $encoder->encode_json($result);
-    my ( $fh, $filename ) = File::Temp::tempfile();
-    print $fh $dump;
-
-#Keep a hold of the File::Temp object so that when our program goes out of scope everything is cleaned up.
-    $stashed{$filename} = $fh;
-}
-
-# Let's give it the "college try TM" with a bulk dump, and give up if that second try fails
-sub _resubmit_stashed_documents {
-    my @files = keys(%stashed);
-    return unless @files;
-
-    my $encoder   = JSON::MaybeXS->new( utf8 => 1 );
-    my @bulk_load = map {
-        my $out = File::Slurper::read_text($_);
-        $encoder->decode_json($out);
-        close $stashed{$_};
-        delete $stashed{$_};
-    } @files;
-    return bulk_index_results(@bulk_load);
-}
-
-=head2 bulk_index_results(@results)
-
-Helper method for migration scripts.
-Uploads an array of results in bulk such as would be fed to index_results.
-
-It is up to the caller to chunk inputs as is appropriate for your installation.
-
-=cut
-
-sub bulk_index_results {
-    my @results = @_;
-    $bulk_helper //= $e->bulk_helper(
-        index => $index,
-        type  => $index,
-    );
-
-    $idx //= App::Prove::Elasticsearch::Utils::get_last_index( $e, $index );
-
-    $bulk_helper->index( map { $idx++; { id => $idx, source => $_ } }
-          @results );
-    $bulk_helper->flush();
-}
-
-=head2 associate_case_with_result(%config)
-
-Associate an indexed result with a tracked defect.
-
-Requires configuration to be inside of ENV vars already.
-
-Arguments Hash:
-
-=over 4
-
-=item B<case STRING>     - case to associate defect to
-
-=item B<defects ARRAY>   - defects to associate with case
-
-=item B<platforms ARRAY> - filter out any results not having these platforms
-
-=item B<versions ARRAY>  - filter out any results not having these versions
-
-=back
-
-=cut
-
-sub associate_case_with_result {
-    my %opts = @_;
-
-    die("check_index must be run first") unless $e;
-
-    my %q = (
-        index => $index,
-        body  => {
-            query => {
-                bool => {
-                    must => [
-                        {
-                            match => {
-                                name => $opts{case},
-                            }
-                        },
-                    ],
-                },
-            },
-        },
-    );
-
-    #It's normal to have multiple platforms in a document.
-    foreach my $plat ( @{ $opts{platforms} } ) {
-        push(
-            @{ $q{body}{query}{bool}{must} },
-            { match => { platform => $plat } }
-        );
-    }
-
-    #It's NOT normal to have multiple versions in a document.
-    foreach my $version ( @{ $opts{versions} } ) {
-        push(
-            @{ $q{body}{query}{bool}{should} },
-            { match => { version => $version } }
-        );
-    }
-
-    #Paginate the query, TODO short-circuit when we stop getting results?
-    my $hits = App::Prove::Elasticsearch::Utils::do_paginated_query( $e,
-        $max_query_size, %q );
-    return 0 unless scalar(@$hits);
-
-    #Now, update w/ the defect.
-    my $failures = 0;
-    my $attempts = 0;
-    foreach my $hit (@$hits) {
-        $hit->{_source}->{platform} = [ $hit->{_source}->{platform} ]
-          if ref( $hit->{_source}->{platform} ) ne 'ARRAY';
-        next
-          if ( scalar( @{ $opts{versions} } ) && !$hit->{_source}->{version} );
-        next
-          unless List::Util::any { $hit->{_source}->{version} eq $_ }
-        @{ $opts{versions} };
-        next
-          if ( scalar( @{ $opts{platforms} } )
-            && !$hit->{_source}->{platform} );
-        next unless List::Util::all {
-            my $p = $_;
-            grep { $_ eq $p } @{ $hit->{_source}->{platform} }
-        }
-        @{ $opts{platforms} };
-        next unless $hit->{_source}->{name} eq $opts{case};
-
-        $attempts++;
-
-        #Merge the existing defects with the ones we are adding in
-        $hit->{defect} //= [];
-        my @df_merged =
-          List::Util::uniq( ( @{ $hit->{defect} }, @{ $opts{defects} } ) );
-
-        my %update = (
-            index => $index,
-            id    => $hit->{_id},
-            type  => 'result',
-            body  => {
-                doc => {
-                    defect => \@df_merged,
-                },
-            }
-        );
-        $update{body}{doc}{status} = $opts{status} if $opts{status};
-
-        my $res = $e->update(%update);
-
-        print "Associated cases to document $hit->{_id}\n"
-          if $res->{result} eq 'updated';
-        if ( !grep { $res->{result} eq $_ } qw{updated noop} ) {
-            print
-"Something went wrong associating cases to document $hit->{_id}!\n$res->{result}\n";
-            $failures++;
-        }
-    }
-
-    print "No cases matching your query could be found.  No action was taken.\n"
-      unless $attempts;
-
-    return $failures;
-}
-
-1;
-
-__END__
-
-=head1 SPECIAL THANKS
-
-Thanks to cPanel Inc, for graciously funding the creation of this module.

+ 6 - 2
Makefile

@@ -1,8 +1,12 @@
+.PHONY: install
+install:
+	mkdir $(HOME)/.tcms; /bin/true
+
 .PHONY: test
 test:
 	prove t/*.t
 
 .PHONY: depend
 depend:
-	sudo apt install cpanminus starman  libcal-dav-perl libtext-xslate-perl libserver-starter-perl liburl-encode-perl libplack-perl libcal-dav-perl libconfig-tiny-perl libdatetime-format-http-perl libjson-maybexs-perl libuuid-tiny-perl libcapture-tiny-perl libconfig-simple-perl
-	sudo cpanm Mojo::File Date::Format
+	sudo apt install -y cpanminus starman  libcal-dav-perl libtext-xslate-perl libserver-starter-perl liburl-encode-perl libplack-perl libcal-dav-perl libconfig-tiny-perl libdatetime-format-http-perl libjson-maybexs-perl libuuid-tiny-perl libcapture-tiny-perl libconfig-simple-perl libdbi-perl libfile-slurper-perl libfile-touch-perl libfile-copy-recursive-perl
+	sudo cpanm Mojo::File Date::Format DBD::SQLite

+ 90 - 0
lib/Trog/Auth.pm

@@ -0,0 +1,90 @@
+package Trog::Auth;
+
+use strict;
+use warnings;
+
+no warnings 'experimental';
+use feature qw{signatures};
+
+use DBI;
+use DBD::SQLite;
+use File::Slurper qw{read_text};
+use UUID::Tiny ':std';
+use Digest::SHA 'sha256';
+
+=head1 Trog::Auth
+
+An SQLite3 authdb.
+
+=head1 Termination Conditions
+
+Throws exceptions in the event the session database cannot be accessed.
+
+=head1 FUNCTIONS
+
+=head2 session2user(sessid) = STRING
+
+Translate a session UUID into a username.
+
+Returns empty string on no active session.
+
+=cut
+
+sub session2user ($sessid) {
+    my $dbh = _dbh();
+    my $rows = $dbh->selectall_arrayref("SELECT name FROM sess_user WHERE session=?",{ Slice => {} }, $sessid);
+    return '' unless ref $rows eq 'ARRAY' && @$rows;
+    return $rows->[0]->{name};
+}
+
+=head2 mksession(user, pass) = STRING
+
+Create a session for the user and waste all other sessions.
+
+Returns a session ID, or blank string in the event the user does not exist or incorrect auth was passed.
+
+=cut
+
+sub mksession ($user,$pass) {
+    my $dbh = _dbh();
+    my $records = $dbh->selectall_arrayref("SELECT salt FROM user WHERE name = ?", { Slice => {} }, $user);
+    return '' unless ref $records eq 'ARRAY' && @$records;
+    my $salt = $records->[0]->{salt};
+    my $hash = sha256($pass.$salt);
+    my $worked = $dbh->selectall_arrayref("SELECT id FROM user WHERE hash=? AND name = ?", { Slice => {} }, $hash, $user);
+    return '' unless ref $worked eq 'ARRAY' && @$worked;
+    my $uid = $worked->[0]->{id};
+    my $uuid = create_uuid_as_string(UUID_V1, UUID_NS_DNS);
+    $dbh->do("INSERT OR REPLACE INTO session (id,user_id) VALUES (?,?)", undef, $uuid, $uid) or return '';
+    return $uuid;
+}
+
+=head2 useradd(user, pass) = BOOL
+
+Adds a user identified by the provided password into the auth DB.
+
+Returns True or False (likely false when user already exists).
+
+=cut
+
+sub useradd ($user, $pass) {
+    my $dbh = _dbh();
+    my $salt = create_uuid();
+    my $hash = sha256($pass.$salt);
+    return $dbh->do("INSERT INTO user (name,salt,hash) VALUES (?,?,?)", undef, $user, $salt, $hash);
+}
+
+my $dbh;
+# Ensure the db schema is OK, and give us a handle
+sub _dbh {
+    return $dbh if $dbh;
+    my $qq = read_text('schema/auth.schema');
+    my $dbname = "$ENV{HOME}/.tcms/auth.db";
+    $dbh = DBI->connect("dbi:SQLite:dbname=$dbname","","");
+    $dbh->{sqlite_allow_multiple_statements} = 1;
+    $dbh->do($qq) or die "Could not ensure auth database consistency";
+    $dbh->{sqlite_allow_multiple_statements} = 0;
+    return $dbh;
+}
+
+1;

+ 4 - 4
lib/Trog/Config.pm

@@ -6,11 +6,11 @@ use warnings;
 use Config::Simple;
 
 sub get {
-    my $cf = {};
+    my $cf;
     my $home_cfg = "$ENV{HOME}/.tcms/main.cfg"; #XXX probably should pass this in and sanitize ENV
-    Config::Simple->import_from($home_cfg, $cf) if -f $home_cfg;
-    return $cf if %$cf;
-    Config::Simple->import_from('config/default.cfg', $cf);
+    $cf = Config::Simple->new($home_cfg) if -f $home_cfg;
+    return $cf if $cf;
+    $cf = Config::Simple->new('config/default.cfg');
     return $cf;
 }
 

+ 2 - 2
lib/Trog/Data.pm

@@ -8,8 +8,8 @@ use feature qw{signatures};
 
 #It's just a factory
 
-sub new($class,$config) {
-    my $module = "Trog::Data::$config->{'general.data_model'}";
+sub new( $class, $config ) {
+    my $module = "Trog::Data::".$config->param('general.data_model');
     my $req = $module;
     $req =~ s/::/\//g;
     require "$req.pm";

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

@@ -6,13 +6,147 @@ use warnings;
 no warnings 'experimental';
 use feature qw{signatures};
 
+=head1 QUERY FORMAT
+
+The $query_language and $query_help variables are presented to the user as to how to use the search box in the tCMS header.
+
+=cut
+
+our $query_language = 'Perl Regex in Quotemeta';
+our $query_help     = 'https://perldoc.perl.org/functions/quotemeta.html';
+
+=head1 POST STRUCTURE
+
+Posts generally need to have the following:
+
+    data: Brief description of content, or the content itself.
+    content_type: What this content actually is.  Used to filter into the appropriate pages.
+    href: Primary link.  This is the subject of a news post, or a link to the item itself.  Can be local or remote.
+    local_href: Backup link.  Automatically created link to a static cache of the content.
+    title: Title of the content.  Used as link name for the 'href' attribute.
+    user: User was banned for this post
+    id: Internal identifier in datastore for the post.
+    tags: array ref of appropriate tags.
+    created: timestamp of creation of this version of the post
+    version: revision # of this post.
+
+=cut
+
+my $example_posts = [
+    {
+        content_type => "text/html",
+        data         => "Here, caveman",
+        href         => '/',
+        local_href   => "/assets/today.html",
+        title        => 'Example Post',
+        user         => 'Nobody',
+        id           => 665,
+        tags         => ['news'],
+        created      => time(),
+        version      => 0,
+    },
+    {
+        content_type => "text/html",
+        data         => "Is amazing",
+        href         => '/',
+        local_href   => "/assets/blog/Muh Blog.html",
+        title        => 'Muh Blog',
+        user         => 'Nobody',
+        id           => 666,
+        tags         => ['blog'],
+        created      => time(),
+        version      => 0,
+    },
+    {
+        content_type => "text/html",
+        data         => "Vote for Nobody, nobody really cares!",
+        href         => '/',
+        local_href   => "/assets/about/Nobody.html",
+        title        => 'Nobody',
+        user         => 'Nobody',
+        id           => 669,
+        tags         => ['about', 'profile'],
+        created      => time(),
+        version      => 0,
+    },
+    { 
+        content_type => "image/gif",
+        data         => "Default avatar for new users",
+        href         => "/img/avatar/humm.gif",
+        local_href   => "/img/avatar/humm.gif",
+        title        => "humm.gif",
+        user         => 'Nobody',
+        id           => 420,
+        tags         => ['image', 'files', 'profile-image'],
+        created      => time(),
+        version      => 0,
+    },
+    {
+        content_type => "audio/mpeg",
+        data         => "Test recording for tCMS",
+        href         => "/assets/audio/test.mp3",
+        local_href   => "/assets/audio/test.mp3",
+        title        => "test.mp3",
+        user         => "Nobody",
+        id           => 111,
+        tags         => ["audio", "files"],
+        created      => time(),
+        version      => 0,
+    },
+    {
+        content_type => "video/ogg",
+        data         => "Test video for tCMS",
+        href         => "/assets/video/test.ogv",
+        local_href   => "/assets/video/test.ogv",
+        title        => "test.ogv",
+        user         => "Nobody",
+        id           => "222",
+        tags         => ["video", "files"],
+        created      => time(),
+        version      => 0,
+    },
+];
+
+=head1 CONSTRUCTOR
+
+=head2 new(Config::Simple $config)
+
+Try not to do expensive things here.  
+
+=cut
+
 sub new ($class, $config) {
+    $config = $config->vars();
+    $config->{lang} = $query_language;
+    $config->{help} = $query_help;
     return bless($config,__PACKAGE__);
 }
 
+=head1 METHODS
+
+=cut
+
 # These have to be sorted as requested by the client
 sub get ($self, %request) {
-    return [{ data => "<hr><h3 class='blogtitles'><a href='/'>Example Post</a></h3>Here, caveman", id => 666 }]
+    my @filtered = @$example_posts;
+    @filtered = grep { my $tags = $_->{tags}; grep { my $t = $_; grep {$t eq $_ } @{$request{tags}} } @$tags } @filtered if @{$request{tags}};
+    @filtered = grep { $_->{data} =~ m/\Q$request{like}\E/i } @filtered if $request{like};
+
+    # Next, go ahead and build the "post type"
+    @filtered = _add_post_type(@filtered);
+
+    return \@filtered;
+}
+
+sub _add_post_type (@posts) {
+    return map {
+        my $post = $_;
+        my $type = 'file';
+        $type = 'blog' if grep { $_ eq 'blog' } @{$post->{tags}};
+        $type = 'microblog' if grep { $_ eq 'news' } @{$post->{tags}};
+        $post->{type} = $type;
+        $post
+    } @posts;
 }
 
 sub add ($self, @posts) {

+ 218 - 42
lib/Trog/Routes/HTML.pm

@@ -6,11 +6,15 @@ use warnings;
 no warnings 'experimental';
 use feature qw{signatures};
 
+use File::Touch();
+
 use Trog::Config;
+use Trog::Data;
+
 my $conf = Trog::Config::get();
 my $template_dir = 'www/templates';
 my $theme_dir;
-$theme_dir = "themes/$conf->{'general.theme'}" if $conf->{'general.theme'} && -d "www/themes/$conf->{'general.theme'}";
+$theme_dir = "themes/".$conf->param('general.theme') if $conf->param('general.theme') && -d "www/themes/".$conf->param('general.theme');
 
 use lib 'www';
 
@@ -23,20 +27,25 @@ our $leftbar      = 'leftbar.tx';
 our $footbar      = 'footbar.tx';
 
 our %routes = (
+    default => {
+        callback => \&Trog::Routes::HTML::setup,
+    },
     '/' => {
         method   => 'GET',
         callback => \&Trog::Routes::HTML::index,
     },
-    '/setup' => {
-        method   => 'GET',
-        callback => \&Trog::Routes::HTML::setup,
-    },
+# This should only be enabled to debug
+#    '/setup' => {
+#        method   => 'GET',
+#        callback => \&Trog::Routes::HTML::setup,
+#    },
     '/login' => {
         method   => 'GET',
         callback => \&Trog::Routes::HTML::login,
     },
     '/auth' => {
         method   => 'POST',
+        nostatic => 1,
         callback => \&Trog::Routes::HTML::login,
     },
     '/config' => {
@@ -47,7 +56,7 @@ our %routes = (
     '/config/save' => {
         method   => 'POST',
         auth     => 1,
-        callback => \&Trog::Routes::HTML::config,
+        callback => \&Trog::Routes::HTML::config_save,
     },
     '/post' => {
         method   => 'GET',
@@ -63,27 +72,37 @@ our %routes = (
         method   => 'GET',
         callback => \&Trog::Routes::HTML::posts,
     },
-    '/files' => {
-        method   => 'GET',
-        callback => \&Trog::Routes::HTML::files
+    '/profile' => {
+        method   => 'POST',
+        auth     => 1,
+        callback => \&Trog::Routes::HTML::profile,
+    },
+    '/themeclone' => {
+        method   => 'POST',
+        auth     => 1,
+        callback => \&Trog::Routes::HTML::themeclone,
     },
 );
 
 # Build aliases for /post with extra data
-my @post_aliases = qw{news blog wiki video audio about};
+my @post_aliases = qw{news blog image video audio about files};
 @routes{map { "/$_" } @post_aliases} = map { my %copy = %{$routes{'/posts'}}; $copy{data} = { tag => [$_] }; \%copy } @post_aliases;
 
 # Grab theme routes
+my $themed = 0;
 if ($theme_dir) {
     my $theme_mod = "$theme_dir/routes.pm";
-    if (-f $theme_mod ) {
+    if (-f "www/$theme_mod" ) {
         require $theme_mod;
         @routes{keys(%Theme::routes)} = values(%Theme::routes);
+        $themed = 1;
     }
 }
 
+# TODO build a sitemap.xml based on the above routing table, and robots.txt
+
 sub index ($query, $input, $render_cb, $content = '', $i_styles = []) {
-    $input->{theme_dir}  = $theme_dir || '';
+    $query->{theme_dir}  = $theme_dir || '';
 
     my $processor = Text::Xslate->new(
         path   => $template_dir,
@@ -94,7 +113,7 @@ sub index ($query, $input, $render_cb, $content = '', $i_styles = []) {
         path =>  "www/$theme_dir/templates",
     ) if $theme_dir;
 
-    $content ||= _pick_processor($rightbar,$processor,$t_processor)->render($landing_page,$input);
+    $content ||= _pick_processor($rightbar,$processor,$t_processor)->render($landing_page,$query);
 
     my @styles = ('/styles/avatars.css'); #TODO generate file for users
     if ($theme_dir) {
@@ -105,21 +124,27 @@ sub index ($query, $input, $render_cb, $content = '', $i_styles = []) {
 
     #TODO allow theming of print css
 
+    my $search_info = Trog::Data->new($conf);
+
     return $render_cb->('index.tx',{
         user        => $query->{user},
+        search_lang => $search_info->{lang},
+        search_help => $search_info->{help},
+        route       => $query->{route},
         theme_dir   => $theme_dir,
         content     => $content,
-        title       => $conf->{'general.title'},
-        htmltitle   => _pick_processor("templates/$htmltitle" ,$processor,$t_processor)->render($htmltitle,$input),
-        midtitle    => _pick_processor("templates/$midtitle" ,$processor,$t_processor)->render($midtitle,$input),
-        rightbar    => _pick_processor("templates/$rightbar" ,$processor,$t_processor)->render($rightbar,$input),
-        leftbar     => _pick_processor("templates/$leftbar"  ,$processor,$t_processor)->render($leftbar,$input),
-        footbar     => _pick_processor("templates/$footbar"  ,$processor,$t_processor)->render($footbar,$input),
+        title       => $conf->param('general.title'), #TODO control in theme instead
+        htmltitle   => _pick_processor("templates/$htmltitle" ,$processor,$t_processor)->render($htmltitle,$query),
+        midtitle    => _pick_processor("templates/$midtitle" ,$processor,$t_processor)->render($midtitle,$query),
+        rightbar    => _pick_processor("templates/$rightbar" ,$processor,$t_processor)->render($rightbar,$query),
+        leftbar     => _pick_processor("templates/$leftbar"  ,$processor,$t_processor)->render($leftbar,$query),
+        footbar     => _pick_processor("templates/$footbar"  ,$processor,$t_processor)->render($footbar,$query),
         stylesheets => \@styles,
     });
 }
 
 sub setup ($query, $input, $render_cb) {
+    File::Touch::touch("$ENV{HOME}/.tcms/setup");
     return $render_cb->('notconfigured.tx', {
         title => 'tCMS Requires Setup to Continue...',
         stylesheets => _build_themed_styles('notconfigured.css'),
@@ -127,33 +152,159 @@ sub setup ($query, $input, $render_cb) {
 }
 
 sub login ($query, $input, $render_cb) {
-    # TODO actually do login processing
+
+    # 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} //= '/config';
+    if ($query->{user}) {
+        return $routes{$query->{to}}{callback}->($query,$input,$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 (!$hasusers) {
+            # Make the first user
+            Trog::Auth::useradd($postdata->{username}, $postdata->{password});
+            File::Touch::touch("$ENV{HOME}/.tcms/has_users");
+        }
+
+        $query->{failed} = 1;
+        my $cookie = Trog::Auth::mksession($postdata->{username}, $postdata->{password});
+        if ($cookie) {
+            # TODO secure / sameSite cookie to kill csrf, maybe do rememberme with Expires=~0
+            @headers = (
+                "Set-Cookie: tcmslogin=$cookie; HttpOnly",
+            );
+            $query->{failed} = 0;
+        }
+    }
 
     $query->{failed} //= -1;
     return $render_cb->('login.tx', {
         title         => 'tCMS 2 ~ Login',
-        to            => $query->{to} || '/config',
-        login_failure => int( $query->{failed} ),
-        login_message => int( $query->{failed} ) < 1 ? "Login Successful, Redirecting..." : "Login Failed.",
+        to            => $query->{to},
+        failure => int( $query->{failed} ),
+        message => int( $query->{failed} ) < 1 ? "Login Successful, Redirecting..." : "Login Failed.",
+        btnmsg        => $btnmsg,
         stylesheets   => _build_themed_styles('login.css'),
-    });
+    }, @headers);
 }
 
 sub config ($query, $input, $render_cb) {
+    if (!$query->{user}) {
+        $query->{to} = '/config';
+        return login($query,$input,$render_cb);
+    }
+    my $tags = ['profile'];
+    my $posts = _post_helper($query, $tags);
+    my $css   = _build_themed_styles('config.css');
+    my $js    = _build_themed_scripts('post.js');
+    push(@$css, '/styles/avatars.css');
+
+    $query->{failure} //= -1;
+
     return $render_cb->('config.tx', {
-        title => 'Configure tCMS',
-        stylesheets => _build_themed_styles('config.css'),
+        title         => 'Configure tCMS',
+        stylesheets   => $css,
+        scripts       => $js,
+        themes        => _get_themes(),
+        data_models   => _get_data_models(),
+        current_theme => $conf->param('general.theme'),
+        current_data_model => $conf->param('general.data_model'),
+        route       => '/about',
+        category    => '/about',
+        types       => ['profile'],
+        can_edit    => 1,
+        posts       => $posts,
+        edittype    => 'profile',
+        message     => $query->{message},
+        failure     => $query->{failure},
+        to          => '/config',
     });
 }
 
+sub _get_themes {
+    my $dir = 'www/themes';
+    opendir(my $dh, $dir) || die "Can't opendir $dir: $!";
+    my @tdirs = grep { !/^\./ && -d "$dir/$_" } readdir($dh);
+    closedir $dh;
+    return \@tdirs;
+}
+
+sub _get_data_models {
+    my $dir = 'lib/Trog/Data';
+    opendir(my $dh, $dir) || die "Can't opendir $dir: $!";
+    my @dmods = map { s/\.pm$//g; $_ } grep { /\.pm$/ && -f "$dir/$_" } readdir($dh);
+    closedir $dh;
+    return \@dmods
+}
+
 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};
+
+    $query->{failure} = 1;
+    $query->{message} = "Failed to save configuration!";
+    if ($conf->save()) {
+        $query->{failure} = 0;
+        $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);
+}
+
+# TODO actually do stuff
+sub profile ($query, $input, $render_cb) {
+    return config($query, $input, $render_cb);
+}
+
+sub themeclone ($query, $input, $render_cb) {
+    my $postdata = _input2postdata($input);
+    my ($theme, $newtheme) = ($postdata->{theme},$postdata->{newtheme});
+
+    my $themedir = 'www/themes';
+
+    $query->{failure} = 1;
+    $query->{message} = "Failed to clone theme '$theme' as '$newtheme'!";
+    require File::Copy::Recursive;
+    if ($theme && $newtheme && File::Copy::Recursive::dircopy( "$themedir/$theme", "$themedir/$newtheme" )) {
+        $query->{failure} = 0;
+        $query->{message} = "Successfully cloned theme '$theme' as '$newtheme'.";
+    }
     return config($query, $input, $render_cb);
 }
 
 sub post ($query, $input, $render_cb) {
+    if (!$query->{user}) {
+        $query->{to} = '/config';
+        return login($query,$input,$render_cb);
+    }
+
+    my $tags  = _coerce_array($query->{tag});
+    my $posts = _post_helper($query, $tags);
+    my $css   = _build_themed_styles('post.css');
+    my $js    = _build_themed_scripts('post.js');
+    push(@$css, '/styles/avatars.css');
+
     return $render_cb->('post.tx', {
-        title => 'New Post',
-        stylesheets => _build_themed_styles('post.css'),
+        title       => 'New Post',
+        stylesheets => $css,
+        scripts     => $js,
+        posts       => $posts,
+        can_edit    => 1,
+        types       => [qw{microblog blog file}],
+        route       => '/posts',
+        category    => '/posts',
+        edittype    => $query->{type} || 'microblog',
     });
 }
 
@@ -163,9 +314,10 @@ sub post_save ($query, $input, $render_cb) {
 
 sub posts ($query, $input, $render_cb) {
     my $tags = _coerce_array($query->{tag});
+    my $posts = _post_helper($query, $tags);
 
-    require Trog::Data;
-    my $data = Trog::Data->new($conf);
+    my $fmt = $query->{format} || '';
+    return _rss($posts) if $fmt eq 'rss';
 
     my $processor ||= Text::Xslate->new(
         path   => _dir_for_resource('posts.tx'),
@@ -174,21 +326,33 @@ sub posts ($query, $input, $render_cb) {
     my $styles = _build_themed_styles('posts.css');
 
     my $content = $processor->render('posts.tx', {
-        title => "Posts tagged @$tags",
-        date  => 'TODO',
-        posts => $data->get(
-            tags  => $tags,
-            like => $query->{like},
-        ),
+        title    => "Posts tagged @$tags",
+        date     => 'TODO',
+        posts    => $posts,
+        route    => $query->{route},
+        category => $themed ? Theme::path_to_tile($query->{route}) : $query->{route},
     });
     return Trog::Routes::HTML::index($query, $input, $render_cb, $content, $styles);
 }
 
-sub files ($query, $input, $render_cb) {
-    return $render_cb->('fileman.tx', {
-        title => 'tCMS File Browser',
-        stylesheets => _build_themed_styles('fileman.css'),
-    });
+sub _post_helper ($query, $tags) {
+    my $data = Trog::Data->new($conf);
+    return $data->get(
+        tags => $tags,
+        like => $query->{like},
+    );
+}
+
+sub _rss ($posts) {
+    return [200, ["Content-type: text/plain\n"], ["TODO"]];
+}
+
+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
@@ -201,10 +365,17 @@ sub _coerce_array ($param) {
 sub _build_themed_styles ($style) {
     my @styles = ("/styles/$style");
     my $ts = _themed_style($style);
-    push(@styles, $ts) if $theme_dir && -f $ts;
+    push(@styles, $ts) if $theme_dir && -f "www/$ts";
     return \@styles;
 }
 
+sub _build_themed_scripts ($script) {
+    my @scripts = ("/scripts/$script");
+    my $ts = _themed_style($script);
+    push(@scripts, $ts) if $theme_dir && -f "www/$ts";
+    return \@scripts;
+}
+
 sub _pick_processor($file, $normal, $themed) {
     return _dir_for_resource($file) eq $template_dir ? $normal : $themed;
 }
@@ -213,8 +384,13 @@ sub _pick_processor($file, $normal, $themed) {
 sub _dir_for_resource ($resource) {
     return $theme_dir && -f "www/$theme_dir/$resource" ? $theme_dir : $template_dir;
 }
+
 sub _themed_style ($resource) {
     return _dir_for_resource("styles/$resource")."/styles/$resource";
 }
 
+sub _themed_script ($resource) {
+    return _dir_for_resource("scripts/$resource")."/scripts/$resource";
+}
+
 1;

+ 0 - 1
microblog/.gitignore

@@ -1 +0,0 @@
-[^.]*

+ 15 - 0
schema/auth.schema

@@ -0,0 +1,15 @@
+CREATE TABLE IF NOT EXISTS user (
+    id   INTEGER PRIMARY KEY AUTOINCREMENT,
+    name TEXT NOT NULL UNIQUE,
+    salt TEXT NOT NULL,
+    hash TEXT NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS session (
+    id TEXT PRIMARY KEY UNIQUE,
+    user_id INTEGER NOT NULL UNIQUE REFERENCES user(id) ON DELETE CASCADE
+);
+
+CREATE INDEX IF NOT EXISTS username_idx ON user(name);
+
+CREATE VIEW IF NOT EXISTS sess_user AS SELECT user.name AS name, session.id AS session FROM user JOIN session ON session.user_id=user.id;

+ 0 - 1
sys/admin/config/.gitignore

@@ -1 +0,0 @@
-*.json

+ 0 - 27
sys/admin/config/main.json.example

@@ -1,27 +0,0 @@
-{
-    "toptitle" : "templates/default/title.inc",
-    "leftbar" : "templates/default/leftbar.inc",
-    "rightbar" : "templates/default/rightbar.inc",
-    "footbar" : "templates/default/footbar.inc",
-    "about" : "templates/default/about.inc",
-    "home" : "sys/blogroll.inc",
-    "fileshare" : "sys/fileshare/showfiles.inc",
-    "microblog" : "sys/microblog.inc",
-    "blog" : "sys/blogroll.inc",
-    "postloader" : "sys/fileshare/showpost.inc",
-    "codeloader" : "sys/fileshare/showcode.inc",
-    "audioloader" : "sys/fileshare/showaudio.inc",
-    "videoloader" : "sys/fileshare/showvideo.inc",
-    "imgloader" : "sys/fileshare/showimg.inc",
-    "docloader" : "sys/fileshare/showdoc.inc",
-    "blogdir" : "blog/",
-    "microblogdir" : "microblog/",
-    "filesharedir" : "fileshare",
-    "rssdir" : "sys/rss/",
-    "icondir" : "img/mime/",
-    "basedir" : "",
-    "htmltitle" : "Unconfigured tCMS Website",
-    "blogtitle" : "Blog",
-    "microblogtitle" : "Linklog",
-    "timezone" : "America/Chicago"
-}

+ 0 - 14
sys/admin/config/users.inc

@@ -1,14 +0,0 @@
-<?php
-$tcmsUsers = json_decode(file_get_contents('config/users.json'),true);
-if (!empty($_SERVER['REMOTE_USER']) && !empty($tcmsUsers)) {
-    $poster = "admin";
-    foreach (array_keys($tcmsUsers) as $user) {
-        if( $tcmsUsers[$user]['remoteUser'] == $_SERVER['REMOTE_USER']) {
-            $poster = $user;
-            break;
-        }   
-    }   
-} else {
-    $poster = "Nobody";
-}
-?>

+ 0 - 12
sys/admin/config/users.json.example

@@ -1,12 +0,0 @@
-{
-  "bambam" : {
-    "fullName" : "The Administrator",
-    "email" : "admin@example.com",
-    "remoteUser: "barney"
-  },
-  "wilma" : {
-    "fullName" : "Mr. Magoo",
-    "email" : "dev@null.io",
-    "remoteUser: "fred"
-  }
-}

+ 0 - 83
sys/admin/index.php

@@ -1,83 +0,0 @@
-<?php
-
-    // Setup includes to work right. Much of this is duped in Config.inc, but gotta get this info to include it, so..
-    $user_info = posix_getpwuid(posix_geteuid());
-    $dir = ( $user_info['dir'] ? $user_info['dir'] : '/var/www/' );
-    $basedir = ( file_exists( $dir . "/.tCMS_basedir") ? file_get_contents("$dir/.tCMS_basedir") : "$dir/.tCMS" );
-    set_include_path(get_include_path() . PATH_SEPARATOR . "$basedir/lib");
-    require_once "tCMS/Config.inc";
-
-    // Get the config, set the theme (also set the basedir so we don't have to fetch it again).
-    $conf_obj = new Config;
-    $conf_obj->set_base_dir($basedir);
-    $config = $conf_obj->get();
-    $theme = ( !array_key_exists( 'theme', $config ) || empty($config['theme']) ? 'default' : $config['theme'] );
-    $themedir = "$basedir/templates/$theme";
-
-    // Begin dispatch
-    $args = ( $_SERVER['REQUEST_METHOD'] == 'POST' ? $_POST : $_GET );
-    if( !empty($args['app']) && $args['app'] == 'login' ) {
-        include "$themedir/admin/login.inc";
-        die();
-    } elseif( !empty($args['app']) && $args['app'] == 'logout' ) {
-        include "$themedir/admin/logout.inc";
-        die();
-    } else {
-        require_once "tCMS/Auth.inc";
-        $auth = new Auth;
-        $auth->ensure_auth();
-    }
-    if( empty($args['app']) || $args['app'] == 'config' ) {
-        $kontent = "$themedir/admin/settings.inc";
-    } elseif ($args['app'] == 'blog') {
-        if(!empty($args['get_fragment'])) {
-            # Need to sanitize
-            $path = realpath("$basedir/blog/".$args['get_fragment']);
-            if(strpos($path, "$basedir/blog") !== 0 ) die("Forbidden: Tried to load $path, but $basedir/blog is not the start of the real path.");
-            die(file_get_contents("$basedir/blog/".$args['get_fragment']));
-        }
-        $kontent = "$themedir/admin/bengine.inc";
-    } elseif ($args['app'] == 'microblog') {
-        $kontent = "$themedir/admin/mbengine.inc";
-    } elseif ($args['app'] == 'users' ) {
-        $kontent = "$themedir/admin/users.inc";
-    } else {
-        $kontent = "$themedir/admin/settings.inc";
-    }
-?>
-<!doctype html>
-<html dir="ltr" lang="en-US">
- <head>
-  <meta charset="utf-8" />
-  <meta name="description" content="tCMS Control Panel"/>
-  <meta name="viewport" content="width=device-width">
-  <?php
-    $links  = '<link rel="stylesheet" type="text/css" href="../../themed/' . $theme . '/css/structure.css" />';
-    $links .= '<link rel="stylesheet" type="text/css" href="../../themed/' . $theme . '/css/screen.css" media="screen" />';
-    $links .= '<link rel="stylesheet" type="text/css" href="../../themed/' . $theme . '/css/print.css" media="print" />';
-    $links .= '<link rel="icon" type="image/vnd.microsoft.icon" href="../../themed/' . $theme . '/img/icon/favicon.ico" />';
-    echo $links;
-
-    // TODO inject avatars these via style tags based on config
-  ?>
-  <title>tCMS Admin</title>
-  <?php
-  ?>
- </head>
- <body>
-  <div id="topkek" style="text-align: center; vertical-align: middle;">
-   <button title="Menu" id="clickme">&#9776;</button>
-   <span id="configbar">
-    <a class="topbar" title="Edit Various Settings" href="index.php?app=config">Settings</a>
-    <a class="topbar" title="Blog Writer" href="index.php?app=blog">Blog Writer</a>
-    <a class="topbar" title="Pop off about Stuff" href="index.php?app=microblog">MicroBlogger</a>
-    <a class="topbar" title="Logout" href="index.php?app=logout">Logout</a>
-   </span>
-  </div>
-  <div id="kontent" style="display: block;">
-   <?php
-        include $kontent;
-   ?>
-  </div>
- </body>
-</html>

+ 0 - 100
sys/blogroll.inc

@@ -1,100 +0,0 @@
-<p class="title">
- <a title="RSS" href="sys/rss/blog.php" class="rss"></a>
- <?php
-  echo $config['blogtitle'];
- ?>
- <hr />
-</p>
-<?php
-
-if (!empty($_GET['post'])) {
-    $post=urldecode($_GET['post']);
-    $statz = stat($post);
-    $uid = $statz['uid'];
-    $udata = posix_getpwuid($uid);
-    $user = $udata['name'];
-
-    $date =  date("F d Y H:i:s", filemtime($post));
-
-    if (stristr($post,'.') != ".post") {
-        $title = basename($post);
-    } else {
-        $title = substr(strstr(basename($post),'-'),1,-5);
-    }
-
-    echo "<h3 class=\"blogtitles\"><a title=permalink href=\"index.php?nav=3&post=".urlencode($post)."\">$title</a></h3>\n";
-    echo "<em class=\"blogdetail\">Last modified on $date UTC by $user</em><hr />\n\n";
-    include "$post";
-    echo "\n<hr /><a style=\"textalign: center;\" href=\"".$_SERVER["PHP_SELF"]."?nav=3\">Back to Blog</a>";
-} else {
-
-$offset=0;
-if (!empty($_GET['index']) && !is_int($_GET['index'])) {
-	$offset = 10*$_GET['index'];
-}
-
-
-	//slurp up the files
-	$files = glob($basedir . "/blog/*.post");
-	$guid = count($files);
-
-	//sort by filename
-	
-	//initialize an array to house sort results
-	$files2 = array();
-	$files2 = array_pad($files2,$guid,0);
-
-	for ($i=0; $i<$guid; $i++) {
-		$j = explode('-',basename($files[$i]));
-		$j = $j[0];
-		$j = (int)$j;
-		$j--;
-		$files2[$j] = $files[$i];
-	}
-
-	$slen = count($files2)-1;
-	$ctr=0;
-	$older=0;
-
-	for ($i=$slen-$offset; $i>-1; $i--) {
-		$shitpost=$files2[$i];
-
-		//using a counter here to know when to stop, since I don't know how many posts there will be
-		if ($ctr > 9) {
-			$older=1;
-			break;
-		}
-		$ctr++;
-
-		$statz = stat($shitpost);
-		$uid = $statz['uid'];
-		$udata = posix_getpwuid($uid);
-		$user = $udata['name'];
-
-		$date =  date("F d Y H:i:s", filemtime($shitpost));
-
-		$title = substr(strstr(basename($shitpost),'-'),1,-5);
-
-		echo "<h3 class=\"blogtitles\"><a title=permalink href=\"".$_SERVER["PHP_SELF"]."?nav=3&post=".urlencode($shitpost)."\">$title</a></h3>\n";
-		echo "<em class=\"blogdetail\">Last modified on $date UTC by $user</em><br />\n";
-		include $shitpost;
-		echo "<hr />";
-	};
-
-echo "<table width=\"100%\"><tr><td>";
-if ($older) {
-	$offset=$_GET['index']+1;
-	echo "<a href=\"".$_SERVER["PHP_SELF"]."?nav=3&index=$offset\">Older Posts</a>";
-}
-echo "</td><td style=\"text-align: right;\">";
-if (!empty($_GET['index'])) {
-	$offset=$_GET['index']-1;
-	if ($offset == 0) {
-		echo "<a href=\"".$_SERVER["PHP_SELF"]."?nav=3\">Newer Posts</a>";
-	} else {
-		echo "<a href=\"".$_SERVER["PHP_SELF"]."?nav=3&index=$offset\">Newer Posts</a>";
-	}
-}
-echo "</td></tr></table>\n";
-}
-?>

+ 0 - 1
sys/fileshare/include/blacklist.json

@@ -1 +0,0 @@
-[ "sys", "microblog", "themed" ]

+ 0 - 12
sys/fileshare/include/forbidden.inc

@@ -1,12 +0,0 @@
-<center>
-<?php
-
-$pwd = $_GET['dir'];
-
-echo '<p style="vertical-align: middle;">';
-echo '<img src="img/mime/denied.gif" alt="deeenied" />';
-echo 'Access to '.$pwd.' Denied';
-echo '<img src="img/mime/denied.gif" alt="deeniedagain" />';
-echo '</p>'
-
-?>

+ 0 - 5
sys/fileshare/include/notfound

@@ -1,5 +0,0 @@
-<p style="text-align: center; vertical-align: center;">
- <img src="img/mime/missing.gif" alt="burritos" />
-  File not found
- <img src="img/mime/missing.gif" alt="getdownagain" />
-</p>

+ 0 - 20
sys/fileshare/sanitize.inc

@@ -1,20 +0,0 @@
-<?php 
-if( !empty($pwd) ) {
-    //Forbid anything starting with / and anything with .. in it; also protocol links (://)
-    $forbidden = preg_match( "/|..|://", $pwd );
-    if ( $forbidden ) {
-        include 'sys/fileshare/include/forbidden.inc';
-        die();	
-    }
-
-    //Check the list of other forbidden directories
-    $blacklist = json_decode( file_get_contents("sys/fileshare/include/blacklist.json"), true );
-    if( !empty( $blacklist ) ) {
-    foreach ( $blacklist as $blacklisted ) {
-        if ( preg_match('^' . $blacklisted, $pwd) ) {
-            include 'sys/fileshare/include/forbidden.inc';
-            die();
-        }
-    }
-}
-?>

+ 0 - 17
sys/fileshare/showaudio.inc

@@ -1,17 +0,0 @@
-<?php
-//Listen to audio -- ONLY MP3s IN THIS HOUSE
-//$title = basename($post);
- $tag = id3_get_tag($post);
- $date =  date("F d Y H:i:s", filemtime($post));
- $parent = dirname($post);
- echo "<h3><a class=nudes title=permalink href=\"$post\">".$tag["title"]."</a></h3>";
- echo "By: ".$tag["artist"]."<br />";
- echo "$date UTC<br/ >";
- echo "<p id=\"audioplayer_1\">Alternative content</p>";
- echo "<script type=\"text/javascript\">\n";
- echo "AudioPlayer.embed(\"audioplayer_1\", {soundFile: \"".$protocol."://teodesian.net/".$post."\"});\n";
- echo "</script><br />\n";
- echo "Description:<br />";
- echo $tag["comment"];
- echo "<a title=back href=\"index.php?nav=1&dir=$parent\"><img alt=back src=".$icondir."tsfolder-up.gif />$parent</a><hr />";
-?>

+ 0 - 36
sys/fileshare/showcode.inc

@@ -1,36 +0,0 @@
-<?php
-//Browse text files (like code)
-$statz = stat($post);
-$uid = $statz['uid'];
-$udata = posix_getpwuid($uid);
-$user = $udata['name'];
-$date =  date("F d Y H:i:s", filemtime($post));
-$title = basename($post);
-echo "<h3 class=\"blogtitles\"><a title=permalink href=\"$post\">$title</a></h3>\n";
-echo "Last modified on $date UTC by $user<br /><br />\n";
-$text = file_get_contents($post);
-
-// Convert UTF-8 string to HTML entities
-$text = mb_convert_encoding($text, 'HTML-ENTITIES',"UTF-8");
-// Convert HTML entities into ISO-8859-1
-$text = html_entity_decode($text,ENT_NOQUOTES, "ISO-8859-1");
-// Convert characters > 127 into their hexidecimal equivalents
-for($i = 0; $i < strlen($text); $i++) {
- $letter = $text[$i];
- $num = ord($letter);
- if($num>127) {
-  $out .= "&#$num;";
- } elseif ($letter == "\n") {
-  $out .= "<br />";
- } elseif ($letter == "\t") {
-  $out .= "&#8194;&#8194;&#8194;&#8194;";
- } elseif ($letter == " ") {
-  $out .= "&#8194;";
- } else {
-  $out .=  $letter;
- }
-}
-echo $out;
-$parent = dirname($post);
-echo "<hr /><a title=back href=\"index.php?nav=1&dir=$parent\"><img alt=back src=img/mime/tsfolder-up.gif />$parent</a>";
-?>

+ 0 - 15
sys/fileshare/showdoc.inc

@@ -1,15 +0,0 @@
-<?php
-// Using googgle docs until a decent PDF viewer actually exists
-// PDFObject would be nice if it didn't require a browser plugin
-$statz = stat($post);
-$uid = $statz['uid'];
-$udata = posix_getpwuid($uid);
-$user = $udata['name'];
-$date =  date("F d Y H:i:s", filemtime($post));
-$title = basename($post);
-$parent = dirname($post);
-echo "<a title=back href=\"index.php?nav=1&dir=$parent\"><img alt=back src=img/mime/tsfolder-up.gif />$parent</a><hr />";
-echo "<h3 class=blogtitles><a title=permalink href=\"$post\">$title</a></h3>\n";
-echo "<em class=blogdetail>Last modified on $date UTC by $user</em><br /><br />\n";
-echo "<iframe src=\"http://docs.google.com/gview?url=http://teodesian.net/$post&embedded=true\" style=\"width:100%; min-height:500px;\" frameborder=\"0\"></iframe>";
-?>

+ 0 - 143
sys/fileshare/showfiles.inc

@@ -1,143 +0,0 @@
-<?php
- $pwd = $_GET['dir'];
-
- //These variables are to check whether the directory we will link to exists, and to know what directory we are in 
- $check = @scandir($pwd.'/../', 1);
- $splode = preg_split('[/]', $pwd, -1);
- $predir = count($splode)-1;
- $test = array_slice($splode, -1, 1);
-
- #Establish MIME Type data
- #Link is used to specify special handler URLS for particular files
- #File specifies the link icon
- $arch_types = array(".tar",".gz",".z7",".bz",".bz2",".zip",".rar",".lsz",
-		    "link" => "",
-		    "file" => "tsarchive.gif");
- //Only MP3 supported for now
- $audio_types = array(".mp3",
-		     "link" => "index.php?nav=7&post=",
-		     "file" => "tsaudio.gif");
- $video_types = array(".ogv",
-		     "link" => "index.php?nav=8&post=",
-		     "file" => "tsmovie.gif");
- $image_types = array(".bmp",".gif",".jpg",".jpeg",".png",".ico",".svg",".xpm",
-		     "link" => "index.php?nav=9&post=",
-		     "file" => "tsimage.gif");
- $schematic_types = array(".dwg",".dxf",".cad",".gcode",".mcode",
-			 "link" => "",
-			 "file" => "tsschematic.gif");
- $model_types = array(".stl",".blend",".3ds",
-		     "link" => "",
-		     "file" => "tsmodel.gif");
- $binary_types = array(".exe",".o",".dll",".so",".jnilib",".a",".bin",".hex",
-		      "link" => "",
-		      "file" => "tssoftware.gif");
- $code_types = array(".py",".c",".h",".js",".php",".tcl",".m",".txt",
-		    "link" => "index.php?nav=6&post=",
-		    "file" => "tscode.gif");
- $doc_types = array(".htm",".html",".post",
-		   "link" => "index.php?nav=5&post=",
-                   "file" => "tsdoc.png");
- $legacy_types = array(".pdf",".doc",".docx",".xls",".xlsx",".ppt",".pptx",".pages",".ai",".psd",".tiff",".tif",".eps",".ps",".xps",
-		   "link" => "index.php?nav=10&post=",
-		   "file" => "tsprop.png");
- $mime_types = array($arch_types,$audio_types,$video_types,$image_types,$schematic_types,$model_types,$binary_types,$code_types,$doc_types,$legacy_types);
-
- //Find the directory contents
- $ls = @scandir($pwd, 1);
-
-// $dlist = array_filter($ls,function ($a) {return is_dir($a);});
- $dban1 = '.';
- $dban2 = '..';
- $dkey = array_search($dban1,$ls);
- if ($dkey!==false){unset($ls[$dkey]);};
- $dkey = array_search($dban2,$ls);
- if ($dkey!==false){unset($ls[$dkey]);};
-
- //See if this directory is even there
- if (@in_array($test[0], $check)) {
-	echo 'Directory listing for '.$pwd;
-	echo '<hr />';
-	$cnt = count($ls);
-    $dlist = array();
-    $flist = array();
-
-    //Trying to sort here
-    for ($n = 0; $n < $cnt; $n++) {
-        $filechk = @scandir($pwd.'/'.$ls[$n]);
-        if (! $filechk) {
-            foreach($mime_types as $mimes) {
-                foreach($mimes as $type) {
-                    $sstrn = @stristr($ls[$n],$type);
-                    if($sstrn !== FALSE && strlen($sstrn) == strlen($type)) {
-                        array_push($flist, $ls[$n]);
-                    }
-                }
-            }
-        }
-        else {
-            array_push($dlist, $ls[$n]);
-        }
-    }
-    sort($dlist);
-    sort($flist);
-    $ls = array_merge($dlist,$flist);
-	
-    //Yeah, I know this looks familiar. There's prolly some way to combine this by setting the $ikon and $link vars as an array.
-    for ($n = 0; $n < $cnt; $n++) {
-
-		//create links based on whether we are a file, and if otherwise we are a directory
-		$filechk = @scandir($pwd.'/'.$ls[$n]);
-		if (! $filechk) {
-			//default values for uncaught filetypes
-			$ikon = "tsfile.gif";
-			$link = "";
-
-			foreach ($mime_types as $mimes) {
-				foreach ($mimes as $type) {
-					//$link = $mimes["link"];
-                                        $sstrn = @stristr($ls[$n],$type);
-					if ($sstrn !== FALSE && strlen($sstrn) == strlen($type)) {
-						$ikon = $mimes["file"];
-						$link = $mimes["link"];
-						break 2;
-					};
-				};
-			};
-			echo '<img class="icon" src="/themed/'.$theme.'/img/mime/'.$ikon.'" />';
-			echo '<a href="'.$link.$pwd.'/'.$ls[$n].'">'.$ls[$n].'</a><br />';
-		}
-		else {
-			echo '<img src="/themed/'.$theme.'/img/mime/tsfolder.gif" />';
-			echo '<a href="index.php?nav=1&dir='.$pwd.'/'.$ls[$n].'">'.$ls[$n].'</a><br />'."\n";
-		}
-	}
-
-	//Figure out what the previous directory is
-	$prevdirname = '';
-
-	for ($i =0; $i < $predir; $i++) {
-
-		//We want to catch the first case, and not put a / before it
-		if ($i == 0) {
-			$prevdirname = $prevdirname.$splode[$i];
-		}
-		else {
-			$prevdirname = $prevdirname.'/'.$splode[$i];
-		}
-	}
-
-	//If we are not in the TLD, make a link to the previous dir
-	if ($prevdirname <> '') {
-
-		echo '<img src="'.$config['icondir'].'tsfolder-up.gif" />';
-		echo '<a href="index.php?nav=1&dir='.$prevdirname.'">Up one level</a>'."\n";
-
-	}
- }
-
- //Catch bogus directories
- else {
- 	include 'sys/fileshare/include/notfound';
- }
-?> 

+ 0 - 15
sys/fileshare/showimg.inc

@@ -1,15 +0,0 @@
-<?php
-$info =  getimagesize($post);
-$ratio = round($info[0]/$info[1],2);
-$parent = dirname($post);
-if ($ratio == 1.00) {
- echo "<img alt=\"$post\" src=\"$post\" height=100% width=100% />";
-} elseif ($ratio > 1.00) {
- $ht = (1.20-($ratio-1))*100;
- echo "<img alt=\"$post\" src=\"$post\" height=$ht% width=100% />";
-} else {
- $wd = 100-(($ratio-1)*100);
- echo "<img alt=\"$post\" src=\"$post\" style=\"padding-left: auto; padding-right: auto;\" />";
-}
-echo "<a title=back href=\"index.php?nav=1&dir=$parent\"><img alt=back src=".$icondir."tsfolder-up.gif /></a>".basename($post)."&nbsp".$info[0]."x".$info[1]."<hr />";
-?>

+ 0 - 23
sys/fileshare/showpost.inc

@@ -1,23 +0,0 @@
-<?php
-//Generic .post file loader
-if ($nav == 5 && file_exists($post)) {
- $statz = stat($post);
- $uid = $statz['uid'];
- $udata = posix_getpwuid($uid);
- $user = $udata['name'];
- $date =  date("F d Y H:i:s", filemtime($post));
- if (stristr($post,'.') != ".post") {
-  $title = basename($post);
- } else {
-  $title = strstr(basename($post),'.', true);
- }
- echo "<h3 class=\"blogtitles\"><a title=permalink href=\"index.php?nav=5&post=$post\">$title</a></h3>\n";
- echo "<em class=\"blogdetail\">Last modified on $date UTC by $user</em><br /><hr />\n";
- include "$post";
- $parent = dirname($post);
- echo "<hr /><a style=\"padding-left: auto; padding-right: auto;\" title=back href=\"index.php?nav=1&dir=$parent\"><img alt=back src=img/mime/tsfolder-up.gif /></a>$parent";
-}
-
-//404 Loader for files specified in GET param that don't actually exist
-elseif ($nav == 5 && !file_exists($post)) {echo "<h1 style=\"padding-left: auto; padding-right: auto;\">404 - Not Found</h1>";}
-?>

+ 0 - 19
sys/fileshare/showvideo.inc

@@ -1,19 +0,0 @@
-<?php
-//watch movies -- Only OGV is supported
-$parent = dirname($post);
-echo "<a title=back href=\"index.php?nav=1&dir=$parent\"><img alt=back src=".$icondir."tsfolder-up.gif /></a>$parent<hr />";
-$title = basename($post);
-echo "<h3 class=blogtitles><a title=permalink href=\"$post\">".$title."</a></h3>";
-echo "<video src=\"".$post."\" type=\"video/ogg\" codecs=\"theora, vorbis\" controls=\"controls\" width=\"100%\" height=\"80%\" poster=\"img/sys/testpattern.jpg\">";
-echo "<applet code=\"com.fluendo.player.Cortado.class\" archive=\"sys/fileshare/video/cortado.jar\" width=\"100%\" height=\"80%\">";
-echo "<param name=\"url\" value=\"http://teodesian.net/".$post."\"/>\n";
-echo "<param name='bufferSize' value='4096'>\n";
-echo "<param name='bufferHigh' value='25'>\n";
-echo "<param name='bufferLow' value='5'>\n";
-echo "<param name='autoPlay' value='false'>\n";
-echo "<param name='statusHeight' value='20'>\n";
-echo "Install the Java Plugin, or Enable Scripts to see video here.<br />";
-echo "Or, <a href=\"".$post."\">download the video.\n";
-echo "</applet>";
-echo "</video>";
-?>

+ 0 - 161
sys/microblog.inc

@@ -1,161 +0,0 @@
-<?php
-  if ( !empty( $editable ) ) { //Insert the Only JS the project should have, all it does is toggle a div
-    echo "
-      <script type=\"text/javascript\">
-        function switchMenu(obj) {
-          var el = document.getElementById(obj);
-          if ( el.style.display != 'none' ) {
-            el.style.display = 'none';
-          }
-          else {
-            el.style.display = '';
-	  }
-        }
-      </script>\n";
-  }
-  $title = !empty($config['microblogtitle']) ? $config['microblogtitle'] : "Microblog";
-  echo "<p class=\"title\"><a title=\"RSS\" class=\"rss\" href=\"sys/rss/microblog.php\"></a> $title";
-  //Set important times - $tdtime is today's date, $oldtime is the oldest known date a tCMS install had nuze for - defaults to today then searches microblog dir for entries to set date
-  $tdtime = new DateTime();
-  $oldtime = clone $tdtime;
-  //limit results of directory read to first entry -- much faster than doing it with PHP once you get a large filelist. 
-  exec("ls -tr1 $basedir/microblog |head -1", $cmd_out);
-  if(!empty($cmd_out[0])) {
-    $oldtime = $oldtime = DateTime::createFromFormat('m.d.y', $cmd_out[0]);
-  }
-  $oldtime->sub(new DateInterval('P1D'));
-  /*$today and $tmrw refer to times relative to what is passed by GET params -
-  $today is the date requested by GET, $tmrw is bool designating whether $today is something other than $tdtime 
-  error indicates whether you supplied a bogus GET param for date.*/
-  $tmrw = 0;
-  $error = 0;
-  $today = clone $tdtime;
-  if(!empty($_GET["date"])) {
-    $today = DateTime::createFromFormat('m.d.y', $_GET["date"]);
-    //Catch bogus input, set $tmwr to TRUE if $today was set to something other than today's date
-    if (!filter_var($_GET["date"],FILTER_VALIDATE_REGEXP,array('options' => array('regexp' => "/^(0[1-9]|1[012])[.](0[1-9]|[12][0-9]|3[01])[.]\d\d/")))) {
-      echo "</p>That's a funny looking date you provided there, mister.\n";
-      $error=1;
-    }
-    else if ($today > $tdtime) { //catch if day supplied by GET is IN THE FUTURE
-      echo "</p>Welcome to the future<br /><img style=\"max-width:100%; padding-left: auto; padding-right: auto;\" src=\"http://gunshowcomic.com/comics/20090930.png\" />\n";
-      $error=1;
-    }   
-    else if ($today < $tdtime) {
-      $tmrw = 1;
-    }
-  }
-  /*Catch if day in question has no news -
-  If not, display day before (or before that if still no news.
-  $oldtime used here to tell when to stop looping back)*/
-  if (!$error) {
-    while (empty($todaysnews)) {
-      $todaysnews = ""; //Set it to be something empty to prevent logspam
-      $yesterday = clone $today;//This may look strange, but it'll make sense later
-      $tomorrow = clone $today;
-      $tomorrow->add(new DateInterval('P1D'));
-      $yesterday->sub(new DateInterval('P1D'));
-      //Detect if We're at the end of postings
-      if ($yesterday < $oldtime) {
-        echo " (".$today->format('m.d.y')."):</p><hr />";
-        echo "For me, it was a beginning, but for you it is the end of the road.<br /><hr />\n";
-        if ($oldtime != $tdtime) {
-          $tomorrow = clone $oldtime;
-          $tomorrow->add(new DateInterval('P1D'));
-	  $tomorrow = $tomorrow->format('m.d.y');
-        }
-        $todaysnews = "end";
-      }
-      if ($todaysnews != "end") {
-        //Get news from directory if any exists for that day, glob will return empty if nothing is in dir
-        $todaysnews = glob("$basedir/microblog/".$today->format('m.d.y')."/*");
-        //Set display date for today's news, set $today to be yesterday in order to get while loop to recurse correctly
-        $realtime = $today->format('m.d.y');
-        if(!empty($_GET['fwd']) && $_GET['fwd']) {//Check whether we are traversing forward or backward in time
-          $today = clone $tomorrow;
-        } else { //Default to going back
-          $today = clone $yesterday;
-        }
-        //Finish by setting times for Yesterday and Tomorrow so that they can be used in links below
-        $tomorrow = clone $yesterday;
-        $tomorrow->add(new DateInterval('P2D'));
-        $tomorrow = $tomorrow->format('m.d.y');
-        $yesterday = $yesterday->format('m.d.y');
-      }
-    }
-    if ($todaysnews != "end") {
-      echo " (".$realtime."):</p><hr />\n";
-      foreach ($todaysnews as $i) {
-        $fh = fopen($i,'r');
-        $fc = fread($fh,10000); //If a microblog item is more than 1kb, you are doing something wrong.
-        fclose($fh);
-        $json = json_decode($fc);
-        if(is_null($json)) {
-          echo $fc;
-        } elseif (!empty($json->title) && !empty($json->url) && !empty($json->poster)) {
-          $out = '<h3 class="blogtitles">
-                    <a href="'.$json->url.'">'.$json->title.'</a>
-                    <a class="usericon '.$json->poster.'" title="Posted by '.$json->poster.'"></a>
-                  </h3>';
-          if(!empty($json->image)) {
-            $out .= '<img class="mblogimg" src="'.$json->image.'" />';
-          }
-          if(!empty($json->audio)) {
-            $out .= '<audio src="'.$json->audio.'" controls>
-                       Download Audio 
-                       <a href="'.$json->audio.'">Here</a><br />
-                     </audio>';
-          }
-          if(!empty($json->video)) {
-            $out .= '<video src="'.$json->video.'" controls>
-                       Download Video
-                       <a href="'.$json->video.'">Here</a><br />
-                     </video>';
-          }
-          if(!empty($json->comment)) {
-            $out .= $json->comment;
-          }
-          $out .= '<hr />';
-          echo $out;
-        } #Note that if nothing works out here, I'm just opting not to show anything.
-        if ( !empty( $editable ) ) {
-          $id=basename($i);
-		  $editblock = "
-            <a style=\"display: inline-block;\" onclick=\"switchMenu('$id');\">[Edit]</a>
-            <div style=\"display: none;\" id=\"$id\">
-             <form style=\"display: inline\" method=\"POST\">
-              <input type=\"hidden\" name=\"id\" value=\"$i\" />
-              <input type='hidden' name='action' value='Edit' />";
-          if(is_null($json)) {
-            $editblock .= "<textarea class=\"mbedit_text\" name=\"content\">$fc</textarea>";
-          } else {
-          $editblock .= '<input type="hidden" name="type" value="JSON" />
-            Title: <input class="cooltext" type="text" name="title"
-			value="' . str_replace( '"' ,'&quot;' , $json->title ) .'" /><br />
-            URL: <input class="cooltext" type="text" name="URL" value="'.$json->url.'" /><br />
-            Image: <input class="cooltext" type="text" name="IMG" value="'.$json->image.'" /><br />
-            Audio: <input class="cooltext" type="text" name="AUD" value="'.$json->audio.'" /><br />
-            Video: <input class="cooltext" type="text" name="VID" value="'.$json->video.'" /><br />
-            Comments: <textarea class="cooltext" name="comment">'.$json->comment.'</textarea>';
-          }
-          $editblock .= "<input type=\"hidden\" name=\"app\" value=\"microblog\" />
-             <input class=\"coolbutton mbedit_button\" type=\"submit\" Value=\"Edit\" />
-             </form>
-             <form style=\"display: inline\" method=\"POST\">
-              <input type=\"hidden\" name=\"app\" value=\"microblog\" />
-              <input type=\"hidden\" name=\"id\" value=\"$i\" />
-              <input type='hidden' name='action' value='Delete' />
-              <input class=\"coolbutton mbedit_button\" type=\"submit\" value=\"Delete\" />
-             </form>
-            </div>
-            <hr class=\"clear\" />";
-          echo $editblock;
-        }
-      }
-      echo "<a style=\"float: left;\" title=\"skips empty days\" href=\"?nav=2&date=".$yesterday."&fwd=0\">Older Entries</a>\n";
-    }
-    if ($tmrw) {
-      echo "<a style=\"float: right;\" href=\"?nav=2&date=".$tomorrow."&fwd=1\">Newer Entries</a>\n";
-    }
-  }
-?>

+ 0 - 71
sys/rss/blog.php

@@ -1,71 +0,0 @@
-<?php
-  header('Content-Type: application/rss+xml'); 
-  if (!empty($_SERVER["HTTPS"])) {
-    $protocol = "http";
-  } else {
-    $protocol = "https";
-  }
-	extract(json_decode(file_get_contents('../admin/config/main.json'),true));
-    echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
-	echo "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\">\n";
-	echo "<channel>\n";
-    $atomlink = "$protocol://".$_SERVER["SERVER_NAME"]."/".$basedir.$rssdir."blog.php";
-	echo "<atom:link href=\"".$atomlink."\" rel=\"self\" type=\"application/rss+xml\" />";
-	echo "\t<title>".$htmltitle."</title>\n";
-	echo "\t<description>".$blogtitle."</description>\n";
-	echo "\t<link>http://".$_SERVER["SERVER_NAME"]."/".$basedir."</link>\n";
-
-	$tiem = date(DATE_RFC2822, time());
-
-	echo "\t<lastBuildDate>$tiem</lastBuildDate>\n";
-	echo "\t<pubDate>$tiem</pubDate>\n";
-
-	$files = glob($_SERVER["DOCUMENT_ROOT"]."/".$basedir.$blogdir."*.post");
-	$guid = count($files);
-
-	//sort by filename
-	
-	//initialize an array to house sort results
-	$files2 = array();
-	$files2 = array_pad($files2,$guid,0);
-
-	for ($i=0; $i<$guid; $i++) {
-		$j = explode('-',basename($files[$i]));
-		$j = $j[0];
-		$j = (int)$j;
-		$j--;
-		$files2[$j] = $files[$i];
-	}
-
-	$slen = count($files2)-1;
-	$ctr = 0;
-
-		for ($i=$slen; $i>-1; $i--) {
-			$shitpost=$files2[$i];
-		
-			if ($ctr > 9) {break;};
-			$ctr++;
-
-                	$statz = stat($shitpost);
-                	$uid = $statz['uid'];
-                	$udata = posix_getpwuid($uid);
-                	$user = $udata['name'];
-
-                	$date =  date(DATE_RFC2822, filemtime($shitpost));
-
-                	$title = substr(strstr(basename($shitpost),'-'),1,-5);
-			$contents = file_get_contents($shitpost);
-
-			echo "\t<item>\n";
-               		echo "\t\t<title>$title</title>\n";
-                	echo "\t\t<description><![CDATA[".$contents."]]>\t\t</description>\n";
-			echo "\t\t<link>http://teodesian.net/index.php?nav=8&amp;post=".$shitpost."</link>\n";
-			echo "\t\t<guid isPermaLink=\"false\">$guid-teodesian.net</guid>\n";
-			echo "\t\t<pubDate>".$date."</pubDate>\n";
-			echo "\t\t<author>".$user."</author>\n";
-			echo "\t</item>\n";
-			$guid--;
-		}
-	echo "</channel>\n";
-	echo "</rss>";
-?>

+ 0 - 78
sys/rss/microblog.php

@@ -1,78 +0,0 @@
-<?php
-  header('Content-Type: application/rss+xml'); 
-  if (!empty($_SERVER["HTTPS"])) {
-    $protocol = "http";
-  } else {
-    $protocol = "https";
-  }
-  //Import your config, set some stuff up, then construct the mining laser
-  extract(json_decode(file_get_contents('../admin/config/main.json'),true));
-  $tcmsUsers = json_decode(file_get_contents('../admin/config/users.json'),true);
-  date_default_timezone_set($timezone);
-  $tiem = date(DATE_RSS);
-  $today = date("m.d.y");
-  $atomlink = "$protocol://".$_SERVER["SERVER_NAME"]."/".$basedir.$rssdir."microblog.php";
-  $newsdir = $_SERVER["DOCUMENT_ROOT"]."/".$basedir.$microblogdir;
-  $files = glob($newsdir.$today."/*");
-  $slen = count($files);
-  $feed = '<?xml version="1.0" encoding="UTF-8"?>
-            <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
-	     <channel>
-	      <atom:link href="'.$atomlink.'" rel="self" type="application/rss+xml" />
-	      <title>'.$htmltitle.'</title>
-	      <description>'.$microblogtitle.' RSS Feed</description>
-	      <link>http://'.$_SERVER['SERVER_NAME'].'/'.$basedir.'</link>
-	      <lastBuildDate>'.$tiem.'</lastBuildDate>
-	      <pubDate>'.$tiem.'</pubDate>';
-  foreach ($files as $shitpost) {
-    $storyPubDate =  date(DATE_RSS, strtotime(basename($shitpost)));
-    $contents = file_get_contents($shitpost);
-    #Set some sane defaults for cases where no user exists
-    $email = "null@example.com";
-    $author = "X";
-    #Check the format, do needful based on what's here
-    $json = json_decode($contents);
-    if(is_null($json)) {
-      //HAHAHA You thought you needed an XML parser, didn't you?
-      $theRipper = explode("<",$contents);
-      $theRipper = explode(">",$theRipper[2]);
-      $storyTitle = $theRipper[1];
-      $theRipper = explode('"',$theRipper[0]);
-      $storyLink = htmlspecialchars($theRipper[1]);
-      $theRipper = explode("</h3>",$contents);
-      $theRipper = explode("<hr />",$theRipper[1]);
-      $storyText = $theRipper[0];
-      $theRipper = explode("title=\"Posted by ",$contents);
-      $theRipper = explode('"',$theRipper[1]);
-      $poster = $theRipper[0];
-      if(isset($tcmsUsers[$poster])) {
-          $email = $tcmsUsers[$poster]["email"];
-          $author = $tcmsUsers[$poster]["fullName"]; 
-      }
-      $feed .= '<item>
-                 <title>'.$storyTitle.'</title>
-                 <description><![CDATA['.$storyText.']]></description>
-                 <link>'.preg_replace('/&/', '&#038;', $storyLink).'</link>
-                 <guid isPermaLink="false">'.basename($shitpost).'-'.$_SERVER["SERVER_NAME"].'</guid>
-                 <pubDate>'.$storyPubDate.'</pubDate>
-                 <author>'.$email.' ('.$author.')</author>
-                </item>';
-    } elseif (!empty($json->title) && !empty($json->url) && !empty($json->poster)) {
-        if(isset($tcmsUsers[$json->poster])) {
-            $email = $tcmsUsers[$json->poster]["email"];
-            $author = $tcmsUsers[$json->poster]["fullName"]; 
-        }
-        $feed .= '<item>
-                   <title>'.$json->title.'</title>
-                   <description><![CDATA['.$json->comment.']]></description>
-                   <link>'.preg_replace('/&/', '&#038;', $json->url).'</link>
-                   <guid isPermaLink="false">'.basename($shitpost).'-'.$_SERVER["SERVER_NAME"].'</guid>
-                   <pubDate>'.$storyPubDate.'</pubDate>
-                   <author>'.$email.' ('.$author.')</author>
-                  </item>';
-    }
-  }
-  $feed .= ' </channel>
-            </rss>';
-  print_r($feed);
- ?>

BIN
www/assets/audio/test.mp3


BIN
www/assets/video/test.ogv


BIN
www/img/sys/rummy.jpg


+ 8 - 0
www/scripts/post.js

@@ -0,0 +1,8 @@
+function switchMenu(obj) {
+    var el = document.getElementById(obj);
+    if ( el.style.display != 'none' ) {
+        el.style.display = 'none';
+    } else {
+        el.style.display = '';
+    }
+}

+ 26 - 5
www/server.psgi

@@ -12,11 +12,13 @@ use Plack::MIME  ();
 use Mojo::File   ();
 use DateTime::Format::HTTP();
 use Encode qw{encode_utf8};
+use CGI::Cookie ();
 
 #Grab our custom routes
 use lib 'lib';
 use Trog::Routes::HTML;
 use Trog::Routes::JSON;
+use Trog::Auth;
 
 # Troglodyne philosophy - simple as possible
 
@@ -58,6 +60,22 @@ my $app = sub {
     $query = URL::Encode::url_params_mixed($env->{QUERY_STRING}) if $env->{QUERY_STRING};
     my $path = $env->{PATH_INFO};
 
+    # Let's open up our default route before we bother to see if users even exist
+    return $routes{default}{callback}->($query,$env->{'psgi.input'}, \&_render) unless -f "$ENV{HOME}/.tcms/setup";
+
+    my $cookies = {};
+    if ($env->{HTTP_COOKIE}) {
+        $cookies = CGI::Cookie->parse($env->{HTTP_COOKIE});
+    }
+
+    my $active_user = '';
+    if (exists $cookies->{tcmslogin}) {
+         $active_user = Trog::Auth::session2user($cookies->{tcmslogin}->value);
+    }
+    $query->{user}   = $active_user;
+    $query->{domain} = $env->{HTTP_HOST};
+    $query->{route}  = $path;
+
     #Disallow any paths that are naughty ( starman auto-removes .. up-traversal)
     if (index($path,'/templates') == 0 || $path =~ m/.*\.psgi$/i ) {
         return [ 403, [$content_types{plain}], ["STAY OUT YOU RED MENACE"]];
@@ -70,10 +88,9 @@ my $app = sub {
     return [ 404, [$content_types{plain}], ["RETURN TO SENDER"]] unless exists $routes{$path};
     return [ 400, [$content_types{plain}], ["BAD REQUEST"]] unless $routes{$path}{method} eq $env->{REQUEST_METHOD};
 
-    #TODO fish out what user is in cookie
-    $query->{user} = 'Nobody';
+    @{$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->{input}, \&_render);
+    my $output =  $routes{$path}{callback}->($query,$env->{'psgi.input'}, \&_render);
     return $output;
 };
 
@@ -108,7 +125,7 @@ sub _serve ($path, $last_fetch=0) {
     return [ 403, [$content_types{plain}], ["STAY OUT YOU RED MENACE"]];
 }
 
-sub _render ($template, $vars) {
+sub _render ($template, $vars, @headers) {
 
     my $processor = Text::Xslate->new(
         path   => 'www/templates',
@@ -125,10 +142,14 @@ sub _render ($template, $vars) {
     #XXX Need to have minification detection, use Typescript
     $vars->{scripts} //= [];
 
+    # Absolute-ize the paths for scripts & stylesheets
+    @{$vars->{stylesheets}} = map { index($_, '/') == 0 ? $_ : "/$_" } @{$vars->{stylesheets}};
+    @{$vars->{scripts}}     = map { index($_, '/') == 0 ? $_ : "/$_" } @{$vars->{scripts}};
+
     $vars->{contenttype} //= $content_types{html};
     $vars->{cachecontrol} //= $cache_control{revalidate};
 
-    my @headers = ($vars->{contenttype});
+    push(@headers, $vars->{contenttype});
     push(@headers,$vars->{contentdisposition}) if $vars->{contentdisposition};
     push(@headers, $vars->{cachecontrol}) if $vars->{cachecontrol};
     my $h = join("\n",@headers);

+ 57 - 0
www/styles/config.css

@@ -0,0 +1,57 @@
+body, html {
+    font-size: 100%;
+    margin: 0;
+    background-color:white;
+}
+nav {
+    padding: .5rem;
+    height: 2rem;
+    line-height: 2rem;
+    font-size: 1.5rem;
+    background-color: black;
+    color: white;
+}
+section {
+    display: block;
+    margin: 1rem auto 0 auto;
+    padding: 0 1rem;
+}
+#notice {
+    display: table;
+    padding: .5rem;
+    background-color: rgba( 0, 0, 0, .75 );
+    color: #00FF00;
+    border-radius: .25rem;
+}
+#notice > img, #notice > span {
+    margin: .25rem;
+    display: table-cell;
+    vertical-align: middle;
+}
+/* Styles for larger viewports */
+@media( min-width: 768px ) {
+    section {
+        width: 80%;
+    }
+    #notice {
+        width: calc( 100% - 1rem);
+    }
+}
+a.topbar {
+    margin-top: .5rem;
+}
+
+#posttype, #subtitle {
+    display: none;
+}
+
+iframe.responsive, video.responsive { /*Close enough to 16:9*/
+  width: 35rem;
+  height: 20rem;
+}
+@media(max-width: 35rem) {
+  iframe.responsive, video.responsive {
+    width: 100%;
+    height: 10rem;
+  }
+}

+ 1 - 12
www/styles/login.css

@@ -25,18 +25,7 @@ body {
     font-size: .75rem;
     text-align: center;
 }
-#jsalert {
-    margin: 1rem;
-    border-radius: .75rem;
-    border-color: rgba(255,0,0,.75);
-    padding: 1rem;
-}
-.alert-danger {
-    background-color: rgba(255,0,0,.25);
-}
-.alert-success {
-    background-color: rgba(0,255,0,.25);
-}
+
 input {
     box-sizing: border-box;
     border-radius: .5em;

+ 53 - 0
www/styles/post.css

@@ -0,0 +1,53 @@
+body, html {
+    font-size: 100%;
+    margin: 0;
+    background-color:white;
+}
+nav {
+    padding: .5rem;
+    height: 2rem;
+    line-height: 2rem;
+    font-size: 1.5rem;
+    background-color: black;
+    color: white;
+}
+section {
+    display: block;
+    margin: 1rem auto 0 auto;
+    padding: 0 1rem;
+}
+#notice {
+    display: table;
+    padding: .5rem;
+    background-color: rgba( 0, 0, 0, .75 );
+    color: #00FF00;
+    border-radius: .25rem;
+}
+#notice > img, #notice > span {
+    margin: .25rem;
+    display: table-cell;
+    vertical-align: middle;
+}
+/* Styles for larger viewports */
+@media( min-width: 768px ) {
+    section {
+        width: 80%;
+    }
+    #notice {
+        width: calc( 100% - 1rem);
+    }
+}
+a.topbar {
+    margin-top: .5rem;
+}
+
+iframe.responsive, video.responsive { /*Close enough to 16:9*/
+  width: 35rem;
+  height: 20rem;
+}
+@media(max-width: 35rem) {
+  iframe.responsive, video.responsive {
+    width: 100%;
+    height: 10rem;
+  }
+}

+ 2 - 2
www/styles/screen.css

@@ -164,13 +164,13 @@ button#clickme:active {
  background-color: #333;
  width: 100%;
 }
-#Submissions input, #Submissions textarea {
+.Submissions input, .Submissions textarea {
  width: 95%;
  display: block;
  margin-right: auto;
  margin-left: auto;
 }
-#Submissions textarea {
+.Submissions textarea {
  height: 20em;
  vertical-align: top;
 }

+ 4 - 0
www/styles/structure.css

@@ -94,3 +94,7 @@ body {
 blockquote {
   font-style: italic;
 }
+
+.inline {
+    display: inline;
+}

+ 6 - 0
www/templates/blog.tx

@@ -0,0 +1,6 @@
+<form class="Submissions" action="/post/save" method="POST">
+    Title *<br /><input class="cooltext" type="text" name="title" placeholder="Iowa Man Destroys Moon" />
+    Content<br /><textarea class="cooltext" name="comment" placeholder="Potzrebie"></textarea>
+    <input type="hidden" name="app" value="blog" />
+    <input class="coolbutton" type="submit" value="Publish" text="Publish" />
+</form>

+ 35 - 21
www/templates/config.tx

@@ -1,10 +1,28 @@
+: include "sysbar.tx";
+: include "jsalert.tx";
 <p class="title">
  General settings:
 </p>
+This controls your Theme and Data Model used.
+The Data Model *must* be 0-configuration.
+If for example, you use mysql it will have to rely on either a local server, valid config file or connection proxy/pooler locally.
 <hr />
 <form id="mainConfig" method="post" action="/config/save">
-    TODO configuration form here
+    Theme:
+    <select class="cooltext" name="theme">
+        <option value="" <: if ( $current_theme == '' ) { :>selected<: } :> >default</option>
+        : for $themes -> $theme {
+        <option value="<: $theme :>" <: if ( $current_theme == $theme ) { :>selected<: } :> ><: $theme :></option>
+        : }
+    </select>
     <br />
+    Data Model:
+    <select class="cooltext" name="data_model">
+        : for $data_models -> $dm {
+        <option value="<: $dm :>"><: $dm :></option>
+        : }
+    </select>
+    <br /><br />
     <input type="submit" class="coolbutton" value="Commit Changes" />
 </form>
 <hr />
@@ -12,30 +30,26 @@
 <p class="title">
  User management:
 </p>
+Users and their sessions are stored in ~/.tcms/auth.db (sqlite3).  All other user data is considered a post like any other.
 <hr />
-<form id="userConfig" method="post" action="/posts/save">
-    TODO include the posts/ view here to show all users so we can b&
-    <br />
-    <input type="submit" class="coolbutton" value="Commit Changes" />
-</form>
-<br />
-<p class="title">
- Add User:
-</p>
-<form id="addUser" method="post" action="index.php">
-    TODO include the /post view here so we can add new user entries which *don't* have a domain email account
-    <br />
-    <input type="submit" class="coolbutton" value="Commit Changes" />
-</form>
+: include "mbengine.tx";
 <hr />
 
 <p class="title">
  Theme cloner:
 </p>
+Want to write your own theme?
+Clone a theme here then see the <a href="https://tcms.troglodyne.net/index.php?nav=5&post=fileshare/manual/Chapter 03-Customization.post" title="GET UR MIND RITE">styling guide</a>
+for information on how tCMS' templates, image sets and CSS work in the theming system.
 <hr />
-<p>
-  Want to write your own theme?
-  Clone a theme here then see the <a href="https://tcms.troglodyne.net/index.php?nav=5&post=fileshare/manual/Chapter 03-Customization.post" title="GET UR MIND RITE">styling guide</a>
-  for information on how tCMS' templates, image sets and CSS work in the theming system.
-</p>
-INSERT FORM HERE
+<form id="themeCloner" method="post" action="/themeclone">
+    Theme:
+    <select class="cooltext" name="theme">
+        : for $themes -> $theme {
+        <option value="<: $theme :>"><: $theme :></option>
+        : }
+    </select>
+    <br />
+    <input type="text" class="cooltext" placeholder="newTheme" name="newtheme" />
+    <input type="submit" class="coolbutton" value="Clone" />
+</form>

+ 9 - 0
www/templates/file.tx

@@ -0,0 +1,9 @@
+<form class="Submissions" action="/post/save" method="POST">
+    Title *<br /><input class="cooltext" type="text" name="title" placeholder="Iowa Man Destroys Moon" />
+    File *<br /><input class="cooltext" type="file" name="file" />
+    <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"></input>
+    Comments<br /><textarea class="cooltext" name="comment" placeholder="Potzrebie"></textarea>
+    <input type="hidden" name="app" value="file" />
+    <input class="coolbutton" type="submit" value="Publish" text="Publish" />
+</form>

+ 4 - 4
www/templates/header.tx

@@ -6,15 +6,15 @@
         <link rel="icon" type="image/vnd.microsoft.icon" href="<: $theme_dir :>/img/icon/favicon.ico" />
         <meta name="description" content="A Simple CMS by teodesian.net"/>
         <meta name="viewport" content="width=device-width">
-        <link rel="stylesheet" type="text/css" href="styles/structure.css" />
-        <link rel="stylesheet" type="text/css" href="styles/screen.css" media="screen" />
-        <link rel="stylesheet" type="text/css" href="styles/print.css" media="print" />
+        <link rel="stylesheet" type="text/css" href="/styles/structure.css" />
+        <link rel="stylesheet" type="text/css" href="/styles/screen.css" media="screen" />
+        <link rel="stylesheet" type="text/css" href="/styles/print.css" media="print" />
         <!-- Stylesheets !-->
         : for $stylesheets -> $stylesheet {
         <link rel="stylesheet" type="text/css" href="<: $stylesheet :>" media="screen" />
         : }
         : for $scripts -> $script {
-        <script type="text/javascript" src="scripts/<: $script :>"></script>
+        <script type="text/javascript" src="<: $script :>"></script>
         : }
     </head>
     <body>

+ 15 - 7
www/templates/index.tx

@@ -7,14 +7,22 @@
     </div>
     <button title="Menu" id="clickme">&#9776;</button>
     <div id="righttitle" class="toplel">
-        <a href="/news"  title="Micro Blog" class="topbar">LinkLog</a>
-        <a href="/blog"  title="Blog" class="topbar">Blog</a>
-        <a href="/wiki"  title="Wiki" class="topbar">Wiki</a>
-        <a href="/video" title="Video" class="topbar">Video</a>
-        <a href="/audio" title="Audio" class="topbar">Audio</a>
-        <a href="/files" title="Files" class="topbar">Files</a>
-        <a href="/about" title="About" class="topbar">About</a>
+        <a href="/news"   title="Micro Blog" class="topbar">News</a>
+        <a href="/blog"   title="Blog" class="topbar">Blog</a>
+        <a href="/image" title="Images" class="topbar">Images</a>
+        <a href="/video"  title="Video" class="topbar">Video</a>
+        <a href="/audio"  title="Audio" class="topbar">Audio</a>
+        <a href="/files"  title="Files" class="topbar">Files</a>
+        <a href="/series" title="Series" class="topbar">Series</a>
+        <a href="/about"  title="About" class="topbar">About</a>
+        <form action="<: $route :>" method="GET" class="inline">
+            <input type="text" name="like"></input>
+            <input type="submit" value="🔎"></input>
+            <a class="topbar" target="_blank" href="<: $search_help :>" title="<: $search_lang :>">❓</a>
+        </form>
+        : if ($user) {
         <a href="/config" title="config" class="topbar usericon <: $user :>"></a>
+        : }
     </div>
 </div>
 <div id="littlemenu">

+ 50 - 0
www/templates/jsalert.tx

@@ -0,0 +1,50 @@
+<script>
+    document.addEventListener("DOMContentLoaded", function(event) {
+        var loginFailure = <: $failure :>;
+        if( loginFailure === -1 ) {
+            document.querySelector('#jsalert').style.cssText = 'visibility: hidden;';
+        } else if ( loginFailure === 1 ) {
+            document.querySelector('#jsalert').classList.remove("alert-success");
+            document.querySelector('#jsalert').classList.add("alert-danger");
+            document.querySelector('#msgIcon').innerHTML = "❌";
+            document.querySelector('#message').innerHTML = "<: $message :>";
+        } else {
+            document.querySelector('#jsalert').classList.remove("alert-danger");
+            document.querySelector('#jsalert').classList.add("alert-success");
+            document.querySelector('#msgIcon').innerHTML = "✓";
+            document.querySelector('#message').innerHTML = "<: $message :>";
+            //JS redir better ux than 302
+            window.setTimeout(function() {
+                window.location="<: $to :>";
+            }, 500);
+        }
+    });
+</script>
+
+<style>
+    #jsalert {
+        margin: 1rem;
+        border-radius: .75rem;
+        border-color: rgba(255,0,0,.75);
+        padding: 1rem;
+    }
+    .alert-danger {
+        background-color: rgba(255,0,0,.25);
+    }
+    .alert-success {
+        background-color: rgba(0,255,0,.25);
+    }
+</style>
+
+<div id="jsalert" class="alert-danger">
+  <table>
+    <tr>
+      <td id="msgIcon">
+        ⚠
+      </td>
+      <td id="message" style="padding-left: 1rem;">
+        Please enable JavaScript on this domain.
+      </td>
+    </tr>
+  </table>
+</div>

+ 2 - 35
www/templates/login.tx

@@ -1,38 +1,5 @@
-<script>
-    document.addEventListener("DOMContentLoaded", function(event) {
-        var loginFailure = <: $login_failure :>;
-        if( loginFailure === -1 ) {
-            document.querySelector('#jsalert').style.cssText = 'visibility: hidden;';
-        } else if ( loginFailure === 1 ) {
-            document.querySelector('#jsalert').classList.remove("alert-success");
-            document.querySelector('#jsalert').classList.add("alert-danger");
-            document.querySelector('#msgIcon').innerHTML = "❌";
-            document.querySelector('#message').innerHTML = "<: $login_message :>";
-        } else {
-            document.querySelector('#jsalert').classList.remove("alert-danger");
-            document.querySelector('#jsalert').classList.add("alert-success");
-            document.querySelector('#msgIcon').innerHTML = "✓";
-            document.querySelector('#message').innerHTML = "<: $login_message :>";
-            //JS redir better ux than 302
-            window.setTimeout(function() {
-              window.location="<: $to :>";
-            }, 500);
-        }
-    });
-</script>
 <div id="login">
-    <div id="jsalert" class="alert-danger">
-      <table>
-        <tr>
-          <td id="msgIcon">
-            ⚠
-          </td>
-          <td id="message" style="padding-left: 1rem;">
-            Please enable JavaScript on this domain.
-          </td>
-        </tr>
-      </table>
-    </div>
+    : include "jsalert.tx";
     <div>
       <img id="logo" src="img/icon/tCMS.svg" style="float:left" /><span style="font-family:courier;font-size:2rem;">CMS Login</span>
     </div>
@@ -52,6 +19,6 @@
         <input name="password" id="password" placeholder="hunter2" value="" type="password"></input>
       </div>
       <br />
-      <input type="submit" id="maximumGo" value="Log in"></input>
+      <input type="submit" id="maximumGo" value="<: $btnmsg :>"></input>
     </form>
 </div>

+ 18 - 0
www/templates/mbengine.tx

@@ -0,0 +1,18 @@
+<div id="mbengine">
+    <div id="submissions">
+        <p id="subtitle" class="title">Submissions:</p>
+        <form id="posttype">
+            New post Type:
+            <select class="cooltext" name="type" onchange="this.form.submit()" >
+                : for $types -> $type {
+                    <option value="<: $type :>" <: if ($edittype == $type) { :>selected<: } :> ><: $type :></option>
+                : }
+            </select>
+            <hr />
+        </form>
+        : include $edittype ~ ".tx";
+    </div>
+    <div id="stories">
+        : include "posts.tx";
+    </div>
+</div>

+ 10 - 0
www/templates/microblog.tx

@@ -0,0 +1,10 @@
+<form class="Submissions" action="/post/save" method="POST">
+    Title *<br /><input class="cooltext" type="text" name="title" placeholder="Iowa Man Destroys Moon" />
+    URL *<br /><input class="cooltext" type="text" name="URL" placeholder="https://oneweirdtrick.scam" />
+    Image<br /><input class="cooltext" type="text" name="IMG" placeholder="https://gifdump.tld/Advice_Dog.jpg" />
+    Audio<br /><input class="cooltext" type="text" name="AUD" placeholder="https://soundclod.com/static.mp3"/>
+    Video<br /><input class="cooltext" type="text" name="VID" placeholder="https://youvimeo.tv/infomercial.mp4" />
+    Comments<br /><textarea class="cooltext" name="comment" placeholder="Potzrebie"></textarea>
+    <input type="hidden" name="app" value="microblog" />
+    <input class="coolbutton" type="submit" value="Publish" text="Publish" />
+</form>

+ 1 - 1
www/templates/notconfigured.tx

@@ -16,6 +16,6 @@
         </a>
         for full instructions on how configure tCMS.
         <br /><br />
-        Please <a href="sys/admin/config" alt="Login">Log In</a> and Configure tCMS.
+        Please <a href="/config" alt="Login">Log In</a> and Configure tCMS.
     </p>
 </section>

+ 2 - 18
www/templates/post.tx

@@ -1,18 +1,2 @@
-<div id="mbengine">
- <div id="submissions">
-  <p class="title">Submissions:</p>
-  <form id="Submissions" method="POST">
-   Title *<br /><input class="cooltext" type="text" name="title" placeholder="Iowa Man Destroys Moon" />
-   URL *<br /><input class="cooltext" type="text" name="URL" placeholder="https://oneweirdtrick.scam" />
-   Image<br /><input class="cooltext" type="text" name="IMG" placeholder="https://gifdump.tld/Advice_Dog.jpg" />
-   Audio<br /><input class="cooltext" type="text" name="AUD" placeholder="https://soundclod.com/static.mp3"/>
-   Video<br /><input class="cooltext" type="text" name="VID" placeholder="https://youvimeo.tv/infomercial.mp4" />
-   Comments<br /><textarea class="cooltext" name="comment" placeholder="Potzrebie"></textarea>
-   <input type="hidden" name="app" value="microblog" />
-   <input class="coolbutton" type="submit" value="Publish" text="Publish" />
-  </form>
- </div>
- <div id="stories">
-   : include "posts.tx";
- </div>
-</div>
+: include "sysbar.tx";
+: include "mbengine.tx";

+ 21 - 2
www/templates/posts.tx

@@ -1,4 +1,23 @@
-<p class="title"><a title="RSS" class="rss" href="/sys/rss/microblog.php"></a> Headline Nudes (<: $date :>):</p>
+<p class="title">
+<a title="RSS" class="rss" href="<: $route :>?format=rss"></a>
+<: $category :> (<: $date :>):
+</p>
 : for $posts -> $post {
-:   $post.data | mark_raw
+    <hr>
+    <h3 class='blogtitles'>
+        <a href='<: $post.href :>'><: $post.title :></a>
+        <a class='usericon <: $post.user :>' title='Posted by <: $post.user :>'></a>
+    </h3>
+    : $post.data | mark_raw;
+    : if ( $can_edit ) {
+    <br />
+    <a style="display: inline-block;" onclick="switchMenu('<: $post.id :>');">[Edit]</a>
+    <div id="<: $post.id :>" style="display:none;">
+        : include $post.type ~ ".tx";
+        <form class="Submissions" action="/post/delete" class="inline">
+            <input type="hidden" name="id" value="<: $post.id :>"></input>
+            <input class="coolbutton" type="submit" value="Delete"></input>
+        </form>
+    </div>
+    : }
 : }

+ 10 - 0
www/templates/profile.tx

@@ -0,0 +1,10 @@
+<form class="Submissions" action="/profile" method="POST">
+    Username *<br /><input class="cooltext" type="text" name="title" placeholder="AzureDiamond" />
+    Password *<br /><input class="cooltext" type="text" name="password" placeholder="hunter2" />
+    Avatar *<br /><input class="cooltext" type="file" name="file" />
+    Wallpaper<br /><input type="file" class="cooltext" name="wallpaper" placeholder="PROMO.JPG"></input>
+    Title  <br /><input class="cooltext" type="text" name="title" />
+    Content<br /><textarea class="cooltext" name="comment" placeholder="Potzrebie"></textarea>
+    <input type="hidden" name="app" value="file" />
+    <input class="coolbutton" type="submit" value="Publish" text="Publish" />
+</form>

+ 9 - 0
www/templates/sysbar.tx

@@ -0,0 +1,9 @@
+<div id="topkek" style="text-align: center; vertical-align: middle;">
+    <button title="Menu" id="clickme">☰</button>
+    <span id="configbar">
+        <a class="topbar" title="Back home" href="/">Home</a>
+        <a class="topbar" title="Edit Various Settings" href="/config">Settings</a>
+        <a class="topbar" title="Compose Posts" href="/post">Post</a>
+    </span>
+</div>
+<div style="height:3rem;">hidon</div>

BIN
www/themes/teodesian.net/img/links/LR.png


BIN
www/themes/teodesian.net/img/links/alp.png


BIN
www/themes/teodesian.net/img/links/anarchast.png


BIN
www/themes/teodesian.net/img/links/antiWarRadio.png


BIN
www/themes/teodesian.net/img/links/atland.png


BIN
www/themes/teodesian.net/img/links/avtm.png


BIN
www/themes/teodesian.net/img/links/awc.png


BIN
www/themes/teodesian.net/img/links/axiom.png


BIN
www/themes/teodesian.net/img/links/bash.png


BIN
www/themes/teodesian.net/img/links/bfp.png


BIN
www/themes/teodesian.net/img/links/bq.png


BIN
www/themes/teodesian.net/img/links/charleyjones.png


BIN
www/themes/teodesian.net/img/links/cheeses.png


BIN
www/themes/teodesian.net/img/links/civ.png


BIN
www/themes/teodesian.net/img/links/corbett.png


BIN
www/themes/teodesian.net/img/links/cryptogon.png


BIN
www/themes/teodesian.net/img/links/dailybell.png


BIN
www/themes/teodesian.net/img/links/dr.png


BIN
www/themes/teodesian.net/img/links/dudeenough.png


BIN
www/themes/teodesian.net/img/links/e2.png


BIN
www/themes/teodesian.net/img/links/econned.png


BIN
www/themes/teodesian.net/img/links/ehancock.png


BIN
www/themes/teodesian.net/img/links/epj.png


BIN
www/themes/teodesian.net/img/links/feens.png


BIN
www/themes/teodesian.net/img/links/ff.png


BIN
www/themes/teodesian.net/img/links/fourmilab.png


BIN
www/themes/teodesian.net/img/links/haskell.png


BIN
www/themes/teodesian.net/img/links/hn.png


BIN
www/themes/teodesian.net/img/links/infowars.png


BIN
www/themes/teodesian.net/img/links/insideauto.png


BIN
www/themes/teodesian.net/img/links/intelnews.png


BIN
www/themes/teodesian.net/img/links/kittyfeet.png


BIN
www/themes/teodesian.net/img/links/lrcpod.png


BIN
www/themes/teodesian.net/img/links/lrn.png


BIN
www/themes/teodesian.net/img/links/maddox.png


BIN
www/themes/teodesian.net/img/links/mcninja.png


BIN
www/themes/teodesian.net/img/links/mises.png


BIN
www/themes/teodesian.net/img/links/mogambo.png


BIN
www/themes/teodesian.net/img/links/nuk.png


BIN
www/themes/teodesian.net/img/links/octave.png


BIN
www/themes/teodesian.net/img/links/pennyarcade.png


BIN
www/themes/teodesian.net/img/links/pimp.png


BIN
www/themes/teodesian.net/img/links/ritholtz.png


BIN
www/themes/teodesian.net/img/links/robandelliot.png


BIN
www/themes/teodesian.net/img/links/rumblo.png


BIN
www/themes/teodesian.net/img/links/scarygoround.png


BIN
www/themes/teodesian.net/img/links/schiffradio.png


Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov