From a8baa3ce54074ad8e1b8f9ec3721bb2891638228 Mon Sep 17 00:00:00 2001 From: Corban-Lee Jones Date: Wed, 14 Aug 2024 21:56:19 +0100 Subject: [PATCH 01/18] work on guild settings model --- apps/home/admin.py | 10 +- ...21_alter_guildsettings_options_and_more.py | 122 ++++++++++++++++++ apps/home/models.py | 28 +++- 3 files changed, 151 insertions(+), 9 deletions(-) create mode 100644 apps/home/migrations/0021_alter_guildsettings_options_and_more.py diff --git a/apps/home/admin.py b/apps/home/admin.py index 2b498a2..f6855fc 100644 --- a/apps/home/admin.py +++ b/apps/home/admin.py @@ -2,7 +2,7 @@ from django.contrib import admin -from .models import Subscription, SavedGuilds, Filter, SubChannel, TrackedContent, ArticleMutator +from .models import Subscription, SavedGuilds, Filter, SubChannel, TrackedContent, ArticleMutator, GuildSettings @admin.register(Subscription) @@ -44,4 +44,10 @@ class SavedGuildAdmin(admin.ModelAdmin): class ArticleMutatorAdmin(admin.ModelAdmin): list_display = [ "id", "name", "value" - ] \ No newline at end of file + ] + +@admin.register(GuildSettings) +class GuildSettingsAdmin(admin.ModelAdmin): + list_display = [ + "id", "guild_id", "default_embed_colour", "active" + ] diff --git a/apps/home/migrations/0021_alter_guildsettings_options_and_more.py b/apps/home/migrations/0021_alter_guildsettings_options_and_more.py new file mode 100644 index 0000000..1ff9025 --- /dev/null +++ b/apps/home/migrations/0021_alter_guildsettings_options_and_more.py @@ -0,0 +1,122 @@ +# Generated by Django 5.0.4 on 2024-08-14 20:41 + +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0020_guildsettings'), + ] + + operations = [ + migrations.AlterModelOptions( + name='guildsettings', + options={'get_latest_by': 'id', 'verbose_name': 'guild settings', 'verbose_name_plural': 'guild settings'}, + ), + migrations.RemoveField( + model_name='savedguilds', + name='default_embed_colour', + ), + migrations.AddField( + model_name='guildsettings', + name='active', + field=models.BooleanField(default=True, help_text='Subscriptions of inactive guilds will also be treated as inactive', verbose_name='Active'), + ), + migrations.AlterField( + model_name='guildsettings', + name='guild_id', + field=models.CharField(help_text='Discord snowflake ID for the represented guild.', max_length=128, unique=True, verbose_name='guild id'), + ), + migrations.AlterField( + model_name='savedguilds', + name='id', + field=models.AutoField(primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='subscription', + name='active', + field=models.BooleanField(default=True, verbose_name='Active'), + ), + migrations.AlterField( + model_name='subscription', + name='article_fetch_image', + field=models.BooleanField(default=True, help_text='Will the resulting article have an image?', verbose_name='Fetch Article Images'), + ), + migrations.AlterField( + model_name='subscription', + name='creation_datetime', + field=models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Created At'), + ), + migrations.AlterField( + model_name='subscription', + name='embed_colour', + field=models.CharField(blank=True, default='3498db', max_length=6, verbose_name='Embed Colour'), + ), + migrations.AlterField( + model_name='subscription', + name='extra_notes', + field=models.CharField(blank=True, max_length=250, null=True, verbose_name='Extra Notes'), + ), + migrations.AlterField( + model_name='subscription', + name='guild_id', + field=models.CharField(max_length=128, verbose_name='Guild ID'), + ), + migrations.AlterField( + model_name='subscription', + name='name', + field=models.CharField(max_length=32, verbose_name='Name'), + ), + migrations.AlterField( + model_name='subscription', + name='published_threshold', + field=models.DateTimeField(blank=True, default=django.utils.timezone.now, verbose_name='Published Threshold'), + ), + migrations.AlterField( + model_name='subscription', + name='url', + field=models.URLField(verbose_name='URL'), + ), + migrations.AlterField( + model_name='trackedcontent', + name='blocked', + field=models.BooleanField(default=False, verbose_name='Blocked'), + ), + migrations.AlterField( + model_name='trackedcontent', + name='channel_id', + field=models.CharField(max_length=128, verbose_name='Channel ID'), + ), + migrations.AlterField( + model_name='trackedcontent', + name='creation_datetime', + field=models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Created At'), + ), + migrations.AlterField( + model_name='trackedcontent', + name='guid', + field=models.CharField(help_text='RSS provided GUID of the content', max_length=256, verbose_name='GUID'), + ), + migrations.AlterField( + model_name='trackedcontent', + name='id', + field=models.AutoField(primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='trackedcontent', + name='message_id', + field=models.CharField(max_length=128, verbose_name='Message ID'), + ), + migrations.AlterField( + model_name='trackedcontent', + name='title', + field=models.CharField(max_length=728, verbose_name='Title'), + ), + migrations.AlterField( + model_name='trackedcontent', + name='url', + field=models.URLField(verbose_name='URL'), + ), + ] diff --git a/apps/home/models.py b/apps/home/models.py index 9d5138b..6c0fe2e 100644 --- a/apps/home/models.py +++ b/apps/home/models.py @@ -23,7 +23,8 @@ class GuildSettings(models.Model): guild_id = models.CharField( verbose_name=_("guild id"), max_length=128, - help_text=_("Discord snowflake ID for the represented guild.") + help_text=_("Discord snowflake ID for the represented guild."), + unique=True ) default_embed_colour = models.CharField( @@ -33,6 +34,21 @@ class GuildSettings(models.Model): blank=True ) + active = models.BooleanField( + verbose_name=_("Active"), + default=True, + help_text=_("Subscriptions of inactive guilds will also be treated as inactive") + ) + + class Meta: + """ + Metadata for the GuildSettings model. + """ + + verbose_name = "guild settings" + verbose_name_plural = "guild settings" + get_latest_by = "id" + class SavedGuilds(models.Model): """ @@ -79,12 +95,6 @@ class SavedGuilds(models.Model): help_text=_("Does the 'added by' user own this guild?") ) - default_embed_colour = models.CharField( - max_length=6, - default="3498db", - blank=True - ) - class Meta: """ Metadata for the SavedGuilds Model. @@ -109,6 +119,10 @@ class SavedGuilds(models.Model): def settings(self): return GuildSettings.objects.get(guild_id=self.guild_id) + def save(self, *args, **kwargs): + GuildSettings.objects.get_or_create(guild_id=self.guild_id) + super().save(*args, **kwargs) + class SubChannel(models.Model): """ -- 2.47.2 From 90a4a648fb0a16602452f0e3a24624be858bdfc6 Mon Sep 17 00:00:00 2001 From: Corban-Lee Jones Date: Wed, 14 Aug 2024 21:56:38 +0100 Subject: [PATCH 02/18] create guild settings for existing saved guild --- .../0022_populate_guild_settings.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 apps/home/migrations/0022_populate_guild_settings.py diff --git a/apps/home/migrations/0022_populate_guild_settings.py b/apps/home/migrations/0022_populate_guild_settings.py new file mode 100644 index 0000000..e00e7e4 --- /dev/null +++ b/apps/home/migrations/0022_populate_guild_settings.py @@ -0,0 +1,22 @@ +# Generated by Django 5.0.4 on 2024-08-14 20:46 + +from django.db import migrations + + +def create_missing_guild_settings(apps, scheme_editor): + SavedGuilds = apps.get_model("home", "SavedGuilds") + GuildSettings = apps.get_model("home", "GuildSettings") + + for saved_guild in SavedGuilds.objects.all(): + GuildSettings.objects.get_or_create(guild_id=saved_guild.guild_id) + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0021_alter_guildsettings_options_and_more'), + ] + + operations = [ + migrations.RunPython(create_missing_guild_settings), + ] -- 2.47.2 From 5711c3bef9c77b47ea4b91ed226b1d18a8f91725 Mon Sep 17 00:00:00 2001 From: Corban-Lee Jones Date: Wed, 14 Aug 2024 22:43:42 +0100 Subject: [PATCH 03/18] settings modal --- apps/static/js/home/settings.js | 11 +++++++++- .../home/includes/settingsmodal.html | 20 +++++++++++++++++++ apps/templates/home/index.html | 12 +++++++---- 3 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 apps/templates/home/includes/settingsmodal.html diff --git a/apps/static/js/home/settings.js b/apps/static/js/home/settings.js index 64b95ac..7503987 100644 --- a/apps/static/js/home/settings.js +++ b/apps/static/js/home/settings.js @@ -14,7 +14,16 @@ $(document).on("selectedServerChange", async function() { updateColourInput("defaultEmbedColour", "#" + server.default_embed_colour); }); +$("#serverSettingsBtn").on("click", async function() { + await showServerSettingsModal(); +}); + +async function showServerSettingsModal() { + const server = getCurrentlyActiveServer(); + $("#serverSettingsModal").modal("show"); +} + $("#serverSettingsForm").on("submit", function(e) { e.preventDefault(); alert("not implemented"); -}) \ No newline at end of file +}) diff --git a/apps/templates/home/includes/settingsmodal.html b/apps/templates/home/includes/settingsmodal.html new file mode 100644 index 0000000..b123974 --- /dev/null +++ b/apps/templates/home/includes/settingsmodal.html @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/apps/templates/home/index.html b/apps/templates/home/index.html index 7c6cc10..acc7eac 100644 --- a/apps/templates/home/index.html +++ b/apps/templates/home/index.html @@ -49,7 +49,10 @@
- + -
@@ -93,7 +96,7 @@
-
{% include "home/includes/settingstab.html" %}
+ @@ -104,6 +107,7 @@ {% include "home/includes/submodal.html" %} {% include "home/includes/filtermodal.html" %} {% include "home/includes/deletemodal.html" %} +{% include "home/includes/settingsmodal.html" %} {% endblock content %} {% block javascript %} -- 2.47.2 From 21bddbb300f9f142ea58619131e989f6341aa736 Mon Sep 17 00:00:00 2001 From: Corban-Lee Jones Date: Wed, 14 Aug 2024 22:43:45 +0100 Subject: [PATCH 04/18] Update CHANGELOG.md --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9115d01..3e18f22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ -**unreleased** +**unreleased breaking changes v.0.3.0** +- Enhancement: store guild settings in separate model +- Broken: server `default_embed_colour` setting will be reset due to changes on how settings are stored +- Enhancement: moved server settinsg from tab to pop-out modal +- Enhancement: `active` flag added as server setting, soft-toggles activeness of associated subscriptions +- **v0.2.2** - Enhancement: added open graph meta tags -- 2.47.2 From 90b3dde68d26fcc1ccb54aba15b790c9a458e376 Mon Sep 17 00:00:00 2001 From: Corban-Lee Jones Date: Fri, 16 Aug 2024 18:44:52 +0100 Subject: [PATCH 05/18] saved guilds api - active flag - allow PUT method - typo fix - remove settings from savedguild serialiser --- apps/api/serializers.py | 4 ++-- apps/api/views.py | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/api/serializers.py b/apps/api/serializers.py index 473ee94..e827bd2 100644 --- a/apps/api/serializers.py +++ b/apps/api/serializers.py @@ -173,7 +173,7 @@ class SavedGuildSerializer(DynamicModelSerializer): class Meta: model = SavedGuilds - fields = ("id", "guild_id", "name", "icon", "added_by", "permissions", "default_embed_colour", "owner") + fields = ("id", "guild_id", "name", "icon", "added_by", "permissions", "owner") class GuildSettingsSerializer(DynamicModelSerializer): @@ -183,7 +183,7 @@ class GuildSettingsSerializer(DynamicModelSerializer): class Meta: model = GuildSettings - fields = ("id", "guild_id", "default_embed_colour") + fields = ("id", "guild_id", "default_embed_colour", "active") class TrackedContentSerializer_GET(DynamicModelSerializer): diff --git a/apps/api/views.py b/apps/api/views.py index 6d68d5f..5015346 100644 --- a/apps/api/views.py +++ b/apps/api/views.py @@ -405,7 +405,7 @@ class GuildSettings_ListView(generics.ListCreateAPIView): metadata_class = ExpandedMetadata filter_backends = [filters.SearchFilter, rest_filters.DjangoFilterBackend, filters.OrderingFilter] - filterset_fields = ["id", "guild_id", "default_embed_colour"] + filterset_fields = ["id", "guild_id", "default_embed_colour", "active"] def get_queryset(self): if self.request.user.is_superuser: @@ -414,9 +414,7 @@ class GuildSettings_ListView(generics.ListCreateAPIView): return GuildSettings.objects.filter(added_by=self.request.user) def post(self, request): - - guild_id = request.data["guild_id"] - saved_guilds = SavedGuild.objects.filter(added_by=request.user) + saved_guilds = SavedGuilds.objects.filter(added_by=request.user) if not saved_guilds: return Response( @@ -445,7 +443,7 @@ class GuildSettings_ListView(generics.ListCreateAPIView): return (int(permissions) & 1 << 3) == 1 << 3 -class GuildSettings_DetailView(generics.RetrieveDestroyAPIView): +class GuildSettings_DetailView(generics.RetrieveUpdateDestroyAPIView): """ View to provide details on a particular GuildSettings model instances. @@ -462,7 +460,7 @@ class GuildSettings_DetailView(generics.RetrieveDestroyAPIView): if self.request.user.is_superuser: return GuildSettings.objects.all() - saved_guilds = SavedGuild.objects.filter(added_by=self.request.user) + saved_guilds = SavedGuilds.objects.filter(added_by=self.request.user) guild_ids = [guild.guild_id for guild in saved_guilds] return GuildSettings.objects.filter(guild_id__in=guild_ids) -- 2.47.2 From b908dff129cf97f58b0849a5bd5fac0662cdf68a Mon Sep 17 00:00:00 2001 From: Corban-Lee Jones Date: Fri, 16 Aug 2024 18:45:08 +0100 Subject: [PATCH 06/18] api functions for guild settings --- apps/static/js/api.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/static/js/api.js b/apps/static/js/api.js index f3e1047..da3fd29 100644 --- a/apps/static/js/api.js +++ b/apps/static/js/api.js @@ -24,7 +24,6 @@ async function ajaxRequest(url, method, data) { } function makeQuerystring(filters, sort) { - console.log(JSON.stringify(filters, null, 4)) let querystring = "?"; for (key in filters) { querystring += `${key}=${filters[key]}&`; @@ -163,4 +162,14 @@ async function getTrackedContentOptions() { async function getMutators() { return await ajaxRequest("/api/article-mutator/?page_size=25", "GET"); -} \ No newline at end of file +} + +// guild settings + +async function getGuildSettings(guildId) { + return await ajaxRequest(`/api/guild-settings/?guild_id=${guildId}`, "GET"); +} + +async function editGuildSettings(id, formData) { + return await ajaxRequest(`/api/guild-settings/${id}/`, "PUT", formData); +} -- 2.47.2 From e30f431dc359c01d682181738dd000ea2f6b3d53 Mon Sep 17 00:00:00 2001 From: Corban-Lee Jones Date: Fri, 16 Aug 2024 18:45:30 +0100 Subject: [PATCH 07/18] hex string normalisation and get value from field --- apps/static/js/home/index.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/apps/static/js/home/index.js b/apps/static/js/home/index.js index acf55ed..8e00c97 100644 --- a/apps/static/js/home/index.js +++ b/apps/static/js/home/index.js @@ -86,11 +86,30 @@ $(".colour-control-text").on("change", function() { }); function updateColourInput(id, hexString) { - hexString = hexString.toUpperCase(); + hexString = normaliseHexString(hexString.toUpperCase()); $(`#${id} .colour-picker`).val(hexString); $(`#${id} .colour-text`).val(hexString); } +function getColourInputVal(id, includeHashtag=true) { + const hexString = $(`#${id}Text`).val(); + return normaliseHexString(hexString, includeHashtag); +} + +function normaliseHexString(hexString, includeHashtag=true) { + console.debug(`normalising hex string '${hexString}' include hashtag '${includeHashtag}'`); + + // Remove any non-hex characters (e.g., additional hashtags) + hexString = hexString.replace(/[^A-F0-9]/gi, ''); + + // Ensure the hex string has a valid length of either 3, 6, or 8 characters + if (![3, 6, 8].includes(hexString.length)) { + throw new Error(`Invalid hex string length. Must be 3, 6, or 8 characters. hexString=${hexString}`); + } + + return includeHashtag ? `#${hexString}` : hexString; +} + $(document).ready(function() { $(".colour-input").each(function() { let id = $(this).attr("data-id") -- 2.47.2 From 1d1f5192168be2295ab176ae6bb8dfe052c1d101 Mon Sep 17 00:00:00 2001 From: Corban-Lee Jones Date: Fri, 16 Aug 2024 18:45:46 +0100 Subject: [PATCH 08/18] functional modal for settings --- apps/static/js/home/settings.js | 63 ++++++++++++++----- .../home/includes/settingsmodal.html | 20 +++++- 2 files changed, 65 insertions(+), 18 deletions(-) diff --git a/apps/static/js/home/settings.js b/apps/static/js/home/settings.js index 7503987..3f3b48c 100644 --- a/apps/static/js/home/settings.js +++ b/apps/static/js/home/settings.js @@ -1,29 +1,58 @@ -/** Here is how this should work - * - * Currently SavedGuild is created and destroyed on demand, it's not viable to store settings here - * instead lets create a new database obj `GuildSettings` to store these, an automated job can erase - * a `GuildSettings` instance if no matching `SavedGuilds` are found within x number of days. - * - * The tables will not be linked directly, but will share a common `guild_id` field to find - * each other, or the saved guild will have a foreign key for the related `GuildSettings`. -*/ - -$(document).on("selectedServerChange", async function() { - server = getCurrentlyActiveServer(); - updateColourInput("defaultEmbedColour", "#" + server.default_embed_colour); -}); - $("#serverSettingsBtn").on("click", async function() { await showServerSettingsModal(); }); async function showServerSettingsModal() { const server = getCurrentlyActiveServer(); + var guildSettings; + + try { guildSettings = (await getGuildSettings(server.guild_id))[0] } + catch (error) { + console.error(error) + return; + } + + $("#guildSettingsId").val(guildSettings.id); + $("#guildSettingsGuildId").val(guildSettings.guild_id); + $("#guildSettingsActive").prop("checked", guildSettings.active); + updateColourInput("guildSettingsDefaultEmbedColour", guildSettings.default_embed_colour); + $("#serverSettingsModal").modal("show"); } -$("#serverSettingsForm").on("submit", function(e) { +$("#serverSettingsForm").on("submit", async function(e) { e.preventDefault(); - alert("not implemented"); + + var id = $("#guildSettingsId").val(); + guildId = $("#guildSettingsGuildId").val(); + active = $("#guildSettingsActive").prop("checked"); + defaultEmbedColour = getColourInputVal("guildSettingsDefaultEmbedColour", false); + + const pk = await saveGuildSettings(id, guildId, defaultEmbedColour, active); + + if (pk) { + showToast("success", "Server Settings Saved", "Primary Key: " + pk); + } + + $("#serverSettingsModal").modal("hide"); + }) + +async function saveGuildSettings(id, guildId, defaultEmbedColour, active) { + var formData = new FormData(); + formData.append("guild_id", guildId); + formData.append("default_embed_colour", defaultEmbedColour); + formData.append("active", active); + + var response; + try { + response = await editGuildSettings(id, formData); + } + catch (err) { + console.error(err); + return false; + } + + return response.id; +} diff --git a/apps/templates/home/includes/settingsmodal.html b/apps/templates/home/includes/settingsmodal.html index b123974..e673180 100644 --- a/apps/templates/home/includes/settingsmodal.html +++ b/apps/templates/home/includes/settingsmodal.html @@ -8,7 +8,25 @@ -- 2.47.2 From 656e6c1106dcb475286b5597ea676eb33920d3ee Mon Sep 17 00:00:00 2001 From: Corban-Lee Jones Date: Fri, 16 Aug 2024 18:59:32 +0100 Subject: [PATCH 13/18] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0fcd24..532a17c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ +**unreleased v.0.3.1** + +- Fix: Incorrect margin on server settings modal buttons +- + **breaking changes v.0.3.0** - Enhancement: store guild settings in separate model -- 2.47.2 From 41a3376033111df0e294aa334f5ebdedb499f136 Mon Sep 17 00:00:00 2001 From: Corban-Lee Jones Date: Fri, 16 Aug 2024 19:04:24 +0100 Subject: [PATCH 14/18] plural results text --- apps/static/js/table.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/static/js/table.js b/apps/static/js/table.js index 6154536..9c607ed 100644 --- a/apps/static/js/table.js +++ b/apps/static/js/table.js @@ -321,7 +321,7 @@ function createTableControls(containingSelector, pageSizeId) {
-  Results +
@@ -382,7 +382,7 @@ function updateTableContainer(containerId, page, pageSize, itemsCount, totalItem // Updates the pagination text for a given pageInfoId function updateTablePaginationInfo(pageInfoId, showing, total) { $(`${pageInfoId} .pageinfo-showing`).text(showing); - $(`${pageInfoId} .pageinfo-total`).text(total); + $(`${pageInfoId} .pageinfo-total`).text(`${total} Result${total > 1 ? "s" : ""}`); } // Updates the pagination buttons for a given pageControlsId -- 2.47.2 From c7ca92cf82db889ffeb884a7f0bef2129e836415 Mon Sep 17 00:00:00 2001 From: Corban-Lee Jones Date: Fri, 16 Aug 2024 19:04:34 +0100 Subject: [PATCH 15/18] footer margin --- apps/templates/home/includes/servermodal.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/templates/home/includes/servermodal.html b/apps/templates/home/includes/servermodal.html index c23b7a7..49b4ade 100644 --- a/apps/templates/home/includes/servermodal.html +++ b/apps/templates/home/includes/servermodal.html @@ -23,7 +23,7 @@ You must be an administrator, or own the selected server.

-