joeni

Debug login screen, add new thesis files

Author
Maarten Vangeneugden
Date
July 26, 2018, 8:50 p.m.
Hash
8c5913dc86daa60acef075b2b04036bd6ad46a5e
Parent
02c51992c9908cc62f4bc5da1fee6bf1516ac055
Modified files
administration/views.py
docs/thesis/BlackBoard.org
docs/thesis/aandachtspunten.org
docs/thesis/beveiliging.org
docs/thesis/huisstijl.org
docs/thesis/master.org
docs/thesis/softwarekeuzes.org
docs/thesis/voorwoord.org
joeni/settings.py
joeni/urls.py

administration/views.py

2 additions and 0 deletions.

View changes Hide changes
1
1
from collections import OrderedDict
2
2
from django.http import HttpResponseRedirect
3
3
import datetime
4
4
from django.urls import reverse # Why?
5
5
from django.utils.translation import gettext as _
6
6
from .models import *
7
7
from .forms import UserDataForm
8
8
from .new_roster import create_roster_rows
9
9
import administration
10
10
from django.contrib.auth.decorators import login_required
11
11
from django.contrib.auth import authenticate
12
12
+
13
13
14
@login_required
14
15
def roster(request, begin=None, end=None):
15
16
    """Collects and renders the data that has to be displayed in the roster.
16
17
17
18
    The begin and end date can be specified. Only roster points in that range
18
19
    will be included in the response. If no begin and end are specified, it will
19
20
    take the current week as begin and end point. If it's
20
21
    weekend, it will take next week."""
21
22
22
23
    # TODO Handle given begin and end
23
24
    context = dict()
24
25
    #context = {'money' : update_balance(None)}
25
26
    template = "administration/roster.djhtml"
26
27
27
28
    if begin is None or end is None:
28
29
        today = datetime.date.today()
29
30
        if today.isoweekday() in {6,7}:  # Weekend
30
31
            begin = today + datetime.timedelta(days=8-today.isoweekday())
31
32
            end = today + datetime.timedelta(days=13-today.isoweekday())
32
33
        else:  # Same week
33
34
            begin = today - datetime.timedelta(days=today.weekday())
34
35
            end = today + datetime.timedelta(days=5-today.isoweekday())
35
36
    else:  # Changing regexes to date objects
36
37
        b = begin.split("-")
37
38
        e = end.split("-")
38
39
        begin = datetime.datetime(int(b[2]),int(b[1]),int(b[0]))
39
40
        end = datetime.datetime(int(e[2]),int(e[1]),int(e[0]))
40
41
41
42
    context['begin'] = begin
42
43
    context['end'] = end
43
44
44
45
    context['prev_begin'] = (begin - datetime.timedelta(days=7)).strftime("%d-%m-%Y")
45
46
    context['prev_end'] = (begin - datetime.timedelta(days=2)).strftime("%d-%m-%Y")
46
47
    context['next_begin'] = (end + datetime.timedelta(days=2)).strftime("%d-%m-%Y")
47
48
    context['next_end'] = (end + datetime.timedelta(days=7)).strftime("%d-%m-%Y")
48
49
49
50
    days = [begin]
50
51
    while (end-days[-1]).days != 0:
51
52
        # Human translation: Keep adding days until the last day in the array of
52
53
        # days is the same day as the last day the user wants to see the roster for.
53
54
        days.append(days[-1] + datetime.timedelta(days=1))
54
55
    context['days'] = days
55
56
56
57
    # Collecting events
57
58
    course_events = CourseEvent.objects.filter(begin_time__gte=begin).filter(end_time__lte=end).order_by("begin_time")
58
59
    #university_events = UniversityEvent.objects.filter(begin_time__gte=begin).filter(end_time__lte=end)
59
60
    #study_events = StudyEvent.objects.filter(begin_time__gte=begin).filter(end_time__lte=end)
60
61
    #events = Event.objects.filter(begin_time__gte=begin).filter(end_time__lte=end)
61
62
    conflicts, table_code = create_roster_rows(course_events)
62
63
63
64
    context['time_blocks'] = table_code
64
65
    context['conflicts'] = conflicts
65
66
    #print(time_blocks)
66
67
    return render(request, template, context)
67
68
    # TODO Finish!
68
69
69
70
def roster_ics(request, user_slug):
70
71
    template = "administration/roster.ics"
71
72
    context = dict()
72
73
    context['events'] = CourseEvent.objects.all()  # FIXME: Filter to personal calendar items!
73
74
    return render(request, template, context)
74
75
75
76
def index(request):
76
77
    template = "administration/index.djhtml"
77
78
    #context = {'money': update_balance(None)}
78
79
    context = dict()
