diff --git a/cypress/integration/persistent-identifiers.spec.js b/cypress/integration/persistent-identifiers.spec.js --- a/cypress/integration/persistent-identifiers.spec.js +++ b/cypress/integration/persistent-identifiers.spec.js @@ -5,14 +5,15 @@ * See top-level LICENSE file for more information */ -let origin; -let url; +let origin, originBadgeUrl, originBrowseUrl; +let url, urlPrefix; let browsedObjectMetadata; let cntPid, cntPidWithOrigin, cntPidWithOriginAndLines; let dirPid, dirPidWithOrigin; let relPid, relPidWithOrigin; let revPid, revPidWithOrigin; let snpPid, snpPidWithOrigin; +let testsData; const firstSelLine = 6; const lastSelLine = 12; @@ -22,10 +23,13 @@ origin = this.origin[1]; url = this.Urls.browse_origin_content(origin.url, origin.content[0].path); url = `${url}?release=${origin.release}#L${firstSelLine}-L${lastSelLine}`; - }); - - beforeEach(function() { + originBadgeUrl = this.Urls.swh_badge('origin', origin.url); + originBrowseUrl = this.Urls.browse_origin(origin.url); cy.visit(url).window().then(win => { + urlPrefix = `${win.location.protocol}//${win.location.hostname}`; + if (win.location.port) { + urlPrefix += `:${win.location.port}`; + } browsedObjectMetadata = win.swh.webapp.getBrowsedSwhObjectMetadata(); cntPid = `swh:1:cnt:${browsedObjectMetadata.sha1_git}`; cntPidWithOrigin = `${cntPid};origin=${origin.url}`; @@ -38,9 +42,52 @@ relPidWithOrigin = `${relPid};origin=${origin.url}`; snpPid = `swh:1:snp:${browsedObjectMetadata.snapshot}`; snpPidWithOrigin = `${snpPid};origin=${origin.url}`; + + testsData = [ + { + 'objectType': 'content', + 'objectPids': [cntPidWithOriginAndLines, cntPidWithOrigin, cntPid], + 'badgeUrl': this.Urls.swh_badge('content', browsedObjectMetadata.sha1_git), + 'badgePidUrl': this.Urls.swh_badge_pid(cntPidWithOriginAndLines), + 'browseUrl': this.Urls.browse_swh_id(cntPidWithOriginAndLines) + }, + { + 'objectType': 'directory', + 'objectPids': [dirPidWithOrigin, dirPid], + 'badgeUrl': this.Urls.swh_badge('directory', browsedObjectMetadata.directory), + 'badgePidUrl': this.Urls.swh_badge_pid(dirPidWithOrigin), + 'browseUrl': this.Urls.browse_swh_id(dirPidWithOrigin) + }, + { + 'objectType': 'release', + 'objectPids': [relPidWithOrigin, relPid], + 'badgeUrl': this.Urls.swh_badge('release', browsedObjectMetadata.release), + 'badgePidUrl': this.Urls.swh_badge_pid(relPidWithOrigin), + 'browseUrl': this.Urls.browse_swh_id(relPidWithOrigin) + }, + { + 'objectType': 'revision', + 'objectPids': [revPidWithOrigin, revPid], + 'badgeUrl': this.Urls.swh_badge('revision', browsedObjectMetadata.revision), + 'badgePidUrl': this.Urls.swh_badge_pid(revPidWithOrigin), + 'browseUrl': this.Urls.browse_swh_id(revPidWithOrigin) + }, + { + 'objectType': 'snapshot', + 'objectPids': [snpPidWithOrigin, snpPid], + 'badgeUrl': this.Urls.swh_badge('snapshot', browsedObjectMetadata.snapshot), + 'badgePidUrl': this.Urls.swh_badge_pid(snpPidWithOrigin), + 'browseUrl': this.Urls.browse_swh_id(snpPidWithOrigin) + } + ]; + }); }); + beforeEach(function() { + cy.visit(url); + }); + it('should open and close identifiers tab when clicking on handle', function() { cy.get('#swh-identifiers') .should('have.class', 'ui-slideouttab-ready'); @@ -63,30 +110,7 @@ cy.get('.ui-slideouttab-handle') .click(); - const testData = [ - { - 'objectType': 'content', - 'objectPid': cntPidWithOriginAndLines - }, - { - 'objectType': 'directory', - 'objectPid': dirPidWithOrigin - }, - { - 'objectType': 'release', - 'objectPid': relPidWithOrigin - }, - { - 'objectType': 'revision', - 'objectPid': revPidWithOrigin - }, - { - 'objectType': 'snapshot', - 'objectPid': snpPidWithOrigin - } - ]; - - for (let td of testData) { + for (let td of testsData) { cy.get(`a[href="#swh-id-tab-${td.objectType}"]`) .click(); @@ -94,8 +118,8 @@ .should('be.visible'); cy.get(`#swh-id-tab-${td.objectType} .swh-id`) - .contains(td.objectPid) - .should('have.attr', 'href', this.Urls.browse_swh_id(td.objectPid)); + .contains(td.objectPids[0]) + .should('have.attr', 'href', this.Urls.browse_swh_id(td.objectPids[0])); } @@ -143,26 +167,11 @@ cy.get('.ui-slideouttab-handle') .click(); - const testData = [ - { - 'objectType': 'directory', - 'objectPids': [dirPidWithOrigin, dirPid] - }, - { - 'objectType': 'release', - 'objectPids': [relPidWithOrigin, relPid] - }, - { - 'objectType': 'revision', - 'objectPids': [revPidWithOrigin, revPid] - }, - { - 'objectType': 'snapshot', - 'objectPids': [snpPidWithOrigin, snpPid] - } - ]; + for (let td of testsData) { + + // already tested + if (td.objectType === 'content') continue; - for (let td of testData) { cy.get(`a[href="#swh-id-tab-${td.objectType}"]`) .click(); @@ -187,4 +196,64 @@ }); + it('should display swh badges in identifiers tab for browsed objects', function() { + cy.get('.ui-slideouttab-handle') + .click(); + + const originBadgeUrl = this.Urls.swh_badge('origin', origin.url); + + for (let td of testsData) { + cy.get(`a[href="#swh-id-tab-${td.objectType}"]`) + .click(); + + cy.get(`#swh-id-tab-${td.objectType} .swh-badge-origin`) + .should('have.attr', 'src', originBadgeUrl); + + cy.get(`#swh-id-tab-${td.objectType} .swh-badge-${td.objectType}`) + .should('have.attr', 'src', td.badgeUrl); + + } + + }); + + it('should display badge integration info when clicking on it', function() { + + cy.get('.ui-slideouttab-handle') + .click(); + + for (let td of testsData) { + cy.get(`a[href="#swh-id-tab-${td.objectType}"]`) + .click(); + + cy.get(`#swh-id-tab-${td.objectType} .swh-badge-origin`) + .click() + .wait(500); + + for (let badgeType of ['html', 'md', 'rst']) { + cy.get(`.modal .swh-badge-${badgeType}`) + .contains(`${urlPrefix}${originBrowseUrl}`) + .contains(`${urlPrefix}${originBadgeUrl}`); + } + + cy.get('.modal.show .close') + .click() + .wait(500); + + cy.get(`#swh-id-tab-${td.objectType} .swh-badge-${td.objectType}`) + .click() + .wait(500); + + for (let badgeType of ['html', 'md', 'rst']) { + cy.get(`.modal .swh-badge-${badgeType}`) + .contains(`${urlPrefix}${td.browseUrl}`) + .contains(`${urlPrefix}${td.badgePidUrl}`); + } + + cy.get('.modal.show .close') + .click() + .wait(500); + + } + }); + }); diff --git a/swh/web/assets/src/bundles/browse/swh-ids-utils.js b/swh/web/assets/src/bundles/browse/swh-ids-utils.js --- a/swh/web/assets/src/bundles/browse/swh-ids-utils.js +++ b/swh/web/assets/src/bundles/browse/swh-ids-utils.js @@ -88,6 +88,7 @@ let tabSlideOptions = { tabLocation: 'right', + clickScreenToCloseFilters: ['.ui-slideouttab-panel', '.modal'], offset: function() { const width = $(window).width(); if (width < BREAKPOINT_SM) { diff --git a/swh/web/assets/src/bundles/webapp/badges.js b/swh/web/assets/src/bundles/webapp/badges.js new file mode 100644 --- /dev/null +++ b/swh/web/assets/src/bundles/webapp/badges.js @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2019 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 showBadgeInfoModal(objectType, objectPid) { + let badgeImageUrl; + let badgeLinkUrl; + if (objectType === 'origin') { + badgeImageUrl = Urls.swh_badge(objectType, objectPid); + badgeLinkUrl = Urls.browse_origin(objectPid); + } else { + badgeImageUrl = Urls.swh_badge_pid(objectPid); + badgeLinkUrl = Urls.browse_swh_id(objectPid); + } + let urlPrefix = `${window.location.protocol}//${window.location.hostname}`; + if (window.location.port) { + urlPrefix += `:${window.location.port}`; + } + const absoluteBadgeImageUrl = `${urlPrefix}${badgeImageUrl}`; + const absoluteBadgeLinkUrl = `${urlPrefix}${badgeLinkUrl}`; + const html = ` + + + +
+ +
<a href="${absoluteBadgeLinkUrl}">
+    <img src="${absoluteBadgeImageUrl}">
+</a>
+
+
+ +
[![SWH](${absoluteBadgeImageUrl})](${absoluteBadgeLinkUrl})
+
+
+ +
.. image:: ${absoluteBadgeImageUrl}
+    :target: ${absoluteBadgeLinkUrl}
+
`; + swh.webapp.showModalHtml('Software Heritage badge integration', html); +} diff --git a/swh/web/assets/src/bundles/webapp/index.js b/swh/web/assets/src/bundles/webapp/index.js --- a/swh/web/assets/src/bundles/webapp/index.js +++ b/swh/web/assets/src/bundles/webapp/index.js @@ -24,3 +24,4 @@ export * from './notebook-rendering'; export * from './xss-filtering'; export * from './history-counters'; +export * from './badges'; diff --git a/swh/web/assets/src/bundles/webapp/webapp-utils.js b/swh/web/assets/src/bundles/webapp/webapp-utils.js --- a/swh/web/assets/src/bundles/webapp/webapp-utils.js +++ b/swh/web/assets/src/bundles/webapp/webapp-utils.js @@ -198,6 +198,12 @@ $('#swh-web-modal-confirm').modal('show'); } +export function showModalHtml(title, html) { + $('#swh-web-modal-html .modal-title').text(title); + $('#swh-web-modal-html .modal-body').html(html); + $('#swh-web-modal-html').modal('show'); +} + export function addJumpToPagePopoverToDataTable(dataTableElt) { dataTableElt.on('draw.dt', function() { $('.paginate_button.disabled').css('cursor', 'pointer'); diff --git a/swh/web/assets/src/bundles/webapp/webapp.css b/swh/web/assets/src/bundles/webapp/webapp.css --- a/swh/web/assets/src/bundles/webapp/webapp.css +++ b/swh/web/assets/src/bundles/webapp/webapp.css @@ -182,6 +182,7 @@ .modal { text-align: center; padding: 0 !important; + z-index: 50000; } .modal::before { @@ -580,3 +581,14 @@ .wrapper { overflow: hidden; } + +.swh-badge { + padding-bottom: 1rem; + cursor: pointer; +} + +.swh-badge-html, +.swh-badge-md, +.swh-badge-rst { + white-space: pre-wrap; +} diff --git a/swh/web/browse/utils.py b/swh/web/browse/utils.py --- a/swh/web/browse/utils.py +++ b/swh/web/browse/utils.py @@ -1091,6 +1091,7 @@ swh_ids.append({ 'object_type': swh_object['type'], + 'object_id': swh_object['id'], 'object_icon': object_icon, 'swh_id': swh_id, 'swh_id_url': reverse('browse-swh-id', diff --git a/swh/web/templates/includes/global-modals.html b/swh/web/templates/includes/global-modals.html --- a/swh/web/templates/includes/global-modals.html +++ b/swh/web/templates/includes/global-modals.html @@ -18,21 +18,36 @@