From f6f5309a02d8e472ee27098f4f97156c68f3b1dc Mon Sep 17 00:00:00 2001 From: Eelco Dolstra <e.dolstra@tudelft.nl> Date: Thu, 13 Nov 2008 14:54:50 +0000 Subject: [PATCH] --- .../lib/HydraFrontend/Controller/Root.pm | 78 ++++++++++++++- src/HydraFrontend/root/error.tt | 3 +- src/HydraFrontend/root/hydra.css | 15 +++ src/HydraFrontend/root/job.tt | 2 +- src/HydraFrontend/root/layout.tt | 3 +- src/HydraFrontend/root/project.tt | 99 +++++++++++++++++-- src/hydra.sql | 2 +- src/test.sql | 2 +- 8 files changed, 185 insertions(+), 19 deletions(-) diff --git a/src/HydraFrontend/lib/HydraFrontend/Controller/Root.pm b/src/HydraFrontend/lib/HydraFrontend/Controller/Root.pm index 925637f5..14356877 100644 --- a/src/HydraFrontend/lib/HydraFrontend/Controller/Root.pm +++ b/src/HydraFrontend/lib/HydraFrontend/Controller/Root.pm @@ -49,13 +49,51 @@ sub index :Path :Args(0) { } +sub updateProject { + my ($c, $project) = @_; + + my $projectName = $c->request->params->{name}; + die "Invalid project name: $projectName" unless $projectName =~ /^[[:alpha:]]\w*$/; + + my $displayName = $c->request->params->{displayname}; + die "Invalid display name: $displayName" unless $displayName =~ /^\w.*\w$/; + + $project->name($projectName); + $project->displayname($displayName); + $project->description($c->request->params->{description}); + + $project->update; +} + + sub project :Local { - my ( $self, $c, $projectName ) = @_; + my ( $self, $c, $projectName, $subcommand ) = @_; $c->stash->{template} = 'project.tt'; (my $project) = $c->model('DB::Projects')->search({ name => $projectName }); return error($c, "Project <tt>$projectName</tt> doesn't exist.") if !defined $project; - + + my $isPosted = $c->request->method eq "POST"; + + $subcommand = "" unless defined $subcommand; + + if ($subcommand eq "edit") { + $c->stash->{edit} = 1; + } elsif ($subcommand eq "submit" && $isPosted) { + $c->model('DB')->schema->txn_do(sub { + updateProject($c, $project); + }); + return $c->res->redirect($c->uri_for("/project", $projectName)); + } elsif ($subcommand eq "delete" && $isPosted) { + $c->model('DB')->schema->txn_do(sub { + $project->delete; + }); + return $c->res->redirect($c->uri_for("/")); + } elsif ($subcommand eq "") { + } else { + return error($c, "Unknown subcommand $subcommand."); + } + $c->stash->{curProject} = $project; $c->stash->{finishedBuilds} = $c->model('DB::Builds')->search( @@ -83,13 +121,43 @@ sub project :Local { } +sub createproject :Local { + my ( $self, $c, $subcommand ) = @_; + + if (defined $subcommand && $subcommand eq "submit") { + eval { + my $projectName = $c->request->params->{name}; + $c->model('DB')->schema->txn_do(sub { + # Note: $projectName is validated in updateProject, + # which will abort the transaction if the name isn't + # valid. + my $project = $c->model('DB::Projects')->create({name => $projectName, displayname => ""}); + updateProject($c, $project); + }); + return $c->res->redirect($c->uri_for("/project", $projectName)); + }; + if ($@) { + return error($c, $@); + } + } + + $c->stash->{template} = 'project.tt'; + $c->stash->{create} = 1; + $c->stash->{edit} = 1; +} + + sub job :Local { - my ( $self, $c, $project, $jobName ) = @_; + my ( $self, $c, $projectName, $jobName ) = @_; $c->stash->{template} = 'job.tt'; - $c->stash->{projectName} = $project; + + (my $project) = $c->model('DB::Projects')->search({ name => $projectName }); + return error($c, "Project <tt>$projectName</tt> doesn't exist.") if !defined $project; + $c->stash->{curProject} = $project; + $c->stash->{jobName} = $jobName; $c->stash->{builds} = [$c->model('DB::Builds')->search( - {finished => 1, project => $project, attrName => $jobName}, + {finished => 1, project => $projectName, attrName => $jobName}, {order_by => "timestamp DESC"})]; } diff --git a/src/HydraFrontend/root/error.tt b/src/HydraFrontend/root/error.tt index 2d8e61ab..01f7ac95 100644 --- a/src/HydraFrontend/root/error.tt +++ b/src/HydraFrontend/root/error.tt @@ -1,7 +1,8 @@ [% WRAPPER layout.tt title="Hydra Overview" %] +[% USE HTML %] <h1>Error</h1> -<p>I'm very sorry, but an error occurred: <span class="error-msg">[% error %]</span></p> +<p>I'm very sorry, but an error occurred: <span class="error-msg">[% HTML.escape(error) %]</span></p> [% END %] diff --git a/src/HydraFrontend/root/hydra.css b/src/HydraFrontend/root/hydra.css index 5059109b..2f3f5d89 100644 --- a/src/HydraFrontend/root/hydra.css +++ b/src/HydraFrontend/root/hydra.css @@ -38,6 +38,10 @@ td, th { border: solid black 1px; } +td { + vertical-align: top; +} + th { background: #ffffc0; } @@ -145,6 +149,7 @@ td.buildfarmMainColumn { .error-msg { color: red; + white-space: pre; } pre.buildlog { @@ -321,3 +326,13 @@ h1 { #footer { font-size: 80%; } + + +/* Editing */ + +input.string { + font-family: sans-serif; + font-size: 100%; + background-color: #fffff0; + width: 30em; +} diff --git a/src/HydraFrontend/root/job.tt b/src/HydraFrontend/root/job.tt index 62d6aa8b..293b5442 100644 --- a/src/HydraFrontend/root/job.tt +++ b/src/HydraFrontend/root/job.tt @@ -1,6 +1,6 @@ [% WRAPPER layout.tt title="Hydra Overview" %] -<h1>All builds for job <tt>[% projectName %]:[% jobName %]</tt></h1> +<h1>All builds for job <tt>[% curProject.name %]:[% jobName %]</tt></h1> <table class="tablesorter"> <thead> diff --git a/src/HydraFrontend/root/layout.tt b/src/HydraFrontend/root/layout.tt index 064551fa..ace946b0 100644 --- a/src/HydraFrontend/root/layout.tt +++ b/src/HydraFrontend/root/layout.tt @@ -50,6 +50,7 @@ <div class="title"><a href="[% c.uri_for('/project' project.name) %]">[% project.displayname %]</a></div> [% IF curProject.name == project.name %] <ul class="subsubmenu"> + [% INCLUDE makeLink uri = c.uri_for('/project' project.name 'edit') title = "Edit" %] [% INCLUDE makeLink uri = c.uri_for('/project' project.name 'status') title = "Status" %] [% INCLUDE makeLink uri = c.uri_for('/project' project.name 'all') title = "All builds" %] </ul> @@ -63,7 +64,7 @@ <div class="title">Admin</div> <ul class="submenu"> [% INCLUDE makeLink uri = c.uri_for('/users') title = "Users" %] - [% INCLUDE makeLink uri = c.uri_for('/create-project') title = "Create a project" %] + [% INCLUDE makeLink uri = c.uri_for('/createproject') title = "Create a project" %] </ul> </li> </ul> diff --git a/src/HydraFrontend/root/project.tt b/src/HydraFrontend/root/project.tt index 227c28ad..38b4b3c5 100644 --- a/src/HydraFrontend/root/project.tt +++ b/src/HydraFrontend/root/project.tt @@ -1,9 +1,46 @@ [% WRAPPER layout.tt title="Hydra Overview" %] - -<h1>Project <tt>[% curProject.name %]</tt></h1> +[% USE HTML %] -<p><strong>Description:</strong> [% curProject.description %]</p> +[% BLOCK maybeEditString %] + [% IF edit %] + <input type='text' class='string' [% HTML.attributes(name => param, value => value) %] /> + [% ELSE %] + [% HTML.escape(value) %] + [% END %] +[% END %] + + +[% IF edit %] + <form action="[% IF create %][% c.uri_for('/createproject/submit') %][% ELSE %][% c.uri_for('/project' curProject.name 'submit') %][% END %]" method="post"> +[% END %] + + +[% IF create %] + <h1>New Project</h1> +[% ELSE %] + <h1>Project <tt>[% curProject.name %]</tt></h1> +[% END %] + + +<h2>General information</h2> + +<table> + [% IF edit %] + <tr> + <th>Identifier:</th> + <td><tt>[% INCLUDE maybeEditString param="name" value=curProject.name %]</tt></td> + </tr> + [% END %] + <tr> + <th>Display name:</th> + <td>[% INCLUDE maybeEditString param="displayname" value=curProject.displayname %]</td> + </tr> + <tr> + <th>Description:</th> + <td>[% INCLUDE maybeEditString param="description" value=curProject.description %]</td> + </tr> +</table> <h2>Definition</h2> @@ -17,13 +54,19 @@ <h4>Information</h4> <table> + [% IF edit %] + <tr> + <th>Identifier:</th> + <td><tt>[% INCLUDE maybeEditString value=jobset.name %]</tt></td> + </tr> + [% END %] <tr> <th>Description:</th> - <td>[% jobset.description %]</td> + <td>[% INCLUDE maybeEditString value=jobset.description %]</td> </tr> <tr> <th>Nix expression:</th> - <td><tt>[% jobset.nixexprpath %]</tt> in input <tt>[% jobset.nixexprinput %]</tt></td> + <td><tt>[% INCLUDE maybeEditString value=jobset.nixexprpath %]</tt> in input <tt>[% INCLUDE maybeEditString value=jobset.nixexprinput %]</tt></td> </tr> </table> @@ -36,14 +79,33 @@ <tbody> [% FOREACH input IN jobset.jobsetinputs -%] <tr> - <td><tt>[% input.name %]</tt></td> - <td><tt>[% input.type %]</tt></td> + <td><tt>[% INCLUDE maybeEditString value=input.name %]</tt></td> + <td><tt> + [% IF edit %] + <select> + <option>svn</option> + <option>cvs</option> + <option>uri</option> + <option>string</option> + <option>path</option> + </select> + [% ELSE %] + [% input.type %] + [% END %] + </tt></td> <td> [% FOREACH alt IN input.jobsetinputalts -%] [% IF input.type == "string" %] - <tt>"[% alt.value %]"</tt> + <tt> + [% IF edit %] + <input type='text' class='string' value='[% alt.value %]' /> + <br /> + [% ELSE %] + "[% alt.value %]" + [% END %] + </tt> [% ELSE %] - <tt>[% alt.uri %]</tt> + <tt>[% INCLUDE maybeEditString value=alt.uri %]</tt> [% END %] [% END %] </td> @@ -61,6 +123,9 @@ [% END %] +[% IF !edit %] + + <h2>Jobs</h2> [% IF jobNames.size > 0 %] @@ -108,4 +173,20 @@ </table> +[% END %] + + +[% IF edit %] + + <p><input type="submit" value="Apply" /></p> + + </form> + + <form action="[% c.uri_for('/project' curProject.name 'delete') %]" method="post"> + <p><input type="submit" value="Delete this project" /></p> + </form> + +[% END %] + + [% END %] diff --git a/src/hydra.sql b/src/hydra.sql index 8ddd8725..ff0f14a7 100644 --- a/src/hydra.sql +++ b/src/hydra.sql @@ -174,7 +174,7 @@ create table JobsetInputs ( project text not null, jobset text not null, name text not null, - type text not null, -- "svn", "cvs", "path", "file", "string" + type text not null, -- "svn", "cvs", "path", "uri", "string" primary key (project, jobset, name), foreign key (project, jobset) references Jobsets(project, name) on delete cascade -- ignored by sqlite ); diff --git a/src/test.sql b/src/test.sql index ebf4f31f..8c7ad093 100644 --- a/src/test.sql +++ b/src/test.sql @@ -1,5 +1,5 @@ insert into projects(name, displayName, description) values('patchelf', 'PatchELF', 'A tool for modifying ELF binaries'); -insert into jobSets(project, name, description, nixExprInput, nixExprPath) values('patchelf', 'trunk', 'PatchELF', 'patchelfSrc', 'release.nix'); +insert into jobSets(project, name, description, nixExprInput, nixExprPath) values('patchelf', 'trunk', 'PatchELF trunk', 'patchelfSrc', 'release.nix'); insert into jobSetInputs(project, jobset, name, type) values('patchelf', 'trunk', 'patchelfSrc', 'path'); insert into jobSetInputAlts(project, jobset, input, altnr, uri) values('patchelf', 'trunk', 'patchelfSrc', 0, '/home/eelco/Dev/patchelf-wc'); insert into jobSetInputs(project, jobset, name, type) values('patchelf', 'trunk', 'nixpkgs', 'path');