From 6a2ea41aa6edc4ce02323c24e45b9664b08ad8fa Mon Sep 17 00:00:00 2001 From: Ben Sturmfels Date: Mon, 29 Jul 2024 23:29:25 +1000 Subject: [PATCH 1/8] usethesource: Add prototype "upload offer for source" --- conservancy/usethesource/forms.py | 13 ++++- .../migrations/0009_sourceoffer.py | 30 ++++++++++++ conservancy/usethesource/models.py | 6 +++ .../templates/usethesource/upload_offer.html | 49 +++++++++++++++++++ conservancy/usethesource/urls.py | 1 + conservancy/usethesource/views.py | 20 +++++++- 6 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 conservancy/usethesource/migrations/0009_sourceoffer.py create mode 100644 conservancy/usethesource/templates/usethesource/upload_offer.html diff --git a/conservancy/usethesource/forms.py b/conservancy/usethesource/forms.py index 497c66f4..e7618c6d 100644 --- a/conservancy/usethesource/forms.py +++ b/conservancy/usethesource/forms.py @@ -1,6 +1,6 @@ from django import forms -from .models import Comment +from .models import Comment, SourceOffer class CommentForm(forms.ModelForm): @@ -17,3 +17,14 @@ class CommentForm(forms.ModelForm): class DownloadForm(forms.Form): agree = forms.BooleanField(label="I understand that the goal of this process is to determine compliance with FOSS licenses, and that in downloading the source code candidate and/or firmware image, I am assisting SFC as a volunteer to investigate that question. I, therefore, promise and represent that I will not copy, distribute, modify, or otherwise use this source code candidate and/or firmware image for any purpose other than to help SFC evaluate the source code candidate for compliance with the terms of FOSS licenses, including but not limited to any version of the GNU General Public License. Naturally, if I determine in good faith that portions of the source code candidate and/or firmware image are subject to a FOSS license and are compliant with it, I may copy, distribute, modify, or otherwise use those portions in accordance with the FOSS license, and I take full responsibility for that determination and subsequent use.") + + +class SourceOfferForm(forms.ModelForm): + class Meta: + model = SourceOffer + fields = ['vendor', 'device', 'photo'] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['photo'].widget.attrs['capture'] = 'camera' + self.fields['photo'].widget.attrs['accept'] = 'image/*' diff --git a/conservancy/usethesource/migrations/0009_sourceoffer.py b/conservancy/usethesource/migrations/0009_sourceoffer.py new file mode 100644 index 00000000..5eb1094d --- /dev/null +++ b/conservancy/usethesource/migrations/0009_sourceoffer.py @@ -0,0 +1,30 @@ +# Generated by Django 4.2.11 on 2024-07-22 08:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('usethesource', '0008_comment_attribute_to'), + ] + + operations = [ + migrations.CreateModel( + name='SourceOffer', + fields=[ + ( + 'id', + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name='ID', + ), + ), + ('vendor', models.CharField(max_length=50, verbose_name='Vendor name')), + ('device', models.CharField(max_length=50, verbose_name='Device name')), + ('photo', models.ImageField(upload_to='usethesource/offers')), + ], + ), + ] diff --git a/conservancy/usethesource/models.py b/conservancy/usethesource/models.py index 6b070e54..64e817f5 100644 --- a/conservancy/usethesource/models.py +++ b/conservancy/usethesource/models.py @@ -67,3 +67,9 @@ class Comment(models.Model): class Meta: ordering = ['id'] + + +class SourceOffer(models.Model): + vendor = models.CharField('Vendor name', max_length=50) + device = models.CharField('Device name', max_length=50) + photo = models.ImageField(upload_to='usethesource/offers') diff --git a/conservancy/usethesource/templates/usethesource/upload_offer.html b/conservancy/usethesource/templates/usethesource/upload_offer.html new file mode 100644 index 00000000..6c93a6a7 --- /dev/null +++ b/conservancy/usethesource/templates/usethesource/upload_offer.html @@ -0,0 +1,49 @@ +{% extends "usethesource/base.html" %} + +{% block title %}Upload an offer for source - Software Freedom Conservancy{% endblock %} + +{% block head %} + {{ block.super }} + +{% endblock %} + +{% block content %} + {{ block.super }} + +
+

Upload an offer for source

