gitprep / templates / pull.html.ep /
97bccf3 7 years ago
1 contributor
575 lines | 19.513kb
<%
  # API
  my $api = gitprep_api;

  # Parameters
  my $base_user_id = param('user');
  my $base_project_id = param('project');
  my $issue_number = param('number');
  
  # Issue
  my $issue = app->dbi->model('issue')->select(
    [
      {__MY__ => '*'},
      {open_user => ['id']}
    ],
    where => {
      'project.id' => $base_project_id,
      'issue.number' => $issue_number
    }
  )->one;
  
  # Pull requests
  my $pull_request = app->dbi->model('pull_request')->select(
    where => {row_id => $issue->{pull_request}}
  )->one;
  
  # Base information
  my $base_project_row_id = $pull_request->{base_project};
  my $base_project = app->dbi->model('project')->select(
    [
      {__MY__ => '*'},
      {user => ['id']}
    ],
    where => {'project.row_id' => $base_project_row_id}
  )->one;
  my $base_branch = $pull_request->{base_branch};
  
  Carp::croak "pull_request invalid user id"
    if $base_user_id ne $base_project->{'user.id'};
  Carp::croak "pull_request invalid project id"
    if $base_project_id ne $base_project->{id};
  
  # Target information
  my $target_project_row_id = $pull_request->{target_project};
  my $target_project = app->dbi->model('project')->select(
    [
      {__MY__ => '*'},
      {user => ['id']}
    ],
    where => {'project.row_id' => $target_project_row_id}
  )->one;
  my $target_branch = $pull_request->{target_branch};
  my $target_user_id = $target_project->{'user.id'};
  my $target_project_id = $target_project->{id};
  
  # Session
  my $session_user_row_id = $api->session_user_row_id;
  my $session_user_id = $api->session_user_id;
  
  # Base repository information
  my $base_rep_info = app->rep_info($base_user_id, $base_project_id);
  
  # Target repository information
  my $target_rep_info = app->rep_info($target_user_id, $base_project_id);
  
  # Working repository information
  my $work_rep_info = app->work_rep_info($base_user_id, $base_project_id);

  # Display patch
  if (stash('patch')) {
    # Lock working repository
    my $lock_fh = $self->app->manager->lock_rep($work_rep_info);
    
    # Prepare merge
    $self->app->manager->prepare_merge(
      $work_rep_info,
      $base_rep_info,
      $base_branch,
      $target_rep_info,
      $target_branch
    );
    
    # Create patch
    my $patch = $self->app->manager->get_patch(
      $work_rep_info,
      $target_rep_info,
      $target_branch
    );
    
    $self->res->headers->content_type('text/plain; charset=utf-8');
    $self->render(text => $patch);
    return;
  }

  # Git
  my $git = $self->app->git;
  
  my $op = param('op') // '';
  
  my $errors;
  if (lc $self->req->method eq 'post') {
    
    # Close pull request
    my $op = param('op');
    if ($op eq 'close-issue') {
      app->dbi->model('issue')->update(
        {
          open => 0
        },
        where => {row_id => $issue->{row_id}}
      );
      $self->redirect_to('current');
      return;
    }
    elsif ($op eq 'reopen-issue') {
      my $open_time = time;
      app->dbi->model('issue')->update(
        {
          open => 1,
          open_time => $open_time,
          open_user => $session_user_row_id
        },
        where => {
          row_id => $issue->{row_id}
        }
      );
      $self->redirect_to('current');
      return;
    }
    elsif ($op eq 'merge') {

      # Access controll
      unless ($api->can_write_access($session_user_id, $base_user_id, $base_project_id)) {
        $self->reply->exception('Forbbiden');
        return;
      }
      
      # Lock working repository
      my $lock_fh = $self->app->manager->lock_rep($work_rep_info);

      # Prepare merge
      $self->app->manager->prepare_merge(
        $work_rep_info,
        $base_rep_info,
        $base_branch,
        $target_rep_info,
        $target_branch
      );
      
      # Merge
      my $merge_success = $self->app->manager->merge(
        $work_rep_info,
        $target_rep_info,
        $target_branch
      );
      
      
      if ($merge_success) {
        # Push
        app->manager->push($work_rep_info, $base_branch);
        
        # Close
        app->dbi->model('issue')->update(
          {
            open => 0
          },
          where => {row_id => $issue->{row_id}}
        );
        
        $self->redirect_to("/$base_user_id/$base_project_id/tree/$base_branch");
      }
    }
    elsif ($op eq 'add-comment') {

      # Parameters
      my $message = param('message');
      
      # Validation
      my $vc = app->vc;
      my $validation = $vc->validation;
      
      # Check Message
      if (!length $message) {
        $validation->add_failed(message => 'message is empty');
      }
      elsif (length $message > 1000) {
        $validation->add_failed(message => 'message is too long');
      }
      
      if ($validation->is_valid) {

        $api->add_issue_message($base_user_id, $base_project_id, $issue_number, $message);
        
        $self->redirect_to;
        return;
      }
      else {
        $errors = $validation->messages;
      }
    }
    elsif ($op eq 'api-delete-issue-message') {
      my $issue_message_row_id = param('issue_message_row_id');
      
      my $json = $api->api_delete_issue_message($issue_message_row_id, $base_user_id);
      
      $self->render(json => $json);
      return;
    }
    elsif ($op eq 'api-update-issue-message') {
      
      my $issue_message_row_id = param('issue_message_row_id');
      my $message = param('message');
      
      my $json = $api->api_update_issue_message($issue_message_row_id, $message, $base_user_id);
      
      $self->render(json => $json);
      return;
    }
  }
  
  # Commits
  my $commits = $git->forward_commits(
    $work_rep_info,
    $base_rep_info,
    $base_branch,
    $target_rep_info,
    $target_branch
  );
  my $commits_count = @$commits;
  my $commits_date = {};
  my $authors = {};
  for my $commit (@$commits) {
    my $date = $commit->{age_string_date_local};
    $commits_date->{$date} ||= [];
    $authors->{$commit->{author}} = 1;
    push @{$commits_date->{$date}}, $commit;
  }
  my $authors_count = keys %$authors;

  # Start commit
  my $start_commit_id = app->git->ref_to_object_id($base_rep_info, $base_branch);

  # End commit
  my $end_commit_id = app->git->ref_to_object_id($target_rep_info, $target_branch);
  
  # Issue messages
  my $issue_messages = app->dbi->model('issue_message')->select(
    [
      {__MY__ => '*'},
      {user => ['id']}
    ],
    where => {issue => $issue->{row_id}}
  )->all;
  
  # Issue message count
  my $issue_messages_count = app->dbi->model('issue_message')->select(
    'count(*)',
    where => {issue => $issue->{row_id}},
  )->value;

  # Check merge automatically
  my $merge_success;
  if ($api->can_write_access($session_user_id, $base_user_id, $base_project_id) && $issue->{open}) {
    
    my $lock_fh = $self->app->manager->lock_rep($work_rep_info);
    
    # Prepare merge
    $self->app->manager->prepare_merge(
      $work_rep_info,
      $base_rep_info,
      $base_branch,
      $target_rep_info,
      $target_branch
    );
    
    # Check merge automatical
    $merge_success = $self->app->manager->merge(
      $work_rep_info,
      $target_rep_info,
      $target_branch
    );
  }
  
  # HTTP repository URL
  my $http_rep_url = url_for("$target_user_id/$target_project_id.git")->to_abs;
  
  # SSH repository URL
  my $url = url_for->to_abs;
  $url->base(undef);
  my $ssh_port = config->{basic}{ssh_port};
  my $rep_home = app->rep_home;
  my $execute_user = getpwuid($>);
  my $ssh_rep_url_base = defined app->config->{basic}{'ssh_rep_url_base'}
    ? app->config->{basic}{'ssh_rep_url_base'} : $rep_home;
  my $ssh_rep_url = "ssh://$execute_user\@" . $url->host
    . ($ssh_port ? ":$ssh_port" : '') . "$ssh_rep_url_base/$user/$project.git";

  my $patch_url = url_for("/$base_user_id/$base_project_id/pull/$issue->{number}.patch")->to_abs;

  my $pre_content_base = "git checkout -b $target_user_id-$target_branch $base_branch";
  my $pre_content_http = "git pull $http_rep_url $target_branch";
  my $pre_content_ssh = "git pull $ssh_rep_url $target_branch";
  
  # Commit body arguments
  my %commit_body_args = (
    rep_info => $work_rep_info,
    rev => $end_commit_id,
    from_rev => $start_commit_id
  );

  layout 'common', title => "Pull Requests Tags \x{30fb} $base_user_id/$base_project_id";
