Skip to content

Commit

Permalink
Merge pull request #1757 from MTG/attribution-button
Browse files Browse the repository at this point in the history
Attribution button
  • Loading branch information
ffont authored Mar 15, 2024
2 parents 59c2ade + b564d47 commit 37b7455
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 20 deletions.
35 changes: 32 additions & 3 deletions accounts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
from django.utils.http import int_to_base36
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_exempt
from general.templatetags.absurl import url2absurl
from oauth2_provider.models import AccessToken

import tickets.views as TicketViews
Expand Down Expand Up @@ -858,7 +859,9 @@ def attribution(request):

@login_required
def download_attribution(request):
content = {'csv': 'csv', 'txt': 'plain'}
content = {'csv': 'csv',
'txt': 'plain',
'json': 'json'}

qs_sounds = Download.objects.annotate(download_type=Value('sound', CharField()))\
.values('download_type', 'sound_id', 'sound__user__username', 'sound__original_filename',
Expand All @@ -871,9 +874,9 @@ def download_attribution(request):
qs = qs_sounds.union(qs_packs).order_by('-created')

download = request.GET.get('dl', '')
now = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
filename = f'{request.user}_{now}_attribution.{download}'
if download in ['csv', 'txt']:
now = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
filename = f'{request.user}_{now}_attribution.{download}'
response = HttpResponse(content_type=f'text/{content[download]}')
response['Content-Disposition'] = f'attachment; filename="{filename}"'
output = io.StringIO()
Expand All @@ -896,6 +899,32 @@ def download_attribution(request):
row['created']))
response.writelines(output.getvalue())
return response
elif download == 'json':
output = []
for row in qs:
if row['download_type'][0].upper() == 'S':
output.append({
'sound_url': url2absurl(reverse("sound", args=[row['sound__user__username'], row['sound_id']])),
'sound_name': row['sound__original_filename'],
'author_url': url2absurl(reverse("account", args=[row['sound__user__username']])),
'author_name': row['sound__user__username'],
'license_url': row['license__deed_url'] or row['sound__license__deed_url'],
'license_name': license_with_version(row['license__name'] or row['sound__license__name'],
row['license__deed_url'] or row['sound__license__deed_url']),
'timestamp': str(row['created'])
})
elif row['download_type'][0].upper() == 'P':
output.append({
'pack_url': url2absurl(reverse("pack", args=[row['sound__user__username'], row['sound_id']])),
'pack_name': row['sound__original_filename'],
'author_url': url2absurl(reverse("account", args=[row['sound__user__username']])),
'author_name': row['sound__user__username'],
'license_url': row['license__deed_url'] or row['sound__license__deed_url'],
'license_name': license_with_version(row['license__name'] or row['sound__license__name'],
row['license__deed_url'] or row['sound__license__deed_url']),
'timestamp': str(row['created'])
})
return JsonResponse(output, safe=False)
else:
return HttpResponseRedirect(reverse('accounts-attribution'))

