diff --git a/swh/web/assets/src/bundles/browse/browse-utils.js b/swh/web/assets/src/bundles/browse/browse-utils.js new file mode 100644 index 000000000..f5b2d7da3 --- /dev/null +++ b/swh/web/assets/src/bundles/browse/browse-utils.js @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2018 The Software Heritage developers + * See the AUTHORS file at the top-level directory of this distribution + * License: GNU Affero General Public License version 3, or any later version + * See top-level LICENSE file for more information + */ + +export function initBrowse(page) { + + $(document).ready(() => { + + $('.dropdown-submenu a.dropdown-item').on('click', e => { + $(e.target).next('div').toggle(); + if ($(e.target).next('div').css('display') !== 'none') { + $(e.target).focus(); + } else { + $(e.target).blur(); + } + e.stopPropagation(); + e.preventDefault(); + }); + + $(`.browse-${page}-item`).addClass('active'); + $(`.browse-${page}-link`).addClass('active'); + + $(`.browse-main-link`).click(event => { + let lastBrowsePage = sessionStorage.getItem('last-browse-page'); + if (lastBrowsePage) { + event.preventDefault(); + window.location = lastBrowsePage; + } + }); + + window.onunload = () => { + if (page === 'main') { + sessionStorage.setItem('last-browse-page', window.location); + } + }; + + }); + +} diff --git a/swh/web/assets/src/bundles/browse/index.js b/swh/web/assets/src/bundles/browse/index.js index e03d15f7c..1639c1fb0 100644 --- a/swh/web/assets/src/bundles/browse/index.js +++ b/swh/web/assets/src/bundles/browse/index.js @@ -1,17 +1,17 @@ /** * Copyright (C) 2018 The Software Heritage developers * See the AUTHORS file at the top-level directory of this distribution * License: GNU Affero General Public License version 3, or any later version * See top-level LICENSE file for more information */ // main bundle for the swh-web/browse application import './browse.css'; import './breadcrumbs.css'; import './content.css'; import './snapshot-navigation.css'; export * from './snapshot-navigation'; export * from './origin-search'; -export * from './main-navigation'; +export * from './browse-utils'; diff --git a/swh/web/assets/src/bundles/browse/main-navigation.js b/swh/web/assets/src/bundles/browse/main-navigation.js deleted file mode 100644 index 14420c5f3..000000000 --- a/swh/web/assets/src/bundles/browse/main-navigation.js +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (C) 2018 The Software Heritage developers - * See the AUTHORS file at the top-level directory of this distribution - * License: GNU Affero General Public License version 3, or any later version - * See top-level LICENSE file for more information - */ - -export let browseTabsHash = ['#browse', '#search', '#help', '#vault']; - -export function removeHash() { - history.replaceState('', document.title, window.location.pathname + window.location.search); -} - -export function showTab(hash) { - $('.navbar-nav.swh-browse-nav a[href="' + hash + '"]').tab('show'); - window.scrollTo(0, 0); -} - -export function showRequestedTab() { - let hash = window.location.hash; - if (hash && browseTabsHash.indexOf(hash) === -1) { - return; - } - if (hash) { - showTab(hash); - } else { - showTab('#browse'); - } -} - -export function initMainNavigation() { - - $(document).ready(() => { - - $('.dropdown-submenu a.dropdown-item').on('click', e => { - $(e.target).next('div').toggle(); - if ($(e.target).next('div').css('display') !== 'none') { - $(e.target).focus(); - } else { - $(e.target).blur(); - } - e.stopPropagation(); - e.preventDefault(); - }); - - // Change hash for page reload - $('.navbar-nav.swh-browse-nav a').on('shown.bs.tab', e => { - if (e.target.hash.trim() !== '#browse') { - window.location.hash = e.target.hash; - } else { - let hash = window.location.hash; - if (browseTabsHash.indexOf(hash) !== -1) { - removeHash(); - } - } - showRequestedTab(); - }); - - // update displayed tab when the url fragment changes - $(window).on('hashchange', () => { - showRequestedTab(); - }); - - // show requested tab when loading the page - showRequestedTab(); - }); - -} diff --git a/swh/web/assets/src/bundles/vault/vault-create-tasks.js b/swh/web/assets/src/bundles/vault/vault-create-tasks.js index 0045793c6..a9d7ddf3e 100644 --- a/swh/web/assets/src/bundles/vault/vault-create-tasks.js +++ b/swh/web/assets/src/bundles/vault/vault-create-tasks.js @@ -1,90 +1,90 @@ /** * Copyright (C) 2018 The Software Heritage developers * See the AUTHORS file at the top-level directory of this distribution * License: GNU Affero General Public License version 3, or any later version * See top-level LICENSE file for more information */ import {handleFetchError} from 'utils/functions'; function addVaultCookingTask(cookingTask) { let vaultCookingTasks = JSON.parse(localStorage.getItem('swh-vault-cooking-tasks')); if (!vaultCookingTasks) { vaultCookingTasks = []; } if (vaultCookingTasks.find(val => { return val.object_type === cookingTask.object_type && val.object_id === cookingTask.object_id; }) === undefined) { let cookingUrl; if (cookingTask.object_type === 'directory') { cookingUrl = Urls.vault_cook_directory(cookingTask.object_id); } else { cookingUrl = Urls.vault_cook_revision_gitfast(cookingTask.object_id); } if (cookingTask.email) { cookingUrl += '?email=' + cookingTask.email; } fetch(cookingUrl, {credentials: 'same-origin', method: 'POST'}) .then(handleFetchError) .then(() => { vaultCookingTasks.push(cookingTask); localStorage.setItem('swh-vault-cooking-tasks', JSON.stringify(vaultCookingTasks)); $('#vault-cook-directory-modal').modal('hide'); $('#vault-cook-revision-modal').modal('hide'); - swh.browse.showTab('#vault'); + window.location = Urls.browse_vault(); }) .catch(() => { $('#vault-cook-directory-modal').modal('hide'); $('#vault-cook-revision-modal').modal('hide'); }); } else { - swh.browse.showTab('#vault'); + window.location = Urls.browse_vault(); } } function validateEmail(email) { let re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return re.test(String(email).toLowerCase()); } export function cookDirectoryArchive(directoryId) { let email = $('#swh-vault-directory-email').val().trim(); if (!email || validateEmail(email)) { let cookingTask = {'object_type': 'directory', 'object_id': directoryId, 'email': email, 'status': 'new'}; addVaultCookingTask(cookingTask); } else { $('#invalid-email-modal').modal('show'); } } export function cookRevisionArchive(revisionId) { let email = $('#swh-vault-revision-email').val().trim(); if (!email || validateEmail(email)) { let cookingTask = { 'object_type': 'revision', 'object_id': revisionId, 'email': email, 'status': 'new' }; addVaultCookingTask(cookingTask); } else { $('#invalid-email-modal').modal('show'); } } export function initTaskCreationUi() { // reparent the modals to the top navigation div in order to be able // to display them $(document).ready(function() { $('.swh-browse-top-navigation').append($('#vault-cook-directory-modal')); $('.swh-browse-top-navigation').append($('#vault-cook-revision-modal')); $('.swh-browse-top-navigation').append($('#invalid-email-modal')); }); } diff --git a/swh/web/assets/src/bundles/webapp/code-highlighting.js b/swh/web/assets/src/bundles/webapp/code-highlighting.js index 872a52739..424f09ed1 100644 --- a/swh/web/assets/src/bundles/webapp/code-highlighting.js +++ b/swh/web/assets/src/bundles/webapp/code-highlighting.js @@ -1,118 +1,121 @@ /** * Copyright (C) 2018 The Software Heritage developers * See the AUTHORS file at the top-level directory of this distribution * License: GNU Affero General Public License version 3, or any later version * See top-level LICENSE file for more information */ export async function highlightCode(showLineNumbers = true) { await import(/* webpackChunkName: "highlightjs" */ 'utils/highlightjs'); // empty hljs language definition function noHighlight(hljs) { return {}; } // just a trick to get line numbers working when no highlight // has to be performed hljs.registerLanguage('nohighlight-swh', noHighlight); // keep track of the first highlighted line let firstHighlightedLine = null; // highlighting color let lineHighlightColor = 'rgb(193, 255, 193)'; // function to highlight a line function highlightLine(i) { let lineTd = $(`.swh-content div[data-line-number="${i}"]`).parent().parent(); lineTd.css('background-color', lineHighlightColor); return lineTd; } function removeHash() { history.replaceState('', document.title, window.location.pathname + window.location.search); } // function to reset highlighting function resetHighlightedLines() { firstHighlightedLine = null; $('.swh-content tr').css('background-color', 'inherit'); } function scrollToLine(lineDomElt) { if ($(lineDomElt).closest('.swh-content').length > 0) { $('html, body').animate({ scrollTop: $(lineDomElt).offset().top - 70 }, 500); } } // function to highlight lines based on a url fragment // in the form '#Lx' or '#Lx-Ly' function parseUrlFragmentForLinesToHighlight() { let lines = []; let linesRegexp = new RegExp(/L(\d+)/g); let line = linesRegexp.exec(window.location.hash); while (line) { lines.push(parseInt(line[1])); line = linesRegexp.exec(window.location.hash); } resetHighlightedLines(); if (lines.length === 1) { firstHighlightedLine = parseInt(lines[0]); scrollToLine(highlightLine(lines[0])); } else if (lines[0] < lines[lines.length - 1]) { firstHighlightedLine = parseInt(lines[0]); scrollToLine(highlightLine(lines[0])); for (let i = lines[0] + 1; i <= lines[lines.length - 1]; ++i) { highlightLine(i); } } } $(document).ready(() => { // highlight code and add line numbers $('code').each((i, block) => { hljs.highlightBlock(block); if (showLineNumbers) { hljs.lineNumbersBlock(block); } }); if (!showLineNumbers) { return; } // click handler to dynamically highlight line(s) // when the user clicks on a line number (lines range // can also be highlighted while holding the shift key) $('body').click(evt => { if (evt.target.classList.contains('hljs-ln-n')) { let line = parseInt($(evt.target).data('line-number')); if (evt.shiftKey && firstHighlightedLine && line > firstHighlightedLine) { let firstLine = firstHighlightedLine; resetHighlightedLines(); for (let i = firstLine; i <= line; ++i) { highlightLine(i); } firstHighlightedLine = firstLine; window.location.hash = `#L${firstLine}-L${line}`; } else { resetHighlightedLines(); highlightLine(line); window.location.hash = `#L${line}`; scrollToLine(evt.target); } } else { resetHighlightedLines(); removeHash(); } }); // update lines highlighting when the url fragment changes $(window).on('hashchange', () => parseUrlFragmentForLinesToHighlight()); - $('.navbar-nav.swh-browse-nav a[href="#browse"]').tab('show'); + // schedule lines highlighting if any as hljs.lineNumbersBlock() is async + setTimeout(() => { + parseUrlFragmentForLinesToHighlight(); + }); }); } diff --git a/swh/web/browse/urls.py b/swh/web/browse/urls.py index fc2bf7b8f..99f65719c 100644 --- a/swh/web/browse/urls.py +++ b/swh/web/browse/urls.py @@ -1,39 +1,49 @@ # Copyright (C) 2017-2018 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU Affero General Public License version 3, or any later version # See top-level LICENSE file for more information from django.conf.urls import url from django.shortcuts import render import swh.web.browse.views.directory # noqa import swh.web.browse.views.content # noqa import swh.web.browse.views.identifiers # noqa import swh.web.browse.views.origin # noqa import swh.web.browse.views.person # noqa import swh.web.browse.views.release # noqa import swh.web.browse.views.revision # noqa import swh.web.browse.views.snapshot # noqa from swh.web.browse.browseurls import BrowseUrls -def default_browse_view(request): - """Default django view used as an entry point - for the swh browse ui web application. - - The url that point to it is /browse/. - - Args: - request: input django http request - """ +def _default_browse_view(request): return render(request, 'browse.html', - {'heading': 'Browse the Software Heritage archive', + {'heading': 'Browse', 'empty_browse': True}) +def _browse_help_view(request): + return render(request, 'browse-help.html', + {'heading': 'Help'}) + + +def _browse_search_view(request): + return render(request, 'browse-search.html', + {'heading': 'Search'}) + + +def _browse_vault_view(request): + return render(request, 'browse-vault-ui.html', + {'heading': 'Vault'}) + + urlpatterns = [ - url(r'^$', default_browse_view, name='browse-homepage') + url(r'^$', _default_browse_view, name='browse-mainpage'), + url(r'^help/$', _browse_help_view, name='browse-help'), + url(r'^search/$', _browse_search_view, name='browse-search'), + url(r'^vault/$', _browse_vault_view, name='browse-vault') ] urlpatterns += BrowseUrls.get_url_patterns() diff --git a/swh/web/templates/includes/browse-help.html b/swh/web/templates/browse-help.html similarity index 95% rename from swh/web/templates/includes/browse-help.html rename to swh/web/templates/browse-help.html index daa03f5ee..30537238c 100644 --- a/swh/web/templates/includes/browse-help.html +++ b/swh/web/templates/browse-help.html @@ -1,167 +1,176 @@ +{% extends "browse-layout.html" %} + {% comment %} Copyright (C) 2017-2018 The Software Heritage developers See the AUTHORS file at the top-level directory of this distribution License: GNU Affero General Public License version 3, or any later version See top-level LICENSE file for more information {% endcomment %} +{% block browse-content %} +

