Subversion Repositories DevTools

Rev

Rev 1295 | Blame | Compare with Previous | Last modification | View Log | RSS feed

###############################################################################
# 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 viewing of a file.

package Codestriker::Action::ViewTopicFile;

use strict;

use Codestriker::Model::File;
use Codestriker::Model::Comment;
use Codestriker::Http::Render;
use Codestriker::Repository::RepositoryFactory;

# If the input is valid, display the topic.
sub process($$$) {
    my ($type, $http_input, $http_response) = @_;

    # Retrieve the parameters for this action.
    my $query = $http_response->get_query();
    my $topicid = $http_input->get('topic');
    my $mode = $http_input->get('mode');
    my $tabwidth = $http_input->get('tabwidth');
    my $email = $http_input->get('email');
    my $fn = $http_input->get('fn');
    my $new = $http_input->get('new');
    my $parallel = $http_input->get('parallel');
    my $brmode = $http_input->get('brmode');
    my $fview = $http_input->get('fview');

    # Check if this action is allowed.
    if (scalar(@Codestriker::valid_repositories) == 0) {
        $http_response->error("This function has been disabled");
    }

    # Retrieve the appropriate topic details.
    my $topic = Codestriker::Model::Topic->new($topicid);

    # Retrieve the corresponding repository object.
    my $repository =
            Codestriker::Repository::RepositoryFactory->get($topic->{repository});

    # Retrieve the deltas corresponding to this file.
    my @deltas = Codestriker::Model::Delta->get_deltas($topicid, $fn);

    # We need to untaint the filename and revision values, as they will
    # potentially be used to launch an external program.
    $deltas[0]->{filename} =~ /^(.*)$/o;
    my $filename = $1;
    $deltas[0]->{revision} =~ /^(.*)$/o;
    my $revision = $1;

    # Retrieve the comment details for this topic.
    my @comments = $topic->read_comments();

    # Load the appropriate original form of this file into memory.
    my ($filedata_max_line_length, @filedata);
    if (!_read_repository_file($filename, $revision, $tabwidth,
                               $repository, \@filedata,
                               \$filedata_max_line_length)) {
        #
        # 13-Sep-07 SJK: Provide some more info in this message, suggesting the likely root cause.
        #
        #$http_response->error("Couldn't get repository data for $filename " .
        #                     "$revision: $!");
        $http_response->error("Couldn't get repository data for $filename " .
                              "$revision: $!.  Check repository $repository->{dynamic_view_name} can navigate to this file.");
    }

    # This could be done more efficiently, but for now, read through the
    # file, and determine the longest line length for the resulting
    # data that is to be viewed.  Note it is not 100% accurate, but it will
    # do for now, to reduce the resulting page size.
    my $max_line_length = $filedata_max_line_length;
    for (my $d = 0; $d <= $#deltas; $d++) {
        my @difflines = split /\n/, $deltas[$d]->{text};
        for (my $i = 0; $i <= $#difflines; $i++) {
            my $line = $difflines[$i];
            if ($line =~ /^\s(.*)$/o || $line =~ /^\+(.*)$/o ||
                $line =~ /^\-(.*)$/o) {
                my $line_length = length($1);
                if ($line_length > $max_line_length) {
                    $max_line_length = $line_length;
                }
            }
        }
    }

    # Output the new file, with the deltas applied.
    my $title;
    if ($parallel) {
        $title = "View File: Parallel view of $filename v$revision";
    } else {
        $title = $new ? "View File: New $filename" :
            "View File: $filename v$revision";
    }

    $http_response->generate_header(topic=>$topic,
                                    comments=>\@comments,
                                    topic_title=>$title,
                                    mode=>$mode,
                                    tabwidth=>$tabwidth,
                                    fview=>$fview,
                                    repository=>$Codestriker::repository_name_map->{$topic->{repository}}, 
                                    reload=>0, cache=>1);

    # Render the HTML header.
    my $vars = {};
    $vars->{'closehead'} = 1;

    my $header = Codestriker::Http::Template->new("header");
    $header->process($vars);

    my $max_digit_width = length($#filedata);

    # Create a new render object to perform the line rendering.
    my @toc_filenames = ();
    my @toc_revisions = ();
    my @toc_binaries = ();
    my $url_builder = Codestriker::Http::UrlBuilder->new($query);
    my $render =
        Codestriker::Http::Render->new($query, $url_builder, $parallel,
                                       $max_digit_width, $topicid, $mode,
                                       \@comments, $tabwidth,
                                       $repository, \@toc_filenames,
                                       \@toc_revisions, \@toc_binaries,
                                       undef, $max_line_length, $brmode,
                                       $fview);
    # Prepare the output.

    if ($parallel) {
        $render->print_coloured_table();
    }
    else {
        print "<PRE class=\"ms\">\n";
    }

    # Read through all the deltas, and apply them to the original form of the
    # file.
    my $delta = undef;
    for (my $delta_index = 0; $delta_index <= $#deltas; $delta_index++) {
        $delta = $deltas[$delta_index];

        # Output those lines leading up to the start of the next delta.
        # Build up a delta with no changes, and render it.
        my $delta_text = "";
        my $next_delta_linenumber = $delta->{old_linenumber};
        for (my $i = $render->{old_linenumber};
             $i < $next_delta_linenumber; $i++) {
            $delta_text .= " $filedata[$i]\n";
        }
        $render->delta_text($filename, $fn, $revision,
                            $render->{old_linenumber},
                            $render->{new_linenumber},
                            $delta_text, 0, $new, 0);
                            
        # Render the actual change delta.
        $render->delta_text($filename, $fn, $revision,
                            $delta->{old_linenumber},
                            $delta->{new_linenumber}, $delta->{text}, 1,
                            $new, 1);
    }

    # Render the tail part of the file, again by building up a delta.
    my $delta_text = "";
    for (my $i = $render->{old_linenumber}; $i <= $#filedata; $i++) {
        $delta_text .= " $filedata[$i]\n";
    }
    $render->delta_text($filename, $fn, $revision, $render->{old_linenumber},
                        $render->{new_linenumber}, $delta_text, 0, $new, 0);
    
    # Close off the rendering.    
    if ($parallel) {
        print $query->end_table();
    }
    else {
        print "</PRE>\n";
    }

    # Render the HTML trailer.
    my $trailer = Codestriker::Http::Template->new("trailer");
    $trailer->process();

    print $query->end_html();

    $http_response->generate_footer();

    # Fire the topic listener to indicate that the user has viewed the topic.
    Codestriker::TopicListeners::Manager::topic_viewed($email, $topic);
}

# Read the specified repository file and revision into memory.  Return true if
# successful, false otherwise.
sub _read_repository_file ($$$$$$) {
    my ($filename, $revision, $tabwidth, $repository, $data_array_ref,
        $maxline_length_ref) = @_;

    # Read the file data.
    $repository->retrieve($filename, $revision, $data_array_ref);
    
    #
    # 13-Sep-07 SJK: Return false if no data read from repository
    #
    if ( $#$data_array_ref <= 0 ) {
        return 0;
    }

    # Determine the maximum line length, and replace tabs with spaces.
    $$maxline_length_ref = 0;
    for (my $i = 1; $i <= $#$data_array_ref; $i++) {
        $$data_array_ref[$i] =
            Codestriker::Http::Render::tabadjust($tabwidth,
                                                 $$data_array_ref[$i], 0);
        my $line_length = length($$data_array_ref[$i]);
        if ($line_length > $$maxline_length_ref) {
            $$maxline_length_ref = $line_length;
        }
    }
    return 1;
}

1;