package PgCommitFest::Patch; use strict; use warnings; sub bump { my ($r) = @_; $r->authenticate('require_login' => 1); $r->set_title('Move Patch To New CommitFest'); # Fetch patch. my $d = $r->db->select_one(<cgi_required_id); SELECT id, name, commitfest_id FROM patch_view WHERE id = ? EOM $r->error_exit('Patch not found.') if !defined $d; $r->set_title("Move Patch %s To Another CommitFest", $d->{'name'}); $r->add_link('/action/patch_view?id=' . $d->{'id'}, 'Return to Patch View'); # Fetch list of commitfests. my $list = $r->db->select(<{'commitfest_id'}); SELECT id, name, commitfest_status FROM commitfest_view WHERE id != ? ORDER BY name DESC EOM # Display template. $r->render_template('patch_bump', { 'd' => $d, 'list' => $list }); } sub delete { my ($r) = @_; my $aa = $r->authenticate('require_login' => 1); $r->set_title('Delete Patch'); my $d; eval { # Don't bump last_updated_time, as that would trigger an activity log # record. But do change the last_updater, so that the subsequent # delete picks up the correct user id. This is a pretty ugly kludge, # but I don't immediately have a better idea. $r->db->update('patch', { 'id' => $r->cgi_required_id }, { 'last_updater' => $aa->{'userid'} }); $d = $r->db->select_one(<cgi_required_id); DELETE FROM patch AS p USING commitfest_topic t WHERE p.commitfest_topic_id = t.id AND p.id = ? RETURNING t.commitfest_id EOM }; my $err = $@; if (! $err) { $r->error_exit('Patch not found.') if !defined $d; $r->db->commit; $r->redirect('/action/commitfest_view?id=' . $d->{'commitfest_id'}); } if ($err =~ /patch_comment_patch_id_fkey/) { $r->error(<error("Internal error: $@"); } $r->render_template('patch_delete', { 'id' => $r->cgi_required_id }); } sub form { my ($r) = @_; my $aa = $r->authenticate('require_login' => 1); # Decide whether this is a new patch or an edit of an existing # patch, and if editing reload data from database. my ($d, $cf); my $id = $r->cgi_id(); if (defined $id) { $r->set_title('Edit Patch'); $d = $r->db->select_one(<error_exit('Patch not found.') if !defined $d; $r->redirect('/action/patch_view?id=' . $id) if $r->cgi('cancel'); my $cfid = $r->cgi_id('commitfest'); if (defined $cfid && $cfid != $d->{'commitfest_id'}) { $cf = $r->db->select_one(<error_exit('New CommitFest not found.') if !defined $cf; $d->{'commitfest_id'} = $cf->{'id'}; } } else { $d = $r->db->select_one(<cgi_required_id('commitfest')); SELECT id AS commitfest_id FROM commitfest WHERE id = ? EOM $r->error_exit('CommitFest topic not found.') if !defined $d; $r->set_title('New Patch'); $r->redirect('/action/commitfest_view?id=' . $d->{'commitfest_id'}) if $r->cgi('cancel'); } # Add controls. $r->add_control('name', 'text', 'Name', 'required' => 1); $r->add_control('commitfest_topic', 'select', 'CommitFest Topic', 'required' => 1); my $commitfest_topic = $r->db->select(<{'commitfest_id'}); SELECT id, name FROM commitfest_topic WHERE commitfest_id = ? ORDER BY name EOM my $commitfest_topic_warning = ! @$commitfest_topic; unshift @$commitfest_topic, { 'id' => '', 'name' => '(None Selected)' }; $r->control('commitfest_topic')->choice($commitfest_topic); $r->add_control('patch_status', 'select', 'Patch Status', 'required' => 1); $r->control('patch_status')->choice($r->db->select(<add_control('author', 'text', 'Author', 'required' => 1); $r->add_control('reviewers', 'text', 'Reviewers'); $r->add_control('committer', 'text', 'Committer'); $r->add_control('date_closed', 'date', 'Date Closed'); if (!defined $id) { $r->add_control('message_id', 'text', 'Message-ID for Original Patch', 'required' => 1, 'maxlength' => 255); } my %value = $r->initialize_controls($d); # Cross-field validation. if ($r->cgi('go')) { if (!defined $value{'date_closed'} && grep { $_ eq $value{'patch_status_id'} } qw(4 5 6)) { $value{'date_closed'} = \'now()::date'; } elsif (defined $value{'date_closed'} && !grep { $_ eq $value{'patch_status_id'} } qw(4 5 6)) { $r->error(<cgi('go') && ! $r->is_error()) { $value{'last_updated_time'} = \'now()'; $value{'last_updater'} = $aa->{'userid'}; if (defined $id) { $r->db->update('patch', { 'id' => $id }, \%value); } else { my $message_id = $value{'message_id'}; delete $value{'message_id'}; $id = $r->db->insert_returning_id('patch', \%value); $r->db->insert('patch_comment', { 'patch_id' => $id, 'patch_comment_type_id' => 2, 'message_id' => $message_id, 'content' => 'Initial version.', 'creator' => $aa->{'userid'}, 'last_updater' => $aa->{'userid'}, }); } $r->db->commit; $r->redirect('/action/patch_view?id=' . $id); } # Display template. $r->render_template('patch_form', { 'id' => $id, 'd' => $d, 'commitfest_topic_warning' => $commitfest_topic_warning, 'new_commitfest' => $cf }); } sub view { my ($r) = @_; my $aa = $r->authenticate(); my $id = $r->cgi_id(); my $d = $r->db->select_one(<error_exit('Patch not found.') if !defined $d; $r->set_title('Patch: %s', $d->{'name'}); my $patch_comment_list = $r->db->select(<{'id'}); SELECT v.id, v.patch_comment_type, v.message_id, v.content, v.creator, to_char(v.creation_time, 'YYYY-MM-DD HH:MI:SS AM') AS creation_time FROM patch_comment_view v WHERE v.patch_id = ? ORDER BY v.creation_time EOM # Add patch comment controls, so that users can add a patch comment # without needing to visit a separate page. if (defined $aa) { my %value = PgCommitFest::PatchComment::controls($r, {}); if ($r->cgi('go') && ! $r->is_error()) { $value{'patch_id'} = $d->{'id'}; $value{'creator'} = $aa->{'userid'}; $value{'last_updater'} = $aa->{'userid'}; $r->db->insert('patch_comment', \%value); $r->db->commit; $r->redirect('/action/patch_view?id=' . $d->{'id'}); } } $r->add_link('/action/patch_form?id=' . $id, 'Edit Patch'); $r->add_link('/action/patch_bump?id=' . $id, 'Move To Another CommitFest'); $r->add_link('/action/patch_delete?id=' . $id, 'Delete Patch', 'Are you sure you want to delete this patch?'); $r->render_template('patch_view', { 'd' => $d, 'patch_comment_list' => $patch_comment_list }); } 1;