Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add online video link to menu #150

Merged
merged 15 commits into from
Sep 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 81 additions & 22 deletions src/pretalx/agenda/templates/agenda/header_row.html
Original file line number Diff line number Diff line change
@@ -1,30 +1,88 @@
{% load i18n %}
{% load static %}
{% load compress %}

<nav id="schedule-nav"{% if with_margin %} class="p-3"{% endif %}>
<div class="navigation">
<a href="{{ request.event.urls.schedule }}" class="btn btn-outline-success {% if "/schedule/" in request.path %} active{% endif %}">
<i class="fa fa-calendar"></i> {% translate "Schedule" %}
</a>
{% if request.event.display_settings.schedule_display != "list" %}
<a href="{{ request.event.urls.talks }}" class="btn btn-outline-success {% if "/talk/" in request.path %} active{% endif %}">
<i class="fa fa-comments-o"></i> {% translate "Sessions" %}
</a>
{% endif %}
<a href="{{ request.event.urls.speakers }}" class="btn btn-outline-success {% if "/speaker/" in request.path %} active{% endif %}">
<i class="fa fa-group"></i> {% translate "Speakers" %}
</a>
{% if request.event.display_settings.ticket_link %}
<a href="{{ request.event.display_settings.ticket_link }}" class="btn btn-outline-success">
<i class="fa fa-ticket"></i> {% translate "Tickets" %}
</a>
{% endif %}
</div>
<div class="header-right">
{% block agenda_header_row %}
{% compress js %}
<script src="{% static "jquery/js/jquery-3.7.1.min.js" %}"></script>
<script src="{% static "agenda/js/join-online-event.js" %}"></script>
{% endcompress %}
{% endblock %}

{% block agenda_content %}
<div id="join-video-popupmodal" hidden aria-live="polite">
<div class="modal-card">
<div class="modal-card-content-join">
<div>
<h3>
{% trans "This is a ticketed event. If you ordered a ticket as a guest user, you don't have an account but can still access the ticket using the secret link in your ticket confirmation email. On the tickets page there is also a link to join online sessions." %}
</h3>
<h3>
{% trans "As a ticket holder please also check your email for the unique link to join online sessions." %}
</h3>
<p class="text">
{% trans "Can't find your ticket?" %}
<a id="resend_link" class="btn btn-default" href='{{ request.event.display_settings.ticket_link }}resend'>
{% trans "Resend the email" %}
</a>
{% trans "to receive it." %}
</p>
<p class="text">
{% trans "Want to order a ticket?" %}
<a id="ticket_link" class="btn btn-default" href='{{ request.event.display_settings.ticket_link }}'>
{% trans "Get a ticket here" %}
</a>
</p>
</div>
</div>
<div class="join-online-close">
<button id="join-online-close-button" class="btn btn-default join-online-close-button">Close</button>
</div>
</div>
</div>
<div id="join-video-popupmodal-missing-config" hidden aria-live="polite">
<div class="modal-card">
<div class="modal-card-content-join">
<div>
<h3>
{% trans "Some configurations is missing for video setting, please contact organizer." %}
</h3>
</div>
</div>
<div class="join-online-close">
<button id="join-online-close-button-missing-config" class="btn btn-default join-online-close-button">Close</button>
</div>
</div>
</div>
<nav id="schedule-nav"{% if with_margin %} class="p-3"{% endif %}>
<div class="navigation">
<a href="{{ request.event.urls.schedule }}" class="btn btn-outline-success {% if "/schedule/" in request.path %} active{% endif %}">
<i class="fa fa-calendar"></i> {% translate "Schedule" %}
</a>
{% if request.event.display_settings.schedule_display != "list" %}
<a href="{{ request.event.urls.talks }}" class="btn btn-outline-success {% if "/talk/" in request.path %} active{% endif %}">
<i class="fa fa-comments-o"></i> {% translate "Sessions" %}
</a>
{% endif %}
<a href="{{ request.event.urls.speakers }}" class="btn btn-outline-success {% if "/speaker/" in request.path %} active{% endif %}">
<i class="fa fa-group"></i> {% translate "Speakers" %}
</a>
{% if request.event.display_settings.ticket_link %}
<a href="{{ request.event.display_settings.ticket_link }}" class="btn btn-outline-success">
<i class="fa fa-group"></i> {% translate "Tickets" %}
</a>
{% endif %}

