From 37dd1a25cb6e6c447c6a1cab580831337d139b60 Mon Sep 17 00:00:00 2001 From: MattBild <34771705+mattbild@users.noreply.github.com> Date: Tue, 29 Aug 2023 15:12:00 +0200 Subject: [PATCH 1/7] [DONE] fix filters on events (#916) * fix filters on events * flake8 changes needed * events : hide/ show aside filters * Js doc + aria-hidden sur icones * change html and css to match video cards * ajout submit bouton invisible sur fieldset * add JsDoc * translation and pydoc * css in dedicated file * edit pydoc * remove deprecated request.is_ajax() * edit pydoc --- pod/live/static/css/event.css | 6 +- pod/live/static/css/event_list.css | 4 + pod/live/static/js/filter_aside_event_list.js | 128 ++++++++++++++++++ pod/live/templates/live/direct.html | 16 +-- pod/live/templates/live/directs.html | 2 +- pod/live/templates/live/directs_all.html | 10 +- pod/live/templates/live/event-all-info.html | 2 +- pod/live/templates/live/event-info.html | 16 +-- pod/live/templates/live/event-script.html | 2 +- pod/live/templates/live/event.html | 18 +-- pod/live/templates/live/event_card.html | 4 +- pod/live/templates/live/event_delete.html | 2 +- pod/live/templates/live/event_edit.html | 6 +- .../templates/live/event_immediate_edit.html | 2 +- pod/live/templates/live/event_videos.html | 4 +- pod/live/templates/live/events.html | 11 +- pod/live/templates/live/events_list.html | 88 +++++++----- pod/live/templates/live/events_next.html | 4 +- pod/live/templates/live/filter_aside.html | 44 +++--- pod/live/templates/live/my_events.html | 10 +- pod/live/utils.py | 2 +- pod/live/views.py | 118 +++++++++++----- pod/video_encode_transcript/utils.py | 2 +- 23 files changed, 351 insertions(+), 150 deletions(-) create mode 100644 pod/live/static/css/event_list.css create mode 100644 pod/live/static/js/filter_aside_event_list.js diff --git a/pod/live/static/css/event.css b/pod/live/static/css/event.css index 331566be25..16bfe0589e 100644 --- a/pod/live/static/css/event.css +++ b/pod/live/static/css/event.css @@ -6,4 +6,8 @@ } .video-js .vjs-remaining-time { display: none; -} \ No newline at end of file +} + .filter-event-img { + max-height:32px; + max-width:32px; +} diff --git a/pod/live/static/css/event_list.css b/pod/live/static/css/event_list.css new file mode 100644 index 0000000000..d8b38cec81 --- /dev/null +++ b/pod/live/static/css/event_list.css @@ -0,0 +1,4 @@ +.event-card-container { + min-width: 12rem; + min-height: 11rem; +} diff --git a/pod/live/static/js/filter_aside_event_list.js b/pod/live/static/js/filter_aside_event_list.js new file mode 100644 index 0000000000..d49bec6f4c --- /dev/null +++ b/pod/live/static/js/filter_aside_event_list.js @@ -0,0 +1,128 @@ + + + let loader = document.querySelector(".lds-ring"); + let checkedInputs = []; + + /** + * Enable /disable all checkboxes. + * + * @param {boolean} value + */ + function disableCheckboxes(value) { + document + .querySelectorAll("input[type=checkbox]") + .forEach((checkbox) => { + checkbox.disabled = value; + }); + } + + /** + * Return url with filters params. + * + * @returns {string} + */ + function getUrlForRefresh() { + let newUrl = window.location.pathname + "?"; + + checkedInputs.forEach((input) => { + newUrl += input.name + "=" + input.value + "&"; + }); + + // Add page parameter + newUrl += "page=1"; + return newUrl; + } + + /** + * Remove loader height. + * + * @param height + * @returns {number} + */ + function getHeightMinusLoader(height) { + let loader_style = getComputedStyle(loader); + let loader_height = loader_style.height; + loader_height = loader_height.replace("px", ""); + return height - loader_height; + } + + /** + * Async request to refresh view with filtered events list. + */ + function refreshEvents() { + + // Erase list and enable loader + const events_content = document.getElementById("events_content"); + let width = events_content.offsetWidth; + let height = getHeightMinusLoader(events_content.offsetHeight); + + events_content.innerHTML = "
"; + loader.classList.add("show"); + + let url = getUrlForRefresh(); + + // Async GET request wth parameters by fetch method + fetch(url, { + method: "GET", + headers: { + "X-CSRFToken": "{{ csrf_token }}", + "X-Requested-With": "XMLHttpRequest" + }, + cache: "no-store" + }) + .then((response) => response.text()) + .then((data) => { + // parse data into html and replace videos list + let parser = new DOMParser(); + let html = parser.parseFromString(data, "text/html").body; + events_content.outerHTML = html.innerHTML; + + // change url with params sent + window.history.pushState({}, "", url); + }) + .catch(() => { + events_content.innerHTML = gettext("An Error occurred while processing."); + }) + .finally(() => { + // Re-enable inputs and dismiss loader + disableCheckboxes(false); + loader.classList.remove("show"); + }); + } + + /** + * Check or uncheck checkbox regarding url params. + * + * @param el + */ + function setCheckboxStatus(el) { + let currentUrl = window.location.href; + el.checked = currentUrl.includes("type="+ el.value + "&"); + } + + /** + * Add listener to refresh events list on checkbox status change. + * + * @param el + */ + function addCheckboxListener(el) { + el.addEventListener("change", () => { + checkedInputs = []; + disableCheckboxes(true); + document + .querySelectorAll("#collapseFilterType input[type=checkbox]:checked") + .forEach((e) => { + checkedInputs.push(e); + }); + refreshEvents(); + }); + } + + // On page load + document + .querySelectorAll("#collapseFilterType input[type=checkbox]") + .forEach((el) => { + setCheckboxStatus(el); + addCheckboxListener(el); + }); + diff --git a/pod/live/templates/live/direct.html b/pod/live/templates/live/direct.html index 6f7228f493..00775ef3f4 100644 --- a/pod/live/templates/live/direct.html +++ b/pod/live/templates/live/direct.html @@ -33,7 +33,7 @@

- + {% trans "Plan an event" %} @@ -65,7 +65,7 @@

- {% blocktrans %}Recording in progress{% endblocktrans %} + {% blocktrans %}Recording in progress{% endblocktrans %}
{% else %} @@ -93,12 +93,12 @@

{% trans "Next events" %}

{% if request.user.is_superuser %}

- {% trans "Manage broadcaster" %} + {% trans "Manage broadcaster" %}

@@ -106,7 +106,7 @@

- + {{ broadcaster.building.name }} @@ -127,12 +127,12 @@

{% if otherbroadcaster.status %} - + {{ otherbroadcaster.name }} {% else %} - + {{ otherbroadcaster.name }} ({% trans "no broadcast in progress" %}) {% endif %} @@ -146,7 +146,7 @@

- + {% trans "access map" %} diff --git a/pod/live/templates/live/directs.html b/pod/live/templates/live/directs.html index 177f0b4de8..fb30bdb0a6 100644 --- a/pod/live/templates/live/directs.html +++ b/pod/live/templates/live/directs.html @@ -15,7 +15,7 @@ {% block page_title %}{{building.name}}{% endblock %} {% block page_content %} -

 {{building.name}}

+

 {{building.name}}

{% if building.headband %} {{building.name}} {% endif %} diff --git a/pod/live/templates/live/directs_all.html b/pod/live/templates/live/directs_all.html index a0acc49485..5fe9a7ec26 100644 --- a/pod/live/templates/live/directs_all.html +++ b/pod/live/templates/live/directs_all.html @@ -17,7 +17,7 @@ {% block page_title %}{% trans "Lives" %}{% endblock %} {% block page_content %} -

 {% trans "Lives" %}

+

 {% trans "Lives" %}

{% for building in buildings %} @@ -29,20 +29,20 @@
{{building.name}} - +
{% for broadcaster in building.broadcaster_set.all %}

- {% if broadcaster.status %} {{broadcaster.name}} - {% else %} {{broadcaster.name}} ({% trans "no broadcast in progress" %}){% endif %} + {% if broadcaster.status %} {{broadcaster.name}} + {% else %} {{broadcaster.name}} ({% trans "no broadcast in progress" %}){% endif %}

{% empty %}

{% trans "Sorry, no lives found." %}

{% endfor %}
{% if building.gmapurl %} -

+

{% trans "access map" %}

{% endif %} diff --git a/pod/live/templates/live/event-all-info.html b/pod/live/templates/live/event-all-info.html index 71f755b4e3..05d2d4a499 100644 --- a/pod/live/templates/live/event-all-info.html +++ b/pod/live/templates/live/event-all-info.html @@ -7,7 +7,7 @@ {% if event.description %} {% endif %} diff --git a/pod/live/templates/live/event-info.html b/pod/live/templates/live/event-info.html index eb6a845f56..432647ba0f 100644 --- a/pod/live/templates/live/event-info.html +++ b/pod/live/templates/live/event-info.html @@ -6,7 +6,7 @@
{% if event.description %}
-

 {% trans 'Summary' %}

+

 {% trans 'Summary' %}


{{ event.description|safe }}

@@ -62,7 +62,7 @@

{% trans 'Type:' %}

{% if display_creation_button %} @@ -36,10 +36,10 @@

{% trans "Please use the thumbnails toolbar which is located under the event on which you want to work with." %}

{% trans "Coming events" %}

- {% include "live/events_list.html" with events=coming_events urlpage=coming_events_url urland=past_events_url_page %} + {% include "live/events_list.html" with hide_counter=True events=coming_events urlpage=coming_events_url urland=past_events_url_page %}

{% trans "Past events" %}

- {% include "live/events_list.html" with events=past_events urlpage=past_events_url urland=coming_events_url_page %} + {% include "live/events_list.html" with hide_counter=True events=past_events urlpage=past_events_url urland=coming_events_url_page %} {% endif %} diff --git a/pod/live/utils.py b/pod/live/utils.py index 47e84045c0..5fb5503d38 100644 --- a/pod/live/utils.py +++ b/pod/live/utils.py @@ -94,7 +94,7 @@ def get_event_url(event): def get_bcc(manager): - if type(manager) in (list, tuple): + if isinstance(manager, (list, tuple)): return manager elif isinstance(manager, str): return [manager] diff --git a/pod/live/views.py b/pod/live/views.py index 1eba4a1c12..3fd202103f 100644 --- a/pod/live/views.py +++ b/pod/live/views.py @@ -49,7 +49,7 @@ ) from ..main.utils import is_ajax from ..main.views import in_maintenance -from ..video.models import Video +from ..video.models import Video, Type HEARTBEAT_DELAY = getattr(settings, "HEARTBEAT_DELAY", 45) @@ -72,7 +72,8 @@ @login_required(redirect_field_name="referrer") -def directs_all(request): # affichage des directs +def directs_all(request): + """Show all directs.""" check_permission(request) site = get_current_site(request) @@ -96,7 +97,8 @@ def directs_all(request): # affichage des directs @login_required(redirect_field_name="referrer") -def directs(request, building_id): # affichage des directs d'un batiment +def directs(request, building_id): + """Show all directs of a given building.""" check_permission(request) building = get_object_or_404(Building, id=building_id) return render( @@ -107,7 +109,8 @@ def directs(request, building_id): # affichage des directs d'un batiment @login_required(redirect_field_name="referrer") -def direct(request, slug): # affichage du flux d'un diffuseur +def direct(request, slug): + """Show the stream of a given broadcaster.""" check_permission(request) site = get_current_site(request) @@ -251,7 +254,6 @@ def is_in_event_groups(user, evt): def get_event_access(request, evt, slug_private, is_owner): """Return True if access is granted to current user.""" - if is_owner: return True @@ -278,9 +280,9 @@ def get_event_access(request, evt, slug_private, is_owner): return True -def event(request, slug, slug_private=None): # affichage d'un event - # modif de l'url d'appel pour compatibilité - # avec le template link_video.html (variable: urleditapp) +def event(request, slug, slug_private=None): + """Show an event.""" + # change request url for compatibility purpose with template link_video.html (var: urleditapp) request.resolver_match.namespace = "" try: @@ -361,12 +363,26 @@ def render_event_template(request, evemnt, user_owns_event): def events(request): - """Affichage des events.""" - # Tous les events à venir (sauf les drafts sont affichés) - queryset = Event.objects.filter(end_date__gt=timezone.now(), is_draft=False) + """Show all events.""" + # All events not ended are shown (except drafts) + queryset = Event.objects.filter( + end_date__gt=timezone.now(), + broadcaster__building__sites__exact=get_current_site(request), + is_draft=False, + ) + + available_types = Type.objects.filter(event__in=queryset.all()).distinct() + + # can be filtered by Type + filter_type = request.GET.getlist("type") + if filter_type: + queryset = queryset.filter(type__slug__in=filter_type) + events_list = queryset.all().order_by("start_date", "end_date") + # pagination page = request.GET.get("page", 1) + full_path = "" if page: full_path = ( @@ -383,12 +399,25 @@ def events(request): except EmptyPage: events_found = paginator.page(paginator.num_pages) + if is_ajax(request): + return render( + request, + "live/events_list.html", + { + "events": events_found, + "full_path": full_path, + "count_events": events_list.count(), + }, + ) + return render( request, "live/events.html", { + "count_events": events_list.count(), "events": events_found, "full_path": full_path, + "types": available_types, "DEFAULT_EVENT_THUMBNAIL": DEFAULT_EVENT_THUMBNAIL, "display_broadcaster_name": False, "display_direct_button": request.user.is_superuser @@ -403,6 +432,7 @@ def events(request): @ensure_csrf_cookie @login_required(redirect_field_name="referrer") def my_events(request): + """Show owner's events.""" queryset = ( request.user.event_set.all().distinct() | request.user.owners_events.all().distinct() @@ -414,7 +444,7 @@ def my_events(request): coming_events = [evt for evt in queryset if not evt.is_past()] coming_events = sorted(coming_events, key=lambda evt: (evt.start_date, evt.end_date)) - events_number = len(past_events) + len(coming_events) + count_events = len(past_events) + len(coming_events) PREVIOUS_EVENT_URL_NAME = "ppage" NEXT_EVENT_URL_NAME = "npage" @@ -449,7 +479,7 @@ def my_events(request): { "full_path": full_path, "types": request.GET.getlist("type"), - "events_number": events_number, + "count_events": count_events, "past_events": past_events, "past_events_url": PREVIOUS_EVENT_URL_NAME, "past_events_url_page": PREVIOUS_EVENT_URL_NAME + "=" + str(pageP), @@ -465,6 +495,7 @@ def my_events(request): def get_event_edition_access(request, evt): + """Check if user can edit the event.""" # creation if evt is None: return can_manage_event(request.user) @@ -593,6 +624,7 @@ def event_delete(request, slug=None): @csrf_protect @login_required(redirect_field_name="referrer") def event_immediate_edit(request, broadcaster_id=None): + """Edition of an immediate event (created with QrCode).""" if in_maintenance(): return redirect(reverse("maintenance")) @@ -645,6 +677,10 @@ def event_immediate_edit(request, broadcaster_id=None): def broadcasters_from_building(request): + """Retrieve broadcasters of a given building. + + Returns: an HttpResponse. + """ building_name = request.GET.get("building") if not building_name: return HttpResponseBadRequest() @@ -664,6 +700,10 @@ def broadcasters_from_building(request): def broadcaster_restriction(request): + """Check if broadcaster's access is restricted. + + Returns: a JsonResponse. + """ if request.method == "GET": event_id, broadcaster_id = get_event_id_and_broadcaster_id(request) if not broadcaster_id: @@ -706,6 +746,7 @@ def ajax_is_stream_available_to_record(request): @csrf_protect @login_required(redirect_field_name="referrer") def ajax_event_startrecord(request): + """Start the record.""" if request.method == "POST" and is_ajax(request): event_id, broadcaster_id = get_event_id_and_broadcaster_id(request) return event_startrecord(event_id, broadcaster_id) @@ -714,7 +755,7 @@ def ajax_event_startrecord(request): def event_startrecord(event_id, broadcaster_id): - """Calls the start method of the broadcaster's implementation. + """Call the start method of the broadcaster's implementation. Returns: a JsonResponse with success state and the error (in case of failure). """ @@ -739,6 +780,7 @@ def event_startrecord(event_id, broadcaster_id): @csrf_protect @login_required(redirect_field_name="referrer") def ajax_event_splitrecord(request): + """Split the record.""" if request.method == "POST" and is_ajax(request): event_id, broadcaster_id = get_event_id_and_broadcaster_id(request) @@ -748,7 +790,7 @@ def ajax_event_splitrecord(request): def event_splitrecord(event_id, broadcaster_id): - """Calls the split method of the broadcaster's implementation + """Call the split method of the broadcaster's implementation and converts the file to a Pod video (linked to the event). Returns: a JsonResponse with success state and the error (in case of failure). @@ -775,6 +817,7 @@ def event_splitrecord(event_id, broadcaster_id): @csrf_protect @login_required(redirect_field_name="referrer") def ajax_event_stoprecord(request): + """Stop the record.""" if request.method == "POST" and is_ajax(request): event_id, broadcaster_id = get_event_id_and_broadcaster_id(request) return event_stoprecord(event_id, broadcaster_id) @@ -783,7 +826,7 @@ def ajax_event_stoprecord(request): def event_stoprecord(event_id, broadcaster_id): - """Calls the stop method of the broadcaster's implementation + """Call the stop method of the broadcaster's implementation and converts the file to a Pod video (linked to the event). Returns: a JsonResponse with success state and the error (in case of failure). @@ -812,6 +855,7 @@ def event_stoprecord(event_id, broadcaster_id): @login_required(redirect_field_name="referrer") def ajax_event_info_record(request): + """Get the infos of the record.""" if request.method == "POST" and is_ajax(request): event_id, broadcaster_id = get_event_id_and_broadcaster_id(request) return event_info_record(event_id, broadcaster_id) @@ -820,7 +864,7 @@ def ajax_event_info_record(request): def event_info_record(event_id, broadcaster_id): - """Returns a JsonResponse with success state and : + """Return a JsonResponse with success state and : * the duration of the recording in seconds * or the error (in case of failure). @@ -843,7 +887,7 @@ def event_info_record(event_id, broadcaster_id): def check_event_record(broadcaster, with_file_check=False): - """Checks whether the given broadcaster is recording an event.""" + """Check whether the given broadcaster is recording an event.""" if not check_piloting_conf(broadcaster): return False, JsonResponse({"success": False, "error": "implementation error"}) @@ -858,19 +902,19 @@ def check_event_record(broadcaster, with_file_check=False): @csrf_protect @login_required(redirect_field_name="referrer") def ajax_event_start_streaming(request): - """Starts the stream.""" + """Start the stream.""" return ajax_event_change_streaming(request, "start") @csrf_protect @login_required(redirect_field_name="referrer") def ajax_event_stop_streaming(request): - """Stops the stream.""" + """Stop the stream.""" return ajax_event_change_streaming(request, "stop") def ajax_event_change_streaming(request, action): - """Starts or stops the stream.""" + """Start or stops the stream.""" if request.method != "POST" or not is_ajax(request): return HttpResponseNotAllowed(["POST"]) @@ -897,7 +941,7 @@ def ajax_event_change_streaming(request, action): @csrf_protect @login_required(redirect_field_name="referrer") def ajax_event_get_rtmp_config(request): - """Checks if the broadcaster is configured for rtmp stream and return his config.""" + """Check if the broadcaster is configured for rtmp stream and return his config.""" if request.method != "GET" or not is_ajax(request): return HttpResponseNotAllowed(["GET"]) @@ -918,7 +962,7 @@ def ajax_event_get_rtmp_config(request): @csrf_protect def event_get_video_cards(request): - """Returns the template with the videos link to the event.""" + """Return the template with the videos link to the event.""" if is_ajax(request): event_id, broadcaster_id = get_event_id_and_broadcaster_id(request) evt = Event.objects.get(pk=event_id) @@ -935,7 +979,7 @@ def event_get_video_cards(request): def create_video(event_id, current_file, segment_number): - """Creates a video from the file_path given in the parameters.""" + """Create a video from the file_path given in the parameters.""" live_event = Event.objects.get(pk=event_id) filename = os.path.basename(current_file) @@ -1027,23 +1071,23 @@ def create_video(event_id, current_file, segment_number): def check_dir_exists(dest_dir_name, max_attempt=6): - """Checks a directory exists.""" + """Check a directory exists.""" return check_exists(dest_dir_name, True, max_attempt) def check_file_exists(full_file_name, max_attempt=6): - """Checks a file exists.""" + """Check a file exists.""" return check_exists(full_file_name, False, max_attempt) def check_piloting_conf(broadcaster: Broadcaster) -> bool: - """Returns if the piloting configuration of the broadcaster is correct.""" + """Returs if the piloting configuration of the broadcaster is correct.""" impl_class = get_piloting_implementation(broadcaster) return impl_class.check_piloting_conf() if impl_class else False def start_record(broadcaster: Broadcaster, event_id) -> bool: - """Starts the recording and return if successfully done.""" + """Stars the recording and return if successfully done.""" impl_class = get_piloting_implementation(broadcaster) return impl_class.start_recording(event_id) if impl_class else False @@ -1055,19 +1099,19 @@ def split_record(broadcaster: Broadcaster) -> bool: def stop_record(broadcaster: Broadcaster) -> bool: - """Stops the recording and return if successfully done.""" + """Stop the recording and return if successfully done.""" impl_class = get_piloting_implementation(broadcaster) return impl_class.stop_recording() if impl_class else False def use_split(broadcaster: Broadcaster) -> bool: - """Returns if the implementation allows to split the current recording.""" + """Return if the implementation allows to split the current recording.""" impl_class = get_piloting_implementation(broadcaster) return impl_class is not None and impl_class.can_split() def get_info_current_record(broadcaster: Broadcaster) -> dict: - """Returns the infos of the current recording.""" + """Return the infos of the current recording.""" impl_class = get_piloting_implementation(broadcaster) if not impl_class: return { @@ -1080,13 +1124,13 @@ def get_info_current_record(broadcaster: Broadcaster) -> dict: def is_available_to_record(broadcaster: Broadcaster) -> bool: - """Returns the broadcaster is ready to record.""" + """Return the broadcaster is ready to record.""" impl_class = get_piloting_implementation(broadcaster) return impl_class.is_available_to_record() if impl_class else False def is_recording(broadcaster: Broadcaster, with_file_check=False) -> bool: - """Returns the broadcaster is actually recording.""" + """Return the broadcaster is actually recording.""" impl_class = get_piloting_implementation(broadcaster) return impl_class.is_recording(with_file_check) if impl_class else False @@ -1125,7 +1169,7 @@ def transform_to_video(broadcaster, event_id, current_record_info): def copy_and_transform(impl_class, event_id, current_record_info): """ - Copies the file from remote to Pod and creates the video + Copy the file from remote to Pod and creates the video Args: impl_class (PilotingInterface): the piloting interface of the broadcaster event_id (int): event's id @@ -1147,13 +1191,13 @@ def copy_and_transform(impl_class, event_id, current_record_info): def can_manage_stream(broadcaster: Broadcaster) -> bool: - """Returns if the implementation allows to manage the stream.""" + """Return if the implementation allows to manage the stream.""" impl_class = get_piloting_implementation(broadcaster) return impl_class is not None and impl_class.can_manage_stream() def start_stream(broadcaster: Broadcaster) -> bool: - """Starts the streaming and return if successfully done.""" + """Start the streaming and return if successfully done.""" impl_class = get_piloting_implementation(broadcaster) if impl_class is None or not impl_class.can_manage_stream(): return False @@ -1169,7 +1213,7 @@ def start_stream(broadcaster: Broadcaster) -> bool: def stop_stream(broadcaster: Broadcaster) -> bool: - """Stops the streaming and return if successfully done.""" + """Stop the streaming and return if successfully done.""" impl_class = get_piloting_implementation(broadcaster) if impl_class is None or not impl_class.can_manage_stream(): return False diff --git a/pod/video_encode_transcript/utils.py b/pod/video_encode_transcript/utils.py index 3c50e2716c..65c021df01 100644 --- a/pod/video_encode_transcript/utils.py +++ b/pod/video_encode_transcript/utils.py @@ -198,7 +198,7 @@ def send_notification_email(video_to_encode, subject_prefix): bcc_email = [] video_estab = video_to_encode.owner.owner.establishment.lower() manager = dict(MANAGERS)[video_estab] - if type(manager) in (list, tuple): + if isinstance(manager, (list, tuple)): bcc_email = manager elif isinstance(manager, str): bcc_email.append(manager) From 4578b01c36a40ca1c1a931aac12b90a05b59e4a5 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 29 Aug 2023 13:12:25 +0000 Subject: [PATCH 2/7] Fixup. Format code with Prettier --- pod/live/static/js/filter_aside_event_list.js | 247 +++++++++--------- 1 file changed, 121 insertions(+), 126 deletions(-) diff --git a/pod/live/static/js/filter_aside_event_list.js b/pod/live/static/js/filter_aside_event_list.js index d49bec6f4c..0cdb07eb3a 100644 --- a/pod/live/static/js/filter_aside_event_list.js +++ b/pod/live/static/js/filter_aside_event_list.js @@ -1,128 +1,123 @@ - - - let loader = document.querySelector(".lds-ring"); - let checkedInputs = []; - - /** - * Enable /disable all checkboxes. - * - * @param {boolean} value - */ - function disableCheckboxes(value) { - document - .querySelectorAll("input[type=checkbox]") - .forEach((checkbox) => { - checkbox.disabled = value; - }); - } - - /** - * Return url with filters params. - * - * @returns {string} - */ - function getUrlForRefresh() { - let newUrl = window.location.pathname + "?"; - - checkedInputs.forEach((input) => { - newUrl += input.name + "=" + input.value + "&"; - }); - - // Add page parameter - newUrl += "page=1"; - return newUrl; - } - - /** - * Remove loader height. - * - * @param height - * @returns {number} - */ - function getHeightMinusLoader(height) { - let loader_style = getComputedStyle(loader); - let loader_height = loader_style.height; - loader_height = loader_height.replace("px", ""); - return height - loader_height; - } - - /** - * Async request to refresh view with filtered events list. - */ - function refreshEvents() { - - // Erase list and enable loader - const events_content = document.getElementById("events_content"); - let width = events_content.offsetWidth; - let height = getHeightMinusLoader(events_content.offsetHeight); - - events_content.innerHTML = "
"; - loader.classList.add("show"); - - let url = getUrlForRefresh(); - - // Async GET request wth parameters by fetch method - fetch(url, { - method: "GET", - headers: { - "X-CSRFToken": "{{ csrf_token }}", - "X-Requested-With": "XMLHttpRequest" - }, - cache: "no-store" - }) - .then((response) => response.text()) - .then((data) => { - // parse data into html and replace videos list - let parser = new DOMParser(); - let html = parser.parseFromString(data, "text/html").body; - events_content.outerHTML = html.innerHTML; - - // change url with params sent - window.history.pushState({}, "", url); - }) - .catch(() => { - events_content.innerHTML = gettext("An Error occurred while processing."); - }) - .finally(() => { - // Re-enable inputs and dismiss loader - disableCheckboxes(false); - loader.classList.remove("show"); - }); - } - - /** - * Check or uncheck checkbox regarding url params. - * - * @param el - */ - function setCheckboxStatus(el) { - let currentUrl = window.location.href; - el.checked = currentUrl.includes("type="+ el.value + "&"); - } - - /** - * Add listener to refresh events list on checkbox status change. - * - * @param el - */ - function addCheckboxListener(el) { - el.addEventListener("change", () => { - checkedInputs = []; - disableCheckboxes(true); - document - .querySelectorAll("#collapseFilterType input[type=checkbox]:checked") - .forEach((e) => { - checkedInputs.push(e); - }); - refreshEvents(); - }); - } - - // On page load +let loader = document.querySelector(".lds-ring"); +let checkedInputs = []; + +/** + * Enable /disable all checkboxes. + * + * @param {boolean} value + */ +function disableCheckboxes(value) { + document.querySelectorAll("input[type=checkbox]").forEach((checkbox) => { + checkbox.disabled = value; + }); +} + +/** + * Return url with filters params. + * + * @returns {string} + */ +function getUrlForRefresh() { + let newUrl = window.location.pathname + "?"; + + checkedInputs.forEach((input) => { + newUrl += input.name + "=" + input.value + "&"; + }); + + // Add page parameter + newUrl += "page=1"; + return newUrl; +} + +/** + * Remove loader height. + * + * @param height + * @returns {number} + */ +function getHeightMinusLoader(height) { + let loader_style = getComputedStyle(loader); + let loader_height = loader_style.height; + loader_height = loader_height.replace("px", ""); + return height - loader_height; +} + +/** + * Async request to refresh view with filtered events list. + */ +function refreshEvents() { + // Erase list and enable loader + const events_content = document.getElementById("events_content"); + let width = events_content.offsetWidth; + let height = getHeightMinusLoader(events_content.offsetHeight); + + events_content.innerHTML = + "
"; + loader.classList.add("show"); + + let url = getUrlForRefresh(); + + // Async GET request wth parameters by fetch method + fetch(url, { + method: "GET", + headers: { + "X-CSRFToken": "{{ csrf_token }}", + "X-Requested-With": "XMLHttpRequest", + }, + cache: "no-store", + }) + .then((response) => response.text()) + .then((data) => { + // parse data into html and replace videos list + let parser = new DOMParser(); + let html = parser.parseFromString(data, "text/html").body; + events_content.outerHTML = html.innerHTML; + + // change url with params sent + window.history.pushState({}, "", url); + }) + .catch(() => { + events_content.innerHTML = gettext("An Error occurred while processing."); + }) + .finally(() => { + // Re-enable inputs and dismiss loader + disableCheckboxes(false); + loader.classList.remove("show"); + }); +} + +/** + * Check or uncheck checkbox regarding url params. + * + * @param el + */ +function setCheckboxStatus(el) { + let currentUrl = window.location.href; + el.checked = currentUrl.includes("type=" + el.value + "&"); +} + +/** + * Add listener to refresh events list on checkbox status change. + * + * @param el + */ +function addCheckboxListener(el) { + el.addEventListener("change", () => { + checkedInputs = []; + disableCheckboxes(true); document - .querySelectorAll("#collapseFilterType input[type=checkbox]") - .forEach((el) => { - setCheckboxStatus(el); - addCheckboxListener(el); + .querySelectorAll("#collapseFilterType input[type=checkbox]:checked") + .forEach((e) => { + checkedInputs.push(e); }); - + refreshEvents(); + }); +} + +// On page load +document + .querySelectorAll("#collapseFilterType input[type=checkbox]") + .forEach((el) => { + setCheckboxStatus(el); + addCheckboxListener(el); + }); From 776d6e3f0b7feb21cbad84f43c6a6eb13f4a3498 Mon Sep 17 00:00:00 2001 From: Aymeric Jakobowski <106442526+AymericJak@users.noreply.github.com> Date: Tue, 29 Aug 2023 15:17:50 +0200 Subject: [PATCH 3/7] [DONE] Fix : accessibility issues (#913) * Fix footer and header * Add fieldset and legend in recurring form * Fix completion JS * Change optgroup for languages in forms * Add autocomplete to completion & contact forms * Add filter information * Fix focus search input * Fix pod-aside in responsive * Change Twitter name & alternative text for these icons * Fix tabindex focus for comments & configuration elements * Add event listener for 'Advanced options' in meetings * Fix CSS conflict * Add documentation and some fixes * Change Twitter name --- pod/completion/forms.py | 2 + pod/completion/models.py | 2 +- pod/completion/static/js/completion.js | 12 +- pod/completion/views.py | 2 +- pod/live/models.py | 2 +- pod/live/templates/live/event-info.html | 21 ++- pod/locale/fr/LC_MESSAGES/django.mo | Bin 160709 -> 161174 bytes pod/locale/fr/LC_MESSAGES/django.po | 40 ++++-- pod/locale/fr/LC_MESSAGES/djangojs.po | 2 +- pod/locale/nl/LC_MESSAGES/django.po | 33 +++-- pod/locale/nl/LC_MESSAGES/djangojs.po | 2 +- pod/main/forms.py | 14 +- pod/main/static/css/dark.css | 3 +- pod/main/static/css/dyslexia.css | 3 +- pod/main/static/css/pod.css | 33 ++--- pod/main/static/js/main.js | 28 ++++ pod/main/templates/aside.html | 15 +- pod/main/templates/base.html | 2 +- pod/main/templates/footer.html | 6 +- pod/main/templates/navbar.html | 8 +- pod/meeting/forms.py | 2 +- .../templates/meeting/add_or_edit.html | 10 +- .../meeting/recurring_options_modal_form.html | 48 ++++--- pod/recorder/models.py | 2 +- pod/video/models.py | 2 +- pod/video/static/js/comment-script.js | 128 +++++++++++++----- pod/video/templates/videos/filter_aside.html | 5 +- pod/video/templates/videos/video-info.html | 37 +++-- 28 files changed, 332 insertions(+), 132 deletions(-) diff --git a/pod/completion/forms.py b/pod/completion/forms.py index 7b17d1abcd..9c15e54681 100644 --- a/pod/completion/forms.py +++ b/pod/completion/forms.py @@ -27,6 +27,8 @@ def __init__(self, *args, **kwargs): """Initialize fields.""" super(ContributorForm, self).__init__(*args, **kwargs) self.fields["video"].widget = HiddenInput() + self.fields["name"].widget.attrs["autocomplete"] = "name" + self.fields["email_address"].widget.attrs["autocomplete"] = "email" self.fields = add_placeholder_and_asterisk(self.fields) class Meta(object): diff --git a/pod/completion/models.py b/pod/completion/models.py index b1b3eaaac0..80b14677ca 100644 --- a/pod/completion/models.py +++ b/pod/completion/models.py @@ -45,7 +45,7 @@ LANG_CHOICES = getattr( settings, "LANG_CHOICES", - ((" ", PREF_LANG_CHOICES), ("----------", ALL_LANG_CHOICES)), + ((_("-- Frequently used languages --"), PREF_LANG_CHOICES), (_("-- All languages --"), ALL_LANG_CHOICES)), ) __LANG_CHOICES_DICT__ = { key: value for key, value in LANG_CHOICES[0][1] + LANG_CHOICES[1][1] diff --git a/pod/completion/static/js/completion.js b/pod/completion/static/js/completion.js index 56c6c669ac..26c8d410f7 100644 --- a/pod/completion/static/js/completion.js +++ b/pod/completion/static/js/completion.js @@ -99,8 +99,9 @@ document.addEventListener("submit", (e) => { e.target.id != "form_new_document" && e.target.id != "form_new_track" && e.target.id != "form_new_overlay" && - !e.target.matches(".form_change") - ) + !e.target.matches(".form_change") && + !e.target.matches(".form_delete") + ) return; e.preventDefault(); @@ -374,8 +375,11 @@ function refresh_list(data, form, list) { document.querySelectorAll("a.title").forEach(function (element) { element.style.display = "initial"; }); - document.getElementById("enrich_player").innerHTML = data.player; - documentL.getElementById(list).innerHTML = data.list_data; + if (data.player) { + document.getElementById("enrich_player").innerHTML = data.player; + } + document.getElementById(list).innerHTML = data.list_data; + } // Check fields diff --git a/pod/completion/views.py b/pod/completion/views.py index 602ea72ac6..c61014ca64 100644 --- a/pod/completion/views.py +++ b/pod/completion/views.py @@ -36,7 +36,7 @@ LANG_CHOICES = getattr( settings, "LANG_CHOICES", - ((" ", PREF_LANG_CHOICES), ("----------", ALL_LANG_CHOICES)), + ((_("-- Frequently used languages --"), PREF_LANG_CHOICES), (_("-- All languages --"), ALL_LANG_CHOICES)), ) __LANG_CHOICES_DICT__ = { key: value for key, value in LANG_CHOICES[0][1] + LANG_CHOICES[1][1] diff --git a/pod/live/models.py b/pod/live/models.py index a893e388ae..b261e8f82c 100644 --- a/pod/live/models.py +++ b/pod/live/models.py @@ -51,7 +51,7 @@ LANG_CHOICES = getattr( settings, "LANG_CHOICES", - ((" ", __PREF_LANG_CHOICES__), ("----------", __ALL_LANG_CHOICES__)), + ((_("-- Frequently used languages --"), __PREF_LANG_CHOICES__), (_("-- All languages --"), __ALL_LANG_CHOICES__)), ) MEDIA_URL = getattr(settings, "MEDIA_URL", "/media/") LIVE_TRANSCRIPTIONS_FOLDER = getattr( diff --git a/pod/live/templates/live/event-info.html b/pod/live/templates/live/event-info.html index 432647ba0f..03ebe960e2 100644 --- a/pod/live/templates/live/event-info.html +++ b/pod/live/templates/live/event-info.html @@ -89,9 +89,24 @@
{% endif %} diff --git a/pod/main/templates/base.html b/pod/main/templates/base.html index 1c0d869b23..8818a5ae02 100644 --- a/pod/main/templates/base.html +++ b/pod/main/templates/base.html @@ -64,7 +64,7 @@ {% get_maintenance_welcome as maintenance_text %}
{{maintenance_text}}
{% endif %} -
+
diff --git a/pod/main/templates/footer.html b/pod/main/templates/footer.html index 747017db2f..f5387d3b92 100644 --- a/pod/main/templates/footer.html +++ b/pod/main/templates/footer.html @@ -6,8 +6,8 @@

-