forked from lix-project/hydra
added newsitems, added some admin options to clear various caches.
This commit is contained in:
parent
e18fe1078a
commit
7a79d17a36
92
src/lib/Hydra/Controller/Admin.pm
Normal file
92
src/lib/Hydra/Controller/Admin.pm
Normal file
|
@ -0,0 +1,92 @@
|
|||
package Hydra::Controller::Admin;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use base 'Catalyst::Controller';
|
||||
use Hydra::Helper::Nix;
|
||||
use Hydra::Helper::CatalystUtils;
|
||||
|
||||
sub admin : Path('/admin') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
requireAdmin($c);
|
||||
|
||||
$c->stash->{template} = 'admin.tt';
|
||||
}
|
||||
|
||||
sub clearfailedcache : Path('/admin/clear-failed-cache') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
requireAdmin($c);
|
||||
|
||||
my $r = `nix-store --clear-failed-paths '*'`;
|
||||
|
||||
$c->res->redirect("/admin");
|
||||
}
|
||||
|
||||
sub clearevalcache : Path('/admin/clear-eval-cache') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
requireAdmin($c);
|
||||
|
||||
print "Clearing evaluation cache\n";
|
||||
$c->model('DB::JobsetInputHashes')->delete_all;
|
||||
|
||||
$c->res->redirect("/admin");
|
||||
}
|
||||
|
||||
sub clearvcscache : Path('/admin/clear-vcs-cache') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
requireAdmin($c);
|
||||
|
||||
print "Clearing path cache\n";
|
||||
$c->model('DB::CachedPathInputs')->delete_all;
|
||||
|
||||
print "Clearing git cache\n";
|
||||
$c->model('DB::CachedGitInputs')->delete_all;
|
||||
|
||||
print "Clearing subversion cache\n";
|
||||
$c->model('DB::CachedSubversionInputs')->delete_all;
|
||||
|
||||
$c->res->redirect("/admin");
|
||||
}
|
||||
|
||||
sub managenews : Path('/admin/news') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
requireAdmin($c);
|
||||
|
||||
$c->stash->{newsItems} = [$c->model('DB::NewsItems')->search({}, {order_by => 'createtime DESC'})];
|
||||
|
||||
$c->stash->{template} = 'news.tt';
|
||||
}
|
||||
|
||||
sub news_submit : Path('/admin/news/submit') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
requireAdmin($c);
|
||||
requirePost($c);
|
||||
|
||||
my $contents = trim $c->request->params->{"contents"};
|
||||
my $createtime = time;
|
||||
|
||||
$c->model('DB::NewsItems')->create({
|
||||
createtime => $createtime,
|
||||
contents => $contents,
|
||||
author => $c->user->username
|
||||
});
|
||||
|
||||
$c->res->redirect("/admin/news");
|
||||
}
|
||||
|
||||
sub news_delete : Path('/admin/news/delete') Args(1) {
|
||||
my ($self, $c, $id) = @_;
|
||||
|
||||
requireAdmin($c);
|
||||
|
||||
txn_do($c->model('DB')->schema, sub {
|
||||
my $newsItem = $c->model('DB::NewsItems')->find($id)
|
||||
or notFound($c, "Newsitem with id $id doesn't exist.");
|
||||
$newsItem->delete;
|
||||
});
|
||||
|
||||
$c->res->redirect("/admin/news");
|
||||
}
|
||||
|
||||
1;
|
|
@ -27,6 +27,7 @@ sub index :Path :Args(0) {
|
|||
my ($self, $c) = @_;
|
||||
$c->stash->{template} = 'overview.tt';
|
||||
$c->stash->{projects} = [$c->model('DB::Projects')->search({}, {order_by => 'name'})];
|
||||
$c->stash->{newsItems} = [$c->model('DB::NewsItems')->search({}, { order_by => ['createtime DESC'], rows => 5 })];
|
||||
}
|
||||
|
||||
|
||||
|
|
112
src/lib/Hydra/Schema/NewsItems.pm
Normal file
112
src/lib/Hydra/Schema/NewsItems.pm
Normal file
|
@ -0,0 +1,112 @@
|
|||
package Hydra::Schema::NewsItems;
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader
|
||||
# DO NOT MODIFY THE FIRST PART OF THIS FILE
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class::Core';
|
||||
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Hydra::Schema::NewsItems
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->table("NewsItems");
|
||||
|
||||
=head1 ACCESSORS
|
||||
|
||||
=head2 id
|
||||
|
||||
data_type: integer
|
||||
default_value: undef
|
||||
is_auto_increment: 1
|
||||
is_nullable: 0
|
||||
size: undef
|
||||
|
||||
=head2 contents
|
||||
|
||||
data_type: text
|
||||
default_value: undef
|
||||
is_nullable: 0
|
||||
size: undef
|
||||
|
||||
=head2 createtime
|
||||
|
||||
data_type: integer
|
||||
default_value: undef
|
||||
is_nullable: 0
|
||||
size: undef
|
||||
|
||||
=head2 author
|
||||
|
||||
data_type: text
|
||||
default_value: undef
|
||||
is_foreign_key: 1
|
||||
is_nullable: 0
|
||||
size: undef
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
"id",
|
||||
{
|
||||
data_type => "integer",
|
||||
default_value => undef,
|
||||
is_auto_increment => 1,
|
||||
is_nullable => 0,
|
||||
size => undef,
|
||||
},
|
||||
"contents",
|
||||
{
|
||||
data_type => "text",
|
||||
default_value => undef,
|
||||
is_nullable => 0,
|
||||
size => undef,
|
||||
},
|
||||
"createtime",
|
||||
{
|
||||
data_type => "integer",
|
||||
default_value => undef,
|
||||
is_nullable => 0,
|
||||
size => undef,
|
||||
},
|
||||
"author",
|
||||
{
|
||||
data_type => "text",
|
||||
default_value => undef,
|
||||
is_foreign_key => 1,
|
||||
is_nullable => 0,
|
||||
size => undef,
|
||||
},
|
||||
);
|
||||
__PACKAGE__->set_primary_key("id");
|
||||
|
||||
=head1 RELATIONS
|
||||
|
||||
=head2 author
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Hydra::Schema::Users>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to("author", "Hydra::Schema::Users", { username => "author" }, {});
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.05000 @ 2010-04-27 15:13:51
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:SX13YZYhf5Uz5KZGphG/+w
|
||||
|
||||
use Hydra::Helper::Nix;
|
||||
|
||||
# !!! Ugly, should be generated.
|
||||
my $hydradbi = getHydraDBPath;
|
||||
if ($hydradbi =~ m/^dbi:Pg/) {
|
||||
__PACKAGE__->sequence('newsitems_id_seq');
|
||||
}
|
||||
|
||||
1;
|
|
@ -118,6 +118,20 @@ Related object: L<Hydra::Schema::Users>
|
|||
|
||||
__PACKAGE__->belongs_to("owner", "Hydra::Schema::Users", { username => "owner" }, {});
|
||||
|
||||
=head2 projectmembers
|
||||
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Hydra::Schema::ProjectMembers>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"projectmembers",
|
||||
"Hydra::Schema::ProjectMembers",
|
||||
{ "foreign.project" => "self.name" },
|
||||
);
|
||||
|
||||
=head2 jobsets
|
||||
|
||||
Type: has_many
|
||||
|
@ -231,7 +245,15 @@ __PACKAGE__->has_many(
|
|||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.05000 @ 2010-03-05 13:07:45
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:SXJ+FzgNDad87OKSBH2qrg
|
||||
# Created by DBIx::Class::Schema::Loader v0.05000 @ 2010-04-20 11:21:42
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1VZpwwaEdEJzrrV31ErPzw
|
||||
# These lines were loaded from '/home/rbvermaa/src/hydra/src/lib/Hydra/Schema/Projects.pm' found in @INC.
|
||||
# They are now part of the custom portion of this file
|
||||
# for you to hand-edit. If you do not either delete
|
||||
# this section or remove that file from @INC, this section
|
||||
# will be repeated redundantly when you re-create this
|
||||
# file again via Loader! See skip_load_external to disable
|
||||
# this feature.
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
1;
|
||||
|
|
|
@ -120,8 +120,180 @@ __PACKAGE__->has_many(
|
|||
{ "foreign.owner" => "self.username" },
|
||||
);
|
||||
|
||||
=head2 projectmembers
|
||||
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Hydra::Schema::ProjectMembers>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"projectmembers",
|
||||
"Hydra::Schema::ProjectMembers",
|
||||
{ "foreign.username" => "self.username" },
|
||||
);
|
||||
|
||||
=head2 newsitems
|
||||
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Hydra::Schema::NewsItems>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"newsitems",
|
||||
"Hydra::Schema::NewsItems",
|
||||
{ "foreign.author" => "self.username" },
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.05000 @ 2010-04-27 15:13:51
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:C5uoz6EYyYL442zRYmXkyw
|
||||
# These lines were loaded from '/home/rbvermaa/src/hydra/src/lib/Hydra/Schema/Users.pm' found in @INC.
|
||||
# They are now part of the custom portion of this file
|
||||
# for you to hand-edit. If you do not either delete
|
||||
# this section or remove that file from @INC, this section
|
||||
# will be repeated redundantly when you re-create this
|
||||
# file again via Loader! See skip_load_external to disable
|
||||
# this feature.
|
||||
|
||||
package Hydra::Schema::Users;
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader
|
||||
# DO NOT MODIFY THE FIRST PART OF THIS FILE
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class::Core';
|
||||
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Hydra::Schema::Users
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->table("Users");
|
||||
|
||||
=head1 ACCESSORS
|
||||
|
||||
=head2 username
|
||||
|
||||
data_type: text
|
||||
default_value: undef
|
||||
is_nullable: 0
|
||||
size: undef
|
||||
|
||||
=head2 fullname
|
||||
|
||||
data_type: text
|
||||
default_value: undef
|
||||
is_nullable: 1
|
||||
size: undef
|
||||
|
||||
=head2 emailaddress
|
||||
|
||||
data_type: text
|
||||
default_value: undef
|
||||
is_nullable: 0
|
||||
size: undef
|
||||
|
||||
=head2 password
|
||||
|
||||
data_type: text
|
||||
default_value: undef
|
||||
is_nullable: 0
|
||||
size: undef
|
||||
|
||||
=head2 emailonerror
|
||||
|
||||
data_type: integer
|
||||
default_value: 0
|
||||
is_nullable: 0
|
||||
size: undef
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
"username",
|
||||
{
|
||||
data_type => "text",
|
||||
default_value => undef,
|
||||
is_nullable => 0,
|
||||
size => undef,
|
||||
},
|
||||
"fullname",
|
||||
{
|
||||
data_type => "text",
|
||||
default_value => undef,
|
||||
is_nullable => 1,
|
||||
size => undef,
|
||||
},
|
||||
"emailaddress",
|
||||
{
|
||||
data_type => "text",
|
||||
default_value => undef,
|
||||
is_nullable => 0,
|
||||
size => undef,
|
||||
},
|
||||
"password",
|
||||
{
|
||||
data_type => "text",
|
||||
default_value => undef,
|
||||
is_nullable => 0,
|
||||
size => undef,
|
||||
},
|
||||
"emailonerror",
|
||||
{ data_type => "integer", default_value => 0, is_nullable => 0, size => undef },
|
||||
);
|
||||
__PACKAGE__->set_primary_key("username");
|
||||
|
||||
=head1 RELATIONS
|
||||
|
||||
=head2 userroles
|
||||
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Hydra::Schema::UserRoles>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"userroles",
|
||||
"Hydra::Schema::UserRoles",
|
||||
{ "foreign.username" => "self.username" },
|
||||
);
|
||||
|
||||
=head2 projects
|
||||
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Hydra::Schema::Projects>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"projects",
|
||||
"Hydra::Schema::Projects",
|
||||
{ "foreign.owner" => "self.username" },
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.05003 @ 2010-02-25 10:29:41
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:vHluB+s1FkpJBPWmpv+wUQ
|
||||
|
||||
1;
|
||||
# End of lines loaded from '/home/rbvermaa/src/hydra/src/lib/Hydra/Schema/Users.pm'
|
||||
# These lines were loaded from '/home/rbvermaa/src/hydra/src/lib/Hydra/Schema/Users.pm' found in @INC.
|
||||
# They are now part of the custom portion of this file
|
||||
# for you to hand-edit. If you do not either delete
|
||||
# this section or remove that file from @INC, this section
|
||||
# will be repeated redundantly when you re-create this
|
||||
# file again via Loader! See skip_load_external to disable
|
||||
# this feature.
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
1;
|
||||
|
|
19
src/root/admin.tt
Normal file
19
src/root/admin.tt
Normal file
|
@ -0,0 +1,19 @@
|
|||
[% WRAPPER layout.tt title="Admin" %]
|
||||
[% PROCESS common.tt %]
|
||||
|
||||
<h1>Admin</h1>
|
||||
|
||||
<ul>
|
||||
<li>[% INCLUDE maybeLink uri = c.uri_for(c.controller('Project').action_for('create')) content = "Create project" %]</li>
|
||||
<li>Caching
|
||||
<ul>
|
||||
<li>[% INCLUDE maybeLink uri = c.uri_for(c.controller('Admin').action_for('clearfailedcache')) content = "Clear failed builds cache" confirmmsg = "Are you sure you want to clear the failed builds cache?" %]</li>
|
||||
<li>[% INCLUDE maybeLink uri = c.uri_for(c.controller('Admin').action_for('clearevalcache')) content = "Clear evaluation cache" confirmmsg = "Are you sure you want to clear the evaluation cache?" %]</li>
|
||||
<li>[% INCLUDE maybeLink uri = c.uri_for(c.controller('Admin').action_for('clearvcscache')) content = "Clear VCS caches" confirmmsg = "Are you sure you want to clear the VCS caches?" %]</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>[% INCLUDE maybeLink uri = c.uri_for(c.controller('Admin').action_for('managenews')) content = "News" %]</li>
|
||||
</ul>
|
||||
|
||||
|
||||
[% END %]
|
|
@ -174,7 +174,7 @@
|
|||
|
||||
|
||||
[% BLOCK maybeLink -%]
|
||||
[% IF uri %]<a [% HTML.attributes(href => uri) %]>[% content %]</a>[% ELSE; content; END -%]
|
||||
[% IF uri %]<a [% HTML.attributes(href => uri) %][% IF confirmmsg %]onclick="javascript:return confirm('[% confirmmsg %]')"[% END %]>[% content %]</a>[% ELSE; content; END -%]
|
||||
[% END -%]
|
||||
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="last-modified">
|
||||
<em>Hydra [% HTML.escape(version) %] (using [% HTML.escape(nixVersion) %]).</em>
|
||||
<em><a href="http://nixos.org/hydra" target="_new">Hydra</a> [% HTML.escape(version) %] (using [% HTML.escape(nixVersion) %]).</em>
|
||||
Page generated on [% INCLUDE renderDateTime %].
|
||||
[% IF c.user_exists %]
|
||||
You are logged in as <tt>[% c.user.username %]</tt>.
|
||||
|
|
39
src/root/news.tt
Normal file
39
src/root/news.tt
Normal file
|
@ -0,0 +1,39 @@
|
|||
[% WRAPPER layout.tt title="News items" %]
|
||||
[% PROCESS common.tt %]
|
||||
|
||||
[% USE String %]
|
||||
<h1>News items</h1>
|
||||
|
||||
[% IF newsItems.size == 0 %]
|
||||
<p>No news items</p>
|
||||
[% ELSE %]
|
||||
<table>
|
||||
<thead><th>Date</th><th>Contents</th><th></th></thead>
|
||||
<tbody>
|
||||
[% FOREACH i IN newsItems %]
|
||||
[% contents = String.new(i.contents) %]
|
||||
<tr>
|
||||
<td>[% INCLUDE renderDateTime timestamp=i.createtime %]</td>
|
||||
<td>[% contents.replace('\n','<br />\n') %]</td>
|
||||
<td>[ [% INCLUDE maybeLink uri = c.uri_for(c.controller('Admin').action_for('news_delete') i.id) content = "Delete" confirmmsg = "Are you sure you want to delete this news item?" %] ]</td>
|
||||
</tr>
|
||||
[% END %]
|
||||
</tbody>
|
||||
</table>
|
||||
[% END %]
|
||||
|
||||
<form action="[% c.uri_for('/admin/news/submit') %]" method="post">
|
||||
|
||||
<h2>Add news item</h2>
|
||||
<p>
|
||||
<textarea class="longString" name="contents"></textarea>
|
||||
</p>
|
||||
<p>
|
||||
<button type="submit">Post</button>
|
||||
</p>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
[% END %]
|
|
@ -1,6 +1,15 @@
|
|||
[% WRAPPER layout.tt title="Overview" %]
|
||||
[% PROCESS common.tt %]
|
||||
|
||||
[% IF newItems.size != 0 %]
|
||||
<div class="newsbar">
|
||||
[% FOREACH i IN newsItems %]
|
||||
[% contents = String.new(i.contents) %]
|
||||
<p><b>[% INCLUDE renderDateTime timestamp=i.createtime %]</b> <tt>by [% i.author.fullname %]</tt> <br/>[% contents.replace('\n','<br />\n') %]
|
||||
[% END %]
|
||||
</div>
|
||||
[% END %]
|
||||
|
||||
<h2>Projects</h2>
|
||||
|
||||
<p>The following projects are hosted on this server:</p>
|
||||
|
@ -33,5 +42,4 @@
|
|||
</p>
|
||||
[% END %]
|
||||
|
||||
|
||||
[% END %]
|
||||
|
|
|
@ -187,6 +187,10 @@ input.longString {
|
|||
width: 40em;
|
||||
}
|
||||
|
||||
textarea.longString {
|
||||
width: 40em;
|
||||
}
|
||||
|
||||
select {
|
||||
background-color: #fffff0;
|
||||
}
|
||||
|
@ -208,3 +212,14 @@ form.inline {
|
|||
.green {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.newsbar {
|
||||
background-color:#D9E3EA;
|
||||
border:1px solid #999999;
|
||||
float:right;
|
||||
font-size:x-small;
|
||||
margin:0 0 0.5em 0.5em;
|
||||
overflow:hidden;
|
||||
padding:0.5em;
|
||||
width:30em;
|
||||
}
|
|
@ -33,10 +33,12 @@
|
|||
[% INCLUDE makeLink
|
||||
uri = c.uri_for(c.controller('Root').action_for('errors'))
|
||||
title = "Errors" %]
|
||||
[% INCLUDE makeLink
|
||||
uri = "http://nixos.org/hydra"
|
||||
title = "About" %]
|
||||
[% IF c.user_exists %]
|
||||
[% IF c.check_user_roles('admin') %]
|
||||
[% INCLUDE makeLink
|
||||
uri = c.uri_for(c.controller('Admin').action_for('admin'))
|
||||
title = "Admin" %]
|
||||
[% END %]
|
||||
[% INCLUDE makeLink
|
||||
uri = c.uri_for(c.controller('Root').action_for('logout'))
|
||||
title = "Sign out" %]
|
||||
|
|
|
@ -458,6 +458,18 @@ create table UriRevMapper (
|
|||
primary key (baseuri)
|
||||
);
|
||||
|
||||
create table NewsItems (
|
||||
#ifdef POSTGRESQL
|
||||
id serial primary key not null,
|
||||
#else
|
||||
id integer primary key autoincrement not null,
|
||||
#endif
|
||||
contents text not null,
|
||||
createTime integer not null,
|
||||
author text not null,
|
||||
foreign key (author) references Users(userName) on delete cascade on update cascade
|
||||
);
|
||||
|
||||
|
||||
-- Some indices.
|
||||
create index IndexBuildInputsOnBuild on BuildInputs(build);
|
||||
|
|
Loading…
Reference in a new issue