79
80
    context['links'] = [
80
81
        ("administration-settings",
81
82
         _("Personal settings"),
82
83
         _("Edit your personal information, billing address, home address, and so on.")),
83
84
        ("administration-curriculum",
84
85
         _("Curricula"),
85
86
         _("View all information related to your curricula, including exam results.<br />"
86
87
           "You can also change your current curriculum here, or request a change.")),
87
88
        ("administration-forms",
88
89
         _("Forms"),
89
90
         _("All forms for special services can be found on this page.")),
90
91
        ("administration-rooms",
91
92
         _("Rooms"),
92
93
         _("Room occupancy, free rooms, properties, ... <br />"
93
94
           "All this and much more is available on this page.")),
94
95
        ("administration-roster",
95
96
         _("Personal roster"),
96
97
         _("Everything about your roster and events at Hasselt University is available here.")),
97
98
        ("administration-bulletin-board",
98
99
         _("Bulletin board"),
99
100
         _("From time to time, UHasselt publishes announcements regarding changes, events, ..."
100
101
           "<br />All publications are neatly organized here for easy reference.")),
101
102
        ]
102
103
103
104
    return render(request, template, context)
104
105
105
106
    pass
106
107
107
108
def pre_registration(request):
108
109
    user_data_form = UserDataForm()
109
110
    template = "administration/pre_registration.djhtml"
110
111
    context = dict()
111
112
112
113
    if request.method == 'POST':
113
114
        user_data_form = UserDataForm(request.POST)
114
115
        context['user_data_form'] = user_data_form
115
116
        if user_data_form.is_valid():
116
117
            user_data_form.save()
117
118
            context['messsage'] = _("Your registration has been completed. You will receive an e-mail shortly.")
118
119
        else:
119
120
            context['messsage'] = _("The data you supplied had errors. Please review your submission.")
120
121
    else:
121
122
        context['user_data_form'] = UserDataForm(instance = user_data_form)
122
123
123
124
    return render(request, template, context)
124
125
    pass
125
126
126
127
@login_required
127
128
def settings(request):
128
129
    user_data = UserData.objects.get(user=request.user)
129
130
    user_data_form = UserDataForm(instance = user_data)
130
131
    template = "administration/settings.djhtml"
131
132
    context = dict()
132
133
    #context = {'money' : update_balance(None)}
133
134
134
135
    if request.method == 'POST':
135
136
        user_data_form = UserDataForm(request.POST, instance = user_data)
136
137
        context['user_data_form'] = user_data_form
137
138
        if user_data_form.is_valid():
138
139
            user_data_form.save()
139
140
            context['messsage'] = _("Your settings were successfully updated.")
140
141
        else:
141
142
            context['messsage'] = _("The data you supplied had errors. Please review your submission.")
142
143
    else:
143
144
        context['user_data_form'] = UserDataForm(instance = user_data)
144
145
145
146
    return render(request, template, context)
146
147
147
148
@login_required
148
149
def bulletin_board(request):
149
150
    context = dict()
150
151
    #context = {'money' : update_balance(None)}
151
152
    context['exam_commission_decisions'] = ExamCommissionDecision.objects.filter(user=request.user)
152
153
    context['education_department_messages'] = EducationDepartmentMessages.objects.all()
153
154
    for item in context['education_department_messages']:
154
155
        print(item.text)
155
156
    template = "administration/bulletin_board.djhtml"
156
157
    return render(request, template, context)
157
158
158
159
def jobs(request):
159
160
    context = dict()
160
161
    #context = {'money' : update_balance(None)}
161
162
    template = "administration/jobs.djhtml"
162
163
    #@context['decisions'] = ExamCommissionDecision.objects.filter(user=request.user)
163
164
    return render(request, template, context)
164
165
165
166
166
167
@login_required
167
168
def curriculum(request):
168
169
    context = dict()
169
170
    #context = {'money' : update_balance(None)}
170
171
    template = "administration/curriculum.djhtml"
171
172
    context['curricula'] = Curriculum.objects.filter(student=request.user)
172
173
    for item in context['curricula']:
173
174
        for co in item.course_programmes_results():
174
175
            print(co)
175
176
    return render(request, template, context)
176
177
177
178
def result(request):
178
179
    return render(request, template, context)
179
180
180
181
@login_required
181
182
def results(request):
182
183
    results = CourseResult.objects.filter(student=request.user)
183
184
    template = "administration/results.djhtml"
184
185
    # TODO
185
186
    return render(request, template, context)
186
187
187
188
def forms(request):
188
189
    context = dict()
189
190
    #context = {'money' : update_balance(None)}
190
191
    template = "administration/forms.djhtml"
191
192
    return render(request, template, context)
192
193
193
194
def user(request, slug_name):
194
195
    pass
195
196
196
197
def rooms(request):
197
198
    context = dict()
198
199
    #context = {'money' : update_balance(None)}
199
200
    context['rooms'] = Room.objects.all()
200
201
    context['room_reservations'] = RoomReservation.objects.all()
201
202
    context['course_events'] = CourseEvent.objects.all()
202
203
    context['blocks'] = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
203
204
204
205
    # Collecting all rooms that are free for at least one two hours from now
205
206
    now = datetime.datetime.now(datetime.timezone.utc)
206
207
    end = now + datetime.timedelta(hours=2)
207
208
    free_rooms = dict()
208
209
    for room in context['rooms']:
209
210
        if room.reservation_possible(now, end):
210
211
            event = room.next_event(end)
211
212
            reservation = room.next_reservation(end)
212
213
            if event is None and reservation is None:
213
214
                free_rooms[room] = None
214
215
            elif reservation is not None:
215
216
                free_rooms[room] = event.begin_time
