from django.urls import path
from django.utils.translation import gettext_lazy as _
from reviewboard.extensions.base import ExtensionManager
from reviewboard.site.urlresolvers import local_site_reverse
from django.http import JsonResponse
from django.shortcuts import render
from reviewboard.scmtools.models import Repository # updated import
from djblets.extensions.hooks import TemplateHook
import os
import subprocess
import logging
logger = logging.getLogger("reviewboard.extensions.FileReview")
class FileReviewExtension(Extension):
metadata = {
'Name': 'FileReview',
'Summary': _('Review complete files from a repository'),
'Author': 'Roland',
'Version': '0.1.0',
}
def initialize(self):
logger.info("Initializing FileReviewExtension and registering URL patterns.")
self.url_patterns = [
path(
'filereview/new/',
self.create_file_review_request,
name='filereview-new'
),
path(
'filereview/list-files/',
self.list_repo_files,
name='filereview-list-files'
),
]
logger.debug("URL patterns registered: %s", self.url_patterns)
# Inject a link into the main content area of the New Review Request page
TemplateHook(
self,
"new-review-request-main",
"filereview/navbar_link.html"
)
def create_file_review_request(self, request):
logger.info("Accessed create_file_review_request view with method: %s", request.method)
if request.method == 'POST':
repo_id = request.POST.get('repository')
file_paths = request.POST.getlist('files')
logger.info("Received POST for repo_id=%s, files=%s", repo_id, file_paths)
# ...logic to create a review request for selected files...
return JsonResponse({'success': True, 'files': file_paths})
else:
repositories = Repository.objects.all()
logger.info("Fetched %d repositories.", repositories.count())
file_select_repos = [
repo for repo in repositories
if getattr(repo, 'tool', None) and getattr(repo.tool, 'name', '').lower() in ('git', 'subversion')
]
logger.info("Filtered %d file-selectable repositories (Git/SVN).", len(file_select_repos))
return render(request, 'filereview/new_review_request.html', {
'repositories': repositories,
'file_select_repos': file_select_repos,
})
def list_repo_files(self, request):
repo_id = request.GET.get('repository')
logger.info("Listing files for repository id: %s", repo_id)
try:
repo = Repository.objects.get(pk=repo_id)
except Repository.DoesNotExist:
logger.error("Repository with id %s does not exist.", repo_id)
return JsonResponse({'files': [], 'error': 'Repository not found'}, status=404)
files = []
tool = getattr(repo, 'tool', None)
tool_name = getattr(tool, 'name', '').lower() if tool else ''
repo_path = repo.path
username = getattr(repo, 'username', None)
password = getattr(repo, 'password', None)
logger.info("Repository type: %s, path: %s", tool_name, repo_path)
if tool_name == 'git':
try:
logger.info("Attempting to list files from Git repository.")
if username and password and repo_path.startswith('http'):
from urllib.parse import urlparse, urlunparse
parts = urlparse(repo_path)
netloc = f"{username}:{password}@{parts.hostname}"
if parts.port:
netloc += f":{parts.port}"
repo_url = urlunparse((parts.scheme, netloc, parts.path, parts.params, parts.query, parts.fragment))
else:
repo_url = repo_path
ref_hash_output = subprocess.check_output(
['git', 'ls-remote', repo_url, 'refs/heads/master'],
universal_newlines=True
)
ref_hash = ref_hash_output.split('\t')[0].strip()
files_output = subprocess.check_output(
['git', 'ls-tree', '-r', '--name-only', ref_hash],
universal_newlines=True
)
files = [f for f in files_output.strip().split('\n') if f]
logger.info("Found %d files in Git repository.", len(files))
except Exception as e:
logger.error("Error listing files from Git: %s", e)
files = []
elif tool_name == 'subversion':
try:
logger.info("Attempting to list files from SVN repository.")
svn_cmd = ['svn', 'list', '-R', repo_path]
if username:
svn_cmd.extend(['--username', username])
if password:
svn_cmd.extend(['--password', password])
output = subprocess.check_output(
svn_cmd,
universal_newlines=True
)
files = [line.strip() for line in output.splitlines() if line and not line.endswith('/')]
logger.info("Found %d files in SVN repository.", len(files))
except Exception as e:
logger.error("Error listing files from SVN: %s", e)
files = []
else:
logger.warning("Repository type %s is not supported for file listing.", tool_name)
files = []
return JsonResponse({'files': files})