<a id="join-event-link" href='{% url "agenda:event.onlinevideo.join" event=request.event.slug %}' class="btn btn-outline-success">
<i class="fa fa-video-camera"></i> {% translate "Videos" %}
</a>
</div>
<div class="header-right">
{% if with_extra %}
<span>{% translate "Version" %} <a href="{{ request.event.urls.changelog }}">{{ schedule.version|default:"–" }}</a></span>
<details class="dropdown">
<summary class="btn btn-sm btn-outline-info">
<i class="fa fa-calendar-plus-o"></i>
<i class="fa fa-calendar-plus-o"></i>
<span class="calendar-text"> Add to Calendar </span>
<i class="fa fa-caret-down"></i>
</summary>
Expand Down Expand Up @@ -70,4 +128,5 @@
</details>
{% endif %}
</div>
</nav>
</nav>
{% endblock %}
5 changes: 5 additions & 0 deletions src/pretalx/agenda/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ def get_schedule_urls(regex_prefix, name_prefix=""):
EventSocialMediaCard.as_view(),
name="event-social",
),
path(
"online-video/join/",
talk.OnlineVideoJoin.as_view(),
name="event.onlinevideo.join",
),
]
),
),
Expand Down
103 changes: 100 additions & 3 deletions src/pretalx/agenda/views/talk.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
from urllib.parse import urlparse
import datetime as dt
from urllib.parse import unquote, urlparse

import jwt
import requests
import vobject
from django.conf import settings
from django.contrib import messages
from django.db.models import Q
from django.http import Http404, HttpResponse
from django.http import Http404, HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, render
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
from django.views import View
from django.views.generic import DetailView, FormView, TemplateView
from django_context_decorator import context

from pretalx.agenda.signals import register_recording_provider
from pretalx.cfp.views.event import EventPageMixin
from pretalx.common.mixins.views import PermissionRequired, SocialMediaCardMixin
from pretalx.common.mixins.views import (
EventPermissionRequired,
PermissionRequired,
SocialMediaCardMixin,
)
from pretalx.common.text.phrases import phrases
from pretalx.schedule.models import Schedule, TalkSlot
from pretalx.submission.forms import FeedbackForm
Expand Down Expand Up @@ -264,3 +272,92 @@ def get_success_url(self):
class TalkSocialMediaCard(SocialMediaCardMixin, TalkView):
def get_image(self):
return self.get_object().image


class OnlineVideoJoin(EventPermissionRequired, View):
permission_required = "agenda.view_schedule"

def get(self, request, *args, **kwargs):
# First check video is configured or not
if (
"pretalx_venueless" not in request.event.plugin_list
or not request.event.venueless_settings
or not request.event.venueless_settings.join_url
or not request.event.venueless_settings.secret
or not request.event.venueless_settings.issuer
or not request.event.venueless_settings.audience
):
return HttpResponse(status=403, content="missing_configuration")

if not request.user.is_authenticated:
# redirect to login page if user not logged in yet
return HttpResponse(status=403, content="user_not_allowed")

# prepare event data to check from ticket
if "ticket_link" not in request.event.display_settings:
return HttpResponse(status=403, content="missing_configuration")

base_url, organizer, event = self.retrieve_info_from_url(
request.event.display_settings["ticket_link"]
)

if not organizer or not event or not base_url:
return HttpResponse(status=403, content="missing_configuration")

# call to ticket to check if user order ticket yet or not
response = requests.get(
f"{base_url}/api/v1/{organizer}/{event}/customer/{request.user.code}/ticket-check"
)

if response.status_code != 200:
return HttpResponse(status=403, content="user_not_allowed")

else:
# Redirect user to online event
iat = dt.datetime.utcnow()
exp = iat + dt.timedelta(days=30)
profile = {
"display_name": request.user.name,
"fields": {
"pretalx_id": request.user.code,
},
}
if request.user.avatar_url:
profile["profile_picture"] = request.user.get_avatar_url(request.event)

payload = {
"iss": request.event.venueless_settings.issuer,
"aud": request.event.venueless_settings.audience,
"exp": exp,
"iat": iat,
"uid": request.user.code,
"profile": profile,
"traits": list(
{
f"eventyay-video-event-{request.event.slug}",
}
),
}
token = jwt.encode(
payload, request.event.venueless_settings.secret, algorithm="HS256"
)

return JsonResponse(
{
"redirect_url": "{}/#token={}".format(
request.event.venueless_settings.join_url, token
).replace("//#", "/#")
},
status=200,
)

