});
});
+ document.querySelectorAll('a.selup').forEach((a) => {
+ a.title = 'Click to mark all entries from this row and up, based on the current view';
+ a.addEventListener('click', (e) => {
+ const tbody = e.target.closest('tbody');
+ for (let current = tbody ; current != null ; current = current.previousElementSibling) {
+ const cb = current.querySelector('tr td.flt-seq input[type="checkbox"]');
+ if (cb && current.querySelector('tr.sessionrow').style.display != 'none') {
+ cb.checked = true;
+ }
+ };
+ });
+ });
+ document.querySelectorAll('a.seldown').forEach((a) => {
+ a.title = 'Click to mark all entries from this row and down, based on the current view';
+ a.addEventListener('click', (e) => {
+ const tbody = e.target.closest('tbody');
+ for (let current = tbody ; current != null ; current = current.nextElementSibling) {
+ const cb = current.querySelector('tr td.flt-seq input[type="checkbox"]');
+ if (cb && current.querySelector('tr.sessionrow').style.display != 'none') {
+ cb.checked = true;
+ }
+ };
+ });
+ });
+
const dlgStatus = document.getElementById('dlgStatus');
dlgStatus.querySelectorAll('button').forEach((b) => {
b.addEventListener("click", (e) => {
});
});
+ if (document.getElementById('btnClearCheckboxes')) {
+ document.getElementById('btnClearCheckboxes').addEventListener('click', (e) => {
+ document.querySelectorAll('td.flt-seq input[type="checkbox"]').forEach((c) => {
+ c.checked = false;
+ });
+ });
+ }
+
+ if (document.getElementById('btnBulkStatus')) {
+ document.getElementById('btnBulkStatus').addEventListener('click', (e) => {
+ const idlist = [...document.querySelectorAll('tr.sessionrow:has(td.flt-seq input[type="checkbox"]:checked)')].map((e) => e.dataset.sid);
+ const statuslist = new Set([...document.querySelectorAll('tr.sessionrow:has(td.flt-seq input[type="checkbox"]:checked)')].map((e) => e.dataset.status));
+ const transitions = [...statuslist].map((s) => new Set(Object.keys(valid_status_transitions[s])));
+
+ const valid = transitions.reduce((acc, currval) => {
+ return acc.intersection(currval);
+ });
+
+ if (!valid.size) {
+ alert('There are no valid status transitions for all the selected sessions.');
+ return;
+ }
+
+ const dialog = document.getElementById('dlgStatus');
+ dialog.dataset.sid = idlist;
+ dialog.getElementsByTagName('h3')[0].innerText = "Bulk change status for ids " + idlist;;
+ const buttonDiv = dialog.getElementsByTagName('div')[0];
+ buttonDiv.querySelectorAll('button').forEach((btn) => {
+ btn.style.display = (valid.has(btn.dataset.statusid)) ? "inline-block": "none";
+ });
+
+ dialog.showModal();
+ });
+ }
+
filter_sessions();
});
document.getElementById('detailsrow_' + row.dataset.sid).style.display = visible ? "" : "none";
if (visible) {
- row.querySelector('td').innerText = seq;
+ row.querySelector('td div.seq').innerText = seq;
seq += 1;
} else {
- row.querySelector('td').innerText = '';
+ row.querySelector('td div.seq').innerText = '';
}
});
}
}
async function doUpdateStatus(id, statusval) {
- const targetRow = document.querySelector('tr.sessionrow[data-sid="' + id + '"]');
- const targetFld = targetRow.querySelector('td.fld-status');
+ if (!statusval) return;
const response = await fetch('changestatus/', {
'method': 'POST',
});
if (response.ok) {
const j = await response.json();
- targetRow.dataset.status = statusval;
- targetFld.getElementsByTagName('a')[0].text = j.newstatus;
- targetFld.style.backgroundColor = j.statechanged ? 'yellow' : 'white';
+
+ id.split(',').forEach((i) => {
+ const targetRow = document.querySelector('tr.sessionrow[data-sid="' + i + '"]');
+ const targetFld = targetRow.querySelector('td.fld-status');
+ targetRow.dataset.status = statusval;
+ targetFld.getElementsByTagName('a')[0].text = j.newstatus;
+ targetFld.style.backgroundColor = j.statechanged[i] ? 'yellow' : 'white';
+ });
+
document.getElementById('pendingNotificationsButton').style.display = j.pending ? 'inline-block': 'none';
setAjaxStatus('Changed status to ' + j.newstatus, false);
}
raise Http404("No sessionid")
newstatus = get_int_or_error(request.POST, 'newstatus')
- session = get_object_or_404(ConferenceSession, conference=conference, id=get_int_or_error(request.POST, 'sessionid'))
- if newstatus not in valid_status_transitions[session.status]:
- return HttpResponse("Cannot change from {} to {}".format(get_status_string(session.status), get_status_string(newstatus)), status=400)
+ try:
+ idlist = [int(i) for i in request.POST.get('sessionid').split(',')]
+ except ValueError:
+ raise Http404("Parameter idlist contains non-integers")
- session.status = newstatus
- session.save()
- statechange = session.speaker.exists() and (session.status != session.lastnotifiedstatus)
+ sessions = list(ConferenceSession.objects.only('id', 'status').filter(conference=conference, id__in=idlist))
+ if len(idlist) != len(sessions):
+ return HttpResponse("Invalid number of sessions matched", status=400)
- if statechange:
- # If *this* session has a state changed, then we can shortcut the lookup for
- # others and just indicate we know there is one.
- pendingnotifications = True
- else:
- # Otherwise we have to see if there are any others
- pendingnotifications = conference.pending_session_notifications
+ statechange = {}
+ for session in sessions:
+ if newstatus not in valid_status_transitions[session.status]:
+ return HttpResponse("Cannot change from {} to {}".format(get_status_string(session.status), get_status_string(newstatus)), status=400)
+
+ session.status = newstatus
+ session.save(update_fields=['status'])
+
+ statechange[session.id] = session.speaker.exists() and (session.status != session.lastnotifiedstatus)
return HttpResponse(json.dumps({
'newstatus': get_status_string(session.status),
'statechanged': statechange,
- 'pending': bool(pendingnotifications),
+ 'pending': bool(conference.pending_session_notifications),
}), content_type='application/json')
{%for s in sessionvotes%}
<tbody>
<tr class="headerrow sessionrow" data-sid="{{s.id}}" data-status="{{s.statusid}}" data-track="{{s.trackid|default:"0"}}"{% if conference.callforpaperstags %} data-tags="{{s.tags|join_dictkeys:"id,,"}}"{% endif %}>
- <td class="flt-seq text-center">{{forloop.counter}}</td>
+ <td class="flt-seq text-center"><div>
+ <div class="seq">{{forloop.counter}}</div>
+{%if isadmin%}<div><a class="selup"></a><input type="checkbox"><a class="seldown"></a></div>{%endif%}
+ </div></td>
<td class="flt-id">{{s.id}}</td>
<td>
<h3><label class="dropdown-checkbox"><input type="checkbox" data-sid="{{s.id}}"><span></span></label> {{s.title}}</h3>
</tr>
</tbody>
{%endfor%}
+{%if isadmin %}
+ <tbody class="header">
+ <tr class="headerrow tableheader">
+ <th colspan="2" class="flt-seq">
+ <button id="btnClearCheckboxes" class="btn">Clear checkboxes</button>
+ <button id="btnBulkStatus" class="btn">Bulk set status</button>
+ </th>
+ </tr>
+ </tbody>
+{%endif%}
+
</table>
</fieldset>