use Plack::App::Gruntmaster;
use Plack::Builder;
use Plack::Request;
+use Plack::Util;
use Digest::SHA qw/sha256/;
use Log::Log4perl;
use Tie::Hash::Expire;
sub authenticate {
my ($user, $pass, $env) = @_;
- say "Checking $user and $pass";
my $key = sha256 "$user:$pass";
$env->{'gruntmaster.user'} = $user;
return 1 if exists $auth{$key};
$auth{key} = 1;
}
+sub add_headers {
+ my $app = $_[0];
+ sub {
+ my $resp = $app->($_[0]);
+ my $hdrs = Plack::Util::headers($resp->[1]);
+ $hdrs->set('Content-Security-Policy', CONTENT_SECURITY_POLICY);
+ $hdrs->set('Cache-Control', 'public, max-age=604800') if $_[0]->{PATH_INFO} =~ qr,^/static/,;
+ $resp->[1] = $hdrs->headers;
+ $resp;
+ }
+}
+
Log::Log4perl->init('log.conf');
my $access_logger = Log::Log4perl->get_logger('access');
$ENV{DBIC_NULLABLE_KEY_NOWARN} = 1;
builder {
enable 'AccessLog', format => ACCESSLOG_FORMAT, logger => sub { $access_logger->info(@_) };
enable 'ContentLength';
- enable Header => set => ['Content-Security-Policy', CONTENT_SECURITY_POLICY];
- enable_if { $_[0]->{PATH_INFO} =~ qr,^/static/,} Header => set => ['Cache-Control', 'public, max-age=604800'];
+ enable \&add_headers;
enable 'Static', path => qr,^/static/,;
enable 'Log4perl', category => 'plack';
- enable_if { shift->{HTTP_WWW_AUTHENTICATE} } 'Auth::Basic', authenticator => \&authenticate, realm => 'Gruntmaster 6000';
+ enable_if { shift->{HTTP_AUTHORIZATION} } 'Auth::Basic', authenticator => \&authenticate, realm => 'Gruntmaster 6000';
enable_if { $_[0]->{PATH_INFO} eq '/ok' } sub { sub{ [200, [], []] }};
enable sub { my $app = $_[0]; sub { $_[0]->{'gruntmaster.dbic'} = $db; $app->($_[0]) } };
Plack::App::Gruntmaster->run_if_script
width: 60em;
margin: auto;
margin-top: 6em;
- white-space: pre-wrap;
color: #777;
}
#clock{
float: right;
margin-right: 1em;
-}
\ No newline at end of file
+}
use strict;
our $VERSION = '5999.000_001';
-use Apache2::Authen::Passphrase qw/pwcheck pwset/;
use CSS::Minifier::XS;
+use Encode qw/encode decode/;
use File::Slurp qw/read_file/;
use JavaScript::Minifier::XS;
-
-use HTML::Template::Compiled;
+use JSON::MaybeXS qw/encode_json/;
use PerlX::Maybe;
use Scope::Upper qw/unwind SUB UP/;
+use Web::Simple;
use Gruntmaster::Data;
-use Web::Simple;
-no warnings FATAL => 'all';
-use warnings;
-no warnings::illegalproto;
+use Plack::App::Gruntmaster::HTML;
+
+use warnings NONFATAL => 'all';
+no warnings 'illegalproto';
no if $] >= 5.017011, warnings => 'experimental::smartmatch';
##################################################
-sub read_templates {
- my $name = shift;
-
- my %tmpl = map { m/\.(.+)$/; $1 => scalar read_file $_ } <tmpl/$name.*>;
- my %arti = map { m/\.(.+)$/; $1 => scalar read_file $_ } <a/$name.*>;
- return %tmpl ? %tmpl : %arti
-}
-
-my %header_templates = read_templates 'header';
-my %footer_templates = read_templates 'footer';
-my %templates;
-
-sub render {
- my ($tmpl, $title, %params) = @_;
- unless ($templates{$tmpl}) {
- $templates{$tmpl} = { read_templates $tmpl };
- for my $lang (keys $templates{$tmpl}) {
- my $header = $header_templates{$lang} =~ s/TITLE_GOES_HERE/$title/rg;
- $templates{$tmpl}{$lang} = $header . $templates{$tmpl}{$lang};
- }
- $templates{$tmpl}{$_} .= $footer_templates{$_} for keys $templates{$tmpl};
-
- }
-
- my $htc = HTML::Template::Compiled->new(scalarref => \$templates{$tmpl}{en}, default_escape => 'HTML', use_perl => 1);
- $htc->param(%params);
- [200, ['Content-Type' => 'text/html'], [$htc->output]]
-}
-
use constant USER_REGEX => qr/^\w{2,20}$/a;
use constant CONTENT_TYPES => +{
};
use constant NOT_FOUND => [404, ['Content-Type' => 'text/plain'], ['Not found']];
-use constant FORBIDDEN => [401, ['Content-Type' => 'text/plain', 'WWW-Authenticate' => ' Basic realm="Gruntmaster 6000"'], ['Forbidden']];
+use constant FORBIDDEN => [401, ['Content-Type' => 'text/plain', 'WWW-Authenticate' => 'Basic realm="Gruntmaster 6000"'], ['Forbidden']];
my $env;
$user
}
-sub admin { remote_user && remote_user->isadmin }
+sub admin { remote_user && remote_user->admin }
sub contest { db->contest ($_{contest}) }
sub problem { db->problem ($_{problem}) }
sub job { db->job ($_{job}) }
}
bless {template => $template, title => $title, params => $params}, __PACKAGE__.'::Response'
}
+
sub forbid {
return if !shift || admin;
unwind FORBIDDEN, SUB UP
response_filter {
my ($r) = @_;
return $r if ref $r ne 'Plack::App::Gruntmaster::Response';
- return [200, ['Content-Type' => 'application/json', 'X-Forever' => 1], [encode_json $r->{params}]] if $env->{HTTP_ACCEPT} =~ m,^\s*application/json\s*$,g;
- render $r->{template}, $r->{title}, %{$r->{params}}
+ return [200, ['Content-Type' => 'application/json', 'X-Forever' => 1], [encode 'UTF-8', encode_json $r->{params}]] if $env->{HTTP_ACCEPT} =~ m,^\s*application/json\s*$,g;
+ my $ret = render $r->{template}, 'en', title => $r->{title}, %{$r->{params}};
+ [200, ['Content-Type' => 'text/html'], [encode 'UTF-8', $ret]]
},
},
sub (/us/) { response us => 'Users', {us => db->user_list} },
sub (/ct/ + ?:owner~) { response ct => 'Contests', db->contest_list(%_) },
- sub (/log/ + ?:contest~&:owner~&:page~&:problem~) { response log => 'Job list', {log => db->job_list(%_)} },
- sub (/pb/ + ?:owner~&:contest~) { response pb => 'Problems', db->problem_list(%_) },
+ sub (/log/ + ?:contest~&:owner~&:page~&:problem~) { response log => 'Job list', {%{db->job_list(%_)}, maybe contest => $_{contest}} },
+ sub (/pb/ + ?:owner~&:contest~) { response pb => 'Problems', {%{db->problem_list(%_)}, maybe contest => $_{contest}} },
sub (/us/:user) { response us_entry => user->name, db->user_entry($_{user}) },
sub (/ct/:contest) { response ct_entry => contest->name, db->contest_entry($_{contest}) },
},
sub (/) { redispatch_to '/index' },
-
- sub (/:article) {
- my $title = read_file "a/$_{article}.en.title";
- response $_{article} => $title, {};
- }
+ sub (/:article) { [200, ['Content-Type' => 'text/html'], [render_article $_{article}, 'en']] }
},
sub (POST) {
reply 'Password changed successfully';
},
- sub (/action/submit + %:problem=&:contest~&prog_format=&private~ + *source_code=) {
+ sub (/action/submit + %:problem=&:contest~&:prog_format=&:private~&:source_code~ + *:prog~) {
forbid !remote_user;
- return reply 'This contest has finished' if contest->is_finished;
- return reply 'This contest has not yet started' if !admin && contest->is_pending;
- return reply 'Maximum source size is 10KB' if $_{source_code}->size > 25 * 1024;
+ return reply 'This contest has finished' if contest && contest->is_finished;
+ return reply 'This contest has not yet started' if !admin && contest && contest->is_pending;
+ return reply 'Maximum source size is 10KB' if ($_{prog} ? $_{prog}->size : length $_{source_code}) > 10 * 1024;
return reply 'You must wait 30 seconds between jobs' if !admin && time <= remote_user->lastjob + 30;
remote_user->update({lastjob => time});
- my $prog = read_file $_{source_code}->path;
- unlink $_{source_code}->path;
+ my $prog = $_{prog} ? read_file $_{prog}->path : $_{source_code};
+ unlink $_{prog}->path if $_{prog};
db->jobs->create({
maybe contest => $_{contest},
maybe private => $_{private},
--- /dev/null
+package Plack::App::Gruntmaster::HTML;
+use v5.14;
+use parent qw/Exporter/;
+our @EXPORT = qw/render render_article/;
+
+use File::Slurp qw/read_file/;
+use HTML::Seamstress;
+use POSIX qw//;
+use Data::Dumper qw/Dumper/;
+
+sub ftime ($) { POSIX::strftime '%c', localtime shift }
+sub literal ($) { HTML::Element::Library::super_literal shift // '' }
+
+sub HTML::Element::edit_href {
+ my ($self, $sub) = @_;
+ local $_ = $self->attr('href');
+ $sub->();
+ $self->attr(href => $_);
+}
+
+sub HTML::Element::iter3 {
+ my ($self, $data, $code) = @_;
+ my $orig = $self;
+ my $prev = $orig;
+ for my $el (@$data) {
+ my $current = $orig->clone;
+ $code->($el, $current);
+ $prev->postinsert($current);
+ $prev = $current;
+ }
+ $orig->detach;
+}
+
+sub HTML::Element::fid { shift->look_down(id => shift) }
+sub HTML::Element::fclass { shift->look_down(class => shift) }
+
+sub HTML::Element::namedlink {
+ my ($self, $id, $name) = @_;
+ $name = $id unless $name =~ /[[:graph:]]/;
+ $self = $self->find('a');
+ $self->edit_href(sub {s/id/$id/});
+ $self->replace_content($name);
+}
+
+sub render {
+ my ($tmpl, $lang, %args) = @_;
+ $lang //= 'en';
+ my $meat = _render($tmpl, $lang, %args);
+ _render('skel', $lang, %args, meat => $meat)
+}
+
+sub render_article {
+ my ($art, $lang) = @_;
+ $lang //= 'en';
+ my $title = read_file "a/$art.$lang.title";
+ my $meat = read_file "a/$art.$lang";
+ _render('skel', $lang, title => $title , meat => $meat)
+}
+
+sub _render {
+ my ($tmpl, $lang, %args) = @_;
+ my $builder = HTML::Seamstress->new;
+ $builder->ignore_unknown(0);
+ my $tree = $builder->parse_file("tmpl/$tmpl.$lang");
+ $tree = $tree->guts unless $tmpl eq 'skel';
+ $tree->defmap(smap => \%args);
+ my $process = __PACKAGE__->can("process_$tmpl");
+ $process->($tree, %args) if $process;
+ $tree->as_HTML;
+}
+
+sub process_skel {
+ my ($tree, %args) = @_;
+ $tree->content_handler(
+ title => $args{title},
+ content => literal $args{meat});
+}
+
+sub process_us_entry {
+ my ($tree, %args) = @_;
+ $tree->fid($_)->attr('href', "/$_/?owner=$args{id}") for qw/log pb/;
+}
+
+sub process_us {
+ my ($tree, %args) = @_;
+ my $item = $tree->fclass('list-group-item');
+ $item->replace_with(map {
+ my $new = $item->clone;
+ $new->attr(href => $_->{id});
+ $new->replace_content($_->{name} || $_->{id});
+ $new
+ } @{$args{us}});
+}
+
+sub process_ct_entry {
+ my ($tree, %args) = @_;
+ $_->edit_href (sub {s/contest_id/$args{id}/}) for $tree->find('a');
+ $tree->fid('links')->detach unless $args{started};
+ $tree->content_handler(
+ start => ftime $args{start},
+ stop => ftime $args{stop},
+ description => literal $args{description});
+}
+
+sub process_ct {
+ my ($tree, %args) = @_;
+ my $iter = sub {
+ my ($data, $tr) = @_;
+ $data->{$_} = ftime $data->{$_} for qw/start stop/;
+ $tr->hashmap(class => $data, [qw/name owner/]);
+ $tr->fclass('name')->namedlink($data->{id}, $data->{name});
+ $tr->fclass('owner')->namedlink($data->{owner}, $data->{owner_name});
+ };
+ $args{$_} ? $tree->fid($_)->find('tbody')->find('tr')->iter3($args{$_}, $iter) : $tree->fid($_)->detach for qw/running pending finished/;
+}
+
+sub process_pb_entry {
+ my ($tree, %args) = @_;
+ $tree->fid('owner')->edit_href(sub{s/owner_id/$args{owner}/});
+ $tree->fid('job_log')->edit_href(sub{s/problem_id/$args{id}/});
+ $tree->content_handler(
+ statement => literal $args{statement},
+ author => $args{author},
+ owner => $args{owner_name} || $args{owner});
+ if ($args{cansubmit}) {
+ $tree->look_down(name => 'problem')->attr(value => $args{id});
+ my $contest = $tree->look_down(name => 'contest');
+ $contest->attr(value => $args{contest}) if $args{contest};
+ $contest->detach unless $args{contest}
+ } else {
+ $tree->fid('submit')->detach
+ }
+}
+
+sub process_pb {
+ my ($tree, %args) = @_;
+ my $titer = sub {
+ my ($data, $tr) = @_;
+ $tr->set_child_content(class => 'author', $data->{author});
+ $tr->fclass('name')->namedlink($data->{id}, $data->{name});
+ $tr->fclass('name')->find('a')->edit_href(sub {$_ .= "?contest=$args{contest}"}) if $args{contest};
+ $tr->fclass('owner')->namedlink($data->{owner}, $data->{owner_name});
+ };
+ my $iter = sub {
+ my ($data, $div) = @_;
+ $div->attr(id => $data);
+ $div->find('h2')->replace_content(ucfirst $data);
+ $div->find('tbody')->iter3($args{$data}, $titer);
+ };
+ $tree->fid('beginner')->iter3([grep {$args{$_}} qw/beginner easy medium hard/], $iter);
+}
+
+sub process_log_entry {
+ my ($tree, %args) = @_;
+ $args{errors} ? $tree->fid('errors')->find('pre')->replace_content($args{errors}) : $tree->fid('errors')->detach;
+ my $iter = sub {
+ my ($data, $tr) = @_;
+ $data->{time} = sprintf '%.4fs', $data->{time};
+ $tr->defmap(class => $data);
+ $tr->fclass('result_text')->attr(class => "r$data->{result}")
+ };
+ @{$args{results}} ? $tree->fid('results')->find('tbody')->find('tr')->iter3($args{results}, $iter) : $tree->fid('results')->detach;
+}
+
+sub process_log {
+ my ($tree, %args) = @_;
+ my $iter = sub {
+ my ($data, $tr) = @_;
+ $tr->fclass('id')->namedlink($data->{id});
+ $tr->fclass('problem')->namedlink($data->{problem}, $data->{problem_name});
+ $tr->fclass('problem')->find('a')->edit_href(sub{$_ .= "?contest=$args{contest}"}) if $args{contest};
+ $tr->fclass('date')->replace_content(ftime $data->{date});
+ $tr->fclass('size')->namedlink("$data->{id}.$data->{extension}", sprintf "%.2fKB", $data->{size}/1024);
+ $tr->fclass('size')->attr('data-private', '') if $data->{private};
+ $tr->fclass('owner')->namedlink($data->{owner}, $data->{owner_name});
+ $tr->fclass('result_text')->replace_content($data->{result_text});
+ $tr->fclass('result_text')->attr(class => "r$data->{result}");
+ };
+ $tree->find('table')->find('tbody')->find('tr')->iter3($args{log}, $iter);
+ $args{next_page} ? $tree->fclass('next')->namedlink($args{next_page}, 'Next') : $tree->fclass('next')->attr(class => 'next disabled');
+ $args{previous_page} ? $tree->fclass('previous')->namedlink($args{previous_page}, 'Previous') : $tree->fclass('previous')->attr(class => 'previous disabled');
+ $tree->fclass('current')->replace_content("Page $args{current_page} of $args{last_page}");
+}
+
+sub process_st {
+ my ($tree, %args) = @_;
+ $args{problems} //= [];
+ my $pbiter = sub {
+ my ($data, $th) = @_;
+ $th->attr(class => undef);
+ $th->namedlink($data->id, $data->name);
+ };
+ $tree->fclass('problem')->iter3($args{problems}, $pbiter);
+ my $iter = sub {
+ my ($st, $tr) = @_;
+ $tr->set_child_content(class => 'rank', $st->{rank});
+ $tr->set_child_content(class => 'score', $st->{score});
+ $tr->fclass('user')->namedlink($st->{user}->id, $st->{user}->name);
+ my $pbscore = $tr->fclass('pbscore');
+ $pbscore->detach unless $st->{problems};
+ $pbscore->iter($pbscore => @{$st->{scores}});
+ };
+ $tree->find('tbody')->find('tr')->iter3($args{st}, $iter);
+}
-<tmpl_if running>
+<div id="running">
<h1>Running contests</h1>
<table border class="table table-bordered table-striped">
<thead>
<tr><th>Name<th>Start date<th>Stop date<th>Owner
+</thead>
+
<tbody>
-<tmpl_loop running><tr><td><a href="/ct/<tmpl_var id>"><tmpl_var name></a>
-<td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{start}; %>
-<td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{stop}; %>
-<td><a href="/us/<tmpl_var owner>"><tmpl_if owner_name><tmpl_var owner_name><tmpl_else><tmpl_var owner></tmpl_if></a>
-</tmpl_loop>
+<tr><td class="name"><a href="/ct/id">Contest name</a><td class="start">...<td class="stop">...<td class="owner"><a href="/us/id">Owner name</a>
</table>
-</tmpl_if>
+</div>
-<tmpl_if pending>
+<div id="pending">
<h1>Pending contests</h1>
<table border class="table table-bordered table-striped">
<thead>
<tr><th>Name<th>Start date<th>Stop date<th>Owner
+</thead>
+
<tbody>
-<tmpl_loop pending><tr><td><a href="/ct/<tmpl_var id>"><tmpl_var name></a>
-<td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{start}; %>
-<td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{stop}; %>
-<td><a href="/us/<tmpl_var owner>"><tmpl_if owner_name><tmpl_var owner_name><tmpl_else><tmpl_var owner></tmpl_if></a>
-</tmpl_loop>
+<tr><td class="name"><a href="/ct/id">Contest name</a><td class="start">...<td class="stop">...<td class="owner"><a href="/us/id">Owner name</a>
</table>
-</tmpl_if>
+</div>
-<tmpl_if finished>
+<div id="finished">
<h1>Finished contests</h1>
<table border class="table table-bordered table-striped">
<thead>
<tr><th>Name<th>Start date<th>Stop date<th>Owner
+</thead>
+
<tbody>
-<tmpl_loop finished><tr><td><a href="/ct/<tmpl_var id>"><tmpl_var name></a>
-<td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{start}; %>
-<td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{stop}; %>
-<td><a href="/us/<tmpl_var owner>"><tmpl_if owner_name><tmpl_var owner_name><tmpl_else><tmpl_var owner></tmpl_if></a>
-</tmpl_loop>
+<tr><td class="name"><a href="/ct/id">Contest name</a><td class="start">...<td class="stop">...<td class="owner"><a href="/us/id">Owner name</a>
</table>
-</tmpl_if>
+</div>
-Contest start time: <%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{start}; %><br>
-Contest stop time: <%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{stop}; %><p>
+<dl>
+<dt>Contest start time</dt> <dd id="start">start</dd>
+<dt>Contest stop time</dt> <dd id="stop">stop</dd>
+</dl>
-<tmpl_var ESCAPE=0 description>
+<div id="description">description</div>
-<tmpl_if started><a href="/pb/?contest=<tmpl_var id>">Problems</a><br>
-<a href="/log/?contest=<tmpl_var id>">Job log</a><br>
-<a href="/st/<tmpl_var id>">Standings</a></tmpl_if>
+<div id="links">
+<a href="/pb/?contest=contest_id">Problems</a><br>
+<a href="/log/?contest=contest_id">Job log</a><br>
+<a href="/st/contest_id">Standings</a>
+</div>
+++ /dev/null
-<footer>
-Dilmom: Why don't you call your product the Gruntmaster 6000?
-Dilbert: What kind of product do you see when you imagine a Gruntmaster 6000?
-Dilmom: Well, it's a stripped-down version of the Gruntmaster 9000, of course. But it's software-upgradeable.
-</footer>
+++ /dev/null
-<!DOCTYPE html>
-<title>TITLE_GOES_HERE</title>
-<meta charset="utf-8">
-<meta name="viewport" content="width=device-width, initial-scale=1.0">
-
-<link rel="stylesheet" href="/css/cyborg.css" id="stylesheet">
-<script src="/js.js" type="text/javascript"></script>
-
-<nav class="navbar navbar-default navbar-static-top" role="navigation">
-<div class="container-fluid">
-<div class="navbar-header">
-<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"> <span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button>
-<a class="navbar-brand" href="/">Gruntmaster 6000</a>
-</div>
-
-<div class="collapse navbar-collapse">
-<ul class="nav navbar-nav">
-<li><a href="/pb/">Problems</a>
-<li><a href="/ct/">Contests</a>
-<li><a href="/account">Account</a>
-</ul>
-
-<ul class="nav navbar-nav navbar-right">
-<li><a class="dropdown-toggle" data-toggle="dropdown"> Theme <span class="caret"></span></a>
-
-<ul class="dropdown-menu" role="menu">
-<li><a href="#" id="theme_slate">Gunmetal gray</a>
-<li><a href="#" id="theme_cyborg">Black</a>
-<li><a href="#" id="theme_cerulean">White</a>
-<li><a href="#" id="theme_cosmo">Metro</a>
-</ul>
-
-<li><a href="/log/">Job log</a>
-</ul>
-</div>
-</div>
-</nav>
-
-<iframe src="http://free.timeanddate.com/clock/i47sdccv/n49/tlro/fc99f/tct/pct/ta1" frameborder="0" id="clock" width="98" height="18" sandbox="allow-scripts allow-same-origin"></iframe>
-
-<div class="container-fluid">
-
-<h1 id="title">TITLE_GOES_HERE</h1>
-<div id="result"></div>
<table border class="table table-bordered table-striped">
-<thead>
-<tr><th>ID<th>Problem<th>Date<th>Size<th>User<th>Result
+<thead><tr><th>ID<th>Problem<th>Date<th>Size<th>User<th>Result</thead>
+
<tbody>
-<tmpl_loop log><tr><td><a href="/log/<tmpl_var id>"><tmpl_var id></a>
-<td><a href="/pb/<tmpl_var problem>"><tmpl_var problem_name></a>
-<td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{date}; %>
-<td><a href="/src/<tmpl_var id>.<tmpl_var extension>"<tmpl_if private> data-private</tmpl_if>><%perl __OUT__ sprintf '%.2fKB', __CURRENT__->{size}/1024; %></a>
-<td><a href="/us/<tmpl_var owner>"><tmpl_if owner_name><tmpl_var owner_name><tmpl_else><tmpl_var user></tmpl_if></a>
-<td class="r<tmpl_var result>"><tmpl_var result_text>
+<tr><td class="id"><a href="/log/id">Job ID</a>
+<td class="problem"><a href="/pb/id">Problem name</a>
+<td class="date">Date
+<td class="size"><a href="/src/id">3.14KB</a>
+<td class="owner"><a href="/us/id">Owner name</a>
+<td class="result_text"><tmpl_var result_text>
</tmpl_loop>
</table>
<ul class="pager">
-<%perl if (__CURRENT__->{page} > 1) { %><li class="previous"><a href="?page=<%perl __OUT__ __CURRENT__->{page} - 1; %>">Previous</a><%perl } %>
-<%perl if (__CURRENT__->{page} < __CURRENT__->{pages} - 1) { %><li class="next"><a href="?page=<%perl __OUT__ __CURRENT__->{page} + 1; %>">Next</a><%perl } %>
+<li class="previous"><a href="?page=id">Previous</a>
+<li class="current">Page 1 of 100
+<li class="next"><a href="?page=id">Next</a>
</ul>
-<tmpl_if errors>
+<div id="errors">
<h2>Compiler output</h2>
-<pre><tmpl_var errors></pre>
-</tmpl_if>
+<pre></pre>
+</div>
-<tmpl_if results>
+<div id="results">
<h2>Results</h2>
<table border class="table table-border table-striped">
<thead>
<tr><th>Test number<th>Result<th>Time
+</thead>
+
<tbody>
-<tmpl_loop results><tr><td><tmpl_var id><td class="r<tmpl_var result>"><tmpl_var result_text><td><%perl __OUT__ sprintf "%.4fs", __CURRENT__->{time}; %>
-</tmpl_loop>
+<tr><td class="id"><td class="result_text"><td class="time">
</table>
-</tmpl_if>
\ No newline at end of file
+</div>
\ No newline at end of file
-<tmpl_if beginner>
+<div>
+<div id="beginner">
<h2>Beginner</h2>
<table border class="table table-bordered table-striped table-fixed">
-<thead><tr><th>Name<th>Author<th>Owner
-<tbody>
-<tmpl_loop beginner><tr><td><a href="<tmpl_var id>"><tmpl_var name></a><td><tmpl_var author><td><a href="/us/<tmpl_var owner>"><tmpl_if owner_name><tmpl_var owner_name><tmpl_else><tmpl_var owner></tmpl_if></a>
-</tmpl_loop>
-</table>
-</tmpl_if>
-
-<tmpl_if easy>
-<h2>Easy</h2>
-<table border class="table table-bordered table-striped table-fixed">
-<thead><tr><th>Name<th>Author<th>Owner
-<tbody>
-<tmpl_loop easy><tr><td><a href="<tmpl_var id>"><tmpl_var name></a><td><tmpl_var author><td><a href="/us/<tmpl_var owner>"><tmpl_if owner_name><tmpl_var owner_name><tmpl_else><tmpl_var owner></tmpl_if></a>
-</tmpl_loop>
-</table>
-</tmpl_if>
+<thead><tr><th>Name<th>Author<th>Owner</thead>
-<tmpl_if medium>
-<h2>Medium</h2>
-<table border class="table table-bordered table-striped table-fixed">
-<thead><tr><th>Name<th>Author<th>Owner
-<tbody>
-<tmpl_loop medium><tr><td><a href="<tmpl_var id>"><tmpl_var name></a><td><tmpl_var author><td><a href="/us/<tmpl_var owner>"><tmpl_if owner_name><tmpl_var owner_name><tmpl_else><tmpl_var owner></tmpl_if></a>
-</tmpl_loop>
-</table>
-</tmpl_if>
-
-<tmpl_if hard>
-<h2>Hard</h2>
-<table border class="table table-bordered table-striped table-fixed">
-<thead><tr><th>Name<th>Author<th>Owner
<tbody>
-<tmpl_loop hard><tr><td><a href="<tmpl_var id>"><tmpl_var name></a><td><tmpl_var author><td><a href="/us/<tmpl_var owner>"><tmpl_if owner_name><tmpl_var owner_name><tmpl_else><tmpl_var owner></tmpl_if></a>
-</tmpl_loop>
+<tr><td class="name"><a href="id">Name</a><td class="author">author<td class="owner"><a href="/us/id">Owner name</a>
</table>
-</tmpl_if>
+</div>
+</div>
\ No newline at end of file
<div class="row">
<div class="col-md-9">
-<tmpl_var ESCAPE=0 statement>
+<div id="statement"></div>
</div>
<div class="col-md-3">
<dl>
-<dt>Author</dt> <dd><tmpl_var author></dd>
-<dt>Owner</dt> <dd><a href="/us/<tmpl_var owner>"><tmpl_if owner_name><tmpl_var owner_name><tmpl_else><tmpl_var owner></tmpl_if></a></dd>
+<dt>Author</dt> <dd id="author">author</dd>
+<dt>Owner</dt> <dd id="owner">owner</dd>
</dl>
-<a href="/pb/<tmpl_var id>/log/">Job log</a>
+<a href="/pb/problem_id/log/" id="job_log">Job log</a>
-<tmpl_if cansubmit>
+<div id="submit">
<h1>Submit solution</h1>
-<form action="<tmpl_var id>/submit" method="POST" enctype="multipart/form-data" role="form">
-<input type="hidden" name="problem" value="<tmpl_var id>">
-<tmpl_if_defined contest><input type="hidden" name="contest" value="<tmpl_var contest>"></tmpl_if_defined>
+<form action="/action/submit" method="POST" enctype="multipart/form-data" role="form">
+<input type="hidden" name="problem" value="problem_id">
+<input type="hidden" name="contest" value="contest_id">
<div class="form-group"><label for="prog">File:</label><input id="prog" name="prog" type="file"></div>
<div class="form-group"><label for="source_code">Source code:</label> <textarea class="form-control" id="source_code" name="source_code"></textarea></div>
<div class="form-group"><label for="prog_format">File format:</label><select id="prog_format" name="prog_format" class="form-control" required>
-<option value="C">C</option>
-<option value="CPP">C++</option>
-<option value="JAVA">Java</option>
-<option value="PASCAL">Pascal</option>
-<option value="PERL">Perl</option>
-<option value="PYTHON">Python</option>
+<option value="C">C (gcc)</option>
+<option value="CPP">C++ (g++)</option>
+<option value="GCCGO">Go (gccgo)</option>
+<option value="GOLANG">Go (gc)</option>
+<option value="HASKELL">Haskell (ghc)</option>
+<option value="JAVA">Java (javac)</option>
+<option value="PASCAL">Pascal (fpc)</option>
+<option value="PERL">Perl (perl)</option>
+<option value="PYTHON">Python (python)</option>
</select></div>
<input type="submit" value="Submit job" class="btn btn-primary">
</form>
-</tmpl_if>
+</div>
</div>
</div>
--- /dev/null
+<!DOCTYPE html>
+<title smap="title">TITLE</title>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+<link rel="stylesheet" href="/css/cyborg.css" id="stylesheet">
+<script src="/js.js" type="text/javascript"></script>
+
+<body>
+<nav class="navbar navbar-default navbar-static-top" role="navigation">
+<div class="container-fluid">
+<div class="navbar-header">
+<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"> <span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button>
+<a class="navbar-brand" href="/">Gruntmaster 6000</a>
+</div>
+
+<div class="collapse navbar-collapse">
+<ul class="nav navbar-nav">
+<li><a href="/pb/">Problems</a>
+<li><a href="/ct/">Contests</a>
+<li><a href="/account">Account</a>
+</ul>
+
+<ul class="nav navbar-nav navbar-right">
+<li><a class="dropdown-toggle" data-toggle="dropdown"> Theme <span class="caret"></span></a>
+
+<ul class="dropdown-menu" role="menu">
+<li><a href="#" id="theme_slate">Gunmetal gray</a>
+<li><a href="#" id="theme_cyborg">Black</a>
+<li><a href="#" id="theme_cerulean">White</a>
+<li><a href="#" id="theme_cosmo">Metro</a>
+</ul>
+
+<li><a href="/log/">Job log</a>
+</ul>
+</div>
+</div>
+</nav>
+
+<iframe src="http://free.timeanddate.com/clock/i47sdccv/n49/tlro/fc99f/tct/pct/ta1" frameborder="0" id="clock" width="98" height="18" sandbox="allow-scripts allow-same-origin"></iframe>
+
+<div class="container-fluid">
+
+<h1 id="title">TITLE</h1>
+<div id="result"></div>
+
+<div id="content">Content goes here</div>
+
+<footer>
+Dilmom: Why don't you call your product the Gruntmaster 6000?<br>
+Dilbert: What kind of product do you see when you imagine a Gruntmaster 6000?<br>
+Dilmom: Well, it's a stripped-down version of the Gruntmaster 9000, of course. But it's software-upgradeable.
+</footer>
<table border class="table table-border table-striped">
<thead>
-<tmpl_if problems><tr><th>Rank<th>User<tmpl_loop problems><th><a href="/pb/<tmpl_var _.id>"><tmpl_var _.name></a></tmpl_loop><th>Total
-<tmpl_else><tr><th>Rank<th>User<th>Score
-</tmpl_if>
+<tr><th>Rank<th>User<th class="problem"><a href="/pb/id">Problem name</a><th>Score
+</thead>
<tbody>
-<tmpl_loop st><tr><td><tmpl_var rank><td><a href="/us/<tmpl_var user.id>"><tmpl_if user.name><tmpl_var user.name><tmpl_else><tmpl_var user.id></tmpl_if></a>
-<tmpl_if problems><tmpl_loop scores><td><tmpl_var _>
-</tmpl_loop></tmpl_if><td><tmpl_var score>
-</tmpl_loop>
+<tr><td class="rank">1<td class="user"><a href="/us/id">User noame</tmpl_if></a><td class="pbscore">100<td class="score">100
</table>
-<div class="list-group"><tmpl_loop us><a class="list-group-item" href="/us/<tmpl_var id>"><tmpl_var name></a>
-</tmpl_loop><div>
+<div class="list-group">
+<a class="list-group-item" href="/us/id">Name</a>
+<div>
<dl>
-<dt>Town</dt> <dd><tmpl_var town></dd>
-<dt>University</dt> <dd><tmpl_var university></dd>
-<dt>Level</dt> <dd><tmpl_var level></dd>
+<dt>Town</dt> <dd smap="town">Town</dd>
+<dt>University</dt> <dd smap="university">University</dd>
+<dt>Level</dt> <dd smap="level">Level</dd>
</dl>
-<a href="/log/?owner=<tmpl_var id>">Job log</a><br>
-<a href="/pb/?owner=<tmpl_var id>">Owned problems</a>
+<a href="/log/?owner=id" id="log">Job log</a><br>
+<a href="/pb/?owner=id" id="pb">Owned problems</a>