+
+ +
+ {% csrf_token %} + {{ form.non_field_errors }} +
+ {{ form.vendor.errors }} + + {{ form.vendor }} +
+
+ {{ form.device.errors }} + + {{ form.device }} +
+
+ {{ form.photo.errors }} + + {{ form.photo }} +
+ +
+ +
+
+ + +{% endblock content %} diff --git a/conservancy/usethesource/urls.py b/conservancy/usethesource/urls.py index ba7e3833..0b30afd9 100644 --- a/conservancy/usethesource/urls.py +++ b/conservancy/usethesource/urls.py @@ -13,4 +13,5 @@ urlpatterns = [ path('delete-comment///', views.delete_comment, name='delete_comment'), path('add-button//', views.add_button, name='add_button'), path('ccirt-process/', views.ccirt_process, name='ccirt_process'), + path('offer/', views.upload_offer, name='upload_offer'), ] diff --git a/conservancy/usethesource/views.py b/conservancy/usethesource/views.py index 6d6446ed..f31e5939 100644 --- a/conservancy/usethesource/views.py +++ b/conservancy/usethesource/views.py @@ -3,7 +3,7 @@ from django.core.exceptions import PermissionDenied from django.shortcuts import get_object_or_404, redirect, render from .models import Candidate, Comment -from .forms import CommentForm, DownloadForm +from .forms import CommentForm, DownloadForm, SourceOfferForm from .emails import make_comment_email @@ -91,3 +91,21 @@ def add_button(request, slug): def ccirt_process(request): return render(request, 'usethesource/ccirt_process.html', {}) + + +def handle_uploaded_file(f): + with open("some/file/name.txt", "wb+") as destination: + for chunk in f.chunks(): + destination.write(chunk) + +def upload_offer(request): + if request.method == 'POST': + form = SourceOfferForm(request.POST, request.FILES) + if form.is_valid(): + form.save() + return render(request, 'usethesource/upload_success_partial.html') + else: + return render(request, 'usethesource/upload_offer.html', {'form': form}) + else: + form = SourceOfferForm() + return render(request, 'usethesource/upload_offer.html', {'form': form}) From 5cc22b2db4c1b31d862ab455ebcf20970a260ae0 Mon Sep 17 00:00:00 2001 From: Ben Sturmfels Date: Mon, 29 Jul 2024 23:33:58 +1000 Subject: [PATCH 2/8] usethesource: Add missing template --- .../templates/usethesource/upload_success_partial.html | 1 + 1 file changed, 1 insertion(+) create mode 100644 conservancy/usethesource/templates/usethesource/upload_success_partial.html diff --git a/conservancy/usethesource/templates/usethesource/upload_success_partial.html b/conservancy/usethesource/templates/usethesource/upload_success_partial.html new file mode 100644 index 00000000..e945a161 --- /dev/null +++ b/conservancy/usethesource/templates/usethesource/upload_success_partial.html @@ -0,0 +1 @@ +

Thanks! We've received your offer for source.

From 33b40fb1d2e372fb5590b3e4d64c5649ca6f0c4b Mon Sep 17 00:00:00 2001 From: Ben Sturmfels Date: Mon, 29 Jul 2024 23:44:40 +1000 Subject: [PATCH 3/8] usethesource: Add admin interface --- conservancy/usethesource/admin.py | 9 ++++++++- .../migrations/0010_sourceoffer_time.py | 18 ++++++++++++++++++ conservancy/usethesource/models.py | 4 ++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 conservancy/usethesource/migrations/0010_sourceoffer_time.py diff --git a/conservancy/usethesource/admin.py b/conservancy/usethesource/admin.py index 6a886813..9081928f 100644 --- a/conservancy/usethesource/admin.py +++ b/conservancy/usethesource/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin from .emails import make_candidate_email -from .models import Candidate, Comment +from .models import Candidate, Comment, SourceOffer class CommentInline(admin.TabularInline): @@ -36,3 +36,10 @@ class CandidateAdmin(admin.ModelAdmin): # Announce the new candidate email = make_candidate_email(obj, request.user) email.send() + + +@admin.register(SourceOffer) +class SourceOfferAdmin(admin.ModelAdmin): + list_display = ['time', 'vendor', 'device'] + fields = ['time', 'vendor', 'device', 'photo'] + readonly_fields = ['time'] diff --git a/conservancy/usethesource/migrations/0010_sourceoffer_time.py b/conservancy/usethesource/migrations/0010_sourceoffer_time.py new file mode 100644 index 00000000..0f63063f --- /dev/null +++ b/conservancy/usethesource/migrations/0010_sourceoffer_time.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.11 on 2024-07-29 09:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('usethesource', '0009_sourceoffer'), + ] + + operations = [ + migrations.AddField( + model_name='sourceoffer', + name='time', + field=models.DateTimeField(auto_now_add=True, null=True), + ), + ] diff --git a/conservancy/usethesource/models.py b/conservancy/usethesource/models.py index 64e817f5..5ad68be3 100644 --- a/conservancy/usethesource/models.py +++ b/conservancy/usethesource/models.py @@ -70,6 +70,10 @@ class Comment(models.Model): class SourceOffer(models.Model): + time = models.DateTimeField(auto_now_add=True, null=True) vendor = models.CharField('Vendor name', max_length=50) device = models.CharField('Device name', max_length=50) photo = models.ImageField(upload_to='usethesource/offers') + + def __str__(self): + return f'{self.vendor} {self.device}' From 086ced0623b783244e5d5c7ec1cd4abd2d674eaa Mon Sep 17 00:00:00 2001 From: Ben Sturmfels Date: Tue, 30 Jul 2024 13:03:48 +1000 Subject: [PATCH 4/8] usethesource: Add link to offer upload --- .../usethesource/templates/usethesource/landing_page.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conservancy/usethesource/templates/usethesource/landing_page.html b/conservancy/usethesource/templates/usethesource/landing_page.html index 5274674c..496da5e1 100644 --- a/conservancy/usethesource/templates/usethesource/landing_page.html +++ b/conservancy/usethesource/templates/usethesource/landing_page.html @@ -24,7 +24,7 @@