How to browse the Software Heritage archive ?

Overview

This web application aims to provide HTML views to easily navigate in the Software Heritage archive. This is an ongoing development and new features and improvements will be progressively added over the time.

URI scheme

The current URI scheme of that web application is described below and depends on the type of Software Heritage object to browse. Its exhaustive documentation can be consulted from the official Software Heritage development documentation

Context-independent browsing

Context-independent URLs provide information about SWH objects (e.g., revisions, directories, contents, persons, …), independently of the contexts where they have been found (e.g., specific software origins, branches, commits, …).

Below are some examples of endpoints used to just render the corresponding information for user consumption:

Where hyperlinks are created when browsing these kind of endpoints, they always point to other context-independent browsing URLs.

Context-dependent browsing

Context-dependent URLs provide information about SWH objects, limited to specific contexts where the objects have been found.

Currently, browsing the Software Heritage objects in the context of an origin is available. Below are some examples of such endpoints:

Search software origins to browse

In order to facilitate the browsing of the archive and generate relevant entry points to it, a - search interface is available. Currently, it enables to search software origins from the URLs they were retrieved + search interface is available. Currently, it enables to search software origins from the URLs they were retrieved from. More search criteria will be added in the future.
-
\ No newline at end of file + + + +{% endblock %} \ No newline at end of file diff --git a/swh/web/templates/browse-layout.html b/swh/web/templates/browse-layout.html new file mode 100644 index 000000000..a64a3b0a4 --- /dev/null +++ b/swh/web/templates/browse-layout.html @@ -0,0 +1,40 @@ +{% extends "layout.html" %} + +{% comment %} +Copyright (C) 2017-2018 The Software Heritage developers +See the AUTHORS file at the top-level directory of this distribution +License: GNU Affero General Public License version 3, or any later version +See top-level LICENSE file for more information +{% endcomment %} + +{% load swh_templatetags %} +{% load render_bundle from webpack_loader %} + +{% block title %}{{ heading }} – Software Heritage archive {% endblock %} + +{% block header %} +{% render_bundle 'browse' %} +{% render_bundle 'vault' %} +{% endblock %} + +{% block navbar-content %} + +{% endblock %} + +{% block content %} +
Alpha version
+{% block browse-content %}{% endblock %} +{% endblock %} diff --git a/swh/web/templates/includes/origins-search.html b/swh/web/templates/browse-search.html similarity index 93% rename from swh/web/templates/includes/origins-search.html rename to swh/web/templates/browse-search.html index d297199b4..0df506776 100644 --- a/swh/web/templates/includes/origins-search.html +++ b/swh/web/templates/browse-search.html @@ -1,57 +1,62 @@ +{% extends "browse-layout.html" %} + {% comment %} Copyright (C) 2017-2018 The Software Heritage developers See the AUTHORS file at the top-level directory of this distribution License: GNU Affero General Public License version 3, or any later version See top-level LICENSE file for more information {% endcomment %} {% load static %} +{% block browse-content %}

