Skip to content

Commit

Permalink
Preserve initial demo version #522
Browse files Browse the repository at this point in the history
Reference: #522
Signed-off-by: John M. Horan <[email protected]>
  • Loading branch information
johnmhoran committed Sep 3, 2024
1 parent 790a9d5 commit e738b0e
Show file tree
Hide file tree
Showing 49 changed files with 22,585 additions and 5 deletions.
20 changes: 20 additions & 0 deletions packagedb/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#
# Copyright (c) nexB Inc. and others. All rights reserved.
# purldb is a trademark of nexB Inc.
# SPDX-License-Identifier: Apache-2.0
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
# See https://github.com/aboutcode-org/purldb for support or download.
# See https://aboutcode.org for more information about nexB OSS projects.
#

from django import forms


class PackageSearchForm(forms.Form):

search = forms.CharField(
required=True,
widget=forms.TextInput(
attrs={"placeholder": "pkg:maven/org.elasticsearch/[email protected]?classifier=sources"},
),
)
68 changes: 65 additions & 3 deletions packagedb/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
import sys
import uuid
from collections import OrderedDict
from urllib.parse import urlencode

import natsort
from dateutil.parser import parse as dateutil_parse
from django.conf import settings
from django.contrib.auth.models import UserManager
from django.contrib.postgres.fields import ArrayField
Expand All @@ -25,15 +28,15 @@
from django.dispatch import receiver
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

import natsort
from dateutil.parser import parse as dateutil_parse
from licensedcode.cache import build_spdx_license_expression
from packagedcode.models import normalize_qualifiers
from packageurl import PackageURL
from packageurl.contrib.django.models import PackageURLMixin
from packageurl.contrib.django.models import PackageURLQuerySetMixin
from rest_framework.authtoken.models import Token
from rest_framework.serializers import BooleanField
from rest_framework.serializers import CharField
from rest_framework.serializers import Serializer

from packagedb import schedules

Expand Down Expand Up @@ -81,6 +84,65 @@ def paginated(self, per_page=5000):
page = paginator.page(page_number)
yield from page.object_list

# NOTE Based on class PurlValidateResponseSerializer(Serializer).
class PurlValidateSerializer(Serializer):
valid = BooleanField()
exists = BooleanField(required=False)
message = CharField()
purl = CharField()

def search(self, query: str = None):
"""
Return a Package queryset searching for the ``query``.
- A version is required.
- If only a version is provided, no qualifiers value, and the DB contains both the version alone and the version with a qualifiers value, only the version-only record is returned.
- If a correct qualifiers value is provided, returns an exact match if the record exists, otherwise no match.
- '#' and any characters that follow appear to be ignored, but we have 0 such PURLs in the DB so testing is incomplete.
"""
query = query and query.strip()
if not query:
return self.none()
qs = self

message_not_valid = "The provided PackageURL is not valid."
response = {}
response["exists"] = None
response["purl"] = query
response["valid"] = False
response["message"] = message_not_valid

# validate purl
try:
package_url = PackageURL.from_string(query)
except ValueError:
serializer = self.PurlValidateSerializer(
response
)
# print(f"\nserializer.data = {serializer.data}")
return serializer.data
# return None #throws error AttributeError: 'NoneType' object has no attribute 'prefetch_related'

# NOTE No longer used.
# purl_attributes = utils.simple_validate_from_string(query)

# print(f"\npackage_url = {package_url}")
# print(f"package_url.type = {package_url.type}")
# print(f"package_url.namespace = {package_url.namespace}")
# print(f"package_url.name = {package_url.name}")
# print(f"package_url.version = {package_url.version}")
# print(f"package_url.qualifiers = {package_url.qualifiers}")
# print(f"package_url.subpath = {package_url.subpath}")

qs = qs.filter(
(models.Q(namespace=package_url.namespace) | models.Q(namespace="")),
(models.Q(subpath=package_url.subpath) | models.Q(subpath="")),
type=package_url.type,
name=package_url.name,
version=package_url.version,
qualifiers=urlencode(package_url.qualifiers),
)
return qs


VCS_CHOICES = [
("git", "git"),
Expand Down
137 changes: 137 additions & 0 deletions packagedb/templates/base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}PurlDB.io{% endblock %}</title>
<link rel="icon" href="{% static 'images/favicon.ico' %}" />

<link rel="stylesheet" href="{% static 'css/bulma.css' %}" />
<link rel="stylesheet" href="{% static 'css/custom.css' %}" />
<link rel="stylesheet" href="{% static 'css/font-awesome.css' %}" />
<link rel="stylesheet" href="{% static 'css/bulma-tooltip.css' %}" />