One crucial way to get involved is to let us know about any source candidates you find! Many devices have an offer for source code (check the manual or device's user interface to find it) and we'd be very interested to know what they send you when you request it. Here are the steps to submit a new source candidate to list on this page:

    -
  1. find a source candidate offered by a company - normally this is offered to you in the manual or user interface of your device, through a link or email address (the company's GitHub page is not canonical, unless they explicitly say so in this offer)
  2. +
  3. find a source candidate offered by a company - normally this is offered to you in the manual or user interface of your device, through a link or email address (the company's GitHub page is not canonical, unless they explicitly say so in this offer). If you're curious what an offer is, check out the PDFs referenced in our submission to the FTC, and submit a picture/image of a new offer so we can test it for you if you like
  4. upload the source candidate to us - write down the file name(s) you uploaded for the next step (can be multiple), and upload a firmware image if you have it and are ok with us publishing it
  5. From 9ca2c4f13a6272234feb7bb84018033e68252b05 Mon Sep 17 00:00:00 2001 From: Denver Gingerich Date: Fri, 23 Aug 2024 10:29:46 -0700 Subject: [PATCH 5/8] Add remaining Vizio documents from this year --- conservancy/content/copyleft-compliance/vizio.html | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/conservancy/content/copyleft-compliance/vizio.html b/conservancy/content/copyleft-compliance/vizio.html index 26e1004a..cfb9aff2 100644 --- a/conservancy/content/copyleft-compliance/vizio.html +++ b/conservancy/content/copyleft-compliance/vizio.html @@ -49,10 +49,20 @@ Original Complaint (2021-10-19)
  6. Judge's ruling denying Vizio's Motion for Summary Judgment
  7. +
  8. SFC's +First Amended Complaint (2024-01-10)
  9. SFC's Motion for Summary Adjudication
  10. + Motion for Summary Adjudication +
  11. Vizio's + response to SFC's Motion for Summary Adjudication
  12. +
  13. SFC's + reply to Vizio's response to SFC's Motion for Summary Adjudication
  14. +
  15. Judge's + ruling partially granting SFC's Motion for Summary Adjudication
  16. +

    MEDIA CONTACT

    From 196baacc97491d79276d6494a39337201c474359 Mon Sep 17 00:00:00 2001 From: Ben Sturmfels Date: Thu, 5 Sep 2024 09:36:39 +1000 Subject: [PATCH 6/8] Django 4.2.16 security update --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0a14c626..188e4239 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ # Installed in virtualenv -Django==4.2.11 +Django==4.2.16 # Provided by Debian Bookworm. beautifulsoup4==4.11.2 html5lib==1.1 From d77d80973f1f38863881b806fcdd288cf040dff1 Mon Sep 17 00:00:00 2001 From: Ben Sturmfels Date: Tue, 8 Oct 2024 23:53:15 +1100 Subject: [PATCH 7/8] Use STORAGGES setting to avoid deprecation warning --- conservancy/settings/base.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/conservancy/settings/base.py b/conservancy/settings/base.py index 22ebd150..0ed97181 100644 --- a/conservancy/settings/base.py +++ b/conservancy/settings/base.py @@ -123,12 +123,20 @@ TIME_ZONE = 'America/New_York' LANGUAGE_CODE = 'en-us' USE_TZ = False +STORAGES = { + 'default': { + 'BACKEND': 'django.core.files.storage.FileSystemStorage', + }, + 'staticfiles': { + 'BACKEND': 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage', + }, +} + STATIC_URL = '/static/' STATIC_ROOT = BASE_DIR.parent / 'collected_static' STATICFILES_DIRS = [ BASE_DIR / 'static', ] -STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage' MEDIA_ROOT = BASE_DIR.parent / 'media' MEDIA_URL = '/media/' From f81bede42dec12f38b1471ae80a492024f909f24 Mon Sep 17 00:00:00 2001 From: Ben Sturmfels Date: Tue, 8 Oct 2024 23:53:47 +1100 Subject: [PATCH 8/8] Fix odd-looking red outline on focused form field Can't recall why I added this. It doesn't make much sense from a usability perspective as it suggests the field might be invalid. --- conservancy/static/css/conservancy.css | 6 ------ 1 file changed, 6 deletions(-) diff --git a/conservancy/static/css/conservancy.css b/conservancy/static/css/conservancy.css index fff8b583..ed671721 100644 --- a/conservancy/static/css/conservancy.css +++ b/conservancy/static/css/conservancy.css @@ -76,12 +76,6 @@ form[action$="#fixme"]:before { text-align: center; } -input:focus { - z-index: 3; - border-color: #86b7fe; - box-shadow: 0 0 0 .25rem rgb(236, 99, 67, .5); -} - video { max-width: 100%; }