diff --git a/aleksis/core/frontend/components/calendar/CalendarOverview.vue b/aleksis/core/frontend/components/calendar/CalendarOverview.vue index f5e19bd02210c52ae14bd0067775091efaa9201a..347dd3dcafda32f781f988a6cebddf47425885e1 100644 --- a/aleksis/core/frontend/components/calendar/CalendarOverview.vue +++ b/aleksis/core/frontend/components/calendar/CalendarOverview.vue @@ -50,6 +50,7 @@ <calendar-select v-model="selectedCalendarFeedNames" :calendar-feeds="calendar.calendarFeeds" + @input="storeActivatedCalendars" /> </button-menu> </v-col> @@ -92,6 +93,7 @@ class="mb-4" v-model="selectedCalendarFeedNames" :calendar-feeds="calendar.calendarFeeds" + @input="storeActivatedCalendars" /> <v-btn depressed block v-if="calendar" :href="calendar.allFeedsUrl"> <v-icon left>mdi-download-outline</v-icon> @@ -155,6 +157,7 @@ import { } from "aleksisAppImporter"; import gqlCalendarOverview from "./calendarOverview.graphql"; +import gqlSetCalendarStatus from "./setCalendarStatus.graphql"; export default { name: "CalendarOverview", @@ -197,6 +200,11 @@ export default { calendar: { query: gqlCalendarOverview, skip: true, + result({ data }) { + this.selectedCalendarFeedNames = data.calendar.calendarFeeds + .filter((c) => c.activated) + .map((c) => c.name); + }, }, }, computed: { @@ -268,6 +276,15 @@ export default { getColorForEvent(event) { return event.color; }, + storeActivatedCalendars() { + // Store currently activated calendars in the backend + this.$apollo.mutate({ + mutation: gqlSetCalendarStatus, + variables: { + calendars: this.selectedCalendarFeedNames, + }, + }); + }, fetchMoreCalendarEvents({ start, end }) { // Get the start and end dates of the current date range shown in the calendar let extendedStart = this.$refs.calendar.getStartOfWeek(start).date; diff --git a/aleksis/core/frontend/components/calendar/calendarOverview.graphql b/aleksis/core/frontend/components/calendar/calendarOverview.graphql index 4d55a10ad313b3561a4ec0b9a077b485d9400e23..f277ef4bbffcf9cf68532a5e1ba9ce3ecd2c813b 100644 --- a/aleksis/core/frontend/components/calendar/calendarOverview.graphql +++ b/aleksis/core/frontend/components/calendar/calendarOverview.graphql @@ -7,6 +7,7 @@ query ($start: Date, $end: Date) { description url color + activated feed { events(start: $start, end: $end) { name diff --git a/aleksis/core/frontend/components/calendar/setCalendarStatus.graphql b/aleksis/core/frontend/components/calendar/setCalendarStatus.graphql new file mode 100644 index 0000000000000000000000000000000000000000..633f0791c3e00f0c82886779366704086871c484 --- /dev/null +++ b/aleksis/core/frontend/components/calendar/setCalendarStatus.graphql @@ -0,0 +1,5 @@ +mutation ($calendars: [String]!) { + setCalendarStatus(calendars: $calendars) { + ok + } +} diff --git a/aleksis/core/mixins.py b/aleksis/core/mixins.py index 43bed808ee3f36b98f2745a5bab329f6ece97508..d1599b6b33b553dabcb668e41e00896ba352523c 100644 --- a/aleksis/core/mixins.py +++ b/aleksis/core/mixins.py @@ -718,7 +718,17 @@ class CalendarEventMixin(RegistryObject): def value_color(cls, reference_object, request) -> str: return cls.get_color(request) + @classproperty + def valid_feed(cls): + """Return if the feed is valid.""" + return cls.name != cls.__name__ + @classproperty def valid_feeds(cls): """Return a list of valid feeds.""" - return [feed for feed in cls.registered_objects_list if feed.name != feed.__name__] + return [feed for feed in cls.registered_objects_list if feed.valid_feed] + + @classproperty + def valid_feed_names(cls): + """Return a list of valid feed names.""" + return [feed.name for feed in cls.valid_feeds] diff --git a/aleksis/core/preferences.py b/aleksis/core/preferences.py index 4c9758e16e3722c8775e50a11004c533366b3f05..a3be1ac89770a89c2d3ea82b64aee7adc3913c9f 100644 --- a/aleksis/core/preferences.py +++ b/aleksis/core/preferences.py @@ -16,7 +16,7 @@ from dynamic_preferences.types import ( ) from oauth2_provider.models import AbstractApplication -from .mixins import PublicFilePreferenceMixin +from .mixins import CalendarEventMixin, PublicFilePreferenceMixin from .models import Group, Person from .registries import person_preferences_registry, site_preferences_registry from .util.notifications import get_notification_choices_lazy @@ -502,3 +502,18 @@ class HolidayFeedColor(StringPreference): verbose_name = _("Holiday calendar feed color") widget = ColorWidget required = True + + +@person_preferences_registry.register +class ActivatedCalendars(MultipleChoicePreference): + """Calendars that are activated for a person.""" + + section = calendar + name = "activated_calendars" + default = [] + widget = SelectMultiple + verbose_name = _("Activated calendars") + required = False + + field_attribute = {"initial": []} + choices = [(feed.name, feed.verbose_name) for feed in CalendarEventMixin.valid_feeds] diff --git a/aleksis/core/schema/__init__.py b/aleksis/core/schema/__init__.py index 0ce00ad587d2f43b0ed2776f9c598073c5ae401f..249f5fd7e5382923d9ec6915c3ca666b74208eef 100644 --- a/aleksis/core/schema/__init__.py +++ b/aleksis/core/schema/__init__.py @@ -20,7 +20,7 @@ from ..models import ( ) from ..util.apps import AppConfig from ..util.core_helpers import get_allowed_object_ids, get_app_module, get_app_packages, has_person -from .calendar import CalendarBaseType +from .calendar import CalendarBaseType, SetCalendarStatusMutation from .celery_progress import CeleryProgressFetchedMutation, CeleryProgressType from .custom_menu import CustomMenuType from .dynamic_routes import DynamicRouteType @@ -188,6 +188,8 @@ class Mutation(graphene.ObjectType): revoke_oauth_token = OAuthRevokeTokenMutation.Field() + set_calendar_status = SetCalendarStatusMutation.Field() + def build_global_schema(): """Build global GraphQL schema from all apps.""" diff --git a/aleksis/core/schema/calendar.py b/aleksis/core/schema/calendar.py index 22f8c0687d7bc6dfc626ad75d8c784b497f0b2db..6ad0f35b865d9b2fdc906ecbb494dc9827f30e45 100644 --- a/aleksis/core/schema/calendar.py +++ b/aleksis/core/schema/calendar.py @@ -1,11 +1,13 @@ from datetime import datetime +from django.core.exceptions import PermissionDenied from django.urls import reverse import graphene from graphene import ObjectType from aleksis.core.mixins import CalendarEventMixin +from aleksis.core.util.core_helpers import has_person class CalendarEventType(ObjectType): @@ -71,6 +73,8 @@ class CalendarType(ObjectType): url = graphene.String() + activated = graphene.Boolean() + def resolve_verbose_name(root, info, **kwargs): return root.get_verbose_name(info.context) @@ -86,6 +90,25 @@ class CalendarType(ObjectType): def resolve_color(root, info, **kwargs): return root.get_color(info.context) + def resolve_activated(root, info, **kwargs): + return root.name in info.context.user.person.preferences["calendar__activated_calendars"] + + +class SetCalendarStatusMutation(graphene.Mutation): + """Mutation to change the status of a calendar.""" + + class Arguments: + calendars = graphene.List(graphene.String) + + ok = graphene.Boolean() + + def mutate(root, info, calendars, **kwargs): + if not has_person(info.context): + raise PermissionDenied + calendar_feeds = [cal for cal in calendars if cal in CalendarEventMixin.valid_feed_names] + info.context.user.person.preferences["calendar__activated_calendars"] = calendar_feeds + return SetCalendarStatusMutation(ok=True) + class CalendarBaseType(ObjectType): calendar_feeds = graphene.List(CalendarType)