Commit 8ffb11b4 authored by Luko van der Maas's avatar Luko van der Maas
Browse files

Merge branch 'thumbnail-docs' into 'master'

Improve Sphinx generated documentation

Closes #826

See merge request !1158
parents 17f1d109 0af5b631
......@@ -6,12 +6,6 @@ New new Thalia website, now with extra Django.
#Concrexit
[![build status](https://gitlab.science.ru.nl/thalia/concrexit/badges/master/build.svg)](https://gitlab.science.ru.nl/thalia/concrexit/commits/master)
[![coverage report](https://gitlab.science.ru.nl/thalia/concrexit/badges/master/coverage.svg)](https://gitlab.science.ru.nl/thalia/concrexit/commits/master)
Getting started
---------------
......
......@@ -49,6 +49,7 @@ extensions = [
'sphinx.ext.mathjax',
'sphinx.ext.viewcode',
'sphinx.ext.intersphinx',
'sphinx.ext.graphviz',
]
# Add any paths that contain templates here, relative to this directory.
......@@ -127,6 +128,7 @@ htmlhelp_basename = 'Concrexitdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
'maxlistdepth': '10'
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
......@@ -149,7 +151,7 @@ latex_elements = {
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'Concrexit.tex', 'Concrexit Documentation',
'Thom Wiggers, Luuk Scholten, Joost Rijneveld', 'manual'),
'Thalia Technicie', 'manual'),
]
......@@ -189,9 +191,9 @@ doctest_test_doctest_blocks = ''
# -- intersphinx ---
intersphinx_mapping = {
'python': ('https://docs.python.org/3.6', None),
'django': ('https://docs.djangoproject.com/en/2.0/',
'https://docs.djangoproject.com/en/2.0/_objects/'),
'python': ('https://docs.python.org/3.7', None),
'django': ('https://docs.djangoproject.com/en/2.1/',
'https://docs.djangoproject.com/en/2.1/_objects/'),
}
# -- Supress warnings ---
......
Registrations
=====
This document explains how the registrations module behaviour is defined.
The behaviour of upgrading an existing 'year' membership to a 'study' membership (until graduation) is taken from the HR. If the HR ever changes this behaviour should be changed to reflect those changes.
_Note that registrations and renewals for benefactors are implemented in the models, there are simply no views providing this functionality. If we ever want to implement this then it would be best to create a complete new form just for benefactors registrations._
## New member registration
### Frontend
- User enters information
- User accepts privacy policy
- System validates info
- Correct address
- Valid and unique email address
- Checked against existing users
- Privacy policy accepted
- If the selected member type is 'member':
- valid and unique student number
- selected programme
- cohort
- Registration model created (status: Awaiting email confirmation)
- Email address confirmation sent
- User confirms email address
- Registration model status changed (status: Ready for review)
### Backend
1. Admin accepts registration
- System checks if username is unique
- If it's not unique a username can be entered manually
- If it's still not unique the registration cannot be accepted
- If it's unique the generated username will be added to the registration
- Payment model is created (processed: False)
- Amount is calculated based on the selected length ('study' or 'year')
- Values are located in thaliawebsite.settings
- Email is sent as acceptance confirmation containg instructions for [payment](#payment-processing)
2. Admin rejects registration
- Email is sent as rejection message
## Existing user membership renewal
### Frontend
- User enters information (length, type)
- If latest membership has not ended yet: always allow 'study' length
- If latest membership has ended or ends within 1 month: also allow 'year' length
- If latest membership is 'study' and did not end: do not allow renewal
- Renewal model created (status: Ready for review)
### Backend
1. Admin accepts renewal
- Payment model is created (processed: False)
- Amount is calculated based on selected length ('study' or 'year')
- Values are located in thaliawebsite.settings
- If the current membership has not ended yet and an until date is present for that membership and
the selected length is 'study' the amount will be price['study'] - price['year']
- Email is sent as acceptance confirmation containg instructions for [payment](#payment-processing)
2. Admin rejects renewal
- Email is sent as rejection message
## Payment processing
### Backend
- Admin (or the system, if automated using e.g. iDeal) processes payment
- If this is a Registration model then User and Member models are created
- If this is a Renewal model then the Member is retrieved
- A membership is added to the provided Member model based on the provided length
- If the __latest__ (_not current, since there may have been some time between asking for the upgrade and accepting it_) membership has an until date and
the selected length is 'study' that membership will be updated to have None as until date. No new membership will be created.
- During a lecture year the until date will be the 31 August of the lecture year + 1. Thus is you process payments in November 2016 that means the memberships will end on 31 August 2017
- For payments processed in August the lecture year will be increased by 1. So if you process payments in August 2017 that means the memberships will end on 31 August 2018.
- Payment confirmation sent (if this is a Renewal model)
*************
Registrations
*************
This document explains how the registrations module behaviour is defined.
The behaviour of upgrading an existing 'year' membership to a 'study' membership (until graduation) is taken from the HR. If the HR ever changes this behaviour should be changed to reflect those changes.
*Note that registrations and renewals for benefactors are implemented in the models, there are simply no views providing this functionality. If we ever want to implement this then it would be best to create a complete new form just for benefactors registrations.*
New member registration
=======================
Frontend
--------
- User enters information
- User accepts privacy policy
- System validates info
- Correct address
- Valid and unique email address
- Checked against existing users
- Privacy policy accepted
- If the selected member type is 'member':
- valid and unique student number
- selected programme
- cohort
- Registration model created (status: Awaiting email confirmation)
- Email address confirmation sent
- User confirms email address
- Registration model status changed (status: Ready for review)
Backend
-------
1. Admin accepts registration
- System checks if username is unique
- If it's not unique a username can be entered manually
- If it's still not unique the registration cannot be accepted
- If it's unique the generated username will be added to the registration
- Payment model is created (processed: False)
- Amount is calculated based on the selected length ('study' or 'year')
- Values are located in thaliawebsite.settings
- Email is sent as acceptance confirmation containg instructions for `Payment processing`_
2. Admin rejects registration
- Email is sent as rejection message
Existing user membership renewal
================================
Frontend
--------
- User enters information (length, type)
- If latest membership has not ended yet: always allow 'study' length
- If latest membership has ended or ends within 1 month: also allow 'year' length
- If latest membership is 'study' and did not end: do not allow renewal
- Renewal model created (status: Ready for review)
Backend
-------
1. Admin accepts renewal
- Payment model is created (processed: False)
- Amount is calculated based on selected length ('study' or 'year')
- Values are located in thaliawebsite.settings
- If the current membership has not ended yet and an until date is present for that membership and
the selected length is 'study' the amount will be `price['study'] - price['year']`
- Email is sent as acceptance confirmation containg instructions for `Payment processing`_
2. Admin rejects renewal
- Email is sent as rejection message
Payment processing
==================
Backend
-------
- Admin (or the system, if automated using e.g. iDeal) processes payment
- If this is a Registration model then User and Member models are created
- If this is a Renewal model then the Member is retrieved
- A membership is added to the provided Member model based on the provided length
- If the **latest** (*not current, since there may have been some time between asking for the upgrade and accepting it*) membership has an until date and
the selected length is 'study' that membership will be updated to have None as until date. No new membership will be created.
- During a lecture year the until date will be the 31 August of the lecture year + 1. Thus is you process payments in November 2016 that means the memberships will end on 31 August 2017
- For payments processed in August the lecture year will be increased by 1. So if you process payments in August 2017 that means the memberships will end on 31 August 2018.
- Payment confirmation sent (if this is a Renewal model)
"""
.. The file location is relative to the docs folder.
.. include:: ../website/registrations/README.rst
"""
******
Media & Thumbnailing
=====
******
This document explains how the `utils.media` package enables us to serve public
and private media (user-uploaded) files. We also give an outline of the
workings of our thumbnailing implementation.
## Media types
Media types
===========
We differentiate between two types of media: public and private. Public means
that the files can be served without any kind of authentication. Requests for
private files have to be checked by Django first before we offload serving
the file using [django-sendfile2](https://github.com/moggers87/django-sendfile2).
### Public
Public
------
The public files are saved in `MEDIA_ROOT/public/`.
These files can be served by nginx like one would with a regular
[`MEDIA_ROOT`](https://docs.djangoproject.com/en/2.1/ref/settings/#media-root).
The files should be served at `MEDIA_URL/public/`.
### Private
Private
-------
The private files are saved in `MEDIA_ROOT` where the `public` folder is
the exception. This is a legacy implementation from our previous thumbnailing
......@@ -29,7 +34,8 @@ are located at `MEDIA_URL/private/`. This path should be available to concrexit
so that the media files can be served correctly: the signature
(more on that below) should be valid and match the path.
#### Signatures
Signatures
----------
To make sure the private media files stay private we have implemented a
signature-based security mechanism based on the idea behind the
......@@ -44,23 +50,24 @@ matches the path in the url the file will be made available to the user.
A second, optional, key is `attachment` which is used by the sendfile backend
to force a download if the value is `True`.
```python
sig_info = { 'serve_path': f'{settings.MEDIA_ROOT}/<image location>' }
print(signing.dumps(sig_info))
'eyJzZXJ2ZV9wYXRoIjoiL21lZGlhLzxpbWFnZSBsb2NhdGlvbj4ifQ:1gsbnC:QJTqFUWY6HxMBTEIxYPl9V1yf48'
```
.. code-block:: python
sig_info = { 'serve_path': f'{settings.MEDIA_ROOT}/<image location>' }
print(signing.dumps(sig_info))
'eyJzZXJ2ZV9wYXRoIjoiL21lZGlhLzxpbWFnZSBsb2NhdGlvbj4ifQ:1gsbnC:QJTqFUWY6HxMBTEIxYPl9V1yf48'
The signature is appended to the location using a query parameter with
the key `sig` and is valid for 3 hours as implemented by
`utils.media.views._get_image_information`.
```
https://<base url>/media/private/<image location>?sig=eyJzZXJ2ZV9wYXRoIjoiL21lZGlhLzx...
```
.. code-block::
https://<base url>/media/private/<image location>?sig=eyJzZXJ2ZV9wYXRoIjoiL21lZGlhLzx...
To get the url of a media item you can use `utils.media.services.get_media_url()`. **Never use this to get a url directly from user input!**
## Thumbnails
Thumbnails
==========
To make sure we do not have to serve full-size images to users every time they
open a page we decided to thumbnail our images. This functionality is provided
......@@ -86,37 +93,38 @@ input directly!** They do not protect against directory traversel and assume
the path is correct. They are meant to be used with a path from an `ImageField`
or similar.
```mermaid
graph TB
get[get_thumbnail_url]
generate[generate_thumbnail]
public[serve thumbnail as public media file]
private[serve thumbnail as private media file]
get-->|no thumbnail|generate
get-->|thumbnail exists\n& is public|public
generate-->|redirects|public
generate-->|redirects|private
get-->|thumbnail exists\n& is private|private
```
.. graphviz::
digraph thumbnails {
get[label="get_thumbnail_url"]
generate[label="generate_thumbnail"]
public[label="serve thumbnail as public media file"]
private[label="serve thumbnail as private media file"]
get -> generate [label="no thumbnail"]
get -> public [label="thumbnail exists & is public"]
get -> private [label="thumbnail exists & is private"]
generate -> public [label="redirects"]
generate -> private [label="redirects"]
}
### Signatures
Signatures
----------
The signature used for the generation of thumbnails extends the signature to
load a private media file with keys used for the generation. The signature
contains all information required to generate the thumbnail.
```python
{
'visibility': 'public',
'size': '300x300',
'fit': 0,
'path': 'image.jpeg',
'thumb_path': 'thumbnails/300x300_0/image.jpeg',
'serve_path': '/media/public/thumbnails/300x300_0/image.jpeg'
}
```
.. code-block:: python
{
'visibility': 'public',
'size': '300x300',
'fit': 0,
'path': 'image.jpeg',
'thumb_path': 'thumbnails/300x300_0/image.jpeg',
'serve_path': '/media/public/thumbnails/300x300_0/image.jpeg'
}
The signature protects us against path tampering and thus path traversal and
DDoS attacks. We also know that a user can only get a valid signature if
......
"""
.. The file location is relative to the docs folder.
.. include:: ../website/utils/media/README.rst
"""
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment