Skip to content
Snippets Groups Projects
Commit 2013eff4 authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Merge branch 'feature/vue/about' into 'master'

[Vue] About page

See merge request !1095
parents 0659a50d 6bc1517e
No related branches found
No related tags found
1 merge request!1095[Vue] About page
Pipeline #96497 passed with warnings
Pipeline: AlekSIS

#96501

    Showing
    with 579 additions and 118 deletions
    <template>
    <div class="mt-4 mb-4">
    <about-aleksis></about-aleksis>
    <installed-apps-list />
    </div>
    </template>
    <script>
    import InstalledAppsList from "./InstalledAppsList.vue";
    import AboutAleksis from "./AboutAleksis.vue";
    export default {
    name: "About",
    components: { AboutAleksis, InstalledAppsList },
    };
    </script>
    <template>
    <v-row class="mb-3">
    <v-col cols="12">
    <v-card class="d-flex flex-column">
    <v-card-title>{{ $t("about.about_aleksis") }}</v-card-title>
    <v-card-text>
    <p class="text-body-1">
    {{ $t("about.about_aleksis_1") }}
    </p>
    <p class="text-body-1">
    {{ $t("about.about_aleksis_2") }}
    </p>
    </v-card-text>
    <v-spacer></v-spacer>
    <v-card-actions>
    <v-btn text color="primary" href="https://aleksis.org/">{{
    $t("about.website_of_aleksis")
    }}</v-btn>
    <v-btn text color="primary" href="https://edugit.org/AlekSIS/">{{
    $t("about.source_code")
    }}</v-btn>
    </v-card-actions>
    </v-card>
    </v-col>
    <v-col cols="12">
    <v-card class="d-flex flex-column">
    <v-card-title>{{ $t("about.licence_information") }}</v-card-title>
    <v-card-text>
    <p>
    {{ $t("about.licence_information_1") }}
    </p>
    <p>
    <v-chip color="green" text-color="white" small>{{
    $t("about.free_open_source_licence")
    }}</v-chip>
    <v-chip color="orange" text-color="white" small>{{
    $t("about.other_licence")
    }}</v-chip>
    </p>
    </v-card-text>
    <v-spacer></v-spacer>
    <v-card-actions>
    <v-btn text color="primary" href="https://eupl.eu">{{
    $t("about.full_licence_text")
    }}</v-btn>
    <v-btn
    text
    color="primary"
    href="https://joinup.ec.europa.eu/collection/eupl/guidelines-users-and-developers"
    >
    {{ $t("about.more_information_eupl") }}
    </v-btn>
    </v-card-actions>
    </v-card>
    </v-col>
    </v-row>
    </template>
    <script>
    export default {
    name: "AboutAleksis",
    };
    </script>
    <style scoped></style>
    <template>
    <v-col cols="12" md="6" lg="6" xl="4" class="d-flex align-stretch">
    <v-card :id="app.name" class="d-flex flex-column flex-grow-1">
    <v-card-title>
    {{ app.verboseName }}
    </v-card-title>
    <v-card-subtitle class="text-body-1 black--text">
    {{ app.version }}
    </v-card-subtitle>
    <v-card-text>
    <v-row v-if="app.licence" class="mb-2">
    <v-col cols="6">
    {{ $t("about.licenced_under") }} <br />
    <strong class="text-body-1 black--text">{{
    app.licence.verboseName
    }}</strong>
    </v-col>
    <v-col cols="6">
    {{ $t("about.licence_type") }} <br />
    <v-chip
    v-if="app.licence.flags.isFsfLibre"
    color="green"
    text-color="white"
    small
    >
    {{ $t("about.free_software") }}
    </v-chip>
    <v-chip
    v-else-if="app.licence.flags.isOsiApproved"
    color="green"
    text-color="white"
    small
    >
    {{ $t("about.open_source") }}
    </v-chip>
    <v-chip v-else color="orange" text-color="white" small>
    {{ $t("about.proprietary") }}
    </v-chip>
    </v-col>
    <v-col cols="12" v-if="app.licence.licences.length !== 0">
    {{ $t("about.licence_consists_of") }}
    <div
    v-for="licence in app.licence.licences"
    class="mb-2"
    :key="licence.name"
    >
    <v-chip
    v-if="licence.isOsiApproved || licence.isFsfLibre"
    color="green"
    text-color="green"
    outlined
    small
    :href="licence.url"
    >
    {{ licence.name }}
    </v-chip>
    <v-chip
    v-else
    color="orange"
    text-color="orange"
    outlined
    :href="licence.url"
    >
    {{ licence.name }}
    </v-chip>
    </div>
    </v-col>
    </v-row>
    </v-card-text>
    <v-spacer></v-spacer>
    <v-card-actions v-if="app.urls.length !== 0">
    <v-btn text color="primary" @click="reveal = true">
    Show copyright
    </v-btn>
    <v-btn
    v-for="url in app.urls"
    color="primary"
    text
    :href="url.url"
    :key="url.url"
    >
    {{ url.name }}
    </v-btn>
    </v-card-actions>
    <v-expand-transition>
    <v-card
    v-if="reveal"
    class="transition-fast-in-fast-out v-card--reveal d-flex flex-column"
    >
    <v-card-text class="pb-0">
    <v-row>
    <v-col cols="12" v-if="app.copyrights.length !== 0">
    <span v-for="(copyright, index) in app.copyrights" :key="index">
    Copyright © {{ copyright.years }}
    <a :href="'mailto:' + copyright.email">{{
    copyright.name
    }}</a>
    <br />
    </span>
    </v-col>
    </v-row>
    </v-card-text>
    <v-spacer></v-spacer>
    <v-card-actions class="pt-0">
    <v-btn text color="primary" @click="reveal = false"> Close </v-btn>
    </v-card-actions>
    </v-card>
    </v-expand-transition>
    </v-card>
    </v-col>
    </template>
    <script>
    export default {
    name: "InstalledAppCard",
    data: () => ({
    reveal: false,
    }),
    props: {
    app: {
    type: Object,
    required: true,
    },
    },
    };
    </script>
    <template>
    <ApolloQuery :query="require('./installedApps.graphql')">
    <template #default="{ result: { error, data }, isLoading }">
    <v-row v-if="isLoading">
    <v-col
    v-for="idx in 3"
    :key="idx"
    cols="12"
    md="6"
    lg="6"
    xl="4"
    class="d-flex align-stretch"
    >
    <v-card class="d-flex flex-column flex-grow-1 pa-4">
    <v-skeleton-loader
    type="heading, actions, text@5"
    ></v-skeleton-loader>
    </v-card>
    </v-col>
    </v-row>
    <v-row v-if="data.installedApps">
    <installed-app-card
    v-for="app in data.installedApps"
    :key="app.name"
    :app="app"
    />
    </v-row>
    </template>
    </ApolloQuery>
    </template>
    <script>
    import InstalledAppCard from "./InstalledAppCard.vue";
    export default {
    name: "InstalledAppsList",
    components: { InstalledAppCard },
    };
    </script>
    {
    installedApps {
    name
    verboseName
    version
    copyrights {
    years
    name
    email
    }
    licence {
    verboseName
    flags {
    isFsfLibre
    isOsiApproved
    }
    licences {
    isFsfLibre
    isOsiApproved
    name
    url
    }
    }
    urls {
    name
    url
    }
    }
    }
    ......@@ -21,3 +21,12 @@ h6,
    [v-cloak] {
    display: none;
    }
    .v-card--reveal {
    bottom: 0;
    opacity: 1 !important;
    position: absolute;
    width: 100%;
    height: 100%;
    overflow-y: scroll;
    }
    ......@@ -2,3 +2,7 @@ import "@mdi/font/css/materialdesignicons.css";
    import "./util";
    import "./app";
    import About from "./components/about/About.vue";
    window.router.addRoute({ path: "/about", component: About });
    ......@@ -6,6 +6,25 @@
    },
    "alerts": {
    "page_cached": "This page may contain outdated information since there is no internet connection."
    },
    "about": {
    "about_aleksis": "About AlekSIS®",
    "licenced_under": "Licenced under",
    "licence_type": "Licence Type",
    "free_software": "Free Software",
    "open_source": "Open Source",
    "proprietary": "Proprietary",
    "licence_consists_of": "The licence consists of",
    "about_aleksis_1": "This platform is powered by AlekSIS®, a web-based school information system (SIS) which can be used to manage and/or publish organisational artifacts of educational institutions. AlekSIS is free software and can be used by anyone.",
    "about_aleksis_2": "AlekSIS® is a registered trademark of the AlekSIS open source project, represented by Teckids e.V.",
    "website_of_aleksis": "Website of AlekSIS",
    "source_code": "Source Code",
    "licence_information": "Licence Information",
    "licence_information_1": "The core and the official apps of AlekSIS are licenced under the EUPL, version 1.2 or later. For licence information from third-party apps, if installed, refer to the respective components below. The licences are marked like this:",
    "free_open_source_licence": "Free/Open Source Licence",
    "other_licence": "Other Licence",
    "full_licence_text": "Full Licence Text",
    "more_information_eupl": "More information about the EUPL"
    }
    },
    "de": {
    ......@@ -15,6 +34,25 @@
    },
    "alerts": {
    "page_cached": "Diese Seite enthält vielleicht veraltete Informationen, da es keine Internetverbindung gibt."
    },
    "about": {
    "about_aleksis": "Über AlekSIS®",
    "licenced_under": "Lizensiert unter",
    "licence_type": "Lizenztyp",
    "free_software": "Freie Software",
    "open_source": "Open Source",
    "proprietary": "Proprietär",
    "licence_consists_of": "Die Lizenz besteht aus",
    "about_aleksis_1": "Diese Plattform wird mit AlekSIS®, einem webbasierten Schulinformationssystem (SIS), welches für die Verwaltung und/oder Veröffentlichung von Bildungseinrichtungen verwendet werden kann. AlekSIS ist freie Software und kann von jedem benutzt werden.",
    "about_aleksis_2": " AlekSIS® ist eine eingetragene Wortmarke des Open-Source-Projektes AlekSIS, vertreten durch den Teckids e.V.",
    "website_of_aleksis": "Website von AlekSIS",
    "source_code": "Quellcode",
    "licence_information": "Lizenzinformationen",
    "licence_information_1": " Der Core und die offiziellen Apps von AlekSIS sind unter der EUPL, Version 1.2 oder später, lizenziert. Für Lizenzinformationen zu Apps von Drittanbietern, wenn installiert, siehe direkt bei der jeweiligen App weiter unten auf dieser Seite. Die Lizenzen sind wie folgt markiert:",
    "free_open_source_licence": "Freie/Open Source Lizenz",
    "other_licence": "Andere Lizenz",
    "full_licence_text": "Kompletter Lizenztext",
    "more_information_eupl": "Weitere Informationen über die EUPL"
    }
    }
    }
    from django.conf import settings
    from django.utils import translation
    from django.apps import apps
    import graphene
    from graphene import ObjectType
    from graphene_django import DjangoObjectType
    from graphene_django.forms.mutation import DjangoModelFormMutation
    from .forms import PersonForm
    from .models import Group, Notification, Person
    from .util.core_helpers import get_app_module, get_app_packages, has_person
    from .util.frontend_helpers import get_language_cookie
    class NotificationType(DjangoObjectType):
    class Meta:
    model = Notification
    class PersonType(DjangoObjectType):
    class Meta:
    model = Person
    full_name = graphene.Field(graphene.String)
    def resolve_full_name(root: Person, info, **kwargs):
    return root.full_name
    class GroupType(DjangoObjectType):
    class Meta:
    model = Group
    class LanguageType(ObjectType):
    code = graphene.String(required=True)
    name = graphene.String(required=True)
    name_local = graphene.String(required=True)
    name_translated = graphene.String(required=True)
    bidi = graphene.Boolean(required=True)
    cookie = graphene.String(required=True)
    class SystemPropertiesType(graphene.ObjectType):
    current_language = graphene.String(required=True)
    available_languages = graphene.List(LanguageType)
    def resolve_current_language(parent, info, **kwargs):
    return info.context.LANGUAGE_CODE
    def resolve_available_languages(parent, info, **kwargs):
    return [
    translation.get_language_info(code) | {"cookie": get_language_cookie(code)}
    for code, name in settings.LANGUAGES
    ]
    class PersonMutation(DjangoModelFormMutation):
    person = graphene.Field(PersonType)
    class Meta:
    form_class = PersonForm
    class MarkNotificationReadMutation(graphene.Mutation):
    class Arguments:
    id = graphene.ID() # noqa
    notification = graphene.Field(NotificationType)
    @classmethod
    def mutate(cls, root, info, id): # noqa
    notification = Notification.objects.get(pk=id)
    # FIXME permissions
    notification.read = True
    notification.save()
    return notification
    from ..models import Notification, Person
    from ..util.apps import AppConfig
    from ..util.core_helpers import get_app_module, get_app_packages, has_person
    from .group import GroupType # noqa
    from .installed_apps import AppType
    from .notification import MarkNotificationReadMutation, NotificationType
    from .person import PersonMutation, PersonType
    from .system_properties import SystemPropertiesType
    class Query(graphene.ObjectType):
    ......@@ -88,6 +22,7 @@ class Query(graphene.ObjectType):
    who_am_i = graphene.Field(PersonType)
    system_properties = graphene.Field(SystemPropertiesType)
    installed_apps = graphene.List(AppType)
    def resolve_notifications(root, info, **kwargs):
    # FIXME do permission stuff
    ......@@ -109,6 +44,9 @@ class Query(graphene.ObjectType):
    def resolve_system_properties(root, info, **kwargs):
    return True
    def resolve_installed_apps(root, info, **kwargs):
    return [app for app in apps.get_app_configs() if isinstance(app, AppConfig)]
    class Mutation(graphene.ObjectType):
    update_person = PersonMutation.Field()
    ......
    from graphene_django import DjangoObjectType
    from ..models import Group
    class GroupType(DjangoObjectType):
    class Meta:
    model = Group
    import graphene
    from graphene import ObjectType
    class AppURLType(ObjectType):
    name = graphene.String(required=True)
    url = graphene.String(required=True)
    class CopyrightType(ObjectType):
    years = graphene.String(required=True)
    name = graphene.String(required=True)
    email = graphene.String(required=True)
    class LicenceFlagsType(ObjectType):
    isFsfLibre = graphene.Boolean(required=True)
    isOsiApproved = graphene.Boolean(required=True)
    class SubLicenceType(ObjectType):
    isDeprecatedLicenseId = graphene.Boolean(default_value=False)
    isFsfLibre = graphene.Boolean(default_value=False)
    isOsiApproved = graphene.Boolean(default_value=False)
    licenseId = graphene.String(required=True)
    name = graphene.String(required=True)
    referenceNumber = graphene.Int(default_value=-1)
    url = graphene.String()
    class LicenceType(ObjectType):
    verbose_name = graphene.String(required=True)
    flags = graphene.Field(LicenceFlagsType, required=True)
    licences = graphene.List(SubLicenceType)
    class AppType(ObjectType):
    copyrights = graphene.List(CopyrightType)
    licence = graphene.Field(LicenceType)
    name = graphene.String(required=True)
    verbose_name = graphene.String(required=True)
    version = graphene.String()
    urls = graphene.List(AppURLType)
    def resolve_verbose_name(root, info, **kwargs):
    return root.get_name()
    def resolve_version(root, info, **kwargs):
    return root.get_version()
    def resolve_licence(root, info, **kwargs):
    return root.get_licence_dict()
    def resolve_urls(root, info, **kwargs):
    return root.get_urls_dict()
    def resolve_copyrights(root, info, **kwargs):
    return root.get_copyright_dicts()
    import graphene
    from graphene_django import DjangoObjectType
    from ..models import Notification
    class NotificationType(DjangoObjectType):
    class Meta:
    model = Notification
    class MarkNotificationReadMutation(graphene.Mutation):
    class Arguments:
    id = graphene.ID() # noqa
    notification = graphene.Field(NotificationType)
    @classmethod
    def mutate(cls, root, info, id): # noqa
    notification = Notification.objects.get(pk=id)
    # FIXME permissions
    notification.read = True
    notification.save()
    return notification
    import graphene
    from graphene_django import DjangoObjectType
    from graphene_django.forms.mutation import DjangoModelFormMutation
    from ..forms import PersonForm
    from ..models import Person
    class PersonType(DjangoObjectType):
    class Meta:
    model = Person
    full_name = graphene.Field(graphene.String)
    def resolve_full_name(root: Person, info, **kwargs):
    return root.full_name
    class PersonMutation(DjangoModelFormMutation):
    person = graphene.Field(PersonType)
    class Meta:
    form_class = PersonForm
    from django.conf import settings
    from django.utils import translation
    import graphene
    from ..util.frontend_helpers import get_language_cookie
    class LanguageType(graphene.ObjectType):
    code = graphene.String(required=True)
    name = graphene.String(required=True)
    name_local = graphene.String(required=True)
    name_translated = graphene.String(required=True)
    bidi = graphene.Boolean(required=True)
    cookie = graphene.String(required=True)
    class SystemPropertiesType(graphene.ObjectType):
    current_language = graphene.String(required=True)
    available_languages = graphene.List(LanguageType)
    def resolve_current_language(parent, info, **kwargs):
    return info.context.LANGUAGE_CODE
    def resolve_available_languages(parent, info, **kwargs):
    return [
    translation.get_language_info(code) | {"cookie": get_language_cookie(code)}
    for code, name in settings.LANGUAGES
    ]
    {# -*- engine:django -*- #}
    {% extends "core/base.html" %}
    {% extends "core/vue_base.html" %}
    {% load i18n %}
    ......@@ -7,121 +7,5 @@
    {% block page_title %}{% blocktrans %}AlekSIS® – The Free School Information System{% endblocktrans %}{% endblock %}
    {% block content %}
    <div class="row">
    <div class="col s12">
    <div class="card">
    <div class="card-content">
    <span class="card-title">{% blocktrans %}About AlekSIS{% endblocktrans %}</span>
    <p>
    {% blocktrans %}
    This platform is powered by AlekSIS®, a web-based school information system (SIS) which can be used
    to manage and/or publish organisational artifacts of educational institutions. AlekSIS is free software and
    can be used by anyone.
    {% endblocktrans %}
    </p>
    <p>
    {% blocktrans %}
    AlekSIS® is a registered trademark of the AlekSIS open source project, represented by Teckids e.V.
    {% endblocktrans %}
    </p>
    </div>
    <div class="card-action">
    <a class="" href="https://aleksis.org/">{% trans "Website of AlekSIS" %}</a>
    <a class="" href="https://edugit.org/AlekSIS/">{% trans "Source code" %}</a>
    </div>
    </div>
    </div>
    </div>
    <div class="row">
    <div class="col s12">
    <div class="card">
    <div class="card-content">
    <span class="card-title">{% trans "Licence information" %}</span>
    <p>
    {% blocktrans %}
    The core and the official apps of AlekSIS are licenced under the EUPL, version 1.2 or later. For licence
    information from third-party apps, if installed, refer to the respective components below. The
    licences are marked like this:
    {% endblocktrans %}
    </p>
    <br/>
    <p>
    <span class="chip green white-text">{% trans "Free/Open Source Licence" %}</span>
    <span class="chip orange white-text">{% trans "Other Licence" %}</span>
    </p>
    </div>
    <div class="card-action">
    <a href="https://eupl.eu">{% trans "Full licence text" %}</a>
    <a href="https://joinup.ec.europa.eu/collection/eupl/guidelines-users-and-developers">{% trans "More information about the EUPL" %}</a>
    </div>
    </div>
    </div>
    </div>
    <div class="row">
    {% for app_config in app_configs %}
    <div class="col s12 m12 l6">
    <div class="card " id="{{ app_config.name }}">
    <div class="card-content">
    {% if app_config.get_licence.1.isFsfLibre %}
    <span class="chip green white-text right">Free Software</span>
    {% elif app_config.get_licence.1.isOsiApproved %}
    <span class="chip green white-text right">Open Source</span>
    {% endif %}
    <span class="card-title">{{ app_config.get_name }} <small>{{ app_config.get_version }}</small></span>
    {% if app_config.get_copyright %}
    <p>
    {% for holder in app_config.get_copyright %}
    Copyright © {{ holder.0 }}
    {% if holder.2 %}
    <a href="mailto:{{ holder.2 }}">{{ holder.1 }}</a>
    {% else %}
    {{ holder.1 }}
    {% endif %}
    <br/>
    {% endfor %}
    </p>
    <br/>
    {% endif %}
    {% if app_config.get_licence %}
    {% with licence=app_config.get_licence %}
    <p>
    {% blocktrans with licence=licence.0 %}
    This app is licenced under {{ licence }}.
    {% endblocktrans %}
    </p>
    <br/>
    <p>
    {% for l in licence.2 %}
    <a class="chip white-text {% if l.isOsiApproved or l.isFsfLibre %}green{% else %}orange{% endif %}"
    href="{{ l.url }}">
    {{ l.name }}
    </a>
    {% endfor %}
    </p>
    {% endwith %}
    {% endif %}
    </div>
    {% if app_config.get_urls %}
    <div class="card-action">
    {% for url_name, url in app_config.get_urls.items %}
    <a href="{{ url }}">{{ url_name }}</a>
    {% endfor %}
    </div>
    {% endif %}
    </div>
    </div>
    {% if forloop.counter|divisibleby:2 %}
    </div>
    <div class="row">
    {% endif %}
    {% endfor %}
    </div>
    <router-view></router-view>
    {% endblock %}
    ......@@ -128,12 +128,28 @@ class AppConfig(django.apps.AppConfig):
    # We could not find a valid licence
    return ("Unknown", [default_dict])
    @classmethod
    def get_licence_dict(cls):
    """Get licence information of application package."""
    licence = cls.get_licence()
    return {
    "verbose_name": licence[0],
    "flags": licence[1],
    "licences": licence[2],
    }
    @classmethod
    def get_urls(cls):
    """Get list of URLs for this application package."""
    return getattr(cls, "urls", {})
    # TODO Try getting from distribution if not set
    @classmethod
    def get_urls_dict(cls):
    """Get list of URLs for this application package."""
    urls = cls.get_urls()
    return [{"name": key, "url": value} for key, value in urls.items()]
    @classmethod
    def get_copyright(cls) -> Sequence[tuple[str, str, str]]:
    """Get copyright information tuples for application package."""
    ......@@ -155,6 +171,12 @@ class AppConfig(django.apps.AppConfig):
    return copyrights_processed
    # TODO Try getting from distribution if not set
    @classmethod
    def get_copyright_dicts(cls):
    """Get copyright information dictionaries for application package."""
    infos = cls.get_copyright()
    return [{"years": info[0], "name": info[1], "email": info[2]} for info in infos]
    def preference_updated(
    self,
    sender: Any,
    ......
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Finish editing this message first!
    Please register or to comment