Expand Down
6 changes: 6 additions & 0 deletions freesound/static/bw-frontend/src/components/modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ const handleGenericModal = (fetchContentUrl, onLoadedCallback, onClosedCallback,
if (onLoadedCallback !== undefined){
onLoadedCallback(modalContainer);
}

// Trigger modal loaded event in case it should be used by other components
const event = new CustomEvent("modalLoaded", {
detail: {fetchContentUrl, modalContainer},
});
document.dispatchEvent(event);

// If modal is activated with a param, add the param to the URL when opening the modal
if (modalActivationParam !== undefined){
Expand Down
2 changes: 1 addition & 1 deletion freesound/static/bw-frontend/src/components/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function makeSelect(container) {

selectElement.style.display = 'none';
const wrapper = wrapElement(
document.getElementById(selectElement.id),
selectElement,
document.createElement('div'),
select_i,
selected_index_text
Expand Down
50 changes: 39 additions & 11 deletions freesound/static/bw-frontend/src/pages/sound.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import './page-polyfills';
import {showToast} from '../components/toast';
import {playAtTime} from '../components/player/utils';
import {handleGenericModalWithForm} from '../components/modal';
import {addRecaptchaScriptTagToMainHead} from '../utils/recaptchaDynamicReload'
import { showToast } from '../components/toast';
import { playAtTime } from '../components/player/utils';
import { handleGenericModalWithForm, dismissModal } from '../components/modal';
import { addRecaptchaScriptTagToMainHead } from '../utils/recaptchaDynamicReload'
import { prepareAfterDownloadSoundModals } from '../components/afterDownloadModal.js';

const toggleEmbedCodeElement = document.getElementById('toggle-embed-code');
Expand All @@ -17,20 +17,23 @@ const urlParams = new URLSearchParams(window.location.search);

prepareAfterDownloadSoundModals();

const copyFromInputElement = (inputElement) => {
inputElement.select();
inputElement.setSelectionRange(0, 99999);
inputElement.execCommand("copy");
inputElement.getSelection().removeAllRanges();
}


const copyShareUrlToClipboard = (useFileURL) => {
var shareLinkInputElement = shareLinkElement.getElementsByTagName("input")[0];
console.log(shareLinkElement.dataset.staticFileUrl
, shareLinkElement.dataset.soundPageUrl)
if (useFileURL) {
shareLinkInputElement.value = shareLinkElement.dataset.staticFileUrl;
} else {
shareLinkInputElement.value = shareLinkElement.dataset.soundPageUrl;
}
shareLinkInputElement.select();
shareLinkInputElement.setSelectionRange(0, 99999);
document.execCommand("copy");
showToast('Sound URL copied in the clipboard');
document.getSelection().removeAllRanges();
copyFromInputElement(shareLinkInputElement);
showToast('Sound URL copied to the clipboard');
}

const toggleEmbedCode = () => {
Expand Down Expand Up @@ -143,3 +146,28 @@ if (flagSoundModalParamValue) {
flagSoundButton.addEventListener('click', (evt) => {
handleFlagSoundModal();
})


// Attribution modal
const handleAttributionModal = (modalContainer) => {
const selectElement = modalContainer.getElementsByTagName('select')[0];
const textArea = modalContainer.getElementsByTagName('textarea')[0];
textArea.value = selectElement.value;
selectElement.addEventListener('change', () => {
textArea.value = selectElement.value;
});

const buttons = modalContainer.getElementsByTagName('button');
const copyButton = buttons[buttons.length - 1];
copyButton.addEventListener('click', () => {
copyFromInputElement(textArea);
showToast('Attribution text copied to the clipboard');
dismissModal(modalContainer.id);
});
}

document.addEventListener('modalLoaded', (evt) => {
if (evt.detail.modalContainer.id === 'soundAttributionModal') {
handleAttributionModal(evt.detail.modalContainer);
}
});
1 change: 1 addition & 0 deletions freesound/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
path('people/<username>/sounds/<int:sound_id>/similar/', sounds.views.similar, name="sound-similar"),
path('people/<username>/sounds/<int:sound_id>/downloaders/', sounds.views.downloaders, name="sound-downloaders"),
path('people/<username>/sounds/<int:sound_id>/comments/', comments.views.for_sound, name="sound-comments"),
path('people/<username>/sounds/<int:sound_id>/attribution/', sounds.views.attribution_modal, name="sound-attribution"),
path('people/<username>/packs/', sounds.views.packs_for_user, name="packs-for-user"),
path('people/<username>/packs/<int:pack_id>/', sounds.views.pack, name="pack"),
path('people/<username>/packs/<int:pack_id>/section/stats/', sounds.views.pack_stats_section, name="pack-stats-section"),
Expand Down
25 changes: 25 additions & 0 deletions sounds/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
from comments.models import Comment
from freesound.celery import app as celery_app
from general import tasks
from general.templatetags.absurl import url2absurl
from geotags.models import GeoTag
from ratings.models import SoundRating
from general.templatetags.util import formatnumber
Expand Down Expand Up @@ -928,6 +929,30 @@ def get_license_history(self):
"""
return [(slh.created, slh.license) for slh in
self.soundlicensehistory_set.select_related('license').order_by('-created')]

@cached_property
def attribution_texts(self):
attribution_texts = {
'plain_text': f'{self.original_filename} by {self.user.username} -- {url2absurl(reverse("short-sound-link", args=[self.id]))} -- License: {self.license.name_with_version}',
'html': f'<a href="{url2absurl(self.get_absolute_url())}">{self.original_filename}</a> by <a href="{url2absurl(reverse("account", args=[self.user.username]))}">{self.user.username}</a> | License: <a href="{ self.license.deed_url }">{self.license.name_with_version}</a>',
'json': json.dumps({
'sound_url': url2absurl(self.get_absolute_url()),
'sound_name': self.original_filename,
'author_url': url2absurl(reverse("account", args=[self.user.username])),
'author_name': self.user.username,
'license_url': self.license.deed_url,
'license_name': self.license.name_with_version,
})
}
if not self.sources.exists():
return attribution_texts
else:
sources_attribution_texts = [s.attribution_texts for s in self.sources.all()]
attribution_texts['plain_text'] = "\n\n".join([attribution_texts['plain_text']] + [st['plain_text'] for st in sources_attribution_texts])
attribution_texts['html'] = "<br>\n".join([attribution_texts['html']] + [st['html'] for st in sources_attribution_texts])
attribution_texts['json'] = json.dumps([json.loads(attribution_texts['json'])] + [json.loads(st['json']) for st in sources_attribution_texts])
return attribution_texts


def get_sound_tags(self, limit=None):
"""
Expand Down
9 changes: 9 additions & 0 deletions sounds/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,15 @@ def flag(request, username, sound_id):
return render(request, 'sounds/modal_flag_sound.html', tvars)


def attribution_modal(request, username, sound_id):
if not request.GET.get('ajax'):
# If not loaded as a modal, redirect to the sound page with parameter to open modal
return HttpResponseRedirect(reverse('sound', args=[username, sound_id]) + '?attribution=1')
sound = get_object_or_404(Sound, id=sound_id)
tvars = {'sound': sound}
return render(request, 'sounds/modal_attribution.html', tvars)


def sound_short_link(request, sound_id):
sound = get_object_or_404(Sound, id=sound_id)
return redirect('sound', username=sound.user.username, sound_id=sound.id)
Expand Down
6 changes: 3 additions & 3 deletions templates/accounts/attribution.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
This is the list of files you have downloaded. When you use freesound samples under the Attribution or Attribution NonCommercial license, you have to <a href="{% url "wiki-page" "faq" %}#how-do-i-creditattribute">credit the original creator of the sound</a> in your work.
This list makes it easy to do so. "S" means sound, "P" means pack.
There are 3 flavors of this list: <a href="?format=regular">regular</a>, <a href="?format=html">html</a> or <a href="?format=plaintext">plain text</a>.
Alternatively, you can download the <b>complete record of your downloaded sounds</b> in <a href="{% url "accounts-download-attribution" %}?dl=csv">csv</a> or <a href="{% url "accounts-download-attribution" %}?dl=txt">plain text</a> formats.
Alternatively, you can download the <b>complete record of your downloaded sounds</b> in <a href="{% url "accounts-download-attribution" %}?dl=csv">csv</a>, <a href="{% url "accounts-download-attribution" %}?dl=txt">plain text</a>, or <a href="{% url "accounts-download-attribution" %}?dl=json">json</a> formats.
</p><p>

</p>
Expand All @@ -26,7 +26,7 @@ <h4 class="v-spacing-top-4">Downloaded on {{group.grouper}}</h4>
<ul>
{% for download_item in group.list %}
{% if download_item.download_type == 'sound' %}
<li>S: <a href="{% absurl 'sound' download_item.sound__user__username download_item.sound_id %}">{{download_item.sound__original_filename }}</a> by <a href="{% absurl 'account' download_item.sound__user__username %}">{{download_item.sound__user__username }}</a> | <span class="text-grey">License: </span>{% if download_item.license__name %}{{ download_item.license__name|license_with_version:download_item.license__deed_url}}{% else %}{{ download_item.sound__license__name|license_with_version:download_item.sound__license__deed_url }}{% endif %}</li>
<li>S: <a href="{% absurl 'sound' download_item.sound__user__username download_item.sound_id %}">{{download_item.sound__original_filename }}</a> by <a href="{% absurl 'account' download_item.sound__user__username %}">{{download_item.sound__user__username }}</a> | <span class="text-grey">License: </span>{% if download_item.license__name %}<a class="bw-link--black" href="{{ download_item.license__deed_url }}">{{ download_item.license__name|license_with_version:download_item.license__deed_url}}{% else %}<a class="bw-link--black" href="{{ download_item.sound__license__deed_url }}">{{ download_item.sound__license__name|license_with_version:download_item.sound__license__deed_url }}</a>{% endif %}</li>
{% else %}
{% comment %}NOTE: in the line below, even though we're displaying information about a pack download, we use download_item.X where X takes the same names as in the line above when we were displaying
information about sound download. This is beacuse after doing the uninon of the two QuerySets (see accounts.views.attribution) the names of the columns are "unified" and taken from the main QuerySet{% endcomment %}
Expand All @@ -45,7 +45,7 @@ <h4 class="v-spacing-top-4">Downloaded on {{group.grouper}}</h4>
{% for download_item in group.list %}
&nbsp;&nbsp;&nbsp;&nbsp;{% filter force_escape %}
{% if download_item.download_type == 'sound' %}
<li>S: <a href="{% absurl 'sound' download_item.sound__user__username download_item.sound_id %}">{{download_item.sound__original_filename }}</a> by <a href="{% absurl 'account' download_item.sound__user__username %}">{{download_item.sound__user__username }}</a> | License: {% if download_item.license__name %}{{ download_item.license__name|license_with_version:download_item.license__deed_url}}{% else %}{{ download_item.sound__license__name|license_with_version:download_item.sound__license__deed_url }}{% endif %}</li>
<li>S: <a href="{% absurl 'sound' download_item.sound__user__username download_item.sound_id %}">{{download_item.sound__original_filename }}</a> by <a href="{% absurl 'account' download_item.sound__user__username %}">{{download_item.sound__user__username }}</a> | License: {% if download_item.license__name %}<a href="{{ download_item.license__deed_url }}">{{ download_item.license__name|license_with_version:download_item.license__deed_url}}{% else %}<a href="{{ download_item.sound__license__deed_url }}">{{ download_item.sound__license__name|license_with_version:download_item.sound__license__deed_url }}</a>{% endif %}</li>
{% else %}
{% comment %}NOTE: in the line below, even though we're displaying information about a pack download, we use download_item.X where X takes the same names as in the line above when we were displaying
information about sound download. This is beacuse after doing the uninon of the two QuerySets (see accounts.views.attribution) the names of the columns are "unified" and taken from the main QuerySet{% endcomment %}
Expand Down
28 changes: 28 additions & 0 deletions templates/sounds/modal_attribution.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{% extends "molecules/modal_base.html" %}
{% load util %}

{% block id %}soundAttributionModal{% endblock %}
{% block extra-class %}{% endblock %}
{% block aria-label %}Get attribution text{% endblock %}

{% block body %}
<div class="col-12">
<div class="text-center">
<h4 class="v-spacing-5">Get attribution text</h4>
</div>
<div class="v-spacing-4">
<div class="bw-form">
<div>
Format:
<select>
<option value="{{ sound.attribution_texts.plain_text|safe|force_escape }}">Plain text</option>
<option value="{{ sound.attribution_texts.html|safe|force_escape }}">HTML</option>
<option value="{{ sound.attribution_texts.json|safe|force_escape }}">JSON</option>
</select>
</div>
<textarea></textarea>
</div>
<button class="btn-primary v-spacing-top-1 w-100">Copy text</button>
</div>
</div>
{% endblock %}
4 changes: 2 additions & 2 deletions templates/sounds/sound.html
Original file line number Diff line number Diff line change
Expand Up @@ -250,10 +250,10 @@ <h6 class="text-26 no-margins ">Comments</h6>
</ol>
</div>
<div class="v-spacing-top-5 middle">
{% bw_icon sound.license_bw_icon_name 'text-light-grey text-30' %} <span class="text-20 h-spacing-left-2 padding-right-4">{{ sound.license.name_with_version }}</span>
{% bw_icon sound.license_bw_icon_name 'text-light-grey text-30' %} <a title="Go to the full license text" href="{{ sound.license.deed_url }}" target="_blank" class="bw-link--black text-20 h-spacing-left-2 padding-right-4">{{ sound.license.name_with_version }}</a>
</div>
<div class="text-grey {% if sound.user == request.user %}v-spacing-4{% endif %} v-spacing-top-1">
{{ sound.license.get_short_summary|safe }} <a href="{{ sound.license.deed_url }}" target="_blank" class="bw-link--black">More...</a>
{{ sound.license.get_short_summary|safe }} <a href="javascript:void(0);" data-toggle="modal-default" data-modal-content-url="{% url 'sound-attribution' sound.user.username sound.id %}?ajax=1" data-modal-activation-param="attribution" class="bw-link--black">Get attribution text...</a>
</div>
{% endcache %}

Expand Down

0 comments on commit 37b7455

Please sign in to comment.