def retrieve_info_from_url(self, url):
parsed_url = urlparse(url)
ticket_host = settings.EVENTYAY_TICKET_BASE_PATH
path = parsed_url.path
parts = path.strip("/").split("/")
if len(parts) >= 2:
organizer, event = parts[-2:]
return ticket_host, unquote(organizer), unquote(event)
else:
return ticket_host, None, None
9 changes: 9 additions & 0 deletions src/pretalx/orga/forms/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,14 @@ class EventForm(ReadOnlyFlag, I18nHelpText, JsonSubfieldMixin, I18nModelForm):
),
required=False,
)
video_link = forms.URLField(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider refactoring the URL field definitions to reduce redundancy and improve maintainability.

The new code introduces some complexity and redundancy by adding a video_link field that is very similar to the existing ticket_link field. This can lead to maintenance issues and makes the form definition longer and harder to read. To improve maintainability and readability, consider refactoring the code to use a common method for defining URL fields. Here's an example:

def create_url_field(label, help_text, placeholder):
    return forms.URLField(
        label=label,
        help_text=help_text,
        widget=forms.TextInput(attrs={'placeholder': placeholder}),
        required=False,
    )

class EventForm(forms.Form):
    ticket_link = create_url_field(
        label=_("Event ticket shop URL"),
        help_text=_("Ticket shop link will be shown on event menu."),
        placeholder='e.g: https://tickets-dev.eventyay.com/2024/wikimania/'
    )
    video_link = create_url_field(
        label=_("Video Live URL"),
        help_text=_("Online video link will be shown on event menu."),
        placeholder='e.g: https://wikimania-live.eventyay.com/'
    )
    header_pattern = forms.ChoiceField(
        label=_("Frontpage header pattern"),
        help_text=_(
            "Choose how the frontpage header banner will be styled if you don’t upload an image. Pattern source: "
            '<a href="http://www.heropatterns.com/">heropatterns.com</a>, CC BY 4.0.'
        ),
        choices=(
            # Add your choices here
        ),
        required=False,
    )

    json_fields = {
        "imprint_url": "display_settings",
        "show_schedule": "feature_flags",
        "schedule": "display_settings",
        "show_featured": "feature_flags",
        "use_feedback": "feature_flags",
        "export_html_on_release": "feature_flags",
        "html_export_url": "display_settings",
        "ticket_link": "display_settings",
        "video_link": "display_settings",
        "header_pattern": "display_settings",
        "meta_noindex": "display_settings",
    }

This refactoring reduces redundancy and makes the code easier to maintain.

label=_("Video Live URL"),
help_text=("Online video link will be shown on event menu."),
widget=forms.TextInput(
attrs={"placeholder": " e.g: https://app.eventyay.com/video"}
),
required=False,
)
header_pattern = forms.ChoiceField(
label=_("Frontpage header pattern"),
help_text=_(
Expand Down Expand Up @@ -369,6 +377,7 @@ class Meta:
"export_html_on_release": "feature_flags",
"html_export_url": "display_settings",
"ticket_link": "display_settings",
"video_link": "display_settings",
"header_pattern": "display_settings",
"meta_noindex": "display_settings",
}
Expand Down
1 change: 1 addition & 0 deletions src/pretalx/orga/templates/orga/settings/form.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ <h2>{% translate "Settings" %}</h2>
{% bootstrap_field form.html_export_url layout='event' %}
{% bootstrap_field form.meta_noindex layout='event' %}
{% bootstrap_field form.ticket_link layout='event' %}
{% bootstrap_field form.video_link layout='event' %}

</fieldset>
<fieldset>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% extends "./base.html" %}
{% extends "socialaccount/base.html" %}
{% load bootstrap4 %}
{% load compress %}
{% load i18n %}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% extends "./base.html" %}
{% extends "socialaccount/base.html" %}
{% load bootstrap4 %}
{% load compress %}
{% load i18n %}
Expand Down
41 changes: 41 additions & 0 deletions src/pretalx/static/agenda/js/join-online-event.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
$(function () {
var popup_window = null
var popup_check_interval = null

$("#join-event-link").on("click", function (e) {
e.preventDefault(); // prevent the default action (redirecting to the href)
var url = $(this).attr('href'); // get the href attribute
$.ajax({
"method": "GET",
"url": url,
"success": function(json) {
if(json.redirect_url) {
window.location.href = json.redirect_url;
}
},
"error": function(jqXHR, textStatus, errorThrown) {
// handle any errors that occur while making the AJAX request
$("body").addClass("has-join-popup")
if(jqXHR.responseText === 'user_not_allowed') {
// handle 'user_not_allowed' error
$("body").addClass("has-join-popup")
$("#join-video-popupmodal").removeAttr("hidden");
} else if (jqXHR.responseText === 'missing_configuration'){
// handle other errors
$("body").addClass("has-join-popup")
$("#join-video-popupmodal-missing-config").removeAttr("hidden");

}
}
});
});
$('#join-online-close-button').click(function() {
$('#join-video-popupmodal').attr('hidden', 'true');
$("body").removeClass("has-join-popup")
});
$('#join-online-close-button-missing-config').click(function() {
$('#join-video-popupmodal-missing-config').attr('hidden', 'true');
$("body").removeClass("has-join-popup")
});
})

Loading
Loading