Showing 5 changed files with 169 additions and 39 deletions
+12
lib/Gitprep.pm
... ...
@@ -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) });
+8
lib/Gitprep/API.pm
... ...
@@ -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
 
+46 -37
lib/Gitprep/Git.pm
... ...
@@ -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
   
+97 -2
templates/main/settings.html.ep
... ...
@@ -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';
+6
templates/main/user.html.ep
... ...
@@ -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">&times;</button>
21
+          <strong><%= $message %></strong>
22
+        </div>
23
+      % }
18 24
 
19 25
       <h3>Repositories</h3>
20 26