<script src="{% static 'js/clipboard-2.0.0.min.js' %}" integrity="sha384-5tfO0soa+FisnuBhaHP2VmPXQG/JZ8dLcRL43IkJFzbsXTXT6zIX8q8sIT0VSe2G" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>

{% block extrahead %}{% endblock %}
</head>
<body class="Site">
<div class="container max-desktop-width is-fullheight">
{% include "navbar.html" %}
{% block content %}{% endblock %}
{% include "footer.html" %}
</div>

{% block scripts %}
{% endblock %}

{% block javascript %}
<script>
document.addEventListener('DOMContentLoaded', function () {
// Set initial data-tooltip value
document.querySelectorAll('.btn-clipboard').forEach(button => {
// Next line is just for dev.
console.info('span:', button.querySelector('span'));
const span = button.querySelector('span');
if (span && !span.getAttribute('data-tooltip')) {
span.setAttribute('data-tooltip', 'Copy to clipboard');
span.setAttribute('class', 'tooltip-narrow');
}
});

// clipboard.js
let clip = new ClipboardJS(".btn-clipboard", {
target: function (trigger) {
return trigger.nextElementSibling
}
});

clip.on("success", function (clip) {
const button = clip.trigger;
const span = button.querySelector('span');
// Change tooltip text
span.setAttribute('data-tooltip', 'Copied!');
span.setAttribute('class', 'tooltip-narrow-success');
//span.setAttribute('visibility', 'visible');
//2024-09-02 Monday 12:08:55. Try this instead.
//span.classList.add('tooltip-visible');
//Does not keep tooltip visible. Try this instead.
//span.classList.add('is-active');
span.classList.add('has-tooltip-active');
//how about this?
//span.setAttribute('style', 'visibility: visible; opacity: 1;');
// Try this
button.classList.add('tooltip-visible');
clip.clearSelection()
});

clip.on("error", function (e) {
console.error('Failed to copy: ', e);
const span = e.trigger.querySelector('span');
span.setAttribute('data-tooltip', 'Failed to copy');
//Keep visible
//span.classList.add('tooltip-visible');
//Does not keep tooltip visible. Try this instead.
//span.classList.add('is-active');
//Try this
//button.classList.add('tooltip-visible');
});

// Reset button/tooltip to default state.
function resetButtonAndTooltip(button) {
button.style.display = 'none';
const span = button.querySelector('span');
if (span) {
const defaultTooltip = span.getAttribute('data-original-tooltip') || 'Copy to clipboard';
span.setAttribute('data-tooltip', defaultTooltip);
span.setAttribute('class', 'tooltip-narrow');
//Reset to not visible
//span.classList.remove('tooltip-visible');
//Does not keep tooltip visible. Try this instead.
//span.classList.remove('is-active');

//Try this
//button.classList.remove('tooltip-visible');
}
}

// Add click event listener to document.
document.addEventListener('click', function(event) {
const clickedButton = event.target.closest('.btn-clipboard');
//Next line is just for dev.
console.info('clickedButton:', clickedButton);
document.querySelectorAll('.btn-clipboard').forEach(button => {
if (button !== clickedButton) {
resetButtonAndTooltip(button);
}
});
});

// Display button when mouse enters the copyable area -- includes "Note" area below textbox/pre.
// TODO Is 'span is null' still an issue?
document.querySelectorAll('.btn-clipboard').forEach(button => {
const span = button.querySelector('span');
if (span) {
span.setAttribute('data-original-tooltip', span.getAttribute('data-tooltip'));
button.nextElementSibling.addEventListener('mouseenter', () => {
button.style.display = '';
});
}
});

//Adjust tooltip position when close to page left/right border.
document.querySelectorAll('.has-tooltip-multiline').forEach(el => {
const rect = el.getBoundingClientRect();
if (rect.left < 100) {
el.classList.add('has-tooltip-right');
} else if (rect.right > window.innerWidth - 100) {
el.classList.add('has-tooltip-left');
}
});
});
</script>
{% endblock %}
</body>
</html>
9 changes: 9 additions & 0 deletions packagedb/templates/footer.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<footer class="footer">
<div class="content has-text-centered">
<p>
<strong>PurlDB</strong> is free software by <a href="https://www.aboutcode.org/">https://www.aboutcode.org/</a> and others</a> |
Source code license: <a href="https://github.com/aboutcode-org/purldb/blob/main/apache-2.0.LICENSE">Apache-2.0</a> |
Data license: <a href="https://github.com/aboutcode-org/purldb/blob/main/cc-by-sa-4.0.LICENSE">CC-BY-SA-4.0</a> | <span style="color: #ff0000;">Terms of Service</span>
</p>
</div>
</footer>
39 changes: 39 additions & 0 deletions packagedb/templates/includes/pagination.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<nav class="pagination is-centered is-small" aria-label="pagination">
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}&search={{ search|urlencode }}" class="pagination-previous">Previous</a>
{% else %}
<a class="pagination-previous" disabled>Previous</a>
{% endif %}

