├── ads ├── __init__.py ├── migrations │ ├── __init__.py │ └── 0001_initial.py ├── tests.py ├── apps.py ├── admin.py ├── urls.py ├── views.py └── models.py ├── alumni ├── __init__.py ├── util.py ├── migrations │ ├── __init__.py │ ├── 0001_initial.py │ └── 0002_alumniupdatetoken.py ├── tests.py ├── admin.py ├── apps.py ├── urls.py ├── models.py └── gsuite_adapter.py ├── ctf ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0003_alter_ctf_content_alter_flag_clues.py │ └── 0002_guess.py ├── tests.py ├── apps.py ├── urls.py ├── forms.py └── admin.py ├── date ├── __init__.py ├── apps.py ├── functions.py ├── admin.py └── tests.py ├── events ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0011_event_redirect_link.py │ ├── 0006_event_passcode.py │ ├── 0010_event_captcha.py │ ├── 0005_event_members_only.py │ ├── 0009_alter_eventattendees_attendee_nr.py │ ├── 0012_eventregistrationform_choice_number.py │ ├── 0013_alter_event_content.py │ ├── 0014_alter_event_sign_up_max_participants.py │ ├── 0002_auto_20200116_2218.py │ ├── 0007_auto_20220529_1750.py │ ├── 0015_event_parent_eventattendees_original_event.py │ ├── 0003_auto_20200331_1855.py │ └── 0004_auto_20220217_1841.py ├── apps.py ├── urls.py ├── routing.py ├── widgets.py ├── consumers.py ├── feed.py └── websocket_utils.py ├── lucia ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0003_alter_candidate_content.py │ ├── 0002_auto_20201129_1238.py │ └── 0001_initial.py ├── tests.py ├── apps.py ├── admin.py ├── urls.py ├── views.py └── models.py ├── media └── .gitkeep ├── news ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0005_remove_post_albins_angels.py │ ├── 0002_post_albins_angels.py │ └── 0003_alter_post_content.py ├── apps.py ├── urls.py ├── feed.py ├── tests.py ├── admin.py └── views.py ├── polls ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0001_initial.py │ └── 0003_remove_question_members_only_and_more.py ├── apps.py ├── urls.py ├── views.py └── admin.py ├── social ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0001_initial.py │ ├── 0002_harassment.py │ └── 0003_harassmentemailrecipient.py ├── tests.py ├── apps.py ├── admin.py ├── urls.py ├── forms.py ├── models.py └── igupdate.py ├── archive ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0005_collection_hide_for_gulis.py │ ├── 0002_auto_20200109_1909.py │ └── 0006_auto_20221004_1958.py ├── templatetags │ └── __init__.py ├── apps.py ├── tables.py ├── fields.py ├── filters.py └── urls.py ├── billing ├── __init__.py ├── migrations │ ├── __init__.py │ └── 0002_alter_eventbillingconfiguration_price_selector.py ├── tests.py ├── views.py ├── context_processors.py ├── apps.py └── models.py ├── core ├── urls │ ├── __init__.py │ ├── biocum.py │ └── on.py ├── settings │ ├── __init__.py │ ├── dependencies │ │ └── __init__.py │ ├── test.py │ └── biocum.py ├── .gitignore ├── __init__.py ├── context_processors.py ├── celery.py ├── wsgi.py ├── routing.py ├── storage_backends.py └── utils.py ├── members ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0008_alumnisignup_operation.py │ ├── 0011_delete_alumniemailrecipient_delete_alumnisignup.py │ ├── 0005_alter_alumniemailrecipient_options.py │ ├── 0003_auto_20200109_1909.py │ ├── 0010_member_year_of_admission.py │ ├── 0004_alumniemailrecipient.py │ └── 0006_alumnisignup.py ├── apps.py ├── tokens.py ├── backends.py ├── urls.py └── managers.py ├── staticpages ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0003_staticpage_members_only.py │ ├── 0004_alter_staticpage_content.py │ ├── 0008_staticurl_logged_in_only.py │ ├── 0007_staticpagenav_url_staticpagenav_use_category_url.py │ ├── 0006_remove_staticpage_category_and_more.py │ ├── 0005_staticpage_nav_to_url.py │ ├── 0002_auto_20200805_1027.py │ └── 0001_initial.py ├── tests.py ├── apps.py ├── urls.py ├── context_processors.py ├── views.py └── admin.py ├── publications ├── __init__.py ├── migrations │ └── __init__.py ├── tests.py ├── apps.py ├── urls.py ├── fields.py ├── admin.py └── views.py ├── static ├── common │ ├── polls │ │ └── css │ │ │ └── style.css │ ├── events │ │ └── css │ │ │ ├── style.css │ │ │ ├── detail.css │ │ │ ├── event-card.css │ │ │ └── form.css │ ├── members │ │ └── css │ │ │ ├── userinfo.css │ │ │ ├── functionary.css │ │ │ └── certificate.css │ ├── news │ │ └── css │ │ │ ├── article.css │ │ │ └── style.css │ ├── staticpages │ │ └── css │ │ │ └── staticpage.css │ ├── core │ │ ├── css │ │ │ ├── ckeditor.css │ │ │ ├── footer.css │ │ │ ├── error-view.css │ │ │ └── cookie-banner.css │ │ ├── images │ │ │ ├── HQKK_2.png │ │ │ ├── albin.ico │ │ │ ├── albin.png │ │ │ ├── axel.png │ │ │ ├── goldbg.png │ │ │ ├── kklogo.ico │ │ │ ├── logo.ico │ │ │ ├── smoke.jpg │ │ │ ├── stina.png │ │ │ ├── waves.png │ │ │ ├── fallback.png │ │ │ ├── albin_black.png │ │ │ ├── albin_gold.png │ │ │ ├── albin_gold2.png │ │ │ ├── albin_white.png │ │ │ ├── cog_black.png │ │ │ ├── footerlogo.png │ │ │ ├── goldthingy.png │ │ │ ├── headerlogo.png │ │ │ ├── albin_glitch.png │ │ │ ├── albins-angels.png │ │ │ ├── glitchoverlay.png │ │ │ ├── albin_black_pink.png │ │ │ ├── goldborderleft.png │ │ │ └── goldborderright.png │ │ └── js │ │ │ ├── cookie-banner.js │ │ │ ├── eventform.js │ │ │ └── live-attendee-list.js │ ├── ctf │ │ └── css │ │ │ └── style.css │ ├── publications │ │ └── js │ │ │ └── main.js │ ├── archive │ │ └── css │ │ │ ├── document.css │ │ │ ├── style.css │ │ │ └── detailed.css │ └── prettyjson │ │ └── prettyjson.css ├── biocum │ ├── core │ │ ├── css │ │ │ ├── ckeditor.css │ │ │ ├── footer.css │ │ │ └── error-view.css │ │ └── images │ │ │ ├── logo.ico │ │ │ ├── biocumlogo.jpeg │ │ │ ├── footerlogo.png │ │ │ └── headerlogo.png │ ├── events │ │ └── css │ │ │ ├── style.css │ │ │ └── event-card.css │ ├── archive │ │ └── css │ │ │ └── document.css │ └── date │ │ └── css │ │ └── date-root.css ├── kk │ ├── core │ │ └── images │ │ │ ├── logo.ico │ │ │ ├── paroc.png │ │ │ ├── stars.png │ │ │ ├── footerlogo.png │ │ │ ├── headerlogo.png │ │ │ ├── luciapic.png │ │ │ ├── twinkling.png │ │ │ ├── 100logogold.png │ │ │ ├── andritzLogo.png │ │ │ ├── axelstinagold.png │ │ │ ├── ballong_black.png │ │ │ ├── doktorprofessor.png │ │ │ ├── twinklingstars.png │ │ │ └── doktorprofessorgold.png │ ├── archive │ │ └── css │ │ │ └── document.css │ ├── events │ │ └── css │ │ │ ├── style.css │ │ │ └── event-card.css │ └── date │ │ └── css │ │ └── date-root.css ├── on │ ├── events │ │ ├── img │ │ │ ├── onlogo.png │ │ │ ├── onbackground.jpg │ │ │ └── on100background.png │ │ └── css │ │ │ └── form.css │ ├── core │ │ └── images │ │ │ ├── footerlogo.png │ │ │ └── headerlogo.png │ └── date │ │ └── css │ │ └── date-root.css ├── date │ ├── events │ │ └── img │ │ │ ├── arsfest26-bg.png │ │ │ ├── metal-plate-alt.jpg │ │ │ └── arsfest26-bg-border.png │ └── date │ │ ├── js │ │ └── albin.js │ │ └── css │ │ └── date-root.css └── demo │ └── date │ ├── js │ └── albin.js │ └── css │ └── date-root.css ├── templates ├── common │ ├── social │ │ ├── socialIndex.html │ │ ├── harassment_admin_email.html │ │ └── harassment_success.html │ ├── ads │ │ └── adsindex.html │ ├── members │ │ ├── registration │ │ │ ├── password_reset_subject.txt │ │ │ ├── password_reset_email.html │ │ │ ├── password_reset_complete.html │ │ │ ├── password_change_done.html │ │ │ ├── login.html │ │ │ ├── password_reset_done.html │ │ │ ├── password_reset_confirm.html │ │ │ ├── registration_complete.html │ │ │ └── password_reset_form.html │ │ ├── alumni_signup_email_admin.html │ │ ├── acc_active_email.html │ │ └── alumni_signup_email.html │ ├── events │ │ ├── calendar.html │ │ ├── jsonwidget.html │ │ └── event_passcode.html │ ├── admin │ │ ├── archive │ │ │ └── collection │ │ │ │ └── change_list.html │ │ └── events │ │ │ └── delete_participants_confirmation.html │ ├── alumni │ │ ├── update_token_email.html │ │ ├── update_complete.html │ │ ├── check_email.html │ │ └── update_verify.html │ ├── billing │ │ ├── free_event_confirmation_email.txt │ │ └── invoice_email.txt │ ├── core │ │ ├── cookie_banner.html │ │ ├── 500.html │ │ ├── 404.html │ │ ├── 418.html │ │ └── footer.html │ ├── archive │ │ ├── upload.html │ │ ├── edit.html │ │ ├── exams_index.html │ │ ├── exam_detail.html │ │ └── document_index.html │ ├── staticpages │ │ └── staticpage.html │ ├── news │ │ ├── article.html │ │ └── author.html │ ├── ctf │ │ ├── index.html │ │ ├── detail.html │ │ └── flag_detail.html │ └── polls │ │ ├── index.html │ │ ├── results.html │ │ └── detail.html └── date │ └── news │ └── article.html ├── .dockerignore ├── .gitattributes ├── CHANGELOG.md ├── Dockerfile ├── .gitignore ├── requirements.txt ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── fix_request.md │ ├── config_request.md │ └── feature_request.md ├── workflows │ ├── docker_build.yaml │ └── web_startup.yaml └── dependabot.yml ├── scripts ├── resend_signup_emails.py ├── export_subscription_status.py ├── export_ctf_guesses.py ├── create_album.py └── merge_events.py ├── manage.py └── nginx.conf /ads/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /alumni/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /alumni/util.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ctf/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /date/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /events/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lucia/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /media/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /news/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /polls/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /social/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /archive/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /billing/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /core/urls/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /members/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /staticpages/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ads/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /core/settings/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ctf/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lucia/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /news/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /polls/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /publications/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /alumni/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /archive/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /archive/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /billing/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /core/.gitignore: -------------------------------------------------------------------------------- 1 | *.env 2 | /.env -------------------------------------------------------------------------------- /events/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /members/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /social/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/common/polls/css/style.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /core/settings/dependencies/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /publications/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/common/events/css/style.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/common/members/css/userinfo.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/common/news/css/article.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /staticpages/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/common/social/socialIndex.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/common/staticpages/css/staticpage.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/common/ads/adsindex.html: -------------------------------------------------------------------------------- 1 | {%for ad in ads%} 2 | {{ad}} 3 | {% endfor %} -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | *.env 2 | *.md 3 | media/ 4 | .git/ 5 | .idea/ 6 | .vscode/ 7 | venv/ 8 | -------------------------------------------------------------------------------- /ads/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /alumni/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /billing/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /ctf/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /lucia/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /social/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /alumni/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /billing/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- 1 | from .celery import app as celery_app 2 | 3 | __all__ = ('celery_app',) 4 | -------------------------------------------------------------------------------- /publications/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /static/biocum/core/css/ckeditor.css: -------------------------------------------------------------------------------- 1 | .ck-content { 2 | color: #000000 !important; 3 | } -------------------------------------------------------------------------------- /static/biocum/events/css/style.css: -------------------------------------------------------------------------------- 1 | .handelser { 2 | color: var(--textColorLightish) 3 | } -------------------------------------------------------------------------------- /static/common/core/css/ckeditor.css: -------------------------------------------------------------------------------- 1 | .ck-content { 2 | color: #000000 !important; 3 | } -------------------------------------------------------------------------------- /staticpages/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /templates/common/members/registration/password_reset_subject.txt: -------------------------------------------------------------------------------- 1 | Password reset - {{ ASSOCIATION_NAME }} -------------------------------------------------------------------------------- /ads/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AdsConfig(AppConfig): 5 | name = 'ads' 6 | -------------------------------------------------------------------------------- /ctf/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CtfConfig(AppConfig): 5 | name = 'ctf' 6 | -------------------------------------------------------------------------------- /templates/common/events/calendar.html: -------------------------------------------------------------------------------- 1 | 2 | {% block content %} 3 | 4 | {{ calendar }} 5 | 6 | {% endblock %} -------------------------------------------------------------------------------- /date/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CoreConfig(AppConfig): 5 | name = 'date' 6 | -------------------------------------------------------------------------------- /lucia/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class LuciaConfig(AppConfig): 5 | name = 'lucia' 6 | -------------------------------------------------------------------------------- /news/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class NewsConfig(AppConfig): 5 | name = 'news' 6 | -------------------------------------------------------------------------------- /polls/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class PollsConfig(AppConfig): 5 | name = 'polls' 6 | -------------------------------------------------------------------------------- /events/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class EventsConfig(AppConfig): 5 | name = 'events' 6 | -------------------------------------------------------------------------------- /members/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class MemberConfig(AppConfig): 5 | name = 'members' 6 | -------------------------------------------------------------------------------- /social/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class SocialConfig(AppConfig): 5 | name = 'social' 6 | -------------------------------------------------------------------------------- /templates/common/social/harassment_admin_email.html: -------------------------------------------------------------------------------- 1 | En ny trakasserianmälan har inkommit 2 | 3 | {{ harassment_url }} 4 | -------------------------------------------------------------------------------- /archive/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ArchiveConfig(AppConfig): 5 | name = 'archive' 6 | -------------------------------------------------------------------------------- /staticpages/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class StaticpagesConfig(AppConfig): 5 | name = 'staticpages' 6 | -------------------------------------------------------------------------------- /static/common/ctf/css/style.css: -------------------------------------------------------------------------------- 1 | .valid { 2 | color: rgb(54, 176, 95); 3 | } 4 | 5 | .invalid { 6 | color: rgb(176, 54, 89); 7 | } 8 | -------------------------------------------------------------------------------- /static/kk/core/images/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/kk/core/images/logo.ico -------------------------------------------------------------------------------- /static/kk/core/images/paroc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/kk/core/images/paroc.png -------------------------------------------------------------------------------- /static/kk/core/images/stars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/kk/core/images/stars.png -------------------------------------------------------------------------------- /static/on/events/img/onlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/on/events/img/onlogo.png -------------------------------------------------------------------------------- /billing/context_processors.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | 3 | 4 | def billing_context(_): 5 | return settings.BILLING_CONTEXT 6 | -------------------------------------------------------------------------------- /ads/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import AdUrl 4 | 5 | # Register your models here. 6 | admin.site.register(AdUrl) 7 | -------------------------------------------------------------------------------- /lucia/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Candidate 3 | 4 | # Register your models here. 5 | admin.site.register(Candidate) -------------------------------------------------------------------------------- /static/biocum/core/images/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/biocum/core/images/logo.ico -------------------------------------------------------------------------------- /static/common/core/images/HQKK_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/HQKK_2.png -------------------------------------------------------------------------------- /static/common/core/images/albin.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/albin.ico -------------------------------------------------------------------------------- /static/common/core/images/albin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/albin.png -------------------------------------------------------------------------------- /static/common/core/images/axel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/axel.png -------------------------------------------------------------------------------- /static/common/core/images/goldbg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/goldbg.png -------------------------------------------------------------------------------- /static/common/core/images/kklogo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/kklogo.ico -------------------------------------------------------------------------------- /static/common/core/images/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/logo.ico -------------------------------------------------------------------------------- /static/common/core/images/smoke.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/smoke.jpg -------------------------------------------------------------------------------- /static/common/core/images/stina.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/stina.png -------------------------------------------------------------------------------- /static/common/core/images/waves.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/waves.png -------------------------------------------------------------------------------- /static/kk/core/images/footerlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/kk/core/images/footerlogo.png -------------------------------------------------------------------------------- /static/kk/core/images/headerlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/kk/core/images/headerlogo.png -------------------------------------------------------------------------------- /static/kk/core/images/luciapic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/kk/core/images/luciapic.png -------------------------------------------------------------------------------- /static/kk/core/images/twinkling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/kk/core/images/twinkling.png -------------------------------------------------------------------------------- /static/on/core/images/footerlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/on/core/images/footerlogo.png -------------------------------------------------------------------------------- /static/on/core/images/headerlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/on/core/images/headerlogo.png -------------------------------------------------------------------------------- /static/common/core/css/footer.css: -------------------------------------------------------------------------------- 1 | .footer-img { 2 | max-height: 200px; 3 | width: auto; 4 | } 5 | 6 | footer section { 7 | margin: 25px; 8 | } -------------------------------------------------------------------------------- /static/common/core/images/fallback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/fallback.png -------------------------------------------------------------------------------- /static/kk/core/images/100logogold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/kk/core/images/100logogold.png -------------------------------------------------------------------------------- /static/kk/core/images/andritzLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/kk/core/images/andritzLogo.png -------------------------------------------------------------------------------- /static/on/events/img/onbackground.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/on/events/img/onbackground.jpg -------------------------------------------------------------------------------- /static/biocum/core/images/biocumlogo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/biocum/core/images/biocumlogo.jpeg -------------------------------------------------------------------------------- /static/biocum/core/images/footerlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/biocum/core/images/footerlogo.png -------------------------------------------------------------------------------- /static/biocum/core/images/headerlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/biocum/core/images/headerlogo.png -------------------------------------------------------------------------------- /static/common/core/images/albin_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/albin_black.png -------------------------------------------------------------------------------- /static/common/core/images/albin_gold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/albin_gold.png -------------------------------------------------------------------------------- /static/common/core/images/albin_gold2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/albin_gold2.png -------------------------------------------------------------------------------- /static/common/core/images/albin_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/albin_white.png -------------------------------------------------------------------------------- /static/common/core/images/cog_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/cog_black.png -------------------------------------------------------------------------------- /static/common/core/images/footerlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/footerlogo.png -------------------------------------------------------------------------------- /static/common/core/images/goldthingy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/goldthingy.png -------------------------------------------------------------------------------- /static/common/core/images/headerlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/headerlogo.png -------------------------------------------------------------------------------- /static/date/events/img/arsfest26-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/date/events/img/arsfest26-bg.png -------------------------------------------------------------------------------- /static/kk/core/images/axelstinagold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/kk/core/images/axelstinagold.png -------------------------------------------------------------------------------- /static/kk/core/images/ballong_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/kk/core/images/ballong_black.png -------------------------------------------------------------------------------- /static/kk/core/images/doktorprofessor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/kk/core/images/doktorprofessor.png -------------------------------------------------------------------------------- /static/kk/core/images/twinklingstars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/kk/core/images/twinklingstars.png -------------------------------------------------------------------------------- /static/on/events/img/on100background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/on/events/img/on100background.png -------------------------------------------------------------------------------- /static/common/core/images/albin_glitch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/albin_glitch.png -------------------------------------------------------------------------------- /static/common/core/images/albins-angels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/albins-angels.png -------------------------------------------------------------------------------- /static/common/core/images/glitchoverlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/glitchoverlay.png -------------------------------------------------------------------------------- /static/date/events/img/metal-plate-alt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/date/events/img/metal-plate-alt.jpg -------------------------------------------------------------------------------- /static/common/core/images/albin_black_pink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/albin_black_pink.png -------------------------------------------------------------------------------- /static/common/core/images/goldborderleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/goldborderleft.png -------------------------------------------------------------------------------- /static/common/core/images/goldborderright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/common/core/images/goldborderright.png -------------------------------------------------------------------------------- /static/date/events/img/arsfest26-bg-border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/date/events/img/arsfest26-bg-border.png -------------------------------------------------------------------------------- /static/kk/core/images/doktorprofessorgold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datateknologerna-vid-Abo-Akademi/date-website/HEAD/static/kk/core/images/doktorprofessorgold.png -------------------------------------------------------------------------------- /alumni/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AlumniConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'alumni' 7 | -------------------------------------------------------------------------------- /ads/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = 'ads' 6 | 7 | urlpatterns = [ 8 | path('', views.adsIndex, name='index'), 9 | ] 10 | -------------------------------------------------------------------------------- /billing/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class BillingConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'billing' 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Enforce LF endings for all files by default 2 | * text=auto eol=lf 3 | 4 | # Override to CRLF for Windows-specific scripts 5 | *.bat text eol=crlf 6 | *.ps1 text eol=crlf 7 | -------------------------------------------------------------------------------- /publications/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class PdfViewerConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'publications' 7 | -------------------------------------------------------------------------------- /static/kk/archive/css/document.css: -------------------------------------------------------------------------------- 1 | 2 | .form-group{ 3 | margin-bottom: 5px; 4 | } 5 | 6 | table tbody{ 7 | word-break: break-all; 8 | word-break: break-word; 9 | hyphens: auto; 10 | } -------------------------------------------------------------------------------- /staticpages/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import re_path 2 | 3 | from . import views 4 | 5 | app_name = 'staticpages' 6 | 7 | urlpatterns = [ 8 | re_path(r'^(?P[-\w]+)/$', views.StaticPageView.as_view(), name='page'), 9 | ] 10 | -------------------------------------------------------------------------------- /publications/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from . import views 3 | 4 | app_name = 'publications' 5 | 6 | urlpatterns = [ 7 | path('', views.pdf_list, name='pdf_list'), 8 | path('/', views.pdf_view, name='pdf_view'), 9 | ] -------------------------------------------------------------------------------- /social/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import Harassment, IgUrl, HarassmentEmailRecipient 4 | 5 | 6 | admin.site.register(IgUrl) 7 | admin.site.register(Harassment) 8 | admin.site.register(HarassmentEmailRecipient) 9 | -------------------------------------------------------------------------------- /templates/common/events/jsonwidget.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | 4 |

5 | {{ html }} 6 |
7 |
-------------------------------------------------------------------------------- /core/context_processors.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | 3 | 4 | def captcha_context(_): 5 | return {"CAPTCHA_SITE_KEY": settings.CAPTCHA_SITE_KEY} 6 | 7 | 8 | def apply_content_variables(_): 9 | return settings.CONTENT_VARIABLES 10 | -------------------------------------------------------------------------------- /ads/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | from . import models 4 | 5 | # Create your views here. 6 | 7 | 8 | def adsIndex(request): 9 | ads = models.AdUrl.objects.all() 10 | return render(request, 'ads/adsindex.html', {'ads': ads}) 11 | -------------------------------------------------------------------------------- /social/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = 'social' 6 | 7 | urlpatterns = [ 8 | path('', views.socialIndex, name='index'), 9 | path('harassment/', views.harassment_form, name='harassment'), 10 | ] 11 | -------------------------------------------------------------------------------- /templates/common/members/alumni_signup_email_admin.html: -------------------------------------------------------------------------------- 1 | {% autoescape off %} 2 | {{ alumni.name }} har registrerat sig till {{ ALUMNI_ASSOCIATION_NAME_SHORT }}. 3 | 4 | Full information: {{ SITE_URL }}/admin/members/alumnisignup/{{ alumni_id }} 5 | {% endautoescape %} -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All changes committed to `master` will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | -------------------------------------------------------------------------------- /static/biocum/core/css/footer.css: -------------------------------------------------------------------------------- 1 | .footer-img { 2 | max-height: 200px; 3 | width: auto; 4 | filter: brightness(0) invert(1); 5 | } 6 | 7 | footer section { 8 | margin: 25px; 9 | } 10 | 11 | footer { 12 | color: var(--textColorLightish); 13 | filter: brightness(0) invert(1); 14 | } -------------------------------------------------------------------------------- /static/kk/events/css/style.css: -------------------------------------------------------------------------------- 1 | .content-past { 2 | background-color: var(--secondaryColor); 3 | padding: 5px; 4 | border-radius: var(--defaultRadius); 5 | margin: 5px; 6 | } 7 | 8 | .content-past h4 { 9 | margin: 0; 10 | } 11 | 12 | .card { 13 | color: var(--textColorDark); 14 | } -------------------------------------------------------------------------------- /lucia/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = 'lucia' 6 | 7 | urlpatterns = [ 8 | path('', views.index, name='index'), 9 | path('candidates', views.candidates, name='candidates'), 10 | path('/', views.candidate, name='candidate'), 11 | ] 12 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.13-alpine 2 | RUN apk add --no-cache gcc musl-dev libffi-dev libldap libsasl libressl bash 3 | ENV PYTHONUNBUFFERED 1 4 | ENV PYTHONDONTWRITEBYTECODE 1 5 | RUN mkdir /code 6 | WORKDIR /code 7 | 8 | ADD requirements.txt /code/ 9 | RUN pip install -r requirements.txt 10 | 11 | ADD . /code/ 12 | -------------------------------------------------------------------------------- /core/celery.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from celery import Celery 4 | 5 | 6 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', f'core.settings.{os.environ.get("PROJECT_NAME", "date")}') 7 | 8 | app = Celery('date') 9 | 10 | app.config_from_object('django.conf:settings', namespace='CELERY') 11 | 12 | app.autodiscover_tasks() 13 | -------------------------------------------------------------------------------- /templates/common/admin/archive/collection/change_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'admin/change_list.html' %} 2 | {% load i18n admin_list %} 3 | 4 | {% block object-tools-items %} 5 | {{ block.super }} 6 |
  • 7 | {% trans 'Städa upp media' %} 8 |
  • 9 | {% endblock %} -------------------------------------------------------------------------------- /static/common/publications/js/main.js: -------------------------------------------------------------------------------- 1 | import { initPDFViewer } from './pdfViewer.js'; 2 | 3 | document.addEventListener('DOMContentLoaded', () => { 4 | const pdfUrl = document.getElementById('pdf-viewer').dataset.pdfUrl; 5 | const viewerElement = document.getElementById('pdf-viewer'); 6 | initPDFViewer(pdfUrl, viewerElement); 7 | }); -------------------------------------------------------------------------------- /events/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import re_path 2 | 3 | from . import feed, views 4 | 5 | app_name = 'events' 6 | 7 | urlpatterns = [ 8 | re_path(r'^$', views.IndexView.as_view(), name='index'), 9 | re_path(r'^(?P[-\w]+)/$', views.EventDetailView.as_view(), name='detail'), 10 | re_path(r'^feed$', feed.EventFeed(), name='feed'), 11 | ] 12 | -------------------------------------------------------------------------------- /static/common/members/css/functionary.css: -------------------------------------------------------------------------------- 1 | .btn-link-delete { 2 | color: var(--warning-darker); 3 | } 4 | 5 | .btn-link-delete:hover { 6 | color: var(--warning); 7 | } 8 | 9 | .card { 10 | background-color: transparent; 11 | border: 1px solid var(--secondaryColorDarker); 12 | } 13 | 14 | .card-body h5 { 15 | color: var(--helpTextLighter); 16 | } -------------------------------------------------------------------------------- /alumni/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = "alumni" 6 | 7 | urlpatterns = [ 8 | path('signup/', views.alumni_signup, name='alumni_signup'), 9 | path('update/', views.alumni_update_verify, name='alumni_update'), 10 | path('update//', views.alumni_update_form, name='alumni_update_with_token'), 11 | ] 12 | -------------------------------------------------------------------------------- /staticpages/context_processors.py: -------------------------------------------------------------------------------- 1 | from .models import StaticPageNav, StaticUrl 2 | 3 | 4 | def get_categories(context): 5 | categories = StaticPageNav.objects.all().order_by('nav_element') 6 | return {'categories': categories} 7 | 8 | 9 | def get_urls(context): 10 | urls = StaticUrl.objects.all().order_by('dropdown_element') 11 | return {'urls': urls} 12 | -------------------------------------------------------------------------------- /ads/models.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from django.db import models 4 | 5 | logger = logging.getLogger('date') 6 | 7 | 8 | # Create your models here. 9 | 10 | class AdUrl(models.Model): 11 | ad_url = models.URLField(max_length=255, blank=False) 12 | company_url = models.URLField(max_length=255, blank=True) 13 | 14 | def __str__(self): 15 | return self.ad_url 16 | -------------------------------------------------------------------------------- /polls/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = 'polls' 6 | urlpatterns = [ 7 | path('', views.IndexView.as_view(), name='index'), 8 | path('/', views.DetailView.as_view(), name='detail'), 9 | path('/results/', views.ResultsView.as_view(), name='results'), 10 | path('/vote/', views.vote, name='vote'), 11 | ] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | 4 | *.env 5 | !*example.env 6 | 7 | *.mo 8 | 9 | __pycache__/ 10 | *.py[cod] 11 | *$py.class 12 | core/static/ 13 | core/templates/static/admin 14 | core/templates/static/ckeditor 15 | 16 | media 17 | !media/.gitkeep 18 | 19 | #minio storage 20 | storage 21 | 22 | .DS_Store 23 | 24 | # Database dumps from backup 25 | *.bck 26 | 27 | # CSV files 28 | *.csv 29 | -------------------------------------------------------------------------------- /static/biocum/archive/css/document.css: -------------------------------------------------------------------------------- 1 | 2 | .form-group{ 3 | margin-bottom: 5px; 4 | } 5 | 6 | table tbody{ 7 | word-break: break-all; 8 | word-break: break-word; 9 | hyphens: auto; 10 | color: var(--textColorLightish); 11 | } 12 | 13 | .even{ 14 | background-color: var(--primaryColorLighter); 15 | } 16 | 17 | .odd{ 18 | background-color: var(--primaryColorLight); 19 | } -------------------------------------------------------------------------------- /static/common/archive/css/document.css: -------------------------------------------------------------------------------- 1 | 2 | .form-group{ 3 | margin-bottom: 5px; 4 | } 5 | 6 | table tbody{ 7 | word-break: break-all; 8 | word-break: break-word; 9 | hyphens: auto; 10 | color: var(--textColorLightish); 11 | } 12 | 13 | .even{ 14 | background-color: var(--primaryColorLighter); 15 | } 16 | 17 | .odd{ 18 | background-color: var(--primaryColorLight); 19 | } -------------------------------------------------------------------------------- /templates/common/alumni/update_token_email.html: -------------------------------------------------------------------------------- 1 | Bäste alumn, 2 | 3 | Du kan uppdatera dina uppgifter genom att klicka på länken nedan: 4 | 5 | {{ SITE_URL }}/alumni/update/{{ TOKEN }}/ 6 | Länken är giltig i 24 timmar. 7 | 8 | 9 | Ifall du vill ändra din e-postaddress, vänligen kontakta styrelsen på {{ ALUMNI_ASSOCIATION_EMAIL }}. 10 | 11 | Med vänliga hälsningar, 12 | {{ ALUMNI_ASSOCIATION_NAME }} 13 | -------------------------------------------------------------------------------- /members/tokens.py: -------------------------------------------------------------------------------- 1 | import six 2 | from django.contrib.auth.tokens import PasswordResetTokenGenerator 3 | 4 | 5 | class TokenGenerator(PasswordResetTokenGenerator): 6 | def _make_hash_value(self, user, timestamp): 7 | return ( 8 | six.text_type(user.pk) + six.text_type(timestamp) + 9 | six.text_type(user.username) 10 | ) 11 | 12 | 13 | account_activation_token = TokenGenerator() 14 | -------------------------------------------------------------------------------- /date/functions.py: -------------------------------------------------------------------------------- 1 | from django.utils.text import slugify 2 | 3 | 4 | def slugify_max(text, max_length=50): 5 | slug = slugify(text) 6 | if len(slug) <= max_length: 7 | return slug 8 | trimmed_slug = slug[:max_length].rsplit('-', 1)[0] 9 | if len(trimmed_slug) <= max_length: 10 | return trimmed_slug 11 | # First word is > max_length chars, so we have to break it 12 | return slug[:max_length] 13 | -------------------------------------------------------------------------------- /ctf/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from django.contrib.auth.decorators import login_required 3 | 4 | from . import views 5 | 6 | app_name = 'ctf' 7 | urlpatterns = [ 8 | path('', login_required(views.IndexView.as_view()), name='index'), 9 | path('', login_required(views.DetailView.as_view()), name='detail'), 10 | path('/', login_required(views.flag), name='flag_detail'), 11 | ] -------------------------------------------------------------------------------- /publications/fields.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.db import models 3 | from core.storage_backends import PublicMediaStorage 4 | 5 | class PublicFileField(models.FileField): 6 | def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs): 7 | if settings.USE_S3: 8 | storage = PublicMediaStorage() 9 | super().__init__(verbose_name, name, upload_to, storage, **kwargs) 10 | -------------------------------------------------------------------------------- /news/migrations/0005_remove_post_albins_angels.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.3 on 2024-04-02 02:05 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('news', '0004_category_post_category'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='post', 15 | name='albins_angels', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /static/biocum/core/css/error-view.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: black; 3 | } 4 | .container-soft{ 5 | background-color: var(--primaryColor); 6 | padding-top: 20vh; 7 | min-height: 60vh; 8 | color: var(--textColorLight); 9 | text-align: center; 10 | font-size: 1.9em; 11 | } 12 | 13 | .container-soft h1 { 14 | font-size: 4.5em; 15 | } 16 | .container-soft a:hover { 17 | color: yellow; 18 | transition: 0.3s; 19 | } 20 | -------------------------------------------------------------------------------- /static/common/core/css/error-view.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: black; 3 | } 4 | .container-soft{ 5 | background-color: var(--primaryColor); 6 | padding-top: 20vh; 7 | min-height: 60vh; 8 | color: var(--textColorLight); 9 | text-align: center; 10 | font-size: 1.9em; 11 | } 12 | 13 | .container-soft h1 { 14 | font-size: 4.5em; 15 | } 16 | .container-soft a:hover { 17 | color: yellow; 18 | transition: 0.3s; 19 | } 20 | -------------------------------------------------------------------------------- /events/routing.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.urls import re_path 3 | 4 | from . import consumers 5 | 6 | websocket_urlpatterns = [ 7 | re_path(r'ws/events/(?P\w+)/$', consumers.EventConsumer.as_asgi()), 8 | ] 9 | 10 | # TODO: Fix this in a better way 11 | if settings.PROJECT_NAME == 'on': 12 | websocket_urlpatterns = [ 13 | re_path(r'ws/(?P\w+)/$', consumers.EventConsumer.as_asgi()), 14 | ] 15 | -------------------------------------------------------------------------------- /templates/common/members/acc_active_email.html: -------------------------------------------------------------------------------- 1 | {% autoescape off %} 2 | En ny användare har registrerat sig: 3 | Epost: {{ user.email }} 4 | Namn: {{ user.first_name }} {{ user.last_name }} 5 | 6 | Aktivera användaren genom att klicka på länken nedan, 7 | http://{{ domain }}{% url 'members:activate' uidb64=uid token=token %} 8 | 9 | Eller granska och aktivera medlemmen via admin sidan, 10 | http://{{ domain }}/admin/members/member 11 | 12 | {% endautoescape %} -------------------------------------------------------------------------------- /archive/tables.py: -------------------------------------------------------------------------------- 1 | import django_tables2 as tables 2 | 3 | from .models import Document 4 | 5 | 6 | class DocumentTable(tables.Table): 7 | 8 | class Meta: 9 | model = Document 10 | template_name = "django_tables2/bootstrap.html" 11 | fields = ('collection', 'title', 'document') 12 | 13 | 14 | class SumColumn(tables.Column): 15 | 16 | def render_footer(self, bound_column, table): 17 | return sum(bound_column.accessor.reslove(row) for row in table.data) 18 | -------------------------------------------------------------------------------- /archive/fields.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.db import models 3 | 4 | from core.storage_backends import PublicMediaStorage 5 | 6 | 7 | class PublicFileField(models.FileField): 8 | def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs): 9 | storage = None 10 | if hasattr(settings, 'PUBLIC_MEDIA_LOCATION'): 11 | storage = PublicMediaStorage() 12 | super().__init__(verbose_name, name, upload_to, storage, **kwargs) -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django~=5.2 2 | asgiref 3 | django-ckeditor-5 4 | django-ckeditor 5 | requests 6 | django-environ 7 | psycopg2-binary 8 | pillow 9 | python-dateutil 10 | icalendar 11 | django-ical 12 | django-admin-ordering==0.16.1 13 | whitenoise 14 | channels 15 | channels_redis 16 | websocket_client 17 | six 18 | instaloader 19 | schedule 20 | boto3 21 | django-storages 22 | django-tables2 23 | django-filter 24 | django-bootstrap3 25 | django_cleanup 26 | daphne 27 | celery[redis] 28 | gunicorn 29 | gspread 30 | -------------------------------------------------------------------------------- /templates/common/billing/free_event_confirmation_email.txt: -------------------------------------------------------------------------------- 1 | {{ signup.event.title }} - Betalningsuppgifter 2 | 3 | Hej {{ signup.user }}, 4 | 5 | Du har anmält dig till {{ signup.event.title }}! 6 | 7 | Vänligen notera att sista avanmälningsdatum är {{ signup.event.sign_up_cancelling_deadline.date }}. 8 | 9 | OBS! Svara inte på detta mail utan frågor och dylikt skall riktas åt {{ ASSOCIATION_EMAIL }}! 10 | 11 | 12 | Med vänliga hälsningar, 13 | 14 | {{ ASSOCIATION_NAME_FULL }} 15 | {{ ASSOCIATION_EMAIL }} 16 | -------------------------------------------------------------------------------- /templates/common/social/harassment_success.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load static %} 3 | {% load i18n %} 4 | 5 | {% block title %}{{ article.title }}{% endblock %} 6 | {% block content %} 7 |
    8 |
    9 |
    10 |

    {% translate "Tack för att du rapporterade." %}

    11 |
    12 |
    13 |
    14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /ctf/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | import logging 4 | logger = logging.getLogger('date') 5 | 6 | class FlagForm(forms.Form): 7 | flag = forms.CharField(label='Insert Flag', max_length=100) 8 | 9 | def __init__(self, *args, **kwargs): 10 | super(FlagForm, self).__init__(*args, **kwargs) 11 | initial = kwargs.get('initial', {}) 12 | if initial and initial.get('disable_field'): 13 | self.fields[initial['disable_field']].disabled = True 14 | -------------------------------------------------------------------------------- /news/migrations/0002_post_albins_angels.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.12 on 2021-08-22 17:22 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('news', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='post', 15 | name='albins_angels', 16 | field=models.BooleanField(default=False, verbose_name='Albins Angels'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /events/migrations/0011_event_redirect_link.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.7 on 2023-11-30 19:59 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('events', '0010_event_captcha'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='event', 15 | name='redirect_link', 16 | field=models.URLField(blank=True, verbose_name='Redirect Link'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /members/migrations/0008_alumnisignup_operation.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.1 on 2024-08-11 17:27 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('members', '0007_functionaryrole_functionary'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='alumnisignup', 15 | name='operation', 16 | field=models.CharField(blank=True, max_length=200), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /events/migrations/0006_event_passcode.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.13 on 2022-05-28 12:47 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('events', '0005_event_members_only'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='event', 15 | name='passcode', 16 | field=models.CharField(blank=True, max_length=255, verbose_name='Passcode'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /events/migrations/0010_event_captcha.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.7 on 2023-11-16 14:48 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('events', '0009_alter_eventattendees_attendee_nr'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='event', 15 | name='captcha', 16 | field=models.BooleanField(default=False, verbose_name='Captcha'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /templates/common/members/registration/password_reset_email.html: -------------------------------------------------------------------------------- 1 | {% autoescape off %} 2 | För att inleda lösenordsåterställningen för kontot {{ user.get_username }}, 3 | klicka på länken nedan: 4 | 5 | {{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %} 6 | 7 | Ifall det inte går att klicka på länken, vänligen kopiera och klistra in länken i din webbläsare. 8 | 9 | Om ytterligare frågor uppstår, vänligen kontakta styrelsen. 10 | 11 | 12 | Mvh, 13 | {{ ASSOCIATION_NAME_FULL }} 14 | {% endautoescape %} -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: 'bug:' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | 12 | A clear and concise description of what the bug is. 13 | 14 | 15 | **To Reproduce** 16 | 17 | Steps to reproduce the behavior: 18 | 19 | 20 | **Screenshots** 21 | 22 | If applicable, add screenshots to help explain your problem. 23 | 24 | 25 | **Additional context** 26 | Add any other context about the problem here. 27 | -------------------------------------------------------------------------------- /members/migrations/0011_delete_alumniemailrecipient_delete_alumnisignup.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.2 on 2025-05-18 16:00 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('members', '0010_member_year_of_admission'), 10 | ] 11 | 12 | operations = [ 13 | migrations.DeleteModel( 14 | name='AlumniEmailRecipient', 15 | ), 16 | migrations.DeleteModel( 17 | name='AlumniSignUp', 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /social/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from .models import Harassment 3 | 4 | 5 | class HarassmentForm(forms.ModelForm): 6 | class Meta: 7 | model = Harassment 8 | fields = ['email', 'message'] 9 | 10 | def __init__(self, *args, **kwargs): 11 | super(HarassmentForm, self).__init__(*args, **kwargs) 12 | # Add a class to the 'message' field 13 | self.fields['message'].widget.attrs.update({'class': 'form-control'}) 14 | self.fields['email'].widget.attrs.update({'class': 'form-control'}) -------------------------------------------------------------------------------- /core/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for date project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/ 8 | """ 9 | # update 10 | 11 | import os 12 | 13 | from django.core.wsgi import get_wsgi_application 14 | 15 | proj_name = os.environ.get("PROJECT_NAME", "date") 16 | 17 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', f'core.settings.{proj_name}') 18 | 19 | application = get_wsgi_application() 20 | -------------------------------------------------------------------------------- /events/migrations/0005_event_members_only.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.13 on 2022-03-05 16:01 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('events', '0004_auto_20220217_1841'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='event', 15 | name='members_only', 16 | field=models.BooleanField(default=False, verbose_name='Kräv inloggning för innehåll'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /archive/migrations/0005_collection_hide_for_gulis.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.13 on 2022-08-25 15:37 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('archive', '0004_auto_20210713_1729'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='collection', 15 | name='hide_for_gulis', 16 | field=models.BooleanField(default=False, verbose_name='Göm för gulisar'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /billing/migrations/0002_alter_eventbillingconfiguration_price_selector.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.1.4 on 2025-01-19 17:44 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('billing', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='eventbillingconfiguration', 15 | name='price_selector', 16 | field=models.CharField(blank=True, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /events/migrations/0009_alter_eventattendees_attendee_nr.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.7 on 2023-03-04 22:32 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('events', '0008_auto_20230215_2036'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='eventattendees', 15 | name='attendee_nr', 16 | field=models.PositiveSmallIntegerField(blank=True, verbose_name='#'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /members/migrations/0005_alter_alumniemailrecipient_options.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.2 on 2023-06-25 20:43 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('members', '0004_alumniemailrecipient'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='alumniemailrecipient', 15 | options={'verbose_name': 'Emailmottagare för ARG', 'verbose_name_plural': 'Emailmottagare för ARG'}, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /staticpages/migrations/0003_staticpage_members_only.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.13 on 2022-03-05 16:01 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staticpages', '0002_auto_20200805_1027'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='staticpage', 15 | name='members_only', 16 | field=models.BooleanField(default=False, verbose_name='Kräv inloggning'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /archive/filters.py: -------------------------------------------------------------------------------- 1 | import django_filters 2 | 3 | from .models import Document, get_collections_of_type 4 | 5 | class DocumentFilter(django_filters.FilterSet): 6 | collection = django_filters.ModelChoiceFilter(queryset=get_collections_of_type('Documents')) 7 | 8 | class Meta: 9 | model = Document 10 | fields = { 11 | 'collection': ['exact'], 12 | 'title': ['contains'], 13 | } 14 | 15 | 16 | class ExamFilter(django_filters.FilterSet): 17 | 18 | class Meta: 19 | model = Document 20 | fields = {} -------------------------------------------------------------------------------- /news/migrations/0003_alter_post_content.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.2 on 2024-03-03 20:18 2 | 3 | import django_ckeditor_5.fields 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('news', '0002_post_albins_angels'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='post', 16 | name='content', 17 | field=django_ckeditor_5.fields.CKEditor5Field(blank=True, verbose_name='Innehåll'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /static/date/date/js/albin.js: -------------------------------------------------------------------------------- 1 | 2 | function loadSvgIntoContainer(svgUrl, containerId) { 3 | fetch(svgUrl) 4 | .then(response => response.text()) 5 | .then(svgContent => { 6 | const container = document.getElementById(containerId); 7 | if(container) { 8 | container.innerHTML = svgContent; 9 | } 10 | }) 11 | .catch(error => console.error('Error loading the SVG:', error)); 12 | } 13 | 14 | // Use the static path to the SVG and the ID of the container 15 | loadSvgIntoContainer('/static/date/svg/albin.svg', 'albin-svg-container'); 16 | -------------------------------------------------------------------------------- /static/demo/date/js/albin.js: -------------------------------------------------------------------------------- 1 | 2 | function loadSvgIntoContainer(svgUrl, containerId) { 3 | fetch(svgUrl) 4 | .then(response => response.text()) 5 | .then(svgContent => { 6 | const container = document.getElementById(containerId); 7 | if(container) { 8 | container.innerHTML = svgContent; 9 | } 10 | }) 11 | .catch(error => console.error('Error loading the SVG:', error)); 12 | } 13 | 14 | // Use the static path to the SVG and the ID of the container 15 | loadSvgIntoContainer('/static/date/svg/albin.svg', 'albin-svg-container'); 16 | -------------------------------------------------------------------------------- /events/migrations/0012_eventregistrationform_choice_number.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.8 on 2023-12-14 17:11 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('events', '0011_event_redirect_link'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='eventregistrationform', 15 | name='choice_number', 16 | field=models.PositiveSmallIntegerField(blank=True, default=0, verbose_name='#'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /lucia/migrations/0003_alter_candidate_content.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.3 on 2024-04-08 21:01 2 | 3 | import django_ckeditor_5.fields 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('lucia', '0002_auto_20201129_1238'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='candidate', 16 | name='content', 17 | field=django_ckeditor_5.fields.CKEditor5Field(blank=True, verbose_name='Innehåll'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /members/migrations/0003_auto_20200109_1909.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.15 on 2020-01-09 17:09 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('members', '0002_subscription_subscriptionpayment'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='subscriptionpayment', 15 | name='date_expires', 16 | field=models.DateField(blank=True, default=None, null=True, verbose_name='Upphör'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/fix_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Fix request 3 | about: I've found something that works but could be fixed 4 | title: 'fix:' 5 | labels: fix 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your fix request related to a problem?** 11 | 12 | Consider creating a bug report. You can also describe why it should be a fix and not a bug. 13 | 14 | 15 | **Describe the solution you'd like** 16 | 17 | A clear and concise description of what you want to happen. 18 | 19 | 20 | **Additional context** 21 | 22 | Add any other context or screenshots about the feature request here. 23 | -------------------------------------------------------------------------------- /members/migrations/0010_member_year_of_admission.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.1.4 on 2025-04-22 15:31 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('members', '0009_membershiptype_alter_member_membership_type'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='member', 15 | name='year_of_admission', 16 | field=models.IntegerField(blank=True, null=True, verbose_name='Inskrivningsår'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /archive/migrations/0002_auto_20200109_1909.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.15 on 2020-01-09 17:09 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('archive', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='document', 16 | name='collection', 17 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archive.Collection'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /events/migrations/0013_alter_event_content.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.2 on 2024-03-03 20:18 2 | 3 | import django_ckeditor_5.fields 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('events', '0012_eventregistrationform_choice_number'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='event', 16 | name='content', 17 | field=django_ckeditor_5.fields.CKEditor5Field(blank=True, verbose_name='Innehåll'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Configuration request 3 | about: I need something configured 4 | title: 'config:' 5 | labels: configuration 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the configuration you need** 11 | 12 | A clear and concise description of what you need configured. 13 | 14 | 15 | **Describe when you need this to be configured** 16 | 17 | A date or time frame when you need this configuration to be completed. 18 | 19 | 20 | **Additional context** 21 | 22 | Add any other context or screenshots about the configuration request here. 23 | 24 | 25 | -------------------------------------------------------------------------------- /events/migrations/0014_alter_event_sign_up_max_participants.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.1.1 on 2024-11-19 15:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('events', '0013_alter_event_content'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='event', 15 | name='sign_up_max_participants', 16 | field=models.IntegerField(default=0, verbose_name='Maximal antal deltagare (0 för ingen begränsning)'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /staticpages/migrations/0004_alter_staticpage_content.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.2 on 2024-03-03 20:18 2 | 3 | import django_ckeditor_5.fields 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('staticpages', '0003_staticpage_members_only'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='staticpage', 16 | name='content', 17 | field=django_ckeditor_5.fields.CKEditor5Field(blank=True, verbose_name='Innehåll'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: 'feat:' 5 | labels: feature 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | 12 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 13 | 14 | 15 | **Describe the solution you'd like** 16 | 17 | A clear and concise description of what you want to happen. 18 | 19 | 20 | **Additional context** 21 | 22 | Add any other context or screenshots about the feature request here. 23 | -------------------------------------------------------------------------------- /templates/common/alumni/update_complete.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% load static %} 4 | {% load i18n %} 5 | 6 | {% block title %} 7 | {{ ALUMNI_ASSOCIATION_NAME_SHORT }} - Uppdatering slutförd 8 | {% endblock %} 9 | 10 | {% block content %} 11 | 12 | 13 | 14 | 15 | 16 |
    17 |
    18 |

    Din information har uppdaterats

    19 | 20 |

    Din information är nu uppdaterad

    21 |
    22 |
    23 | 24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /staticpages/migrations/0008_staticurl_logged_in_only.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.4 on 2024-08-08 18:30 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staticpages', '0007_staticpagenav_url_staticpagenav_use_category_url'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='staticurl', 15 | name='logged_in_only', 16 | field=models.BooleanField(default=False, verbose_name='Visa endast åt inloggade användare'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /static/common/events/css/detail.css: -------------------------------------------------------------------------------- 1 | table { 2 | border-collapse: collapse; 3 | } 4 | 5 | .event-full{ 6 | color: var(--helpText); 7 | } 8 | 9 | th { 10 | padding-bottom: 1rem; 11 | } 12 | 13 | th, td { 14 | text-align: left; 15 | padding: 8px; 16 | } 17 | 18 | th:first-child, td:first-child { 19 | border-radius: var(--defaultRadius) 0 0 var(--defaultRadius); 20 | } 21 | 22 | th:last-child, td:last-child { 23 | border-radius: 0 var(--defaultRadius) var(--defaultRadius) 0; 24 | } 25 | 26 | tr:hover { 27 | transition: 200ms; 28 | background-color: var(--primaryColorLighter); 29 | } -------------------------------------------------------------------------------- /templates/common/core/cookie_banner.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | -------------------------------------------------------------------------------- /staticpages/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import redirect, render, get_object_or_404 2 | from django.views import View 3 | 4 | import staticpages.models 5 | from . import models 6 | 7 | 8 | class StaticPageView(View): 9 | def get(self, request, slug): 10 | page = get_object_or_404(models.StaticPage, slug=slug) 11 | show_content = not page.members_only or (page.members_only and request.user.is_authenticated) 12 | if show_content: 13 | return render(request, 'staticpages/staticpage.html', {'page': page, 'show_content': show_content}) 14 | else: 15 | return redirect('/members/login') 16 | -------------------------------------------------------------------------------- /core/settings/test.py: -------------------------------------------------------------------------------- 1 | from .date import * # noqa 2 | 3 | # Use in-memory sqlite database for tests 4 | DATABASES = { 5 | 'default': { 6 | 'ENGINE': 'django.db.backends.sqlite3', 7 | 'NAME': ':memory:', 8 | } 9 | } 10 | 11 | CHANNEL_LAYERS = { 12 | 'default': { 13 | 'BACKEND': 'channels.layers.InMemoryChannelLayer', 14 | } 15 | } 16 | 17 | # Use local memory cache to avoid Redis dependency during tests 18 | CACHES = { 19 | 'default': { 20 | 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 21 | } 22 | } 23 | 24 | PASSWORD_HASHERS = ['django.contrib.auth.hashers.MD5PasswordHasher'] 25 | -------------------------------------------------------------------------------- /members/migrations/0004_alumniemailrecipient.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.1 on 2023-05-08 17:01 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('members', '0003_auto_20200109_1909'), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='AlumniEmailRecipient', 15 | fields=[ 16 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 17 | ('recipient_email', models.EmailField(max_length=256)), 18 | ], 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /ads/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.14 on 2021-04-27 18:00 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='AdUrl', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('ad_url', models.URLField(max_length=255)), 19 | ('company_url', models.URLField(blank=True, max_length=255)), 20 | ], 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /core/routing.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from channels.auth import AuthMiddlewareStack 4 | from channels.routing import ProtocolTypeRouter, URLRouter 5 | from django.core.asgi import get_asgi_application 6 | 7 | import events.routing 8 | 9 | proj_name = os.environ.get("PROJECT_NAME", "date") 10 | 11 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', f'core.settings.{proj_name}') 12 | 13 | application = ProtocolTypeRouter({ 14 | 'http': get_asgi_application(), 15 | 16 | # (http->django views is added by default) 17 | 'websocket': AuthMiddlewareStack( 18 | URLRouter( 19 | events.routing.websocket_urlpatterns 20 | ) 21 | ), 22 | }) 23 | -------------------------------------------------------------------------------- /templates/common/alumni/check_email.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% load static %} 4 | {% load i18n %} 5 | 6 | {% block title %} 7 | {{ ALUMNI_ASSOCIATION_NAME_SHORT }} - Verifiera din e-postadress 8 | {% endblock %} 9 | 10 | {% block content %} 11 | 12 | 13 | 14 | 15 | 16 |
    17 |
    18 |

    Verifiera din E-Postaddress

    19 | 20 |

    Du har nu fått ett e-postmeddelande till din e-post om den finns i registret. Kom ihåg att också kolla skräpposten.

    21 |
    22 |
    23 | 24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /templates/common/core/500.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load static %} 3 | {% load i18n %} 4 | {% block title %}{{ event.title }}{% endblock %} 5 | 6 | {% block content %} 7 | 8 | 9 | Page not found 10 | 11 | 12 | 13 |
    14 |
    15 |

    - 500 -

    16 |

    {% trans 'Serverfel, försök igen om en stund' %}

    17 |

    {% trans 'Till startsidan' %}

    18 |
    19 |
    20 | 21 | 22 | {% endblock %} -------------------------------------------------------------------------------- /social/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.14 on 2021-04-27 18:00 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='IgUrl', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('url', models.CharField(max_length=255, verbose_name='URL')), 19 | ('shortcode', models.CharField(max_length=255, verbose_name='SHORTCODE')), 20 | ], 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /core/storage_backends.py: -------------------------------------------------------------------------------- 1 | from storages.backends.s3boto3 import S3Boto3Storage 2 | from django.conf import settings 3 | 4 | 5 | class StaticStorage(S3Boto3Storage): 6 | location = 'static' 7 | default_acl = 'public-read' 8 | 9 | 10 | class PrivateMediaStorage(S3Boto3Storage): 11 | location = settings.PRIVATE_MEDIA_LOCATION 12 | default_acl = 'private' 13 | file_overwrite = False 14 | 15 | 16 | class PublicMediaStorage(S3Boto3Storage): 17 | location = settings.PUBLIC_MEDIA_LOCATION 18 | default_acl = 'public-read' 19 | file_overwrite = False 20 | querystring_auth = False 21 | 22 | 23 | class PublicCKEditorStorage(PublicMediaStorage): 24 | location = settings.PUBLIC_MEDIA_LOCATION + '/ckeditor' 25 | -------------------------------------------------------------------------------- /news/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, re_path 2 | 3 | from . import feed, views 4 | 5 | app_name = 'news' 6 | 7 | urlpatterns = [ 8 | path('feed/', feed.LatestPosts()), 9 | # Keep this regex to support legacy usernames that may contain spaces or special characters 10 | re_path( 11 | r'author/(?P[\w\s.@\u00C0-\u00FF\u00C5\u00C4\u00D6\u00E5\u00E4\u00F6-]+)/$', 12 | views.author, 13 | name='author', 14 | ), 15 | path('', views.index, name='index'), 16 | path('/', views.category_index, name='aa_index'), 17 | path('articles//', views.article, name='detail'), 18 | path('//', views.category_article, name='detail'), 19 | ] 20 | -------------------------------------------------------------------------------- /templates/common/archive/upload.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load app_filters %} 3 | {% load static %} 4 | {% load i18n %} 5 | {% block title %}{% trans "Ladda upp bilder" %}{% endblock %} 6 | 7 | {% block content %} 8 | 9 | 10 | 11 |
    12 |
    13 | {% csrf_token %} 14 | {{ picture_form.as_p }} 15 | 16 |
    17 |

    Return

    18 |
    19 | {% endblock %} 20 | 21 | -------------------------------------------------------------------------------- /templates/common/members/registration/password_reset_complete.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load static %} 3 | 4 | {% load i18n %} 5 | {% load static %} 6 | {% block title %}{% trans 'Glömt lösenord' %}{% endblock %} 7 | {% block content %} 8 | 9 | 10 | 11 |
    12 |
    13 |

    14 | {% trans 'Ditt lösenord har updaterats' %}.
    {% trans 'Du kan nu logga in som vanligt' %} 15 |

    16 |

    {% trans 'Logga in' %}

    17 |
    18 |
    19 | {% endblock %} -------------------------------------------------------------------------------- /templates/common/members/registration/password_change_done.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load static %} 3 | {% load i18n %} 4 | {% block title %}{% trans 'Password change successful' %}{% endblock %} 5 | {% block content %} 6 | 7 | 8 | 9 |
    10 |
    11 |
    12 |

    {% trans 'Password change successful' %}

    13 |

    {% translate 'Your password was changed.' %}

    14 |
    15 |
    16 |
    17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /ctf/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Ctf, Flag, Guess 3 | 4 | 5 | # Register your models here. 6 | 7 | 8 | class FlagInline(admin.TabularInline): 9 | model = Flag 10 | can_delete = True 11 | extra = 0 12 | exclude = ('solved_date',) 13 | 14 | 15 | @admin.register(Ctf) 16 | class CtfAdmin(admin.ModelAdmin): 17 | model = Ctf 18 | save_on_top = True 19 | inlines = [ 20 | FlagInline, 21 | ] 22 | 23 | 24 | @admin.register(Guess) 25 | class GuessAdmin(admin.ModelAdmin): 26 | list_display = ('ctf', 'flag', 'user', 'guess', 'timestamp', 'correct') 27 | list_filter = ('ctf', 'flag', 'user', 'timestamp', 'correct') 28 | search_fields = ('ctf__title', 'flag__title', 'user__username', 'guess') 29 | -------------------------------------------------------------------------------- /lucia/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | import logging 3 | 4 | from . import models 5 | 6 | logger = logging.getLogger('date') 7 | 8 | # Create your views here. 9 | def index(request): 10 | candidates = models.Candidate.objects.all() 11 | context = {'candidates': candidates} 12 | return render(request, 'lucia/index.html', context) 13 | 14 | def candidates(request): 15 | candidates = models.Candidate.objects.all() 16 | context = {'candidates': candidates} 17 | return render(request, 'lucia/candidates.html', context) 18 | 19 | def candidate(request, slug): 20 | candidate = models.Candidate.objects.get(slug=slug, published=True) 21 | context = {'candidate': candidate} 22 | return render(request, 'lucia/candidate.html', context) -------------------------------------------------------------------------------- /social/migrations/0002_harassment.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.6 on 2023-10-21 22:01 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('social', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='Harassment', 15 | fields=[ 16 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 17 | ('email', models.EmailField(blank=True, max_length=255, null=True, verbose_name='Email')), 18 | ('message', models.TextField(max_length=1500, verbose_name='Beskrivning av händelsen')), 19 | ], 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /ctf/migrations/0003_alter_ctf_content_alter_flag_clues.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.2 on 2024-03-03 20:18 2 | 3 | import django_ckeditor_5.fields 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('ctf', '0002_guess'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='ctf', 16 | name='content', 17 | field=django_ckeditor_5.fields.CKEditor5Field(blank=True, verbose_name='Innehåll'), 18 | ), 19 | migrations.AlterField( 20 | model_name='flag', 21 | name='clues', 22 | field=django_ckeditor_5.fields.CKEditor5Field(blank=True, verbose_name='Clue'), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /lucia/migrations/0002_auto_20201129_1238.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.3 on 2020-11-29 10:38 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('lucia', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='candidate', 15 | name='poll_url', 16 | field=models.URLField(default='', max_length=255, verbose_name='Poll URL'), 17 | preserve_default=False, 18 | ), 19 | migrations.AlterField( 20 | model_name='candidate', 21 | name='img_url', 22 | field=models.URLField(blank=True, max_length=255, verbose_name='Bild URL'), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /events/migrations/0002_auto_20200116_2218.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.15 on 2020-01-16 20:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('events', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='event', 15 | name='sign_up_cancelling_deadline', 16 | field=models.DateTimeField(blank=True, null=True, verbose_name='Avanmälningen stängs'), 17 | ), 18 | migrations.AlterField( 19 | model_name='event', 20 | name='sign_up_deadline', 21 | field=models.DateTimeField(blank=True, null=True, verbose_name='Anmälningen stängs'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /templates/common/members/alumni_signup_email.html: -------------------------------------------------------------------------------- 1 | {% autoescape off %} 2 | Bäste Alumn, 3 | 4 | 5 | Tack för din anmälan till {{ ALUMNI_ASSOCIATION_NAME }}! 6 | 7 | För att slutföra processen, vänligen betala medlemsavgiften på 85 euro till: 8 | 9 | Mottagare: Kemistklubben vid Åbo Akademi rf 10 | Kontonummer: FI38 6601 0001 0479 43 11 | BIC: AABAFI22 12 | Referens: {{ reference }} 13 | 14 | Efter att betalningen är gjord kommer du att bli tillagd i vårt medlemsregister. 15 | 16 | Kom även ihåg att hålla dina kontaktuppgifter uppdaterade via {{ SITE_URL }} så att vi alltid når dig. 17 | 18 | Mvh, 19 | {{ ALUMNI_ASSOCIATION_NAME }} 20 | 21 | Detta är ett automatiskt meddelande och skall ej besvaras. 22 | 23 | {% endautoescape %} -------------------------------------------------------------------------------- /news/feed.py: -------------------------------------------------------------------------------- 1 | from django.contrib.syndication.views import Feed 2 | from django.urls import reverse 3 | 4 | from . import models 5 | 6 | 7 | class LatestPosts(Feed): 8 | title = "DaTe nyheter" 9 | link = "/news/feed" 10 | description = "DaTe nyhetsflöde" 11 | 12 | def items(self): 13 | return models.Post.objects.order_by('modified_time')[:10] 14 | 15 | def get_description(self, item): 16 | return item.content[:50] + "..." 17 | 18 | def get_author(self, item): 19 | return item.author 20 | 21 | def get_created_time(self, item): 22 | return item.created_time 23 | 24 | def get_modified_time(self, item): 25 | return item.modified_time 26 | 27 | def item_link(self, item): 28 | return reverse('news:detail', args=[item.slug]) 29 | -------------------------------------------------------------------------------- /alumni/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.2 on 2025-04-18 14:13 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='AlumniEmailRecipient', 16 | fields=[ 17 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('recipient_email', models.EmailField(max_length=256)), 19 | ], 20 | options={ 21 | 'verbose_name': 'Emailmottagare för ÅAATK', 22 | 'verbose_name_plural': 'Emailmottagare för ÅAATK', 23 | }, 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /members/backends.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from django.contrib.auth import get_user_model 4 | from django.contrib.auth.backends import ModelBackend 5 | 6 | User = get_user_model() 7 | 8 | 9 | logger = logging.getLogger('date') 10 | 11 | 12 | class AuthBackend(ModelBackend): 13 | 14 | def authenticate(self, request, username=None, password=None, **kwargs): 15 | try: 16 | user = User.objects.get(email=username) 17 | if user.check_password(password): 18 | return user 19 | except User.DoesNotExist: 20 | try: 21 | user = User.objects.get(username=username) 22 | if user.check_password(password): 23 | return user 24 | except User.DoesNotExist: 25 | return None 26 | -------------------------------------------------------------------------------- /staticpages/migrations/0007_staticpagenav_url_staticpagenav_use_category_url.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.3 on 2024-04-08 10:36 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staticpages', '0006_remove_staticpage_category_and_more'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='staticpagenav', 15 | name='url', 16 | field=models.CharField(blank=True, max_length=200, verbose_name='Url'), 17 | ), 18 | migrations.AddField( 19 | model_name='staticpagenav', 20 | name='use_category_url', 21 | field=models.BooleanField(default=False, verbose_name='Använd kategorins URL'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /templates/common/members/registration/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% load i18n %} 4 | {% load static %} 5 | {% block title %}Log in{% endblock %} 6 | {% block content %} 7 | 8 | 9 | 10 | 22 | {% endblock %} -------------------------------------------------------------------------------- /scripts/resend_signup_emails.py: -------------------------------------------------------------------------------- 1 | import time 2 | import datetime 3 | import sys 4 | import os 5 | import django 6 | 7 | import csv 8 | 9 | 10 | sys.path.append("/code") 11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings.on") 12 | django.setup() 13 | 14 | from billing.models import EventInvoice 15 | from billing.util import send_event_invoice 16 | 17 | emails_filename = sys.argv[1] 18 | 19 | with open(emails_filename, 'r') as file: 20 | reader = csv.DictReader(file) 21 | emails = [row['email_address'] for row in reader] 22 | 23 | invoices = EventInvoice.objects.filter( 24 | participant__email__in=emails, due_date__gte=datetime.datetime.now()).prefetch_related("participant") 25 | for invoice in invoices: 26 | send_event_invoice(invoice.participant, invoice) 27 | time.sleep(1.1) 28 | -------------------------------------------------------------------------------- /staticpages/migrations/0006_remove_staticpage_category_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.3 on 2024-04-02 19:25 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('staticpages', '0005_staticpage_nav_to_url'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='staticpage', 15 | name='category', 16 | ), 17 | migrations.RemoveField( 18 | model_name='staticpage', 19 | name='dropdown_element', 20 | ), 21 | migrations.AlterField( 22 | model_name='staticurl', 23 | name='dropdown_element', 24 | field=models.PositiveSmallIntegerField(blank=True, verbose_name='#'), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /templates/common/core/404.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load static %} 3 | {% load i18n %} 4 | {% block title %}{{ event.title }}{% endblock %} 5 | 6 | {% block content %} 7 | 8 | 9 | 10 | Page not found 11 | 12 | 13 | 14 | 15 |
    16 |
    17 | {% if error_msg %} 18 |

    {{ error_msg }}

    19 | {% else %} 20 |

    - 404 -

    21 |

    {% trans 'Sidan hittades inte' %}

    22 | {% endif %} 23 |

    {% trans 'Till startsidan' %}

    24 |
    25 |
    26 | 27 | 28 | {% endblock %} -------------------------------------------------------------------------------- /social/migrations/0003_harassmentemailrecipient.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.4 on 2024-04-10 16:39 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('social', '0002_harassment'), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='HarassmentEmailRecipient', 15 | fields=[ 16 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 17 | ('recipient_email', models.EmailField(max_length=320)), 18 | ], 19 | options={ 20 | 'verbose_name': 'Emailmottagare för Trakasserianmälan', 21 | 'verbose_name_plural': 'Emailmottagare för Trakasserianmälan', 22 | }, 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /static/common/archive/css/style.css: -------------------------------------------------------------------------------- 1 | .card { 2 | transition: .3s; 3 | } 4 | 5 | .card:hover { 6 | transform: scale(1.01); 7 | } 8 | 9 | .card .fa-chevron-right{ 10 | transition: .3s; 11 | } 12 | 13 | .card:hover .fa-chevron-right{ 14 | transform: translateX(10px); 15 | } 16 | 17 | .grid-item .card { 18 | background: var(--primaryColorLight); 19 | } 20 | 21 | /* Masonary image gallery */ 22 | .grid { 23 | margin: 0 auto; 24 | max-width: 100%; 25 | } 26 | 27 | /* fluid 5 columns */ 28 | .grid-sizer, 29 | .grid-item { 30 | width: 97%; 31 | margin: 5px; 32 | text-align: center; 33 | } 34 | 35 | .grid-item .card{ 36 | border-radius: var(--defaultRadius); 37 | width: 100% !important; 38 | } 39 | 40 | @media only screen and (min-width: 768px) { 41 | .grid-sizer, 42 | .grid-item { 43 | width: 32.5%; 44 | } 45 | } -------------------------------------------------------------------------------- /templates/common/billing/invoice_email.txt: -------------------------------------------------------------------------------- 1 | {{ signup.event.title }} - Betalningsuppgifter 2 | 3 | Hej {{ signup.user }}, 4 | 5 | Du har anmält dig till {{ signup.event.title }}, här är dina betalningsuppgifter: 6 | 7 | Mottagare: {{ INVOICE_RECIPIENT }} 8 | IBAN: {{ IBAN }} 9 | BIC: {{ BIC }} 10 | Referensnummer: {{ invoice.reference_number }} 11 | Summa: {{ invoice.amount }} {{ invoice.currency }} 12 | Sista betalningsdag: {{ invoice.due_date }} 13 | 14 | Vid betalning bör referensnummer anges. 15 | 16 | Vänligen notera att sista avanmälningsdatum är {{ signup.event.sign_up_cancelling_deadline.date }}. Om du avanmäler dig efter detta datum kommer du att debiteras fullt pris. 17 | 18 | OBS! Svara inte på detta mail utan frågor och dylikt skall riktas åt {{ ASSOCIATION_EMAIL }}! 19 | 20 | 21 | Med vänliga hälsningar, 22 | 23 | {{ ASSOCIATION_NAME_FULL }} 24 | {{ ASSOCIATION_EMAIL }} 25 | -------------------------------------------------------------------------------- /templates/common/staticpages/staticpage.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load static %} 3 | {% load i18n %} 4 | {% block title %}{{page.title}}{% endblock %} 5 | 6 | {% block content %} 7 | 8 | 9 | 10 | 11 | 12 |
    13 |
    14 | {% if show_content %} 15 |
    16 | {{page.content | safe}} 17 |
    18 | {% else %} 19 |
    20 |
    {% trans "Logga in för att se den här sidan" %}
    21 |
    22 | {% endif %} 23 |
    24 |
    25 | 26 | {% endblock %} 27 | -------------------------------------------------------------------------------- /scripts/export_subscription_status.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | from io import StringIO 4 | 5 | import django 6 | import csv 7 | 8 | sys.path.append("/code") 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings.date") 10 | django.setup() 11 | 12 | from members.models import SubscriptionPayment, Member 13 | 14 | with StringIO() as csvfile: 15 | fieldnames = ['member_username', 'member_name', 'payment_date', 'expires', 'active'] 16 | writer = csv.DictWriter(csvfile, fieldnames=fieldnames) 17 | 18 | writer.writeheader() 19 | for payment in SubscriptionPayment.objects.all(): 20 | member = Member.objects.filter(id=payment.member_id).first() 21 | writer.writerow({'member_username': member, 'member_name': member.full_name, 'payment_date': payment.date_paid, 'expires': payment.expires, 'active': payment.is_active}) 22 | 23 | print(csvfile.getvalue()) 24 | -------------------------------------------------------------------------------- /templates/common/admin/events/delete_participants_confirmation.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | {% block content %} 3 |

    Confirm Deletion

    4 |

    The following attendees will be deleted:

    5 |
      6 | {% for attendee in attendees %} 7 |
    • {{ attendee.user }} ({{ attendee.email }})
    • 8 | {% endfor %} 9 |
    10 |
    11 | {% csrf_token %} 12 | 13 | 14 | {% for obj in events %} 15 | 16 | {% endfor %} 17 | 18 | 19 | Cancel 20 |
    21 | {% endblock %} -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | proj_name = os.environ.get("PROJECT_NAME") or "date" 6 | 7 | # When running the Django test suite we default to the test settings module. 8 | if "test" in sys.argv and "DJANGO_SETTINGS_MODULE" not in os.environ: 9 | proj_name = "test" 10 | print("Running tests with core.settings.test") 11 | 12 | if __name__ == '__main__': 13 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', f'core.settings.{proj_name}') 14 | try: 15 | from django.core.management import execute_from_command_line 16 | except ImportError as exc: 17 | raise ImportError( 18 | "Couldn't import Django. Are you sure it's installed and " 19 | "available on your PYTHONPATH environment variable? Did you " 20 | "forget to activate a virtual environment?" 21 | ) from exc 22 | execute_from_command_line(sys.argv) 23 | -------------------------------------------------------------------------------- /events/widgets.py: -------------------------------------------------------------------------------- 1 | from django.forms import widgets 2 | 3 | from django.conf import settings 4 | from django.template.loader import render_to_string 5 | 6 | 7 | class PrettyJSONWidget(widgets.Textarea): 8 | 9 | def render(self, name, value, attrs=None, **kwargs): 10 | html = super(PrettyJSONWidget, self).render(name, value, attrs) 11 | start_as = self.attrs.get('initial', None) or 'raw' 12 | 13 | ctx = { 14 | "html": html, 15 | "start_as": start_as 16 | } 17 | 18 | return render_to_string("events/jsonwidget.html", ctx) 19 | 20 | @property 21 | def media(self): 22 | extra = '' if settings.DEBUG else '.min' 23 | return widgets.Media( 24 | js=( 25 | 'admin/js/vendor/jquery/jquery%s.js' % extra, 26 | 'admin/js/jquery.init.js', 27 | 'prettyjson/prettyjson.js', 28 | ), 29 | ) 30 | -------------------------------------------------------------------------------- /templates/common/members/registration/password_reset_done.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% load i18n %} 4 | {% load static %} 5 | {% block title %}Password reset - Email sent{% endblock %} 6 | {% block content %} 7 | 8 | 9 | 10 |
    11 |
    12 |

    13 | {% trans 'Vi har skicket instruktionerna till din mail, ifall en användare är registrerad med denna epost. Du borde få mailet inom kort' %} 14 |

    15 |

    16 | {% trans 'Ifall du inte fått något mail. Säkerställ att du angett rätt e-post adress samt vänligen granska skräpkorgen.' %} 17 |

    18 |

    {% trans 'Gå till Startsidan' %}

    19 |
    20 |
    21 | {% endblock %} -------------------------------------------------------------------------------- /date/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.contrib.admin.models import LogEntry 3 | 4 | 5 | @admin.register(LogEntry) 6 | class LogEntryAdmin(admin.ModelAdmin): 7 | """Read-only display of admin actions for audit logging.""" 8 | 9 | date_hierarchy = "action_time" 10 | list_display = ( 11 | "action_time", 12 | "user", 13 | "content_type", 14 | "object_repr", 15 | "action_flag", 16 | "change_message", 17 | ) 18 | list_filter = ("user", "content_type", "action_flag") 19 | search_fields = ("object_repr", "change_message") 20 | ordering = ("-action_time",) 21 | readonly_fields = list_display 22 | 23 | def has_add_permission(self, request): 24 | return False 25 | 26 | def has_change_permission(self, request, obj=None): 27 | return False 28 | 29 | def has_delete_permission(self, request, obj=None): 30 | return False 31 | -------------------------------------------------------------------------------- /static/biocum/date/css/date-root.css: -------------------------------------------------------------------------------- 1 | /* Root colors */ 2 | /* use with var(--color); included in base.html*/ 3 | 4 | :root { 5 | --primaryColor: #225E41; 6 | --primaryColorLight: #2F4C42; 7 | --primaryColorLighte:#35564a; 8 | --primaryColorLighter:#3c6153; 9 | --primaryColorTransparent: #225e41ad; 10 | --primaryColorTransparenter: rgba(0,0,0,0.8); 11 | --secondaryColor: #ffffff; 12 | --secondaryColorDarker: #f8f8f8; 13 | --textColorLight: #fff; 14 | --textColorLightish: #eaeaea; 15 | --textColorDark: #000; 16 | --textColorDarkish: #202020; 17 | --helpText: rgb(171, 171, 171); 18 | --helpTextLighter: rgb(199, 199, 199); 19 | --linkColor: #49796b; 20 | --linkColorHover: #225E41; 21 | --linkColorSecondary: #49796b; 22 | --linkSecondaryHover: #225E41; 23 | --warning: rgb(216, 153, 146); 24 | --badgeColor: #49796b; 25 | --defaultPadding: 20px; 26 | --defaultRadius: 5px; 27 | } -------------------------------------------------------------------------------- /events/migrations/0007_auto_20220529_1750.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.13 on 2022-05-29 14:50 2 | 3 | import archive.fields 4 | import core.storage_backends 5 | from django.db import migrations, models 6 | import events.models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('events', '0006_event_passcode'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='event', 18 | name='image', 19 | field=models.ImageField(blank=True, null=True, upload_to=events.models.upload_to, verbose_name='Bakgrundsbild'), 20 | ), 21 | migrations.AddField( 22 | model_name='event', 23 | name='s3_image', 24 | field=archive.fields.PublicFileField(blank=True, null=True, storage=core.storage_backends.PublicMediaStorage(), upload_to=events.models.upload_to, verbose_name='Bakgrundsbild'), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /news/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import Client, TestCase 2 | from django.urls import reverse 3 | 4 | from members.models import Member 5 | from news.models import Post 6 | 7 | 8 | class NewsTestCase(TestCase): 9 | def setUp(self): 10 | self.member = Member.objects.create(username='Test', password='test', is_superuser=True) 11 | self.post = Post.objects.create(title='Test news', slug='test', author_id=self.member.id) 12 | self.assertIsNotNone(self.post) 13 | self.assertTrue(self.post.published) 14 | self.assertIsInstance(self.post, Post) 15 | 16 | def test_get_news_index(self): 17 | c = Client() 18 | response = c.get(reverse('news:index')) 19 | self.assertEqual(response.status_code, 200) 20 | 21 | def test_get_news_detail(self): 22 | c = Client() 23 | response = c.get(reverse('news:detail', args=[self.post.slug])) 24 | self.assertEqual(response.status_code, 200) 25 | -------------------------------------------------------------------------------- /scripts/export_ctf_guesses.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | from io import StringIO 4 | import django 5 | 6 | 7 | sys.path.append("/code") 8 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings.date") 9 | django.setup() 10 | 11 | from ctf.models import Guess 12 | 13 | member_mask = "***********" 14 | 15 | ctf_id = sys.argv[1] 16 | 17 | guesses_per_flag = {} 18 | total_guesses = 0 19 | 20 | with StringIO() as output: 21 | guesses = Guess.objects.filter(ctf=ctf_id).all() 22 | for guess in guesses: 23 | guesses_per_flag[guess.flag] = guesses_per_flag.get(guess.flag, 0) + 1 24 | total_guesses += 1 25 | output.write(f"FLAG: {guess.flag} USER: {member_mask} INPUT: {guess.guess}\n") 26 | 27 | output.write(f"\n\nTotal guesses: {total_guesses}\n\n") 28 | for flag, count in guesses_per_flag.items(): 29 | output.write(f"FLAG: {flag} - Number of inputs: {count}\n") 30 | 31 | print(output.getvalue()) 32 | 33 | -------------------------------------------------------------------------------- /staticpages/admin.py: -------------------------------------------------------------------------------- 1 | from admin_ordering.admin import OrderableAdmin 2 | from django.contrib import admin 3 | 4 | from .models import StaticPage, StaticPageNav, StaticUrl 5 | 6 | 7 | # Register your models here. 8 | 9 | 10 | class UrlInline(OrderableAdmin, admin.TabularInline): 11 | model = StaticUrl 12 | can_delete = True 13 | extra = 0 14 | line_numbering = 0 15 | ordering_field = ('dropdown_element',) 16 | ordering = ['dropdown_element'] 17 | ordering_field_hide_input = True 18 | fields = ('dropdown_element', 'title', 'url', 'logged_in_only') 19 | 20 | 21 | @admin.register(StaticPageNav) 22 | class StaticPageNavAdmin(admin.ModelAdmin): 23 | model = StaticPageNav 24 | save_on_top = True 25 | inlines = [ 26 | UrlInline, 27 | ] 28 | 29 | 30 | @admin.register(StaticPage) 31 | class StaticPageAdmin(admin.ModelAdmin): 32 | model = StaticPage 33 | list_display = ('title', 'slug', 'members_only') 34 | -------------------------------------------------------------------------------- /events/consumers.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from channels.generic.websocket import AsyncWebsocketConsumer 4 | 5 | 6 | class EventConsumer(AsyncWebsocketConsumer): 7 | 8 | async def connect(self): 9 | self.event = self.scope['url_route']['kwargs']['event_name'] 10 | self.event_group_name = 'event_%s' % self.event 11 | 12 | # Join event group 13 | await self.channel_layer.group_add( 14 | self.event_group_name, 15 | self.channel_name 16 | ) 17 | await self.accept() 18 | 19 | async def disconnect(self, code): 20 | # Leave event group 21 | await self.channel_layer.group_discard( 22 | self.event_group_name, 23 | self.channel_name 24 | ) 25 | 26 | async def event_message(self, event): 27 | # Send message 28 | msg = event['data'] 29 | await self.send(text_data=json.dumps({ 30 | 'data': msg 31 | })) 32 | -------------------------------------------------------------------------------- /alumni/migrations/0002_alumniupdatetoken.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.2.1 on 2025-05-29 14:14 2 | 3 | import uuid 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('alumni', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='AlumniUpdateToken', 16 | fields=[ 17 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('token', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)), 19 | ('email', models.EmailField(max_length=256)), 20 | ('created_at', models.DateTimeField(auto_now_add=True)), 21 | ], 22 | options={ 23 | 'verbose_name': 'Alumni Update Token', 24 | 'verbose_name_plural': 'Alumni Update Tokens', 25 | }, 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /events/migrations/0015_event_parent_eventattendees_original_event.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.2.6 on 2025-09-19 06:20 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('events', '0014_alter_event_sign_up_max_participants'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='event', 16 | name='parent', 17 | field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='events.event'), 18 | ), 19 | migrations.AddField( 20 | model_name='eventattendees', 21 | name='original_event', 22 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='original_event', to='events.event', verbose_name='Ursprungligt evenemang'), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /lucia/models.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from django.db import models 4 | from django.utils.translation import gettext_lazy as _ 5 | from django_ckeditor_5.fields import CKEditor5Field 6 | from polls.models import Question 7 | 8 | logger = logging.getLogger('date') 9 | 10 | POST_SLUG_MAX_LENGTH = 50 11 | 12 | 13 | class Candidate(models.Model): 14 | img_url = models.URLField(_('Bild URL'), max_length=255, blank=True) 15 | title = models.CharField(_('Titel'), max_length=255, blank=False) 16 | content = CKEditor5Field(_('Innehåll'), blank=True) 17 | published = models.BooleanField(_('Publicera'), default=True) 18 | slug = models.SlugField(_('Slug'), unique=True, allow_unicode=False, max_length=POST_SLUG_MAX_LENGTH) 19 | poll_url = models.URLField(_('Poll URL'), max_length=255, blank=False) 20 | 21 | class Meta: 22 | verbose_name = _('Lucia') 23 | verbose_name_plural = _('Lucian') 24 | ordering = ('id',) 25 | 26 | def __str__(self): 27 | return self.title 28 | -------------------------------------------------------------------------------- /.github/workflows/docker_build.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - develop 5 | - master 6 | workflow_dispatch: 7 | schedule: 8 | - cron: '0 0 1 * *' 9 | jobs: 10 | build: 11 | concurrency: 12 | group: 'docker-publish-${{ github.ref_name }}' 13 | cancel-in-progress: true 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v6 18 | - name: Convert github.repo to lower 19 | id: string 20 | uses: ASzc/change-string-case-action@v6 21 | with: 22 | string: ${{ github.repository }} 23 | - name: Docker login 24 | run: | 25 | echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin 26 | - name: Docker build 27 | run: | 28 | docker buildx create --use 29 | docker buildx build --push --platform=linux/amd64,linux/arm64 -t ghcr.io/${{ steps.string.outputs.lowercase }}:${{ github.ref_name }} . 30 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "pip" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "monthly" 12 | 13 | # Maintain dependencies for GitHub Actions 14 | - package-ecosystem: "github-actions" 15 | # Workflow files stored in the default location of `.github/workflows`. (You don't need to specify `/.github/workflows` for `directory`. You can use `directory: "/"`.) 16 | directory: "/" 17 | schedule: 18 | interval: "monthly" 19 | 20 | - package-ecosystem: "docker" 21 | directory: "/" 22 | schedule: 23 | interval: "monthly" 24 | -------------------------------------------------------------------------------- /templates/common/members/registration/password_reset_confirm.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% load i18n %} 4 | {% load static %} 5 | {% block title %}{% trans 'Glömt lösenord' %}{% endblock %} 6 | {% block content %} 7 | 8 | 9 | 10 |
    11 | {% if validlink %} 12 | 13 |
    14 |

    {% trans 'Byt lösenord' %}

    15 |
    16 | {% csrf_token %} 17 | {{ form.as_p }} 18 | 19 |
    20 |
    21 | {% else %} 22 |
    23 |

    24 | {% trans 'Denna länk är inte giltigt, det kan hända att länken redan använts. Vänligen fråga efter en ny länk.' %} 25 |

    26 |
    27 | {% endif %} 28 |
    29 | {% endblock %} -------------------------------------------------------------------------------- /staticpages/migrations/0005_staticpage_nav_to_url.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.3 on 2024-04-02 08:51 2 | 3 | from django.db import migrations 4 | from django.urls import reverse 5 | 6 | 7 | def change_nav_to_url(apps, schema_editor): 8 | StaticPage = apps.get_model('staticpages', 'StaticPage') 9 | StaticUrl = apps.get_model('staticpages', 'StaticUrl') 10 | 11 | all_pages = StaticPage.objects.filter(category__isnull=False) 12 | 13 | for page in all_pages: 14 | category = page.category 15 | idx = page.dropdown_element 16 | url = reverse("staticpages:page", args=[page.slug]) 17 | nav_url = StaticUrl(title=page.title, url=url, category=category, dropdown_element=idx) 18 | nav_url.save() 19 | 20 | page.category = None 21 | page.save() 22 | 23 | 24 | class Migration(migrations.Migration): 25 | 26 | dependencies = [ 27 | ('staticpages', '0004_alter_staticpage_content'), 28 | ] 29 | 30 | operations = [ 31 | migrations.RunPython(change_nav_to_url), 32 | ] 33 | -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | upstream web { 2 | server web:8000; 3 | } 4 | 5 | upstream ws { 6 | server asgi:8000; 7 | } 8 | 9 | server { 10 | listen 80; 11 | 12 | resolver 127.0.0.11 valid=5s; 13 | 14 | include /etc/nginx/mime.types; 15 | 16 | location / { 17 | proxy_pass http://web/; 18 | proxy_set_header Host $host; 19 | proxy_set_header X-Real-IP $remote_addr; 20 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 21 | proxy_set_header X-Forwarded-Host $server_name; 22 | client_max_body_size 5000M; 23 | } 24 | 25 | location /ws/ { 26 | proxy_http_version 1.1; 27 | proxy_set_header Upgrade $http_upgrade; 28 | proxy_set_header Connection "upgrade"; 29 | proxy_redirect off; 30 | proxy_pass http://ws/ws/; 31 | proxy_set_header Host $host; 32 | proxy_set_header X-Real-IP $remote_addr; 33 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 34 | proxy_set_header X-Forwarded-Host $server_name; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /archive/migrations/0006_auto_20221004_1958.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.13 on 2022-10-04 16:58 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('archive', '0005_collection_hide_for_gulis'), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='ExamCollection', 15 | fields=[ 16 | ], 17 | options={ 18 | 'verbose_name': 'Tentarkiv', 19 | 'verbose_name_plural': 'Tentarkiv', 20 | 'proxy': True, 21 | 'indexes': [], 22 | 'constraints': [], 23 | }, 24 | bases=('archive.collection',), 25 | ), 26 | migrations.AlterField( 27 | model_name='collection', 28 | name='type', 29 | field=models.CharField(choices=[('Pictures', 'Bilder'), ('Documents', 'Dokument'), ('Exams', 'Tenter'), ('PublicFiles', 'OffentligaFiler')], max_length=20), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /static/kk/events/css/event-card.css: -------------------------------------------------------------------------------- 1 | .card { 2 | border-radius: var(--defaultRadius); 3 | } 4 | 5 | .card .fa-chevron-right{ 6 | transition: .3s; 7 | } 8 | 9 | .card a:hover .fa-chevron-right{ 10 | transform: translateX(10px); 11 | } 12 | 13 | .evet-card > .card .list-inline-item { 14 | color: var(--helpText); 15 | font-size: 14px; 16 | } 17 | 18 | .evet-card > .card .badge { 19 | background: var(--primaryColorLighter); 20 | } 21 | 22 | .evet-card > .card { 23 | background: var(--secondaryColor); 24 | } 25 | 26 | .card-body a{ 27 | color: var(--linkColor); 28 | border-color: var(--linkColor); 29 | } 30 | 31 | .card-body a:active{ 32 | color: var(--linkColor); 33 | } 34 | 35 | .card-body a:hover{ 36 | background-color: inherit; 37 | color: var(--linkSecondaryHover); 38 | border-color: var(--linkSecondaryHover); 39 | } 40 | 41 | .card-body a:focus{ 42 | background-color: inherit; 43 | box-shadow: none; 44 | } 45 | 46 | .event-date-txt{ 47 | white-space: nowrap; 48 | } 49 | -------------------------------------------------------------------------------- /templates/common/members/registration/registration_complete.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% load i18n %} 4 | {% load static %} 5 | {% block title %}Registration completed{% endblock %} 6 | {% block content %} 7 | 8 | 9 | 10 |
    11 |
    12 |

    13 | {% trans 'Styrelsen har blivit notifierad angående din anmälan.' %} 14 |

    15 | {% if alumni %} 16 |

    17 | {% trans 'Kolla e-postadressen du registrerade dig med för ytterligare instruktioner' %} 18 |

    19 | {% else %} 20 |

    21 | {% trans 'Du kan logga in efter att styrelsen har granskat och godkänt ditt konto.' %} 22 |

    23 | {% endif %} 24 |

    {% trans 'Gå till Startsidan' %}

    25 |
    26 |
    27 | {% endblock %} -------------------------------------------------------------------------------- /billing/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | 5 | class EventInvoice(models.Model): 6 | participant = models.ForeignKey('events.EventAttendees', on_delete=models.CASCADE) 7 | invoice_number = models.IntegerField(unique=True) 8 | reference_number = models.CharField(max_length=20, unique=True) 9 | invoice_date = models.DateField() 10 | due_date = models.DateField() 11 | amount = models.FloatField(default=0) 12 | currency = models.CharField(max_length=3, default='EUR') 13 | 14 | def __str__(self): 15 | return f"{self.participant.event.title} - {self.participant.user} - {self.invoice_number}" 16 | 17 | 18 | class EventBillingConfiguration(models.Model): 19 | event = models.OneToOneField('events.Event', blank=False, on_delete=models.CASCADE) 20 | due_date = models.DateField() 21 | integration_type = models.IntegerField() 22 | price = models.CharField() 23 | price_selector = models.CharField(blank=True, null=True) 24 | 25 | def __str__(self): 26 | return self.event.title 27 | -------------------------------------------------------------------------------- /static/common/events/css/event-card.css: -------------------------------------------------------------------------------- 1 | .card { 2 | border-radius: var(--defaultRadius); 3 | } 4 | 5 | .card .fa-chevron-right{ 6 | transition: .3s; 7 | } 8 | 9 | .card a:hover .fa-chevron-right{ 10 | transform: translateX(10px); 11 | } 12 | 13 | .evet-card > .card .list-inline-item { 14 | color: var(--helpText); 15 | font-size: 14px; 16 | } 17 | 18 | .evet-card > .card .badge { 19 | background: var(--helpText); 20 | } 21 | 22 | .evet-card > .card { 23 | background: var(--primaryColorLight); 24 | } 25 | 26 | .card-body a{ 27 | color: var(--linkColorLight); 28 | border-color: var(--linkColorLight); 29 | } 30 | 31 | .card-body a:active{ 32 | color: var(--linkColorLight); 33 | } 34 | 35 | .card-body a:hover{ 36 | background-color: inherit; 37 | color: var(--linkColorLightHover); 38 | border-color: var(--linkColorLightHover); 39 | } 40 | 41 | .card-body a:focus{ 42 | background-color: inherit; 43 | box-shadow: none; 44 | } 45 | 46 | .event-date-txt{ 47 | white-space: nowrap; 48 | } 49 | -------------------------------------------------------------------------------- /static/common/core/js/cookie-banner.js: -------------------------------------------------------------------------------- 1 | // Cookie banner functionality 2 | document.addEventListener('DOMContentLoaded', function() { 3 | // Check if user has already accepted cookies 4 | if (!localStorage.getItem('cookiesAccepted')) { 5 | // Show the banner 6 | document.getElementById('cookie-banner').style.display = 'block'; 7 | } 8 | 9 | // Handle accept button click 10 | document.getElementById('accept-cookies').addEventListener('click', function() { 11 | // Set cookie acceptance in localStorage 12 | localStorage.setItem('cookiesAccepted', 'true'); 13 | // Hide the banner 14 | document.getElementById('cookie-banner').style.display = 'none'; 15 | }); 16 | 17 | // Handle decline button click 18 | document.getElementById('decline-cookies').addEventListener('click', function() { 19 | // Set cookie acceptance in localStorage 20 | localStorage.setItem('cookiesAccepted', 'false'); 21 | // Hide the banner 22 | document.getElementById('cookie-banner').style.display = 'none'; 23 | }); 24 | }); -------------------------------------------------------------------------------- /templates/common/news/article.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load static %} 3 | {% load i18n %} 4 | 5 | {% block title %}{{ article.title }}{% endblock %} 6 | {% block content %} 7 | 8 | 9 | 10 | 11 |
    12 |
    13 |
    14 |

    {{ article.title }}

    15 |

    {% trans "Skriven " %}{{ article.published_time }} {% trans "av" %} 16 | 17 | {% if article.author.get_full_name != " " %}{{ article.author.get_full_name }}{% else %} 18 | {{ article.author }}{% endif %}

    19 | {{ article.content | safe }} 20 |
    21 |
    22 |
    23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /templates/common/archive/edit.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load app_filters %} 3 | {% load static %} 4 | {% block content %} 5 | 6 | 7 |
    8 |

    {{ collection.title }}

    9 |

    Published on {{ collection.pub_date_pretty }}

    10 |

    You're in edit mode Return

    11 | {% if error_msg %} 12 |

    {{ error_msg }}

    13 | {% endif %} 14 | {% for picture in collection.picture_set.all %} 15 |
    16 | 17 |

    Remove

    18 |
    19 | {% endfor %} 20 |

    Return

    21 |
    22 | {% endblock %} -------------------------------------------------------------------------------- /scripts/create_album.py: -------------------------------------------------------------------------------- 1 | """ 2 | Usage: 3 | python scripts/create_album.py title dir/ 4 | 5 | Docker usage: 6 | docker-compose run -v host/dir:code/img:ro web python scripts/create_album.py title img/ 7 | """ 8 | import datetime 9 | import sys 10 | import os 11 | import django 12 | from django.core.files import File 13 | 14 | 15 | sys.path.append("/code") 16 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings.date") 17 | django.setup() 18 | 19 | from archive.models import Picture, Collection 20 | 21 | collection = Collection( 22 | title=sys.argv[1], 23 | type="Pictures", 24 | pub_date=datetime.datetime.now(tz=datetime.timezone.utc) 25 | ) 26 | collection.save() 27 | 28 | print("created collection") 29 | 30 | for root, _, files in os.walk(sys.argv[2]): 31 | for file in files: 32 | with open(os.path.join(root, file), 'rb') as file_obj: 33 | print(f"Uploading {file}") 34 | pic = Picture.objects.create( 35 | collection=collection, 36 | image=File(file_obj, file) 37 | ) 38 | pic.save() 39 | -------------------------------------------------------------------------------- /social/models.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from django.db import models 4 | from django.utils.translation import gettext_lazy as _ 5 | 6 | logger = logging.getLogger('date') 7 | 8 | # Create your models here. 9 | 10 | class IgUrl(models.Model): 11 | url = models.CharField(_('URL'), max_length=255, blank=False) 12 | shortcode = models.CharField(_('SHORTCODE'), max_length=255, blank=False) 13 | 14 | def __str__(self): 15 | return self.url 16 | 17 | 18 | class Harassment(models.Model): 19 | email = models.EmailField(_('Email'), max_length=255, blank=True, null=True) 20 | message = models.TextField(_('Beskrivning av händelsen'), blank=False, max_length=1500) 21 | 22 | def __str__(self): 23 | return self.message 24 | 25 | 26 | class HarassmentEmailRecipient(models.Model): 27 | recipient_email = models.EmailField(max_length=320) 28 | 29 | def __str__(self): 30 | return self.recipient_email 31 | 32 | class Meta: 33 | verbose_name = _("Emailmottagare för Trakasserianmälan") 34 | verbose_name_plural = _("Emailmottagare för Trakasserianmälan") 35 | -------------------------------------------------------------------------------- /templates/common/core/418.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load static %} 3 | {% load i18n %} 4 | {% block title %}{{ event.title }}{% endblock %} 5 | 6 | {% block content %} 7 | 8 | 9 | 10 | I'm a teapot 11 | 12 | 13 | 14 | 15 |
    16 |
    17 | {% if error_msg %} 18 |

    {{ error_msg }}

    19 | {% else %} 20 |

    - 418 -

    21 |

    Jag är en tekokare

    22 | Teapot pouring red liquid 23 |

    24 |

    {% trans 'Inget vatten i glöggkokaren, tack' %}

    25 | {% endif %} 26 |

    {% trans 'Till startsidan' %}

    27 |
    28 |
    29 | 30 | 31 | {% endblock %} 32 | -------------------------------------------------------------------------------- /members/urls.py: -------------------------------------------------------------------------------- 1 | import django.views.generic 2 | from django.conf import settings 3 | from django.urls import include, path, re_path 4 | 5 | from . import views 6 | 7 | app_name = 'members' 8 | 9 | urlpatterns = [ 10 | re_path(r'^signup/$', views.signup, name='signup'), 11 | path('activate///', views.activate, name='activate'), 12 | path('password_reset/', views.CustomPasswordResetView.as_view(), name='password_reset'), 13 | re_path('^', include('django.contrib.auth.urls')), 14 | path('password_change/', views.CustomPasswordChangeView.as_view(), name='custom_password_change'), 15 | path('info/', views.UserinfoView.as_view(), name='info'), 16 | path('cert/', views.CertificateView.as_view(), name='certificate'), 17 | path('funktionar/', views.FunctionaryView.as_view(), name='functionary'), 18 | path('funktionarer/', views.FunctionariesView.as_view(), name='functionaries'), 19 | ] 20 | 21 | if "alumni" in settings.INSTALLED_APPS: 22 | urlpatterns += [path('alumn/signup', django.views.generic.RedirectView.as_view(url='/alumni/signup/', permanent=True), name='alumni_signup'),] 23 | -------------------------------------------------------------------------------- /static/date/date/css/date-root.css: -------------------------------------------------------------------------------- 1 | /* Root colors */ 2 | /* use with var(--color); included in base.html*/ 3 | 4 | :root { 5 | --primaryColor: #000; 6 | --primaryColorLight: #202020; 7 | --primaryColorLighte:#3d3d3d; 8 | --primaryColorLighter:#505050; 9 | --primaryColorTransparent: rgba(0,0,0,0.9); 10 | --primaryColorTransparenter: rgba(0,0,0,0.8); 11 | --secondaryColor: #fff; 12 | --secondaryColorDarker: #eaeaea; 13 | --textColorLight: #fff; 14 | --textColorLightish: #eaeaea; 15 | --textColorDark: #000; 16 | --textColorDarkish: #202020; 17 | --textColorMedium: #606060; 18 | --helpText: #888; 19 | --helpTextLighter: rgb(199, 199, 199); 20 | --linkColorDark: #3a80c3; 21 | --linkColorDarkHover: #26547f; 22 | --linkColorLight: rgb(208, 255, 0); 23 | --linkColorLightHover: rgb(146, 179, 0); 24 | --warning: rgb(216, 153, 146); 25 | --warning-darker: rgb(213, 91, 78); 26 | --navClickHighlight: rgba(255, 255, 255, 0.25); 27 | --navHoverHighlight: rgba(255, 255, 255, 0.15); 28 | --defaultPadding: 20px; 29 | --defaultRadius: 5px; 30 | } 31 | -------------------------------------------------------------------------------- /static/demo/date/css/date-root.css: -------------------------------------------------------------------------------- 1 | /* Root colors */ 2 | /* use with var(--color); included in base.html*/ 3 | 4 | :root { 5 | --primaryColor: #0000FF; 6 | --primaryColorLight: #2020FF; 7 | --primaryColorLighte:#3d3dFF; 8 | --primaryColorLighter:#5050FF; 9 | --primaryColorTransparent: rgba(0,0,255,0.9); 10 | --primaryColorTransparenter: rgba(0,0,255,0.8); 11 | --secondaryColor: #fff; 12 | --secondaryColorDarker: #eaeaea; 13 | --textColorLight: #fff; 14 | --textColorLightish: #eaeaea; 15 | --textColorDark: #000; 16 | --textColorDarkish: #202020; 17 | --textColorMedium: #606060; 18 | --helpText: #888; 19 | --helpTextLighter: rgb(199, 199, 199); 20 | --linkColorDark: #0080FF; 21 | --linkColorDarkHover: #004080; 22 | --linkColorLight: rgb(0, 255, 255); 23 | --linkColorLightHover: rgb(0, 192, 192); 24 | --warning: rgb(255, 0, 0); 25 | --warning-darker: rgb(128, 0, 0); 26 | --navClickHighlight: rgba(255, 255, 255, 0.25); 27 | --navHoverHighlight: rgba(255, 255, 255, 0.15); 28 | --defaultPadding: 20px; 29 | --defaultRadius: 5px; 30 | } 31 | -------------------------------------------------------------------------------- /social/igupdate.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import sys 4 | 5 | import django 6 | 7 | sys.path.append("/code") 8 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings.date") 9 | django.setup() 10 | 11 | import time 12 | from datetime import datetime 13 | from itertools import islice 14 | 15 | import instaloader 16 | import schedule 17 | 18 | from social.models import IgUrl 19 | 20 | logger = logging.getLogger('date') 21 | 22 | SCHEDULED_TIME = '00:00' 23 | 24 | logger.info("STARTING IG SCHEDULER") 25 | 26 | 27 | def updateIg(): 28 | logger.info("IGSCHEDULER WORKING") 29 | logger.info(datetime.now()) 30 | L = instaloader.Instaloader() 31 | igProfile = instaloader.Profile.from_username(L.context, "kemistklubben") 32 | posts = igProfile.get_posts() 33 | top40 = islice(posts, 40) 34 | 35 | IgUrl.objects.all().delete() 36 | 37 | for post in top40: 38 | u = IgUrl(url=post.url, shortcode=post.shortcode) 39 | u.save() 40 | 41 | 42 | schedule.every().day.at(SCHEDULED_TIME).do(updateIg) 43 | 44 | while True: 45 | schedule.run_pending() 46 | time.sleep(60) 47 | -------------------------------------------------------------------------------- /static/biocum/events/css/event-card.css: -------------------------------------------------------------------------------- 1 | .card { 2 | border-radius: var(--defaultRadius); 3 | color: var(--textColorDarkish); 4 | } 5 | 6 | .card .fa-chevron-right{ 7 | transition: .3s; 8 | } 9 | 10 | .card a:hover .fa-chevron-right{ 11 | transform: translateX(10px); 12 | } 13 | 14 | .evet-card > .card .list-inline-item { 15 | color: var(--helpText); 16 | font-size: 14px; 17 | } 18 | 19 | .evet-card > .card .badge { 20 | background: var(--badgeColor); 21 | } 22 | 23 | .evet-card > .card { 24 | background: var(--secondaryColor); 25 | } 26 | 27 | .card-body a{ 28 | color: var(--linkColorSecondary); 29 | border-color: var(--linkColorSecondary); 30 | } 31 | 32 | .card-body a:active{ 33 | color: var(--linkColorSecondary); 34 | } 35 | 36 | .card-body a:hover{ 37 | background-color: inherit; 38 | color: var(--linkSecondaryHover); 39 | border-color: var(--linkSecondaryHover); 40 | } 41 | 42 | .card-body a:focus{ 43 | background-color: inherit; 44 | box-shadow: none; 45 | } 46 | 47 | .event-date-txt{ 48 | white-space: nowrap; 49 | } 50 | -------------------------------------------------------------------------------- /templates/common/events/event_passcode.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load static %} 3 | {% load i18n %} 4 | {% block title %}{{ ASSOCIATION_NAME_SHORT }} - {% trans "Händelser" %}{% endblock %} 5 | 6 | {% block content %} 7 | 8 | 9 | 10 | 11 |
    12 |
    13 |
    14 |

    {{event.title}}

    15 | {% if passcode_error %} 16 |
    17 | {{ passcode_error }} 18 |
    19 | {% endif %} 20 |
    21 | {{ form.as_p }} 22 | 23 | {% csrf_token %} 24 |
    25 |
    26 |
    27 |
    28 | 29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /static/on/date/css/date-root.css: -------------------------------------------------------------------------------- 1 | /* Root colors */ 2 | /* use with var(--color); included in base.html*/ 3 | 4 | :root { 5 | --primaryColor: #84030B; 6 | --primaryColorLight: #a8131d; 7 | --primaryColorLighte: #b52831; 8 | --primaryColorLighter: #df5b63; 9 | --primaryColorTransparent: rgba(132, 3, 11, 0.7); 10 | --primaryColorTransparenter: rgba(132, 3, 11, 0.65); 11 | --secondaryColor: #ffc400; 12 | --secondaryColorDarker: #131313; 13 | --textColorLight: #faf0dc; 14 | --textColorLightish: #faf0dc; 15 | --textColorDark: #000; 16 | --textColorDarkish: #202020; 17 | --textColorMedium: #606060; 18 | --helpText: var(--textColorLightish); 19 | --helpTextLighter: rgb(199, 199, 199); 20 | --linkColorDark: #3a80c3; 21 | --linkColorDarkHover: #26547f; 22 | --linkColorLight: #ffc400; 23 | --linkColorLightHover: #edc057; 24 | --warning: rgb(216, 153, 146); 25 | --warning-darker: rgb(213, 91, 78); 26 | --navClickHighlight: rgba(255, 255, 255, 0.25); 27 | --navHoverHighlight: rgba(255, 255, 255, 0.15); 28 | --defaultPadding: 20px; 29 | --defaultRadius: 5px; 30 | } 31 | -------------------------------------------------------------------------------- /static/common/archive/css/detailed.css: -------------------------------------------------------------------------------- 1 | .gallery-image{ 2 | transition: .3s; 3 | border-radius: var(--defaultRadius); 4 | } 5 | 6 | .gallery-image:hover{ 7 | transform: scale(1.01); 8 | } 9 | 10 | /* pagination */ 11 | .center { 12 | text-align: center; 13 | } 14 | 15 | .pagination { 16 | display: inline-block; 17 | margin-top: 30px; 18 | } 19 | 20 | .pagination a { 21 | color: var(--textColorLightish); 22 | background-color: var(--primaryColorLighter); 23 | float: left; 24 | padding: 8px 16px; 25 | text-decoration: none; 26 | transition: background-color .3s; 27 | border: 1px solid #ddd; 28 | margin: 0 4px; 29 | border-radius: var(--defaultRadius); 30 | } 31 | 32 | .pagination .disabled{ 33 | border: 0; 34 | } 35 | 36 | .pagination a.active { 37 | color: var(--textColorLight); 38 | border: 1px solid var(--linkColorLight); 39 | } 40 | 41 | .pagination a:hover:not(.active, .disabled) {background-color: var(--primaryColorLight);} 42 | 43 | @media only screen and (min-width: 768px) { 44 | .grid-sizer, 45 | .grid-item { 46 | width: 24.2%; 47 | } 48 | 49 | .grid img{ 50 | max-height: 70vh; 51 | } 52 | } -------------------------------------------------------------------------------- /templates/common/archive/exams_index.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load app_filters %} 3 | {% load static %} 4 | {% load i18n %} 5 | {% block title %}{% trans 'Tentarkiv' %}{% endblock %} 6 | 7 | {% block content %} 8 | 9 | 10 | 11 |
    12 |
    13 |
    14 |
    15 | {% trans "Lägg till arkiv" %} 16 |

    {% trans "Tentarkiv" %}

    17 |
    18 | {% for collection in collections %} 19 | 22 | {% endfor %} 23 |
    24 |
    25 |
    26 | {% endblock %} 27 | 28 | -------------------------------------------------------------------------------- /templates/common/ctf/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load static %} 3 | {% load i18n %} 4 | {% block title %}{{ ASSOCIATION_NAME_SHORT }} - CTF{% endblock %} 5 | 6 | {% block content %} 7 | 8 | 9 | 10 | 11 | 12 |
    13 |
    14 |
    15 |

    {{ ASSOCIATION_NAME_SHORT }} CTF

    16 | {% if latest_ctf_list %} 17 |
      18 | {% for ctf in latest_ctf_list %} 19 | {% if ctf.published %} 20 |
    • 21 | {{ ctf.title }} 22 |
    • 23 | {% endif %} 24 | {% endfor %} 25 |
    26 | {% else %} 27 |

    No CTF's are available.

    28 | {% endif %} 29 |
    30 |
    31 |
    32 | 33 | {% endblock %} 34 | -------------------------------------------------------------------------------- /templates/common/members/registration/password_reset_form.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | 3 | {% load i18n %} 4 | {% load static %} 5 | {% block title %}{% trans 'Glömt lösenord' %}{% endblock %} 6 | {% block content %} 7 | 8 | 9 | 10 |
    11 |
    12 |

    {% trans 'Glömt lösenord' %}

    13 |
    14 | {% csrf_token %} 15 | {% for field in form %} 16 |

    17 | {{ field.label_tag }}
    18 | {% if field.help_text %} 19 | {{ field.help_text }} 20 | {% endif %} 21 | {{ field }} 22 | {% for error in field.errors %} 23 |

    {{ error }}

    24 | {% endfor %} 25 |

    26 | {% endfor %} 27 | 28 |
    29 |
    30 |
    31 | {% endblock %} -------------------------------------------------------------------------------- /static/common/members/css/certificate.css: -------------------------------------------------------------------------------- 1 | .content { 2 | padding: 5px; 3 | } 4 | 5 | .card { 6 | color: var(--textColorDark); 7 | } 8 | 9 | .validation { 10 | height: 250px; 11 | position: relative; 12 | border-radius:var(--defaultRadius); 13 | padding: 5px; 14 | } 15 | 16 | .valid { 17 | background-color: rgb(31, 141, 31); 18 | } 19 | 20 | .invalid { 21 | background-color: rgb(141, 31, 31); 22 | } 23 | 24 | .supporting { 25 | background-color: orange; 26 | } 27 | 28 | 29 | .checkmark { 30 | position: absolute; 31 | color: white; 32 | bottom: 5px; 33 | margin-left: auto; 34 | margin-right: auto; 35 | left: 0; 36 | right: 0; 37 | text-align: center; 38 | } 39 | 40 | 41 | .fa-rotate { 42 | animation: infinite-spinning 4s linear infinite; 43 | position: absolute; 44 | margin-left: auto; 45 | margin-right: auto; 46 | left: 0; 47 | right: 0; 48 | text-align: center; 49 | top: 25%; 50 | transform: translateY(-25%); 51 | color: white; 52 | } 53 | 54 | @keyframes infinite-spinning { 55 | from { 56 | transform: rotate(0deg); 57 | } 58 | to { 59 | transform: rotate(360deg); 60 | } 61 | } -------------------------------------------------------------------------------- /lucia/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.7 on 2020-11-22 21:48 2 | 3 | import ckeditor.fields 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Candidate', 17 | fields=[ 18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('img_url', models.URLField(max_length=255, verbose_name='Bild URL')), 20 | ('title', models.CharField(max_length=255, verbose_name='Titel')), 21 | ('content', ckeditor.fields.RichTextField(blank=True, verbose_name='Innehåll')), 22 | ('published', models.BooleanField(default=True, verbose_name='Publicera')), 23 | ('slug', models.SlugField(unique=True, verbose_name='Slug')), 24 | ], 25 | options={ 26 | 'verbose_name': 'Lucia', 27 | 'verbose_name_plural': 'Lucian', 28 | 'ordering': ('id',), 29 | }, 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /static/common/news/css/style.css: -------------------------------------------------------------------------------- 1 | .aa-img img{ 2 | max-width: 100%; 3 | max-height: 20vh; 4 | display: block; 5 | margin-left: auto; 6 | margin-right: auto; 7 | margin-bottom: 2vh; 8 | } 9 | 10 | /* pagination */ 11 | .center { 12 | text-align: center; 13 | } 14 | 15 | .pagination { 16 | display: inline-block; 17 | margin-top: 30px; 18 | } 19 | 20 | .pagination a { 21 | color: var(--textColorLightish); 22 | background-color: var(--primaryColorLighter); 23 | float: left; 24 | padding: 8px 16px; 25 | text-decoration: none; 26 | transition: background-color .3s; 27 | border: 1px solid #ddd; 28 | margin: 0 4px; 29 | border-radius: var(--defaultRadius); 30 | } 31 | 32 | .pagination .disabled{ 33 | border: 0; 34 | } 35 | 36 | .pagination a.active { 37 | color: var(--textColorLight); 38 | border: 1px solid var(--linkColorLight); 39 | } 40 | 41 | @media only screen and (max-width: 1200px) { 42 | .container{ 43 | grid-template-columns: 10vw 1fr 10vw; 44 | } 45 | } 46 | @media only screen and (max-width: 900px) { 47 | .container{ 48 | grid-template-columns: 3vw 1fr 3vw; 49 | } 50 | } -------------------------------------------------------------------------------- /templates/common/archive/exam_detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load app_filters %} 3 | {% load static %} 4 | {% load i18n %} 5 | {% load render_table from django_tables2 %} 6 | {% load bootstrap3 %} 7 | {% block title %}{% trans 'Tentarkiv' %}{% endblock %} 8 | 9 | {% block content %} 10 | 11 | 12 | 13 |
    14 |
    15 |
    16 |
    17 | {% trans "Lägg till tentamen" %} 18 |

    {{ collection.title }}

    19 |
    20 |
    21 | {% render_table table 'django_tables2/bootstrap.html' %} 22 |
    23 |
    24 |
    25 |
    26 | {% endblock %} 27 | 28 | -------------------------------------------------------------------------------- /templates/common/news/author.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load static %} 3 | {% load i18n %} 4 | 5 | {% block title %}{{ article.title }}{% endblock %} 6 | {% block content %} 7 | 8 | 9 | 10 |
    11 |
    12 | {% for article in articles %} 13 |
    14 |

    {{ article.title }}

    15 |

    {% trans "Skriven " %}{{ article.published_time }}{% trans " av " %} 16 | 17 | {% if article.author.get_full_name != " " %}{{ article.author.get_full_name }}{% else %} 18 | {{ article.author }}{% endif %}

    19 | {{ article.content | safe }} 20 |
    21 | {% empty %} 22 |

    {% trans "Inga artiklar hittades..." %}

    23 | {% endfor %} 24 |
    25 |
    26 | {% endblock %} 27 | -------------------------------------------------------------------------------- /core/urls/biocum.py: -------------------------------------------------------------------------------- 1 | """Biocum URL Configuration. 2 | 3 | The `urlpatterns` list routes URLs to views. 4 | """ 5 | 6 | from django.conf import settings 7 | from django.conf.urls.static import static 8 | from django.contrib import admin 9 | from django.urls import include, path 10 | 11 | from date import views as date 12 | 13 | app_name = 'core' 14 | 15 | urlpatterns = [ 16 | path('', date.index, name='index'), 17 | path('news/', include('news.urls')), 18 | path('members/', include('members.urls')), 19 | path('members/', include('django.contrib.auth.urls')), 20 | path('archive/', include('archive.urls')), 21 | path('events/', include('events.urls')), 22 | path('pages/', include('staticpages.urls')), 23 | path('ads/', include('ads.urls')), 24 | path('social/', include('social.urls')), 25 | path('polls/', include('polls.urls')), 26 | path('admin/', admin.site.urls), 27 | path("ckeditor5/", include('django_ckeditor_5.urls')), 28 | ] 29 | 30 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 31 | urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 32 | 33 | handler404 = date.handler404 34 | handler500 = date.handler500 35 | -------------------------------------------------------------------------------- /date/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from django.urls import reverse 3 | from django.contrib.auth import get_user_model 4 | from django.contrib.contenttypes.models import ContentType 5 | from django.contrib.admin.models import LogEntry, ADDITION 6 | 7 | 8 | class AuditLogTestCase(TestCase): 9 | def setUp(self): 10 | self.user = get_user_model().objects.create_superuser( 11 | username="admin", 12 | password="pass", 13 | email="admin@example.com", 14 | ) 15 | ct = ContentType.objects.get_for_model(get_user_model()) 16 | LogEntry.objects.log_action( 17 | user_id=self.user.pk, 18 | content_type_id=ct.pk, 19 | object_id=self.user.pk, 20 | object_repr=str(self.user), 21 | action_flag=ADDITION, 22 | change_message="created user", 23 | ) 24 | 25 | def test_audit_log_accessible(self): 26 | self.client.login(username="admin", password="pass") 27 | url = reverse("admin:admin_logentry_changelist") 28 | response = self.client.get(url) 29 | self.assertEqual(response.status_code, 200) 30 | self.assertContains(response, "created user") 31 | -------------------------------------------------------------------------------- /alumni/models.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from django.db import models 4 | from django.utils.translation import gettext_lazy as _ 5 | 6 | from django.utils import timezone 7 | 8 | # Create your models here. 9 | 10 | class AlumniEmailRecipient(models.Model): 11 | recipient_email = models.EmailField(max_length=256) 12 | 13 | def __str__(self): 14 | return self.recipient_email 15 | 16 | class Meta: 17 | verbose_name = _("Emailmottagare för ÅAATK") 18 | verbose_name_plural = _("Emailmottagare för ÅAATK") 19 | 20 | 21 | class AlumniUpdateToken(models.Model): 22 | token = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) 23 | email = models.EmailField(max_length=256) 24 | created_at = models.DateTimeField(auto_now_add=True) 25 | 26 | def __str__(self): 27 | return self.token 28 | 29 | class Meta: 30 | verbose_name = _("Alumni Update Token") 31 | verbose_name_plural = _("Alumni Update Tokens") 32 | 33 | def is_valid(self): 34 | """Check if the token is not expired. Tokens are valid for 24 hours.""" 35 | expiration_time = self.created_at + timezone.timedelta(hours=24) 36 | return timezone.now() < expiration_time 37 | -------------------------------------------------------------------------------- /polls/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.7 on 2020-10-22 12:14 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Question', 17 | fields=[ 18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('question_text', models.CharField(max_length=200)), 20 | ('pub_date', models.DateTimeField(verbose_name='date published')), 21 | ], 22 | ), 23 | migrations.CreateModel( 24 | name='Choice', 25 | fields=[ 26 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 27 | ('choice_text', models.CharField(max_length=200)), 28 | ('votes', models.IntegerField(default=0)), 29 | ('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='polls.Question')), 30 | ], 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /events/migrations/0003_auto_20200331_1855.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.3 on 2020-03-31 15:55 2 | 3 | from django.db import migrations, models 4 | import django.utils.timezone 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('events', '0002_auto_20200116_2218'), 11 | ] 12 | 13 | operations = [ 14 | migrations.RemoveField( 15 | model_name='eventregistrationform', 16 | name='published', 17 | ), 18 | migrations.AlterField( 19 | model_name='event', 20 | name='sign_up_cancelling_deadline', 21 | field=models.DateTimeField(blank=True, default=django.utils.timezone.now, null=True, verbose_name='Avanmälningen stängs'), 22 | ), 23 | migrations.AlterField( 24 | model_name='event', 25 | name='sign_up_deadline', 26 | field=models.DateTimeField(blank=True, default=django.utils.timezone.now, null=True, verbose_name='Anmälningen stängs'), 27 | ), 28 | migrations.AlterField( 29 | model_name='event', 30 | name='slug', 31 | field=models.SlugField(blank=True, unique=True, verbose_name='Slug'), 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /core/utils.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from smtplib import SMTPException 3 | 4 | import requests 5 | from celery import shared_task 6 | from django.conf import settings 7 | from django.core.mail import send_mail 8 | 9 | logger = logging.getLogger("date") 10 | 11 | VALIDATION_URL = "https://challenges.cloudflare.com/turnstile/v0/siteverify" 12 | 13 | 14 | def validate_captcha(response: str) -> bool: 15 | secret_key = settings.TURNSTILE_SECRET_KEY 16 | if secret_key == "": 17 | logger.info("No captcha secret key defined") 18 | return True 19 | if response == "": 20 | logger.info("No captcha found in response") 21 | return False 22 | 23 | data = { 24 | 'secret': secret_key, 25 | 'response': response, 26 | } 27 | 28 | try: 29 | res = requests.post(VALIDATION_URL, data=data) 30 | except Exception: 31 | logger.info("Request to cloudflare failed") 32 | return False 33 | 34 | return res.json().get('success', False) 35 | 36 | 37 | @shared_task 38 | def send_email_task(*args, **kwargs) -> None: 39 | try: 40 | send_mail(*args, **kwargs) 41 | except SMTPException: 42 | logger.error(f"Failed sending email to: {args[3] or kwargs.get('to', '')}") 43 | -------------------------------------------------------------------------------- /templates/date/news/article.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load static %} 3 | {% load i18n %} 4 | 5 | {% block title %}{{ article.title }}{% endblock %} 6 | {% block content %} 7 | 8 | 9 | 10 | 11 |
    12 |
    13 | {% if article.category.name == "Albins Angels" %} 14 |
    15 | {% endif %} 16 |
    17 |

    {{ article.title }}

    18 |

    {% trans "Skriven " %}{{ article.published_time }} {% trans "av" %} 19 | 20 | {% if article.author.get_full_name != " " %}{{ article.author.get_full_name }}{% else %} 21 | {{ article.author }}{% endif %}

    22 | {{ article.content | safe }} 23 |
    24 |
    25 |
    26 | {% endblock %} 27 | -------------------------------------------------------------------------------- /polls/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import get_object_or_404 2 | from django.views import generic 3 | from members.models import Member 4 | import logging 5 | 6 | from .models import Question 7 | from .vote import handle_vote 8 | 9 | logger = logging.getLogger('date') 10 | 11 | 12 | class IndexView(generic.ListView): 13 | template_name = 'polls/index.html' 14 | context_object_name = 'latest_question_list' 15 | 16 | def get_queryset(self): 17 | """Return the last five published questions.""" 18 | return Question.objects.filter(published=True).order_by('-pub_date')[:5] 19 | 20 | 21 | class DetailView(generic.DetailView): 22 | model = Question 23 | template_name = 'polls/detail.html' 24 | 25 | 26 | class ResultsView(generic.DetailView): 27 | model = Question 28 | template_name = 'polls/results.html' 29 | 30 | 31 | def vote(request, question_id): 32 | question = get_object_or_404(Question, pk=question_id) 33 | 34 | if request.user.is_authenticated: 35 | user = Member.objects.get(username=request.user.username) 36 | else: 37 | user = request.user 38 | 39 | selected_choices = [choice_id for choice_id in set(request.POST.getlist('choice'))] 40 | 41 | return handle_vote(request, question, user, selected_choices) 42 | -------------------------------------------------------------------------------- /events/migrations/0004_auto_20220217_1841.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.13 on 2022-02-17 16:41 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('events', '0003_auto_20200331_1855'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='event', 16 | name='sign_up_avec', 17 | field=models.BooleanField(default=False, verbose_name='Avec'), 18 | ), 19 | migrations.AddField( 20 | model_name='eventattendees', 21 | name='avec_for', 22 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='events.eventattendees', verbose_name='Avec till'), 23 | ), 24 | migrations.AddField( 25 | model_name='eventregistrationform', 26 | name='hide_for_avec', 27 | field=models.BooleanField(default=False, verbose_name='Göm för avec'), 28 | ), 29 | migrations.AlterField( 30 | model_name='eventattendees', 31 | name='preferences', 32 | field=models.JSONField(blank=True, default=list, verbose_name='Svar'), 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /news/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from news import forms 4 | from news.models import Post, Category 5 | 6 | 7 | class CategoryAdmin(admin.ModelAdmin): 8 | list_display = ('name',) 9 | search_fields = ('name',) 10 | 11 | 12 | class PostAdmin(admin.ModelAdmin): 13 | 14 | list_display = ('title', 'author', 'category', 'created_time', 'modified_time', 'published') 15 | search_fields = ('title', 'author', 'created_time') 16 | 17 | def add_view(self, request, form_url='', extra_context=None): 18 | self.fields = forms.PostCreationForm.Meta.fields 19 | return super(PostAdmin, self).add_view(request, form_url, extra_context) 20 | 21 | def change_view(self, request, object_id, form_url='', extra_context=None): 22 | self.fields = forms.PostEditForm.Meta.fields 23 | return super(PostAdmin, self).change_view(request, object_id, form_url, extra_context) 24 | 25 | def get_form(self, request, obj=None, change=False, **kwargs): 26 | if obj is None: 27 | form = forms.PostCreationForm 28 | else: 29 | form = forms.PostEditForm 30 | 31 | form.user = request.user 32 | return form 33 | 34 | 35 | admin.site.register(Category, CategoryAdmin) 36 | admin.site.register(Post, PostAdmin) 37 | -------------------------------------------------------------------------------- /scripts/merge_events.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import sys 3 | import os 4 | import django 5 | from django.core.files import File 6 | 7 | 8 | sys.path.append("/code") 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings.date") 10 | django.setup() 11 | 12 | from events.models import EventAttendees, Event 13 | 14 | source_index = sys.argv[1] 15 | target_index = sys.argv[2] 16 | 17 | source_event = Event.objects.filter(id=source_index).first() 18 | target_event = Event.objects.filter(id=target_index).first() 19 | 20 | source_participants = EventAttendees.objects.filter(event=source_event.id).all() 21 | 22 | for participant in source_participants: 23 | if participant.avec_for: 24 | target_event.add_event_attendance( 25 | user= participant.user, 26 | email=participant.email, 27 | anonymous=participant.anonymous, 28 | preferences=participant.preferences, 29 | avec_for=EventAttendees.objects.filter(event=target_event.id, email=participant.avec_for.email).first() 30 | ) 31 | continue 32 | target_event.add_event_attendance( 33 | user= participant.user, 34 | email=participant.email, 35 | anonymous=participant.anonymous, 36 | preferences=participant.preferences 37 | ) 38 | 39 | 40 | -------------------------------------------------------------------------------- /static/kk/date/css/date-root.css: -------------------------------------------------------------------------------- 1 | /* Root colors */ 2 | /* use with var(--color); included in base.html*/ 3 | 4 | :root { 5 | --primaryColor: rgb(10, 140, 65); 6 | --primaryColorLight:#2a693d; 7 | --primaryColorLighte:#3b8b53; 8 | --primaryColorLighter: rgb(10, 170, 77); 9 | --linkColorDark: #3a80c3; 10 | --linkColorDarkHover: #26547f; 11 | --secondaryColor: #fff; 12 | --secondaryColorDarker: #e0e0e0; 13 | --luciaBackgroundColor: #960014; 14 | --primaryColorTransparent: rgb(10, 140, 65, 0.9); 15 | --primaryColorTransparenter: rgb(10, 140, 65, 0.8); 16 | --textColorLight: #fff; 17 | --textColorLightish: #eaeaea; 18 | --textColorDark: #000; 19 | --textColorDarkish: #202020; 20 | --textColorMedium: #606060; 21 | --helpText: #888; 22 | --helpTextLighter: rgb(199, 199, 199); 23 | --linkColor: rgb(44, 148, 96); 24 | --linkColorSecondary: rgb(10, 170, 77); 25 | --linkSecondaryHover: #2a693d; 26 | --linkColorLight: rgb(37, 240, 122); 27 | --linkColorLightHover: rgb(29, 185, 94); 28 | --warning: rgb(216, 153, 146); 29 | --warning-darker: rgb(213, 91, 78); 30 | --navClickHighlight: rgba(255, 255, 255, 0.25); 31 | --navHoverHighlight: rgba(255, 255, 255, 0.15); 32 | --defaultPadding: 20px; 33 | --defaultRadius: 5px; 34 | } -------------------------------------------------------------------------------- /polls/migrations/0003_remove_question_members_only_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.7 on 2023-11-16 18:04 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('polls', '0002_auto_20201111_1932'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='question', 15 | name='members_only', 16 | ), 17 | migrations.RemoveField( 18 | model_name='question', 19 | name='ordinary_members_only', 20 | ), 21 | migrations.RemoveField( 22 | model_name='question', 23 | name='vote_members_only', 24 | ), 25 | migrations.AddField( 26 | model_name='question', 27 | name='required_multiple_choices', 28 | field=models.IntegerField(blank=True, null=True, verbose_name='Antal flerval som krävs'), 29 | ), 30 | migrations.AddField( 31 | model_name='question', 32 | name='voting_options', 33 | field=models.IntegerField(choices=[(1, 'Vem som helst'), (2, 'Endast medlemmar'), (3, 'Endast ordinarie medlemmar'), (4, 'Endast röstberättigade medlemmar')], default=1, verbose_name='Valmöjligheter'), 34 | ), 35 | ] 36 | -------------------------------------------------------------------------------- /archive/urls.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from django.contrib.auth.decorators import login_required 4 | from django.urls import path 5 | 6 | from . import views 7 | 8 | app_name = 'archive' 9 | 10 | urlpatterns = [ 11 | # New pictures 12 | path('pictures/', login_required(views.year_index), name='years'), 13 | # /archive/pictures// 14 | path('pictures//', login_required(views.picture_index), name='pictures'), 15 | # /archive/pictures/// 16 | path('pictures/id//', login_required(views.picture_detail), name='picture_detail'), 17 | path('pictures///', login_required(views.picture_detail), name='detail'), 18 | # /documents/ 19 | path('documents/', login_required(views.FilteredDocumentsListView.as_view()), name='documents'), 20 | path('exams/', login_required(views.exams_index), name='exams'), 21 | path('exams//', login_required(views.FilteredExamsListView.as_view()), name='exams_detail'), 22 | path('exam-upload//', login_required(views.exam_upload), name='exam_upload'), 23 | path('exam-archive-upload/', login_required(views.exam_archive_upload), name='exam_archive_upload'), 24 | path('upload/', login_required(views.upload), name='upload'), 25 | path('cleanMedia/', login_required(views.clean_media), name='cleanMedia'), 26 | 27 | ] -------------------------------------------------------------------------------- /members/managers.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.base_user import BaseUserManager 2 | 3 | 4 | class MemberManager(BaseUserManager): 5 | use_in_migrations = True 6 | 7 | def _create_user(self, username, password, **extra_fields): 8 | """ 9 | Creates and saves members with email and password 10 | """ 11 | if not username: 12 | raise ValueError('Username is required') 13 | member = self.model(username=username, **extra_fields) 14 | member.set_password(password) 15 | member.save(using=self._db) 16 | return member 17 | 18 | def create_user(self, username, password=None, **extra_fields): 19 | return self._create_user(username, password, **extra_fields) 20 | 21 | def create_superuser(self, username, password, **extra_fields): 22 | extra_fields.setdefault('is_superuser', True) 23 | # extra_fields.setdefault('is_staff', True) 24 | 25 | if extra_fields.get('is_superuser') is not True: 26 | raise ValueError('Superuser must have is_superuser=True.') 27 | 28 | return self._create_user(username, password, **extra_fields) 29 | 30 | def get_by_natural_key(self, username): 31 | case_insensitive_username_field = '{}__iexact'.format(self.model.USERNAME_FIELD) 32 | return self.get(**{case_insensitive_username_field: username}) -------------------------------------------------------------------------------- /templates/common/polls/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load static %} 3 | {% load i18n %} 4 | {% block title %}{{ ASSOCIATION_NAME_SHORT }} - {% trans "Poll" %}{% endblock %} 5 | 6 | {% block content %} 7 | 8 | 9 | 10 | 11 | 12 |
    13 |
    14 |
    15 |

    Polls

    16 | {% if latest_question_list %} 17 |
      18 | {% for question in latest_question_list %} 19 | {% if question.published %} 20 |
    • 21 | {{ question.question_text }} 22 |
    • 23 | {% endif %} 24 | 25 | {% endfor %} 26 |
    27 | {% else %} 28 |

    {% trans "Det finns inga aktiva omröstningar" %}

    29 | {% endif %} 30 |
    31 |
    32 |
    33 | 34 | {% endblock %} 35 | -------------------------------------------------------------------------------- /polls/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import Choice, Question, Vote 4 | 5 | 6 | class ChoiceInline(admin.TabularInline): 7 | model = Choice 8 | extra = 0 9 | readonly_fields = ['votes'] 10 | 11 | 12 | class VoteInline(admin.TabularInline): 13 | model = Vote 14 | extra = 0 15 | readonly_fields = ['user'] 16 | 17 | def has_add_permission(self, request, obj=None): 18 | return False 19 | 20 | def has_delete_permission(self, request, obj=None): 21 | if request.user.is_superuser: 22 | return True 23 | return False 24 | 25 | def full_name(self, obj): 26 | return obj.user.get_full_name() 27 | 28 | 29 | class QuestionAdmin(admin.ModelAdmin): 30 | fieldsets = [ 31 | (None, 32 | {'fields': 33 | [ 34 | 'question_text', 35 | 'voting_options', 36 | 'multiple_choice', 37 | 'required_multiple_choices', 38 | 'published', 39 | 'show_results', 40 | 'end_vote' 41 | ]}), 42 | ] 43 | list_display = ('question_text', 'pub_date',) 44 | inlines = [ChoiceInline, VoteInline] 45 | list_filter = ['pub_date'] 46 | search_fields = ['question_text'] 47 | 48 | 49 | admin.site.register(Question, QuestionAdmin) 50 | -------------------------------------------------------------------------------- /templates/common/ctf/detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load static %} 3 | {% load i18n %} 4 | {% block title %}{{ ASSOCIATION_NAME_SHORT }} - CTF{% endblock %} 5 | 6 | {% block content %} 7 | 8 | 9 | 10 | 11 | 12 |
    13 |
    14 |
    15 | 16 |
      17 | {% if ctf.ctf_is_open %} 18 |

      Flags:

      19 | {% for flag in flags %} 20 |
    • 21 | {{ flag.title }} 22 | {% if flag.solver %} 23 | 24 | {% endif %} 25 |
    • 26 | {% endfor %} 27 | {% endif %} 28 |
    29 |

    {{ ctf.title }}

    30 |

    {{ ctf.content | safe }}

    31 |
    32 |
    33 |
    34 | 35 | {% endblock %} 36 | -------------------------------------------------------------------------------- /static/common/core/js/eventform.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | $(document).ready(function() { 3 | 4 | if (!$('#id_sign_up').is(':checked')) { 5 | $('[class*="form-row field-sign_up_"]').hide(); 6 | $('fieldset.module').find('h2').each((index, element) => { 7 | if ($(element).text().match("Anmälningsfält")) $(element).parent().hide(); 8 | }); 9 | $('#eventattendees_set-empty').parents('fieldset.module').hide(); 10 | } 11 | $('select[id$="type"]').change( function() { 12 | var rowEdit = $($(this).parents('tr')).find('input[id$="choice_list"]'); 13 | if( this.value == "select") { 14 | rowEdit.prop('disabled', false); 15 | } else { 16 | rowEdit.prop('disabled', true); 17 | } 18 | }); 19 | $('#id_sign_up').change( function() { 20 | $('[class*="form-row field-sign_up_"]').toggle(); 21 | $('fieldset.module').find('h2').each((index, element) => { 22 | if ($(element).text().match("Anmälningsfält")) $(element).parent().toggle(); 23 | }); 24 | $('#eventattendees_set-empty').parents('fieldset.module').toggle(); 25 | }); 26 | $('select[id$="type"]').change(); 27 | 28 | $('p.datetime').find('br').replaceWith("  "); 29 | }); 30 | })(django.jQuery); 31 | -------------------------------------------------------------------------------- /publications/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import PDFFile 3 | 4 | @admin.register(PDFFile) 5 | class PDFFileAdmin(admin.ModelAdmin): 6 | list_display = ('title', 'publication_date', 'is_public', 'requires_login', 'uploaded_at', 'updated_at') 7 | list_filter = ('is_public', 'requires_login', 'uploaded_at', 'updated_at', 'publication_date') 8 | search_fields = ('title', 'slug', 'description') 9 | prepopulated_fields = {'slug': ('title',)} 10 | readonly_fields = ('uploaded_at', 'updated_at') 11 | fieldsets = ( 12 | (None, { 13 | 'fields': ('title', 'slug', 'publication_date', 'description', 'file') 14 | }), 15 | ('Access Control', { 16 | 'fields': ('is_public', 'requires_login'), 17 | 'description': 'Control who can access this PDF.' 18 | }), 19 | ('Timestamps', { 20 | 'fields': ('uploaded_at', 'updated_at'), 21 | 'classes': ('collapse',) 22 | }), 23 | ) 24 | 25 | def get_prepopulated_fields(self, request, obj=None): 26 | # Only prepopulate slug for new objects 27 | if obj is None: 28 | return self.prepopulated_fields 29 | return {} 30 | 31 | def get_readonly_fields(self, request, obj=None): 32 | if obj: 33 | return self.readonly_fields + ('file',) 34 | return self.readonly_fields 35 | -------------------------------------------------------------------------------- /static/on/events/css/form.css: -------------------------------------------------------------------------------- 1 | .form p{ 2 | display: grid; 3 | grid-template-columns: 30% 1fr; 4 | grid-gap: 5px; 5 | box-sizing: border-box; 6 | } 7 | 8 | .form p label, input, select{ 9 | font-family: 'Josefin Sans', sans-serif; 10 | padding: 5px; 11 | border-radius: 5px; 12 | align-self: end; 13 | } 14 | 15 | .form p label{ 16 | word-break: normal; 17 | } 18 | 19 | .form p input[type=checkbox]{ 20 | width: 20px; 21 | height: 20px; 22 | border-radius: 5px; 23 | } 24 | 25 | .form > ul.errorlist{ 26 | display: none; 27 | } 28 | 29 | .button{ 30 | background-color: var(--primaryColorLighter); 31 | border: none; 32 | color: var(--textColorLight); 33 | padding: 10px 20px; 34 | text-align: center; 35 | font-size: 16px; 36 | border-radius: 5px; 37 | font-family: 'Josefin Sans', sans-serif; 38 | cursor: pointer; 39 | } 40 | 41 | .button:hover{ 42 | transition: 0.2s ease; 43 | background-color: var(--primaryColorLighte); 44 | color: var(--linkColorLightHover); 45 | } 46 | 47 | .button:active{ 48 | background-color: var(--primaryColorLighter); 49 | } 50 | 51 | .warning{ 52 | color: var(--warning) 53 | } 54 | 55 | @media only screen and (max-width: 768px) { 56 | .form p{ 57 | display: block; 58 | } 59 | input, select, label { 60 | display: block; 61 | width: 100%; 62 | } 63 | } -------------------------------------------------------------------------------- /static/common/events/css/form.css: -------------------------------------------------------------------------------- 1 | .form p{ 2 | display: grid; 3 | grid-template-columns: 30% 1fr; 4 | grid-gap: 5px; 5 | box-sizing: border-box; 6 | } 7 | 8 | .form p label, input, select{ 9 | font-family: 'Josefin Sans', sans-serif; 10 | padding: 5px; 11 | border-radius: 5px; 12 | align-self: end; 13 | } 14 | 15 | .form p label{ 16 | word-break: break-all; 17 | } 18 | 19 | .form p input[type=checkbox]{ 20 | width: 20px; 21 | height: 20px; 22 | border-radius: 5px; 23 | } 24 | 25 | .form > ul.errorlist{ 26 | display: none; 27 | } 28 | 29 | .button{ 30 | background-color: var(--primaryColorLighter); 31 | border: none; 32 | color: var(--textColorLight); 33 | padding: 10px 20px; 34 | text-align: center; 35 | font-size: 16px; 36 | border-radius: 5px; 37 | font-family: 'Josefin Sans', sans-serif; 38 | cursor: pointer; 39 | } 40 | 41 | .button:hover{ 42 | transition: 0.2s ease; 43 | background-color: var(--primaryColorLighte); 44 | color: var(--linkColorLightHover); 45 | } 46 | 47 | .button:active{ 48 | background-color: var(--primaryColorLighter); 49 | } 50 | 51 | .warning{ 52 | color: var(--warning) 53 | } 54 | 55 | @media only screen and (max-width: 768px) { 56 | .form p{ 57 | display: block; 58 | } 59 | input, select, label { 60 | display: block; 61 | width: 100%; 62 | } 63 | } -------------------------------------------------------------------------------- /.github/workflows/web_startup.yaml: -------------------------------------------------------------------------------- 1 | name: Run unit tests and ping website 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - develop 7 | push: 8 | branches: 9 | - develop 10 | workflow_dispatch: 11 | 12 | jobs: 13 | test: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v6 19 | 20 | - name: Install Docker Compose 21 | run: | 22 | sudo apt-get update 23 | sudo apt-get install -y docker-compose 24 | 25 | - name: Run django unit tests 26 | run: | 27 | source env.sh dev 28 | date-test 29 | 30 | ping_test: 31 | runs-on: ubuntu-latest 32 | 33 | steps: 34 | - name: Checkout repository 35 | uses: actions/checkout@v6 36 | 37 | - name: Install Docker Compose 38 | run: | 39 | sudo apt-get update 40 | sudo apt-get install -y docker-compose 41 | 42 | - name: Build and run Docker Compose 43 | run: | 44 | source env.sh dev 45 | docker-compose up -d 46 | 47 | - name: Wait for web container 48 | run: | 49 | sleep 20 50 | 51 | - name: Check HTTP response 52 | run: | 53 | if ! curl -sSf http://localhost:8000 >/dev/null; then 54 | echo "Failed to get HTTP response. Exiting..." 55 | exit 1 56 | fi 57 | -------------------------------------------------------------------------------- /templates/common/polls/results.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load static %} 3 | {% load i18n %} 4 | {% block title %}{{ ASSOCIATION_NAME_SHORT }} - {% trans "Poll" %}{% endblock %} 5 | 6 | {% block content %} 7 | 8 | 9 | 10 | 11 | 12 |
    13 |
    14 |
    15 | {% trans "Tillbaka" %} 16 |
    17 |

    {{ question.question_text }}

    18 |

    {% trans "Resultat: "%}

    19 |
      20 | {% if question.show_results %} 21 | {% for choice in question.choice_set.all %} 22 |
    • {{ choice.choice_text }}: {{ choice.votes }} vote{{ choice.votes|pluralize }} -- {{ choice.get_vote_percentage }}%
    • 23 | {% endfor %} 24 |

      {% trans "Totala röster: "%} {{ question.get_total_votes }}

      25 | {% else %} 26 |

      {% trans "Resultaten är gömda" %}

      27 | {% endif %} 28 |
    29 |
    30 |
    31 |
    32 |
    33 | 34 | 35 | {% endblock %} -------------------------------------------------------------------------------- /templates/common/ctf/flag_detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load static %} 3 | {% load i18n %} 4 | {% block title %}{{ ASSOCIATION_NAME_SHORT }} - CTF{% endblock %} 5 | 6 | {% block content %} 7 | 8 | 9 | 10 | 11 | 12 |
    13 |
    14 |
    15 | {% if ctf.ctf_is_open %} 16 | 17 |

    {{ flag.title }}

    18 |

    {{ flag.clues | safe }}

    19 | {% if valid %}

    {{ valid }}

    {% endif %} 20 | {% if invalid %}

    {{ invalid }}

    {% endif %} 21 | {% if flag.solver or solved %} 22 |

    [SOLVED]
    av: {{ flag.solver }}
    tid: {{ flag.solved_date|timeuntil:ctf.start_date }}

    23 | {% endif %} 24 |
    25 | {% csrf_token %} 26 | {{ form.as_p }} 27 | 28 |
    29 | {% endif %} 30 |
    31 |
    32 |
    33 | 34 | {% endblock %} 35 | -------------------------------------------------------------------------------- /ctf/migrations/0002_guess.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.1 on 2024-02-06 23:34 2 | 3 | import django.db.models.deletion 4 | from django.conf import settings 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('ctf', '0001_initial'), 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Guess', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('guess', models.CharField(max_length=200, verbose_name='Gissning')), 21 | ('correct', models.BooleanField(default=False, verbose_name='Korrekt')), 22 | ('timestamp', models.DateTimeField(auto_now_add=True, verbose_name='Tid')), 23 | ('ctf', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ctf.ctf', verbose_name='CTF')), 24 | ('flag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ctf.flag', verbose_name='Flagga')), 25 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Användare')), 26 | ], 27 | options={ 28 | 'verbose_name': 'Gissning', 29 | 'verbose_name_plural': 'Gissningar', 30 | }, 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /events/feed.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from django.urls import reverse 4 | from django.utils import timezone 5 | from django.utils.html import strip_tags 6 | from django_ical.views import ICalFeed 7 | 8 | from events.models import Event 9 | 10 | 11 | class EventFeed(ICalFeed): 12 | product_id = '-//date.abo.fi//feed//EN' 13 | timezone = timezone.get_current_timezone_name() 14 | file_name = "DaTe-events.ics" 15 | 16 | def __call__(self, request, *args, **kwargs): 17 | self.host = request.get_host() 18 | return super(EventFeed, self).__call__(request, *args, **kwargs) 19 | 20 | def items(self): 21 | return Event.objects.filter(published=True).order_by('-event_date_start') 22 | 23 | def item_guid(self, item): 24 | return "{}{}".format(item.id, 'date.abo.fi') 25 | 26 | def item_title(self, item): 27 | return "{}".format(item.title) 28 | 29 | def item_description(self, item): 30 | text_only = re.sub('[ \t]+', ' ', strip_tags(item.content)) 31 | return text_only.replace('\n ', '\n').strip() 32 | 33 | def item_start_datetime(self, item): 34 | return item.event_date_start 35 | 36 | def item_end_datetime(self, item): 37 | return item.event_date_end 38 | 39 | def item_created(self, item): 40 | return item.created_time 41 | 42 | def item_updateddate(self, item): 43 | return item.modified_time 44 | 45 | def item_link(self, item): 46 | return '{a}{b}'.format(a=self.host, b=reverse('events:detail', args=[item.slug])) 47 | -------------------------------------------------------------------------------- /core/urls/on.py: -------------------------------------------------------------------------------- 1 | """date URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.1/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | # update 17 | 18 | from django.conf import settings 19 | from django.conf.urls.static import static 20 | from django.contrib import admin 21 | from django.urls import include, path 22 | 23 | from events import views as events 24 | from date import views as date 25 | 26 | app_name = 'core' 27 | 28 | urlpatterns = [ 29 | path('', events.IndexView.as_view(), name='index'), 30 | path('admin/', admin.site.urls), 31 | path('', include('events.urls')), 32 | path('pages/', include('staticpages.urls')), 33 | path('users/', include('members.urls')), 34 | path("ckeditor5/", include('django_ckeditor_5.urls')), 35 | ] 36 | 37 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 38 | urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 39 | 40 | handler404 = date.handler404 41 | handler500 = date.handler500 42 | -------------------------------------------------------------------------------- /templates/common/polls/detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load static %} 3 | {% load i18n %} 4 | {% block title %}{{ ASSOCIATION_NAME_SHORT }} - {% trans "Poll" %}{% endblock %} 5 | 6 | {% block content %} 7 | 8 | 9 | 10 | 11 | 12 |
    13 |
    14 |
    15 | {% trans "Tillbaka" %} 16 |

    {{ question.question_text }}

    17 | 18 | {% if error_message %}

    {{ error_message }}

    {% endif %} 19 | 20 |
    21 | {% csrf_token %} 22 | {% for choice in question.choice_set.all %} 23 | 30 |
    31 | {% endfor %} 32 | 33 |
    34 |
    35 |
    36 |
    37 | 38 | 39 | 40 | {% endblock %} -------------------------------------------------------------------------------- /static/common/core/css/cookie-banner.css: -------------------------------------------------------------------------------- 1 | #cookie-banner { 2 | display: none; 3 | position: fixed; 4 | bottom: 0; 5 | left: 0; 6 | right: 0; 7 | background-color: #f8f9fa; 8 | border-top: 1px solid #dee2e6; 9 | padding: 1rem; 10 | z-index: 1000; 11 | box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1); 12 | } 13 | 14 | .cookie-banner-content { 15 | max-width: 1200px; 16 | margin: 0 auto; 17 | display: flex; 18 | justify-content: space-between; 19 | align-items: center; 20 | flex-wrap: wrap; 21 | gap: 1rem; 22 | } 23 | 24 | .cookie-banner-text { 25 | flex: 1; 26 | margin: 0; 27 | color: #212529; 28 | } 29 | 30 | .cookie-banner-buttons { 31 | display: flex; 32 | gap: 1rem; 33 | } 34 | 35 | .cookie-banner-button { 36 | padding: 0.5rem 1rem; 37 | border-radius: 0.25rem; 38 | border: none; 39 | cursor: pointer; 40 | font-weight: 500; 41 | transition: background-color 0.2s; 42 | } 43 | 44 | #accept-cookies { 45 | background-color: #0d6efd; 46 | color: white; 47 | } 48 | 49 | #accept-cookies:hover { 50 | background-color: #0b5ed7; 51 | } 52 | 53 | #decline-cookies { 54 | background-color: #6c757d; 55 | color: white; 56 | } 57 | 58 | #decline-cookies:hover { 59 | background-color: #5c636a; 60 | } 61 | 62 | @media (max-width: 768px) { 63 | .cookie-banner-content { 64 | flex-direction: column; 65 | text-align: center; 66 | } 67 | 68 | .cookie-banner-buttons { 69 | width: 100%; 70 | justify-content: center; 71 | } 72 | } -------------------------------------------------------------------------------- /templates/common/alumni/update_verify.html: -------------------------------------------------------------------------------- 1 | {% extends "core/base.html" %} 2 | {% load static %} 3 | {% load i18n %} 4 | 5 | {% block title %} 6 | Verifiera din e-postadress 7 | {% endblock %} 8 | 9 | {% block content %} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 42 | 43 | {% endblock %} 44 | 45 | -------------------------------------------------------------------------------- /publications/views.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponseForbidden 2 | from django.shortcuts import render, get_object_or_404, redirect 3 | from .models import PDFFile 4 | from django.core.paginator import Paginator 5 | 6 | 7 | def pdf_view(request, slug): 8 | pdf_file = get_object_or_404(PDFFile, slug=slug) 9 | 10 | # Check if the PDF is public and if login is required 11 | if not pdf_file.is_public: 12 | return HttpResponseForbidden("You do not have permission to access this PDF.") 13 | 14 | if pdf_file.requires_login and not request.user.is_authenticated: 15 | return redirect('login') 16 | 17 | context = { 18 | 'pdf_url': pdf_file.get_file_url(), 19 | 'pdf_file': pdf_file, 20 | } 21 | return render(request, 'publications/viewer.html', context) 22 | 23 | def pdf_list(request): 24 | if request.user.is_authenticated: 25 | # For authenticated users, show public PDFs and those that require login, but hide non-public ones 26 | pdfs = PDFFile.objects.filter(is_public=True) 27 | else: 28 | # For non-authenticated users, only show public PDFs that don't require login 29 | pdfs = PDFFile.objects.filter(is_public=True, requires_login=False) 30 | 31 | pdfs = pdfs.order_by('-publication_date') 32 | 33 | paginator = Paginator(pdfs, 10) # Show 10 PDFs per page 34 | page_number = request.GET.get('page') 35 | page_obj = paginator.get_page(page_number) 36 | 37 | context = { 38 | 'page_obj': page_obj, 39 | } 40 | 41 | return render(request, 'publications/list.html', context) 42 | 43 | -------------------------------------------------------------------------------- /templates/common/archive/document_index.html: -------------------------------------------------------------------------------- 1 | {% extends 'core/base.html' %} 2 | {% load app_filters %} 3 | {% load static %} 4 | {% load i18n %} 5 | {% load render_table from django_tables2 %} 6 | {% load bootstrap3 %} 7 | {% block title %}{% trans 'Dokument arkiv' %}{% endblock %} 8 | 9 | {% block content %} 10 | 11 | 12 | 13 |
    14 |
    15 |
    16 |
    17 |

    {% trans "Dokumentarkiv" %}

    18 | {% if request.user|in_group:"styrelse"%} 19 | {% trans 'Ladda upp dokument' %} 20 | {% endif %} 21 |
    22 |
    23 | {% if filter %} 24 |
    25 | {% bootstrap_form filter.form layout='inline' %} 26 | {% bootstrap_button 'filter' button_class="btn-light" %} 27 |
    28 | {% endif %} 29 |
    30 |
    31 | {% render_table table 'django_tables2/bootstrap.html' %} 32 |
    33 |
    34 |
    35 |
    36 | {% endblock %} 37 | 38 | -------------------------------------------------------------------------------- /staticpages/migrations/0002_auto_20200805_1027.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.3 on 2020-08-05 07:27 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('staticpages', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='staticpage', 16 | name='dropdown_element', 17 | field=models.IntegerField(default=0), 18 | ), 19 | migrations.AddField( 20 | model_name='staticpagenav', 21 | name='nav_element', 22 | field=models.IntegerField(default=0), 23 | ), 24 | migrations.AlterField( 25 | model_name='staticpage', 26 | name='category', 27 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='staticpages.StaticPageNav'), 28 | ), 29 | migrations.CreateModel( 30 | name='StaticUrl', 31 | fields=[ 32 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 33 | ('title', models.CharField(max_length=255, verbose_name='Titel')), 34 | ('url', models.CharField(max_length=200, verbose_name='Url')), 35 | ('dropdown_element', models.IntegerField(default=0)), 36 | ('category', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to='staticpages.StaticPageNav')), 37 | ], 38 | ), 39 | ] 40 | -------------------------------------------------------------------------------- /news/views.py: -------------------------------------------------------------------------------- 1 | from django.core.paginator import Paginator 2 | from django.shortcuts import render, get_object_or_404 3 | 4 | from . import models 5 | 6 | LATEST_NEWS_POSTS = 10 7 | 8 | 9 | def index(request): 10 | latest_news = models.Post.objects.filter(published=True, category__isnull=True).reverse() 11 | paginator = Paginator(latest_news, LATEST_NEWS_POSTS) 12 | page_number = request.GET.get('page') 13 | page_obj = paginator.get_page(page_number) 14 | return render(request, 'news/index.html', {'latest_news_items': page_obj}) 15 | 16 | 17 | def article(request, slug): 18 | post = get_object_or_404(models.Post, slug=slug, published=True, category__isnull=True) 19 | return render(request, 'news/article.html', {'article': post}) 20 | 21 | 22 | def author(request, author): 23 | articles = models.Post.objects.filter(author__username__exact=author, published=True).order_by('-published_time') 24 | return render(request, 'news/author.html', {'articles': articles}) 25 | 26 | 27 | def category_index(request, category): 28 | latest_news = models.Post.objects.filter(published=True, category__slug=category).reverse() 29 | paginator = Paginator(latest_news, LATEST_NEWS_POSTS) 30 | page_number = request.GET.get('page') 31 | page_obj = paginator.get_page(page_number) 32 | return render(request, 'news/index.html', {'latest_news_items': page_obj}) 33 | 34 | 35 | def category_article(request, category, slug): 36 | post = get_object_or_404(models.Post, slug=slug, published=True, category__slug=category) 37 | return render(request, 'news/article.html', {'article': post}) 38 | -------------------------------------------------------------------------------- /staticpages/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.7 on 2020-07-14 09:16 2 | 3 | import ckeditor.fields 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import django.utils.timezone 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='StaticPageNav', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('category_name', models.CharField(max_length=255, verbose_name='Kategori')), 22 | ], 23 | ), 24 | migrations.CreateModel( 25 | name='StaticPage', 26 | fields=[ 27 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 28 | ('title', models.CharField(max_length=255, verbose_name='Titel')), 29 | ('content', ckeditor.fields.RichTextField(blank=True, verbose_name='Innehåll')), 30 | ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Skapad')), 31 | ('modified_time', models.DateTimeField(blank=True, editable=False, null=True, verbose_name='Modifierad')), 32 | ('slug', models.SlugField(unique=True, verbose_name='Slug')), 33 | ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='staticpages.StaticPageNav')), 34 | ], 35 | ), 36 | ] 37 | -------------------------------------------------------------------------------- /core/settings/biocum.py: -------------------------------------------------------------------------------- 1 | from .common import * # noqa 2 | 3 | 4 | TEMPLATES = [ 5 | { 6 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 7 | 'DIRS': [ 8 | 'templates/biocum', 9 | 'templates/date', 10 | *COMMON_TEMPLATE_DIRS, 11 | ], 12 | 'APP_DIRS': True, 13 | 'OPTIONS': { 14 | 'context_processors': [ 15 | *COMMON_CONTEXT_PROCESSORS, 16 | ], 17 | }, 18 | }, 19 | ] 20 | 21 | INSTALLED_APPS = get_installed_apps([ 22 | 'staticpages', 23 | 'news', 24 | 'events', 25 | 'ads', 26 | 'social', 27 | 'polls', 28 | 'archive.apps.ArchiveConfig', 29 | ]) 30 | 31 | ROOT_URLCONF = 'core.urls.biocum' 32 | 33 | STAFF_GROUPS = get_staff_groups([ 34 | 'styrelse', 35 | 'admin', 36 | 'fotograf', 37 | 'rösträknare' 38 | ]) 39 | 40 | STATICFILES_DIRS = [ 41 | os.path.join(BASE_DIR, 'static/biocum'), 42 | os.path.join(BASE_DIR, 'static/common'), 43 | ] 44 | 45 | CONTENT_VARIABLES = { 46 | "SITE_URL": "https://biologica.fi", 47 | "ASSOCIATION_NAME": "Biologica", 48 | "ASSOCIATION_NAME_FULL": "Biologica rf", 49 | "ASSOCIATION_NAME_SHORT": "Biologica", 50 | "ASSOCIATION_EMAIL": "biologica@abo.fi", 51 | "ASSOCIATION_ADDRESS_L1": "Biocity, 2:a vån", 52 | "ASSOCIATION_ADDRESS_L2": "Artillerigatan 6", 53 | "ASSOCIATION_POSTAL_CODE": "20520 ÅBO", 54 | "SOCIAL_BUTTONS": [ 55 | ["fa-facebook-f", "https://www.facebook.com/Biologicarf/"], 56 | ["fa-instagram", "https://www.instagram.com/biologica_rf/"], 57 | ], 58 | } 59 | -------------------------------------------------------------------------------- /static/common/prettyjson/prettyjson.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | .jsonview { 3 | font-family: monospace; 4 | font-size: 1.1em; 5 | white-space: pre-wrap 6 | } 7 | 8 | .jsonview .prop { 9 | font-weight: 700 10 | } 11 | 12 | .jsonview .null { 13 | color: red 14 | } 15 | 16 | .jsonview .bool, .jsonview .num { 17 | color: #00f 18 | } 19 | 20 | .jsonview .string { 21 | color: green; 22 | white-space: pre-wrap 23 | } 24 | 25 | .jsonview .string.multiline { 26 | display: inline-block; 27 | vertical-align: text-top 28 | } 29 | 30 | .jsonview .collapser { 31 | position: absolute; 32 | left: -1em; 33 | cursor: pointer 34 | } 35 | 36 | .jsonview .collapsible { 37 | transition: height 1.2s; 38 | transition: width 1.2s 39 | } 40 | 41 | .jsonview .collapsible.collapsed { 42 | height: .8em; 43 | width: 1em; 44 | display: inline-block; 45 | overflow: hidden; 46 | margin: 0 47 | } 48 | 49 | .jsonview .collapsible.collapsed:before { 50 | content: "…"; 51 | width: 1em; 52 | margin-left: .2em 53 | } 54 | 55 | .jsonview .collapser.collapsed { 56 | transform: rotate(0) 57 | } 58 | 59 | .jsonview .q { 60 | display: inline-block; 61 | width: 0; 62 | color: transparent 63 | } 64 | 65 | .jsonview li { 66 | position: relative 67 | } 68 | 69 | .jsonview ul { 70 | list-style: none; 71 | margin: 0 0 0 2em; 72 | padding: 0 73 | } 74 | 75 | .jsonview h1 { 76 | font-size: 1.2em 77 | } 78 | 79 | form .jsonview ul { 80 | margin: 0 0 0 2em; 81 | padding: 0; 82 | } 83 | 84 | .parsed { 85 | display: none; 86 | } 87 | 88 | div.parsed { 89 | max-height: 25em; 90 | } -------------------------------------------------------------------------------- /events/websocket_utils.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from copy import deepcopy 3 | from django.utils.translation import gettext 4 | 5 | from asgiref.sync import async_to_sync 6 | from channels.layers import get_channel_layer 7 | 8 | logger = logging.getLogger('date') 9 | 10 | 11 | def ws_send(event_slug, form, public_info): 12 | channel_layer = get_channel_layer() 13 | async_to_sync(channel_layer.group_send)(f"event_{event_slug}", 14 | {"type": "event_message", **ws_data(form, public_info)}) 15 | # Send ws again if avec 16 | if dict(form.cleaned_data).get('avec'): 17 | newform = deepcopy(form) 18 | newform.cleaned_data['user'] = dict(newform.cleaned_data).get('avec_user') 19 | public_info = '' 20 | async_to_sync(channel_layer.group_send)(f"event_{event_slug}", 21 | {"type": "event_message", **ws_data(newform, public_info)}) 22 | 23 | 24 | def ws_data(form, public_info): 25 | pref = dict(form.cleaned_data) # Creates copy of form 26 | 27 | fields = [] # (fieldName, value) tuples 28 | anonymous = pref['anonymous'] 29 | 30 | fields.append(("user", gettext("Anonymt") if anonymous else pref['user'])) 31 | # parse the public info and only send that through websockets. 32 | # the extra fields are reversed in the details.html template and must be reversed here too 33 | for info in reversed(public_info): 34 | key = str(info) 35 | if key in pref: 36 | fields.append(( info.name, str(pref[key]) )) # stringify the field value before sending, for consistency 37 | 38 | return {"data": { "fields": fields, "anonymous": anonymous }} 39 | -------------------------------------------------------------------------------- /members/migrations/0006_alumnisignup.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.4 on 2023-08-17 16:59 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('members', '0005_alter_alumniemailrecipient_options'), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='AlumniSignUp', 15 | fields=[ 16 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 17 | ('name', models.CharField(max_length=200)), 18 | ('email', models.EmailField(max_length=320, unique=True)), 19 | ('phone_number', models.CharField(blank=True, max_length=20)), 20 | ('address', models.CharField(blank=True, max_length=200)), 21 | ('year_of_admission', models.IntegerField(null=True)), 22 | ('employer', models.CharField(blank=True, max_length=200)), 23 | ('work_title', models.CharField(blank=True, max_length=200)), 24 | ('tfif_membership', models.CharField(blank=True, max_length=50)), 25 | ('alumni_newsletter_consent', models.BooleanField(default=False)), 26 | ('signup_date', models.DateTimeField(auto_now_add=True, verbose_name='Registreringsdatum')), 27 | ('acknowledge', models.BooleanField(default=False, verbose_name='Processerad')), 28 | ], 29 | options={ 30 | 'verbose_name': 'Alumnregistrering', 31 | 'verbose_name_plural': 'Alumnregistreringar', 32 | 'ordering': ('id',), 33 | }, 34 | ), 35 | ] 36 | -------------------------------------------------------------------------------- /templates/common/core/footer.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load i18n %} 3 | 4 | 5 | 6 | 7 | 8 |
    9 |
    10 | 11 | 12 |
    13 | 14 | {% for btn in SOCIAL_BUTTONS %} 15 | 16 | 19 | 20 | {% endfor %} 21 | 22 |
    23 | 24 |
    25 |
    26 | Footer Logo 27 |
    28 |
    29 |

    30 |
    31 | Styrelsen för {{ ASSOCIATION_NAME_FULL }} kan kontaktas på följande sätt.

    32 | {% trans 'E-post' %}: 33 | {{ ASSOCIATION_EMAIL }} 34 |
    35 | {{ ASSOCIATION_OFFICE_HOURS }} 36 |

    37 |

    Adress:
    38 | {{ ASSOCIATION_ADDRESS_L1 }}
    39 | {{ ASSOCIATION_ADDRESS_L2 }}
    40 | {{ ASSOCIATION_POSTAL_CODE }} 41 |

    42 |
    43 |
    44 | 45 |
    46 | © {% now "Y" %} 47 |
    48 | 49 | 50 |
    -------------------------------------------------------------------------------- /alumni/gsuite_adapter.py: -------------------------------------------------------------------------------- 1 | import gspread 2 | import time 3 | 4 | class DateSheetsAdapter: 5 | def __init__(self, service_account: dict, sheet: str, worksheet: str): 6 | self.client = gspread.service_account_from_dict(service_account) 7 | self.sheet = self.client.open_by_key(sheet).worksheet(worksheet) 8 | 9 | def change_sheet(self, sheet: str, worksheet: str): 10 | self.sheet = self.client.open_by_key(sheet).worksheet(worksheet) 11 | 12 | def append_row(self, data: list, retries: int = 5): 13 | try: 14 | self.sheet.append_row(data) 15 | except gspread.exceptions.APIError as e: 16 | if retries > 0: 17 | time.sleep(min(5 ** (6 - retries), 60)) 18 | self.append_row(data, retries - 1) 19 | else: 20 | print(f"Failed to append row after retries: {e}") 21 | raise 22 | 23 | def get_column_by_name(self, name: str): 24 | rowvals = self.sheet.row_values(1) 25 | return rowvals.index(name) + 1 26 | 27 | def get_row_values(self, row: int) -> list: 28 | return self.sheet.row_values(row) 29 | 30 | def get_column_values(self, column: int) -> list: 31 | return self.sheet.col_values(column) 32 | 33 | def set_value(self, row: int, col: int, value: int | float | str): 34 | self.sheet.update_cell(row, col, value) 35 | 36 | def get_last_row(self): 37 | return self.sheet.get()[-1] 38 | 39 | def update_row(self, row: int, data: list): 40 | """Update a specific row with new data.""" 41 | for cell_index, value in enumerate(data, start=1): 42 | if value: 43 | self.sheet.update_cell(row, cell_index, value) 44 | -------------------------------------------------------------------------------- /static/common/core/js/live-attendee-list.js: -------------------------------------------------------------------------------- 1 | // noinspection DuplicatedCode 2 | $(function() { 3 | 4 | // for HTTPS also use WSS. 5 | let ws_scheme = window.location.protocol === "https:" ? "wss" : "ws"; 6 | 7 | let ws_url = ws_scheme + '://' + window.location.host + '/ws' + window.location.pathname; 8 | 9 | if (document.getElementById('attendee-list').dataset.parentSlug) { 10 | ws_url = ws_scheme + '://' + window.location.host + '/ws/' + document.getElementById('attendee-list').dataset.parentSlug + '/'; 11 | } 12 | 13 | let socket = new WebSocket(ws_url); 14 | 15 | socket.onmessage = function(e) { 16 | const { fields, anonymous } = JSON.parse(e.data).data; 17 | 18 | const list = $('#attendees tbody'); 19 | $('#attendees-header').css("display", "table-row"); 20 | $('#no-attendee').css("display", "none"); 21 | 22 | if (fields) { 23 | // includes header row and is thus equivalent to current amount of attendees + 1 24 | const attendeeNumber = list.children().length; 25 | const row = $(''); 26 | row.append($('').text(attendeeNumber)); 27 | for (const [field, value] of fields) { 28 | const cell = $(''); 29 | // names of anonymous attendees use 30 | if (field === "user" && anonymous) 31 | cell.append($('').text(value)) 32 | else 33 | cell.text(value); 34 | row.append(cell); 35 | } 36 | list.append(row); 37 | } 38 | }; 39 | socket.onclose = function(e) { 40 | console.error('Event socket closed unexpectedly'); 41 | }; 42 | 43 | }); 44 | --------------------------------------------------------------------------------