diff --git a/swh/web/browse/views/content.py b/swh/web/browse/views/content.py index 7c55f5eb..de00d609 100644 --- a/swh/web/browse/views/content.py +++ b/swh/web/browse/views/content.py @@ -1,267 +1,244 @@ # Copyright (C) 2017-2018 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information import difflib import json from distutils.util import strtobool from django.http import HttpResponse from django.utils.safestring import mark_safe from django.shortcuts import render from django.template.defaultfilters import filesizeformat from swh.model.hashutil import hash_to_hex from swh.web.common import query from swh.web.common.utils import reverse, gen_path_info from swh.web.common.exc import handle_view_exception from swh.web.browse.utils import ( request_content, prepare_content_for_display, content_display_max_size ) from swh.web.browse.browseurls import browse_route @browse_route(r'content/(?P.+)/raw/', view_name='browse-content-raw') def content_raw(request, query_string): """Django view that produces a raw display of a SWH content identified by its hash value. The url that points to it is :http:get:`/browse/content/[(algo_hash):](hash)/raw/` - - Args: - request: input django http request - query_string: a string of the form "[ALGO_HASH:]HASH" where - optional ALGO_HASH can be either *sha1*, *sha1_git*, *sha256*, - or *blake2s256* (default to *sha1*) and HASH the hexadecimal - representation of the hash value - - Returns: - The raw bytes of the content. - - """ # noqa try: algo, checksum = query.parse_hash(query_string) checksum = hash_to_hex(checksum) content_data = request_content(query_string, max_size=None) except Exception as exc: return handle_view_exception(request, exc) filename = request.GET.get('filename', None) if not filename: filename = '%s_%s' % (algo, checksum) if content_data['mimetype'].startswith('text/') or \ content_data['mimetype'] == 'inode/x-empty': response = HttpResponse(content_data['raw_data'], content_type="text/plain") response['Content-disposition'] = 'filename=%s' % filename else: response = HttpResponse(content_data['raw_data'], content_type='application/octet-stream') response['Content-disposition'] = 'attachment; filename=%s' % filename return response _auto_diff_size_limit = 20000 @browse_route(r'content/(?P.*)/diff/(?P.*)', # noqa view_name='diff-contents') def _contents_diff(request, from_query_string, to_query_string): """ Browse endpoint used to compute unified diffs between two contents. Diffs are generated only if the two contents are textual. By default, diffs whose size are greater than 20 kB will not be generated. To force the generation of large diffs, the 'force' boolean query parameter must be used. Args: request: input django http request from_query_string: a string of the form "[ALGO_HASH:]HASH" where optional ALGO_HASH can be either *sha1*, *sha1_git*, *sha256*, or *blake2s256* (default to *sha1*) and HASH the hexadecimal representation of the hash value identifying the first content to_query_string: same as above for identifying the second content Returns: A JSON object containing the unified diff. """ diff_data = {} content_from = None content_to = None content_from_size = 0 content_to_size = 0 content_from_lines = [] content_to_lines = [] force = request.GET.get('force', 'false') path = request.GET.get('path', None) language = 'nohighlight-swh' force = bool(strtobool(force)) if from_query_string == to_query_string: diff_str = 'File renamed without changes' else: text_diff = True if from_query_string: content_from = request_content(from_query_string, max_size=None) content_from_display_data = prepare_content_for_display( content_from['raw_data'], content_from['mimetype'], path) language = content_from_display_data['language'] content_from_size = content_from['length'] if not (content_from['mimetype'].startswith('text/') or content_from['mimetype'] == 'inode/x-empty'): text_diff = False if text_diff and to_query_string: content_to = request_content(to_query_string, max_size=None) content_to_display_data = prepare_content_for_display( content_to['raw_data'], content_to['mimetype'], path) language = content_to_display_data['language'] content_to_size = content_to['length'] if not (content_to['mimetype'].startswith('text/') or content_to['mimetype'] == 'inode/x-empty'): text_diff = False diff_size = abs(content_to_size - content_from_size) if not text_diff: diff_str = 'Diffs are not generated for non textual content' language = 'nohighlight-swh' elif not force and diff_size > _auto_diff_size_limit: diff_str = 'Large diffs are not automatically computed' language = 'nohighlight-swh' else: if content_from: content_from_lines = content_from['raw_data'].decode('utf-8')\ .splitlines(True) if content_from_lines and content_from_lines[-1][-1] != '\n': content_from_lines[-1] += '[swh-no-nl-marker]\n' if content_to: content_to_lines = content_to['raw_data'].decode('utf-8')\ .splitlines(True) if content_to_lines and content_to_lines[-1][-1] != '\n': content_to_lines[-1] += '[swh-no-nl-marker]\n' diff_lines = difflib.unified_diff(content_from_lines, content_to_lines) diff_str = ''.join(list(diff_lines)[2:]) diff_data['diff_str'] = diff_str diff_data['language'] = language diff_data_json = json.dumps(diff_data, separators=(',', ': ')) return HttpResponse(diff_data_json, content_type='application/json') @browse_route(r'content/(?P.+)/', view_name='browse-content') def content_display(request, query_string): """Django view that produces an HTML display of a SWH content identified by its hash value. The url that points to it is :http:get:`/browse/content/[(algo_hash):](hash)/` - - Args: - request: input django http request - query_string: a string of the form "[ALGO_HASH:]HASH" where - optional ALGO_HASH can be either *sha1*, *sha1_git*, *sha256*, - or *blake2s256* (default to *sha1*) and HASH the hexadecimal - representation of the hash value - - Returns: - The HTML rendering of the requested content. - """ # noqa try: algo, checksum = query.parse_hash(query_string) checksum = hash_to_hex(checksum) content_data = request_content(query_string) except Exception as exc: return handle_view_exception(request, exc) path = request.GET.get('path', None) content = None language = None if content_data['raw_data'] is not None: content_display_data = prepare_content_for_display( content_data['raw_data'], content_data['mimetype'], path) content = content_display_data['content_data'] language = content_display_data['language'] root_dir = None filename = None path_info = None breadcrumbs = [] if path: split_path = path.split('/') root_dir = split_path[0] filename = split_path[-1] path = path.replace(root_dir + '/', '') path = path[:-len(filename)] path_info = gen_path_info(path) breadcrumbs.append({'name': root_dir[:7], 'url': reverse('browse-directory', kwargs={'sha1_git': root_dir})}) for pi in path_info: breadcrumbs.append({'name': pi['name'], 'url': reverse('browse-directory', kwargs={'sha1_git': root_dir, 'path': pi['path']})}) breadcrumbs.append({'name': filename, 'url': None}) query_params = None if filename: query_params = {'filename': filename} content_raw_url = reverse('browse-content-raw', kwargs={'query_string': query_string}, query_params=query_params) content_metadata = { 'sha1 checksum': content_data['checksums']['sha1'], 'sha1_git checksum': content_data['checksums']['sha1_git'], 'sha256 checksum': content_data['checksums']['sha256'], 'blake2s256 checksum': content_data['checksums']['blake2s256'], 'mime type': content_data['mimetype'], 'encoding': content_data['encoding'], 'size': filesizeformat(content_data['length']), 'language': content_data['language'], 'licenses': content_data['licenses'] } return render(request, 'content.html', {'empty_browse': False, 'heading': 'Content information', 'top_panel_visible': True, 'top_panel_collapsible': True, 'top_panel_text': 'SWH object: Content', 'swh_object_metadata': content_metadata, 'main_panel_visible': True, 'content': content, 'content_size': content_data['length'], 'max_content_size': content_display_max_size, 'mimetype': content_data['mimetype'], 'language': language, 'breadcrumbs': breadcrumbs, 'top_right_link': content_raw_url, 'top_right_link_text': mark_safe( 'Raw File'), 'origin_context': None, 'vault_cooking': None, 'show_actions_menu': False }) diff --git a/swh/web/browse/views/directory.py b/swh/web/browse/views/directory.py index 456e00e7..1753c96f 100644 --- a/swh/web/browse/views/directory.py +++ b/swh/web/browse/views/directory.py @@ -1,115 +1,106 @@ # Copyright (C) 2017-2018 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information from django.shortcuts import render from django.template.defaultfilters import filesizeformat from swh.web.common import service from swh.web.common.utils import reverse, gen_path_info from swh.web.common.exc import handle_view_exception from swh.web.browse.utils import get_directory_entries from swh.web.browse.browseurls import browse_route @browse_route(r'directory/(?P[0-9a-f]+)/', r'directory/(?P[0-9a-f]+)/(?P.+)/', view_name='browse-directory') def directory_browse(request, sha1_git, path=None): """Django view for browsing the content of a SWH directory identified by its sha1_git value. The url that points to it is :http:get:`/browse/directory/(sha1_git)/[(path)/]` - - Args: - request: input django http request - sha1_git: swh sha1_git identifer of the directory to browse - path: optionnal path parameter used to navigate in directories - reachable from the provided root one - - Returns: - The HTML rendering for the content of the provided directory. """ # noqa root_sha1_git = sha1_git try: if path: dir_info = service.lookup_directory_with_path(sha1_git, path) sha1_git = dir_info['target'] dirs, files = get_directory_entries(sha1_git) except Exception as exc: return handle_view_exception(request, exc) path_info = gen_path_info(path) breadcrumbs = [] breadcrumbs.append({'name': root_sha1_git[:7], 'url': reverse('browse-directory', kwargs={'sha1_git': root_sha1_git})}) for pi in path_info: breadcrumbs.append({'name': pi['name'], 'url': reverse('browse-directory', kwargs={'sha1_git': root_sha1_git, 'path': pi['path']})}) path = '' if path is None else (path + '/') for d in dirs: d['url'] = reverse('browse-directory', kwargs={'sha1_git': root_sha1_git, 'path': path + d['name']}) sum_file_sizes = 0 readme_name = None readme_url = None for f in files: query_string = 'sha1_git:' + f['target'] f['url'] = reverse('browse-content', kwargs={'query_string': query_string}, query_params={'path': root_sha1_git + '/' + path + f['name']}) sum_file_sizes += f['length'] f['length'] = filesizeformat(f['length']) if f['name'].lower().startswith('readme'): readme_name = f['name'] readme_sha1 = f['checksums']['sha1'] readme_url = reverse('browse-content-raw', kwargs={'query_string': readme_sha1}) sum_file_sizes = filesizeformat(sum_file_sizes) dir_metadata = {'id': sha1_git, 'number of regular files': len(files), 'number of subdirectories': len(dirs), 'sum of regular file sizes': sum_file_sizes} vault_cooking = { 'directory_context': True, 'directory_id': sha1_git, 'revision_context': False, 'revision_id': None } return render(request, 'directory.html', {'empty_browse': False, 'heading': 'Directory information', 'top_panel_visible': True, 'top_panel_collapsible': True, 'top_panel_text': 'SWH object: Directory', 'swh_object_metadata': dir_metadata, 'main_panel_visible': True, 'dirs': dirs, 'files': files, 'breadcrumbs': breadcrumbs, 'top_right_link': None, 'top_right_link_text': None, 'readme_name': readme_name, 'readme_url': readme_url, 'origin_context': None, 'vault_cooking': vault_cooking, 'show_actions_menu': True}) diff --git a/swh/web/browse/views/identifiers.py b/swh/web/browse/views/identifiers.py index 47a96617..8ff74ca1 100644 --- a/swh/web/browse/views/identifiers.py +++ b/swh/web/browse/views/identifiers.py @@ -1,59 +1,52 @@ # Copyright (C) 2017-2018 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information from django.shortcuts import redirect from swh.model.identifiers import parse_persistent_identifier from swh.web.browse.browseurls import browse_route from swh.web.common.utils import reverse from swh.web.common.exc import BadInputExc, handle_view_exception @browse_route(r'(?Pswh:[0-9]+:[a-z]+:[0-9a-f]+)/', view_name='browse-swh-id') def swh_id_browse(request, swh_id): """ Django view enabling to browse the SWH archive using :ref:`persistent-identifiers`. The url that points to it is :http:get:`/browse/(swh_id)/`. - - Args: - swh_id: A SWH persistent identifier - - Returns: - A redirection to the adequate view to browse the - requested SWH object """ try: swh_id_parsed = parse_persistent_identifier(swh_id) object_type = swh_id_parsed['object_type'] object_id = swh_id_parsed['object_id'] view_url = None if object_type == 'cnt': query_string = 'sha1_git:' + object_id view_url = reverse('browse-content', kwargs={'query_string': query_string}, query_params=request.GET) elif object_type == 'dir': view_url = reverse('browse-directory', kwargs={'sha1_git': object_id}, query_params=request.GET) elif object_type == 'rel': view_url = reverse('browse-release', kwargs={'sha1_git': object_id}, query_params=request.GET) elif object_type == 'rev': view_url = reverse('browse-revision', kwargs={'sha1_git': object_id}, query_params=request.GET) else: msg = '\'%s\' is not a valid SWH persistent identifier!' % swh_id raise BadInputExc(msg) except Exception as exc: return handle_view_exception(request, exc) return redirect(view_url) diff --git a/swh/web/browse/views/origin.py b/swh/web/browse/views/origin.py index c3e60abb..d1f6297f 100644 --- a/swh/web/browse/views/origin.py +++ b/swh/web/browse/views/origin.py @@ -1,896 +1,829 @@ # Copyright (C) 2017-2018 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information import json from distutils.util import strtobool from django.http import HttpResponse from django.shortcuts import render from django.utils.safestring import mark_safe from django.template.defaultfilters import filesizeformat from swh.web.common import service from swh.web.common.utils import ( gen_path_info, reverse, format_utc_iso_date, parse_timestamp ) from swh.web.common.exc import NotFoundExc, handle_view_exception from swh.web.browse.utils import ( get_origin_visits, get_directory_entries, request_content, prepare_content_for_display, prepare_revision_log_for_display, get_origin_context, gen_directory_link, gen_revision_link, gen_revision_log_link, gen_content_link, gen_origin_directory_link, content_display_max_size ) from swh.web.browse.browseurls import browse_route def _branch_not_found(origin_info, timestamp, branch_type, branch, branches, visit_id=None): """ Utility function to raise an exception when a specified branch/release can not be found. """ if branch_type: occ_type = 'Branch' occ_type_plural = 'branches' else: occ_type = 'Release' occ_type_plural = 'releases' if visit_id: if len(branches) == 0: raise NotFoundExc('Origin with type %s and url %s' ' for visit with id %s has an empty list' ' of %s!' % (origin_info['type'], origin_info['url'], visit_id, occ_type_plural)) else: raise NotFoundExc('%s %s associated to visit with' ' id %s for origin with type %s and url %s' ' not found!' % (occ_type, branch, visit_id, origin_info['type'], origin_info['url'])) else: if len(branches) == 0: raise NotFoundExc('Origin with type %s and url %s' ' for visit with timestamp %s has an empty list' ' of %s!' % (origin_info['type'], origin_info['url'], timestamp, occ_type_plural)) else: raise NotFoundExc('%s %s associated to visit with' ' timestamp %s for origin with type %s' ' and url %s not found!' % (occ_type, branch, timestamp, origin_info['type'], origin_info['url'])) def _get_branch(branches, branch_name): """ Utility function to get a specific branch from an origin branches list. Its purpose is to get the default HEAD branch as some SWH origin (e.g those with svn type) does not have it. In that latter case, check if there is a master branch instead and returns it. """ filtered_branches = \ [b for b in branches if b['name'].endswith(branch_name)] if len(filtered_branches) > 0: return filtered_branches[0] elif branch_name == 'HEAD': filtered_branches = \ [b for b in branches if b['name'].endswith('master')] if len(filtered_branches) > 0: return filtered_branches[0] elif len(branches) > 0: return branches[0] return None def _get_release(releases, release_name): filtered_releases = \ [r for r in releases if r['name'] == release_name] if len(filtered_releases) > 0: return filtered_releases[0] else: return None def _process_origin_request(request, origin_type, origin_url, timestamp, path, browse_view_name): """ Utility function to perform common input request processing for origin context views. """ visit_id = request.GET.get('visit_id', None) origin_context = get_origin_context(origin_type, origin_url, timestamp, visit_id) for b in origin_context['branches']: branch_url_args = dict(origin_context['url_args']) if path: b['path'] = path branch_url_args['path'] = path b['url'] = reverse(browse_view_name, kwargs=branch_url_args, query_params={'branch': b['name'], 'visit_id': visit_id}) for r in origin_context['releases']: release_url_args = dict(origin_context['url_args']) if path: r['path'] = path release_url_args['path'] = path r['url'] = reverse(browse_view_name, kwargs=release_url_args, query_params={'release': r['name'], 'visit_id': visit_id}) root_sha1_git = None query_params = origin_context['query_params'] revision_id = request.GET.get('revision', None) release_name = request.GET.get('release', None) branch_name = None if revision_id: revision = service.lookup_revision(revision_id) root_sha1_git = revision['directory'] origin_context['branches'].append({'name': revision_id, 'revision': revision_id, 'directory': root_sha1_git, 'url': None}) branch_name = revision_id query_params['revision'] = revision_id elif release_name: release = _get_release(origin_context['releases'], release_name) if release: root_sha1_git = release['directory'] query_params['release'] = release_name revision_id = release['target'] else: _branch_not_found(origin_context['origin_info'], timestamp, False, release_name, origin_context['releases'], visit_id) else: branch_name = request.GET.get('branch', None) if branch_name: query_params['branch'] = branch_name branch = _get_branch(origin_context['branches'], branch_name or 'HEAD') if branch: branch_name = branch['name'] root_sha1_git = branch['directory'] revision_id = branch['revision'] else: _branch_not_found(origin_context['origin_info'], timestamp, True, branch_name, origin_context['branches'], visit_id) origin_context['root_sha1_git'] = root_sha1_git origin_context['revision_id'] = revision_id origin_context['branch'] = branch_name origin_context['release'] = release_name return origin_context @browse_route(r'origin/(?P[a-z]+)/url/(?P.+)/visit/(?P.+)/directory/', # noqa r'origin/(?P[a-z]+)/url/(?P.+)/visit/(?P.+)/directory/(?P.+)/', # noqa r'origin/(?P[a-z]+)/url/(?P.+)/directory/', # noqa r'origin/(?P[a-z]+)/url/(?P.+)/directory/(?P.+)/', # noqa view_name='browse-origin-directory') def origin_directory_browse(request, origin_type, origin_url, timestamp=None, path=None): """Django view for browsing the content of a SWH directory associated to an origin for a given visit. The url scheme that points to it is the following: * :http:get:`/browse/origin/(origin_type)/url/(origin_url)/directory/[(path)/]` * :http:get:`/browse/origin/(origin_type)/url/(origin_url)/visit/(timestamp)/directory/[(path)/]` - - Args: - request: input django http request - origin_type: the type of swh origin (git, svn, hg, ...) - origin_url: the url of the swh origin - timestamp: optional swh visit timestamp parameter - (the last one will be used by default) - path: optional path parameter used to navigate in directories - reachable from the origin root one - branch: optional query parameter that specifies the origin branch - from which to retrieve the directory - release: optional query parameter that specifies the origin release - from which to retrieve the directory - revision: optional query parameter to specify the origin revision - from which to retrieve the directory - - Returns: - The HTML rendering for the content of the directory associated - to the provided origin and visit. """ # noqa try: origin_context = _process_origin_request( request, origin_type, origin_url, timestamp, path, 'browse-origin-directory') root_sha1_git = origin_context['root_sha1_git'] sha1_git = root_sha1_git if path: dir_info = service.lookup_directory_with_path(root_sha1_git, path) sha1_git = dir_info['target'] dirs, files = get_directory_entries(sha1_git) except Exception as exc: return handle_view_exception(request, exc) origin_info = origin_context['origin_info'] visit_info = origin_context['visit_info'] url_args = origin_context['url_args'] query_params = origin_context['query_params'] revision_id = origin_context['revision_id'] path_info = gen_path_info(path) breadcrumbs = [] breadcrumbs.append({'name': root_sha1_git[:7], 'url': reverse('browse-origin-directory', kwargs=url_args, query_params=query_params)}) for pi in path_info: bc_url_args = dict(url_args) bc_url_args['path'] = pi['path'] breadcrumbs.append({'name': pi['name'], 'url': reverse('browse-origin-directory', kwargs=bc_url_args, query_params=query_params)}) path = '' if path is None else (path + '/') for d in dirs: bc_url_args = dict(url_args) bc_url_args['path'] = path + d['name'] d['url'] = reverse('browse-origin-directory', kwargs=bc_url_args, query_params=query_params) sum_file_sizes = 0 readme_name = None readme_url = None for f in files: bc_url_args = dict(url_args) bc_url_args['path'] = path + f['name'] f['url'] = reverse('browse-origin-content', kwargs=bc_url_args, query_params=query_params) sum_file_sizes += f['length'] f['length'] = filesizeformat(f['length']) if f['name'].lower().startswith('readme'): readme_name = f['name'] readme_sha1 = f['checksums']['sha1'] readme_url = reverse('browse-content-raw', kwargs={'query_string': readme_sha1}) history_url = reverse('browse-origin-log', kwargs=url_args, query_params=query_params) sum_file_sizes = filesizeformat(sum_file_sizes) browse_dir_link = \ gen_directory_link(sha1_git, link_text='Browse', link_attrs={'class': 'btn btn-md btn-swh', 'role': 'button'}) browse_rev_link = \ gen_revision_link(revision_id, origin_context=origin_context, link_text='Browse', link_attrs={'class': 'btn btn-md btn-swh', 'role': 'button'}) dir_metadata = {'id': sha1_git, 'context-independent directory': browse_dir_link, 'number of regular files': len(files), 'number of subdirectories': len(dirs), 'sum of regular file sizes': sum_file_sizes, 'origin id': origin_info['id'], 'origin type': origin_info['type'], 'origin url': origin_info['url'], 'origin visit date': format_utc_iso_date(visit_info['date']), # noqa 'origin visit id': visit_info['visit'], 'path': '/' + path, 'revision id': revision_id, 'revision': browse_rev_link} vault_cooking = { 'directory_context': True, 'directory_id': sha1_git, 'revision_context': True, 'revision_id': revision_id } return render(request, 'directory.html', {'empty_browse': False, 'heading': 'Directory information', 'top_panel_visible': True, 'top_panel_collapsible': True, 'top_panel_text': 'SWH object: Directory', 'swh_object_metadata': dir_metadata, 'main_panel_visible': True, 'dirs': dirs, 'files': files, 'breadcrumbs': breadcrumbs, 'top_right_link': history_url, 'top_right_link_text': mark_safe( '' 'History' ), 'readme_name': readme_name, 'readme_url': readme_url, 'origin_context': origin_context, 'vault_cooking': vault_cooking, 'show_actions_menu': True}) @browse_route(r'origin/(?P[a-z]+)/url/(?P.+)/visit/(?P.+)/content/(?P.+)/', # noqa r'origin/(?P[a-z]+)/url/(?P.+)/content/(?P.+)/', # noqa view_name='browse-origin-content') def origin_content_display(request, origin_type, origin_url, path, timestamp=None): """Django view that produces an HTML display of a SWH content associated to an origin for a given visit. The url scheme that points to it is the following: * :http:get:`/browse/origin/(origin_type)/url/(origin_url)/content/(path)/` * :http:get:`/browse/origin/(origin_type)/url/(origin_url)/visit/(timestamp)/content/(path)/` - Args: - request: input django http request - origin_type: the type of swh origin (git, svn, hg, ...) - origin_url: the url of the swh origin - path: path of the content relative to the origin root directory - timestamp: optional swh visit timestamp parameter - (the last one will be used by default) - branch: optional query parameter that specifies the origin branch - from which to retrieve the content - release: optional query parameter that specifies the origin release - from which to retrieve the content - revision: optional query parameter to specify the origin revision - from which to retrieve the content - - Returns: - The HTML rendering of the requested content associated to - the provided origin and visit. - """ # noqa try: origin_context = _process_origin_request( request, origin_type, origin_url, timestamp, path, 'browse-origin-content') root_sha1_git = origin_context['root_sha1_git'] content_info = service.lookup_directory_with_path(root_sha1_git, path) sha1_git = content_info['target'] query_string = 'sha1_git:' + sha1_git content_data = request_content(query_string) except Exception as exc: return handle_view_exception(request, exc) url_args = origin_context['url_args'] query_params = origin_context['query_params'] revision_id = origin_context['revision_id'] origin_info = origin_context['origin_info'] visit_info = origin_context['visit_info'] content = None language = None if content_data['raw_data'] is not None: content_display_data = prepare_content_for_display( content_data['raw_data'], content_data['mimetype'], path) content = content_display_data['content_data'] language = content_display_data['language'] filename = None path_info = None breadcrumbs = [] split_path = path.split('/') filename = split_path[-1] path = path[:-len(filename)] path_info = gen_path_info(path) breadcrumbs.append({'name': root_sha1_git[:7], 'url': reverse('browse-origin-directory', kwargs=url_args, query_params=query_params)}) for pi in path_info: bc_url_args = dict(url_args) bc_url_args['path'] = pi['path'] breadcrumbs.append({'name': pi['name'], 'url': reverse('browse-origin-directory', kwargs=bc_url_args, query_params=query_params)}) breadcrumbs.append({'name': filename, 'url': None}) browse_content_link = \ gen_content_link(sha1_git, link_text='Browse', link_attrs={'class': 'btn btn-md btn-swh', 'role': 'button'}) content_raw_url = reverse('browse-content-raw', kwargs={'query_string': query_string}, query_params={'filename': filename}) browse_rev_link = \ gen_revision_link(revision_id, origin_context=origin_context, link_text='Browse', link_attrs={'class': 'btn btn-md btn-swh', 'role': 'button'}) content_metadata = { 'context-independent content': browse_content_link, 'sha1 checksum': content_data['checksums']['sha1'], 'sha1_git checksum': content_data['checksums']['sha1_git'], 'sha256 checksum': content_data['checksums']['sha256'], 'blake2s256 checksum': content_data['checksums']['blake2s256'], 'mime type': content_data['mimetype'], 'encoding': content_data['encoding'], 'size': filesizeformat(content_data['length']), 'language': content_data['language'], 'licenses': content_data['licenses'], 'origin id': origin_info['id'], 'origin type': origin_info['type'], 'origin url': origin_info['url'], 'origin visit date': format_utc_iso_date(visit_info['date']), 'origin visit id': visit_info['visit'], 'path': '/' + path, 'filename': filename, 'revision id': revision_id, 'revision': browse_rev_link } return render(request, 'content.html', {'empty_browse': False, 'heading': 'Content information', 'top_panel_visible': True, 'top_panel_collapsible': True, 'top_panel_text': 'SWH object: Content', 'swh_object_metadata': content_metadata, 'main_panel_visible': True, 'content': content, 'content_size': content_data['length'], 'max_content_size': content_display_max_size, 'mimetype': content_data['mimetype'], 'language': language, 'breadcrumbs': breadcrumbs, 'top_right_link': content_raw_url, 'top_right_link_text': mark_safe( 'Raw File'), 'origin_context': origin_context, 'vault_cooking': None, 'show_actions_menu': False}) PER_PAGE = 20 @browse_route(r'origin/(?P[a-z]+)/url/(?P.+)/visit/(?P.+)/log/', # noqa r'origin/(?P[a-z]+)/url/(?P.+)/log/', view_name='browse-origin-log') def origin_log_browse(request, origin_type, origin_url, timestamp=None): """Django view that produces an HTML display of revisions history (aka the commit log) associated to a SWH origin. The url scheme that points to it is the following: * :http:get:`/browse/origin/(origin_type)/url/(origin_url)/log/` * :http:get:`/browse/origin/(origin_type)/url/(origin_url)/visit/(timestamp)/log/` - - Args: - request: input django http request - origin_type: the type of swh origin (git, svn, hg, ...) - origin_url: the url of the swh origin - timestamp: optional visit timestamp parameter - (the last one will be used by default) - revs_breadcrumb: query parameter used internally to store - the navigation breadcrumbs (i.e. the list of descendant revisions - visited so far). - per_page: optional query parameter used to specify the number of - log entries per page - branch: optional query parameter that specifies the origin branch - from which to retrieve the commit log - release: optional query parameter that specifies the origin release - from which to retrieve the commit log - revision: optional query parameter to specify the origin revision - from which to retrieve the commit log - - Returns: - The HTML rendering of revisions history for a given SWH visit. - """ # noqa try: origin_context = _process_origin_request( request, origin_type, origin_url, timestamp, None, 'browse-origin-log') revision_id = origin_context['revision_id'] current_rev = revision_id per_page = int(request.GET.get('per_page', PER_PAGE)) revs_breadcrumb = request.GET.get('revs_breadcrumb', None) if revs_breadcrumb: current_rev = revs_breadcrumb.split('/')[-1] revision_log = service.lookup_revision_log(current_rev, limit=per_page+1) revision_log = list(revision_log) except Exception as exc: return handle_view_exception(request, exc) origin_info = origin_context['origin_info'] visit_info = origin_context['visit_info'] url_args = origin_context['url_args'] query_params = origin_context['query_params'] query_params['per_page'] = per_page revision_log_display_data = prepare_revision_log_for_display( revision_log, per_page, revs_breadcrumb, origin_context) prev_rev = revision_log_display_data['prev_rev'] prev_revs_breadcrumb = revision_log_display_data['prev_revs_breadcrumb'] prev_log_url = None query_params['revs_breadcrumb'] = prev_revs_breadcrumb if prev_rev: prev_log_url = \ reverse('browse-origin-log', kwargs=url_args, query_params=query_params) next_rev = revision_log_display_data['next_rev'] next_revs_breadcrumb = revision_log_display_data['next_revs_breadcrumb'] next_log_url = None query_params['revs_breadcrumb'] = next_revs_breadcrumb if next_rev: next_log_url = \ reverse('browse-origin-log', kwargs=url_args, query_params=query_params) revision_log_data = revision_log_display_data['revision_log_data'] for i, log in enumerate(revision_log_data): params = { 'revision': revision_log[i]['id'], } if 'visit_id' in query_params: params['visit_id'] = query_params['visit_id'] log['directory'] = gen_origin_directory_link( origin_context, revision_log[i]['id'], link_text='Browse files', link_attrs={'class': 'btn btn-md btn-swh', 'role': 'button'}) browse_log_link = \ gen_revision_log_link(revision_id, link_text='Browse', link_attrs={'class': 'btn btn-md btn-swh', 'role': 'button'}) revision_metadata = { 'context-independent revision history': browse_log_link, 'origin id': origin_info['id'], 'origin type': origin_info['type'], 'origin url': origin_info['url'], 'origin visit date': format_utc_iso_date(visit_info['date']), 'origin visit id': visit_info['visit'] } return render(request, 'revision-log.html', {'empty_browse': False, 'heading': 'Revision history information', 'top_panel_visible': True, 'top_panel_collapsible': True, 'top_panel_text': 'SWH object: Revision history', 'swh_object_metadata': revision_metadata, 'main_panel_visible': True, 'revision_log': revision_log_data, 'next_log_url': next_log_url, 'prev_log_url': prev_log_url, 'breadcrumbs': None, 'top_right_link': None, 'top_right_link_text': None, 'origin_context': origin_context, 'vault_cooking': None, 'show_actions_menu': False}) @browse_route(r'origin/(?P[a-z]+)/url/(?P.+)/visit/(?P.+)/branches/', # noqa r'origin/(?P[a-z]+)/url/(?P.+)/branches/', # noqa view_name='browse-origin-branches') def origin_branches_browse(request, origin_type, origin_url, timestamp=None): """Django view that produces an HTML display of the list of branches associated to an origin for a given visit. The url scheme that points to it is the following: * :http:get:`/browse/origin/(origin_type)/url/(origin_url)/branches/` * :http:get:`/browse/origin/(origin_type)/url/(origin_url)/visit/(timestamp)/branches/` """ # noqa try: origin_context = _process_origin_request( request, origin_type, origin_url, timestamp, None, 'browse-origin-directory') except Exception as exc: return handle_view_exception(request, exc) branches_offset = int(request.GET.get('branches_offset', 0)) origin_info = origin_context['origin_info'] url_args = origin_context['url_args'] query_params = origin_context['query_params'] branches = origin_context['branches'] displayed_branches = \ branches[branches_offset:branches_offset+PER_PAGE] for branch in displayed_branches: revision_url = reverse( 'browse-revision', kwargs={'sha1_git': branch['revision']}, query_params={'origin_type': origin_info['type'], 'origin_url': origin_info['url']}) query_params['branch'] = branch['name'] directory_url = reverse('browse-origin-directory', kwargs=url_args, query_params=query_params) del query_params['branch'] branch['revision_url'] = revision_url branch['directory_url'] = directory_url prev_branches_url = None next_branches_url = None next_offset = branches_offset + PER_PAGE prev_offset = branches_offset - PER_PAGE if next_offset < len(branches): query_params['branches_offset'] = next_offset next_branches_url = reverse('browse-origin-branches', kwargs=url_args, query_params=query_params) query_params['branches_offset'] = None if prev_offset >= 0: if prev_offset != 0: query_params['branches_offset'] = prev_offset prev_branches_url = reverse('browse-origin-branches', kwargs=url_args, query_params=query_params) return render(request, 'branches.html', {'empty_browse': False, 'heading': 'Origin branches list', 'top_panel_visible': False, 'top_panel_collapsible': False, 'top_panel_text': 'SWH object: Origin branches list', 'swh_object_metadata': {}, 'main_panel_visible': True, 'top_right_link': None, 'top_right_link_text': None, 'displayed_branches': displayed_branches, 'prev_branches_url': prev_branches_url, 'next_branches_url': next_branches_url, 'origin_context': origin_context}) @browse_route(r'origin/(?P[a-z]+)/url/(?P.+)/visit/(?P.+)/releases/', # noqa r'origin/(?P[a-z]+)/url/(?P.+)/releases/', # noqa view_name='browse-origin-releases') def origin_releases_browse(request, origin_type, origin_url, timestamp=None): """Django view that produces an HTML display of the list of releases associated to an origin for a given visit. The url scheme that points to it is the following: * :http:get:`/browse/origin/(origin_type)/url/(origin_url)/releases/` * :http:get:`/browse/origin/(origin_type)/url/(origin_url)/visit/(timestamp)/releases/` """ # noqa try: origin_context = _process_origin_request( request, origin_type, origin_url, timestamp, None, 'browse-origin-directory') except Exception as exc: return handle_view_exception(request, exc) releases_offset = int(request.GET.get('releases_offset', 0)) origin_info = origin_context['origin_info'] url_args = origin_context['url_args'] query_params = origin_context['query_params'] releases = origin_context['releases'] displayed_releases = \ releases[releases_offset:releases_offset+PER_PAGE] for release in displayed_releases: release_url = reverse('browse-release', kwargs={'sha1_git': release['id']}, query_params={'origin_type': origin_info['type'], 'origin_url': origin_info['url']}) query_params['release'] = release['name'] del query_params['release'] release['release_url'] = release_url prev_releases_url = None next_releases_url = None next_offset = releases_offset + PER_PAGE prev_offset = releases_offset - PER_PAGE if next_offset < len(releases): query_params['releases_offset'] = next_offset next_releases_url = reverse('browse-origin-releases', kwargs=url_args, query_params=query_params) query_params['releases_offset'] = None if prev_offset >= 0: if prev_offset != 0: query_params['releases_offset'] = prev_offset prev_releases_url = reverse('browse-origin-releases', kwargs=url_args, query_params=query_params) return render(request, 'releases.html', {'empty_browse': False, 'heading': 'Origin releases list', 'top_panel_visible': False, 'top_panel_collapsible': False, 'top_panel_text': 'SWH object: Origin releases list', 'swh_object_metadata': {}, 'main_panel_visible': True, 'top_right_link': None, 'top_right_link_text': None, 'displayed_releases': displayed_releases, 'prev_releases_url': prev_releases_url, 'next_releases_url': next_releases_url, 'origin_context': origin_context, 'vault_cooking': None, 'show_actions_menu': False}) @browse_route(r'origin/(?P[a-z]+)/url/(?P.+)/', view_name='browse-origin') def origin_browse(request, origin_type=None, origin_url=None): """Django view that produces an HTML display of a swh origin identified by its id or its url. The url scheme that points to it is :http:get:`/browse/origin/(origin_type)/url/(origin_url)/`. - - Args: - request: input django http request - origin_type: type of origin (git, svn, ...) - origin_url: url of the origin (e.g. https://github.com//) - - Returns: - The HMTL rendering for the metadata of the provided origin. """ # noqa try: origin_info = service.lookup_origin({ 'type': origin_type, 'url': origin_url }) origin_visits = get_origin_visits(origin_info) origin_visits.reverse() except Exception as exc: return handle_view_exception(request, exc) origin_info['last swh visit browse url'] = \ reverse('browse-origin-directory', kwargs={'origin_type': origin_type, 'origin_url': origin_url}) origin_visits_data = [] visits_splitted = [] visits_by_year = {} for i, visit in enumerate(origin_visits): visit_date = parse_timestamp(visit['date']) visit_year = str(visit_date.year) url_date = format_utc_iso_date(visit['date'], '%Y-%m-%dT%H:%M:%SZ') visit['fmt_date'] = format_utc_iso_date(visit['date']) query_params = {} if i < len(origin_visits) - 1: if visit['date'] == origin_visits[i+1]['date']: query_params = {'visit_id': visit['visit']} if i > 0: if visit['date'] == origin_visits[i-1]['date']: query_params = {'visit_id': visit['visit']} visit['browse_url'] = reverse('browse-origin-directory', kwargs={'origin_type': origin_type, 'origin_url': origin_url, 'timestamp': url_date}, query_params=query_params) origin_visits_data.insert(0, {'date': visit_date.timestamp()}) if visit_year not in visits_by_year: # display 3 years by row in visits list view if len(visits_by_year) == 3: visits_splitted.append(visits_by_year) visits_by_year = {} visits_by_year[visit_year] = [] visits_by_year[visit_year].append(visit) if len(visits_by_year) > 0: visits_splitted.append(visits_by_year) return render(request, 'origin.html', {'empty_browse': False, 'heading': 'Origin information', 'top_panel_visible': False, 'top_panel_collapsible': False, 'top_panel_text': 'SWH object: Visits history', 'swh_object_metadata': origin_info, 'main_panel_visible': True, 'origin_visits_data': origin_visits_data, 'visits_splitted': visits_splitted, 'origin_info': origin_info, 'browse_url_base': '/browse/origin/%s/url/%s/' % (origin_type, origin_url), 'vault_cooking': None, 'show_actions_menu': False}) @browse_route(r'origin/search/(?P.+)/', view_name='browse-origin-search') def origin_search(request, url_pattern): """Search for origins whose urls contain a provided string pattern or match a provided regular expression. The search is performed in a case insensitive way. """ offset = int(request.GET.get('offset', '0')) limit = int(request.GET.get('limit', '50')) regexp = request.GET.get('regexp', 'false') results = service.search_origin(url_pattern, offset, limit, bool(strtobool(regexp))) results = json.dumps(list(results), sort_keys=True, indent=4, separators=(',', ': ')) return HttpResponse(results, content_type='application/json') @browse_route(r'origin/(?P[0-9]+)/latest_snapshot/', view_name='browse-origin-latest-snapshot') def _origin_latest_snapshot(request, origin_id): """ Internal browse endpoint used to check if an origin has already been visited by Software Heritage and has at least one full visit. """ result = service.lookup_latest_origin_snapshot(origin_id, allowed_statuses=['full']) result = json.dumps(result, sort_keys=True, indent=4, separators=(',', ': ')) return HttpResponse(result, content_type='application/json') diff --git a/swh/web/browse/views/person.py b/swh/web/browse/views/person.py index 9b082dbc..f0953d78 100644 --- a/swh/web/browse/views/person.py +++ b/swh/web/browse/views/person.py @@ -1,43 +1,36 @@ # Copyright (C) 2017-2018 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information from django.shortcuts import render from swh.web.common import service from swh.web.common.exc import handle_view_exception from swh.web.browse.browseurls import browse_route @browse_route(r'person/(?P[0-9]+)/', view_name='browse-person') def person_browse(request, person_id): """ Django view that produces an HTML display of a swh person identified by its id. The url that points to it is :http:get:`/browse/person/(person_id)/`. - - Args: - request: input django http request - person_id (int): a swh person id - - Returns: - The HMTL rendering for the metadata of the provided person. """ try: person = service.lookup_person(person_id) except Exception as exc: return handle_view_exception(request, exc) return render(request, 'person.html', {'empty_browse': False, 'heading': 'Person information', 'top_panel_visible': True, 'top_panel_collapsible': False, 'top_panel_text': 'SWH object: Person', 'swh_object_metadata': person, 'main_panel_visible': False, 'vault_cooking': None, 'show_actions_menu': False}) diff --git a/swh/web/browse/views/release.py b/swh/web/browse/views/release.py index d926f1e8..a453c761 100644 --- a/swh/web/browse/views/release.py +++ b/swh/web/browse/views/release.py @@ -1,99 +1,92 @@ # Copyright (C) 2017-2018 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information from django.shortcuts import render from django.utils.safestring import mark_safe from swh.web.common import service from swh.web.common.utils import reverse, format_utc_iso_date from swh.web.common.exc import handle_view_exception from swh.web.browse.browseurls import browse_route from swh.web.browse.utils import ( gen_person_link, gen_revision_link, get_origin_context, gen_link ) @browse_route(r'release/(?P[0-9a-f]+)/', view_name='browse-release') def release_browse(request, sha1_git): """ Django view that produces an HTML display of a SWH release identified by its id. The url that points to it is :http:get:`/browse/release/(sha1_git)/`. - - Args: - request: input django http request - sha1_git: a SWH release id - - Returns: - The HMTL rendering for the metadata of the provided release. """ try: release = service.lookup_release(sha1_git) origin_context = None origin_type = request.GET.get('origin_type', None) origin_url = request.GET.get('origin_url', None) timestamp = request.GET.get('timestamp', None) visit_id = request.GET.get('visit_id', None) if origin_type and origin_url: origin_context = get_origin_context(origin_type, origin_url, timestamp, visit_id) except Exception as exc: return handle_view_exception(request, exc) release_data = {} release_data['author'] = gen_person_link( release['author']['id'], release['author']['name']) release_data['date'] = format_utc_iso_date(release['date']) release_data['id'] = sha1_git release_data['name'] = release['name'] release_data['synthetic'] = release['synthetic'] release_data['target type'] = release['target_type'] if release['target_type'] == 'revision': release_data['target'] = \ gen_revision_link(release['target'], origin_context=origin_context) elif release['target_type'] == 'content': content_url = \ reverse('browse-content', kwargs={'query_string': 'sha1_git:' + release['target']}) release_data['target'] = gen_link(content_url, release['target']) elif release['target_type'] == 'directory': directory_url = \ reverse('browse-directory', kwargs={'sha1_git': release['target']}) release_data['target'] = gen_link(directory_url, release['target']) elif release['target_type'] == 'release': release_url = \ reverse('browse-release', kwargs={'sha1_git': release['target']}) release_data['target'] = gen_link(release_url, release['target']) release_note_lines = release['message'].split('\n') release_target_link = 'Target: ' if release['target_type'] == 'revision': release_target_link += '' # noqa else: release_target_link += release['target_type'] release_target_link += ' ' + release_data['target'] return render(request, 'release.html', {'empty_browse': False, 'heading': 'Release information', 'top_panel_visible': True, 'top_panel_collapsible': True, 'top_panel_text': 'SWH object: Release', 'swh_object_metadata': release_data, 'main_panel_visible': True, 'release_name': release['name'], 'release_note_header': release_note_lines[0], 'release_note_body': '\n'.join(release_note_lines[1:]), 'release_target_link': mark_safe(release_target_link), 'origin_context': origin_context}) diff --git a/swh/web/browse/views/revision.py b/swh/web/browse/views/revision.py index dfbe5c4f..0a12dcf0 100644 --- a/swh/web/browse/views/revision.py +++ b/swh/web/browse/views/revision.py @@ -1,462 +1,448 @@ # Copyright (C) 2017-2018 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information import hashlib import json from django.http import HttpResponse from django.shortcuts import render from django.template.defaultfilters import filesizeformat from django.utils.safestring import mark_safe from swh.web.common import service from swh.web.common.utils import reverse, format_utc_iso_date, gen_path_info from swh.web.common.exc import handle_view_exception from swh.web.browse.browseurls import browse_route from swh.web.browse.utils import ( gen_link, gen_person_link, gen_revision_link, prepare_revision_log_for_display, get_origin_context, gen_origin_directory_link, get_revision_log_url, get_directory_entries, gen_directory_link, request_content, prepare_content_for_display, content_display_max_size ) def _gen_content_url(revision, query_string, path, origin_context): if origin_context: url_args = origin_context['url_args'] url_args['path'] = path query_params = origin_context['query_params'] query_params['revision'] = revision['id'] content_url = reverse('browse-origin-content', kwargs=url_args, query_params=query_params) else: content_path = '%s/%s' % (revision['directory'], path) content_url = reverse('browse-content', kwargs={'query_string': query_string}, query_params={'path': content_path}) return content_url def _gen_diff_link(idx, diff_anchor, link_text): if idx < _max_displayed_file_diffs: return gen_link(diff_anchor, link_text) else: return link_text # TODO: put in conf _max_displayed_file_diffs = 1000 def _gen_revision_changes_list(revision, changes, origin_context): """ Returns a HTML string describing the file changes introduced in a revision. As this string will be displayed in the browse revision view, links to adequate file diffs are also generated. Args: revision (str): hexadecimal representation of a revision identifier changes (list): list of file changes in the revision origin_context (dict): optional origin context used to reverse the content urls Returns: A string to insert in a revision HTML view. """ changes_msg = [] for i, change in enumerate(changes): hasher = hashlib.sha1() from_query_string = '' to_query_string = '' diff_id = 'diff-' if change['from']: from_query_string = 'sha1_git:' + change['from']['target'] diff_id += change['from']['target'] + '-' + change['from_path'] diff_id += '-' if change['to']: to_query_string = 'sha1_git:' + change['to']['target'] diff_id += change['to']['target'] + change['to_path'] change['path'] = change['to_path'] or change['from_path'] url_args = {'from_query_string': from_query_string, 'to_query_string': to_query_string} query_params = {'path': change['path']} change['diff_url'] = reverse('diff-contents', kwargs=url_args, query_params=query_params) hasher.update(diff_id.encode('utf-8')) diff_id = hasher.hexdigest() change['id'] = diff_id panel_diff_link = '#panel_' + diff_id if change['type'] == 'modify': change['content_url'] = \ _gen_content_url(revision, to_query_string, change['to_path'], origin_context) changes_msg.append('modified: %s' % _gen_diff_link(i, panel_diff_link, change['to_path'])) elif change['type'] == 'insert': change['content_url'] = \ _gen_content_url(revision, to_query_string, change['to_path'], origin_context) changes_msg.append('new file: %s' % _gen_diff_link(i, panel_diff_link, change['to_path'])) elif change['type'] == 'delete': parent = service.lookup_revision(revision['parents'][0]) change['content_url'] = \ _gen_content_url(parent, from_query_string, change['from_path'], origin_context) changes_msg.append('deleted: %s' % _gen_diff_link(i, panel_diff_link, change['from_path'])) elif change['type'] == 'rename': change['content_url'] = \ _gen_content_url(revision, to_query_string, change['to_path'], origin_context) link_text = change['from_path'] + ' → ' + change['to_path'] changes_msg.append('renamed: %s' % _gen_diff_link(i, panel_diff_link, link_text)) if not changes: changes_msg.append('No changes') return mark_safe('\n'.join(changes_msg)) @browse_route(r'revision/(?P[0-9a-f]+)/diff/', view_name='diff-revision') def _revision_diff(request, sha1_git): """ Browse internal endpoint to compute revision diff """ try: revision = service.lookup_revision(sha1_git) origin_context = None origin_type = request.GET.get('origin_type', None) origin_url = request.GET.get('origin_url', None) timestamp = request.GET.get('timestamp', None) visit_id = request.GET.get('visit_id', None) if origin_type and origin_url: origin_context = get_origin_context(origin_type, origin_url, timestamp, visit_id) except Exception as exc: return handle_view_exception(request, exc) changes = service.diff_revision(sha1_git) changes_msg = _gen_revision_changes_list(revision, changes, origin_context) diff_data = { 'total_nb_changes': len(changes), 'changes': changes[:_max_displayed_file_diffs], 'changes_msg': changes_msg } diff_data_json = json.dumps(diff_data, separators=(',', ': ')) return HttpResponse(diff_data_json, content_type='application/json') @browse_route(r'revision/(?P[0-9a-f]+)/', view_name='browse-revision') def revision_browse(request, sha1_git): """ Django view that produces an HTML display of a SWH revision identified by its id. The url that points to it is :http:get:`/browse/revision/(sha1_git)/`. - - Args: - request: input django http request - sha1_git: a SWH revision id - - Returns: - The HMTL rendering for the metadata of the provided revision. """ try: revision = service.lookup_revision(sha1_git) origin_info = None origin_context = None origin_type = request.GET.get('origin_type', None) origin_url = request.GET.get('origin_url', None) timestamp = request.GET.get('timestamp', None) visit_id = request.GET.get('visit_id', None) path = request.GET.get('path', None) dir_id = None dirs, files = None, None content_data = None if origin_type and origin_url: origin_context = get_origin_context(origin_type, origin_url, timestamp, visit_id) origin_info = origin_context['origin_info'] if path: path_info = \ service.lookup_directory_with_path(revision['directory'], path) if path_info['type'] == 'dir': dir_id = path_info['target'] else: query_string = 'sha1_git:' + path_info['target'] content_data = request_content(query_string) else: dir_id = revision['directory'] if dir_id: path = '' if path is None else (path + '/') dirs, files = get_directory_entries(dir_id) except Exception as exc: return handle_view_exception(request, exc) revision_data = {} revision_data['author'] = gen_person_link( revision['author']['id'], revision['author']['name']) revision_data['committer'] = gen_person_link( revision['committer']['id'], revision['committer']['name']) revision_data['committer date'] = format_utc_iso_date( revision['committer_date']) revision_data['date'] = format_utc_iso_date(revision['date']) if origin_context: revision_data['directory'] = \ gen_origin_directory_link(origin_context, sha1_git, link_text='Browse', link_attrs={'class': 'btn btn-md btn-swh', # noqa 'role': 'button'}) else: revision_data['directory'] = \ gen_directory_link(revision['directory'], link_text='Browse', link_attrs={'class': 'btn btn-md btn-swh', 'role': 'button'}) revision_data['id'] = sha1_git revision_data['merge'] = revision['merge'] revision_data['metadata'] = json.dumps(revision['metadata'], sort_keys=True, indent=4, separators=(',', ': ')) if origin_info: revision_data['context-independent revision'] = \ gen_revision_link(sha1_git, link_text='Browse', link_attrs={'class': 'btn btn-md btn-swh', 'role': 'button'}) revision_data['origin id'] = origin_info['id'] revision_data['origin type'] = origin_info['type'] revision_data['origin url'] = gen_link(origin_info['url'], origin_info['url']) parents = '' for p in revision['parents']: parent_link = gen_revision_link(p, origin_context=origin_context) parents += parent_link + '
' revision_data['parents'] = mark_safe(parents) revision_data['synthetic'] = revision['synthetic'] revision_data['type'] = revision['type'] message_lines = revision['message'].split('\n') parents_links = '%s parent%s ' % \ (len(revision['parents']), '' if len(revision['parents']) == 1 else 's') parents_links += ' ' for p in revision['parents']: parent_link = gen_revision_link(p, shorten_id=True, origin_context=origin_context) parents_links += parent_link if p != revision['parents'][-1]: parents_links += ' + ' path_info = gen_path_info(path) query_params = {'origin_type': origin_type, 'origin_url': origin_url, 'timestamp': timestamp, 'visit_id': visit_id} breadcrumbs = [] breadcrumbs.append({'name': revision['directory'][:7], 'url': reverse('browse-revision', kwargs={'sha1_git': sha1_git}, query_params=query_params)}) for pi in path_info: query_params['path'] = pi['path'] breadcrumbs.append({'name': pi['name'], 'url': reverse('browse-revision', kwargs={'sha1_git': sha1_git}, query_params=query_params)}) vault_cooking = { 'directory_context': False, 'directory_id': None, 'revision_context': True, 'revision_id': sha1_git } content = None content_size = None mimetype = None language = None readme_name = None readme_url = None if content_data: breadcrumbs[-1]['url'] = None content_size = content_data['length'] mimetype = content_data['mimetype'] if content_data['raw_data']: content_display_data = prepare_content_for_display( content_data['raw_data'], content_data['mimetype'], path) content = content_display_data['content_data'] language = content_display_data['language'] query_params = {} if path: query_params['filename'] = path_info[-1]['name'] top_right_link = reverse('browse-content-raw', kwargs={'query_string': query_string}, query_params=query_params) top_right_link_text = mark_safe( 'Raw File') else: for d in dirs: query_params['path'] = path + d['name'] d['url'] = reverse('browse-revision', kwargs={'sha1_git': sha1_git}, query_params=query_params) for f in files: query_params['path'] = path + f['name'] f['url'] = reverse('browse-revision', kwargs={'sha1_git': sha1_git}, query_params=query_params) f['length'] = filesizeformat(f['length']) if f['name'].lower().startswith('readme'): readme_name = f['name'] readme_sha1 = f['checksums']['sha1'] readme_url = reverse('browse-content-raw', kwargs={'query_string': readme_sha1}) top_right_link = get_revision_log_url(sha1_git, origin_context) top_right_link_text = mark_safe( '' 'History') vault_cooking['directory_context'] = True vault_cooking['directory_id'] = dir_id diff_revision_url = reverse('diff-revision', kwargs={'sha1_git': sha1_git}, query_params={'origin_type': origin_type, 'origin_url': origin_url, 'timestamp': timestamp, 'visit_id': visit_id}) return render(request, 'revision.html', {'empty_browse': False, 'heading': 'Revision information', 'top_panel_visible': True, 'top_panel_collapsible': True, 'top_panel_text': 'SWH object: Revision', 'swh_object_metadata': revision_data, 'message_header': message_lines[0], 'message_body': '\n'.join(message_lines[1:]), 'parents_links': mark_safe(parents_links), 'main_panel_visible': True, 'origin_context': origin_context, 'dirs': dirs, 'files': files, 'content': content, 'content_size': content_size, 'max_content_size': content_display_max_size, 'mimetype': mimetype, 'language': language, 'readme_name': readme_name, 'readme_url': readme_url, 'breadcrumbs': breadcrumbs, 'top_right_link': top_right_link, 'top_right_link_text': top_right_link_text, 'vault_cooking': vault_cooking, 'diff_revision_url': diff_revision_url, 'show_actions_menu': True}) NB_LOG_ENTRIES = 20 @browse_route(r'revision/(?P[0-9a-f]+)/log/', view_name='browse-revision-log') def revision_log_browse(request, sha1_git): """ Django view that produces an HTML display of the history log for a SWH revision identified by its id. The url that points to it is :http:get:`/browse/revision/(sha1_git)/log/`. - - Args: - request: input django http request - sha1_git: a SWH revision id - - Returns: - The HMTL rendering of the revision history log. """ # noqa try: per_page = int(request.GET.get('per_page', NB_LOG_ENTRIES)) revision_log = service.lookup_revision_log(sha1_git, limit=per_page+1) revision_log = list(revision_log) except Exception as exc: return handle_view_exception(request, exc) revs_breadcrumb = request.GET.get('revs_breadcrumb', None) revision_log_display_data = prepare_revision_log_for_display( revision_log, per_page, revs_breadcrumb) prev_rev = revision_log_display_data['prev_rev'] prev_revs_breadcrumb = revision_log_display_data['prev_revs_breadcrumb'] prev_log_url = None if prev_rev: prev_log_url = \ reverse('browse-revision-log', kwargs={'sha1_git': prev_rev}, query_params={'revs_breadcrumb': prev_revs_breadcrumb, 'per_page': per_page}) next_rev = revision_log_display_data['next_rev'] next_revs_breadcrumb = revision_log_display_data['next_revs_breadcrumb'] next_log_url = None if next_rev: next_log_url = \ reverse('browse-revision-log', kwargs={'sha1_git': next_rev}, query_params={'revs_breadcrumb': next_revs_breadcrumb, 'per_page': per_page}) revision_log_data = revision_log_display_data['revision_log_data'] for log in revision_log_data: log['directory'] = gen_directory_link( log['directory'], link_text='Browse files', link_attrs={'class': 'btn btn-md btn-swh', 'role': 'button'}) return render(request, 'revision-log.html', {'empty_browse': False, 'heading': 'Revision history information', 'top_panel_visible': False, 'top_panel_collapsible': False, 'top_panel_text': 'SWH object: Revision history', 'swh_object_metadata': None, 'main_panel_visible': True, 'revision_log': revision_log_data, 'next_log_url': next_log_url, 'prev_log_url': prev_log_url, 'breadcrumbs': None, 'top_right_link': None, 'top_right_link_text': None, 'origin_context': None, 'vault_cooking': None, 'show_actions_menu': False})