%>


  %= javascript begin
    $(document).ready(function() {
      %= include '/include/js/issue';
    });
  % end

  %= include '/include/header';
  
  <div class="container">
    %= include '/include/errors', errors => $errors;
    <div style="font-size:23px;margin-top:20px;margin-bottom:9px;">
      <%
        my $pull_title = $pull_request->{target_branch};
        $pull_title =~ s/_/ /g;
        $pull_title = ucfirst $pull_title;
      %>
      
      <%= $pull_title %> <span style="color:#767676;">#<%= $issue->{number} %></span>
    </div>
    <div>
      <div style="display:inline-block;color:white;margin-right:4px;">
        % if ($issue->{open}) {
          <div style="background:#6cc644;padding:4px 8px;border-radius:3px;">
            Open
          </div>
        % } else {
          <div style="background:#bd2c00;padding:4px 8px;border-radius:3px;">
            Closed
          </div>
        % }
      </div>
      % my $open_user_id = $issue->{'open_user.id'};
      <a style="color:#333333;font-weight:bold" href="<%= url_for("/$open_user_id") %>"><%= $open_user_id %></a> 
      <span style="color:#767676">
        wants to merge <%= $commits_count %> commits
        into
        <span style="display:inline-block;padding:2px 4px;background:#e8f0f8;color:#336479;border-radius:3px;">
          <%= $pull_request->{base_branch} %>
        </span>
        from
        <span style="display:inline-block;padding:2px 4px;background:#e8f0f8;color:#336479;border-radius:3px;">
          % if ($base_user_id ne $target_user_id) {
            <%= $target_user_id %>
            /
          % }
          <%= $pull_request->{target_branch} %>
        </span>
      </span>
    </div>
    % if (!$commits_count) {
      <div class="compare-nothing" style="margin-top:10px">
        <div>
          <b><big>There isn't anything to compare.</big></b>
        </div>
      </div>
    % } else {
      <div>
        <ul class="compare-header">
          <li>
            <b><%= @$commits %></b> <span>commit</span>
          </li>
          <li>
            <b><%= $authors_count %></b> <span>contributor</span>
          </li>
          <li>
            
          </li>
          <li>
            
          </li>
        </ul>

        <div class="issue-panel" style="margin-top:10px;">
          <div>
            % for my $issue_message (@$issue_messages) {
              <%
                my %issue_message_param = (
                  user_id => $base_user_id,
                  issue_message => $issue_message
                );
              %>
              
              %= include '/include/issue_message', %issue_message_param;
            % }
          </div>
        </div>

        <div class="commits">
          % for my $date (reverse sort keys %$commits_date) {
            % my $commits = $commits_date->{$date};
            
            <div class="commit-date">
              <i class="icon-off"></i><span>Commits on <%= $date %></span>
            </div>
            
            <ul class="compare-commits-date-container">
              % for my $commit (sort {$b->{author_epoch} <=> $a->{author_epoch}} @$commits) {
                <%
                  my $commit_author_email = $commit->{author_email};
                  my $commit_author_id = app->dbi->model('user')->select(
                    'id',
                    where => {email => $commit_author_email}
                  )->value;
                %>
                <li>
                  <div class="compare-commits-author">
                    <span title="<%= $commit->{author_email} %>">
                      % if (defined $commit_author_id) {
                        <a href="<%= url_for("/$commit_author_id") %>"><%= $commit_author_id %></a>
                      % } else {
                        <%= $commit->{author_name} %>
                      % }
                    </span>
                  </div>
                  <div class="compare-commits-commit-title">
                    <a style="color:#333" href="<%= url_for("/$base_user_id/$base_project_id/commit/$commit->{id}") %>">
                      <%= $commit->{title_short} %>
                    </a>
                  </div>
                  <div class="compare-commits-commit-id">
                    <a href="<%= url_for("/$base_user_id/$base_project_id/commit/$commit->{id}") %>">
                      <%= substr($commit->{id}, 0, 7) %>
                    </a>
                  </div>
                </li>
              % }
            </ul>
          % }
        </div>

  
        %= include '/include/commit_body', %commit_body_args;
      % }
      
      % if ($api->can_write_access($session_user_id, $base_user_id, $base_project_id)) {
        % if ($commits_count && $merge_success && $issue->{open}) {
          <div class="pull-request-form">
            <div style="overflow:hidden">
              <div style="float:left;padding:10px;padding-right:0">
                <div style="width:30px;height:30px;text-align:center;border-radius:15px;background:#95c97e;color:white;padding-top:5px;"><%= "\x{2714}" %></div>
              </div>
              <div style="float:left">
                <div class="pull-request-form-title">
                  <div>
                    <b>This branch has no conflicts with the base branch</b>
                  </div>
                  <div>
                    <span style="color:#767676">Merging can be performed automatically.</span>
                  </div>
                </div>
              </div>
            </div>
            <div class="pull-request-form-button">
              <form action="<%= url_for %>" method="post">
                <%= hidden_field op => 'merge' %>
                <%= submit_button 'Merge pull request', class => "btn btn-success" %>
                <span style="margin-left:5px">
                  You can also view <a href="javascript:void(0)" onclick="$('#command-line-instructions').toggle()" >command line instructions</a>.
                </span>
              </form>
              
              <div id="command-line-instructions" style="display:none">
                <div style="font-size:18px;padding:10px 0">
                  Merging via command line
                </div>

                <div>
                  If you do not want to use the merge button or an automatic merge cannot be performed, you can perform a manual merge on the command line.
                </div>
                %= javascript begin
                  $(document).ready(function () {
                    var http_rep_url = "<%= $http_rep_url %>";
                    
                    var ssh_rep_url = "<%= $ssh_rep_url %>";
                    var patch_url = "<%= $patch_url %>";
                    
                    var pre_content_base = "<%= $pre_content_base %>";
                    var pre_content_http = "<%= $pre_content_http %>";
                    var pre_content_ssh = "<%= $pre_content_ssh %>";
                    
                    var pre_content_http_all = pre_content_base + "\n" + pre_content_http;
                    var pre_content_ssh_all = pre_content_base + "\n" + pre_content_ssh;
                    
                    $('#http-btn').on('click', function () {
                      $('[name=clone_url]').val(http_rep_url);
                      $('#step1').text(pre_content_http_all);
                    });
                    $('#ssh-btn').on('click', function () {
                      $('[name=clone_url]').val(ssh_rep_url);
                      $('#step1').text(pre_content_ssh_all);
                    });
                    $('#patch-btn').on('click', function () {
                      $('[name=clone_url]').val(patch_url);
                    });
                  });
                % end
                
                <div style="margin-top:10px">
                  <button id="http-btn" class="btn btn-small" style="border-radius:3px 0 0 3px">HTTPS</button><button id="ssh-btn" class="btn btn-small" style="border-radius:0">SSH</button><button id="patch-btn" class="btn btn-small" style="border-radius:0">Patch</button><%= text_field clone_url => $http_rep_url, style => "width:600px;padding:4px 7px 5px 7px;border-radius:0 3px 3px 0" %>
                </div>

                <div style="margin-top:15px;margin-bottom:5px;">
                  <b>Step 1:</b> From your project repository, check out a new branch and test the changes.
                </div>

<pre id="step1" style="background:#f2f2f2; padding:10px; border-radius:3px">
git checkout -b <%= $target_user_id %>-<%= $target_branch %> <%= $base_branch %>
git pull <%= url_for("$target_user_id/$target_project_id.git")->to_abs %> <%= $target_branch %>
</pre>

                <div style="margin-top:15px;margin-bottom:5px;">
                  <b>Step 2:</b> Merge the changes and update on GitPrep.
                </div>

<pre style="background:#f2f2f2; padding:10px; border-radius:3px">
git checkout <%= $base_branch %>
git merge --no-ff <%= $target_user_id %>-<%= $target_branch %>
git push origin <%= $base_branch %>
</pre>
              </div>
            </div>
          </div>
        % }
      % }

      % if ($api->logined) {
        <div class="issue-add-comment">
          <form action="<%= url_for %>" method="post">
            <%= hidden_field 'op' %>
            <div class="issue-add-comment-header">
              <div class="issue-message-write-tab issue-add-comment-header-tab"><a href="javascript:void(0)">Write</a></div>
              <div class="issue-message-preview-tab issue-add-comment-header-tab"><a class="disable" href="javascript:void(0)">Preview</a></div>
              %= include '/include/issue_comment_icon';
            </div>
            <div class="issue-add-comment-body">
              <div class="issue-message-write-area issue-add-comment-message">
                <%= text_area 'message' %>
              </div>
              <div class="issue-message-preview-area issue-add-comment-preview markdown-body" style="padding:10px">
              </div>
              <div class="issue-add-comment-bottom">
                <div class="issue-add-comment-button-left">
                  Styling with Markdown is supported
                </div>
                <div class="issue-add-comment-button">
                  % if ($issue->{open}) {
                    <input type="submit" value="Close pull request" onclick="$(this).closest('form').find('[name=op]').val('close-issue'); $(this).closest('form').submit();" class="btn">
                  % } else {
                    <input type="submit" value="Reopen pull request" onclick="$(this).closest('form').find('[name=op]').val('reopen-issue'); $(this).closest('form').submit();" class="btn">
                  % }
                  <input type="submit" value="Comment" onclick="$(this).closest('form').find('[name=op]').val('add-comment'); $(this).closest('form').submit();" class="btn btn-success">
                </div>
              </div>
            </div>
          </form>
        </div>
      % }
    </div>
  </div>

  %= include '/include/footer';