formset component
This commit is contained in:
parent
6bfcdd06c8
commit
b82f8f4527
|
@ -63,8 +63,8 @@ class SoundCreateForm(forms.ModelForm):
|
|||
TrackFormSet = modelformset_factory(
|
||||
models.Track,
|
||||
fields=[
|
||||
"episode",
|
||||
"position",
|
||||
"episode",
|
||||
"artist",
|
||||
"title",
|
||||
"tags",
|
||||
|
@ -79,15 +79,15 @@ TrackFormSet = modelformset_factory(
|
|||
EpisodeSoundFormSet = modelformset_factory(
|
||||
models.EpisodeSound,
|
||||
fields=(
|
||||
"position",
|
||||
"episode",
|
||||
"sound",
|
||||
"position",
|
||||
"broadcast",
|
||||
),
|
||||
widgets={
|
||||
"broadcast": forms.CheckboxInput(),
|
||||
"episode": forms.HiddenInput(),
|
||||
"sound": forms.HiddenInput(),
|
||||
# "sound": forms.HiddenInput(),
|
||||
"position": forms.HiddenInput(),
|
||||
},
|
||||
can_delete=True,
|
||||
|
|
|
@ -0,0 +1,633 @@
|
|||
# Generated by Django 4.2.9 on 2024-03-15 19:56
|
||||
|
||||
import aircox.models.schedule
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("aircox", "0022_set_group_ownership"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="station",
|
||||
name="legal_label",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
default="",
|
||||
help_text="Displayed at the bottom of pages.",
|
||||
max_length=64,
|
||||
verbose_name="Legal label",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="schedule",
|
||||
name="timezone",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("Africa/Abidjan", "Africa/Abidjan"),
|
||||
("Africa/Accra", "Africa/Accra"),
|
||||
("Africa/Addis_Ababa", "Africa/Addis_Ababa"),
|
||||
("Africa/Algiers", "Africa/Algiers"),
|
||||
("Africa/Asmara", "Africa/Asmara"),
|
||||
("Africa/Asmera", "Africa/Asmera"),
|
||||
("Africa/Bamako", "Africa/Bamako"),
|
||||
("Africa/Bangui", "Africa/Bangui"),
|
||||
("Africa/Banjul", "Africa/Banjul"),
|
||||
("Africa/Bissau", "Africa/Bissau"),
|
||||
("Africa/Blantyre", "Africa/Blantyre"),
|
||||
("Africa/Brazzaville", "Africa/Brazzaville"),
|
||||
("Africa/Bujumbura", "Africa/Bujumbura"),
|
||||
("Africa/Cairo", "Africa/Cairo"),
|
||||
("Africa/Casablanca", "Africa/Casablanca"),
|
||||
("Africa/Ceuta", "Africa/Ceuta"),
|
||||
("Africa/Conakry", "Africa/Conakry"),
|
||||
("Africa/Dakar", "Africa/Dakar"),
|
||||
("Africa/Dar_es_Salaam", "Africa/Dar_es_Salaam"),
|
||||
("Africa/Djibouti", "Africa/Djibouti"),
|
||||
("Africa/Douala", "Africa/Douala"),
|
||||
("Africa/El_Aaiun", "Africa/El_Aaiun"),
|
||||
("Africa/Freetown", "Africa/Freetown"),
|
||||
("Africa/Gaborone", "Africa/Gaborone"),
|
||||
("Africa/Harare", "Africa/Harare"),
|
||||
("Africa/Johannesburg", "Africa/Johannesburg"),
|
||||
("Africa/Juba", "Africa/Juba"),
|
||||
("Africa/Kampala", "Africa/Kampala"),
|
||||
("Africa/Khartoum", "Africa/Khartoum"),
|
||||
("Africa/Kigali", "Africa/Kigali"),
|
||||
("Africa/Kinshasa", "Africa/Kinshasa"),
|
||||
("Africa/Lagos", "Africa/Lagos"),
|
||||
("Africa/Libreville", "Africa/Libreville"),
|
||||
("Africa/Lome", "Africa/Lome"),
|
||||
("Africa/Luanda", "Africa/Luanda"),
|
||||
("Africa/Lubumbashi", "Africa/Lubumbashi"),
|
||||
("Africa/Lusaka", "Africa/Lusaka"),
|
||||
("Africa/Malabo", "Africa/Malabo"),
|
||||
("Africa/Maputo", "Africa/Maputo"),
|
||||
("Africa/Maseru", "Africa/Maseru"),
|
||||
("Africa/Mbabane", "Africa/Mbabane"),
|
||||
("Africa/Mogadishu", "Africa/Mogadishu"),
|
||||
("Africa/Monrovia", "Africa/Monrovia"),
|
||||
("Africa/Nairobi", "Africa/Nairobi"),
|
||||
("Africa/Ndjamena", "Africa/Ndjamena"),
|
||||
("Africa/Niamey", "Africa/Niamey"),
|
||||
("Africa/Nouakchott", "Africa/Nouakchott"),
|
||||
("Africa/Ouagadougou", "Africa/Ouagadougou"),
|
||||
("Africa/Porto-Novo", "Africa/Porto-Novo"),
|
||||
("Africa/Sao_Tome", "Africa/Sao_Tome"),
|
||||
("Africa/Timbuktu", "Africa/Timbuktu"),
|
||||
("Africa/Tripoli", "Africa/Tripoli"),
|
||||
("Africa/Tunis", "Africa/Tunis"),
|
||||
("Africa/Windhoek", "Africa/Windhoek"),
|
||||
("America/Adak", "America/Adak"),
|
||||
("America/Anchorage", "America/Anchorage"),
|
||||
("America/Anguilla", "America/Anguilla"),
|
||||
("America/Antigua", "America/Antigua"),
|
||||
("America/Araguaina", "America/Araguaina"),
|
||||
("America/Argentina/Buenos_Aires", "America/Argentina/Buenos_Aires"),
|
||||
("America/Argentina/Catamarca", "America/Argentina/Catamarca"),
|
||||
("America/Argentina/ComodRivadavia", "America/Argentina/ComodRivadavia"),
|
||||
("America/Argentina/Cordoba", "America/Argentina/Cordoba"),
|
||||
("America/Argentina/Jujuy", "America/Argentina/Jujuy"),
|
||||
("America/Argentina/La_Rioja", "America/Argentina/La_Rioja"),
|
||||
("America/Argentina/Mendoza", "America/Argentina/Mendoza"),
|
||||
("America/Argentina/Rio_Gallegos", "America/Argentina/Rio_Gallegos"),
|
||||
("America/Argentina/Salta", "America/Argentina/Salta"),
|
||||
("America/Argentina/San_Juan", "America/Argentina/San_Juan"),
|
||||
("America/Argentina/San_Luis", "America/Argentina/San_Luis"),
|
||||
("America/Argentina/Tucuman", "America/Argentina/Tucuman"),
|
||||
("America/Argentina/Ushuaia", "America/Argentina/Ushuaia"),
|
||||
("America/Aruba", "America/Aruba"),
|
||||
("America/Asuncion", "America/Asuncion"),
|
||||
("America/Atikokan", "America/Atikokan"),
|
||||
("America/Atka", "America/Atka"),
|
||||
("America/Bahia", "America/Bahia"),
|
||||
("America/Bahia_Banderas", "America/Bahia_Banderas"),
|
||||
("America/Barbados", "America/Barbados"),
|
||||
("America/Belem", "America/Belem"),
|
||||
("America/Belize", "America/Belize"),
|
||||
("America/Blanc-Sablon", "America/Blanc-Sablon"),
|
||||
("America/Boa_Vista", "America/Boa_Vista"),
|
||||
("America/Bogota", "America/Bogota"),
|
||||
("America/Boise", "America/Boise"),
|
||||
("America/Buenos_Aires", "America/Buenos_Aires"),
|
||||
("America/Cambridge_Bay", "America/Cambridge_Bay"),
|
||||
("America/Campo_Grande", "America/Campo_Grande"),
|
||||
("America/Cancun", "America/Cancun"),
|
||||
("America/Caracas", "America/Caracas"),
|
||||
("America/Catamarca", "America/Catamarca"),
|
||||
("America/Cayenne", "America/Cayenne"),
|
||||
("America/Cayman", "America/Cayman"),
|
||||
("America/Chicago", "America/Chicago"),
|
||||
("America/Chihuahua", "America/Chihuahua"),
|
||||
("America/Ciudad_Juarez", "America/Ciudad_Juarez"),
|
||||
("America/Coral_Harbour", "America/Coral_Harbour"),
|
||||
("America/Cordoba", "America/Cordoba"),
|
||||
("America/Costa_Rica", "America/Costa_Rica"),
|
||||
("America/Creston", "America/Creston"),
|
||||
("America/Cuiaba", "America/Cuiaba"),
|
||||
("America/Curacao", "America/Curacao"),
|
||||
("America/Danmarkshavn", "America/Danmarkshavn"),
|
||||
("America/Dawson", "America/Dawson"),
|
||||
("America/Dawson_Creek", "America/Dawson_Creek"),
|
||||
("America/Denver", "America/Denver"),
|
||||
("America/Detroit", "America/Detroit"),
|
||||
("America/Dominica", "America/Dominica"),
|
||||
("America/Edmonton", "America/Edmonton"),
|
||||
("America/Eirunepe", "America/Eirunepe"),
|
||||
("America/El_Salvador", "America/El_Salvador"),
|
||||
("America/Ensenada", "America/Ensenada"),
|
||||
("America/Fort_Nelson", "America/Fort_Nelson"),
|
||||
("America/Fort_Wayne", "America/Fort_Wayne"),
|
||||
("America/Fortaleza", "America/Fortaleza"),
|
||||
("America/Glace_Bay", "America/Glace_Bay"),
|
||||
("America/Godthab", "America/Godthab"),
|
||||
("America/Goose_Bay", "America/Goose_Bay"),
|
||||
("America/Grand_Turk", "America/Grand_Turk"),
|
||||
("America/Grenada", "America/Grenada"),
|
||||
("America/Guadeloupe", "America/Guadeloupe"),
|
||||
("America/Guatemala", "America/Guatemala"),
|
||||
("America/Guayaquil", "America/Guayaquil"),
|
||||
("America/Guyana", "America/Guyana"),
|
||||
("America/Halifax", "America/Halifax"),
|
||||
("America/Havana", "America/Havana"),
|
||||
("America/Hermosillo", "America/Hermosillo"),
|
||||
("America/Indiana/Indianapolis", "America/Indiana/Indianapolis"),
|
||||
("America/Indiana/Knox", "America/Indiana/Knox"),
|
||||
("America/Indiana/Marengo", "America/Indiana/Marengo"),
|
||||
("America/Indiana/Petersburg", "America/Indiana/Petersburg"),
|
||||
("America/Indiana/Tell_City", "America/Indiana/Tell_City"),
|
||||
("America/Indiana/Vevay", "America/Indiana/Vevay"),
|
||||
("America/Indiana/Vincennes", "America/Indiana/Vincennes"),
|
||||
("America/Indiana/Winamac", "America/Indiana/Winamac"),
|
||||
("America/Indianapolis", "America/Indianapolis"),
|
||||
("America/Inuvik", "America/Inuvik"),
|
||||
("America/Iqaluit", "America/Iqaluit"),
|
||||
("America/Jamaica", "America/Jamaica"),
|
||||
("America/Jujuy", "America/Jujuy"),
|
||||
("America/Juneau", "America/Juneau"),
|
||||
("America/Kentucky/Louisville", "America/Kentucky/Louisville"),
|
||||
("America/Kentucky/Monticello", "America/Kentucky/Monticello"),
|
||||
("America/Knox_IN", "America/Knox_IN"),
|
||||
("America/Kralendijk", "America/Kralendijk"),
|
||||
("America/La_Paz", "America/La_Paz"),
|
||||
("America/Lima", "America/Lima"),
|
||||
("America/Los_Angeles", "America/Los_Angeles"),
|
||||
("America/Louisville", "America/Louisville"),
|
||||
("America/Lower_Princes", "America/Lower_Princes"),
|
||||
("America/Maceio", "America/Maceio"),
|
||||
("America/Managua", "America/Managua"),
|
||||
("America/Manaus", "America/Manaus"),
|
||||
("America/Marigot", "America/Marigot"),
|
||||
("America/Martinique", "America/Martinique"),
|
||||
("America/Matamoros", "America/Matamoros"),
|
||||
("America/Mazatlan", "America/Mazatlan"),
|
||||
("America/Mendoza", "America/Mendoza"),
|
||||
("America/Menominee", "America/Menominee"),
|
||||
("America/Merida", "America/Merida"),
|
||||
("America/Metlakatla", "America/Metlakatla"),
|
||||
("America/Mexico_City", "America/Mexico_City"),
|
||||
("America/Miquelon", "America/Miquelon"),
|
||||
("America/Moncton", "America/Moncton"),
|
||||
("America/Monterrey", "America/Monterrey"),
|
||||
("America/Montevideo", "America/Montevideo"),
|
||||
("America/Montreal", "America/Montreal"),
|
||||
("America/Montserrat", "America/Montserrat"),
|
||||
("America/Nassau", "America/Nassau"),
|
||||
("America/New_York", "America/New_York"),
|
||||
("America/Nipigon", "America/Nipigon"),
|
||||
("America/Nome", "America/Nome"),
|
||||
("America/Noronha", "America/Noronha"),
|
||||
("America/North_Dakota/Beulah", "America/North_Dakota/Beulah"),
|
||||
("America/North_Dakota/Center", "America/North_Dakota/Center"),
|
||||
("America/North_Dakota/New_Salem", "America/North_Dakota/New_Salem"),
|
||||
("America/Nuuk", "America/Nuuk"),
|
||||
("America/Ojinaga", "America/Ojinaga"),
|
||||
("America/Panama", "America/Panama"),
|
||||
("America/Pangnirtung", "America/Pangnirtung"),
|
||||
("America/Paramaribo", "America/Paramaribo"),
|
||||
("America/Phoenix", "America/Phoenix"),
|
||||
("America/Port-au-Prince", "America/Port-au-Prince"),
|
||||
("America/Port_of_Spain", "America/Port_of_Spain"),
|
||||
("America/Porto_Acre", "America/Porto_Acre"),
|
||||
("America/Porto_Velho", "America/Porto_Velho"),
|
||||
("America/Puerto_Rico", "America/Puerto_Rico"),
|
||||
("America/Punta_Arenas", "America/Punta_Arenas"),
|
||||
("America/Rainy_River", "America/Rainy_River"),
|
||||
("America/Rankin_Inlet", "America/Rankin_Inlet"),
|
||||
("America/Recife", "America/Recife"),
|
||||
("America/Regina", "America/Regina"),
|
||||
("America/Resolute", "America/Resolute"),
|
||||
("America/Rio_Branco", "America/Rio_Branco"),
|
||||
("America/Rosario", "America/Rosario"),
|
||||
("America/Santa_Isabel", "America/Santa_Isabel"),
|
||||
("America/Santarem", "America/Santarem"),
|
||||
("America/Santiago", "America/Santiago"),
|
||||
("America/Santo_Domingo", "America/Santo_Domingo"),
|
||||
("America/Sao_Paulo", "America/Sao_Paulo"),
|
||||
("America/Scoresbysund", "America/Scoresbysund"),
|
||||
("America/Shiprock", "America/Shiprock"),
|
||||
("America/Sitka", "America/Sitka"),
|
||||
("America/St_Barthelemy", "America/St_Barthelemy"),
|
||||
("America/St_Johns", "America/St_Johns"),
|
||||
("America/St_Kitts", "America/St_Kitts"),
|
||||
("America/St_Lucia", "America/St_Lucia"),
|
||||
("America/St_Thomas", "America/St_Thomas"),
|
||||
("America/St_Vincent", "America/St_Vincent"),
|
||||
("America/Swift_Current", "America/Swift_Current"),
|
||||
("America/Tegucigalpa", "America/Tegucigalpa"),
|
||||
("America/Thule", "America/Thule"),
|
||||
("America/Thunder_Bay", "America/Thunder_Bay"),
|
||||
("America/Tijuana", "America/Tijuana"),
|
||||
("America/Toronto", "America/Toronto"),
|
||||
("America/Tortola", "America/Tortola"),
|
||||
("America/Vancouver", "America/Vancouver"),
|
||||
("America/Virgin", "America/Virgin"),
|
||||
("America/Whitehorse", "America/Whitehorse"),
|
||||
("America/Winnipeg", "America/Winnipeg"),
|
||||
("America/Yakutat", "America/Yakutat"),
|
||||
("America/Yellowknife", "America/Yellowknife"),
|
||||
("Antarctica/Casey", "Antarctica/Casey"),
|
||||
("Antarctica/Davis", "Antarctica/Davis"),
|
||||
("Antarctica/DumontDUrville", "Antarctica/DumontDUrville"),
|
||||
("Antarctica/Macquarie", "Antarctica/Macquarie"),
|
||||
("Antarctica/Mawson", "Antarctica/Mawson"),
|
||||
("Antarctica/McMurdo", "Antarctica/McMurdo"),
|
||||
("Antarctica/Palmer", "Antarctica/Palmer"),
|
||||
("Antarctica/Rothera", "Antarctica/Rothera"),
|
||||
("Antarctica/South_Pole", "Antarctica/South_Pole"),
|
||||
("Antarctica/Syowa", "Antarctica/Syowa"),
|
||||
("Antarctica/Troll", "Antarctica/Troll"),
|
||||
("Antarctica/Vostok", "Antarctica/Vostok"),
|
||||
("Arctic/Longyearbyen", "Arctic/Longyearbyen"),
|
||||
("Asia/Aden", "Asia/Aden"),
|
||||
("Asia/Almaty", "Asia/Almaty"),
|
||||
("Asia/Amman", "Asia/Amman"),
|
||||
("Asia/Anadyr", "Asia/Anadyr"),
|
||||
("Asia/Aqtau", "Asia/Aqtau"),
|
||||
("Asia/Aqtobe", "Asia/Aqtobe"),
|
||||
("Asia/Ashgabat", "Asia/Ashgabat"),
|
||||
("Asia/Ashkhabad", "Asia/Ashkhabad"),
|
||||
("Asia/Atyrau", "Asia/Atyrau"),
|
||||
("Asia/Baghdad", "Asia/Baghdad"),
|
||||
("Asia/Bahrain", "Asia/Bahrain"),
|
||||
("Asia/Baku", "Asia/Baku"),
|
||||
("Asia/Bangkok", "Asia/Bangkok"),
|
||||
("Asia/Barnaul", "Asia/Barnaul"),
|
||||
("Asia/Beirut", "Asia/Beirut"),
|
||||
("Asia/Bishkek", "Asia/Bishkek"),
|
||||
("Asia/Brunei", "Asia/Brunei"),
|
||||
("Asia/Calcutta", "Asia/Calcutta"),
|
||||
("Asia/Chita", "Asia/Chita"),
|
||||
("Asia/Choibalsan", "Asia/Choibalsan"),
|
||||
("Asia/Chongqing", "Asia/Chongqing"),
|
||||
("Asia/Chungking", "Asia/Chungking"),
|
||||
("Asia/Colombo", "Asia/Colombo"),
|
||||
("Asia/Dacca", "Asia/Dacca"),
|
||||
("Asia/Damascus", "Asia/Damascus"),
|
||||
("Asia/Dhaka", "Asia/Dhaka"),
|
||||
("Asia/Dili", "Asia/Dili"),
|
||||
("Asia/Dubai", "Asia/Dubai"),
|
||||
("Asia/Dushanbe", "Asia/Dushanbe"),
|
||||
("Asia/Famagusta", "Asia/Famagusta"),
|
||||
("Asia/Gaza", "Asia/Gaza"),
|
||||
("Asia/Harbin", "Asia/Harbin"),
|
||||
("Asia/Hebron", "Asia/Hebron"),
|
||||
("Asia/Ho_Chi_Minh", "Asia/Ho_Chi_Minh"),
|
||||
("Asia/Hong_Kong", "Asia/Hong_Kong"),
|
||||
("Asia/Hovd", "Asia/Hovd"),
|
||||
("Asia/Irkutsk", "Asia/Irkutsk"),
|
||||
("Asia/Istanbul", "Asia/Istanbul"),
|
||||
("Asia/Jakarta", "Asia/Jakarta"),
|
||||
("Asia/Jayapura", "Asia/Jayapura"),
|
||||
("Asia/Jerusalem", "Asia/Jerusalem"),
|
||||
("Asia/Kabul", "Asia/Kabul"),
|
||||
("Asia/Kamchatka", "Asia/Kamchatka"),
|
||||
("Asia/Karachi", "Asia/Karachi"),
|
||||
("Asia/Kashgar", "Asia/Kashgar"),
|
||||
("Asia/Kathmandu", "Asia/Kathmandu"),
|
||||
("Asia/Katmandu", "Asia/Katmandu"),
|
||||
("Asia/Khandyga", "Asia/Khandyga"),
|
||||
("Asia/Kolkata", "Asia/Kolkata"),
|
||||
("Asia/Krasnoyarsk", "Asia/Krasnoyarsk"),
|
||||
("Asia/Kuala_Lumpur", "Asia/Kuala_Lumpur"),
|
||||
("Asia/Kuching", "Asia/Kuching"),
|
||||
("Asia/Kuwait", "Asia/Kuwait"),
|
||||
("Asia/Macao", "Asia/Macao"),
|
||||
("Asia/Macau", "Asia/Macau"),
|
||||
("Asia/Magadan", "Asia/Magadan"),
|
||||
("Asia/Makassar", "Asia/Makassar"),
|
||||
("Asia/Manila", "Asia/Manila"),
|
||||
("Asia/Muscat", "Asia/Muscat"),
|
||||
("Asia/Nicosia", "Asia/Nicosia"),
|
||||
("Asia/Novokuznetsk", "Asia/Novokuznetsk"),
|
||||
("Asia/Novosibirsk", "Asia/Novosibirsk"),
|
||||
("Asia/Omsk", "Asia/Omsk"),
|
||||
("Asia/Oral", "Asia/Oral"),
|
||||
("Asia/Phnom_Penh", "Asia/Phnom_Penh"),
|
||||
("Asia/Pontianak", "Asia/Pontianak"),
|
||||
("Asia/Pyongyang", "Asia/Pyongyang"),
|
||||
("Asia/Qatar", "Asia/Qatar"),
|
||||
("Asia/Qostanay", "Asia/Qostanay"),
|
||||
("Asia/Qyzylorda", "Asia/Qyzylorda"),
|
||||
("Asia/Rangoon", "Asia/Rangoon"),
|
||||
("Asia/Riyadh", "Asia/Riyadh"),
|
||||
("Asia/Saigon", "Asia/Saigon"),
|
||||
("Asia/Sakhalin", "Asia/Sakhalin"),
|
||||
("Asia/Samarkand", "Asia/Samarkand"),
|
||||
("Asia/Seoul", "Asia/Seoul"),
|
||||
("Asia/Shanghai", "Asia/Shanghai"),
|
||||
("Asia/Singapore", "Asia/Singapore"),
|
||||
("Asia/Srednekolymsk", "Asia/Srednekolymsk"),
|
||||
("Asia/Taipei", "Asia/Taipei"),
|
||||
("Asia/Tashkent", "Asia/Tashkent"),
|
||||
("Asia/Tbilisi", "Asia/Tbilisi"),
|
||||
("Asia/Tehran", "Asia/Tehran"),
|
||||
("Asia/Tel_Aviv", "Asia/Tel_Aviv"),
|
||||
("Asia/Thimbu", "Asia/Thimbu"),
|
||||
("Asia/Thimphu", "Asia/Thimphu"),
|
||||
("Asia/Tokyo", "Asia/Tokyo"),
|
||||
("Asia/Tomsk", "Asia/Tomsk"),
|
||||
("Asia/Ujung_Pandang", "Asia/Ujung_Pandang"),
|
||||
("Asia/Ulaanbaatar", "Asia/Ulaanbaatar"),
|
||||
("Asia/Ulan_Bator", "Asia/Ulan_Bator"),
|
||||
("Asia/Urumqi", "Asia/Urumqi"),
|
||||
("Asia/Ust-Nera", "Asia/Ust-Nera"),
|
||||
("Asia/Vientiane", "Asia/Vientiane"),
|
||||
("Asia/Vladivostok", "Asia/Vladivostok"),
|
||||
("Asia/Yakutsk", "Asia/Yakutsk"),
|
||||
("Asia/Yangon", "Asia/Yangon"),
|
||||
("Asia/Yekaterinburg", "Asia/Yekaterinburg"),
|
||||
("Asia/Yerevan", "Asia/Yerevan"),
|
||||
("Atlantic/Azores", "Atlantic/Azores"),
|
||||
("Atlantic/Bermuda", "Atlantic/Bermuda"),
|
||||
("Atlantic/Canary", "Atlantic/Canary"),
|
||||
("Atlantic/Cape_Verde", "Atlantic/Cape_Verde"),
|
||||
("Atlantic/Faeroe", "Atlantic/Faeroe"),
|
||||
("Atlantic/Faroe", "Atlantic/Faroe"),
|
||||
("Atlantic/Jan_Mayen", "Atlantic/Jan_Mayen"),
|
||||
("Atlantic/Madeira", "Atlantic/Madeira"),
|
||||
("Atlantic/Reykjavik", "Atlantic/Reykjavik"),
|
||||
("Atlantic/South_Georgia", "Atlantic/South_Georgia"),
|
||||
("Atlantic/St_Helena", "Atlantic/St_Helena"),
|
||||
("Atlantic/Stanley", "Atlantic/Stanley"),
|
||||
("Australia/ACT", "Australia/ACT"),
|
||||
("Australia/Adelaide", "Australia/Adelaide"),
|
||||
("Australia/Brisbane", "Australia/Brisbane"),
|
||||
("Australia/Broken_Hill", "Australia/Broken_Hill"),
|
||||
("Australia/Canberra", "Australia/Canberra"),
|
||||
("Australia/Currie", "Australia/Currie"),
|
||||
("Australia/Darwin", "Australia/Darwin"),
|
||||
("Australia/Eucla", "Australia/Eucla"),
|
||||
("Australia/Hobart", "Australia/Hobart"),
|
||||
("Australia/LHI", "Australia/LHI"),
|
||||
("Australia/Lindeman", "Australia/Lindeman"),
|
||||
("Australia/Lord_Howe", "Australia/Lord_Howe"),
|
||||
("Australia/Melbourne", "Australia/Melbourne"),
|
||||
("Australia/NSW", "Australia/NSW"),
|
||||
("Australia/North", "Australia/North"),
|
||||
("Australia/Perth", "Australia/Perth"),
|
||||
("Australia/Queensland", "Australia/Queensland"),
|
||||
("Australia/South", "Australia/South"),
|
||||
("Australia/Sydney", "Australia/Sydney"),
|
||||
("Australia/Tasmania", "Australia/Tasmania"),
|
||||
("Australia/Victoria", "Australia/Victoria"),
|
||||
("Australia/West", "Australia/West"),
|
||||
("Australia/Yancowinna", "Australia/Yancowinna"),
|
||||
("Brazil/Acre", "Brazil/Acre"),
|
||||
("Brazil/DeNoronha", "Brazil/DeNoronha"),
|
||||
("Brazil/East", "Brazil/East"),
|
||||
("Brazil/West", "Brazil/West"),
|
||||
("CET", "CET"),
|
||||
("CST6CDT", "CST6CDT"),
|
||||
("Canada/Atlantic", "Canada/Atlantic"),
|
||||
("Canada/Central", "Canada/Central"),
|
||||
("Canada/Eastern", "Canada/Eastern"),
|
||||
("Canada/Mountain", "Canada/Mountain"),
|
||||
("Canada/Newfoundland", "Canada/Newfoundland"),
|
||||
("Canada/Pacific", "Canada/Pacific"),
|
||||
("Canada/Saskatchewan", "Canada/Saskatchewan"),
|
||||
("Canada/Yukon", "Canada/Yukon"),
|
||||
("Chile/Continental", "Chile/Continental"),
|
||||
("Chile/EasterIsland", "Chile/EasterIsland"),
|
||||
("Cuba", "Cuba"),
|
||||
("EET", "EET"),
|
||||
("EST", "EST"),
|
||||
("EST5EDT", "EST5EDT"),
|
||||
("Egypt", "Egypt"),
|
||||
("Eire", "Eire"),
|
||||
("Etc/GMT", "Etc/GMT"),
|
||||
("Etc/GMT+0", "Etc/GMT+0"),
|
||||
("Etc/GMT+1", "Etc/GMT+1"),
|
||||
("Etc/GMT+10", "Etc/GMT+10"),
|
||||
("Etc/GMT+11", "Etc/GMT+11"),
|
||||
("Etc/GMT+12", "Etc/GMT+12"),
|
||||
("Etc/GMT+2", "Etc/GMT+2"),
|
||||
("Etc/GMT+3", "Etc/GMT+3"),
|
||||
("Etc/GMT+4", "Etc/GMT+4"),
|
||||
("Etc/GMT+5", "Etc/GMT+5"),
|
||||
("Etc/GMT+6", "Etc/GMT+6"),
|
||||
("Etc/GMT+7", "Etc/GMT+7"),
|
||||
("Etc/GMT+8", "Etc/GMT+8"),
|
||||
("Etc/GMT+9", "Etc/GMT+9"),
|
||||
("Etc/GMT-0", "Etc/GMT-0"),
|
||||
("Etc/GMT-1", "Etc/GMT-1"),
|
||||
("Etc/GMT-10", "Etc/GMT-10"),
|
||||
("Etc/GMT-11", "Etc/GMT-11"),
|
||||
("Etc/GMT-12", "Etc/GMT-12"),
|
||||
("Etc/GMT-13", "Etc/GMT-13"),
|
||||
("Etc/GMT-14", "Etc/GMT-14"),
|
||||
("Etc/GMT-2", "Etc/GMT-2"),
|
||||
("Etc/GMT-3", "Etc/GMT-3"),
|
||||
("Etc/GMT-4", "Etc/GMT-4"),
|
||||
("Etc/GMT-5", "Etc/GMT-5"),
|
||||
("Etc/GMT-6", "Etc/GMT-6"),
|
||||
("Etc/GMT-7", "Etc/GMT-7"),
|
||||
("Etc/GMT-8", "Etc/GMT-8"),
|
||||
("Etc/GMT-9", "Etc/GMT-9"),
|
||||
("Etc/GMT0", "Etc/GMT0"),
|
||||
("Etc/Greenwich", "Etc/Greenwich"),
|
||||
("Etc/UCT", "Etc/UCT"),
|
||||
("Etc/UTC", "Etc/UTC"),
|
||||
("Etc/Universal", "Etc/Universal"),
|
||||
("Etc/Zulu", "Etc/Zulu"),
|
||||
("Europe/Amsterdam", "Europe/Amsterdam"),
|
||||
("Europe/Andorra", "Europe/Andorra"),
|
||||
("Europe/Astrakhan", "Europe/Astrakhan"),
|
||||
("Europe/Athens", "Europe/Athens"),
|
||||
("Europe/Belfast", "Europe/Belfast"),
|
||||
("Europe/Belgrade", "Europe/Belgrade"),
|
||||
("Europe/Berlin", "Europe/Berlin"),
|
||||
("Europe/Bratislava", "Europe/Bratislava"),
|
||||
("Europe/Brussels", "Europe/Brussels"),
|
||||
("Europe/Bucharest", "Europe/Bucharest"),
|
||||
("Europe/Budapest", "Europe/Budapest"),
|
||||
("Europe/Busingen", "Europe/Busingen"),
|
||||
("Europe/Chisinau", "Europe/Chisinau"),
|
||||
("Europe/Copenhagen", "Europe/Copenhagen"),
|
||||
("Europe/Dublin", "Europe/Dublin"),
|
||||
("Europe/Gibraltar", "Europe/Gibraltar"),
|
||||
("Europe/Guernsey", "Europe/Guernsey"),
|
||||
("Europe/Helsinki", "Europe/Helsinki"),
|
||||
("Europe/Isle_of_Man", "Europe/Isle_of_Man"),
|
||||
("Europe/Istanbul", "Europe/Istanbul"),
|
||||
("Europe/Jersey", "Europe/Jersey"),
|
||||
("Europe/Kaliningrad", "Europe/Kaliningrad"),
|
||||
("Europe/Kiev", "Europe/Kiev"),
|
||||
("Europe/Kirov", "Europe/Kirov"),
|
||||
("Europe/Kyiv", "Europe/Kyiv"),
|
||||
("Europe/Lisbon", "Europe/Lisbon"),
|
||||
("Europe/Ljubljana", "Europe/Ljubljana"),
|
||||
("Europe/London", "Europe/London"),
|
||||
("Europe/Luxembourg", "Europe/Luxembourg"),
|
||||
("Europe/Madrid", "Europe/Madrid"),
|
||||
("Europe/Malta", "Europe/Malta"),
|
||||
("Europe/Mariehamn", "Europe/Mariehamn"),
|
||||
("Europe/Minsk", "Europe/Minsk"),
|
||||
("Europe/Monaco", "Europe/Monaco"),
|
||||
("Europe/Moscow", "Europe/Moscow"),
|
||||
("Europe/Nicosia", "Europe/Nicosia"),
|
||||
("Europe/Oslo", "Europe/Oslo"),
|
||||
("Europe/Paris", "Europe/Paris"),
|
||||
("Europe/Podgorica", "Europe/Podgorica"),
|
||||
("Europe/Prague", "Europe/Prague"),
|
||||
("Europe/Riga", "Europe/Riga"),
|
||||
("Europe/Rome", "Europe/Rome"),
|
||||
("Europe/Samara", "Europe/Samara"),
|
||||
("Europe/San_Marino", "Europe/San_Marino"),
|
||||
("Europe/Sarajevo", "Europe/Sarajevo"),
|
||||
("Europe/Saratov", "Europe/Saratov"),
|
||||
("Europe/Simferopol", "Europe/Simferopol"),
|
||||
("Europe/Skopje", "Europe/Skopje"),
|
||||
("Europe/Sofia", "Europe/Sofia"),
|
||||
("Europe/Stockholm", "Europe/Stockholm"),
|
||||
("Europe/Tallinn", "Europe/Tallinn"),
|
||||
("Europe/Tirane", "Europe/Tirane"),
|
||||
("Europe/Tiraspol", "Europe/Tiraspol"),
|
||||
("Europe/Ulyanovsk", "Europe/Ulyanovsk"),
|
||||
("Europe/Uzhgorod", "Europe/Uzhgorod"),
|
||||
("Europe/Vaduz", "Europe/Vaduz"),
|
||||
("Europe/Vatican", "Europe/Vatican"),
|
||||
("Europe/Vienna", "Europe/Vienna"),
|
||||
("Europe/Vilnius", "Europe/Vilnius"),
|
||||
("Europe/Volgograd", "Europe/Volgograd"),
|
||||
("Europe/Warsaw", "Europe/Warsaw"),
|
||||
("Europe/Zagreb", "Europe/Zagreb"),
|
||||
("Europe/Zaporozhye", "Europe/Zaporozhye"),
|
||||
("Europe/Zurich", "Europe/Zurich"),
|
||||
("Factory", "Factory"),
|
||||
("GB", "GB"),
|
||||
("GB-Eire", "GB-Eire"),
|
||||
("GMT", "GMT"),
|
||||
("GMT+0", "GMT+0"),
|
||||
("GMT-0", "GMT-0"),
|
||||
("GMT0", "GMT0"),
|
||||
("Greenwich", "Greenwich"),
|
||||
("HST", "HST"),
|
||||
("Hongkong", "Hongkong"),
|
||||
("Iceland", "Iceland"),
|
||||
("Indian/Antananarivo", "Indian/Antananarivo"),
|
||||
("Indian/Chagos", "Indian/Chagos"),
|
||||
("Indian/Christmas", "Indian/Christmas"),
|
||||
("Indian/Cocos", "Indian/Cocos"),
|
||||
("Indian/Comoro", "Indian/Comoro"),
|
||||
("Indian/Kerguelen", "Indian/Kerguelen"),
|
||||
("Indian/Mahe", "Indian/Mahe"),
|
||||
("Indian/Maldives", "Indian/Maldives"),
|
||||
("Indian/Mauritius", "Indian/Mauritius"),
|
||||
("Indian/Mayotte", "Indian/Mayotte"),
|
||||
("Indian/Reunion", "Indian/Reunion"),
|
||||
("Iran", "Iran"),
|
||||
("Israel", "Israel"),
|
||||
("Jamaica", "Jamaica"),
|
||||
("Japan", "Japan"),
|
||||
("Kwajalein", "Kwajalein"),
|
||||
("Libya", "Libya"),
|
||||
("MET", "MET"),
|
||||
("MST", "MST"),
|
||||
("MST7MDT", "MST7MDT"),
|
||||
("Mexico/BajaNorte", "Mexico/BajaNorte"),
|
||||
("Mexico/BajaSur", "Mexico/BajaSur"),
|
||||
("Mexico/General", "Mexico/General"),
|
||||
("NZ", "NZ"),
|
||||
("NZ-CHAT", "NZ-CHAT"),
|
||||
("Navajo", "Navajo"),
|
||||
("PRC", "PRC"),
|
||||
("PST8PDT", "PST8PDT"),
|
||||
("Pacific/Apia", "Pacific/Apia"),
|
||||
("Pacific/Auckland", "Pacific/Auckland"),
|
||||
("Pacific/Bougainville", "Pacific/Bougainville"),
|
||||
("Pacific/Chatham", "Pacific/Chatham"),
|
||||
("Pacific/Chuuk", "Pacific/Chuuk"),
|
||||
("Pacific/Easter", "Pacific/Easter"),
|
||||
("Pacific/Efate", "Pacific/Efate"),
|
||||
("Pacific/Enderbury", "Pacific/Enderbury"),
|
||||
("Pacific/Fakaofo", "Pacific/Fakaofo"),
|
||||
("Pacific/Fiji", "Pacific/Fiji"),
|
||||
("Pacific/Funafuti", "Pacific/Funafuti"),
|
||||
("Pacific/Galapagos", "Pacific/Galapagos"),
|
||||
("Pacific/Gambier", "Pacific/Gambier"),
|
||||
("Pacific/Guadalcanal", "Pacific/Guadalcanal"),
|
||||
("Pacific/Guam", "Pacific/Guam"),
|
||||
("Pacific/Honolulu", "Pacific/Honolulu"),
|
||||
("Pacific/Johnston", "Pacific/Johnston"),
|
||||
("Pacific/Kanton", "Pacific/Kanton"),
|
||||
("Pacific/Kiritimati", "Pacific/Kiritimati"),
|
||||
("Pacific/Kosrae", "Pacific/Kosrae"),
|
||||
("Pacific/Kwajalein", "Pacific/Kwajalein"),
|
||||
("Pacific/Majuro", "Pacific/Majuro"),
|
||||
("Pacific/Marquesas", "Pacific/Marquesas"),
|
||||
("Pacific/Midway", "Pacific/Midway"),
|
||||
("Pacific/Nauru", "Pacific/Nauru"),
|
||||
("Pacific/Niue", "Pacific/Niue"),
|
||||
("Pacific/Norfolk", "Pacific/Norfolk"),
|
||||
("Pacific/Noumea", "Pacific/Noumea"),
|
||||
("Pacific/Pago_Pago", "Pacific/Pago_Pago"),
|
||||
("Pacific/Palau", "Pacific/Palau"),
|
||||
("Pacific/Pitcairn", "Pacific/Pitcairn"),
|
||||
("Pacific/Pohnpei", "Pacific/Pohnpei"),
|
||||
("Pacific/Ponape", "Pacific/Ponape"),
|
||||
("Pacific/Port_Moresby", "Pacific/Port_Moresby"),
|
||||
("Pacific/Rarotonga", "Pacific/Rarotonga"),
|
||||
("Pacific/Saipan", "Pacific/Saipan"),
|
||||
("Pacific/Samoa", "Pacific/Samoa"),
|
||||
("Pacific/Tahiti", "Pacific/Tahiti"),
|
||||
("Pacific/Tarawa", "Pacific/Tarawa"),
|
||||
("Pacific/Tongatapu", "Pacific/Tongatapu"),
|
||||
("Pacific/Truk", "Pacific/Truk"),
|
||||
("Pacific/Wake", "Pacific/Wake"),
|
||||
("Pacific/Wallis", "Pacific/Wallis"),
|
||||
("Pacific/Yap", "Pacific/Yap"),
|
||||
("Poland", "Poland"),
|
||||
("Portugal", "Portugal"),
|
||||
("ROC", "ROC"),
|
||||
("ROK", "ROK"),
|
||||
("Singapore", "Singapore"),
|
||||
("Turkey", "Turkey"),
|
||||
("UCT", "UCT"),
|
||||
("US/Alaska", "US/Alaska"),
|
||||
("US/Aleutian", "US/Aleutian"),
|
||||
("US/Arizona", "US/Arizona"),
|
||||
("US/Central", "US/Central"),
|
||||
("US/East-Indiana", "US/East-Indiana"),
|
||||
("US/Eastern", "US/Eastern"),
|
||||
("US/Hawaii", "US/Hawaii"),
|
||||
("US/Indiana-Starke", "US/Indiana-Starke"),
|
||||
("US/Michigan", "US/Michigan"),
|
||||
("US/Mountain", "US/Mountain"),
|
||||
("US/Pacific", "US/Pacific"),
|
||||
("US/Samoa", "US/Samoa"),
|
||||
("UTC", "UTC"),
|
||||
("Universal", "Universal"),
|
||||
("W-SU", "W-SU"),
|
||||
("WET", "WET"),
|
||||
("Zulu", "Zulu"),
|
||||
],
|
||||
default=aircox.models.schedule.current_timezone_key,
|
||||
help_text="timezone used for the date",
|
||||
max_length=100,
|
||||
verbose_name="timezone",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,22 @@
|
|||
# Generated by Django 4.2.9 on 2024-03-19 22:38
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("aircox", "0023_station_legal_label_alter_schedule_timezone"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name="usersettings",
|
||||
old_name="playlist_editor_columns",
|
||||
new_name="tracklist_editor_columns",
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="usersettings",
|
||||
old_name="playlist_editor_sep",
|
||||
new_name="tracklist_editor_sep",
|
||||
),
|
||||
]
|
|
@ -0,0 +1,36 @@
|
|||
# Generated by Django 4.2.9 on 2024-03-25 20:23
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("aircox", "0024_rename_playlist_editor_columns_usersettings_tracklist_editor_columns_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="sound",
|
||||
name="is_removed",
|
||||
field=models.BooleanField(default=False, help_text="file has been removed", verbose_name="removed"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="sound",
|
||||
name="is_downloadable",
|
||||
field=models.BooleanField(
|
||||
default=False,
|
||||
help_text="sound can be downloaded by visitors (sound must be public)",
|
||||
verbose_name="downloadable",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="sound",
|
||||
name="is_public",
|
||||
field=models.BooleanField(default=False, help_text="sound is available as podcast", verbose_name="public"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="sound",
|
||||
name="type",
|
||||
field=models.SmallIntegerField(choices=[(0, "other"), (1, "archive"), (2, "excerpt")], verbose_name="type"),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,108 @@
|
|||
# Generated by Django 4.2.9 on 2024-03-26 02:53
|
||||
|
||||
import aircox.models.file
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("aircox", "0025_sound_is_removed_alter_sound_is_downloadable_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name="sound",
|
||||
options={"verbose_name": "Sound file", "verbose_name_plural": "Sound files"},
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="sound",
|
||||
name="episode",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="sound",
|
||||
name="position",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="sound",
|
||||
name="type",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="sound",
|
||||
name="broadcast",
|
||||
field=models.BooleanField(
|
||||
default=False, help_text="The sound is broadcasted on air", verbose_name="Broadcast"
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="sound",
|
||||
name="description",
|
||||
field=models.TextField(blank=True, default="", max_length=256, verbose_name="description"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="sound",
|
||||
name="file",
|
||||
field=models.FileField(
|
||||
db_index=True, max_length=256, upload_to=aircox.models.file.File._upload_to, verbose_name="file"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="sound",
|
||||
name="is_downloadable",
|
||||
field=models.BooleanField(
|
||||
default=False, help_text="sound can be downloaded by visitors", verbose_name="downloadable"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="sound",
|
||||
name="is_public",
|
||||
field=models.BooleanField(default=False, help_text="file is publicly accessible", verbose_name="public"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="sound",
|
||||
name="is_removed",
|
||||
field=models.BooleanField(
|
||||
db_index=True, default=False, help_text="file has been removed from server", verbose_name="removed"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="sound",
|
||||
name="name",
|
||||
field=models.CharField(db_index=True, max_length=64, verbose_name="name"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="sound",
|
||||
name="program",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="aircox.program",
|
||||
verbose_name="Program",
|
||||
),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="EpisodeSound",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
(
|
||||
"position",
|
||||
models.PositiveSmallIntegerField(
|
||||
default=0, help_text="position in the playlist", verbose_name="order"
|
||||
),
|
||||
),
|
||||
(
|
||||
"broadcast",
|
||||
models.BooleanField(
|
||||
blank=None, help_text="The sound is broadcasted on air", verbose_name="Broadcast"
|
||||
),
|
||||
),
|
||||
("episode", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="aircox.episode")),
|
||||
("sound", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="aircox.sound")),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Episode Sound",
|
||||
"verbose_name_plural": "Episode Sounds",
|
||||
},
|
||||
),
|
||||
]
|
File diff suppressed because one or more lines are too long
|
@ -7,6 +7,7 @@ Context:
|
|||
- related_field: field name that target object
|
||||
- object: related object
|
||||
- formset: formset used to render the list editor
|
||||
- formset_data: formset data
|
||||
{% endcomment %}
|
||||
|
||||
{% load aircox aircox_admin static i18n %}
|
||||
|
@ -19,6 +20,7 @@ Context:
|
|||
|
||||
<{{ tag }}
|
||||
{% block tag-attrs %}
|
||||
:form-data="{{ formset_data|json }}"
|
||||
:labels="window.aircox.labels"
|
||||
:init-data="{% formset_inline_data formset=formset %}"
|
||||
:columns="[{% for n, f in fields.items %}{% if not f.widget.is_hidden %}'{{ n }}',{% endif %}{% endfor %} ]"
|
||||
|
@ -26,19 +28,6 @@ Context:
|
|||
data-prefix="{{ formset.prefix }}-"
|
||||
{% endblock %}>
|
||||
{% block inner %}
|
||||
<template #top="{items}">
|
||||
{% block top %}
|
||||
<input type="hidden" name="{{ formset.prefix }}-TOTAL_FORMS"
|
||||
:value="items.length || 0"/>
|
||||
<input type="hidden" name="{{ formset.prefix }}-INITIAL_FORMS"
|
||||
value="{{ formset.initial_form_count }}"
|
||||
/>
|
||||
<input type="hidden" name="{{ formset.prefix }}-MIN_NUM_FORMS"
|
||||
value="{{ formset.min_num }}"/>
|
||||
<input type="hidden" name="{{ formset.prefix }}-MAX_NUM_FORMS"
|
||||
value="{{ formset.max_num }}"/>
|
||||
{% endblock %}
|
||||
</template>
|
||||
<template #rows-header-head>
|
||||
{% block rows-header-head %}
|
||||
<th style="max-width:2em" title="{{ fields.position.help_text }}"
|
||||
|
@ -49,46 +38,12 @@ Context:
|
|||
</th>
|
||||
{% endblock %}
|
||||
</template>
|
||||
<template v-slot:row-head="{item,row,attr}">
|
||||
<td>
|
||||
{% block row-head %}
|
||||
[[ row+1 ]]
|
||||
<input type="hidden"
|
||||
:name="'{{ formset.prefix }}-' + row + '-position'"
|
||||
:value="row"/>
|
||||
<input type="hidden"
|
||||
:name="'{{ formset.prefix }}-' + row + '-id'"
|
||||
:value="item.data.id || item.id "/>
|
||||
|
||||
{% for name, field in fields.items %}
|
||||
{% if name == related_field %}
|
||||
<input type="hidden"
|
||||
:name="'{{ formset.prefix }}-' + row + '-{{ name }}'"
|
||||
value="{{ object.id }}"/>
|
||||
{% elif name != 'position' and field.widget.is_hidden %}
|
||||
<input type="hidden"
|
||||
:name="'{{ formset.prefix }}-' + row + '-{{ name }}'"
|
||||
v-model="item.data['{{ name }}']"/>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
</td>
|
||||
</template>
|
||||
{% for name, field in fields.items %}
|
||||
{% if name != related_field and not field.widget.is_hidden and not field.is_readonly %}
|
||||
<template v-slot:row-{{ name }}="{item,cell,value,attr,emit}">
|
||||
<div class="field">
|
||||
{% with full_name="'"|add:formset.prefix|add:"-' + cell.row + '-"|add:name|add:"'" %}
|
||||
{% block row-field %}
|
||||
<div class="control">
|
||||
{% include "./v_form_field.html" with value="item.data."|add:name name=full_name %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endwith %}
|
||||
<p v-for="error in item.error(attr)" class="help is-danger">
|
||||
[[ error ]] !
|
||||
</p>
|
||||
</div>
|
||||
{% if not field.widget.is_hidden and not field.is_readonly %}
|
||||
<template v-slot:control-{{ name }}="{item,cell,value,attr,emit,inputName}">
|
||||
{% block row-control %}
|
||||
{% include "./v_form_field.html" with value="item.data."|add:name name="inputName" %}
|
||||
{% endblock %}
|
||||
</template>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
|
|
@ -20,7 +20,6 @@ Context:
|
|||
sound-list-url="{% url "api:sound-list" %}?program={{ object.parent_id }}"
|
||||
sound-upload-url="{% url "api:sound-list" %}"
|
||||
sound-delete-url="{% url "api:sound-detail" pk=123 %}"
|
||||
:item-defaults="{episode: {{ object.pk }}}"
|
||||
{% endblock %}
|
||||
|
||||
{% block inner %}
|
||||
|
@ -29,19 +28,15 @@ sound-delete-url="{% url "api:sound-detail" pk=123 %}"
|
|||
<template #upload-form>
|
||||
{% for field in sound_form %}
|
||||
{% with field.name as name %}
|
||||
{% with field.initial as value %}
|
||||
{% with field.field as field %}
|
||||
{% if name in "episode,program,sound" %}
|
||||
{% include "./form_field.html" with value=value hidden=True %}
|
||||
{% if name in "episode" %}
|
||||
{% include "./form_field.html" with value=field.initial field=field.field hidden=True %}
|
||||
{% elif name != "file" %}
|
||||
<div class="field is-horizontal">
|
||||
<label class="label mr-3">{{ field.label }}</label>
|
||||
{% include "./form_field.html" with value=value %}
|
||||
{% include "./form_field.html" with value=field.initial field=field.field %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</template>
|
||||
<template #row-delete="{cell}">
|
||||
|
|
|
@ -16,12 +16,19 @@
|
|||
{{ block.super }}
|
||||
{% endblock %}
|
||||
|
||||
{% block row-field %}
|
||||
{% block row-control %}
|
||||
{% if name == "tags" %}
|
||||
<input type="text" class="input"
|
||||
:name="inputName"
|
||||
v-model="item.data[attr]"
|
||||
@change="emit('change', cell.col)"
|
||||
>
|
||||
{% else %}
|
||||
<a-autocomplete
|
||||
:input-class="['input', item.error(attr) ? 'is-danger' : 'half-field']"
|
||||
url="{% url 'api:track-autocomplete' %}?{{ name }}=${query}&field={{ name }}"
|
||||
:name="'{{ formset.prefix }}-' + cell.row + '-{{ name }}'"
|
||||
:name="inputName"
|
||||
v-model="item.data[attr]"
|
||||
title="{{ name }}"
|
||||
@change="emit('change', col)"/>
|
||||
@change="emit('change', cell.col)"/>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<a-playlist v-if="page" :set="podcasts"
|
||||
name="{{ page.title }}"
|
||||
list-class="menu-list" item-class="menu-item"
|
||||
:player="player" :actions="['play']"
|
||||
:player="player" :actions="['play', 'pin']"
|
||||
@select="player.playItems('queue', $event.item)">
|
||||
</a-playlist>
|
||||
</section>
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
<template v-slot="{podcasts,page}">
|
||||
{{ block.super }}
|
||||
<hr/>
|
||||
{% include "./dashboard/tracklist_editor.html" with formset=tracklist_formset %}
|
||||
{% include "./dashboard/tracklist_editor.html" with formset=tracklist_formset formset_data=tracklist_formset_data %}
|
||||
<hr/>
|
||||
<h3 class="title">{% translate "Podcasts" %}</h3>
|
||||
{% include "./dashboard/soundlist_editor.html" with formset=soundlist_formset %}
|
||||
{% include "./dashboard/soundlist_editor.html" with formset=soundlist_formset formset_data=soundlist_formset_data %}
|
||||
</template>
|
||||
</a-episode>
|
||||
{% endblock %}
|
||||
|
|
|
@ -2,8 +2,9 @@ from django.contrib.auth.mixins import UserPassesTestMixin
|
|||
from django.urls import reverse
|
||||
|
||||
from aircox.models import Episode, Program, StaticPage, Track
|
||||
from aircox import forms
|
||||
from ..filters import EpisodeFilters
|
||||
from aircox import forms, filters
|
||||
|
||||
from .mixins import VueFormDataMixin
|
||||
from .page import PageListView
|
||||
from .program import ProgramPageDetailView, BaseProgramMixin
|
||||
from .page import PageUpdateView
|
||||
|
@ -36,7 +37,7 @@ class EpisodeDetailView(ProgramPageDetailView):
|
|||
|
||||
class EpisodeListView(PageListView):
|
||||
model = Episode
|
||||
filterset_class = EpisodeFilters
|
||||
filterset_class = filters.EpisodeFilters
|
||||
parent_model = Program
|
||||
attach_to_value = StaticPage.Target.EPISODES
|
||||
|
||||
|
@ -46,7 +47,7 @@ class PodcastListView(EpisodeListView):
|
|||
queryset = Episode.objects.published().with_podcasts().order_by("-pub_date")
|
||||
|
||||
|
||||
class EpisodeUpdateView(UserPassesTestMixin, BaseProgramMixin, PageUpdateView):
|
||||
class EpisodeUpdateView(UserPassesTestMixin, VueFormDataMixin, BaseProgramMixin, PageUpdateView):
|
||||
model = Episode
|
||||
form_class = forms.EpisodeForm
|
||||
template_name = "aircox/episode_form.html"
|
||||
|
@ -110,6 +111,10 @@ class EpisodeUpdateView(UserPassesTestMixin, BaseProgramMixin, PageUpdateView):
|
|||
for key, func in forms:
|
||||
if key not in kwargs:
|
||||
kwargs[key] = func(self.object)
|
||||
|
||||
for key in ("soundlist_formset", "tracklist_formset"):
|
||||
formset = kwargs[key]
|
||||
kwargs[f"{key}_data"] = self.get_formset_data(formset, {"episode": self.object.id})
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
|
@ -123,7 +128,6 @@ class EpisodeUpdateView(UserPassesTestMixin, BaseProgramMixin, PageUpdateView):
|
|||
for formset in formsets.values():
|
||||
if not formset.is_valid():
|
||||
invalid = True
|
||||
breakpoint()
|
||||
else:
|
||||
formset.save()
|
||||
if invalid:
|
||||
|
|
|
@ -108,3 +108,37 @@ class FiltersMixin:
|
|||
params = self.request.GET.copy()
|
||||
kwargs["get_params"] = params.pop("page", True) and params
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
class VueFormDataMixin:
|
||||
"""Provide form information as data to be used with vue components."""
|
||||
|
||||
# Note: values corresponds to AFormSet expected one
|
||||
|
||||
def get_form_field_data(self, form, values=None):
|
||||
"""Return form fields as data."""
|
||||
model = form.Meta.model
|
||||
fields = ((name, field, model._meta.get_field(name)) for name, field in form.base_fields.items())
|
||||
return [
|
||||
{
|
||||
"name": name,
|
||||
"label": str(m_field.verbose_name).capitalize(),
|
||||
"help": str(m_field.help_text).capitalize(),
|
||||
"hidden": field.widget.is_hidden,
|
||||
"value": values and values.get(name),
|
||||
}
|
||||
for name, field, m_field in fields
|
||||
]
|
||||
|
||||
def get_formset_data(self, formset, field_values=None, **kwargs):
|
||||
"""Return formset as data object."""
|
||||
return {
|
||||
"prefix": formset.prefix,
|
||||
"management": {
|
||||
"initial_forms": formset.initial_form_count(),
|
||||
"min_num_forms": formset.min_num,
|
||||
"max_num_forms": formset.max_num,
|
||||
},
|
||||
"fields": self.get_form_field_data(formset.form, field_values),
|
||||
**kwargs,
|
||||
}
|
||||
|
|
195
assets/src/components/AFormSet.vue
Normal file
195
assets/src/components/AFormSet.vue
Normal file
|
@ -0,0 +1,195 @@
|
|||
<template>
|
||||
<div>
|
||||
<input type="hidden" :name="_prefix + 'TOTAL_FORMS'" :value="items.length || 0"/>
|
||||
<template v-for="(value,name) in formData.management" v-bind:key="name">
|
||||
<input type="hidden" :name="_prefix + name.toUpperCase()"
|
||||
:value="value"/>
|
||||
</template>
|
||||
|
||||
<a-rows ref="rows" :set="set"
|
||||
:columns="visibleFields" :columnsOrderable="columnsOrderable"
|
||||
:orderable="orderable" @move="moveItem" @colmove="onMoveColumn"
|
||||
@cell="e => $emit('cell', e)">
|
||||
|
||||
<template #header-head>
|
||||
<template v-if="orderable">
|
||||
<th style="max-width:2em" :title="orderField.label"
|
||||
:aria-label="orderField.label"
|
||||
:aria-description="orderField.help || ''">
|
||||
<span class="icon">
|
||||
<i class="fa fa-arrow-down-1-9"></i>
|
||||
</span>
|
||||
</th>
|
||||
<slot name="rows-header-head"></slot>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template #row-head="data">
|
||||
<input v-if="orderable" type="hidden"
|
||||
:name="_prefix + data.row + '-' + orderBy"
|
||||
:value="data.row"/>
|
||||
<input type="hidden" :name="_prefix + data.row + '-id'"
|
||||
:value="data.item ? data.item.id : ''"/>
|
||||
|
||||
<template v-for="field of hiddenFields" v-bind:key="field.name">
|
||||
<input type="hidden"
|
||||
v-if="!(field.name in ['id', orderBy])"
|
||||
:name="_prefix + data.row + '-' + field.name"
|
||||
:value="field.value in [null, undefined] ? data.item.data[name] : field.value"/>
|
||||
</template>
|
||||
|
||||
<slot name="row-head" v-bind="data">
|
||||
<td v-if="orderable">{{ data.row+1 }}</td>
|
||||
</slot>
|
||||
</template>
|
||||
|
||||
<template v-for="(field,slot) of fieldSlots" v-bind:key="field.name"
|
||||
v-slot:[slot]="data">
|
||||
<slot :name="slot" v-bind="data" :field="field" :input-name="_prefix + data.cell.row + '-' + field.name">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<slot :name="'control-' + field.name" v-bind="data" :field="field" :input-name="_prefix + data.cell.row + '-' + field.name"/>
|
||||
</div>
|
||||
<p v-for="[error,index] in data.item.error(field.name)" class="help is-danger" v-bind:key="index">
|
||||
{{ error }}
|
||||
</p>
|
||||
</div>
|
||||
</slot>
|
||||
</template>
|
||||
|
||||
<template #row-tail="data">
|
||||
<slot v-if="$slots['row-tail']" name="row-tail" v-bind="data"/>
|
||||
<td class="align-right pr-0">
|
||||
<button type="button" class="button square"
|
||||
@click.stop="removeItem(data.row, data.item)"
|
||||
:title="labels.remove_item"
|
||||
:aria-label="labels.remove_item">
|
||||
<span class="icon"><i class="fa fa-trash" /></span>
|
||||
</button>
|
||||
</td>
|
||||
</template>
|
||||
</a-rows>
|
||||
<div class="a-formset-footer flex-row">
|
||||
<div class="flex-grow-1 flex-row">
|
||||
<slot name="footer"/>
|
||||
</div>
|
||||
<div class="flex-grow-1 align-right">
|
||||
<button type="button" class="button square is-warning p-2"
|
||||
@click="reset()"
|
||||
:title="labels.discard_changes"
|
||||
:aria-label="labels.discard_changes"
|
||||
>
|
||||
<span class="icon"><i class="fa fa-rotate" /></span>
|
||||
</button>
|
||||
<button type="button" class="button square is-primary p-2"
|
||||
@click="onActionAdd"
|
||||
:title="labels.add_item"
|
||||
:aria-label="labels.add_item"
|
||||
>
|
||||
<span class="icon">
|
||||
<i class="fa fa-plus"/></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {cloneDeep} from 'lodash'
|
||||
import Model, {Set} from '../model'
|
||||
|
||||
import ARows from './ARows'
|
||||
|
||||
export default {
|
||||
emit: ['cell', 'move', 'colmove', 'load'],
|
||||
components: {ARows},
|
||||
|
||||
props: {
|
||||
labels: Object,
|
||||
|
||||
//! If provided call this function instead of adding an item to rows on "+" button click.
|
||||
actionAdd: Function,
|
||||
|
||||
//! If True, columns can be reordered
|
||||
columnsOrderable: Boolean,
|
||||
//! Field name used for ordering
|
||||
orderBy: String,
|
||||
|
||||
//! Formset data as returned by get_formset_data
|
||||
formData: Object,
|
||||
//! Model class used for item's set
|
||||
model: {type: Function, default: Model},
|
||||
//! initial data set load at mount
|
||||
initials: Array,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
set: new Set(Model),
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
// ---- fields
|
||||
_prefix() { return this.formData.prefix ? this.formData.prefix + '-' : '' },
|
||||
fields() { return this.formData.fields },
|
||||
orderField() { return this.orderBy && this.fields.find(f => f.name == this.orderBy) },
|
||||
orderable() { return !!this.orderField },
|
||||
|
||||
hiddenFields() { return this.fields.filter(f => f.hidden && !(this.orderable && f == this.orderField)) },
|
||||
visibleFields() { return this.fields.filter(f => !f.hidden) },
|
||||
|
||||
fieldSlots() { return this.visibleFields.reduce(
|
||||
(slots, f) => ({...slots, ['row-' + f.name]: f}),
|
||||
{}
|
||||
)},
|
||||
|
||||
items() { return this.set.items },
|
||||
rows() { return this.$refs.rows },
|
||||
},
|
||||
|
||||
methods: {
|
||||
onCellEvent(event) { this.$emit('cell', event) },
|
||||
onMoveColumn(event) { this.$emit('colmove', event) },
|
||||
onActionAdd() {
|
||||
if(this.actionAdd)
|
||||
return this.actionAdd(this)
|
||||
this.set.push()
|
||||
},
|
||||
|
||||
moveItem(event) {
|
||||
const {from, to} = event
|
||||
const set_ = event.set || this.set
|
||||
set_.move(from, to);
|
||||
this.$emit('move', {...event, seŧ: set_})
|
||||
},
|
||||
|
||||
removeItem(row) {
|
||||
const item = this.items[row]
|
||||
if(item.id) {
|
||||
// TODO
|
||||
}
|
||||
else {
|
||||
this.items.splice(row,1)
|
||||
}
|
||||
},
|
||||
|
||||
//! Load items into set
|
||||
load(items=[], reset=false) {
|
||||
if(reset)
|
||||
this.set.items = []
|
||||
for(var item of items)
|
||||
this.set.push(cloneDeep(item))
|
||||
this.$emit('load', items)
|
||||
},
|
||||
|
||||
//! Reset forms to initials
|
||||
reset() {
|
||||
this.load(this.initials || [], true)
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.reset()
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -27,12 +27,15 @@ import {isReactive, toRefs} from 'vue'
|
|||
import Model from '../model'
|
||||
|
||||
export default {
|
||||
emit: ['move', 'cell'],
|
||||
emits: ['move', 'cell'],
|
||||
|
||||
props: {
|
||||
//! Item to display in row
|
||||
item: Object,
|
||||
item: {type: Object, default: () => ({})},
|
||||
//! Columns to display, as items' attributes
|
||||
//! - name: field name / item attribute value
|
||||
//! - label: display label
|
||||
//! - help: help text
|
||||
columns: Array,
|
||||
//! Default cell's info
|
||||
cell: {type: Object, default() { return {row: 0}}},
|
||||
|
|
|
@ -1,34 +1,38 @@
|
|||
<template>
|
||||
<table class="table is-stripped is-fullwidth">
|
||||
<thead>
|
||||
<a-row :item="labels" :columns="columns" :orderable="orderable"
|
||||
@move="$emit('colmove', $event)">
|
||||
<a-row :columns="columnNames"
|
||||
:orderable="columnsOrderable" cellTag="th"
|
||||
@move="moveColumn">
|
||||
<template v-if="$slots['header-head']" v-slot:head="data">
|
||||
<slot name="header-head" v-bind="data"/>
|
||||
</template>
|
||||
<template v-if="$slots['header-tail']" v-slot:tail="data">
|
||||
<slot name="header-tail" v-bind="data"/>
|
||||
</template>
|
||||
<template v-for="column of columns" v-bind:key="column.name"
|
||||
v-slot:[column.name]="data">
|
||||
<slot :name="'header-' + column.name" v-bind="data">
|
||||
{{ column.label }}
|
||||
<span v-if="column.help" class="icon small"
|
||||
:title="column.help">
|
||||
<i class="fa fa-circle-question"/>
|
||||
</span>
|
||||
</slot>
|
||||
</template>
|
||||
</a-row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<slot name="head"/>
|
||||
<template v-for="(item,row) in items" :key="row">
|
||||
<!-- data-index comes from AList component drag & drop -->
|
||||
<a-row :item="item" :cell="{row}" :columns="columns" :data-index="row"
|
||||
<a-row :item="item" :cell="{row}" :columns="columnNames" :data-index="row"
|
||||
:data-row="row"
|
||||
:draggable="orderable"
|
||||
@dragstart="onDragStart" @dragover="onDragOver" @drop="onDrop"
|
||||
@cell="onCellEvent(row, $event)">
|
||||
<template v-for="[name,slot] of rowSlots" :key="slot" v-slot:[slot]="data">
|
||||
<template v-if="slot == 'head' || slot == 'tail'">
|
||||
<slot :name="name" v-bind="data"/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div>
|
||||
<slot :name="name" v-bind="data"/>
|
||||
</div>
|
||||
</template>
|
||||
<slot :name="name" v-bind="data"/>
|
||||
</template>
|
||||
</a-row>
|
||||
</template>
|
||||
|
@ -43,28 +47,32 @@ import ARow from './ARow.vue'
|
|||
const Component = {
|
||||
extends: AList,
|
||||
components: { ARow },
|
||||
emit: ['cell', 'colmove'],
|
||||
emits: ['cell', 'colmove'],
|
||||
|
||||
props: {
|
||||
...AList.props,
|
||||
//! Ordered list of columns, as objects with:
|
||||
//! - name: item attribute value
|
||||
//! - label: display label
|
||||
//! - help: help text
|
||||
//! - hidden: if true, field is hidden
|
||||
columns: Array,
|
||||
labels: Object,
|
||||
//! If True, columns are orderable
|
||||
columnsOrderable: Boolean,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
...super.data,
|
||||
// TODO: add observer
|
||||
columns_: [...this.columns],
|
||||
extraItem: new this.set.model(),
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
rowCells() {
|
||||
const cells = []
|
||||
for(var row in this.items)
|
||||
cells.push({row})
|
||||
},
|
||||
|
||||
columnNames() { return this.columns_.map(c => c.name) },
|
||||
columnLabels() { return this.columns_.map(c => c.label) },
|
||||
rowSlots() {
|
||||
return Object.keys(this.$slots).filter(x => x.startsWith('row-'))
|
||||
.map(x => [x, x.slice(4)])
|
||||
|
@ -72,6 +80,17 @@ const Component = {
|
|||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Move column using provided event object (as `{from, to}`)
|
||||
*/
|
||||
moveColumn(event) {
|
||||
const {from, to} = event
|
||||
const value = this.columns_[from]
|
||||
this.columns_.splice(from, 1)
|
||||
this.columns_.splice(to, 0, value)
|
||||
this.$emit('colmove', event)
|
||||
},
|
||||
|
||||
/**
|
||||
* React on 'cell' event, re-emitting it with additional values:
|
||||
* - `set`: data set
|
||||
|
|
|
@ -21,141 +21,63 @@
|
|||
</template>
|
||||
</a-select-file>
|
||||
|
||||
<slot name="top" :set="set" :items="set.items"></slot>
|
||||
<a-rows :set="set" :columns="allColumns"
|
||||
:labels="allColumnsLabels" :allow-create="true" :orderable="true"
|
||||
@move="listItemMove">
|
||||
<a-form-set ref="formset" :form-data="formData" :labels="labels"
|
||||
:initials="initData.items"
|
||||
order-by="position"
|
||||
:action-add="actionAdd">
|
||||
<template v-for="[name,slot] of rowsSlots" :key="slot"
|
||||
v-slot:[slot]="data">
|
||||
<slot v-if="name != 'row-tail'" :name="name" v-bind="data"/>
|
||||
</template>
|
||||
|
||||
<template #row-sound="{item}">
|
||||
<template #row-sound="{item,inputName}">
|
||||
<label>{{ item.data.name }}</label><br>
|
||||
<audio controls :src="item.data.url"/>
|
||||
<input type="hidden" :name="inputName" :value="item.data.sound"/>
|
||||
</template>
|
||||
</a-rows>
|
||||
|
||||
<div class="flex-row">
|
||||
<div class="flex-grow-1 flex-row">
|
||||
</div>
|
||||
<div class="flex-grow-1 align-right">
|
||||
<button type="button" class="button square is-warning p-2"
|
||||
@click="loadData({items: this.initData.items},true)"
|
||||
:title="labels.discard_changes"
|
||||
:aria-label="labels.discard_changes"
|
||||
>
|
||||
<span class="icon"><i class="fa fa-rotate" /></span>
|
||||
</button>
|
||||
<button type="button" class="button square is-primary p-2"
|
||||
@click="$refs['select-file'].open()"
|
||||
:title="labels.add_sound"
|
||||
:aria-label="labels.add_sound"
|
||||
>
|
||||
<span class="icon">
|
||||
<i class="fa fa-plus"/></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</a-form-set>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
// import {dropRightWhile, cloneDeep, isEqual} from 'lodash'
|
||||
import {cloneDeep} from 'lodash'
|
||||
import Model, {Set} from '../model'
|
||||
|
||||
import ARows from './ARows'
|
||||
//import AFileUpload from "./AFileUpload"
|
||||
import AFormSet from './AFormSet'
|
||||
import ASelectFile from "./ASelectFile"
|
||||
|
||||
export default {
|
||||
components: {ARows, ASelectFile},
|
||||
components: {AFormSet, ASelectFile},
|
||||
|
||||
props: {
|
||||
// default values of items
|
||||
itemDefaults: Object,
|
||||
formData: Object,
|
||||
labels: Object,
|
||||
// initial datas
|
||||
initData: Object,
|
||||
labels: Object,
|
||||
|
||||
soundListUrl: String,
|
||||
soundUploadUrl: String,
|
||||
soundDeleteUrl: String,
|
||||
|
||||
columns: {
|
||||
type: Array,
|
||||
default: () => ['name', "broadcast"]
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
set: new Set(Model),
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
player_() {
|
||||
return this.player || window.aircox.player
|
||||
},
|
||||
|
||||
allColumns() {
|
||||
return ["sound", ...this.columns, "delete"]
|
||||
},
|
||||
|
||||
allColumnsLabels() {
|
||||
return {...this.labels, ...this.initData.fields}
|
||||
},
|
||||
|
||||
items() {
|
||||
return this.set.items
|
||||
},
|
||||
|
||||
rowsSlots() {
|
||||
return Object.keys(this.$slots)
|
||||
.filter(x => x.startsWith('row-') || x.startsWith('rows-'))
|
||||
.filter(x => x.startsWith('row-') || x.startsWith('rows-') || x.startsWith('control-'))
|
||||
.map(x => [x, x.startsWith('rows-') ? x.slice(5) : x])
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
listItemMove({from, to, set}) {
|
||||
set.move(from, to);
|
||||
actionAdd() {
|
||||
this.$refs['select-file'].open()
|
||||
},
|
||||
|
||||
/**
|
||||
* Load initial data
|
||||
*/
|
||||
loadData({items=[] /*, settings=null*/}, reset=false) {
|
||||
if(reset) {
|
||||
this.set.items = []
|
||||
}
|
||||
for(var index in items)
|
||||
this.set.push(cloneDeep(items[index]))
|
||||
// if(settings)
|
||||
// this.settingsSaved(settings)
|
||||
},
|
||||
|
||||
selected(item) {
|
||||
selected(item) {
|
||||
const data = {
|
||||
...this.itemDefaults,
|
||||
"sound": item.id,
|
||||
"name": item.name,
|
||||
"url": item.url,
|
||||
"broadcast": item.broadcast,
|
||||
}
|
||||
this.set.push(data)
|
||||
this.$refs.formset.set.push(data)
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
initData(val) {
|
||||
this.loadData(val)
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.initData && this.loadData(this.initData)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<slot name="top" :set="set" :columns="allColumns" :items="items"/>
|
||||
<section v-show="page == Page.Text" class="panel">
|
||||
<textarea ref="textarea" class="is-fullwidth is-size-6" rows="20"
|
||||
@change="updateList"
|
||||
|
@ -35,60 +34,33 @@
|
|||
|
||||
</section>
|
||||
<section v-show="page == Page.List" class="panel">
|
||||
<a-rows :set="set" :columns="allColumns" :labels="initData.fields"
|
||||
:orderable="true" @move="listItemMove" @colmove="columnMove"
|
||||
@cell="onCellEvent">
|
||||
<a-form-set ref="formset"
|
||||
:form-data="formData" :initials="initData.items"
|
||||
:columnsOrderable="true" :labels="labels"
|
||||
order-by="position"
|
||||
@load="updateInput" @colmove="updateInput" @move="updateInput"
|
||||
@cell="onCellEvent">
|
||||
<template v-for="[name,slot] of rowsSlots" :key="slot"
|
||||
v-slot:[slot]="data">
|
||||
<slot v-if="name != 'row-tail'" :name="name" v-bind="data"/>
|
||||
</template>
|
||||
|
||||
<template v-slot:row-tail="data">
|
||||
<slot v-if="$slots['row-tail']" :name="row-tail" v-bind="data"/>
|
||||
<td class="align-right pr-0">
|
||||
<button type="button" class="button square"
|
||||
@click.stop="items.splice(data.row,1)"
|
||||
:title="labels.remove_item"
|
||||
:aria-label="labels.remove_item">
|
||||
<span class="icon"><i class="fa fa-trash" /></span>
|
||||
</button>
|
||||
</td>
|
||||
<template #footer>
|
||||
<div class="field">
|
||||
<p class="control">
|
||||
<button type="button" class="button is-info"
|
||||
@click="$refs.settings.open()">
|
||||
<span class="icon is-small">
|
||||
<i class="fa fa-cog"></i>
|
||||
</span>
|
||||
<span>Options</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</a-rows>
|
||||
</a-form-set>
|
||||
</section>
|
||||
|
||||
<div class="flex-row">
|
||||
<div class="flex-grow-1 flex-row">
|
||||
<div class="field">
|
||||
<p class="control">
|
||||
<button type="button" class="button is-info"
|
||||
@click="$refs.settings.open()">
|
||||
<span class="icon is-small">
|
||||
<i class="fa fa-cog"></i>
|
||||
</span>
|
||||
<span>Options</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-grow-1 align-right">
|
||||
<button type="button" class="button square is-warning p-2"
|
||||
@click="loadData({items: this.initData.items},true)"
|
||||
:title="labels.discard_changes"
|
||||
:aria-label="labels.discard_changes"
|
||||
>
|
||||
<span class="icon"><i class="fa fa-rotate" /></span>
|
||||
</button>
|
||||
<button type="button" class="button square is-primary p-2" v-if="page == Page.List"
|
||||
@click="this.set.push(new this.set.model())"
|
||||
:title="labels.add_item"
|
||||
:aria-label="labels.add_item"
|
||||
>
|
||||
<span class="icon"><i class="fa fa-plus"/></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-modal ref="settings" title="Options">
|
||||
<template #default>
|
||||
<div class="field">
|
||||
|
@ -97,12 +69,14 @@
|
|||
</label>
|
||||
<table class="table is-bordered"
|
||||
style="vertical-align: middle">
|
||||
<tr>
|
||||
<a-row :columns="allColumns" :item="initData.fields"
|
||||
@move="formatMove" :orderable="true">
|
||||
<tr v-if="$refs.formset">
|
||||
<a-row :columns="$refs.formset.rows.columnNames"
|
||||
:item="$refs.formset.rows.columnLabels"
|
||||
@move="$refs.formset.rows.moveColumn"
|
||||
>
|
||||
<template v-slot:cell-after="{cell}">
|
||||
<td style="cursor:pointer;" v-if="cell.col < allColumns.length-1">
|
||||
<span class="icon" @click="formatMove({from: cell.col, to: cell.col+1})"
|
||||
<td style="cursor:pointer;" v-if="cell.col < $refs.formset.rows.columns_.length-1">
|
||||
<span class="icon" @click="$refs.formset.rows.moveColumn({from: cell.col, to: cell.col+1})"
|
||||
><i class="fa fa-left-right"/>
|
||||
</span>
|
||||
</td>
|
||||
|
@ -143,16 +117,14 @@
|
|||
</div>
|
||||
</template>
|
||||
</a-modal>
|
||||
<slot name="bottom" :set="set" :columns="allColumns" :items="items"/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {dropRightWhile, cloneDeep, isEqual} from 'lodash'
|
||||
import Model, {Set} from '../model'
|
||||
|
||||
import AActionButton from './AActionButton'
|
||||
import AFormSet from './AFormSet'
|
||||
import ARow from './ARow'
|
||||
import ARows from './ARows'
|
||||
import AModal from "./AModal"
|
||||
|
||||
/// Page display
|
||||
|
@ -161,12 +133,14 @@ export const Page = {
|
|||
}
|
||||
|
||||
export default {
|
||||
components: { AActionButton, ARow, ARows, AModal },
|
||||
components: { AActionButton, AFormSet, ARow, AModal },
|
||||
props: {
|
||||
formData: Object,
|
||||
labels: Object,
|
||||
|
||||
///! initial data as: {items: [], fields: {column_name: label, settings: {}}
|
||||
initData: Object,
|
||||
dataPrefix: String,
|
||||
labels: Object,
|
||||
settingsUrl: String,
|
||||
defaultColumns: {
|
||||
type: Array,
|
||||
|
@ -175,13 +149,12 @@ export default {
|
|||
|
||||
data() {
|
||||
const settings = {
|
||||
tracklist_editor_columns: this.columns,
|
||||
// tracklist_editor_columns: this.columns,
|
||||
tracklist_editor_sep: ' -- ',
|
||||
}
|
||||
return {
|
||||
Page: Page,
|
||||
page: Page.Text,
|
||||
set: new Set(Model),
|
||||
extraData: {},
|
||||
settings,
|
||||
savedSettings: cloneDeep(settings),
|
||||
|
@ -189,6 +162,9 @@ export default {
|
|||
},
|
||||
|
||||
computed: {
|
||||
rows() { return this.$refs.formset && this.$refs.formset.rows },
|
||||
columns() { return this.rows && this.rows.columns_ || [] },
|
||||
|
||||
settingsChanged() {
|
||||
var k = Object.keys(this.savedSettings)
|
||||
.findIndex(k => !isEqual(this.settings[k], this.savedSettings[k]))
|
||||
|
@ -204,25 +180,9 @@ export default {
|
|||
get() { return this.settings.tracklist_editor_sep }
|
||||
},
|
||||
|
||||
allColumns: {
|
||||
set(value) {
|
||||
var cols = value.filter(x => x in this.defaultColumns)
|
||||
var left = this.defaultColumns.filter(x => !(x in cols))
|
||||
value = cols.concat(left)
|
||||
this.settings.tracklist_editor_columns = value
|
||||
},
|
||||
get() {
|
||||
return this.settings.tracklist_editor_columns
|
||||
}
|
||||
},
|
||||
|
||||
items() {
|
||||
return this.set.items
|
||||
},
|
||||
|
||||
rowsSlots() {
|
||||
return Object.keys(this.$slots)
|
||||
.filter(x => x.startsWith('row-') || x.startsWith('rows-'))
|
||||
.filter(x => x.startsWith('row-') || x.startsWith('rows-') || x.startsWith('control-'))
|
||||
.map(x => [x, x.startsWith('rows-') ? x.slice(5) : x])
|
||||
},
|
||||
},
|
||||
|
@ -236,34 +196,30 @@ export default {
|
|||
},
|
||||
|
||||
formatMove({from, to}) {
|
||||
const value = this.allColumns[from]
|
||||
this.settings.tracklist_editor_columns.splice(from, 1)
|
||||
this.settings.tracklist_editor_columns.splice(to, 0, value)
|
||||
if(this.page == Page.Text)
|
||||
this.updateList()
|
||||
else
|
||||
this.updateInput()
|
||||
this.$refs.formset.rows.columnMove({from, to})
|
||||
/*this.settings.tracklist_editor_columns.splice(from, 1)
|
||||
this.settings.tracklist_editor_columns.splice(to, 0, value)*/
|
||||
},
|
||||
|
||||
columnMove({from, to}) {
|
||||
const value = this.allColumns[from]
|
||||
this.allColumns.splice(from, 1)
|
||||
this.allColumns.splice(to, 0, value)
|
||||
this.updateInput()
|
||||
},
|
||||
|
||||
listItemMove({from, to, set}) {
|
||||
moveItem({from, to, set}) {
|
||||
set.move(from, to);
|
||||
this.updateInput()
|
||||
},
|
||||
|
||||
onColumnMove() {
|
||||
if(this.page == this.Page.List)
|
||||
this.updateInput()
|
||||
else
|
||||
this.updateList()
|
||||
},
|
||||
|
||||
updateList() {
|
||||
const items = this.toList(this.$refs.textarea.value)
|
||||
this.set.reset(items)
|
||||
this.$refs.formset.set.reset(items)
|
||||
},
|
||||
|
||||
updateInput() {
|
||||
const input = this.toText(this.items)
|
||||
const input = this.toText(this.$refs.formset.items)
|
||||
this.$refs.textarea.value = input
|
||||
},
|
||||
|
||||
|
@ -271,6 +227,7 @@ export default {
|
|||
* From input and separator, return list of items.
|
||||
*/
|
||||
toList(input) {
|
||||
const columns = this.$refs.formset.rows.columns_
|
||||
var lines = input.split('\n')
|
||||
var items = []
|
||||
|
||||
|
@ -281,11 +238,11 @@ export default {
|
|||
|
||||
var lineBits = line.split(this.separator)
|
||||
var item = {}
|
||||
for(var col in this.allColumns) {
|
||||
for(var col in columns) {
|
||||
if(col >= lineBits.length)
|
||||
break
|
||||
const attr = this.allColumns[col]
|
||||
item[attr] = lineBits[col].trim()
|
||||
const column = columns[col]
|
||||
item[column.name] = lineBits[col].trim()
|
||||
}
|
||||
item && items.push(item)
|
||||
}
|
||||
|
@ -296,14 +253,15 @@ export default {
|
|||
* From items and separator return a string
|
||||
*/
|
||||
toText(items) {
|
||||
const columns = this.$refs.formset.rows.columns_
|
||||
const sep = ` ${this.separator.trim()} `
|
||||
const lines = []
|
||||
for(let item of items) {
|
||||
if(!item)
|
||||
continue
|
||||
var line = []
|
||||
for(var col of this.allColumns)
|
||||
line.push(item.data[col] || '')
|
||||
for(var col of columns)
|
||||
line.push(item.data[col.name] || '')
|
||||
line = dropRightWhile(line, x => !x || !('' + x).trim())
|
||||
line = line.join(sep).trimRight()
|
||||
lines.push(line)
|
||||
|
@ -335,27 +293,17 @@ export default {
|
|||
/**
|
||||
* Load initial data
|
||||
*/
|
||||
loadData({items=[], settings=null}, reset=false) {
|
||||
if(reset) {
|
||||
this.set.items = []
|
||||
}
|
||||
for(var index in items)
|
||||
this.set.push(cloneDeep(items[index]))
|
||||
/*loadData({items=[], settings=null}, reset=false) {
|
||||
if(settings)
|
||||
this.settingsSaved(settings)
|
||||
this.updateInput()
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
initData(val) {
|
||||
this.loadData(val)
|
||||
},
|
||||
},*/
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.initData && this.loadData(this.initData)
|
||||
this.page = this.items.length ? Page.List : Page.Text
|
||||
//this.initData && this.initData.settings &&
|
||||
//this.initData && this.loadData(this.initData)
|
||||
this.page = this.initData.items.length ? Page.List : Page.Text
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -16,6 +16,8 @@ import AFileUpload from "./AFileUpload"
|
|||
import ASelectFile from "./ASelectFile"
|
||||
import AStatistics from './AStatistics'
|
||||
import AStreamer from './AStreamer'
|
||||
|
||||
import AFormSet from './AFormSet'
|
||||
import ATrackListEditor from './ATrackListEditor'
|
||||
import ASoundListEditor from './ASoundListEditor'
|
||||
|
||||
|
@ -37,5 +39,6 @@ export const admin = {
|
|||
|
||||
export const dashboard = {
|
||||
...base,
|
||||
AActionButton, AFileUpload, ASelectFile, AModal, ATrackListEditor, ASoundListEditor
|
||||
AActionButton, AFileUpload, ASelectFile, AModal,
|
||||
AFormSet, ATrackListEditor, ASoundListEditor
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user