############################################################################### # Codestriker: Copyright (c) 2001, 2002 David Sitsky. All rights reserved. # sits@users.sourceforge.net # # This program is free software; you can redistribute it and modify it under # the terms of the GPL. # Action object for handling the submission of a new topic. package Codestriker::Action::SubmitNewTopic; use strict; use File::Temp qw/ tempfile /; use FileHandle; use Codestriker::Model::Topic; use Codestriker::Http::Render; use Codestriker::Repository::RepositoryFactory; use Codestriker::FileParser::Parser; use Codestriker::Model::Project; use Codestriker::TopicListeners::Manager; # If the input is valid, create the appropriate topic into the database. sub process($$$) { my ($type, $http_input, $http_response) = @_; my $query = $http_response->get_query(); # Check that the appropriate fields have been filled in. my $topic_title = $http_input->get('topic_title'); my $topic_description = $http_input->get('topic_description'); my $reviewers = $http_input->get('reviewers'); my $email = $http_input->get('email'); my $cc = $http_input->get('cc'); my $fh = $http_input->get('fh'); my $topic_file = $http_input->get('fh_filename'); my $fh_mime_type = $http_input->get('fh_mime_type'); my $bug_ids = $http_input->get('bug_ids'); my $repository_name = $http_input->get('repository'); my $projectid = $http_input->get('projectid'); my $project_name = $http_input->get('project_name'); my $start_tag = $http_input->get('start_tag'); my $end_tag = $http_input->get('end_tag'); my $module = $http_input->get('module'); my $obsoletes = $http_input->get('obsoletes'); my $default_to_head = $http_input->get('default_to_head'); my $feedback = ""; my $topic_text = ""; my $url_builder = Codestriker::Http::UrlBuilder->new($query); # Indicate whether the topic text needs to be retrieved by the repository # object. my $retrieve_text_from_rep = 0; if (($start_tag ne "" || $end_tag ne "") && $module ne "") { $retrieve_text_from_rep = 1; # Check if this action is permitted. if (scalar(@Codestriker::valid_repositories) == 0) { $feedback .= "Repository functionality has been disabled. " . "Can't create topic text usings tags.\n"; } } if ($topic_title eq "") { $feedback .= "No topic title was entered.\n"; } if ($topic_description eq "") { $feedback .= "No topic description was entered.\n"; } if ($email eq "") { $feedback .= "No email address was entered.\n"; } if (!defined $fh && $retrieve_text_from_rep == 0) { $feedback .= "No filename or module/tags were entered.\n"; } if ($reviewers eq "") { $feedback .= "No reviewers were entered.\n"; } if ($retrieve_text_from_rep && defined $fh) { $feedback .= "Topic text specified using tags and uploaded file.\n"; $feedback .= "Please choose one topic text method, and try again.\n"; } $http_response->generate_header(topic_title=>"Create New Topic", email=>$email, reviewers=>$reviewers, cc=>$cc, repository=>$repository_name, projectid=>$projectid, reload=>0, cache=>0); # Set the error_vars in case of any errors that will require forwarding # to the create topic screen again. my $error_vars = {}; $error_vars->{'version'} = $Codestriker::VERSION; $error_vars->{'feedback'} = $feedback; $error_vars->{'email'} = $email; $error_vars->{'reviewers'} = $reviewers; $error_vars->{'cc'} = $cc; $error_vars->{'topic_file'} = $topic_file; $error_vars->{'topic_description'} = $topic_description; $error_vars->{'topic_title'} = $topic_title; $error_vars->{'bug_ids'} = $bug_ids; $error_vars->{'default_repository'} = $repository_name; $error_vars->{'repositories'} = \@Codestriker::valid_repository_names; $error_vars->{'start_tag'} = $start_tag; $error_vars->{'end_tag'} = $end_tag; $error_vars->{'module'} = $module; $error_vars->{'obsoletes'} = $obsoletes; $error_vars->{'default_to_head'} = $default_to_head; $error_vars->{'default_projectid'} = $projectid; my $repository = undef; my $repository_url = undef; if (scalar(@Codestriker::valid_repositories)) { # Set the repository to the default if it is not entered. if ($repository_name eq "" || scalar(@Codestriker::valid_repository_names) == 1) { $repository_name = $Codestriker::valid_repository_names[0]; } # Check if the repository argument is in fact a configured # repository. $repository_url = $Codestriker::repository_url_map->{$repository_name}; if (defined $repository_url) { $repository = Codestriker::Repository::RepositoryFactory->get($repository_url); } if (! defined $repository) { $feedback .= "The repository value set for \"$repository_name\" is invalid.\n" . "Please correct this value in your codestriker.conf file, " . "and try again.\n"; } } # Set the projectid to the first (default) if it is invalid. my @projects = Codestriker::Model::Project->list(); my $found_project = 0; foreach my $project (@projects) { if ((defined $projectid && $project->{id} == $projectid) || (defined $project_name && $project->{name} eq $project_name)) { $projectid = $project->{id}; $found_project = 1; last; } } if ($found_project == 0) { $projectid = $projects[0]->{id}; } # Make sure all the conditions from the topic listeners are satisified. $feedback .= Codestriker::TopicListeners::Manager::topic_pre_create ($email, $topic_title, $topic_description, $bug_ids, $reviewers, $cc, $repository_url, $projectid); # If there is a problem with the input, redirect to the create screen # with the message. if ($feedback ne "") { if (defined $fh) { $feedback .= "For security reasons, please re-enter the " . "file name to upload, if required.\n"; } _forward_create_topic($error_vars, $feedback, $url_builder); $http_response->generate_footer(); return; } my $topicid = Codestriker::Model::Topic::create_new_topicid(); # If the topic text needs to be retrieved from the repository object, # create a temporary file to store the topic text. my $temp_topic_fh; my $temp_error_fh; if ($retrieve_text_from_rep && defined $repository) { # Store the topic text into temporary files. if (defined $Codestriker::tmpdir && $Codestriker::tmpdir ne "") { $temp_topic_fh = tempfile(DIR => $Codestriker::tmpdir); $temp_error_fh = tempfile(DIR => $Codestriker::tmpdir); } else { $temp_topic_fh = tempfile(); $temp_error_fh = tempfile(); } binmode $temp_topic_fh; binmode $temp_error_fh; my $rc = $repository->getDiff($start_tag, $end_tag, $module, $temp_topic_fh, $temp_error_fh, $default_to_head); # Make sure the data has been flushed to disk. $temp_topic_fh->flush; $temp_error_fh->flush; # Check if the generated diff was too big, and if so, throw an error # message on the screen. if ($rc == $Codestriker::DIFF_TOO_BIG) { $feedback .= "Generated diff file is too big.\n"; } elsif ($rc == $Codestriker::UNSUPPORTED_OPERATION) { $feedback .= "Repository \"" . $repository_name . "\" does not support tag retrieval, you have to use the text file upload.\n"; } elsif ($rc != $Codestriker::OK) { $feedback .= "Unexpected error $rc retrieving diff text.\n"; } if ($repository->{getDiffError}) { $feedback .= $repository->{getDiffError}; } # Seek to the beginning of the temporary file so it can be parsed. seek($temp_topic_fh, 0, 0); # Set $fh to this file reference which contains the topic data. $fh = $temp_topic_fh; } my @deltas = (); if ($feedback eq "") { # Try to parse the topic text into its diff chunks. @deltas = Codestriker::FileParser::Parser->parse($fh, "text/plain", $repository, $topicid, $topic_file); if ($#deltas == -1) { # Nothing in the file, report an error. $feedback .= "Reviewable text in topic is empty.\n"; } } if ($feedback ne "") { # If there was a problem generating the diff file, remove the # temporary files, and direct control to the create screen again. $temp_topic_fh->close if defined $temp_topic_fh; $temp_error_fh->close if defined $temp_error_fh; _forward_create_topic($error_vars, $feedback, $url_builder); $http_response->generate_footer(); return; } # If the topic text has been uploaded from a file, read from it now. if (defined $fh) { while (<$fh>) { $topic_text .= Codestriker::decode_topic_text($_); } if ($topic_text eq "") { if (defined $temp_error_fh) { seek($temp_error_fh, 0, 0); $feedback .= "Problem generating topic text:\n\n"; my $buf = ""; while (read $temp_error_fh, $buf, 16384) { $feedback .= $buf; } } else { $feedback = "Uploaded file doesn't exist or is empty.\n"; } # Remove the temporary files if required, and forward control # back to the create topic page. $temp_topic_fh->close if defined $temp_topic_fh; $temp_error_fh->close if defined $temp_error_fh; _forward_create_topic($error_vars, $feedback, $url_builder); $http_response->generate_footer(); return; } } # Remove the temporary files if required. $temp_topic_fh->close if defined $temp_topic_fh; $temp_error_fh->close if defined $temp_error_fh; # Remove \r from the topic text. $topic_text =~ s/\r//g; # Make sure the topic is not too large, count the number of \n # in the topic content text. my $new_topic_length = 0; ++$new_topic_length while ($topic_text =~ /\n/g); if (defined($Codestriker::maximum_topic_size_lines) && $Codestriker::maximum_topic_size_lines ne "" && $Codestriker::maximum_topic_size_lines < $new_topic_length) { $feedback .= "The topic length of $new_topic_length lines is too long. " . "Topics cannot exceed $Codestriker::maximum_topic_size_lines " . "lines long. Please remove content from topic, or break the " . "topic into several independent topics.\n"; _forward_create_topic($error_vars, $feedback, $url_builder); $http_response->generate_footer(); return; } # Make sure the specified topicids to be obsoleted are in fact valid. if (defined $obsoletes && $obsoletes ne '') { my @data = split ',', $obsoletes; for (my $i = 0; $i <= $#data; $i+=2) { my $id = $data[$i]; my $version = $data[$i+1]; if (! Codestriker::Model::Topic::exists($id)) { $feedback .= "Obsoleted topics specified do not exist.\n"; _forward_create_topic($error_vars, $feedback, $url_builder); $http_response->generate_footer(); return; } } } # Create the topic in the model. my $topic = Codestriker::Model::Topic->new($topicid); $topic->create($topicid, $email, $topic_title, $bug_ids, $reviewers, $cc, $topic_description, $topic_text, $start_tag, $end_tag, $module, $repository_url, $projectid, \@deltas, $obsoletes); # Obsolete any required topics. if (defined $obsoletes && $obsoletes ne '') { my @data = split ',', $obsoletes; for (my $i = 0; $i <= $#data; $i+=2) { my $id = $data[$i]; my $version = $data[$i+1]; Codestriker::Action::SubmitEditTopicsState ->update_state($id, $version, 'Obsoleted', $email); } } # Tell all of the topic listener classes that a topic has # just been created. $feedback = Codestriker::TopicListeners::Manager::topic_create($topic); # Obtain a URL builder object and determine the URL to the topic. my $topic_url = $url_builder->view_url_extended($topicid, -1, "", "", "", $query->url(), 0); # Indicate to the user that the topic has been created and an email has # been sent. my $vars = {}; $vars->{'topic_title'} = $topic->{title}; $vars->{'email'} = $email; $vars->{'topic_url'} = $topic_url; $vars->{'reviewers'} = $reviewers; $vars->{'cc'} = (defined $cc) ? $cc : ""; $vars->{'feedback'} = $feedback; my $template = Codestriker::Http::Template->new("submitnewtopic"); $template->process($vars); $http_response->generate_footer(); } # Direct output to the create topic screen again, with the appropriate feedback # message. sub _forward_create_topic($$$) { my ($vars, $feedback, $url_builder) = @_; $feedback =~ s/\n/
/g; $vars->{'feedback'} = $feedback; my @projects = Codestriker::Model::Project->list(); $vars->{'projects'} = \@projects; Codestriker::Action::CreateTopic-> set_obsoleted_topics_parameter($vars, $url_builder); my $template = Codestriker::Http::Template->new("createtopic"); $template->process($vars); } 1;