{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}&search={{ search|urlencode }}" class="pagination-next">Next</a>
{% else %}
<a class="pagination-next" disabled>Next</a>
{% endif %}

<ul class="pagination-list">
{% if page_obj.number != 1%}
<li>
<a href="?page=1&search={{ search|urlencode }}" class="pagination-link" aria-label="Goto page 1">1</a>
</li>
{% if page_obj.number > 2 %}
<li>
<span class="pagination-ellipsis">&hellip;</span>
</li>
{% endif %}
{% endif %}
<li>
<a class="pagination-link is-current" aria-label="Page {{ page_obj.number }}" aria-current="page">{{ page_obj.number }}</a>
</li>
{% if page_obj.number != page_obj.paginator.num_pages %}
{% if page_obj.next_page_number != page_obj.paginator.num_pages %}
<li>
<span class="pagination-ellipsis">&hellip;</span>
</li>
{% endif %}
<li>
<a href="?page={{ page_obj.paginator.num_pages }}&search={{ search|urlencode }}" class="pagination-link" aria-label="Goto page {{ page_obj.paginator.num_pages }}">{{ page_obj.paginator.num_pages }}</a>
</li>
{% endif %}
</ul>
</nav>
12 changes: 12 additions & 0 deletions packagedb/templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{% extends "base.html" %}
{% load widget_tweaks %}

{% block title %}
PurlDB Home
{% endblock %}

{% block content %}
<section class="section pt-0">
{% include "package_search_box.html" %}
</section>
{% endblock %}
65 changes: 65 additions & 0 deletions packagedb/templates/navbar.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<nav class="navbar is-dark mb-5 border-bottom-radius" role="navigation" aria-label="main navigation">
<div class="navbar-brand ml-3">
<a class="navbar-item is-size-4 has-text-weight-bold" href="/">
PurlDB<span class="nexb-orange">.</span>io
</a>
</div>
<div class="navbar-menu">
<div class="navbar-start">
<a class="navbar-item" href="{% url 'package_search' %}">
Packages
</a>
<a class="navbar-item" href="{% url 'package_search_test_tabset' %}">
test_tabset
</a>
<a class="navbar-item" href="https://aboutcode.readthedocs.io/projects/PURLdb/" target="_blank">
Documentation
</a>
</div>
</div>
<div class="navbar-end mr-3">
<a class="navbar-item" href="/api" target="_blank">
API
</a>
<div class="navbar-item navbar-item is-cursor-help">
<div class="dropdown is-right is-hoverable ">
<div class="dropdown-trigger has-text-grey-light">About</div>
<div class="dropdown-menu navbar-hover-div" role="menu">
<div class="dropdown-content">
<div class="dropdown-item about-hover-div">

PurlDB is a free and open database of <strong>purls</strong>
(<a href="https://github.com/package-url/purl-spec" target="_blank">Package URLs</a>).
<ul>
<li>
Live chat at <a href="https://gitter.im/aboutcode-org/discuss" target="_blank">
https://gitter.im/aboutcode-org/discuss</a>
</li>
<li>
Source code and support at <a href="https://github.com/aboutcode-org/purldb" target="_blank">https://github.com/aboutcode-org/purldb</a>
</li>
<li>
Review and add issues at <a href="https://github.com/aboutcode-org/purldb/issues" target="_blank">https://github.com/aboutcode-org/purldb/issues</a>
</li>
<li>
Docs at <a href="https://aboutcode.readthedocs.io/projects/PURLdb/" target="_blank">
https://aboutcode.readthedocs.io/projects/PURLdb/</a>
</li>
<li>
Sponsored by NLnet <a href="https://nlnet.nl/project/vulnerabilitydatabase/" target="_blank">
https://nlnet.nl/project/vulnerabilitydatabase/</a> for
<a href="https://www.aboutcode.org/" target="_blank">https://www.aboutcode.org/</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="navbar-item navbar-item is-cursor-help">
<div class="dropdown-trigger has-text-grey-light">
v{{ PURLDB_VERSION }}
</div>
</div>
</div>
</nav>
Loading

0 comments on commit e738b0e

Please sign in to comment.