Search Software Heritage origins to browse

Searching origins ...

\ No newline at end of file + +{% endblock %} \ No newline at end of file diff --git a/swh/web/templates/includes/vault-ui.html b/swh/web/templates/browse-vault-ui.html similarity index 89% rename from swh/web/templates/includes/vault-ui.html rename to swh/web/templates/browse-vault-ui.html index cbe999405..05f8595d2 100644 --- a/swh/web/templates/includes/vault-ui.html +++ b/swh/web/templates/browse-vault-ui.html @@ -1,42 +1,48 @@ +{% extends "browse-layout.html" %} + {% comment %} Copyright (C) 2017-2018 The Software Heritage developers See the AUTHORS file at the top-level directory of this distribution License: GNU Affero General Public License version 3, or any later version See top-level LICENSE file for more information {% endcomment %} +{% load render_bundle from webpack_loader %} + +{% block browse-content %}

Download content from the Software Heritage Vault

This interface enables to track the status of the different Software Heritage Vault cooking tasks created while browsing the archive.

Once a cooking task is finished, a link will be made available in order to download the associated archive.

Object type Object id Cooking status
- +{% endblock %} \ No newline at end of file diff --git a/swh/web/templates/browse.html b/swh/web/templates/browse.html index 380a2be7f..3cd33c7be 100644 --- a/swh/web/templates/browse.html +++ b/swh/web/templates/browse.html @@ -1,130 +1,88 @@ -{% extends "layout.html" %} +{% extends "browse-layout.html" %} {% comment %} Copyright (C) 2017-2018 The Software Heritage developers See the AUTHORS file at the top-level directory of this distribution License: GNU Affero General Public License version 3, or any later version See top-level LICENSE file for more information {% endcomment %} {% load swh_templatetags %} -{% load render_bundle from webpack_loader %} -{% block title %}{{ heading }} – Software Heritage archive {% endblock %} - -{% block header %} -{% render_bundle 'browse' %} -{% render_bundle 'vault' %} -{% endblock %} - -{% block navbar-content %} - -{% endblock %} - -{% block content %} -
Alpha version
- -
- -