216
217
            elif event is not None:
217
218
                free_rooms[room] = reservation.begin_time
218
219
            elif event.begin_time < reservation.begin_time:
219
220
                free_rooms[room] = event.begin_time
220
221
            else:
221
222
                free_rooms[room] = reservation.begin_time
222
223
    context['free_rooms'] = free_rooms
223
224
224
225
    template = "administration/rooms.djhtml"
225
226
    return render(request, template, context)
226
227
227
228
def room_detail(request, room):
228
229
    template = "administration/room_detail.djhtml"
229
230
    context = dict()
230
231
    #context = {'money' : update_balance(None)}
231
232
    room = Room.objects.get(name=room)
232
233
    context['room'] = room
233
234
    context['reservations'] = RoomReservation.objects.filter(room=room).filter(begin_time__gte=datetime.datetime.now())
234
235
    context['course_events'] = CourseEvent.objects.filter(room=room).filter(begin_time__gte=datetime.datetime.now())
235
236
    # Building the room occupancy of today:
236
237
    today = datetime.date.today()
237
238
    if today.isoweekday() in {6,7}:  # Weekend
238
239
        today = today + datetime.timedelta(days=8-today.isoweekday())
239
240
240
241
    context['days'] = [today]
241
242
242
243
    # Collecting events
243
244
    course_events = CourseEvent.objects.filter(room=room).filter(begin_time__date=today)
244
245
    print(course_events)
245
246
    #university_events = UniversityEvent.objects.filter(begin_time__gte=begin).filter(end_time__lte=end)
246
247
    #study_events = StudyEvent.objects.filter(begin_time__gte=begin).filter(end_time__lte=end)
247
248
    #events = Event.objects.filter(begin_time__gte=begin).filter(end_time__lte=end)
248
249
249
250
    conflicts, table_code = create_roster_rows(course_events)
250
251
    context['time_blocks'] = table_code
251
252
    context['conflicts'] = conflicts
252
253
    print(context['time_blocks'])
253
254
    return render(request, template, context)
254
255
255
256
def login(request):
256
257
    context = dict()
257
258
    #context = {'money' : update_balance(None)}
258
259
    if request.method == "POST":
259
260
        name = request.POST['name']
260
261
        passphrase = request.POST['pass']
261
262
        user = authenticate(username=name, password=passphrase)
262
263
        if user is not None: # The user was successfully authenticated
263
264
            print("YA")
264
265
            return HttpResponseRedirect(request.POST['next'])
+
266
            return HttpResponseRedirect(request.POST['next'])
265
267
        else: # User credentials were wrong
266
268
            context['next'] = request.POST['next']
267
269
            context['message'] = _("The given credentials were not correct.")
268
270
    else:
269
271
        context['next'] = request.GET.get('next', None)
270
272
        if context['next'] is None:
271
273
            context['next'] = reverse('administration-index')
272
274
273
275
    template = 'administration/login.djhtml'
274
276
275
277
    return render(request, template, context)
276
278

docs/thesis/BlackBoard.org

11 additions and 0 deletions.

View changes Hide changes
+
1
De UHasselt gebruikt al jaren BlackBoard als digitaal platform voor het
+
2
uitwisselen van studiemateriaal tussen docenten en studenten.\\
+
3
** Propriëtaire software
+
4
*** Service as a software substitute (SaaSS)
+
5
** Kostelijk
+
6
+
7
~€100.000 euro kost.>....
+
8
** Geen huisstijl mogelijk
+
9
** Onstabiel
+
10
** Slechte reputatie
+
11
** Slechte reputatie

docs/thesis/aandachtspunten.org

10 additions and 1 deletion.

View changes Hide changes
1
1
Fundamenteel is deze bachelorproef nog altijd een werk dat moet bijdragen aan
2
2
het ALIPA-project van de UHasselt.\\
3
3
Voor het stroomlijnen van de ICT-infrastructuur is het ook nodig om kleine
4
4
problemen en mankementen op te lossen als ze gevonden worden. Een afgewerkt
5
5
geheel voedt het gevoel dat alles goed en orderlijk samenwerkt met elkaar, een
6
6
belangrijke doelstelling om vertrouwen te creëren in de gebruikers.
7
7
8
8
Tijdens het maken van dit proefwerk ben ik op enkele kleine punten gestoten die
9
9
niet direct van invloed waren op mijn proef, maar die ik hier toch wens te
10
10
vermelden. Het gaat dan om kleine slordigheden, schoonheidsfoutjes, verouderde
11
11
informatie, ...
12
12
13
13
Alle benodigde informatie, alsook een redenering wordt per punt uitgeschreven.
14
14
15
15
** Verouderde informatie
16
16
*** Campushopper afgeschaft
17
17
Op de pagina https://www.uhasselt.be/Contact-en-ligging onder de rubriek "Campus
18
18
 Hasselt/Gevangenis" wordt verteld dat men de
