-
- Error
-
+ {% if form.errors %}
+
+
-{% endif %}
-
+
+
+ Error
+
{{ form.errors }}
-
-diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..e2001dd
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,120 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+pip-selfcheck.json
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+coverage.xml
+*.cover
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+
+# Sphinx
+docs/_build
+docs/bin
+docs/build
+docs/include
+docs/Lib
+doc/pyvenv.cfg
+pyvenv.cfg
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# pyenv and pip
+.python-version
+pyvenv.cfg
+bin/
+
+# Environments
+.venv
+env/
+venv/
+ENV/
+
+
+# mypy
+.mypy_cache/
+
+
+# Database
+*.sqlite3*
+
+# Staticfiles
+staticfiles/
+
+# virtualenv
+bin
+include
+lib64
+share
+Pipfile
+
+# editors
+.vscode
+# PyCharm
+.idea/
+
+# pytest
+.pytest_cache
+
+
+# Various junk and temp files
+.DS_Store
+*~
+.*.sw[po]
+.build
+.ve
+*.bak
+/.cache/
+/tmp/
+
+# pyenv
+/.python-version
+/man/
+/.pytest_cache/
+lib64
+tcl
+
+# Ignore Jupyter Notebook related temp files
+.ipynb_checkpoints/
diff --git a/fedcode/forms.py b/fedcode/forms.py
index 908c882..98550b9 100644
--- a/fedcode/forms.py
+++ b/fedcode/forms.py
@@ -20,15 +20,14 @@ class CreateGitRepoForm(forms.ModelForm):
class Meta:
model = Repository
fields = ["url"]
- help_texts = {
- "url": None,
- }
def __init__(self, *args, **kwargs):
super(CreateGitRepoForm, self).__init__(*args, **kwargs)
self.fields["url"].widget.attrs.update(
- {"class": "input mb-5", "placeholder": "https://github.com/nexB/vulnerablecode-data"}
+ {"class": "input", "placeholder": "https://github.com/nexB/vulnerablecode-data"}
)
+ self.fields["url"].help_text = ""
+ self.fields["url"].label = ""
class CreateNoteForm(forms.ModelForm):
@@ -39,9 +38,10 @@ class Meta:
def __init__(self, *args, **kwargs):
super(CreateNoteForm, self).__init__(*args, **kwargs)
self.fields["content"].widget.attrs.update(
- {"class": "textarea", "placeholder": "Add a note...", "rows": 5}
+ {"class": "textarea", "placeholder": "Comment...", "rows": 5}
)
self.fields["content"].label = ""
+ self.fields["content"].help_text = ""
class ReviewStatusForm(forms.ModelForm):
@@ -109,9 +109,8 @@ class SearchPackageForm(forms.Form):
label=False,
widget=forms.TextInput(
attrs={
- "placeholder": "Please enter a valid purl ex: pkg:maven/org.apache.commons/io",
- "class": "input is-rounded",
- "style": "width: 90%;",
+ "placeholder": "Search a package...",
+ "class": "input ",
},
),
)
@@ -137,9 +136,8 @@ class SearchRepositoryForm(forms.Form):
label=False,
widget=forms.TextInput(
attrs={
- "placeholder": "Please Enter a Repository URL ex: https://github.com/nexB/vulnerablecode-data",
- "class": "input is-rounded",
- "style": "width: 90%;",
+ "placeholder": "Search a repository...",
+ "class": "input",
},
),
)
diff --git a/fedcode/middleware.py b/fedcode/middleware.py
new file mode 100644
index 0000000..f08378d
--- /dev/null
+++ b/fedcode/middleware.py
@@ -0,0 +1,30 @@
+#
+# Copyright (c) nexB Inc. and others. All rights reserved.
+# FederatedCode 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/nexB/federatedcode for support or download.
+# See https://aboutcode.org for more information about AboutCode.org OSS projects.
+#
+
+import zoneinfo
+
+from django.utils import timezone
+
+
+class TimezoneMiddleware:
+ def __init__(self, get_response):
+ self.get_response = get_response
+
+ def __call__(self, request):
+ try:
+ # Activate local timezone for user using cookies
+ tzname = request.COOKIES.get("user_timezone")
+ if tzname:
+ timezone.activate(zoneinfo.ZoneInfo(tzname))
+ else:
+ timezone.deactivate()
+ except Exception as e:
+ timezone.deactivate()
+
+ return self.get_response(request)
diff --git a/fedcode/migrations/0005_remove_person_avatar.py b/fedcode/migrations/0005_remove_person_avatar.py
new file mode 100644
index 0000000..219af80
--- /dev/null
+++ b/fedcode/migrations/0005_remove_person_avatar.py
@@ -0,0 +1,17 @@
+# Generated by Django 5.1.2 on 2024-12-27 17:48
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("fedcode", "0004_alter_vulnerability_unique_together"),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name="person",
+ name="avatar",
+ ),
+ ]
diff --git a/fedcode/models.py b/fedcode/models.py
index 2350714..1daf383 100644
--- a/fedcode/models.py
+++ b/fedcode/models.py
@@ -50,6 +50,10 @@ class RemoteActor(models.Model):
help_text="A field to track when remote actor are updated",
)
+ @property
+ def safe_url(self):
+ return f"{self.url.rstrip('/')}/"
+
class Actor(models.Model):
"""
@@ -227,6 +231,11 @@ def reputation_value(self):
def absolute_url(self):
return full_reverse("note-page", self.id)
+ @property
+ def acct_avatar(self):
+ person = Person.objects.get(user__username=self.username)
+ return person.avatar
+
@property
def to_ap(self):
return {
@@ -290,6 +299,10 @@ def __str__(self):
def followers_count(self):
return Follow.objects.filter(package=self).count()
+ @property
+ def notes_count(self):
+ return Note.objects.filter(acct=self.acct).count()
+
@property
def followers(self):
return Follow.objects.filter(package=self).values("person_id")
@@ -350,16 +363,9 @@ def to_ap(self):
class Person(Actor):
"""
- A person is a user can follow pacakge or just vote or create a notes
+ A person is a user can follow package or just vote or create a notes
"""
- avatar = models.ImageField(
- upload_to="uploads/",
- help_text="",
- default="favicon-16x16.png",
- null=True,
- )
-
user = models.OneToOneField(
User,
null=True,
@@ -391,13 +397,24 @@ class Meta:
),
]
+ @property
+ def avatar(self):
+ from hashlib import sha256
+
+ email = ""
+ if self.user and (email := self.user.email):
+ email = email.strip().lower()
+
+ gravatar = sha256(email.encode("utf-8")).hexdigest()
+ return f"https://gravatar.com/avatar/{gravatar}"
+
@property
def local(self):
return bool(self.user)
@property
def avatar_absolute_url(self):
- return f'{"https://"}{FEDERATEDCODE_DOMAIN}{self.avatar.url}'
+ return self.avatar
# TODO raise error if the user doesn't have a user or remote actor
@property
@@ -425,13 +442,13 @@ def absolute_url_ap(self):
@property
def inbox_url(self):
if not self.local:
- return urljoin(self.remote_actor.url, "inbox")
+ return urljoin(self.remote_actor.safe_url, "inbox")
return full_reverse("user-inbox", self.user.username)
@property
def outbox_url(self):
if not self.local:
- return urljoin(self.remote_actor.url, "outbox")
+ return urljoin(self.remote_actor.safe_url, "outbox")
return full_reverse("user-outbox", self.user.username)
@property
@@ -443,7 +460,7 @@ def key_id(self):
if self.user:
return full_reverse("user-ap-profile", self.user.username) + "#main-key"
else:
- return self.remote_actor.url + "#main-key"
+ return self.remote_actor.safe_url + "#main-key"
@property
def to_ap(self):
@@ -604,7 +621,7 @@ class Vulnerability(models.Model):
)
class Meta:
- unique_together = ('id', 'repo')
+ unique_together = ("id", "repo")
@property
def absolute_url(self):
diff --git a/fedcode/pipes/utils.py b/fedcode/pipes/utils.py
index adc06c2..069fa74 100644
--- a/fedcode/pipes/utils.py
+++ b/fedcode/pipes/utils.py
@@ -19,6 +19,8 @@
def create_note(pkg, note_dict):
+ # TODO: also take argument for source of the note ideally github blob for
+ # for file.
note, _ = Note.objects.get_or_create(acct=pkg.acct, content=saneyaml.dump(note_dict))
pkg.notes.add(note)
create_activity = CreateActivity(actor=pkg.to_ap, object=note.to_ap)
diff --git a/fedcode/templates/403.html b/fedcode/templates/403.html
new file mode 100644
index 0000000..ef9c98c
--- /dev/null
+++ b/fedcode/templates/403.html
@@ -0,0 +1,21 @@
+{% extends "base.html" %}
+
+{% block title %}
+Permission Denied
+{% endblock %}
+
+{% block content %}
+
+ You do not have permission to access this page.
+
+ Sorry, the page you're looking for does not exist.
+
+
+
+
+
+ Permission Denied
+
+
+
+
+
+
+ 404 - Page Not Found
+
+