Compare commits
51 Commits
Author | SHA1 | Date |
---|---|---|
Jason McBrayer | e6c5273a2f | |
Jason McBrayer | 012c0b74c1 | |
Jason McBrayer | adc65f8d5a | |
Jason McBrayer | d43d9cf6a6 | |
Jason McBrayer | fd5e4874c8 | |
GCU Prosthetic Conscience | b1a2c7f57d | |
GCU Prosthetic Conscience | 8c5c48fd19 | |
GCU Prosthetic Conscience | ab5b734dbb | |
GCU Prosthetic Conscience | a05d09289a | |
GCU Prosthetic Conscience | 9a99f34945 | |
GCU Prosthetic Conscience | 12f0fab3b2 | |
Jason McBrayer | b2d13e4520 | |
Jason McBrayer | b806d6c89d | |
Jason McBrayer | 0971997ad3 | |
Jason McBrayer | 391f31c76c | |
GCU Prosthetic Conscience | a536c35dfd | |
GCU Prosthetic Conscience | 62362d8ab6 | |
Jason McBrayer | 496d045356 | |
cyisfor | 22036aa22c | |
Jason McBrayer | ae7be271c9 | |
Jason McBrayer | 9a89664baf | |
Cy | 9b062437af | |
Cy | f471cf656f | |
Cy | 6d22b6fc66 | |
Cy | 9dd5e44e6f | |
Cy | 0b418f985d | |
Cy | dd19608b66 | |
Cy | 9415defede | |
Cy | c0caab4919 | |
Cy | 39f13d64fd | |
Cy | 0439440f40 | |
Cy | 0b93eb78f4 | |
Cy | 12d7b4cb7d | |
Cy | 2dfdb0b859 | |
Cy | c3f6503bec | |
Cy | bf0394ca36 | |
Cy | 17aae685ea | |
Jason McBrayer | 2354dadddb | |
GCU Prosthetic Conscience | 00e35409ef | |
Diana Thayer | aa8585bb24 | |
Jason McBrayer | 72c7bbcc3d | |
Jason McBrayer | 31c6d0b5f0 | |
Jason McBrayer | ecdaabff33 | |
Jason McBrayer | f6d0cfee61 | |
Jason McBrayer | 428c1e1508 | |
Jason McBrayer | fb2970af3b | |
Jason McBrayer | 2da4fd0de2 | |
Jason McBrayer | da1de5ea32 | |
Jason McBrayer | c836861027 | |
Jason McBrayer | eaf0cbd46b | |
GCU Prosthetic Conscience | 9744e6f647 |
|
@ -113,3 +113,4 @@ node_modules
|
|||
/TAGS
|
||||
.vscode
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# Brutaldon
|
||||
|
||||
Note: If you are seeing this on Github, this repo is a mirror that may not be up-to-date. Please go to https://git.carcosa.net/jmcbray/brutaldon for the latest code.
|
||||
|
||||
Brutaldon is a [brutalist][0], [Web 1.0][0.5] web interface for [Mastodon][1] and [Pleroma][p]. It is not a Mastodon-compatible social networking server; rather, it is just a client, like the Android or iOS client for Mastodon you may already be using, but it runs in a web server, and is accessed through a web browser. It works great in text-mode browsers such as [Lynx][2], [w3m][3], or [elinks][4], and also in more heavy-weight graphical browsers, such as Firefox. It works completely without JavaScript, but if JavaScript is available and enabled, it will be used to unobtrusively enhance the user experience.
|
||||
|
||||
[0]:http://brutalistwebsites.com/
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
from django.urls import reverse
|
||||
|
||||
|
||||
def bookmarklet_url(request):
|
||||
share_url = request.build_absolute_uri(reverse("share"))
|
||||
return {
|
||||
"bookmarklet_url": f"javascript:location.href='{share_url}?url='+encodeURIComponent(location.href)+';title='+encodeURIComponent(document.title)"
|
||||
}
|
|
@ -28,19 +28,7 @@ class OAuthLoginForm(forms.Form):
|
|||
class PreferencesForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Preference
|
||||
fields = [
|
||||
"theme",
|
||||
"filter_replies",
|
||||
"filter_boosts",
|
||||
"timezone",
|
||||
"no_javascript",
|
||||
"notifications",
|
||||
"click_to_load",
|
||||
"lightbox",
|
||||
"filter_notifications",
|
||||
"bundle_notifications",
|
||||
"poll_frequency",
|
||||
]
|
||||
fields = Preference._fields
|
||||
|
||||
|
||||
class PostForm(forms.Form):
|
||||
|
|
|
@ -5,14 +5,15 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('brutaldon', '0022_auto_20190506_0938'),
|
||||
]
|
||||
dependencies = [("brutaldon", "0022_auto_20190506_0938")]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='preference',
|
||||
name='bundle_notifications',
|
||||
field=models.BooleanField(default=False, help_text='Collapse together boosts or likes of the same toot in the notifications page.'),
|
||||
),
|
||||
model_name="preference",
|
||||
name="bundle_notifications",
|
||||
field=models.BooleanField(
|
||||
default=False,
|
||||
help_text="Collapse together boosts or likes of the same toot in the notifications page.",
|
||||
),
|
||||
)
|
||||
]
|
||||
|
|
|
@ -0,0 +1,474 @@
|
|||
# Generated by Django 3.0.6 on 2020-06-01 13:45
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("brutaldon", "0023_preference_bundle_notifications"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="preference",
|
||||
name="timezone",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
choices=[
|
||||
("Africa/Abidjan", "Africa/Abidjan"),
|
||||
("Africa/Accra", "Africa/Accra"),
|
||||
("Africa/Addis_Ababa", "Africa/Addis_Ababa"),
|
||||
("Africa/Algiers", "Africa/Algiers"),
|
||||
("Africa/Asmara", "Africa/Asmara"),
|
||||
("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/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/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/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/Cambridge_Bay", "America/Cambridge_Bay"),
|
||||
("America/Campo_Grande", "America/Campo_Grande"),
|
||||
("America/Cancun", "America/Cancun"),
|
||||
("America/Caracas", "America/Caracas"),
|
||||
("America/Cayenne", "America/Cayenne"),
|
||||
("America/Cayman", "America/Cayman"),
|
||||
("America/Chicago", "America/Chicago"),
|
||||
("America/Chihuahua", "America/Chihuahua"),
|
||||
("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/Fort_Nelson", "America/Fort_Nelson"),
|
||||
("America/Fortaleza", "America/Fortaleza"),
|
||||
("America/Glace_Bay", "America/Glace_Bay"),
|
||||
("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/Inuvik", "America/Inuvik"),
|
||||
("America/Iqaluit", "America/Iqaluit"),
|
||||
("America/Jamaica", "America/Jamaica"),
|
||||
("America/Juneau", "America/Juneau"),
|
||||
("America/Kentucky/Louisville", "America/Kentucky/Louisville"),
|
||||
("America/Kentucky/Monticello", "America/Kentucky/Monticello"),
|
||||
("America/Kralendijk", "America/Kralendijk"),
|
||||
("America/La_Paz", "America/La_Paz"),
|
||||
("America/Lima", "America/Lima"),
|
||||
("America/Los_Angeles", "America/Los_Angeles"),
|
||||
("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/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/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_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/Santarem", "America/Santarem"),
|
||||
("America/Santiago", "America/Santiago"),
|
||||
("America/Santo_Domingo", "America/Santo_Domingo"),
|
||||
("America/Sao_Paulo", "America/Sao_Paulo"),
|
||||
("America/Scoresbysund", "America/Scoresbysund"),
|
||||
("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/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/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/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/Chita", "Asia/Chita"),
|
||||
("Asia/Choibalsan", "Asia/Choibalsan"),
|
||||
("Asia/Colombo", "Asia/Colombo"),
|
||||
("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/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/Jakarta", "Asia/Jakarta"),
|
||||
("Asia/Jayapura", "Asia/Jayapura"),
|
||||
("Asia/Jerusalem", "Asia/Jerusalem"),
|
||||
("Asia/Kabul", "Asia/Kabul"),
|
||||
("Asia/Kamchatka", "Asia/Kamchatka"),
|
||||
("Asia/Karachi", "Asia/Karachi"),
|
||||
("Asia/Kathmandu", "Asia/Kathmandu"),
|
||||
("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/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/Riyadh", "Asia/Riyadh"),
|
||||
("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/Thimphu", "Asia/Thimphu"),
|
||||
("Asia/Tokyo", "Asia/Tokyo"),
|
||||
("Asia/Tomsk", "Asia/Tomsk"),
|
||||
("Asia/Ulaanbaatar", "Asia/Ulaanbaatar"),
|
||||
("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/Faroe", "Atlantic/Faroe"),
|
||||
("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/Adelaide", "Australia/Adelaide"),
|
||||
("Australia/Brisbane", "Australia/Brisbane"),
|
||||
("Australia/Broken_Hill", "Australia/Broken_Hill"),
|
||||
("Australia/Currie", "Australia/Currie"),
|
||||
("Australia/Darwin", "Australia/Darwin"),
|
||||
("Australia/Eucla", "Australia/Eucla"),
|
||||
("Australia/Hobart", "Australia/Hobart"),
|
||||
("Australia/Lindeman", "Australia/Lindeman"),
|
||||
("Australia/Lord_Howe", "Australia/Lord_Howe"),
|
||||
("Australia/Melbourne", "Australia/Melbourne"),
|
||||
("Australia/Perth", "Australia/Perth"),
|
||||
("Australia/Sydney", "Australia/Sydney"),
|
||||
("Canada/Atlantic", "Canada/Atlantic"),
|
||||
("Canada/Central", "Canada/Central"),
|
||||
("Canada/Eastern", "Canada/Eastern"),
|
||||
("Canada/Mountain", "Canada/Mountain"),
|
||||
("Canada/Newfoundland", "Canada/Newfoundland"),
|
||||
("Canada/Pacific", "Canada/Pacific"),
|
||||
("Europe/Amsterdam", "Europe/Amsterdam"),
|
||||
("Europe/Andorra", "Europe/Andorra"),
|
||||
("Europe/Astrakhan", "Europe/Astrakhan"),
|
||||
("Europe/Athens", "Europe/Athens"),
|
||||
("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/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/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/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"),
|
||||
("GMT", "GMT"),
|
||||
("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"),
|
||||
("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/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/Port_Moresby", "Pacific/Port_Moresby"),
|
||||
("Pacific/Rarotonga", "Pacific/Rarotonga"),
|
||||
("Pacific/Saipan", "Pacific/Saipan"),
|
||||
("Pacific/Tahiti", "Pacific/Tahiti"),
|
||||
("Pacific/Tarawa", "Pacific/Tarawa"),
|
||||
("Pacific/Tongatapu", "Pacific/Tongatapu"),
|
||||
("Pacific/Wake", "Pacific/Wake"),
|
||||
("Pacific/Wallis", "Pacific/Wallis"),
|
||||
("US/Alaska", "US/Alaska"),
|
||||
("US/Arizona", "US/Arizona"),
|
||||
("US/Central", "US/Central"),
|
||||
("US/Eastern", "US/Eastern"),
|
||||
("US/Hawaii", "US/Hawaii"),
|
||||
("US/Mountain", "US/Mountain"),
|
||||
("US/Pacific", "US/Pacific"),
|
||||
("UTC", "UTC"),
|
||||
],
|
||||
default="UTC",
|
||||
max_length=80,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,20 @@
|
|||
# Generated by Django 3.0.6 on 2020-06-01 14:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("brutaldon", "0024_auto_20200601_0945"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="preference",
|
||||
name="preview_sensitive",
|
||||
field=models.BooleanField(
|
||||
default=False, help_text='Show preview for media marked as "sensitive"'
|
||||
),
|
||||
),
|
||||
]
|
|
@ -30,6 +30,26 @@ class Theme(models.Model):
|
|||
return self.name
|
||||
|
||||
|
||||
from django.db.models.fields.related_descriptors import ForeignKeyDeferredAttribute
|
||||
|
||||
|
||||
def set_fields(klass):
|
||||
fields = []
|
||||
for n in dir(klass):
|
||||
assert n != "_fields"
|
||||
v = getattr(klass, n)
|
||||
if not hasattr(v, "field"):
|
||||
continue
|
||||
if not isinstance(v.field, models.Field):
|
||||
continue
|
||||
if isinstance(v, ForeignKeyDeferredAttribute):
|
||||
continue
|
||||
fields.append(n)
|
||||
setattr(klass, "_fields", fields)
|
||||
return klass
|
||||
|
||||
|
||||
@set_fields
|
||||
class Preference(models.Model):
|
||||
theme = models.ForeignKey(Theme, models.CASCADE, null=False, default=1)
|
||||
filter_replies = models.BooleanField(default=False)
|
||||
|
@ -37,6 +57,10 @@ class Preference(models.Model):
|
|||
timezone = models.CharField(
|
||||
max_length=80, blank=True, null=True, choices=timezones, default="UTC"
|
||||
)
|
||||
preview_sensitive = models.BooleanField(
|
||||
default=False, help_text=_('Show preview for media marked as "sensitive"')
|
||||
)
|
||||
|
||||
no_javascript = models.BooleanField(
|
||||
default=False,
|
||||
help_text=_(
|
||||
|
|
|
@ -67,6 +67,7 @@ TEMPLATES = [
|
|||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
"brutaldon.context_processors.bookmarklet_url",
|
||||
]
|
||||
},
|
||||
}
|
||||
|
@ -199,8 +200,16 @@ SESSION_SERIALIZER = "django.contrib.sessions.serializers.PickleSerializer"
|
|||
# URL to redirect users to when not logged in
|
||||
ANONYMOUS_HOME_URL = "about"
|
||||
|
||||
# URL to redirect gab users to
|
||||
GAB_RICKROLL_URL = "https://invidio.us/watch?v=dQw4w9WgXcQ"
|
||||
# URL to redirect galaxy brain users to
|
||||
RICKROLL_URL = "https://invidio.us/watch?v=dQw4w9WgXcQ"
|
||||
|
||||
# Function to check if trying to add an account should trigger a special response
|
||||
def CHECK_INSTANCE_URL(url, redirect):
|
||||
if "gab.com" in url:
|
||||
return redirect(RICKROLL_URL)
|
||||
elif "shitposter.club" in url:
|
||||
return redirect(RICKROLL_URL)
|
||||
|
||||
|
||||
# Version number displayed on about page
|
||||
BRUTALDON_VERSION = "2.14.0"
|
||||
BRUTALDON_VERSION = "2.15.0"
|
||||
|
|
|
@ -255,3 +255,14 @@ div.poll {
|
|||
{
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.input,
|
||||
.textarea {
|
||||
color: inherit;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
color: inherit;
|
||||
background-color: inherit;
|
||||
}
|
|
@ -72,7 +72,7 @@ function expandCWButtonPrepare()
|
|||
{
|
||||
var theButton = document.querySelector('#expandCWs');
|
||||
if (!theButton) {
|
||||
theButton = document.createElement('p');
|
||||
theButton = document.createElement('button');
|
||||
theButton.id = "expandCWs";
|
||||
theButton.textContent = "Expand CWs";
|
||||
theButton.classList.toggle('button');
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
define(["jquery"], function (a0) {
|
||||
return (root['Intercooler'] = factory(a0));
|
||||
});
|
||||
} else if (typeof exports === 'object') {
|
||||
} else if (typeof module === 'object' && module.exports) {
|
||||
// Node. Does not work with strict CommonJS, but
|
||||
// only CommonJS-like environments that support module.exports,
|
||||
// like Node.
|
||||
|
@ -24,7 +24,7 @@ var Intercooler = Intercooler || (function() {
|
|||
|
||||
// work around zepto build issue TODO - fix me
|
||||
if((typeof Zepto !== "undefined") && ($ == null)) {
|
||||
$ = Zepto
|
||||
window["$"] = Zepto
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
|
@ -68,7 +68,7 @@ var Intercooler = Intercooler || (function() {
|
|||
return false;
|
||||
}
|
||||
return adest.slice(0, asrc.length).join("/") == asrc.join("/") ||
|
||||
asrc.slice(0, adest.length).join("/") == adest.join("/");
|
||||
asrc.slice(0, adest.length).join("/") == adest.join("/");
|
||||
};
|
||||
|
||||
//============================================================
|
||||
|
@ -88,9 +88,10 @@ var Intercooler = Intercooler || (function() {
|
|||
}
|
||||
|
||||
function hideIndicator(elt) {
|
||||
if (elt.data('ic-use-transition')) {
|
||||
if (elt.data('ic-use-transition') || elt.data('ic-indicator-cleared')) {
|
||||
elt.data('ic-use-transition', null);
|
||||
elt.addClass('ic-use-transition');
|
||||
elt.data('ic-indicator-cleared', true);
|
||||
} else {
|
||||
elt.hide();
|
||||
}
|
||||
|
@ -259,6 +260,11 @@ var Intercooler = Intercooler || (function() {
|
|||
document.title = xhr.getResponseHeader("X-IC-Title");
|
||||
}
|
||||
|
||||
if (xhr.getResponseHeader("X-IC-Title-Encoded")) {
|
||||
var decodedTitle = decodeURIComponent((xhr.getResponseHeader("X-IC-Title-Encoded")).replace(/\+/g, '%20'));
|
||||
document.title = decodedTitle;
|
||||
}
|
||||
|
||||
if (xhr.getResponseHeader("X-IC-Refresh")) {
|
||||
var pathsToRefresh = xhr.getResponseHeader("X-IC-Refresh").split(",");
|
||||
log(elt, "X-IC-Refresh: refreshing " + pathsToRefresh, "DEBUG");
|
||||
|
@ -351,14 +357,19 @@ var Intercooler = Intercooler || (function() {
|
|||
|
||||
function beforeRequest(elt) {
|
||||
elt.addClass('disabled');
|
||||
elt.addClass('ic-request-in-flight');
|
||||
elt.data('ic-request-in-flight', true);
|
||||
}
|
||||
|
||||
function requestCleanup(indicator, elt) {
|
||||
function requestCleanup(indicator, globalIndicator, elt) {
|
||||
if (indicator.length > 0) {
|
||||
hideIndicator(indicator);
|
||||
}
|
||||
if (globalIndicator.length > 0) {
|
||||
hideIndicator(globalIndicator);
|
||||
}
|
||||
elt.removeClass('disabled');
|
||||
elt.removeClass('ic-request-in-flight');
|
||||
elt.data('ic-request-in-flight', false);
|
||||
if (elt.data('ic-next-request')) {
|
||||
elt.data('ic-next-request')["req"]();
|
||||
|
@ -404,19 +415,19 @@ var Intercooler = Intercooler || (function() {
|
|||
var names = [];
|
||||
var values = [];
|
||||
if (args) {
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
names.push(args[i][0]);
|
||||
values.push(args[i][1]);
|
||||
}
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
names.push(args[i][0]);
|
||||
values.push(args[i][1]);
|
||||
}
|
||||
}
|
||||
if (isIdentifier(script)) {
|
||||
return window[script].apply(this, values);
|
||||
return window[script].apply(this, values);
|
||||
} else {
|
||||
var outerfunc = window["eval"].call(
|
||||
window,
|
||||
'(function (' + names.join(", ") + ') {' + script + '})'
|
||||
);
|
||||
return outerfunc.apply(this, values);
|
||||
var outerfunc = window["eval"].call(
|
||||
window,
|
||||
'(function (' + names.join(", ") + ') {' + script + '})'
|
||||
);
|
||||
return outerfunc.apply(this, values);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -439,12 +450,35 @@ var Intercooler = Intercooler || (function() {
|
|||
return msg;
|
||||
}
|
||||
|
||||
function getLocalURL(baseURL, paramsToPush, data) {
|
||||
if (paramsToPush) {
|
||||
baseURL = baseURL + "?";
|
||||
var vars = {};
|
||||
data.replace(/([^=&]+)=([^&]*)/gi, function(m,key,value) {
|
||||
vars[key] = value;
|
||||
});
|
||||
$(paramsToPush.split(",")).each(function(index) {
|
||||
var param = $.trim(this);
|
||||
var value = vars[param] || "";
|
||||
baseURL += (index == 0) ? "" : "&";
|
||||
baseURL += param + "=" + value;
|
||||
});
|
||||
}
|
||||
return baseURL;
|
||||
}
|
||||
|
||||
function handleRemoteRequest(elt, type, url, data, success) {
|
||||
|
||||
beforeRequest(elt);
|
||||
|
||||
data = replaceOrAddMethod(data, type);
|
||||
|
||||
// Global spinner support
|
||||
var globalIndicator = findGlobalIndicator(elt);
|
||||
if (globalIndicator && globalIndicator.length > 0) {
|
||||
showIndicator(globalIndicator);
|
||||
}
|
||||
|
||||
// Spinner support
|
||||
var indicator = findIndicator(elt);
|
||||
if (indicator.length > 0) {
|
||||
|
@ -479,17 +513,18 @@ var Intercooler = Intercooler || (function() {
|
|||
}
|
||||
maybeInvokeLocalAction(elt, "-beforeSend");
|
||||
},
|
||||
success: function(data, textStatus, xhr) {
|
||||
triggerEvent(elt, "success.ic", [elt, data, textStatus, xhr, requestId]);
|
||||
success: function(responseData, textStatus, xhr) {
|
||||
triggerEvent(elt, "success.ic", [elt, responseData, textStatus, xhr, requestId]);
|
||||
log(elt, "AJAX request " + requestId + " was successful.", "DEBUG");
|
||||
var onSuccess = closestAttrValue(elt, 'ic-on-success');
|
||||
if (onSuccess) {
|
||||
if (globalEval(onSuccess, [["elt", elt], ["data", data], ["textStatus", textStatus], ["xhr", xhr]]) == false) {
|
||||
if (globalEval(onSuccess, [["elt", elt], ["data", responseData], ["textStatus", textStatus], ["xhr", xhr]]) == false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var beforeHeaders = new Date();
|
||||
var oldTitle = document.title;
|
||||
try {
|
||||
if (processHeaders(elt, xhr)) {
|
||||
log(elt, "Processed headers for request " + requestId + " in " + (new Date() - beforeHeaders) + "ms", "DEBUG");
|
||||
|
@ -497,10 +532,12 @@ var Intercooler = Intercooler || (function() {
|
|||
|
||||
if (xhr.getResponseHeader("X-IC-PushURL") || closestAttrValue(elt, 'ic-push-url') == "true") {
|
||||
try {
|
||||
requestCleanup(indicator, elt); // clean up before snap-shotting HTML
|
||||
var newUrl = xhr.getResponseHeader("X-IC-PushURL") || closestAttrValue(elt, 'ic-src');
|
||||
requestCleanup(indicator, globalIndicator, elt); // clean up before snap-shotting HTML
|
||||
var baseURL = closestAttrValue(elt, 'ic-src');
|
||||
var paramsToPush = closestAttrValue(elt, 'ic-push-params');
|
||||
var newUrl = xhr.getResponseHeader("X-IC-PushURL") || getLocalURL(baseURL, paramsToPush, data);
|
||||
if(_history) {
|
||||
_history.snapshotForHistory(newUrl);
|
||||
_history.snapshotForHistory(newUrl, oldTitle);
|
||||
} else {
|
||||
throw "History support not enabled";
|
||||
}
|
||||
|
@ -509,11 +546,11 @@ var Intercooler = Intercooler || (function() {
|
|||
}
|
||||
}
|
||||
|
||||
success(data, textStatus, elt, xhr);
|
||||
success(responseData, textStatus, elt, xhr);
|
||||
|
||||
log(elt, "Process content for request " + requestId + " in " + (new Date() - beforeSuccess) + "ms", "DEBUG");
|
||||
}
|
||||
triggerEvent(elt, "after.success.ic", [elt, data, textStatus, xhr, requestId]);
|
||||
triggerEvent(elt, "after.success.ic", [elt, responseData, textStatus, xhr, requestId]);
|
||||
maybeInvokeLocalAction(elt, "-success");
|
||||
} catch (e) {
|
||||
log(elt, "Error processing successful request " + requestId + " : " + formatError(e), "ERROR");
|
||||
|
@ -531,7 +568,7 @@ var Intercooler = Intercooler || (function() {
|
|||
},
|
||||
complete: function(xhr, status) {
|
||||
log(elt, "AJAX request " + requestId + " completed in " + (new Date() - requestStart) + "ms", "DEBUG");
|
||||
requestCleanup(indicator, elt);
|
||||
requestCleanup(indicator, globalIndicator, elt);
|
||||
try {
|
||||
if ($.contains(document, elt[0])) {
|
||||
triggerEvent(elt, "complete.ic", [elt, data, status, xhr, requestId]);
|
||||
|
@ -557,14 +594,24 @@ var Intercooler = Intercooler || (function() {
|
|||
triggerEvent($(document), "beforeAjaxSend.ic", [ajaxSetup, elt]);
|
||||
|
||||
if(ajaxSetup.cancel) {
|
||||
requestCleanup(indicator, elt);
|
||||
requestCleanup(indicator, globalIndicator, elt);
|
||||
} else {
|
||||
$.ajax(ajaxSetup)
|
||||
}
|
||||
}
|
||||
|
||||
function findGlobalIndicator(elt) {
|
||||
var indicator = $([]);
|
||||
elt = $(elt);
|
||||
var attr = closestAttrValue(elt, 'ic-global-indicator');
|
||||
if (attr && attr !== "false") {
|
||||
indicator = $(attr).first();
|
||||
}
|
||||
return indicator;
|
||||
}
|
||||
|
||||
function findIndicator(elt) {
|
||||
var indicator = null;
|
||||
var indicator = $([]);
|
||||
elt = $(elt);
|
||||
if (getICAttribute(elt, 'ic-indicator')) {
|
||||
indicator = $(getICAttribute(elt, 'ic-indicator')).first();
|
||||
|
@ -612,14 +659,14 @@ var Intercooler = Intercooler || (function() {
|
|||
return data;
|
||||
}
|
||||
|
||||
function appendData(data, string, value) {
|
||||
function appendData(data, key, value) {
|
||||
if ($.type(data) === "string") {
|
||||
if($.type(value) !== "string") {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
return data + "&" + string + "=" + encodeURIComponent(value);
|
||||
return data + "&" + key + "=" + encodeURIComponent(value);
|
||||
} else {
|
||||
data.append(string, value);
|
||||
data.append(key, value);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
@ -727,6 +774,7 @@ var Intercooler = Intercooler || (function() {
|
|||
});
|
||||
} else {
|
||||
processMacros(elt);
|
||||
processEnhancement(elt);
|
||||
processSources(elt);
|
||||
processPolling(elt);
|
||||
processEventSources(elt);
|
||||
|
@ -846,6 +894,18 @@ var Intercooler = Intercooler || (function() {
|
|||
}
|
||||
}
|
||||
|
||||
function processEnhancement(elt) {
|
||||
if (elt.closest('.ic-ignore').length == 0) {
|
||||
if(closestAttrValue(elt, 'ic-enhance') === 'true') {
|
||||
enhanceDomTree(elt);
|
||||
} else {
|
||||
elt.find(getICAttributeSelector('ic-enhance')).each(function(){
|
||||
enhanceDomTree($(this));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function processEventSources(elt) {
|
||||
if (elt.closest('.ic-ignore').length == 0) {
|
||||
handleEventSource(elt);
|
||||
|
@ -1024,14 +1084,15 @@ var Intercooler = Intercooler || (function() {
|
|||
elt = $(elt);
|
||||
if (getICAttribute(elt, 'ic-sse-src')) {
|
||||
var evtSrcUrl = getICAttribute(elt, 'ic-sse-src');
|
||||
var eventSource = initEventSource(elt, evtSrcUrl);
|
||||
var evtSrcWithCredentials = getICAttribute(elt, 'ic-sse-with-credentials') === 'true';
|
||||
var eventSource = initEventSource(elt, evtSrcUrl, evtSrcWithCredentials);
|
||||
elt.data('ic-event-sse-source', eventSource);
|
||||
elt.data('ic-event-sse-map', {});
|
||||
}
|
||||
}
|
||||
|
||||
function initEventSource(elt, evtSrcUrl) {
|
||||
var eventSource = Intercooler._internal.initEventSource(evtSrcUrl);
|
||||
function initEventSource(elt, evtSrcUrl, evtSrcWithCredentials) {
|
||||
var eventSource = Intercooler._internal.initEventSource(evtSrcUrl, evtSrcWithCredentials);
|
||||
eventSource.onmessage = function(e) {
|
||||
processICResponse(e.data, elt, false);
|
||||
};
|
||||
|
@ -1069,7 +1130,9 @@ var Intercooler = Intercooler || (function() {
|
|||
}
|
||||
|
||||
function handleTriggerOn(elt) {
|
||||
if (getICAttribute(elt, 'ic-trigger-on')) {
|
||||
|
||||
var triggerOnValue = getICAttribute(elt, 'ic-trigger-on');
|
||||
if (triggerOnValue) {
|
||||
// record button or submit input click info
|
||||
if(elt.is('form')) {
|
||||
elt.on('click focus', 'input, button, select, textarea', function(e){
|
||||
|
@ -1080,59 +1143,65 @@ var Intercooler = Intercooler || (function() {
|
|||
}
|
||||
});
|
||||
}
|
||||
if (getICAttribute(elt, 'ic-trigger-on') == 'load') {
|
||||
fireICRequest(elt);
|
||||
} else if (getICAttribute(elt, 'ic-trigger-on') == 'scrolled-into-view') {
|
||||
initScrollHandler();
|
||||
setTimeout(function() {
|
||||
triggerEvent($(window), 'scroll');
|
||||
}, 100); // Trigger a scroll in case element is already viewable
|
||||
} else {
|
||||
var triggerOn = getICAttribute(elt, 'ic-trigger-on').split(" ");
|
||||
if(triggerOn[0].indexOf("sse:") == 0) {
|
||||
//Server-sent event, find closest event source and register for it
|
||||
var sourceElt = elt.closest(getICAttributeSelector('ic-sse-src'));
|
||||
if(sourceElt) {
|
||||
registerSSE(sourceElt, triggerOn[0].substr(4))
|
||||
}
|
||||
var triggerOnArray = triggerOnValue.split(",");
|
||||
for (var i = 0; i < triggerOnArray.length; i++) {
|
||||
var triggerOn = $.trim(triggerOnArray[i]);
|
||||
var splitTriggerOn = triggerOn.split(" ");
|
||||
var eventString = eventFor(splitTriggerOn[0], $(elt));
|
||||
var eventModifier = splitTriggerOn[1];
|
||||
|
||||
if (triggerOn == 'load') {
|
||||
fireICRequest(elt);
|
||||
} else if (triggerOn == 'scrolled-into-view') {
|
||||
initScrollHandler();
|
||||
setTimeout(function() {
|
||||
triggerEvent($(window), 'scroll');
|
||||
}, 100); // Trigger a scroll in case element is already viewable
|
||||
} else {
|
||||
var triggerOn = getICAttribute($(elt), 'ic-trigger-on').split(" ");
|
||||
var event = eventFor(triggerOn[0], $(elt));
|
||||
$(getTriggeredElement(elt)).on(event, function(e) {
|
||||
var onBeforeTrigger = closestAttrValue(elt, 'ic-on-beforeTrigger');
|
||||
if (onBeforeTrigger) {
|
||||
if (globalEval(onBeforeTrigger, [["elt", elt], ["evt", e], ["elt", elt]]) == false) {
|
||||
log(elt, "ic-trigger cancelled by ic-on-beforeTrigger", "DEBUG");
|
||||
if(eventString.indexOf("sse:") == 0) {
|
||||
//Server-sent event, find closest event source and register for it
|
||||
var sourceElt = elt.closest(getICAttributeSelector('ic-sse-src'));
|
||||
if(sourceElt.length > 0) {
|
||||
registerSSE(sourceElt, splitTriggerOn[0].substr(4))
|
||||
}
|
||||
} else {
|
||||
$(getTriggeredElement(elt)).on(eventString, function(e) {
|
||||
var onBeforeTrigger = closestAttrValue(elt, 'ic-on-beforeTrigger');
|
||||
if (onBeforeTrigger) {
|
||||
if (globalEval(onBeforeTrigger, [["elt", elt], ["evt", e], ["elt", elt]]) == false) {
|
||||
log(elt, "ic-trigger cancelled by ic-on-beforeTrigger", "DEBUG");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (eventModifier == 'changed') {
|
||||
var currentVal = elt.val();
|
||||
var previousVal = elt.data('ic-previous-val');
|
||||
elt.data('ic-previous-val', currentVal);
|
||||
if (currentVal != previousVal) {
|
||||
fireICRequest(elt);
|
||||
}
|
||||
} else if (eventModifier == 'once') {
|
||||
var alreadyTriggered = elt.data('ic-already-triggered');
|
||||
elt.data('ic-already-triggered', true);
|
||||
if (alreadyTriggered !== true) {
|
||||
fireICRequest(elt);
|
||||
}
|
||||
} else {
|
||||
fireICRequest(elt);
|
||||
}
|
||||
if (preventDefault(elt, e)) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if(eventString && (eventString.indexOf("timeout:") == 0)) {
|
||||
var timeout = parseInterval(eventString.split(":")[1]);
|
||||
setTimeout(function () {
|
||||
$(getTriggeredElement(elt)).trigger(eventString);
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
if (triggerOn[1] == 'changed') {
|
||||
var currentVal = elt.val();
|
||||
var previousVal = elt.data('ic-previous-val');
|
||||
elt.data('ic-previous-val', currentVal);
|
||||
if (currentVal != previousVal) {
|
||||
fireICRequest(elt);
|
||||
}
|
||||
} else if (triggerOn[1] == 'once') {
|
||||
var alreadyTriggered = elt.data('ic-already-triggered');
|
||||
elt.data('ic-already-triggered', true);
|
||||
if (alreadyTriggered !== true) {
|
||||
fireICRequest(elt);
|
||||
}
|
||||
} else {
|
||||
fireICRequest(elt);
|
||||
}
|
||||
if (preventDefault(elt, e)) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if(event && (event.indexOf("timeout:") == 0)) {
|
||||
setTimeout(function () {
|
||||
$(getTriggeredElement(elt)).trigger(event);
|
||||
}, parseInterval(event.split(":")[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1178,6 +1247,7 @@ var Intercooler = Intercooler || (function() {
|
|||
setIfAbsent(elt, 'ic-trigger-on', 'default');
|
||||
setIfAbsent(elt, 'ic-deps', 'ignore');
|
||||
}
|
||||
|
||||
if (macroIs(macro, 'ic-action')) {
|
||||
setIfAbsent(elt, 'ic-trigger-on', 'default');
|
||||
}
|
||||
|
@ -1209,6 +1279,51 @@ var Intercooler = Intercooler || (function() {
|
|||
}
|
||||
}
|
||||
|
||||
function isLocalLink(anchor) {
|
||||
return location.hostname === anchor[0].hostname &&
|
||||
anchor.attr('href') &&
|
||||
!anchor.attr('href').startsWith("#")
|
||||
}
|
||||
|
||||
function enhanceAnchor(anchor) {
|
||||
if (closestAttrValue(anchor, 'ic-enhance') === "true") {
|
||||
if (isLocalLink(anchor)) {
|
||||
setIfAbsent(anchor, 'ic-src', anchor.attr('href'));
|
||||
setIfAbsent(anchor, 'ic-trigger-on', 'default');
|
||||
setIfAbsent(anchor, 'ic-deps', 'ignore');
|
||||
setIfAbsent(anchor, 'ic-push-url', 'true');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function determineFormVerb(form) {
|
||||
return form.find('input[name="_method"]').val() || form.attr('method') || form[0].method;
|
||||
}
|
||||
|
||||
function enhanceForm(form) {
|
||||
if (closestAttrValue(form, 'ic-enhance') === "true") {
|
||||
setIfAbsent(form, 'ic-src', form.attr('action'));
|
||||
setIfAbsent(form, 'ic-trigger-on', 'default');
|
||||
setIfAbsent(form, 'ic-deps', 'ignore');
|
||||
setIfAbsent(form, 'ic-verb', determineFormVerb(form));
|
||||
}
|
||||
}
|
||||
|
||||
function enhanceDomTree(elt) {
|
||||
if(elt.is('a')) {
|
||||
enhanceAnchor(elt);
|
||||
}
|
||||
elt.find('a').each(function(){
|
||||
enhanceAnchor($(this));
|
||||
});
|
||||
if(elt.is('form')){
|
||||
enhanceForm(elt);
|
||||
}
|
||||
elt.find('form').each(function(){
|
||||
enhanceForm($(this));
|
||||
});
|
||||
}
|
||||
|
||||
function setIfAbsent(elt, attr, value) {
|
||||
if (getICAttribute(elt, attr) == null) {
|
||||
setICAttribute(elt, attr, value);
|
||||
|
@ -1222,7 +1337,7 @@ var Intercooler = Intercooler || (function() {
|
|||
function isScrolledIntoView(elem) {
|
||||
elem = $(elem);
|
||||
if (elem.height() == 0 && elem.width() == 0) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
var docViewTop = $(window).scrollTop();
|
||||
var docViewBottom = docViewTop + $(window).height();
|
||||
|
@ -1231,13 +1346,13 @@ var Intercooler = Intercooler || (function() {
|
|||
var elemBottom = elemTop + elem.height();
|
||||
|
||||
return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom)
|
||||
&& (elemBottom <= docViewBottom) && (elemTop >= docViewTop));
|
||||
&& (elemBottom <= docViewBottom) && (elemTop >= docViewTop));
|
||||
}
|
||||
|
||||
function maybeScrollToTarget(elt, target) {
|
||||
if (closestAttrValue(elt, 'ic-scroll-to-target') != "false" &&
|
||||
(closestAttrValue(elt, 'ic-scroll-to-target') == 'true' ||
|
||||
closestAttrValue(target, 'ic-scroll-to-target') == 'true')) {
|
||||
(closestAttrValue(elt, 'ic-scroll-to-target') == 'true' ||
|
||||
closestAttrValue(target, 'ic-scroll-to-target') == 'true')) {
|
||||
var offset = -50; // -50 px default offset padding
|
||||
if (closestAttrValue(elt, 'ic-scroll-offset')) {
|
||||
offset = parseInt(closestAttrValue(elt, 'ic-scroll-offset'));
|
||||
|
@ -1352,6 +1467,19 @@ var Intercooler = Intercooler || (function() {
|
|||
if (forHistory != true) {
|
||||
maybeScrollToTarget(elt, target);
|
||||
}
|
||||
|
||||
var switchClass = elt.closest(getICAttributeSelector('ic-switch-class'));
|
||||
var classToSwitch = switchClass.attr(fixICAttributeName('ic-switch-class'));
|
||||
if(classToSwitch) {
|
||||
switchClass.children().removeClass(classToSwitch);
|
||||
switchClass.children().each(function(){
|
||||
if($.contains($(this)[0], $(elt)[0]) || $(this)[0] == $(elt)[0]) {
|
||||
$(this).addClass(classToSwitch);
|
||||
$(this).addClass(classToSwitch);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1449,7 +1577,7 @@ var Intercooler = Intercooler || (function() {
|
|||
}
|
||||
}
|
||||
|
||||
function fireICRequest(elt, alternateHandler) {
|
||||
function fireICRequest(elt, alternateHandler) {
|
||||
elt = $(elt);
|
||||
|
||||
var triggerOrigin = elt;
|
||||
|
@ -1628,9 +1756,9 @@ var Intercooler = Intercooler || (function() {
|
|||
/* Instance Methods */
|
||||
function historyConfigHasChanged(historySupportData) {
|
||||
return historySupportData == null ||
|
||||
historySupportData.slotLimit != slotLimit ||
|
||||
historySupportData.historyVersion != historyVersion ||
|
||||
historySupportData.lruList == null
|
||||
historySupportData.slotLimit != slotLimit ||
|
||||
historySupportData.historyVersion != historyVersion ||
|
||||
historySupportData.lruList == null
|
||||
}
|
||||
|
||||
function clearHistory() {
|
||||
|
@ -1687,18 +1815,19 @@ var Intercooler = Intercooler || (function() {
|
|||
storage.setItem(restorationData.id, content);
|
||||
} catch (e) {
|
||||
log(getTargetForHistory($('body')), "Unable to save intercooler history with entire history cleared, is something else eating " +
|
||||
"local storage? History Limit:" + slotLimit, "ERROR");
|
||||
"local storage? History Limit:" + slotLimit, "ERROR");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function makeHistoryEntry(html, yOffset, url) {
|
||||
function makeHistoryEntry(html, yOffset, url, title) {
|
||||
var restorationData = {
|
||||
"url": url,
|
||||
"id": HISTORY_SLOT_PREFIX + url,
|
||||
"content": html,
|
||||
"yOffset": yOffset,
|
||||
"timestamp": new Date().getTime()
|
||||
"timestamp": new Date().getTime(),
|
||||
"title": title
|
||||
};
|
||||
updateLRUList(url);
|
||||
// save to the history slot
|
||||
|
@ -1724,18 +1853,18 @@ var Intercooler = Intercooler || (function() {
|
|||
|
||||
function updateHistory() {
|
||||
if (_snapshot) {
|
||||
pushUrl(_snapshot.newUrl, currentUrl(), _snapshot.oldHtml, _snapshot.yOffset);
|
||||
pushUrl(_snapshot.newUrl, currentUrl(), _snapshot.oldHtml, _snapshot.yOffset, _snapshot.oldTitle);
|
||||
_snapshot = null;
|
||||
}
|
||||
}
|
||||
|
||||
function pushUrl(newUrl, originalUrl, originalHtml, yOffset) {
|
||||
function pushUrl(newUrl, originalUrl, originalHtml, yOffset, originalTitle) {
|
||||
|
||||
var historyEntry = makeHistoryEntry(originalHtml, yOffset, originalUrl);
|
||||
var historyEntry = makeHistoryEntry(originalHtml, yOffset, originalUrl, originalTitle);
|
||||
history.replaceState({"ic-id": historyEntry.id}, "", "");
|
||||
|
||||
var t = getTargetForHistory($('body'));
|
||||
var restorationData = makeHistoryEntry(t.html(), window.pageYOffset, newUrl);
|
||||
var restorationData = makeHistoryEntry(t.html(), window.pageYOffset, newUrl, document.title);
|
||||
history.pushState({'ic-id': restorationData.id}, "", newUrl);
|
||||
|
||||
triggerEvent(t, "pushUrl.ic", [t, restorationData]);
|
||||
|
@ -1748,7 +1877,12 @@ var Intercooler = Intercooler || (function() {
|
|||
if (historyData) {
|
||||
processICResponse(historyData["content"], getTargetForHistory($('body')), true);
|
||||
if (historyData["yOffset"]) {
|
||||
window.scrollTo(0, historyData["yOffset"])
|
||||
setTimeout(function () {
|
||||
window.scrollTo(0, historyData["yOffset"]);
|
||||
}, 100);
|
||||
}
|
||||
if (historyData["title"]) {
|
||||
document.title = historyData["title"];
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
|
@ -1771,13 +1905,14 @@ var Intercooler = Intercooler || (function() {
|
|||
}
|
||||
}
|
||||
|
||||
function snapshotForHistory(newUrl) {
|
||||
function snapshotForHistory(newUrl, oldTitle) {
|
||||
var t = getTargetForHistory($('body'));
|
||||
triggerEvent(t, "beforeHistorySnapshot.ic", [t]);
|
||||
_snapshot = {
|
||||
newUrl: newUrl,
|
||||
oldHtml: t.html(),
|
||||
yOffset: window.pageYOffset
|
||||
yOffset: window.pageYOffset,
|
||||
oldTitle: oldTitle
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1897,12 +2032,11 @@ var Intercooler = Intercooler || (function() {
|
|||
if($.zepto) {
|
||||
$('body').data('zeptoDataTest', {});
|
||||
if(typeof($('body').data('zeptoDataTest')) == "string") {
|
||||
console.log("!!!! Please include the data module with Zepto! Intercooler requires full data support to function !!!!")
|
||||
log(null,
|
||||
"!!!! Please include the data module with Zepto! Intercooler requires full data support to function !!!!",
|
||||
"ERROR")
|
||||
}
|
||||
}
|
||||
if (location.search && location.search.indexOf("ic-launch-debugger=true") >= 0) {
|
||||
Intercooler.debug();
|
||||
}
|
||||
}
|
||||
|
||||
$(function() {
|
||||
|
@ -1922,27 +2056,22 @@ var Intercooler = Intercooler || (function() {
|
|||
isDependent: isDependent,
|
||||
getTarget: getTarget,
|
||||
processHeaders: processHeaders,
|
||||
startPolling: startPolling,
|
||||
cancelPolling: cancelPolling,
|
||||
setIsDependentFunction: function(func) {
|
||||
_isDependentFunction = func;
|
||||
},
|
||||
ready: function(readyHandler) {
|
||||
_readyHandlers.push(readyHandler);
|
||||
},
|
||||
debug: function() {
|
||||
var debuggerUrl = closestAttrValue('body', 'ic-debugger-url') ||
|
||||
"https://intercoolerreleases-leaddynocom.netdna-ssl.com/intercooler-debugger.js";
|
||||
$.getScript(debuggerUrl)
|
||||
.fail(function(jqxhr, settings, exception) {
|
||||
log($('body'), formatError(exception), "ERROR");
|
||||
});
|
||||
},
|
||||
_internal: {
|
||||
init: init,
|
||||
replaceOrAddMethod: replaceOrAddMethod,
|
||||
initEventSource: function(url) {
|
||||
return new EventSource(url);
|
||||
initEventSource: function(url, withCredentials) {
|
||||
return new EventSource(url, {withCredentials: withCredentials});
|
||||
},
|
||||
globalEval: globalEval
|
||||
globalEval: globalEval,
|
||||
getLocalURL: getLocalURL
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -183,6 +183,7 @@
|
|||
<a class="level-item is-size-7" href="https://github.com/jfmcbrayer/brutaldon">
|
||||
Source
|
||||
</a>
|
||||
<span class="level-item is-size-7" >Bookmarklet: <a href="{{ bookmarklet_url }}">Share via brutaldon</a></span>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
{% if preferences.theme.is_brutalist %}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{% extends "base.html" %}
|
||||
{% load humanetime %}
|
||||
{% load taglinks %}
|
||||
{% load sanitizer %}
|
||||
|
||||
{% block title %}
|
||||
Brutaldon ({{ own_acct.username }}) - Notifications timelime
|
||||
|
@ -27,7 +28,7 @@ mastodon.notifications()[0]
|
|||
{% if group.0.type == 'favourite' %}
|
||||
<p>
|
||||
{% for account in group.accounts %}
|
||||
{% include "comma.html" %}{{ account.display_name }}
|
||||
{% include "comma.html" %}{{ account.display_name | fix_emojos:account.emojis |strip_html |safe }}
|
||||
(<a href="{{ account.url | localuser}}">{{ account.acct }}</a>)
|
||||
{% endfor %}
|
||||
favorited your toot.
|
||||
|
@ -37,7 +38,7 @@ mastodon.notifications()[0]
|
|||
{% elif group.0.type == 'reblog' %}
|
||||
<p>
|
||||
{% for account in group.accounts %}
|
||||
{% include "comma.html" %}{{ account.display_name }}
|
||||
{% include "comma.html" %}{{ account.display_name | fix_emojos:account.emojis |strip_html |safe }}
|
||||
(<a href="{{ account.url | localuser }}">{{ account.acct }}</a>)
|
||||
{% endfor %}
|
||||
boosted your toot.
|
||||
|
@ -49,7 +50,7 @@ mastodon.notifications()[0]
|
|||
{% for note in group %}
|
||||
{% if note.type == 'mention' %}
|
||||
<p>
|
||||
<strong>{{ note.account.display_name }}</strong>
|
||||
<strong>{{ note.account.display_name | fix_emojos:note.account.emojis |strip_html |safe }}</strong>
|
||||
(<a href="{{ note.account.url | localuser }}">{{ note.account.acct }}</a>)
|
||||
mentioned you.
|
||||
</p>
|
||||
|
@ -58,7 +59,7 @@ mastodon.notifications()[0]
|
|||
<hr class="is-hidden">
|
||||
{% elif note.type == 'reblog' %}
|
||||
<p>
|
||||
{{ note.account.display_name }}
|
||||
{{ note.account.display_name | fix_emojos:note.account.emojis |strip_html |safe }}
|
||||
(<a href="{{ note.account.url | localuser }}">{{ note.account.acct }}</a>)
|
||||
boosted your toot.
|
||||
(<span>
|
||||
|
@ -69,7 +70,7 @@ mastodon.notifications()[0]
|
|||
<hr class="is-hidden">
|
||||
{% elif note.type == 'favourite' %}
|
||||
<p>
|
||||
{{ note.account.display_name }}
|
||||
{{ note.account.display_name | fix_emojos:note.account.emojis |strip_html |safe }}
|
||||
(<a href="{{ note.account.url | localuser}}">{{ note.account.acct }}</a>)
|
||||
favorited your toot.
|
||||
(<span>
|
||||
|
@ -87,7 +88,7 @@ mastodon.notifications()[0]
|
|||
</figure>
|
||||
<div class="media-content" >
|
||||
<div class="content">
|
||||
<strong>{{ note.account.display_name }}</strong>
|
||||
<strong>{{ note.account.display_name | fix_emojos:note.account.emojis |strip_html |safe }}</strong>
|
||||
(<a href="{{ note.account.url |localuser }}">{{ note.account.acct }}</a>)
|
||||
followed you.
|
||||
(<a href="{{ note.url }}">
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
{% load taglinks %}
|
||||
{% load static %}
|
||||
|
||||
|
||||
{% if toot %}
|
||||
{% if active %}
|
||||
<article id="toot-{{toot.id}}" class="media box active-context">
|
||||
{% else %}
|
||||
|
@ -101,7 +101,7 @@
|
|||
<figure class="column attachment-image">
|
||||
<a href="{{ media.url }}">
|
||||
<noscript class="loading-lazy">
|
||||
{% if toot.sensitive %}
|
||||
{% if toot.sensitive and not preferences.preview_sensitive %}
|
||||
<img loading="lazy" src="{% static "images/sensitive.png" %}"
|
||||
{% else %}
|
||||
<img loading="lazy" src="{{ media.preview_url }}"
|
||||
|
@ -127,7 +127,7 @@
|
|||
<source src="{{ media.url }}" type="video/mp4">
|
||||
<a href="{{ media.url }}">
|
||||
<noscript class="loading-lazy">
|
||||
{% if toot.sensitive %}
|
||||
{% if toot.sensitive and not preferences.preview_sensitive %}
|
||||
<img loading="lazy" src="{% static "images/sensitive.png" %}"
|
||||
{% else %}
|
||||
<img loading="lazy" src="{{ media.preview_url }}"
|
||||
|
@ -233,3 +233,4 @@
|
|||
</div>
|
||||
<div class="media-right"></div>
|
||||
</article>
|
||||
{% endif %}
|
||||
|
|
|
@ -2,173 +2,197 @@
|
|||
{% load widget_tweaks %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h1 class="title">Settings</h1>
|
||||
<form method="post" action="{% url "settings" %}" >
|
||||
{% csrf_token %}
|
||||
<div class="container">
|
||||
<h1 class="title">Settings</h1>
|
||||
<form method="post" action="{% url "settings" %}" >
|
||||
{% csrf_token %}
|
||||
|
||||
<h2 class="subtitle">General Options</h2>
|
||||
<div class="field">
|
||||
<label class="label" for="id_theme">{{ form.theme.label }}</label>
|
||||
<div class="control has-icons-left">
|
||||
<div class="select">
|
||||
{% render_field form.theme class+="select" %}
|
||||
<span class="icon is-small is-left">
|
||||
<span class="fa fa-paint-brush"></span>
|
||||
</div>
|
||||
<h2 class="subtitle">General Options</h2>
|
||||
<div class="field">
|
||||
<label class="label" for="id_theme">{{ form.theme.label }}</label>
|
||||
<div class="control has-icons-left">
|
||||
<div class="select">
|
||||
{% render_field form.theme class+="select" %}
|
||||
<span class="icon is-small is-left">
|
||||
<span class="fa fa-paint-brush"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="id_timezone">{{ form.timezone.label }}</label>
|
||||
<div class="control has-icons-left">
|
||||
<div class="select">
|
||||
{% render_field form.timezone class+="select" %}
|
||||
<span class="icon is-small is-left">
|
||||
<span class="fa fa-clock-o"></span>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="id_timezone">{{ form.timezone.label }}</label>
|
||||
<div class="control has-icons-left">
|
||||
<div class="select">
|
||||
{% render_field form.timezone class+="select" %}
|
||||
<span class="icon is-small is-left">
|
||||
<span class="fa fa-clock-o"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="subtitle">Timeline Options</h2>
|
||||
<div class="field">
|
||||
<label class="label checkbox">
|
||||
{% render_field form.filter_replies %}
|
||||
{{ form.filter_replies.label }}
|
||||
<h2 class="subtitle">Content Options</h2>
|
||||
<div class="columns">
|
||||
<div class="column is-quarter">
|
||||
<label class="label checkbox" for="id_preview_sensitive">
|
||||
{% render_field form.preview_sensitive class+="checkbox" %}
|
||||
{{ form.preview_sensitive.label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label checkbox"">
|
||||
{% render_field form.filter_boosts %}
|
||||
{{ form.filter_boosts.label }}
|
||||
<div class="column is-quarter">
|
||||
<p class="notification is-info preferences-help">
|
||||
{{ form.preview_sensitive.help_text }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="column is-half">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="subtitle">Timeline Options</h2>
|
||||
<div class="field">
|
||||
<label class="label checkbox">
|
||||
{% render_field form.filter_replies %}
|
||||
{{ form.filter_replies.label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label checkbox"">
|
||||
{% render_field form.filter_boosts %}
|
||||
{{ form.filter_boosts.label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column is-quarter">
|
||||
<label class="label checkbox" for="id_filter_notifications">
|
||||
{% render_field form.filter_notifications class+="checkbox" %}
|
||||
{{ form.filter_notifications.label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="column is-quarter">
|
||||
<p class="notification is-info preferences-help">
|
||||
{{ form.filter_notifications.help_text }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="column is-half">
|
||||
|
||||
<h2 class="subtitle">JavaScript Options</h2>
|
||||
<div class="columns">
|
||||
<div class="column is-quarter">
|
||||
<label class="label checkbox" for="id_no_javascript">
|
||||
{% render_field form.no_javascript class+="checkbox" %}
|
||||
{{ form.no_javascript.label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="column is-quarter">
|
||||
<p class="notification is-info preferences-help">
|
||||
{{ form.no_javascript.help_text }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="column is-half">
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column is-quarter">
|
||||
<label class="label checkbox" for="id_bundle_notifications">
|
||||
{% render_field form.bundle_notifications class+="checkbox" %}
|
||||
{{ form.bundle_notifications.label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="column is-quarter">
|
||||
<p class="notification is-info preferences-help">
|
||||
{{ form.bundle_notifications.help_text }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="column is-half">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="subtitle">JavaScript Options</h2>
|
||||
<div class="columns">
|
||||
<div class="column is-quarter">
|
||||
<label class="label checkbox" for="id_no_javascript">
|
||||
{% render_field form.no_javascript class+="checkbox" %}
|
||||
{{ form.no_javascript.label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="column is-quarter">
|
||||
<p class="notification is-info preferences-help">
|
||||
{{ form.no_javascript.help_text }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="column is-half">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column is-quarter">
|
||||
<label class="label checkbox" for="id_notifications">
|
||||
{% render_field form.notifications class+="checkbox" %}
|
||||
{{ form.notifications.label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="column is-quarter">
|
||||
<p class="notification is-info preferences-help">
|
||||
{{ form.notifications.help_text }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="column is-half">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column is-quarter">
|
||||
<label class="label checkbox" for="id_click_to_load">
|
||||
{% render_field form.click_to_load class+="checkbox" %}
|
||||
{{ form.click_to_load.label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="column is-quarter">
|
||||
<p class="notification is-info preferences-help">
|
||||
{{ form.click_to_load.help_text }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="column is-half">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column is-quarter">
|
||||
<label class="label checkbox" for="id_lightbox">
|
||||
{% render_field form.lightbox class+="checkbox" %}
|
||||
{{ form.lightbox.label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="column is-quarter">
|
||||
<p class="notification is-info preferences-help">
|
||||
{{ form.lightbox.help_text }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="column is-half">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns">
|
||||
<div class="column is-quarter">
|
||||
<label class="label" for="id_poll_frequency">
|
||||
{{ form.poll_frequency.label }}
|
||||
</label>
|
||||
<div class="control">
|
||||
{% render_field form.poll_frequency class+="input" %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column is-quarter">
|
||||
<label class="label checkbox" for="id_notifications">
|
||||
{% render_field form.notifications class+="checkbox" %}
|
||||
{{ form.notifications.label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="column is-quarter">
|
||||
<p class="notification is-info preferences-help">
|
||||
{{ form.notifications.help_text }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="column is-half">
|
||||
|
||||
</div>
|
||||
<div class="column is-quarter">
|
||||
<p class="notification is-info preferences-help">
|
||||
{{ form.poll_frequency.help_text }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column is-quarter">
|
||||
<label class="label checkbox" for="id_click_to_load">
|
||||
{% render_field form.click_to_load class+="checkbox" %}
|
||||
{{ form.click_to_load.label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="column is-quarter">
|
||||
<p class="notification is-info preferences-help">
|
||||
{{ form.click_to_load.help_text }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="column is-half">
|
||||
<div class="column is-half">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column is-quarter">
|
||||
<label class="label checkbox" for="id_lightbox">
|
||||
{% render_field form.lightbox class+="checkbox" %}
|
||||
{{ form.lightbox.label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="column is-quarter">
|
||||
<p class="notification is-info preferences-help">
|
||||
{{ form.lightbox.help_text }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="column is-half">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column is-quarter">
|
||||
<label class="label checkbox" for="id_filter_notifications">
|
||||
{% render_field form.filter_notifications class+="checkbox" %}
|
||||
{{ form.filter_notifications.label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="column is-quarter">
|
||||
<p class="notification is-info preferences-help">
|
||||
{{ form.filter_notifications.help_text }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="column is-half">
|
||||
<div class="field">
|
||||
<input type="submit" name="submit"
|
||||
value="Save" class="button is-primary" >
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column is-quarter">
|
||||
<label class="label checkbox" for="id_bundle_notifications">
|
||||
{% render_field form.bundle_notifications class+="checkbox" %}
|
||||
{{ form.bundle_notifications.label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="column is-quarter">
|
||||
<p class="notification is-info preferences-help">
|
||||
{{ form.bundle_notifications.help_text }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="column is-half">
|
||||
<h2 class="subtitle">Bookmarklet</h2>
|
||||
<p>
|
||||
<a href="{{ bookmarklet_url }}">Share via brutaldon</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="subtitle">Filters and More</h2>
|
||||
<p><a href="{% url "list_filters" %}">List filters</a></p>
|
||||
<p><a href="{% url "follow_requests" %}">Follow requests</a></p>
|
||||
|
||||
<div class="columns">
|
||||
<div class="column is-quarter">
|
||||
<label class="label" for="id_poll_frequency">
|
||||
{{ form.poll_frequency.label }}
|
||||
</label>
|
||||
<div class="control">
|
||||
{% render_field form.poll_frequency class+="input" %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-quarter">
|
||||
<p class="notification is-info preferences-help">
|
||||
{{ form.poll_frequency.help_text }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="column is-half">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<input type="submit" name="submit"
|
||||
value="Save" class="button is-primary" >
|
||||
</div>
|
||||
</form>
|
||||
<h2 class="subtitle">Filters and More</h2>
|
||||
<p><a href="{% url "list_filters" %}">List filters</a></p>
|
||||
<p><a href="{% url "follow_requests" %}">Follow requests</a></p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -45,8 +45,13 @@ urlpatterns = [
|
|||
path("tags/<tag>", views.tag, name="tag"),
|
||||
path("user/", views.home, name="user_bad"),
|
||||
path("user/<username>", views.user, name="user"),
|
||||
path("user/<username>/next/<int:next>", views.user, name="user_next"),
|
||||
path("user/<username>/prev/<int:prev>", views.user, name="user_prev"),
|
||||
# next/prev are integers, but pleroma uses 128 bit integers
|
||||
# ...encoded in Base62.
|
||||
# aka a "flake_id"
|
||||
# from baseconv import base62, but we don't need to decode it
|
||||
# just pass it along back to pleroma but it is NOT an <int:>
|
||||
path("user/<username>/next/<next>", views.user, name="user_next"),
|
||||
path("user/<username>/prev/<prev>", views.user, name="user_prev"),
|
||||
path("toot/<mention>", views.toot, name="toot"),
|
||||
path("toot", views.toot, name="toot"),
|
||||
path("reply/<id>", views.reply, name="reply"),
|
||||
|
@ -69,5 +74,6 @@ urlpatterns = [
|
|||
path("accounts/", views.accounts, name="accounts"),
|
||||
path("accounts/<id>", views.accounts, name="accounts"),
|
||||
path("vote/<id>", views.vote, name="vote"),
|
||||
path("share/", views.share, name="share"),
|
||||
path("", views.home, name=""),
|
||||
]
|
||||
|
|
|
@ -4,7 +4,6 @@ from django.conf import settings as django_settings
|
|||
from django.shortcuts import render, redirect
|
||||
from django.urls import reverse
|
||||
from django.views.decorators.cache import never_cache, cache_page
|
||||
from django.urls import reverse
|
||||
from django.core.files.uploadhandler import TemporaryFileUploadHandler
|
||||
from django.utils.translation import gettext as _
|
||||
from brutaldon.forms import (
|
||||
|
@ -17,6 +16,7 @@ from brutaldon.forms import (
|
|||
from brutaldon.models import Client, Account, Preference, Theme
|
||||
from mastodon import (
|
||||
Mastodon,
|
||||
MastodonIllegalArgumentError,
|
||||
AttribAccessDict,
|
||||
MastodonError,
|
||||
MastodonAPIError,
|
||||
|
@ -33,20 +33,26 @@ import re
|
|||
|
||||
class NotLoggedInException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class LabeledList(list):
|
||||
"""A subclass of list that can accept additional attributes"""
|
||||
|
||||
def __new__(self, *args, **kwargs):
|
||||
return super(LabeledList, self).__new__(self, args, kwargs)
|
||||
|
||||
def __init(self, *args, **kwargs):
|
||||
if len(args) == 1 and hasattr(args[0], '__iter__'):
|
||||
if len(args) == 1 and hasattr(args[0], "__iter__"):
|
||||
list.__init__(self, args[0])
|
||||
else:
|
||||
list.__init__(self, args)
|
||||
self.__dict__.update(kwargs)
|
||||
|
||||
def __call(self, **kwargs):
|
||||
self.__dict__.update(kwargs)
|
||||
return self
|
||||
|
||||
|
||||
global sessons_cache
|
||||
sessions_cache = {}
|
||||
|
||||
|
@ -66,7 +72,7 @@ def get_session(domain):
|
|||
return s
|
||||
|
||||
|
||||
def get_usercontext(request):
|
||||
def get_usercontext(request, feature_set="mainline"):
|
||||
if is_logged_in(request):
|
||||
try:
|
||||
client = Client.objects.get(api_base_id=request.session["active_instance"])
|
||||
|
@ -85,6 +91,7 @@ def get_usercontext(request):
|
|||
api_base_url=client.api_base_id,
|
||||
session=get_session(client.api_base_id),
|
||||
ratelimit_method="throw",
|
||||
feature_set=feature_set,
|
||||
)
|
||||
return user, mastodon
|
||||
else:
|
||||
|
@ -128,7 +135,7 @@ def br_login_required(function=None, home_url=None, redirect_field_name=None):
|
|||
|
||||
def _dec(view_func):
|
||||
def _view(request, *args, **kwargs):
|
||||
if not is_logged_in(request):
|
||||
def not_logged_in():
|
||||
url = None
|
||||
if redirect_field_name and redirect_field_name in request.REQUEST:
|
||||
url = request.REQUEST[redirect_field_name]
|
||||
|
@ -137,8 +144,15 @@ def br_login_required(function=None, home_url=None, redirect_field_name=None):
|
|||
if not url:
|
||||
url = "/"
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
if not is_logged_in(request):
|
||||
return not_logged_in()
|
||||
else:
|
||||
return view_func(request, *args, **kwargs)
|
||||
try:
|
||||
return view_func(request, *args, **kwargs)
|
||||
except MastodonAPIError:
|
||||
# mastodon must have expired our session
|
||||
return not_logged_in()
|
||||
|
||||
_view.__name__ = view_func.__name__
|
||||
_view.__dict__ = view_func.__dict__
|
||||
|
@ -387,8 +401,9 @@ def login(request):
|
|||
redirect_uris = request.build_absolute_uri(reverse("oauth_callback"))
|
||||
if form.is_valid():
|
||||
api_base_url = form.cleaned_data["instance"]
|
||||
if "gab.com" in api_base_url:
|
||||
return redirect(django_settings.GAB_RICKROLL_URL)
|
||||
resp = django_settings.CHECK_INSTANCE_URL(api_base_url, redirect)
|
||||
if resp is not None:
|
||||
return resp
|
||||
tmp_base = parse.urlparse(api_base_url.lower())
|
||||
if tmp_base.netloc == "":
|
||||
api_base_url = parse.urlunparse(
|
||||
|
@ -600,9 +615,13 @@ def note(request, next=None, prev=None):
|
|||
account, mastodon = get_usercontext(request)
|
||||
except NotLoggedInException:
|
||||
return redirect(about)
|
||||
last_seen = mastodon.notifications(limit=1)[0]
|
||||
account.note_seen = last_seen.id
|
||||
account.save()
|
||||
try:
|
||||
last_seen = mastodon.notifications(limit=1)[0]
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
account.note_seen = last_seen.id
|
||||
account.save()
|
||||
|
||||
notes = mastodon.notifications(limit=40, max_id=next, min_id=prev)
|
||||
filters = get_filters(mastodon, context="notifications")
|
||||
|
@ -629,13 +648,22 @@ def note(request, next=None, prev=None):
|
|||
# Now group notes into lists based on type and status
|
||||
groups = []
|
||||
if account.preferences.bundle_notifications:
|
||||
|
||||
def bundle_key(note):
|
||||
return str(note.status.id) + note.type
|
||||
try:
|
||||
return str(note.status.id) + note.type
|
||||
except:
|
||||
return str(note.id) + note.type
|
||||
|
||||
def group_sort_key(group):
|
||||
return max([k.id for k in group])
|
||||
|
||||
sorted_notes = sorted(notes, key=bundle_key, reverse=True)
|
||||
for _, group in groupby(sorted_notes, bundle_key):
|
||||
group = LabeledList(group)
|
||||
group.accounts = [x.account for x in group]
|
||||
groups.append(group)
|
||||
groups.sort(key=group_sort_key, reverse=True)
|
||||
else:
|
||||
groups.append(notes)
|
||||
|
||||
|
@ -660,9 +688,9 @@ def note(request, next=None, prev=None):
|
|||
@br_login_required
|
||||
def thread(request, id):
|
||||
account, mastodon = get_usercontext(request)
|
||||
toot = mastodon.status(id)
|
||||
root = toot
|
||||
try:
|
||||
toot = mastodon.status(id)
|
||||
root = toot
|
||||
context = mastodon.status_context(id)
|
||||
if context.ancestors and len(context.ancestors) > 0:
|
||||
root = context.ancestors[0]
|
||||
|
@ -692,26 +720,43 @@ def thread(request, id):
|
|||
)
|
||||
|
||||
|
||||
def same_username(account, acct, username):
|
||||
if acct == username:
|
||||
return True
|
||||
try:
|
||||
user, host = username.split("@", 1)
|
||||
except ValueError:
|
||||
user, host = username, ""
|
||||
myhost = account.username.split("@", 1)[1]
|
||||
if acct == user and host == myhost:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@br_login_required
|
||||
def user(request, username, prev=None, next=None):
|
||||
try:
|
||||
account, mastodon = get_usercontext(request)
|
||||
except NotLoggedInException:
|
||||
return redirect(about)
|
||||
try:
|
||||
user_dict = [
|
||||
dict
|
||||
for dict in mastodon.account_search(username)
|
||||
if (
|
||||
(dict.acct == username)
|
||||
or (
|
||||
dict.acct == username.split("@")[0]
|
||||
and username.split("@")[1] == account.username.split("@")[1]
|
||||
)
|
||||
)
|
||||
][0]
|
||||
except (IndexError, AttributeError):
|
||||
raise Http404(_("The user %s could not be found.") % username)
|
||||
user_dict = None
|
||||
# pleroma currently flops if the user's not already locally known
|
||||
# this is a BUG that they MUST FIX
|
||||
# but until then, we might have to fallback to a regular search,
|
||||
# if the account search fails to return results.
|
||||
for dict in mastodon.account_search(username):
|
||||
if not same_username(account, dict.acct, username):
|
||||
continue
|
||||
user_dict = dict
|
||||
break
|
||||
else:
|
||||
for dict in mastodon.search(username, result_type="accounts").accounts:
|
||||
if not same_username(account, dict.acct, username):
|
||||
continue
|
||||
user_dict = dict
|
||||
break
|
||||
else:
|
||||
raise Http404(_("The user %s could not be found.") % username)
|
||||
data = mastodon.account_statuses(user_dict.id, max_id=next, min_id=prev)
|
||||
relationship = mastodon.account_relationships(user_dict.id)[0]
|
||||
notifications = _notes_count(account, mastodon)
|
||||
|
@ -754,21 +799,9 @@ def settings(request):
|
|||
if request.method == "POST":
|
||||
form = PreferencesForm(request.POST)
|
||||
if form.is_valid():
|
||||
account.preferences.theme = form.cleaned_data["theme"]
|
||||
account.preferences.filter_replies = form.cleaned_data["filter_replies"]
|
||||
account.preferences.filter_boosts = form.cleaned_data["filter_boosts"]
|
||||
account.preferences.timezone = form.cleaned_data["timezone"]
|
||||
account.preferences.no_javascript = form.cleaned_data["no_javascript"]
|
||||
account.preferences.notifications = form.cleaned_data["notifications"]
|
||||
account.preferences.click_to_load = form.cleaned_data["click_to_load"]
|
||||
account.preferences.lightbox = form.cleaned_data["lightbox"]
|
||||
account.preferences.filter_notifications = form.cleaned_data[
|
||||
"filter_notifications"
|
||||
]
|
||||
account.preferences.bundle_notifications = form.cleaned_data[
|
||||
"bundle_notifications"
|
||||
]
|
||||
account.preferences.poll_frequency = form.cleaned_data["poll_frequency"]
|
||||
for field in account.preferences._fields:
|
||||
if field in form.cleaned_data:
|
||||
setattr(account.preferences, field, form.cleaned_data[field])
|
||||
request.session["timezone"] = account.preferences.timezone
|
||||
account.preferences.save()
|
||||
account.save()
|
||||
|
@ -796,6 +829,28 @@ def settings(request):
|
|||
)
|
||||
|
||||
|
||||
def status_post(account, request, mastodon, **kw):
|
||||
while True:
|
||||
try:
|
||||
mastodon.status_post(**kw)
|
||||
except MastodonIllegalArgumentError as e:
|
||||
if not "is only available with feature set" in e.args[0]:
|
||||
raise
|
||||
feature_set = e.args[0].rsplit(" ", 1)[-1]
|
||||
|
||||
account, mastodon = get_usercontext(request, feature_set=feature_set)
|
||||
|
||||
continue
|
||||
except TypeError:
|
||||
# not sure why, but the old code retried status_post without a
|
||||
# content_type keyword, if there was a TypeError
|
||||
kw.pop("content_type")
|
||||
continue
|
||||
else:
|
||||
break
|
||||
return account, mastodon
|
||||
|
||||
|
||||
@never_cache
|
||||
@br_login_required
|
||||
def toot(request, mention=None):
|
||||
|
@ -851,26 +906,22 @@ def toot(request, mention=None):
|
|||
),
|
||||
)
|
||||
)
|
||||
|
||||
if form.cleaned_data["visibility"] == "":
|
||||
form.cleaned_data["visibility"] = request.session[
|
||||
"active_user"
|
||||
].source.privacy
|
||||
try:
|
||||
try:
|
||||
mastodon.status_post(
|
||||
status=form.cleaned_data["status"],
|
||||
visibility=form.cleaned_data["visibility"],
|
||||
spoiler_text=form.cleaned_data["spoiler_text"],
|
||||
media_ids=media_objects,
|
||||
content_type="text/markdown",
|
||||
)
|
||||
except TypeError:
|
||||
mastodon.status_post(
|
||||
status=form.cleaned_data["status"],
|
||||
visibility=form.cleaned_data["visibility"],
|
||||
spoiler_text=form.cleaned_data["spoiler_text"],
|
||||
media_ids=media_objects,
|
||||
)
|
||||
status_post(
|
||||
account,
|
||||
request,
|
||||
mastodon,
|
||||
status=form.cleaned_data["status"],
|
||||
visibility=form.cleaned_data["visibility"],
|
||||
spoiler_text=form.cleaned_data["spoiler_text"],
|
||||
media_ids=media_objects,
|
||||
content_type="text/markdown",
|
||||
)
|
||||
except MastodonAPIError as error:
|
||||
form.add_error(
|
||||
"",
|
||||
|
@ -964,23 +1015,17 @@ def redraft(request, id):
|
|||
"active_user"
|
||||
].source.privacy
|
||||
try:
|
||||
try:
|
||||
mastodon.status_post(
|
||||
status=form.cleaned_data["status"],
|
||||
visibility=form.cleaned_data["visibility"],
|
||||
spoiler_text=form.cleaned_data["spoiler_text"],
|
||||
media_ids=media_objects,
|
||||
in_reply_to_id=toot.in_reply_to_id,
|
||||
content_type="text/markdown",
|
||||
)
|
||||
except TypeError:
|
||||
mastodon.status_post(
|
||||
status=form.cleaned_data["status"],
|
||||
visibility=form.cleaned_data["visibility"],
|
||||
spoiler_text=form.cleaned_data["spoiler_text"],
|
||||
media_ids=media_objects,
|
||||
in_reply_to_id=toot.in_reply_to_id,
|
||||
)
|
||||
status_post(
|
||||
account,
|
||||
request,
|
||||
mastodon,
|
||||
status=form.cleaned_data["status"],
|
||||
visibility=form.cleaned_data["visibility"],
|
||||
spoiler_text=form.cleaned_data["spoiler_text"],
|
||||
media_ids=media_objects,
|
||||
in_reply_to_id=toot.in_reply_to_id,
|
||||
content_type="text/markdown",
|
||||
)
|
||||
mastodon.status_delete(id)
|
||||
except MastodonAPIError as error:
|
||||
form.add_error(
|
||||
|
@ -1036,8 +1081,11 @@ def safe_get_attachment(toot, index):
|
|||
def reply(request, id):
|
||||
if request.method == "GET":
|
||||
account, mastodon = get_usercontext(request)
|
||||
toot = mastodon.status(id)
|
||||
context = mastodon.status_context(id)
|
||||
try:
|
||||
toot = mastodon.status(id)
|
||||
context = mastodon.status_context(id)
|
||||
except MastodonNotFoundError:
|
||||
raise Http404(_("Thread not found; the message may have been deleted."))
|
||||
notifications = _notes_count(account, mastodon)
|
||||
if toot.account.acct != request.session["active_user"].acct:
|
||||
initial_text = "@" + toot.account.acct + " "
|
||||
|
@ -1094,23 +1142,17 @@ def reply(request, id):
|
|||
)
|
||||
)
|
||||
try:
|
||||
try:
|
||||
mastodon.status_post(
|
||||
status=form.cleaned_data["status"],
|
||||
visibility=form.cleaned_data["visibility"],
|
||||
spoiler_text=form.cleaned_data["spoiler_text"],
|
||||
media_ids=media_objects,
|
||||
in_reply_to_id=id,
|
||||
content_type="text/markdown",
|
||||
)
|
||||
except TypeError:
|
||||
mastodon.status_post(
|
||||
status=form.cleaned_data["status"],
|
||||
visibility=form.cleaned_data["visibility"],
|
||||
spoiler_text=form.cleaned_data["spoiler_text"],
|
||||
media_ids=media_objects,
|
||||
in_reply_to_id=id,
|
||||
)
|
||||
status_post(
|
||||
account,
|
||||
request,
|
||||
mastodon,
|
||||
status=form.cleaned_data["status"],
|
||||
visibility=form.cleaned_data["visibility"],
|
||||
spoiler_text=form.cleaned_data["spoiler_text"],
|
||||
media_ids=media_objects,
|
||||
in_reply_to_id=id,
|
||||
content_type="text/markdown",
|
||||
)
|
||||
except MastodonAPIError as error:
|
||||
form.add_error(
|
||||
"",
|
||||
|
@ -1154,6 +1196,37 @@ def reply(request, id):
|
|||
return HttpResponseRedirect(reverse("reply", args=[id]) + "#toot-" + str(id))
|
||||
|
||||
|
||||
@br_login_required
|
||||
def share(request):
|
||||
account, mastodon = get_usercontext(request)
|
||||
if request.method == "GET":
|
||||
params = request.GET
|
||||
if request.method == "POST":
|
||||
params = request.POST
|
||||
title = params.get("title")
|
||||
url = params.get("url")
|
||||
if title:
|
||||
initial_text = f"{title}\n\n{url}"
|
||||
else:
|
||||
initial_text = f"{url}"
|
||||
|
||||
form = PostForm(
|
||||
initial={
|
||||
"status": initial_text,
|
||||
"visibility": request.session["active_user"].source.privacy,
|
||||
}
|
||||
)
|
||||
return render(
|
||||
request,
|
||||
"main/post.html",
|
||||
{
|
||||
"form": form,
|
||||
"own_acct": request.session["active_user"],
|
||||
"preferences": account.preferences,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@never_cache
|
||||
@br_login_required
|
||||
def fav(request, id):
|
||||
|
@ -1413,6 +1486,7 @@ def search_results(request):
|
|||
else:
|
||||
query = ""
|
||||
account, mastodon = get_usercontext(request)
|
||||
|
||||
results = mastodon.search(query)
|
||||
notifications = _notes_count(account, mastodon)
|
||||
return render(
|
||||
|
|
Loading…
Reference in New Issue