... | ... |
@@ -115,6 +115,18 @@ EOS |
115 | 115 |
# Validator |
116 | 116 |
my $validator = Validator::Custom->new; |
117 | 117 |
$self->validator($validator); |
118 |
+ $validator->register_constraint( |
|
119 |
+ user_name => sub { |
|
120 |
+ my $value = shift; |
|
121 |
+ |
|
122 |
+ return ($value || '') =~ /^[a-zA-Z0-9_\-]+$/ |
|
123 |
+ }, |
|
124 |
+ project_name => sub { |
|
125 |
+ my $value = shift; |
|
126 |
+ |
|
127 |
+ return ($value || '') =~ /^[a-zA-Z0-9_\-]+$/ |
|
128 |
+ } |
|
129 |
+ ); |
|
118 | 130 |
|
119 | 131 |
# Helper |
120 | 132 |
$self->helper(gitprep_api => sub { Gitprep::API->new(shift) }); |
... | ... |
@@ -93,5 +93,13 @@ sub default_branch { |
93 | 93 |
|
94 | 94 |
return $config->{default_branch}; |
95 | 95 |
} |
96 |
+ |
|
97 |
+sub delete_project { |
|
98 |
+ my ($self, $user, $project) = @_; |
|
99 |
+ |
|
100 |
+ my $c = $self->cntl; |
|
101 |
+ my $dbi = $c->app->dbi; |
|
102 |
+ $dbi->model('project')->delete(id => [$user, $project]); |
|
103 |
+} |
|
96 | 104 |
1; |
97 | 105 |
|
... | ... |
@@ -332,6 +332,15 @@ sub commits_number { |
332 | 332 |
return $commits_num; |
333 | 333 |
} |
334 | 334 |
|
335 |
+sub delete_project { |
|
336 |
+ my ($self, $user, $project) = @_; |
|
337 |
+ |
|
338 |
+ croak "Invalid user name or project" |
|
339 |
+ unless defined $user && defined $project; |
|
340 |
+ my $rep = $self->rep($user, $project); |
|
341 |
+ rmtree($rep); |
|
342 |
+} |
|
343 |
+ |
|
335 | 344 |
sub exists_repository { |
336 | 345 |
my ($self, $user, $project) = @_; |
337 | 346 |
|
... | ... |
@@ -574,14 +583,6 @@ sub path_by_id { |
574 | 583 |
return; |
575 | 584 |
} |
576 | 585 |
|
577 |
-sub rep { |
|
578 |
- my ($self, $user, $project) = @_; |
|
579 |
- |
|
580 |
- my $home = $self->rep_home; |
|
581 |
- |
|
582 |
- return "$home/$user/$project.git"; |
|
583 |
-} |
|
584 |
- |
|
585 | 586 |
sub description { |
586 | 587 |
my ($self, $user, $project, $description) = @_; |
587 | 588 |
|
... | ... |
@@ -671,35 +672,6 @@ sub project_urls { |
671 | 672 |
return \@urls; |
672 | 673 |
} |
673 | 674 |
|
674 |
-sub references { |
|
675 |
- my ($self, $user, $project, $type) = @_; |
|
676 |
- |
|
677 |
- $type ||= ''; |
|
678 |
- |
|
679 |
- # Get references |
|
680 |
- my @cmd = $self->_cmd( |
|
681 |
- $user, |
|
682 |
- $project, |
|
683 |
- 'show-ref', |
|
684 |
- '--dereference', |
|
685 |
- ($type ? ('--', "refs/$type") : ()) |
|
686 |
- ); |
|
687 |
- open my $fh, '-|', @cmd or return; |
|
688 |
- |
|
689 |
- # Parse references |
|
690 |
- my %refs; |
|
691 |
- while (my $line = $self->dec(scalar <$fh>)) { |
|
692 |
- chomp $line; |
|
693 |
- if ($line =~ m!^([0-9a-fA-F]{40})\srefs/($type.*)$!) { |
|
694 |
- if (defined $refs{$1}) { push @{$refs{$1}}, $2 } |
|
695 |
- else { $refs{$1} = [$2] } |
|
696 |
- } |
|
697 |
- } |
|
698 |
- close $fh or return; |
|
699 |
- |
|
700 |
- return \%refs; |
|
701 |
-} |
|
702 |
- |
|
703 | 675 |
sub projects { |
704 | 676 |
my ($self, $user, $opts) = @_; |
705 | 677 |
|
... | ... |
@@ -734,6 +706,43 @@ sub projects { |
734 | 706 |
return \@reps; |
735 | 707 |
} |
736 | 708 |
|
709 |
+sub references { |
|
710 |
+ my ($self, $user, $project, $type) = @_; |
|
711 |
+ |
|
712 |
+ $type ||= ''; |
|
713 |
+ |
|
714 |
+ # Get references |
|
715 |
+ my @cmd = $self->_cmd( |
|
716 |
+ $user, |
|
717 |
+ $project, |
|
718 |
+ 'show-ref', |
|
719 |
+ '--dereference', |
|
720 |
+ ($type ? ('--', "refs/$type") : ()) |
|
721 |
+ ); |
|
722 |
+ open my $fh, '-|', @cmd or return; |
|
723 |
+ |
|
724 |
+ # Parse references |
|
725 |
+ my %refs; |
|
726 |
+ while (my $line = $self->dec(scalar <$fh>)) { |
|
727 |
+ chomp $line; |
|
728 |
+ if ($line =~ m!^([0-9a-fA-F]{40})\srefs/($type.*)$!) { |
|
729 |
+ if (defined $refs{$1}) { push @{$refs{$1}}, $2 } |
|
730 |
+ else { $refs{$1} = [$2] } |
|
731 |
+ } |
|
732 |
+ } |
|
733 |
+ close $fh or return; |
|
734 |
+ |
|
735 |
+ return \%refs; |
|
736 |
+} |
|
737 |
+ |
|
738 |
+sub rep { |
|
739 |
+ my ($self, $user, $project) = @_; |
|
740 |
+ |
|
741 |
+ my $home = $self->rep_home; |
|
742 |
+ |
|
743 |
+ return "$home/$user/$project.git"; |
|
744 |
+} |
|
745 |
+ |
|
737 | 746 |
sub short_id { |
738 | 747 |
my ($self, $project) = (shift, shift); |
739 | 748 |
|
... | ... |
@@ -17,13 +17,49 @@ |
17 | 17 |
$self->render(json => {ok => 1}); |
18 | 18 |
return $self->res->body; |
19 | 19 |
} |
20 |
+ elsif ($op eq 'delete-project') { |
|
21 |
+ my $params = $api->params; |
|
22 |
+ |
|
23 |
+ my $rule = [ |
|
24 |
+ user => [ |
|
25 |
+ 'user_name' |
|
26 |
+ ], |
|
27 |
+ project => [ |
|
28 |
+ 'project_name' |
|
29 |
+ ] |
|
30 |
+ ]; |
|
31 |
+ my $vresult = app->validator->validate($params, $rule); |
|
32 |
+ |
|
33 |
+ if ($vresult->is_ok) { |
|
34 |
+ my $data = $vresult->data; |
|
35 |
+ my $user = $data->{user}; |
|
36 |
+ my $project = $data->{project}; |
|
37 |
+ |
|
38 |
+ eval { $git->delete_project($user, $project) }; |
|
39 |
+ my $error1 = $@; |
|
40 |
+ eval { $api->delete_project($user, $project) }; |
|
41 |
+ my $error2 = $@; |
|
42 |
+ |
|
43 |
+ if (!$error1 && !$error2) { |
|
44 |
+ $self->render(json => {ok => 1}); |
|
45 |
+ } |
|
46 |
+ else { |
|
47 |
+ app->log->fatal($error1) if $error1; |
|
48 |
+ app->log->fatal($error2) if $error2; |
|
49 |
+ $self->render(json => {ok => 0}); |
|
50 |
+ } |
|
51 |
+ return $self->res->body; |
|
52 |
+ } |
|
53 |
+ else { $api->croak('Invalid') } |
|
54 |
+ } |
|
20 | 55 |
%> |
21 | 56 |
|
22 | 57 |
% layout 'common'; |
23 | 58 |
|
24 | 59 |
%= javascript begin |
25 |
- |
|
60 |
+ |
|
26 | 61 |
$(document).ready(function () { |
62 |
+ |
|
27 | 63 |
// Change description |
28 | 64 |
$('a[href="#description"]').on('click', function () { |
29 | 65 |
var description = $('input[name="description"]').val(); |
... | ... |
@@ -36,6 +72,38 @@ |
36 | 72 |
}); |
37 | 73 |
}); |
38 | 74 |
|
75 |
+ // Check matching deleted project |
|
76 |
+ $('input[name="deleted-project"]').on('keyup', function () { |
|
77 |
+ var deleted_project = $(this).val(); |
|
78 |
+ var project = "<%= $project %>"; |
|
79 |
+ |
|
80 |
+ if (deleted_project == project) { |
|
81 |
+ $('#delete').attr('class', 'btn btn-danger') |
|
82 |
+ .removeAttr('disabled'); |
|
83 |
+ } |
|
84 |
+ else { |
|
85 |
+ $('#delete').attr('class', 'btn btn-danger disabled') |
|
86 |
+ .attr('disabled', 'disabled'); |
|
87 |
+ } |
|
88 |
+ }); |
|
89 |
+ |
|
90 |
+ // Delete project |
|
91 |
+ $('#delete').on('click', function () { |
|
92 |
+ var deleted_project = $('input[name="deleted-project"]').val(); |
|
93 |
+ var url = "<%= url_for %>"; |
|
94 |
+ var data = { |
|
95 |
+ op : "delete-project", |
|
96 |
+ user: "<%= $user %>", |
|
97 |
+ project: "<%= $project %>", |
|
98 |
+ "deleted-project" : deleted_project, |
|
99 |
+ }; |
|
100 |
+ $.post(url, data, function (result) { |
|
101 |
+ if (result.ok) { |
|
102 |
+ location.href = "<%= url_for("/$user") %>"; |
|
103 |
+ } |
|
104 |
+ }); |
|
105 |
+ }); |
|
106 |
+ |
|
39 | 107 |
// Select default branch |
40 | 108 |
var default_branch = "<%= $api->default_branch($user, $project) %>"; |
41 | 109 |
$('select[name="default_branch"]').val(default_branch); |
... | ... |
@@ -86,7 +154,9 @@ |
86 | 154 |
<span class="muted"> |
87 | 155 |
Once you delete a repository, there is no going back. |
88 | 156 |
</span> |
89 |
- <a style="color:red" class="btn" href="#delete">Delete this repository</a> |
|
157 |
+ <a style="color:red" href="#delete-confirm" role="button" class="btn" data-toggle="modal"> |
|
158 |
+ Delete this repository |
|
159 |
+ </a> |
|
90 | 160 |
</div> |
91 | 161 |
</div> |
92 | 162 |
|
... | ... |
@@ -98,5 +168,30 @@ |
98 | 168 |
<button class="btn" data-dismiss="modal" aria-hidden="true">OK</button> |
99 | 169 |
</div> |
100 | 170 |
</div> |
171 |
+ |
|
172 |
+ <div id="delete-confirm" class="modal hide" tabindex="-1" role="dialog" aria-labelledby="delete-confirm-label" aria-hidden="true"> |
|
173 |
+ <div class="modal-header"> |
|
174 |
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> |
|
175 |
+ <div id="modal-message" style="font-weight:bold">Are you ABSOLUTELY sure?</div> |
|
176 |
+ </div> |
|
177 |
+ <div class="modal-body"> |
|
178 |
+ <p> |
|
179 |
+ Unexpected bad things will happen if you don't read this. |
|
180 |
+ </p> |
|
181 |
+ <p> |
|
182 |
+ This action <b>CANNOT</b> be undone. This will delete the <b><%= "$user/$project" %></b> |
|
183 |
+ repository, wiki, issues, and comments permanently. |
|
184 |
+ </p> |
|
185 |
+ <p> |
|
186 |
+ Please type in the name of the repository(<b><%= $project %></b>) to confirm. |
|
187 |
+ </p> |
|
188 |
+ %= text_field 'deleted-project', class => 'span5'; |
|
189 |
+ </div> |
|
190 |
+ <div class="modal-footer"> |
|
191 |
+ <button id="delete" class="btn btn-danger disabled" disabled data-dismiss="modal" aria-hidden="true"> |
|
192 |
+ I understand the consequences, delete this repository |
|
193 |
+ </button> |
|
194 |
+ </div> |
|
195 |
+ </div> |
|
101 | 196 |
|
102 | 197 |
%= include '/include/footer'; |
... | ... |
@@ -15,6 +15,12 @@ |
15 | 15 |
/ |
16 | 16 |
<li><a href="<%= url_for %>"><%= $user %></a></li> |
17 | 17 |
</ul> |
18 |
+ % if (my $message = flash('delete_message')) { |
|
19 |
+ <div class="alert alert-success"> |
|
20 |
+ <button type="button" class="close" data-dismiss="alert">×</button> |
|
21 |
+ <strong><%= $message %></strong> |
|
22 |
+ </div> |
|
23 |
+ % } |
|
18 | 24 |
|
19 | 25 |
<h3>Repositories</h3> |
20 | 26 |
|