19
19
Campushopper kan nemen, maar deze werd al in 2017 afgeschaft. Beter is om Boulevardpendel
20
20
te nemen, of elke bus die het Dusartplein aandoet.[fn::Dit zijn de volgende
21
21
 lijnen: 1, 3, 5, 11, 13, 16, 18a, 20a, 22, 23, 35, 36, 45, 46, 48, 51, 52, 180,
22
22
 182, AL, BP, H13, H14, H41, H51. (https://www.delijn.be/nl/haltes/halte/403040/Hasselt_Dusartplein)]
23
23
24
24
** Onnodige informatie
25
25
*** Spartacusplan
26
26
Op https://www.uhasselt.be/Contact-en-ligging staat een link naar een
27
27
PDF-bestand omtrent het Spartacusplan. Dit heeft inderdaad te maken met het
28
28
openbaar vervoer, maar voegt weinig toe aan het eigenlijke doel van deze pagina:
29
29
Bezoekers informeren over hoe ze de UHasselt kunnen bereiken.\\
30
30
Ik stel voor om de vermelding hierover de verwijderen.
31
31
32
32
** Slecht te vinden informatie
33
33
*** Plattegronden
34
34
Er is weinig te vinden van plattegronden op de website; waar zijn welke lokalen
35
35
 te vinden, plattegronden van de 1ste verdieping, ...
36
36
37
37
Het kan ook zijn dat dit niet per sé nodig is; indien op de campus zelf adequate
38
38
 wegbewijzering wordt aangebracht, wordt een plattegrond minder belangrijk.
39
39
 Waar vind ik alle plattegronden van de campussen? Het zijn er maar 2...
40
40
 Ik heb er eentje gevonden van de campus Diepenbeek op https://www.uhasselt.be/Contact-en-ligging:
41
41
 https://www.uhasselt.be/images/UHasselt/maps/2015/campus-diepenbeek/campus-diepenbeek-gebouwDE.jpg
42
42
 Maar dat is ook enkel gelijkvloers, en niet up-to-date met nieuwe aanbouw.
43
43
44
44
** Verbeteringspunten
+
45
*** Speciale logo's voor Apple
+
46
In de broncode van de websites binnen de UHasselt zijn in de header links te
+
47
vinden naar speciale logo's voor Apple-browsers.\\
+
48
Binnen Joeni worden er geen 
+
49
** Verbeteringspunten
45
50
*** Video's op YouTube
46
51
Op https://www.uhasselt.be/Videos zijn er veel filmpjes te vinden over het
47
52
reilen en zeilen in de universiteit. Maar eigenlijk zijn het links naar het
48
53
videoplatform YouTube, onderdeel van Google Inc. Eerder werd in deze thesis
49
54
[[google-privacy][de integratie met diensten van Google]] op de korrel genomen.\\
50
-
Dit is makkelijk op te lossen door de video's simpelweg via de eigen website aan
+
55
Dit is makkelijk op te lossen door de video's simpelweg via de eigen website aan
51
56
te bieden, middels het [[https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Video][~
52
57
**** Licensiëring video's
53
58
De aangegeven licentie van de video's is de "Standaard YouTube-licentie", deze
54
59
licentie voldoet niet aan de vereisten voor een "Vrij cultureel werk".
55
60
56
61
De aard van deze video's is duidelijk: Ze dienen om toekomstige studenten te
57
62
informeren over de UHasselt en al haar richtingen, een blik te werpen op
58
63
studentenfaciliteiten (zoals verenigingen), en ook als een advertentie om voor
59
64
UHasselt te kiezen.\\
60
65
De doelstelling bereiken wordt met deze licentiekeuze actief tegengewerkt;
61
66
mensen mogen deze video's niet delen, of aanpassen voor nieuwe werken. Er zijn
62
67
ook voorwaarden op het privégebruik ervan.\\
63
68
Creatieve werken met deze doelstellingen dienen zo vrij mogelijk uitgegeven te
64
69
worden om de zichtbaarheid en verspreidingskansen ervan te maximaliseren. Door de huidige wetgeving
65
70
omtrent kopieerrechten is dit niet automatisch mogelijk, maar er bestaan wel
66
71
licenties die hiervoor een zeer toereikend soelaas bieden.
67
72
68
73
Ik raad aan om de licenties op de video's te vervangen met de
69
74
[[https://creativecommons.org/licenses/by/4.0/deed.nl][CC-BY 4.0]]-licentie, die deze problemen direct oplost. Dit kan ook gedaan worden als beslist
70
75
wordt de video's op YouTube te laten staan.
71
76
+
77
*** Google & privacy
+
78
+
79
+
80

docs/thesis/beveiliging.org

50 additions and 0 deletions.

View changes Hide changes
+
1
Web-applicaties worden blootgesteld aan tal van mogelijke beveiligingsproblemen.
+
2
Een beetje /cracker/ kan een onbeveiligde website in weinig tijd
+
3
binnendringen.\\
+
4
Mede dankzij het gebruik van Django zijn de meeste mogelijke veiligheidslekken
+
5
gedicht. De belangrijkste onderwerpen (en gemaakte afwegingen) worden hier uitgelegd.
+
6
** Cross site scripting
+
7
Cross site scripting (XSS) laat toe om scripts te injecteren in de browsers van andere
+
8
gebruikers door bv. scripts op te slaan in de databank, die dan later worden
+
9
uitgelezen en op de computer van andere gebruikers wordt uitgevoerd.
+
10
+
11
Binnen Django wordt XSS opgelost door middel van
+
12
/[[https://docs.djangoproject.com/en/2.0/ref/templates/language/#automatic-html-escaping][automatic HTML escaping]]/; de data die gebruikers ingeven, wordt behandeld als "onveilig".
+
13
*** Orgmode
+
14
Binnen Joeni kan gebruik worden gemaakt van Orgmode-syntax om teksten van 
+
15
opmaak te voorzien, zoals vette tekst, hyperlinks, lijsten enz.
+
16
+
17
Het is technisch gezien ook mogelijk om broncode in Org te markeren, zelfs om
+
18
deze uit te laten voeren, met alle [[https://orgmode.org/org.html#Code-evaluation-security][veiligheidsrisico's]] die daaruit volgen.
+
19
+
20
Ik heb toch geopteerd om Orgmode-syntax toe te laten voor data die door
+
21
gebruikers wordt verstuurd, maar *enkel* voor personeel van de UHasselt.
+
22
Personeelsleden kunnen makkelijker aansprakelijk gehouden worden voor misbruik
+
23
van de hen aangeleverde voorrechten, en de mogelijke gevolgen van dergelijk misbruik
+
24
zullen volgens mij afdoende werken om XSS via Orgmode tegen te gaan. Moest dat
+
25
niet kloppen, dan kan Orgmode altijd gemakkelijk uitgeschakeld worden, slechts
+
26
met een tolereerbaar verlies aan opmaak binnen Joeni.
+
27
+
28
** Externe malafide programmatuur
+
29
Bij het opvragen van scripts buiten de eigen server is er steeds een risico dat
+
30
er malafide code wordt binnengehaald; men weet niet welke acties ondernomen
+
31
worden op die andere servers om kraken tegen te gaan.\\
+
32
Dit is al verscheidene keren voorgevallen , en de kern van het probleem is tot nu
+
33
toe nog altijd niet opgelost, omdat het niet kán worden opgelost: Downloaden van
+
34
ongecontroleerde scripts houdt een inherent veiligheidsrisico in.
+
35
+
36
Joeni lost dit op door simpelweg _geen_ externe scripts in te laden. Dit is
+
37
namelijk ook een reden waarom /client-side scripting/ overal vermeden wordt;
+
38
het opent een deur tot problemen waarover geen interne controle mogelijk is.
+
39
+
40
Alle software, media, /stylesheets/, bibliotheken, ... worden vanaf de server
+
41
gedraaid. 
+
42
** Clickjacking
+
43
+
44
** Cross site request forgery
+
45
Cross site request forgery (CSRF) 
+
46
+
47
** Kraken van wachtwoorden en -zinnen
+
48
*** Geen verplichte wijzigingen
+
49
+
50

docs/thesis/huisstijl.org

6 additions and 0 deletions.

View changes Hide changes
1
1
Een mooi uitgewerkte huisstijl is essentieel voor het creëren van een gevoel van
2
2
samenhorigheid met verschillende entiteiten: Ze dienen ter onderscheiding van de
3
3
rest, en 
4
4
In Joeni wordt de huisstijl van de UHasselt verder uitgewerkt en gebruikt. In
5
5
dit hoofdstuk wordt dit in detail uitgelegd, zodat het als basis kan dienen voor
6
6
andere software.
7
7
** Logo
+
8
Ik vermoed dat een deel van de huisstijl simpelweg niet met de studenten mede
+
9
gedeeld wordt; op de website wordt doorgaans gelinkt naar een "Huisstijl voor
+
10
studenten", wat doet vermoeden dat er ook een "Huisstijl voor personeel" moet
+
11
zijn, of iets in die aard.
+
12
+
13
** Logo
8
14
Joeni heeft tot doel als vervanging te dienen voor het Studentendossier en
9
15
BlackBoard. In combinatie met de relatie tot de UHasselt, wordt voor Joeni het
10
16
volgende logo gebruikt:
11
17
12
18
#+CAPTION: Logo van Joeni
13
19
#+NAME:   fig:joeni-logo
14
20
[[./images/joeni-logo.png]]
15
21
16
22
Het logo is duidelijk afgeleid van het logo van de UHasselt. De driehoekjes zijn
17
23
zo geplaatst dat het geheel lijkt op een HTML-element, om het verband als
18
24
webapplicatie aan te duiden.
19
25
20
26
*** UHasselt
21
27
Het logo van de UHasselt is op verschillende plaatsen te vinden op de website.
22
28
De meest prominente locatie is de titelbalk.
23
29
24
30
#+CAPTION: Afbeelding van hoe het logo van de UHasselt in de titelbalk verwerkt is
25
31
#+NAME: fig:uhasselt-titlebar
26
32
[[./img/a.jpg]]
27
33
28
34
Alhoewel de vorm van de driehoekjes lichtjes afwijkt van het echte logo, valt
29
35
dit nauwelijks op. Het logo verwerken in de titel is mogelijk, omdat dit gewoon
30
36
tekst is, meer specifiek, ▶ ~U+25B6 BLACK RIGHT-POINTING TRIANGLE~.
31
37
32
38
Het voordeel hieraan is niet louter een besparing op bytes.[fn:utf-8] Omdat dit
33
39
tekst is, zal de betere webbrowser in staat zijn om, afhankelijk van de
34
40
achtergrond, het logo de juiste kleur te geven. Op de volgende afbeelding is dit
35
41
effect duidelijk te zien, zeker in contrast met het logo dat de
36
42
UHasselt-bibliotheek gebruikt.
37
43
38
44
[fn:utf-8] Meestal wordt een logo meegestuurd die langs de titel wordt
39
45
weergegeven in de titelbalk. Dat is niet fout, maar ze zijn wel een stuk groter in het
40
46
aantal bytes. [[https://bibliotheek.uhasselt.be/sites/default/files/favicon.ico][Het logo dat UHasselt gebruikt op haar eigen website]] is 1,150
41
47
bytes groot, [[https://www.fileformat.info/info/unicode/char/25b6/index.htm][▶(×2) is slechts 3(×2) bytes groot]], dus een aanzienlijke verkleining van de
42
48
benodigde ruimte.
43
49
44
50
#+CAPTION: Vergelijking tussen logo via UTF-8/tekst en als afbeelding. Merk op hoe het logo van de bibliotheek praktisch onzichtbaar is bij de zwarte achtergrond.
45
51
#+NAME: fig:utf8-better
46
52
[[./img/a.jpg]]
47
53
48
54
** Lettertype
49
55
Op de website wordt als [[https://www.uhasselt.be/UH/Help-Studenten-Algemene_Help-Huisstijl/Lettertype.html][lettertype]] Verdana aangereikt. Dit strookt echter niet
50
56
met het lettertype dat wordt gebruikt in de vele documenten die de UHasselt
51
57
verspreidt, gaande van informatieboekjes over de studierichtingen tot de
52
58
spandoeken die buiten aan het universiteitsgebouw worden opgehangen.
53
59
54
60
Het wordt nergens vermeld, maar dit lettertype gelijkt volledig op
55
61
[[https://design.ubuntu.com/font/][het Ubuntu-lettertype]], zoals de volgende vergelijking duidelijk aantoont.
56
62
57
63
#+CAPTION: Vergelijking tussen lettertype gebruikt door UHasselt en Ubuntu-lettertype op voorbeeldtekst
58
64
#+NAME: fig:font-comparison
59
65
[[./images/font-comparison.png]]
60
66
61
67
Dit lettertype wordt doorheen Joeni gebruikt voor het aanduide van belangrijke
62
68
(kop)teksten.
63
69
64
70
** Kleuren
65
71
De UHasselt gebruikt in de informatiebrochures wel een kleurenpalet dat eigen
66
72
lijkt te zijn aan de bepaalde faculteiten, maar verder wordt het haast nergens
67
73
anders gebruikt. Vermoedelijk komt dit omdat nergens in de huisstijl wordt
68
74
vermeld welke kleuren gebruikt kunnen worden voor wat.\\
69
75
Hieronder worden de verschillende kleurwaarden voor elke faculteit uiteengezet.
70
76
Ze geven een eigen gezicht aan de documentatie binnen elke faculteit, en kunnen
71
77
makkelijk gebruikt worden voor zowel voor- als achtergrondkleuren.
72
78
73
79
#+CAPTION: Een kleurenpalet dat gebruikt kan worden voor de huisstijl van de UHasselt verder uit te bouwen
74
80
#+NAME: fig:color-swatches
75
81
[[./images/color-swatches.png]]

docs/thesis/master.org

3 additions and 0 deletions.

View changes Hide changes
1
1
#+language: nl
2
2
#+latex_class: article
3
3
#+latex_class_options: [a4paper]
4
4
#+latex_header: \usepackage{pdfpages}
5
5
#+latex_header: \usepackage[dutch]{babel}
6
6
#+OPTIONS: toc:nil
7
7
#+author: Maarten Vangeneugden @@latex:\\@@ prof. dr. Wim Lamotte
8
8
#+date: 2017-2018
9
9
#+title: Joeni
10
10
#+subtitle: Bachelorproef voorgedragen tot het behalen van de graad van bachelor in de informatica
11
11
12
12
* COMMENT Structuur
13
13
1. Samenvatting
14
14
2. Voorwoord
15
15
3. Inhoudsopgave
16
16
17
17
18
18
* Samenvatting
19
19
20
20
* Voorwoord
21
21
22
22
* Inhoudsopgave
23
23
#+TOC: headlines 6
24
24
25
25
#+INCLUDE: "./aandachtspunten.org"
+
26
#+INCLUDE: "./softwarekeuzes.org"
+
27
+
28

docs/thesis/softwarekeuzes.org

8 additions and 2 deletions.

View changes Hide changes
1
1
In dit hoofdstuk wordt dieper ingegaan op de verantwoording voor de gekozen
2
2
software, en worden de mogelijke alternatieven besproken.
3
3
** Softwareframework
4
4
*** Django
5
5
Voor het schrijven van Joeni heb ik uitgesproken gebruik gemaakt van
6
6
[[https://www.djangoproject.com/][het Django-framework]].
7
7
**** Webframework
8
8
Django is een framework gericht op het ontwikkelen van websites. Omdat Joeni
9
9
hoort te dienen als een directe vervanging voor het Studentendossier en
10
10
BlackBoard, zou een website bouwen de overgang het gemakkelijkst laten gebeuren.
11
11
+
12
Het was vanaf het begin al duidelijk dat Joeni een databank zou gaan bevatten,
+
13
compleet met tabellen, vreemde sleutels en voorwaarden.
+
14
12
15
*** Python (3)
13
16
Alhoewel er wel voor elke programmeertaal één of meerdere frameworks voor het
14
17
maken van websites bestaan, ging de voorkeur uit naar Python.
15
18
16
19
*** UTF-8
17
20
Joeni maakt uitsluitend gebruik van UTF-8 voor encodering van tekst.\\
18
21
Niet alleen is dit standaard voor strings in Python 3, het laat ook toe om het
19
22
logo van de UHasselt in de titelbalk te verwerken, en zorgt ervoor dat alle
20
23
teksten correct gespeld en opgeslagen kunnen worden.
21
-
+
24
Dit is ook handig voor Erasmusstudenten met vreemde namen zoals bv. 
+
25
"Kürt Pðovski", die hun echte naam kunnen gebruiken, zonder substituten te
+
26
moeten gebruiken voor bepaalde tekens.
+
27
22
28
* Andere mogelijkheden
23
29
Tegenover de gemaakte keuzes staan ook een hoop alternatieven die niet werden
24
30
gebruikt. 
25
31
** Ruby (on Rails)
26
32
Mijn ervaring met Ruby is simpelweg onbestaande; ik heb nooit in deze taal
27
33
geprogrammeerd. De beperkte tijd die ik had voor de bachelorproef stond niet toe
28
34
dat ik mij nog zou verdiepen in een nieuwe programmeertaal.
29
35
** PHP
30
36
Ongetwijfeld een van de meest gebruikte talen voor het schrijven van dynamische
31
37
websites.\\
32
38
Het probleem is compleet het tegenovergestelde van [[Ruby (on Rails][Ruby]]; PHP ken ik zeer goed.
33
-
Het is daarom dat ik dit niet beschouw als een goede keuze; de taal zit vol met
+
39
Het is daarom dat ik dit niet beschouw als een goede keuze; de taal zit vol met
34
40
beveiligingslekken, vertoont extreem raar gedrag dat niet voorkomt in Python,
35
41

docs/thesis/voorwoord.org

11 additions and 0 deletions.

View changes Hide changes
+
1
+
2
* Voorwoord
+
3
In deze bachelorthesis komen verschillende onderwerpen aan bod, op te delen in
+
4
die omvattende elementen:
+
5
- Bespreking van Joeni :: Joeni is de praktische uitwerking van deze
+
6
     bachelorproef. Het stelt een vervanging voor voor zowel het
+
7
     Studentendossier als BlackBoard, onderdelen die duidelijk een functionele
+
8
     en visuele /overhaul/ nodig hebben.
+
9
- Voorstellen voor ALIPA :: Deze bachelorproef staat in het teken van ALIPA, om
+
10
     mogelijke oplossingen uit te werken, en deze dan voor te stellen.
+
11
     mogelijke oplossingen uit te werken, en deze dan voor te stellen.

joeni/settings.py

1 addition and 1 deletion.

View changes Hide changes
1
1
Django settings for Joeni project.
2
2
3
3
Generated by 'django-admin startproject' using Django 2.0b1.
4
4
5
5
For more information on this file, see
6
6
https://docs.djangoproject.com/en/dev/topics/settings/
7
7
8
8
For the full list of settings and their values, see
9
9
https://docs.djangoproject.com/en/dev/ref/settings/
10
10
"""
11
11
12
12
import os
13
13
14
14
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
15
15
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
16
16
17
17
18
18
# Quick-start development settings - unsuitable for production
19
19
# See https://docs.djangoproject.com/en/dev/howto/deployment/checklist/
20
20
21
21
# SECURITY WARNING: keep the secret key used in production secret!
22
22
SECRET_KEY = '!2634qc=b*lp0=helzcmvb3+1_wcl!6z@mhzi%p(vg7odq&gfz'
23
23
# TEMP Password: adminadmin | admin
24
24
25
25
# SECURITY WARNING: don't run with debug turned on in production!
26
26
DEBUG = True
27
27
28
28
ALLOWED_HOSTS = []
29
29
30
30
31
31
# Application definition
32
32
33
33
INSTALLED_APPS = [
34
34
    'django.contrib.admin',
35
35
    'django.contrib.auth',
36
36
    'django.contrib.contenttypes',
37
37
    'django.contrib.humanize',
38
38
    'django.contrib.sessions',
39
39
    'django.contrib.messages',
40
40
    'django.contrib.staticfiles',
41
41
    'administration',
42
42
    'agora',
43
43
    'courses',
44
44
    'joeni',
45
45
]
46
46
47
47
MIDDLEWARE = [
48
48
    'django.middleware.security.SecurityMiddleware',
49
49
    'django.middleware.locale.LocaleMiddleware',
50
50
    'django.middleware.common.CommonMiddleware',
51
51
    'django.middleware.csrf.CsrfViewMiddleware',
52
52
    'django.contrib.sessions.middleware.SessionMiddleware',
53
53
    'django.contrib.auth.middleware.AuthenticationMiddleware',
54
54
    'django.contrib.sessions.middleware.SessionMiddleware',
55
55
    'django.contrib.messages.middleware.MessageMiddleware',
56
56
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
57
57
    'django.middleware.security.SecurityMiddleware',
58
58
    # Caching middleware
59
59
    'django.middleware.cache.UpdateCacheMiddleware',
60
60
    'django.middleware.common.CommonMiddleware',
61
61
    'django.middleware.cache.FetchFromCacheMiddleware',
62
62
]
63
63
64
64
# Caching settings
65
65
CACHE_MIDDLEWARE_ALIAS = 'default'
66
66
CACHE_MIDDLEWARE_SECONDS = 300
67
67
CACHE_MIDDLEWARE_KEY_PREFIX = ''
68
68
69
69
ROOT_URLCONF = 'joeni.urls'
70
70
71
71
TEMPLATES = [
72
72
    {
73
73
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
74
74
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
75
75
        'APP_DIRS': True,
76
76
        'OPTIONS': {
77
77
            'context_processors': [
78
78
                'django.template.context_processors.debug',
79
79
                'django.template.context_processors.request',
80
80
                'django.contrib.auth.context_processors.auth',
81
81
                'django.contrib.messages.context_processors.messages',
82
82
            ],
83
83
        },
84
84
    },
85
85
]
86
86
87
87
#WSGI_APPLICATION = 'joeni.wsgi.application'
88
88
89
89
90
90
# Database
91
91
# https://docs.djangoproject.com/en/dev/ref/settings/#databases
92
92
93
93
DATABASES = {
94
94
    'default': {
95
95
        'ENGINE': 'django.db.backends.sqlite3',
96
96
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
97
97
    }
98
98
}
99
99
100
100
# Page to display when a user needs / wants to log in
101
101
LOGIN_URL = '/administration/login'
102
-
+
102
103
103
# Custom User model
104
104
AUTH_USER_MODEL = 'administration.User'
105
105
106
106
# Password validation
107
107
# https://docs.djangoproject.com/en/dev/ref/settings/#auth-password-validators
108
108
109
109
AUTH_PASSWORD_VALIDATORS = []
110
110
if not DEBUG:
111
111
    # XXX: For testing and easily creating new accounts, I've disabled the
112
112
    # validators to make it easier to work with.
113
113
    AUTH_PASSWORD_VALIDATORS = [
114
114
        {
115
115
            'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
116
116
        },
117
117
        {
118
118
            'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
119
119
        },
120
120
        {
121
121
            'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
122
122
        },
123
123
        {
124
124
            'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
125
125
        },
126
126
    ]
127
127
128
128
CACHES = {
129
129
    'default': {
130
130
        'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
131
131
    }
132
132
}
133
133
134
134
# Internationalization
135
135
# https://docs.djangoproject.com/en/dev/topics/i18n/
136
136
137
137
LANGUAGE_CODE = 'en-us'
138
138
139
139
TIME_ZONE = 'UTC'
140
140
141
141
USE_I18N = True
142
142
143
143
USE_L10N = True
144
144
145
145
USE_TZ = True
146
146
147
147
148
148
# Static files (CSS, JavaScript, Images)
149
149
# https://docs.djangoproject.com/en/dev/howto/static-files/
150
150
151
151
STATICFILES_DIRS = [
152
152
    BASE_DIR + "/static",
153
153
    ]
154
154
#STATIC_ROOT = BASE_DIR + '/static/'
155
155
STATIC_URL = '/static/'
156
156
MEDIA_URL = '/media/'
157
157

joeni/urls.py

1 addition and 1 deletion.

View changes Hide changes
1
1
2
2
The `urlpatterns` list routes URLs to views. For more information please see:
3
3
    https://docs.djangoproject.com/en/dev/topics/http/urls/
4
4
Examples:
5
5
Function views
6
6
    1. Add an import:  from my_app import views
7
7
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
8
8
Class-based views
9
9
    1. Add an import:  from other_app.views import Home
10
10
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
11
11
Including another URLconf
12
12
    1. Import the include() function: from django.urls import include, path
13
13
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
14
14
"""
15
15
from django.contrib import admin
16
16
from django.urls import path, include
17
17
from django.conf.urls.i18n import i18n_patterns
18
18
from django.conf.urls.static import static
19
19
from django.utils.translation import ugettext_lazy as _
20
20
from . import settings
21
21
import agora
22
22
import administration
23
23
24
24
urlpatterns = [
25
25
    path('admin/', admin.site.urls),
26
26
    path('agora/', include('agora.urls')),
27
27
    path('administration/', include('administration.urls')),
28
-
    path('courses/', include('courses.urls')),
+
28
    path('courses/', include('courses.urls')),
29
29
#    path(_('administration/'), include('administration.urls')),
30
30
#    path(_('courses/'), include('courses.urls')),
31
31
32
32
] + static(settings.STATIC_URL)#, document_root=settings.STATIC_ROOT)
33
33
#urlpatterns += i18n_patterns(
34
34
#)
35
35