mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-23 20:11:12 -05:00
Add email templates for user notifications and account management
- Created a base email template (base.html) for consistent styling across all emails. - Added moderation approval email template (moderation_approved.html) to notify users of approved submissions. - Added moderation rejection email template (moderation_rejected.html) to inform users of required changes for their submissions. - Created password reset email template (password_reset.html) for users requesting to reset their passwords. - Developed a welcome email template (welcome.html) to greet new users and provide account details and tips for using ThrillWiki.
This commit is contained in:
408
django/staticfiles/admin/js/admin/DateTimeShortcuts.js
Normal file
408
django/staticfiles/admin/js/admin/DateTimeShortcuts.js
Normal file
@@ -0,0 +1,408 @@
|
||||
/*global Calendar, findPosX, findPosY, get_format, gettext, gettext_noop, interpolate, ngettext, quickElement*/
|
||||
// Inserts shortcut buttons after all of the following:
|
||||
// <input type="text" class="vDateField">
|
||||
// <input type="text" class="vTimeField">
|
||||
'use strict';
|
||||
{
|
||||
const DateTimeShortcuts = {
|
||||
calendars: [],
|
||||
calendarInputs: [],
|
||||
clockInputs: [],
|
||||
clockHours: {
|
||||
default_: [
|
||||
[gettext_noop('Now'), -1],
|
||||
[gettext_noop('Midnight'), 0],
|
||||
[gettext_noop('6 a.m.'), 6],
|
||||
[gettext_noop('Noon'), 12],
|
||||
[gettext_noop('6 p.m.'), 18]
|
||||
]
|
||||
},
|
||||
dismissClockFunc: [],
|
||||
dismissCalendarFunc: [],
|
||||
calendarDivName1: 'calendarbox', // name of calendar <div> that gets toggled
|
||||
calendarDivName2: 'calendarin', // name of <div> that contains calendar
|
||||
calendarLinkName: 'calendarlink', // name of the link that is used to toggle
|
||||
clockDivName: 'clockbox', // name of clock <div> that gets toggled
|
||||
clockLinkName: 'clocklink', // name of the link that is used to toggle
|
||||
shortCutsClass: 'datetimeshortcuts', // class of the clock and cal shortcuts
|
||||
timezoneWarningClass: 'timezonewarning', // class of the warning for timezone mismatch
|
||||
timezoneOffset: 0,
|
||||
init: function() {
|
||||
const serverOffset = document.body.dataset.adminUtcOffset;
|
||||
if (serverOffset) {
|
||||
const localOffset = new Date().getTimezoneOffset() * -60;
|
||||
DateTimeShortcuts.timezoneOffset = localOffset - serverOffset;
|
||||
}
|
||||
|
||||
for (const inp of document.getElementsByTagName('input')) {
|
||||
if (inp.type === 'text' && inp.classList.contains('vTimeField')) {
|
||||
DateTimeShortcuts.addClock(inp);
|
||||
DateTimeShortcuts.addTimezoneWarning(inp);
|
||||
}
|
||||
else if (inp.type === 'text' && inp.classList.contains('vDateField')) {
|
||||
DateTimeShortcuts.addCalendar(inp);
|
||||
DateTimeShortcuts.addTimezoneWarning(inp);
|
||||
}
|
||||
}
|
||||
},
|
||||
// Return the current time while accounting for the server timezone.
|
||||
now: function() {
|
||||
const serverOffset = document.body.dataset.adminUtcOffset;
|
||||
if (serverOffset) {
|
||||
const localNow = new Date();
|
||||
const localOffset = localNow.getTimezoneOffset() * -60;
|
||||
localNow.setTime(localNow.getTime() + 1000 * (serverOffset - localOffset));
|
||||
return localNow;
|
||||
} else {
|
||||
return new Date();
|
||||
}
|
||||
},
|
||||
// Add a warning when the time zone in the browser and backend do not match.
|
||||
addTimezoneWarning: function(inp) {
|
||||
const warningClass = DateTimeShortcuts.timezoneWarningClass;
|
||||
let timezoneOffset = DateTimeShortcuts.timezoneOffset / 3600;
|
||||
|
||||
// Only warn if there is a time zone mismatch.
|
||||
if (!timezoneOffset) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if warning is already there.
|
||||
if (inp.parentNode.querySelectorAll('.' + warningClass).length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let message;
|
||||
if (timezoneOffset > 0) {
|
||||
message = ngettext(
|
||||
'Note: You are %s hour ahead of server time.',
|
||||
'Note: You are %s hours ahead of server time.',
|
||||
timezoneOffset
|
||||
);
|
||||
}
|
||||
else {
|
||||
timezoneOffset *= -1;
|
||||
message = ngettext(
|
||||
'Note: You are %s hour behind server time.',
|
||||
'Note: You are %s hours behind server time.',
|
||||
timezoneOffset
|
||||
);
|
||||
}
|
||||
message = interpolate(message, [timezoneOffset]);
|
||||
|
||||
const warning = document.createElement('div');
|
||||
warning.classList.add('help', warningClass);
|
||||
warning.textContent = message;
|
||||
inp.parentNode.appendChild(warning);
|
||||
},
|
||||
// Add clock widget to a given field
|
||||
addClock: function(inp) {
|
||||
const num = DateTimeShortcuts.clockInputs.length;
|
||||
DateTimeShortcuts.clockInputs[num] = inp;
|
||||
DateTimeShortcuts.dismissClockFunc[num] = function() { DateTimeShortcuts.dismissClock(num); return true; };
|
||||
|
||||
// Shortcut links (clock icon and "Now" link)
|
||||
const shortcuts_span = document.createElement('span');
|
||||
shortcuts_span.className = DateTimeShortcuts.shortCutsClass;
|
||||
inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
|
||||
const now_link = document.createElement('a');
|
||||
now_link.href = "#";
|
||||
now_link.textContent = gettext('Now');
|
||||
now_link.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
DateTimeShortcuts.handleClockQuicklink(num, -1);
|
||||
});
|
||||
const clock_link = document.createElement('a');
|
||||
clock_link.href = '#';
|
||||
clock_link.id = DateTimeShortcuts.clockLinkName + num;
|
||||
clock_link.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
// avoid triggering the document click handler to dismiss the clock
|
||||
e.stopPropagation();
|
||||
DateTimeShortcuts.openClock(num);
|
||||
});
|
||||
|
||||
quickElement(
|
||||
'span', clock_link, '',
|
||||
'class', 'clock-icon',
|
||||
'title', gettext('Choose a Time')
|
||||
);
|
||||
shortcuts_span.appendChild(document.createTextNode('\u00A0'));
|
||||
shortcuts_span.appendChild(now_link);
|
||||
shortcuts_span.appendChild(document.createTextNode('\u00A0|\u00A0'));
|
||||
shortcuts_span.appendChild(clock_link);
|
||||
|
||||
// Create clock link div
|
||||
//
|
||||
// Markup looks like:
|
||||
// <div id="clockbox1" class="clockbox module">
|
||||
// <h2>Choose a time</h2>
|
||||
// <ul class="timelist">
|
||||
// <li><a href="#">Now</a></li>
|
||||
// <li><a href="#">Midnight</a></li>
|
||||
// <li><a href="#">6 a.m.</a></li>
|
||||
// <li><a href="#">Noon</a></li>
|
||||
// <li><a href="#">6 p.m.</a></li>
|
||||
// </ul>
|
||||
// <p class="calendar-cancel"><a href="#">Cancel</a></p>
|
||||
// </div>
|
||||
|
||||
const clock_box = document.createElement('div');
|
||||
clock_box.style.display = 'none';
|
||||
clock_box.style.position = 'absolute';
|
||||
clock_box.className = 'clockbox module';
|
||||
clock_box.id = DateTimeShortcuts.clockDivName + num;
|
||||
document.body.appendChild(clock_box);
|
||||
clock_box.addEventListener('click', function(e) { e.stopPropagation(); });
|
||||
|
||||
quickElement('h2', clock_box, gettext('Choose a time'));
|
||||
const time_list = quickElement('ul', clock_box);
|
||||
time_list.className = 'timelist';
|
||||
// The list of choices can be overridden in JavaScript like this:
|
||||
// DateTimeShortcuts.clockHours.name = [['3 a.m.', 3]];
|
||||
// where name is the name attribute of the <input>.
|
||||
const name = typeof DateTimeShortcuts.clockHours[inp.name] === 'undefined' ? 'default_' : inp.name;
|
||||
DateTimeShortcuts.clockHours[name].forEach(function(element) {
|
||||
const time_link = quickElement('a', quickElement('li', time_list), gettext(element[0]), 'href', '#');
|
||||
time_link.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
DateTimeShortcuts.handleClockQuicklink(num, element[1]);
|
||||
});
|
||||
});
|
||||
|
||||
const cancel_p = quickElement('p', clock_box);
|
||||
cancel_p.className = 'calendar-cancel';
|
||||
const cancel_link = quickElement('a', cancel_p, gettext('Cancel'), 'href', '#');
|
||||
cancel_link.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
DateTimeShortcuts.dismissClock(num);
|
||||
});
|
||||
|
||||
document.addEventListener('keyup', function(event) {
|
||||
if (event.which === 27) {
|
||||
// ESC key closes popup
|
||||
DateTimeShortcuts.dismissClock(num);
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
},
|
||||
openClock: function(num) {
|
||||
const clock_box = document.getElementById(DateTimeShortcuts.clockDivName + num);
|
||||
const clock_link = document.getElementById(DateTimeShortcuts.clockLinkName + num);
|
||||
|
||||
// Recalculate the clockbox position
|
||||
// is it left-to-right or right-to-left layout ?
|
||||
if (window.getComputedStyle(document.body).direction !== 'rtl') {
|
||||
clock_box.style.left = findPosX(clock_link) + 17 + 'px';
|
||||
}
|
||||
else {
|
||||
// since style's width is in em, it'd be tough to calculate
|
||||
// px value of it. let's use an estimated px for now
|
||||
clock_box.style.left = findPosX(clock_link) - 110 + 'px';
|
||||
}
|
||||
clock_box.style.top = Math.max(0, findPosY(clock_link) - 30) + 'px';
|
||||
|
||||
// Show the clock box
|
||||
clock_box.style.display = 'block';
|
||||
document.addEventListener('click', DateTimeShortcuts.dismissClockFunc[num]);
|
||||
},
|
||||
dismissClock: function(num) {
|
||||
document.getElementById(DateTimeShortcuts.clockDivName + num).style.display = 'none';
|
||||
document.removeEventListener('click', DateTimeShortcuts.dismissClockFunc[num]);
|
||||
},
|
||||
handleClockQuicklink: function(num, val) {
|
||||
let d;
|
||||
if (val === -1) {
|
||||
d = DateTimeShortcuts.now();
|
||||
}
|
||||
else {
|
||||
d = new Date(1970, 1, 1, val, 0, 0, 0);
|
||||
}
|
||||
DateTimeShortcuts.clockInputs[num].value = d.strftime(get_format('TIME_INPUT_FORMATS')[0]);
|
||||
DateTimeShortcuts.clockInputs[num].focus();
|
||||
DateTimeShortcuts.dismissClock(num);
|
||||
},
|
||||
// Add calendar widget to a given field.
|
||||
addCalendar: function(inp) {
|
||||
const num = DateTimeShortcuts.calendars.length;
|
||||
|
||||
DateTimeShortcuts.calendarInputs[num] = inp;
|
||||
DateTimeShortcuts.dismissCalendarFunc[num] = function() { DateTimeShortcuts.dismissCalendar(num); return true; };
|
||||
|
||||
// Shortcut links (calendar icon and "Today" link)
|
||||
const shortcuts_span = document.createElement('span');
|
||||
shortcuts_span.className = DateTimeShortcuts.shortCutsClass;
|
||||
inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
|
||||
const today_link = document.createElement('a');
|
||||
today_link.href = '#';
|
||||
today_link.appendChild(document.createTextNode(gettext('Today')));
|
||||
today_link.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
DateTimeShortcuts.handleCalendarQuickLink(num, 0);
|
||||
});
|
||||
const cal_link = document.createElement('a');
|
||||
cal_link.href = '#';
|
||||
cal_link.id = DateTimeShortcuts.calendarLinkName + num;
|
||||
cal_link.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
// avoid triggering the document click handler to dismiss the calendar
|
||||
e.stopPropagation();
|
||||
DateTimeShortcuts.openCalendar(num);
|
||||
});
|
||||
quickElement(
|
||||
'span', cal_link, '',
|
||||
'class', 'date-icon',
|
||||
'title', gettext('Choose a Date')
|
||||
);
|
||||
shortcuts_span.appendChild(document.createTextNode('\u00A0'));
|
||||
shortcuts_span.appendChild(today_link);
|
||||
shortcuts_span.appendChild(document.createTextNode('\u00A0|\u00A0'));
|
||||
shortcuts_span.appendChild(cal_link);
|
||||
|
||||
// Create calendarbox div.
|
||||
//
|
||||
// Markup looks like:
|
||||
//
|
||||
// <div id="calendarbox3" class="calendarbox module">
|
||||
// <h2>
|
||||
// <a href="#" class="link-previous">‹</a>
|
||||
// <a href="#" class="link-next">›</a> February 2003
|
||||
// </h2>
|
||||
// <div class="calendar" id="calendarin3">
|
||||
// <!-- (cal) -->
|
||||
// </div>
|
||||
// <div class="calendar-shortcuts">
|
||||
// <a href="#">Yesterday</a> | <a href="#">Today</a> | <a href="#">Tomorrow</a>
|
||||
// </div>
|
||||
// <p class="calendar-cancel"><a href="#">Cancel</a></p>
|
||||
// </div>
|
||||
const cal_box = document.createElement('div');
|
||||
cal_box.style.display = 'none';
|
||||
cal_box.style.position = 'absolute';
|
||||
cal_box.className = 'calendarbox module';
|
||||
cal_box.id = DateTimeShortcuts.calendarDivName1 + num;
|
||||
document.body.appendChild(cal_box);
|
||||
cal_box.addEventListener('click', function(e) { e.stopPropagation(); });
|
||||
|
||||
// next-prev links
|
||||
const cal_nav = quickElement('div', cal_box);
|
||||
const cal_nav_prev = quickElement('a', cal_nav, '<', 'href', '#');
|
||||
cal_nav_prev.className = 'calendarnav-previous';
|
||||
cal_nav_prev.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
DateTimeShortcuts.drawPrev(num);
|
||||
});
|
||||
|
||||
const cal_nav_next = quickElement('a', cal_nav, '>', 'href', '#');
|
||||
cal_nav_next.className = 'calendarnav-next';
|
||||
cal_nav_next.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
DateTimeShortcuts.drawNext(num);
|
||||
});
|
||||
|
||||
// main box
|
||||
const cal_main = quickElement('div', cal_box, '', 'id', DateTimeShortcuts.calendarDivName2 + num);
|
||||
cal_main.className = 'calendar';
|
||||
DateTimeShortcuts.calendars[num] = new Calendar(DateTimeShortcuts.calendarDivName2 + num, DateTimeShortcuts.handleCalendarCallback(num));
|
||||
DateTimeShortcuts.calendars[num].drawCurrent();
|
||||
|
||||
// calendar shortcuts
|
||||
const shortcuts = quickElement('div', cal_box);
|
||||
shortcuts.className = 'calendar-shortcuts';
|
||||
let day_link = quickElement('a', shortcuts, gettext('Yesterday'), 'href', '#');
|
||||
day_link.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
DateTimeShortcuts.handleCalendarQuickLink(num, -1);
|
||||
});
|
||||
shortcuts.appendChild(document.createTextNode('\u00A0|\u00A0'));
|
||||
day_link = quickElement('a', shortcuts, gettext('Today'), 'href', '#');
|
||||
day_link.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
DateTimeShortcuts.handleCalendarQuickLink(num, 0);
|
||||
});
|
||||
shortcuts.appendChild(document.createTextNode('\u00A0|\u00A0'));
|
||||
day_link = quickElement('a', shortcuts, gettext('Tomorrow'), 'href', '#');
|
||||
day_link.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
DateTimeShortcuts.handleCalendarQuickLink(num, +1);
|
||||
});
|
||||
|
||||
// cancel bar
|
||||
const cancel_p = quickElement('p', cal_box);
|
||||
cancel_p.className = 'calendar-cancel';
|
||||
const cancel_link = quickElement('a', cancel_p, gettext('Cancel'), 'href', '#');
|
||||
cancel_link.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
DateTimeShortcuts.dismissCalendar(num);
|
||||
});
|
||||
document.addEventListener('keyup', function(event) {
|
||||
if (event.which === 27) {
|
||||
// ESC key closes popup
|
||||
DateTimeShortcuts.dismissCalendar(num);
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
},
|
||||
openCalendar: function(num) {
|
||||
const cal_box = document.getElementById(DateTimeShortcuts.calendarDivName1 + num);
|
||||
const cal_link = document.getElementById(DateTimeShortcuts.calendarLinkName + num);
|
||||
const inp = DateTimeShortcuts.calendarInputs[num];
|
||||
|
||||
// Determine if the current value in the input has a valid date.
|
||||
// If so, draw the calendar with that date's year and month.
|
||||
if (inp.value) {
|
||||
const format = get_format('DATE_INPUT_FORMATS')[0];
|
||||
const selected = inp.value.strptime(format);
|
||||
const year = selected.getUTCFullYear();
|
||||
const month = selected.getUTCMonth() + 1;
|
||||
const re = /\d{4}/;
|
||||
if (re.test(year.toString()) && month >= 1 && month <= 12) {
|
||||
DateTimeShortcuts.calendars[num].drawDate(month, year, selected);
|
||||
}
|
||||
}
|
||||
|
||||
// Recalculate the clockbox position
|
||||
// is it left-to-right or right-to-left layout ?
|
||||
if (window.getComputedStyle(document.body).direction !== 'rtl') {
|
||||
cal_box.style.left = findPosX(cal_link) + 17 + 'px';
|
||||
}
|
||||
else {
|
||||
// since style's width is in em, it'd be tough to calculate
|
||||
// px value of it. let's use an estimated px for now
|
||||
cal_box.style.left = findPosX(cal_link) - 180 + 'px';
|
||||
}
|
||||
cal_box.style.top = Math.max(0, findPosY(cal_link) - 75) + 'px';
|
||||
|
||||
cal_box.style.display = 'block';
|
||||
document.addEventListener('click', DateTimeShortcuts.dismissCalendarFunc[num]);
|
||||
},
|
||||
dismissCalendar: function(num) {
|
||||
document.getElementById(DateTimeShortcuts.calendarDivName1 + num).style.display = 'none';
|
||||
document.removeEventListener('click', DateTimeShortcuts.dismissCalendarFunc[num]);
|
||||
},
|
||||
drawPrev: function(num) {
|
||||
DateTimeShortcuts.calendars[num].drawPreviousMonth();
|
||||
},
|
||||
drawNext: function(num) {
|
||||
DateTimeShortcuts.calendars[num].drawNextMonth();
|
||||
},
|
||||
handleCalendarCallback: function(num) {
|
||||
const format = get_format('DATE_INPUT_FORMATS')[0];
|
||||
return function(y, m, d) {
|
||||
DateTimeShortcuts.calendarInputs[num].value = new Date(y, m - 1, d).strftime(format);
|
||||
DateTimeShortcuts.calendarInputs[num].focus();
|
||||
document.getElementById(DateTimeShortcuts.calendarDivName1 + num).style.display = 'none';
|
||||
};
|
||||
},
|
||||
handleCalendarQuickLink: function(num, offset) {
|
||||
const d = DateTimeShortcuts.now();
|
||||
d.setDate(d.getDate() + offset);
|
||||
DateTimeShortcuts.calendarInputs[num].value = d.strftime(get_format('DATE_INPUT_FORMATS')[0]);
|
||||
DateTimeShortcuts.calendarInputs[num].focus();
|
||||
DateTimeShortcuts.dismissCalendar(num);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('load', DateTimeShortcuts.init);
|
||||
window.DateTimeShortcuts = DateTimeShortcuts;
|
||||
}
|
||||
295
django/staticfiles/admin/js/admin/RelatedObjectLookups.js
Normal file
295
django/staticfiles/admin/js/admin/RelatedObjectLookups.js
Normal file
@@ -0,0 +1,295 @@
|
||||
/*global SelectBox, interpolate*/
|
||||
// Handles related-objects functionality: lookup link for raw_id_fields
|
||||
// and Add Another links.
|
||||
"use strict";
|
||||
{
|
||||
const $ = django.jQuery;
|
||||
let popupIndex = 0;
|
||||
const relatedWindows = [];
|
||||
|
||||
function dismissChildPopups() {
|
||||
relatedWindows.forEach(function (win) {
|
||||
if (!win.closed) {
|
||||
win.dismissChildPopups();
|
||||
win.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setPopupIndex() {
|
||||
if (document.getElementsByName("_popup").length > 0) {
|
||||
const index = window.name.lastIndexOf("__") + 2;
|
||||
popupIndex = parseInt(window.name.substring(index));
|
||||
} else {
|
||||
popupIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function addPopupIndex(name) {
|
||||
return name + "__" + (popupIndex + 1);
|
||||
}
|
||||
|
||||
function removePopupIndex(name) {
|
||||
return name.replace(new RegExp("__" + (popupIndex + 1) + "$"), "");
|
||||
}
|
||||
|
||||
function showAdminPopup(triggeringLink, name_regexp, add_popup) {
|
||||
const name = addPopupIndex(triggeringLink.id.replace(name_regexp, ""));
|
||||
const href = new URL(triggeringLink.href);
|
||||
if (add_popup) {
|
||||
href.searchParams.set("_popup", 1);
|
||||
}
|
||||
const win = window.open(
|
||||
href,
|
||||
name,
|
||||
"height=768,width=1024,resizable=yes,scrollbars=yes"
|
||||
);
|
||||
relatedWindows.push(win);
|
||||
win.focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
function showRelatedObjectLookupPopup(triggeringLink) {
|
||||
return showAdminPopup(triggeringLink, /^lookup_/, true);
|
||||
}
|
||||
|
||||
function dismissRelatedLookupPopup(win, chosenId) {
|
||||
const name = removePopupIndex(win.name);
|
||||
const elem = document.getElementById(name);
|
||||
if (elem.classList.contains("vManyToManyRawIdAdminField") && elem.value) {
|
||||
elem.value += "," + chosenId;
|
||||
} else {
|
||||
document.getElementById(name).value = chosenId;
|
||||
}
|
||||
const index = relatedWindows.indexOf(win);
|
||||
if (index > -1) {
|
||||
relatedWindows.splice(index, 1);
|
||||
}
|
||||
win.close();
|
||||
}
|
||||
|
||||
function showRelatedObjectPopup(triggeringLink) {
|
||||
return showAdminPopup(triggeringLink, /^(change|add|delete)_/, false);
|
||||
}
|
||||
|
||||
function updateRelatedObjectLinks(triggeringLink) {
|
||||
const $this = $(triggeringLink);
|
||||
const siblings = $this.nextAll(
|
||||
".view-related, .change-related, .delete-related"
|
||||
);
|
||||
if (!siblings.length) {
|
||||
return;
|
||||
}
|
||||
const value = $this.val();
|
||||
if (value) {
|
||||
siblings.each(function () {
|
||||
const elm = $(this);
|
||||
elm.attr(
|
||||
"href",
|
||||
elm.attr("data-href-template").replace("__fk__", value)
|
||||
);
|
||||
elm.removeAttr("aria-disabled");
|
||||
});
|
||||
} else {
|
||||
siblings.removeAttr("href");
|
||||
siblings.attr("aria-disabled", true);
|
||||
}
|
||||
}
|
||||
|
||||
function updateRelatedSelectsOptions(
|
||||
currentSelect,
|
||||
win,
|
||||
objId,
|
||||
newRepr,
|
||||
newId,
|
||||
skipIds = []
|
||||
) {
|
||||
// After create/edit a model from the options next to the current
|
||||
// select (+ or :pencil:) update ForeignKey PK of the rest of selects
|
||||
// in the page.
|
||||
|
||||
const path = win.location.pathname;
|
||||
// Extract the model from the popup url '.../<model>/add/' or
|
||||
// '.../<model>/<id>/change/' depending the action (add or change).
|
||||
const modelName = path.split("/")[path.split("/").length - (objId ? 4 : 3)];
|
||||
// Select elements with a specific model reference and context of "available-source".
|
||||
const selectsRelated = document.querySelectorAll(
|
||||
`[data-model-ref="${modelName}"] [data-context="available-source"]`
|
||||
);
|
||||
|
||||
selectsRelated.forEach(function (select) {
|
||||
if (
|
||||
currentSelect === select ||
|
||||
(skipIds && skipIds.includes(select.id))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
let option = select.querySelector(`option[value="${objId}"]`);
|
||||
|
||||
if (!option) {
|
||||
option = new Option(newRepr, newId);
|
||||
select.options.add(option);
|
||||
// Update SelectBox cache for related fields.
|
||||
if (
|
||||
window.SelectBox !== undefined &&
|
||||
!SelectBox.cache[currentSelect.id]
|
||||
) {
|
||||
SelectBox.add_to_cache(select.id, option);
|
||||
SelectBox.redisplay(select.id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
option.textContent = newRepr;
|
||||
option.value = newId;
|
||||
});
|
||||
}
|
||||
|
||||
function dismissAddRelatedObjectPopup(win, newId, newRepr) {
|
||||
const name = removePopupIndex(win.name);
|
||||
const elem = document.getElementById(name);
|
||||
if (elem) {
|
||||
const elemName = elem.nodeName.toUpperCase();
|
||||
if (elemName === "SELECT") {
|
||||
elem.options[elem.options.length] = new Option(
|
||||
newRepr,
|
||||
newId,
|
||||
true,
|
||||
true
|
||||
);
|
||||
updateRelatedSelectsOptions(elem, win, null, newRepr, newId);
|
||||
} else if (elemName === "INPUT") {
|
||||
if (
|
||||
elem.classList.contains("vManyToManyRawIdAdminField") &&
|
||||
elem.value
|
||||
) {
|
||||
elem.value += "," + newId;
|
||||
} else {
|
||||
elem.value = newId;
|
||||
}
|
||||
}
|
||||
// Trigger a change event to update related links if required.
|
||||
$(elem).trigger("change");
|
||||
} else {
|
||||
const toId = name + "_to";
|
||||
const toElem = document.getElementById(toId);
|
||||
const o = new Option(newRepr, newId);
|
||||
SelectBox.add_to_cache(toId, o);
|
||||
SelectBox.redisplay(toId);
|
||||
if (toElem && toElem.nodeName.toUpperCase() === "SELECT") {
|
||||
const skipIds = [name + "_from"];
|
||||
updateRelatedSelectsOptions(toElem, win, null, newRepr, newId, skipIds);
|
||||
}
|
||||
}
|
||||
const index = relatedWindows.indexOf(win);
|
||||
if (index > -1) {
|
||||
relatedWindows.splice(index, 1);
|
||||
}
|
||||
win.close();
|
||||
}
|
||||
|
||||
function dismissChangeRelatedObjectPopup(win, objId, newRepr, newId) {
|
||||
const id = removePopupIndex(win.name.replace(/^edit_/, ""));
|
||||
const selectsSelector = interpolate("#%s, #%s_from, #%s_to", [id, id, id]);
|
||||
const selects = $(selectsSelector);
|
||||
selects
|
||||
.find("option")
|
||||
.each(function () {
|
||||
if (this.value === objId) {
|
||||
this.textContent = newRepr;
|
||||
this.value = newId;
|
||||
}
|
||||
})
|
||||
.trigger("change");
|
||||
updateRelatedSelectsOptions(selects[0], win, objId, newRepr, newId);
|
||||
selects
|
||||
.next()
|
||||
.find(".select2-selection__rendered")
|
||||
.each(function () {
|
||||
// The element can have a clear button as a child.
|
||||
// Use the lastChild to modify only the displayed value.
|
||||
this.lastChild.textContent = newRepr;
|
||||
this.title = newRepr;
|
||||
});
|
||||
const index = relatedWindows.indexOf(win);
|
||||
if (index > -1) {
|
||||
relatedWindows.splice(index, 1);
|
||||
}
|
||||
win.close();
|
||||
}
|
||||
|
||||
function dismissDeleteRelatedObjectPopup(win, objId) {
|
||||
const id = removePopupIndex(win.name.replace(/^delete_/, ""));
|
||||
const selectsSelector = interpolate("#%s, #%s_from, #%s_to", [id, id, id]);
|
||||
const selects = $(selectsSelector);
|
||||
selects
|
||||
.find("option")
|
||||
.each(function () {
|
||||
if (this.value === objId) {
|
||||
$(this).remove();
|
||||
}
|
||||
})
|
||||
.trigger("change");
|
||||
const index = relatedWindows.indexOf(win);
|
||||
if (index > -1) {
|
||||
relatedWindows.splice(index, 1);
|
||||
}
|
||||
win.close();
|
||||
}
|
||||
|
||||
window.showRelatedObjectLookupPopup = showRelatedObjectLookupPopup;
|
||||
window.dismissRelatedLookupPopup = dismissRelatedLookupPopup;
|
||||
window.showRelatedObjectPopup = showRelatedObjectPopup;
|
||||
window.updateRelatedObjectLinks = updateRelatedObjectLinks;
|
||||
window.dismissAddRelatedObjectPopup = dismissAddRelatedObjectPopup;
|
||||
window.dismissChangeRelatedObjectPopup = dismissChangeRelatedObjectPopup;
|
||||
window.dismissDeleteRelatedObjectPopup = dismissDeleteRelatedObjectPopup;
|
||||
window.dismissChildPopups = dismissChildPopups;
|
||||
|
||||
// Kept for backward compatibility
|
||||
window.showAddAnotherPopup = showRelatedObjectPopup;
|
||||
window.dismissAddAnotherPopup = dismissAddRelatedObjectPopup;
|
||||
|
||||
window.addEventListener("unload", function (evt) {
|
||||
window.dismissChildPopups();
|
||||
});
|
||||
|
||||
$(document).ready(function () {
|
||||
setPopupIndex();
|
||||
$("a[data-popup-opener]").on("click", function (event) {
|
||||
event.preventDefault();
|
||||
opener.dismissRelatedLookupPopup(window, $(this).data("popup-opener"));
|
||||
});
|
||||
$("body").on(
|
||||
"click",
|
||||
'.related-widget-wrapper-link[data-popup="yes"]',
|
||||
function (e) {
|
||||
e.preventDefault();
|
||||
if (this.href) {
|
||||
const event = $.Event("django:show-related", { href: this.href });
|
||||
$(this).trigger(event);
|
||||
if (!event.isDefaultPrevented()) {
|
||||
showRelatedObjectPopup(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
$("body").on("change", ".related-widget-wrapper select", function (e) {
|
||||
const event = $.Event("django:update-related");
|
||||
$(this).trigger(event);
|
||||
if (!event.isDefaultPrevented()) {
|
||||
updateRelatedObjectLinks(this);
|
||||
}
|
||||
});
|
||||
$(".related-widget-wrapper select").trigger("change");
|
||||
$("body").on("click", ".related-lookup", function (e) {
|
||||
e.preventDefault();
|
||||
const event = $.Event("django:lookup-related");
|
||||
$(this).trigger(event);
|
||||
if (!event.isDefaultPrevented()) {
|
||||
showRelatedObjectLookupPopup(this);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user